WhatIGrow — Full App Breakdown
A complete reference for recreating this app in another Base44 account.
1.What the app does
An AI-powered plant care companion. Users photograph a plant; an AI identifies it and generates a complete care guide (temperature, humidity, watering, sunlight, fertilizer, frost tolerance, plus detailed paragraphs per attribute). Plants are organized into "Grow Spaces" (greenhouse, indoor, outdoor, custom), each of which gets AI-generated environment recommendations. Users can also diagnose sick plants from a photo, track problems over time, view local weather, and share a public gallery.
Two access modes:
- Guests (unauthenticated) can identify a plant via a one-off flow (no persistence) and are nudged to register.
- Authenticated users get full persistence: save plants, organize spaces, diagnose problems, gallery, weather.
2.Tech stack & design system
Stack: React + Vite, Tailwind CSS, shadcn/ui (Radix), lucide-react icons, react-router-dom, date-fns, @tanstack/react-query. Base44 BaaS for auth, entities, backend functions, and the Core integration package (InvokeLLM, UploadFile).
Design tokens (src/index.css): A green-themed palette. Key values:
--primary: 142 60% 28%(deep green),--accent: 142 50% 90%(light green)- Fonts: Georgia serif for headings/display, system sans for body
- Full dark mode variants under
.dark --radius: 0.75rem- A
.leaf-patternutility and@media printrules (hides nav/buttons, flattens tables for printing)
tailwind.config.js maps these tokens to classes (bg-primary, font-heading, etc.) plus the standard shadcn color/chart/sidebar mappings, accordion animations, and a full 0–100 opacity scale.
3.Data model (4 entities)
Create these entities in the new account with these exact schemas:
Plant (base44/entities/Plant.jsonc)
Fields: name (string, required), scientific_name (string), type (string), growth_cycle (string), min_temp (number °C), max_temp (number °C), min_humidity (number %), max_humidity (number %), watering_frequency (string), sun_preference (string), fertilizer_ratio (string N-P-K), frost (string enum: "Yes"/"No"/"Protected is okay"), detailed_care_info (string, multi-paragraph guide), care_blurbs (object with 8 string sub-fields: temperature, humidity, watering, sun, fertilizer, growth_cycle, type, frost), image_url (uri), grow_space_id (string ref to GrowSpace), date_planted (date), identification_status (string enum: "identifying"/"identified"/"failed"), identification_error (string).
GrowSpace (base44/entities/GrowSpace.jsonc)
Fields: name (string, required), type (string enum: "greenhouse"/"indoor"/"outdoor"/"custom", default "indoor"), custom_label (string, used when type is "custom").
Problem (base44/entities/Problem.jsonc)
Fields: plant_id (string ref, required), plant_name (string), image_url (uri, required), description (string, user notes), diagnosis (string, AI), instructions (string, AI treatment), severity (string enum: "Low"/"Moderate"/"High"/"Critical", default "Moderate").
GalleryMedia (base44/entities/GalleryMedia.jsonc)
Fields: title (string), media_url (uri, required), media_type (string enum: "image"/"video", required).
Built-in fields on every record (don't declare): id, created_date, updated_date, created_by_id. The User entity is built-in and read-only.
4.Backend functions & shared module
Shared module — base44/shared/plantCare.ts
This is the heart of the AI logic. It exports:
PLANT_CARE_SCHEMA— a JSON schema object (type: object) the LLM must return, with all 14 plant fields + the nestedcare_blurbsobject (8 required sub-fields). Thefrostfield is an enum.IDENTIFY_PROMPT— a botanist persona prompt that embeds aJSON_STRUCTUREtemplate string describing exactly what each field should contain.buildCorrectPrompt(name)— a prompt for re-generating care info from a known plant name (used when a user corrects a misidentified plant).
9 backend functions (each in base44/functions/<name>/entry.ts):
- identifyPlant — Auth-required. Takes
image_url, callsInvokeLLMwithIDENTIFY_PROMPT+ the image asfile_urls+PLANT_CARE_SCHEMA. Returns{{ plant }}. (Synchronous, used by older flows.) - identifyPlantBackground — The main identification path. Takes
plant_id. Fetches the plant (asServiceRole), guards onidentification_status === 'identifying', retries up to 3 times with 2s delay, writes all care fields +identification_status: 'identified'back to the Plant, or'failed'with the error. Fire-and-forget from the client after creating a plant. - correctPlantInfo — Auth-required. Takes
name, callsInvokeLLMwithbuildCorrectPrompt(name)+PLANT_CARE_SCHEMA. Returns{{ plant }}. Used when a user edits a plant's name inline — it regenerates all care data. - diagnoseProblem — Auth-required. Takes
image_url,plant_name,plant_type,care_context,description. Builds a plant-pathologist prompt, callsInvokeLLMwith the image, returns{{ diagnosis, instructions, severity }}. - generateGrowSpaceTips — Auth-required. Takes
space_name,space_type,plants[]. Summarizes the plants, asks the LLM for a compatible temp/humidity range + multi-paragraph tips. Returns{{ recommended_temp_min/max, recommended_humidity_min/max, tips }}. - getPublicGallery — Public. Takes
user_id. Returns that user's GalleryMedia + Plant images (filtered to those with URLs). Powers the shareable/gallery/:userIdpage. - getWeather — Public. Takes
latitude/longitude, calls Open-Meteo, splits today into daytime (7AM–7PM) and overnight (7PM–7AM) highs/lows, returns a 7-day week forecast. (The app actually calls Open-Meteo directly from the browser viasrc/lib/weatherApi.jsto avoid shared-IP rate limits — this backend function exists as a fallback/alternative.) - guestIdentifyPlant — Public (no auth). Takes
image_data_url(base64 data URL), converts to a Blob, uploads viaUploadFile, then callsInvokeLLMwith the image +IDENTIFY_PROMPT. Returns{{ plant, image_url }}. No persistence. - backfillPlantCareData — Admin-only. Finds all plants missing
frostorcare_blurbs.frost, regenerates their care data viabuildCorrectPrompt. One-time migration utility.
All functions import createClientFromRequest from npm:@base44/sdk@0.8.44 and use base44.asServiceRole.integrations.Core.InvokeLLM / UploadFile.
5.Routing (src/App.jsx)
Structure: AuthProvider → QueryClientProvider → Router → AuthenticatedApp (handles loading/auth-error states) → Routes.
- Public routes:
/login,/register,/forgot-password,/reset-password,/gallery/:userId(public gallery),/copy(this page). - Protected routes (wrapped in
ProtectedRoute+AppLayout):/(Home),/plants/:id(PlantDetail),/plants/:id/problems(PlantProblems),/grow-spaces(GrowSpaces),/grow-spaces/:id(GrowSpaceDetail),/preferences(Preferences),/gallery(Gallery). *→PageNotFound.
ProtectedRoute is guest-friendly: if auth fails (expired token / no token), it lets the user browse as a guest rather than redirecting — only user_not_registered shows an error. This is what enables the guest identification flow.
6.Layout & navigation (src/components/AppLayout.jsx)
- Sticky header: "WhatIGrow" logo (leaf icon), nav buttons for Weather (opens
WeatherDialog), Spaces, Gallery, Settings. GuestBanner— shows an amber "browsing as a guest, create an account" bar for unauthenticated users.<main>renders<Outlet />.- Footer +
GuidedTour(5-step intro modal, auto-shows once via localStorage, replayable from Preferences).
7.Pages
Home (src/pages/Home.jsx) — the main garden view
- Loads all Plants + GrowSpaces. Realtime subscription on the Plant entity updates cards in place when background identification finishes.
- Groups plants by grow space into
GrowSpaceSectioncomponents (collapsible). - Two view modes: Garden (grouped cards/tables) and List (
PlantListView— printable, sortable). - Select mode: checkboxes to multi-select plants, then a sticky bottom bar with "Move to…" (
MovePlantsDialogbulk-updatesgrow_space_id). - Empty states differ for guests (shows "Identify a Plant" CTA) vs. authenticated (shows "Add your first plant").
- Each care attribute is a click-to-open
CareBlurbDialog.
PlantDetail (/plants/:id)
Full plant view: large image, editable name (pencil icon → inline edit → calls correctPlantInfo and regenerates ALL care data), grid of 8 care attribute cards (each opens CareBlurbDialog), and the full detailed_care_info guide. "Have a Problem?" button → PlantProblems.
PlantProblems (/plants/:id/problems)
Upload a photo of a sick plant + optional description → calls diagnoseProblem → saves a Problem record → shows diagnosis, severity badge, treatment instructions. Lists problem history below.
GrowSpaces (/grow-spaces)
Grid of grow space cards with plant counts. "New Space" opens AddGrowSpaceModal. Shows a warning for unassigned plants.
GrowSpaceDetail (/grow-spaces/:id)
Calculates the overlapping temp/humidity range across all plants in the space (max of mins / min of maxes) and warns if incompatible. Calls generateGrowSpaceTips for AI recommendations. Lists plants.
Preferences (/preferences)
Temperature unit toggle (°C/°F, persisted to localStorage via useTempUnit). Replay tour button.
Gallery (/gallery) & PublicGallery (/gallery/:userId)
Upload images/videos to GalleryMedia. Shows user's setup media + all plant photos. "Share" copies a public link /gallery/:userId which renders via getPublicGallery (no auth needed).
Auth pages (Login, Register, ForgotPassword, ResetPassword)
Standard Base44 boilerplate — email/password, Google OAuth, OTP verification on register, reset flow. Don't recreate these; they ship with every new app.
8.Components
| Component | Purpose |
|---|---|
| AppLayout | Header/nav/footer wrapper + Weather dialog + GuidedTour |
| AddPlantModal | Camera/Upload buttons → UploadFile → creates Plant with identification_status: 'identifying' → fires identifyPlantBackground → closes immediately |
| GuestIdentifyModal | Same UX for guests → calls guestIdentifyPlant with a base64 data URL → shows results inline → "Create account to save" |
| GrowSpaceSection | Collapsible group of plants (desktop table + mobile cards), handles identifying/failed states, retry button |
| PlantListView | Printable sortable table grouped by space (sort by name/date/type/temp) |
| CareBlurbDialog | Modal showing the detailed paragraph for one care attribute |
| DatePlantedButton | Popover date picker that sets date_planted on a plant |
| MovePlantsDialog | Bulk-move selected plants to a new grow space |
| WeatherDialog / HourlyForecastDialog | Geolocation → Open-Meteo → day/night cards + 7-day forecast + hourly drill-down |
| GuestBanner | Amber "create an account" bar for guests |
| GrowSpaceSelect | Reusable dropdown + inline "create new space" |
| AddGrowSpaceModal | Create a grow space (name, type, custom label) |
| GuidedTour | 5-step intro modal (Welcome → Identify → Spaces → Diagnose → Settings) |
| ProtectedRoute | Guest-friendly auth gate |
| UserNotRegisteredError | Error screen for unregistered users |
9.Lib & hooks
src/lib/tempUtils.js—cToF,formatTemp,formatTempRange(handles °C/°F display).src/lib/growSpaceTypes.js—SPACE_TYPESarray (greenhouse/indoor/outdoor/custom with emoji icons),getSpaceTypeLabel,getSpaceTypeIcon.src/lib/plantAttrs.js—ATTRSarray (the 8 care attributes with labels, blurb keys, formatters) +ATTR_ICONS(emojis). Used by Home and PlantDetail.src/lib/weatherApi.js—fetchLocalWeathercalls Open-Meteo directly from the browser, splits day/night hours.src/lib/AuthContext.jsx— auth provider (checks app public settings, current user, exposesisAuthenticated,user,navigateToLogin,logout).src/hooks/useTempUnit.js— localStorage-backed °C/°F preference with cross-tab sync via custom events.src/api/base44Client.js— pre-initialized Base44 SDK client.
10.Key processes (end-to-end flows)
A. Add a plant (authenticated)
- User opens
AddPlantModal, picks a grow space, takes/uploads a photo. - Photo →
UploadFile→ getfile_url. - Creates a Plant with
name: 'Identifying…',image_url,grow_space_id,identification_status: 'identifying'. - Fires
identifyPlantBackground({{ plant_id }})(fire-and-forget) and closes the modal. - Garden shows an "Identifying…" card with a spinner.
- Backend function calls
InvokeLLMwith the image +PLANT_CARE_SCHEMA(retries 3×), writes all care fields +identification_status: 'identified'. - Realtime subscription in Home picks up the update → card refreshes with the real name + care data.
B. Identify as a guest
GuestIdentifyModal→ photo → base64 data URL →guestIdentifyPlant→ uploads + identifies → returns care info inline (no DB write).- "Create Free Account to Save" CTA →
/register.
C. Diagnose a problem
- From a plant → "Have a Problem?" → upload photo + description.
diagnoseProblem(with plant context) →{{ diagnosis, instructions, severity }}.- Saved as a
Problemrecord; shown in problem history.
D. Correct a plant name
- PlantDetail → pencil icon → edit name →
correctPlantInfo({{ name }})→ regenerates ALL care data from the name → updates the Plant.
E. Grow space recommendations
- GrowSpaceDetail computes the mathematical overlap of all plants' temp/humidity ranges.
generateGrowSpaceTipssends the plant list to the LLM for a compatible range + tips.
11.Suggested build order to recreate
- New Base44 app → you get auth pages,
App.jsx, layout, design tokens, shadcn/ui for free. - Set design tokens in
src/index.css(green palette, Georgia headings, print rules) +tailwind.config.js. - Create 4 entities (Plant, GrowSpace, Problem, GalleryMedia) with the schemas above.
- Create the shared module
base44/shared/plantCare.ts(schema + prompts). - Create the 9 backend functions (copy each
entry.ts). TestidentifyPlantBackgroundandguestIdentifyPlantend-to-end. - Build lib/hooks:
tempUtils,growSpaceTypes,plantAttrs,weatherApi,useTempUnit. - Build components:
AppLayout,GuidedTour,GuestBanner,AddPlantModal,GuestIdentifyModal,GrowSpaceSection,PlantListView,CareBlurbDialog,DatePlantedButton,MovePlantsDialog,WeatherDialog,HourlyForecastDialog,GrowSpaceSelect,AddGrowSpaceModal,ProtectedRoute. - Build pages: Home, PlantDetail, PlantProblems, GrowSpaces, GrowSpaceDetail, Preferences, Gallery, PublicGallery.
- Wire routes in
App.jsx(public gallery + auth routes outside the protected group; everything else insideProtectedRoute+AppLayout). - Update
index.htmltitle to "WhatIGrow" and add a favicon.
All branding strings use "WhatIGrow" (footer in AppLayout, Preferences copy, localStorage keys in GuidedTour/useTempUnit). The old verdant_* localStorage keys are migrated to the new whatigrow_* keys on first read so existing users keep their tour-seen and temperature-unit preferences.