Files
famapp/docs/tasks/71-garden-containers.md
T
ginnoir 2fd0677c5f feat: add garden module infrastructure (task 70)
- Add MinIO service to compose.yaml with named volume
- Add generic /api/uploads POST+GET route with auth, 5 MB, image/* guards
- Add src/lib/minio.ts singleton client with ensureBucket helper
- Add garden Drizzle schema: containers, plants, care_logs, care_schedules, species_cache
- Register garden module in src/modules/index.ts and src/lib/db.ts
- Apply migration 0015_garden_schema.sql
- Add MINIO_* and PERENUAL_API_KEY to .env.example
- Add task briefs 70-75 for the full garden feature arc
- Add uploads.spec.ts E2E test (RED → GREEN on route auth guard)
2026-06-01 19:23:19 -05:00

3.5 KiB
Raw Blame History

71 — Garden containers

Goal

Implement full CRUD for containers — the grouping layer above individual plants (shelves, terrariums, raised beds, etc.). A container has a name, type, location notes, and an optional cover photo.

Depends on

  • 70 (garden infrastructure — schema, MinIO, upload route)

Scope

Server (src/modules/garden/server/)

Add to actions.ts:

  • createContainer(input: { name, type, locationNotes?, coverImageUrl? }) — Zod-validated, getCurrentSession, insert, logActivity, revalidatePath("/garden").
  • updateContainer(input: { id, name?, type?, locationNotes?, coverImageUrl? }) — patch, household membership check, logActivity.
  • deleteContainer(input: { id }) — household membership check; plants with this container_id have it set to null (handled by schema ON DELETE SET NULL); logActivity; revalidatePath.

Add to queries.ts:

  • listContainers() — returns all containers for the current household with a computed plantCount.
  • getContainer(id) — returns the container + all its plants (with last-watered date derived from care logs — use a lateral join or subquery).

UI (src/modules/garden/components/)

container-list.tsx (server component)

  • Grid of cards. Each card: cover photo (placeholder icon if none), name, type badge, plant count.
  • "New container" button → sheet/dialog.

container-detail.tsx (server component)

  • Header: cover photo, name, type, location notes, edit/delete buttons.
  • Plant grid (placeholder for now — task 72 fills in the plant cards).
  • "+ Add plant to this container" button — links to /garden/plants/new?containerId=<id>.

container-form.tsx (client component)

  • Fields: name (required), type (select — 7 options), location notes (textarea), cover photo (file input → POST /api/uploads → stores returned URL).
  • Used for both create and edit via an optional existing prop.

Pages (src/app/garden/)

  • page.tsx/garden root page: two tabs — "Plants" (placeholder for task 72) and "Containers". Containers tab renders <ContainerList />.
  • containers/[id]/page.tsx — renders <ContainerDetail />.
  • containers/new/page.tsx — renders <ContainerForm /> in create mode; redirects to /garden on success.

Nav

Add /garden to the bottom nav and sidebar. Use the sprout Lucide icon (or nearest available). Register in the garden manifest:

nav: { href: "/garden", label: "Garden", icon: "sprout" }

Activity log

renderActivity for garden.container entity type in the manifest:

  • createCreated container "${name}"
  • updateUpdated container "${name}"
  • deleteDeleted container

Containers are shareable (read-only). Add loadForShare and renderSharedView to the entity registration so a share link shows the container's name, type, location notes, cover photo, and plant names. Add loadContainerForShare(id) in src/modules/garden/server/share-queries.ts.

Out of scope

  • Plant cards inside the container detail (task 72 fills those in).
  • Care tracking (tasks 7374).

Acceptance criteria

  • Create / edit / delete containers works end-to-end.
  • Cover photo uploads to MinIO and renders on the card and detail page.
  • Deleting a container nullifies container_id on its plants rather than deleting the plants.
  • /garden page renders with a Containers tab.
  • garden.container entity is registered with share support in the manifest.
  • Activity log entries appear for create / update / delete.