# 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=`. **`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 ``. - `containers/[id]/page.tsx` — renders ``. - `containers/new/page.tsx` — renders `` 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: ```typescript nav: { href: "/garden", label: "Garden", icon: "sprout" } ``` ### Activity log `renderActivity` for `garden.container` entity type in the manifest: - `create` → `Created container "${name}"` - `update` → `Updated container "${name}"` - `delete` → `Deleted container` ### Share links 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 73–74). ## 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.