Back
WhatIGrow — App Breakdown

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-pattern utility and @media print rules (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 nested care_blurbs object (8 required sub-fields). The frost field is an enum.
  • IDENTIFY_PROMPT — a botanist persona prompt that embeds a JSON_STRUCTURE template 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):

  1. identifyPlant — Auth-required. Takes image_url, calls InvokeLLM with IDENTIFY_PROMPT + the image as file_urls + PLANT_CARE_SCHEMA. Returns {{ plant }}. (Synchronous, used by older flows.)
  2. identifyPlantBackground — The main identification path. Takes plant_id. Fetches the plant (asServiceRole), guards on identification_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.
  3. correctPlantInfo — Auth-required. Takes name, calls InvokeLLM with buildCorrectPrompt(name) + PLANT_CARE_SCHEMA. Returns {{ plant }}. Used when a user edits a plant's name inline — it regenerates all care data.
  4. diagnoseProblem — Auth-required. Takes image_url, plant_name, plant_type, care_context, description. Builds a plant-pathologist prompt, calls InvokeLLM with the image, returns {{ diagnosis, instructions, severity }}.
  5. 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 }}.
  6. getPublicGallery — Public. Takes user_id. Returns that user's GalleryMedia + Plant images (filtered to those with URLs). Powers the shareable /gallery/:userId page.
  7. 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 via src/lib/weatherApi.js to avoid shared-IP rate limits — this backend function exists as a fallback/alternative.)
  8. guestIdentifyPlant — Public (no auth). Takes image_data_url (base64 data URL), converts to a Blob, uploads via UploadFile, then calls InvokeLLM with the image + IDENTIFY_PROMPT. Returns {{ plant, image_url }}. No persistence.
  9. backfillPlantCareData — Admin-only. Finds all plants missing frost or care_blurbs.frost, regenerates their care data via buildCorrectPrompt. 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: AuthProviderQueryClientProviderRouterAuthenticatedApp (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 GrowSpaceSection components (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…" (MovePlantsDialog bulk-updates grow_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

ComponentPurpose
AppLayoutHeader/nav/footer wrapper + Weather dialog + GuidedTour
AddPlantModalCamera/Upload buttons → UploadFile → creates Plant with identification_status: 'identifying' → fires identifyPlantBackground → closes immediately
GuestIdentifyModalSame UX for guests → calls guestIdentifyPlant with a base64 data URL → shows results inline → "Create account to save"
GrowSpaceSectionCollapsible group of plants (desktop table + mobile cards), handles identifying/failed states, retry button
PlantListViewPrintable sortable table grouped by space (sort by name/date/type/temp)
CareBlurbDialogModal showing the detailed paragraph for one care attribute
DatePlantedButtonPopover date picker that sets date_planted on a plant
MovePlantsDialogBulk-move selected plants to a new grow space
WeatherDialog / HourlyForecastDialogGeolocation → Open-Meteo → day/night cards + 7-day forecast + hourly drill-down
GuestBannerAmber "create an account" bar for guests
GrowSpaceSelectReusable dropdown + inline "create new space"
AddGrowSpaceModalCreate a grow space (name, type, custom label)
GuidedTour5-step intro modal (Welcome → Identify → Spaces → Diagnose → Settings)
ProtectedRouteGuest-friendly auth gate
UserNotRegisteredErrorError screen for unregistered users

9.Lib & hooks

  • src/lib/tempUtils.jscToF, formatTemp, formatTempRange (handles °C/°F display).
  • src/lib/growSpaceTypes.jsSPACE_TYPES array (greenhouse/indoor/outdoor/custom with emoji icons), getSpaceTypeLabel, getSpaceTypeIcon.
  • src/lib/plantAttrs.jsATTRS array (the 8 care attributes with labels, blurb keys, formatters) + ATTR_ICONS (emojis). Used by Home and PlantDetail.
  • src/lib/weatherApi.jsfetchLocalWeather calls Open-Meteo directly from the browser, splits day/night hours.
  • src/lib/AuthContext.jsx — auth provider (checks app public settings, current user, exposes isAuthenticated, 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)

  1. User opens AddPlantModal, picks a grow space, takes/uploads a photo.
  2. Photo → UploadFile → get file_url.
  3. Creates a Plant with name: 'Identifying…', image_url, grow_space_id, identification_status: 'identifying'.
  4. Fires identifyPlantBackground({{ plant_id }}) (fire-and-forget) and closes the modal.
  5. Garden shows an "Identifying…" card with a spinner.
  6. Backend function calls InvokeLLM with the image + PLANT_CARE_SCHEMA (retries 3×), writes all care fields + identification_status: 'identified'.
  7. Realtime subscription in Home picks up the update → card refreshes with the real name + care data.

B. Identify as a guest

  1. GuestIdentifyModal → photo → base64 data URL → guestIdentifyPlant → uploads + identifies → returns care info inline (no DB write).
  2. "Create Free Account to Save" CTA → /register.

C. Diagnose a problem

  1. From a plant → "Have a Problem?" → upload photo + description.
  2. diagnoseProblem (with plant context) → {{ diagnosis, instructions, severity }}.
  3. Saved as a Problem record; shown in problem history.

D. Correct a plant name

  1. PlantDetail → pencil icon → edit name → correctPlantInfo({{ name }}) → regenerates ALL care data from the name → updates the Plant.

E. Grow space recommendations

  1. GrowSpaceDetail computes the mathematical overlap of all plants' temp/humidity ranges.
  2. generateGrowSpaceTips sends the plant list to the LLM for a compatible range + tips.

11.Suggested build order to recreate

  1. New Base44 app → you get auth pages, App.jsx, layout, design tokens, shadcn/ui for free.
  2. Set design tokens in src/index.css (green palette, Georgia headings, print rules) + tailwind.config.js.
  3. Create 4 entities (Plant, GrowSpace, Problem, GalleryMedia) with the schemas above.
  4. Create the shared module base44/shared/plantCare.ts (schema + prompts).
  5. Create the 9 backend functions (copy each entry.ts). Test identifyPlantBackground and guestIdentifyPlant end-to-end.
  6. Build lib/hooks: tempUtils, growSpaceTypes, plantAttrs, weatherApi, useTempUnit.
  7. Build components: AppLayout, GuidedTour, GuestBanner, AddPlantModal, GuestIdentifyModal, GrowSpaceSection, PlantListView, CareBlurbDialog, DatePlantedButton, MovePlantsDialog, WeatherDialog, HourlyForecastDialog, GrowSpaceSelect, AddGrowSpaceModal, ProtectedRoute.
  8. Build pages: Home, PlantDetail, PlantProblems, GrowSpaces, GrowSpaceDetail, Preferences, Gallery, PublicGallery.
  9. Wire routes in App.jsx (public gallery + auth routes outside the protected group; everything else inside ProtectedRoute + AppLayout).
  10. Update index.html title 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.