Internal docs. Architecture, API reference, deployment, and data model.
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Pi Cron (hourly) â
â ââ save-forecast.py â AMFOR blend â MySQL â
ââââââââââââââââââââââââââââŽâââââââââââââââââââââââââââââââ
â (amforcas_forecasts DB)
ââââââââââââââââââââââââââââžâââââââââââââââââââââââââââââââ
â gardener.amfor-cast.com â â
â ââ index.html (SPA) â /api/plants.php â
â â ââ Plant Now â ââ ?action=now â
â â ââ My Garden â ââ ?action=catalog â
â â ââ Catalog â ââ ?action=plant&slug=X â
â â ââ Companions â ââ ?action=companions â
â â ââ Calendar â ââ ?action=weather â
â â â ââ ?action=full_calendar â
â ââ user-guide.html â ââ ?action=categories â
â ââ dev-docs.html (ð) â â
ââââââââââââââââââââââââââââīâââââââââââââââââââââââââââââââ
â â
âž âž
âââââââââââââââââââââââââ ââââââââââââââââââââââââââââââââ
â amforcas_gardener DB â â amforcas_forecasts DB â
â ââ plants (154 rows) â â ââ forecasts + hours â
â ââ planting_calendar â â ââ users, sessions â
â ââ companions â â ââ (shared with main app) â
â ââ plant_care â ââââââââââââââââââââââââââââââââ
â ââ user_garden â
âââââââââââââââââââââââââ
Two databases: amforcas_gardener for plant data (catalog, calendar, companions) and amforcas_forecasts for weather data (shared with the main amfor-cast.com app). The API uses get_gardener_db() for plant queries and get_db() for weather.
Hash-based SPA routing. The URL reflects the current view:
| URL Hash | View | Function |
|---|---|---|
#now | Plant Now | loadWhatNow() |
#mygarden | My Garden | renderMyGarden() |
#catalog | Catalog | loadCatalog() |
#companions | Companions | renderCompanions() |
#calendar | Calendar | renderCalendar() |
#plant/slug | Plant detail | loadPlantDetail(slug) |
Tab switches push history via history.pushState(). The popstate listener handles back/forward. Search, city change, and language toggle do NOT push history entries.
| Endpoint | Method | Description |
|---|---|---|
?action=now&month=N | GET | Plants active in month N (default: current). Grouped by activity. |
?action=catalog&category=X&difficulty=X | GET | Full plant list with optional filters. Includes current month activities. |
?action=plant&slug=X | GET | Single plant detail with calendar, companions, care instructions. |
?action=companions&ids=1,2,3 | GET | Companion matrix for selected plant IDs + suggestions. |
?action=weather&city=budapest | GET | 7-day forecast from AMFOR model (fallback: ICON â Best match). Uses amforcas_forecasts DB. |
?action=full_calendar | GET | All calendar entries for all plants (for year overview). |
?action=categories | GET | Plant count by category (dynamic). |
Master plant catalog (~154 rows). Key fields:
| Column | Type | Purpose |
|---|---|---|
slug | VARCHAR(50) UNIQUE | URL-safe identifier |
name_en / name_hu | VARCHAR(100) | Bilingual names |
category | ENUM | vegetable, fruit, herb, flower, green_manure, tree, shrub, perennial, evergreen, groundcover, climber |
subcategory | VARCHAR(50) | Finer grouping (nightshade, legume, cucurbit, root, leafy, etc.) |
frost_sensitive | TINYINT | 1 = dies at frost |
min_soil_temp_c | FLOAT | Min soil temp for germination |
seed_depth_cm | FLOAT | How deep to plant |
spacing_cm | INT | Between plants |
days_to_harvest_min/max | INT | Growing duration range |
description_en/hu, tips_en/hu | TEXT | Bilingual content |
When to do what. References plants.id.
| Column | Purpose |
|---|---|
activity | sow_indoor, sow_outdoor, transplant, harvest, prune |
month_start / month_end | Active window (1-12) |
zone | USDA zone (default: 7a = Hungary) |
notes_en / notes_hu | Bilingual activity notes |
Bidirectional relationships between plants.
| Column | Purpose |
|---|---|
plant_id_a / plant_id_b | The two plants |
relationship | good, bad, neutral |
reason_en / reason_hu | Why |
11 categories in the ENUM. Frontend shows 9 in the filter (excluding flower and green_manure which have âĪ3 plants each but are still searchable).
| Category | Icon | Count | Examples |
|---|---|---|---|
| vegetable | ðĨŽ | 38 | tomato, pepper, carrot, cabbage |
| perennial | ðū | 27 | yarrow, sedum, fescue, ornamental grasses |
| tree | ðģ | 16 | oak, maple, ash, goldenrain |
| shrub | ðŠŧ | 16 | smoke tree, hawthorn, privet, roses |
| fruit | ð | 14 | raspberry, grape, strawberry, kiwi |
| groundcover | ðŠī | 13 | thyme, clover, rupturewort, barrenwort |
| evergreen | ðē | 10 | juniper, pine, yew, cedar, yucca |
| herb | ðŋ | 9 | basil, rosemary, oregano, mint |
| climber | ð§ | 5 | clematis, honeysuckle, trumpet vine |
| flower | ðŧ | 3 | marigold, nasturtium, sunflower |
| green_manure | ðą | 3 | white clover, mustard, strawberry clover |
Three themes stored in localStorage('gardener_theme'):
| Theme | Icon | Style |
|---|---|---|
| garden | ðŋ | Light green, clean (default). No data-theme attribute. |
| emerald | âĻ | Dark green glassmorphism. data-theme="emerald" |
| prism | ð | Purple/pink frosted glass. data-theme="prism" |
The theme button cycles through all three. Each theme overrides CSS custom properties and adds specific card/button styles via [data-theme="X"] selectors.
The ?action=weather endpoint (uses amforcas_forecasts DB):
forecasts for the latest snapshot where model='AMFOR' and city=?forecast_hours for the next 7 days of hourly data# Deploy all gardener files: ./gardener/deploy.sh # Manual single-file deploy: scp gardener/index.html amforcas@amfor-cast:/home/amforcas/gardener.amfor-cast.com/ # SQL imports (run on server): mysql -u amforcas_admin -pDeltaechomary_45 amforcas_gardener \ --default-character-set=utf8mb4 < file.sql
| Local | Server |
|---|---|
gardener/ | /home/amforcas/gardener.amfor-cast.com/ |
api/php/auth.php | /home/amforcas/public_html/api/php/auth.php + /api/auth.php |
api/php/config.php | /home/amforcas/public_html/api/php/config.php + /api/config.php |
/api/config.php and /api/php/config.php). CORS origins must be added to BOTH. Same for auth.php.| File | Purpose |
|---|---|
schema.sql | Full schema (clean install) |
seed-data.sql | Original 60 plants + calendar + companions |
seed-drought-tolerant.sql | 100 drought-tolerant Hungarian plants |
seed-drought-calendar.sql | Calendar entries for drought plants |
seed-missing-calendar.sql | Calendar entries for original plants that were missing them |
migrate-categories.sql | Expand ENUM + reclassify drought plants to proper categories |
migrate-gardener-db.sh | Move tables from amforcas_forecasts â amforcas_gardener |
_en / _hu columns. API returns both; frontend picks based on getLang().t() function in index.html. ~70 strings, static.CATEGORY_LABELS object (bilingual).localStorage('weather_lang'). Shared with main amfor-cast.com site.amfor_session, domain=.amfor-cast.com, SameSite=None, Secure, HttpOnlygardener.amfor-cast.com must be in ALLOWED_ORIGINS in both config filesfetch(..., {credentials: 'include'}) for all cross-origin auth callsdemogunmy_garden = JSON array of plant IDs). IDs coerced to Number for comparison.gardener_city = slug). Auto-detected from user profile if logged in.gardener_theme = garden|emerald|prism).weather_lang = en|hu).user_garden table when logged in)