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)
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
# 75 — Garden dashboard, widgets, and manifest completion
|
||||
|
||||
## Goal
|
||||
|
||||
Complete the garden module: full manifest registration, two dashboard widgets, search integration, quick-add polish, and a Playwright happy-path test.
|
||||
|
||||
## Depends on
|
||||
|
||||
- 70–74 (all previous garden tasks)
|
||||
|
||||
## Scope
|
||||
|
||||
### Manifest completion (`src/modules/garden/manifest.tsx`)
|
||||
|
||||
Replace the task-70 stub with the full manifest.
|
||||
|
||||
**Entities:**
|
||||
|
||||
```typescript
|
||||
entities: [
|
||||
{
|
||||
type: "garden.container",
|
||||
label: { singular: "Container", plural: "Containers" },
|
||||
share: { canShare: true, defaultCapabilities: ["read"] },
|
||||
search: { search: searchContainers },
|
||||
resolveUrl: (id) => `/garden/containers/${id}`,
|
||||
loadForShare: loadContainerForShare,
|
||||
renderSharedView: ...,
|
||||
renderActivity: ...,
|
||||
},
|
||||
{
|
||||
type: "garden.plant",
|
||||
label: { singular: "Plant", plural: "Plants" },
|
||||
share: { canShare: true, defaultCapabilities: ["read"] },
|
||||
search: { search: searchPlants },
|
||||
resolveUrl: (id) => `/garden/plants/${id}`,
|
||||
loadForShare: loadPlantForShare,
|
||||
renderSharedView: ...,
|
||||
renderActivity: ...,
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
`garden.schedule` is internal (reminders only) — not registered as a shareable or searchable entity.
|
||||
|
||||
**Quick adds** (consolidate from tasks 71–73):
|
||||
|
||||
- `{ id: "garden.add-plant", label: "Add plant", icon: "leaf", url: "/garden/plants/new" }`
|
||||
- `{ id: "garden.add-container", label: "Add container", icon: "box", url: "/garden/containers/new" }`
|
||||
- `{ id: "garden.log-care", label: "Log plant care", icon: "droplets", url: "/garden?logCare=1" }`
|
||||
|
||||
**Nav**: `{ href: "/garden", label: "Garden", icon: "sprout" }`.
|
||||
|
||||
---
|
||||
|
||||
### Widget 1: `garden.care-due`
|
||||
|
||||
**Title**: "Plants needing care" | **Category**: "Garden"
|
||||
**Default size**: `{ w: 4, h: 4 }` | **Min size**: `{ w: 3, h: 2 }`
|
||||
|
||||
Config schema:
|
||||
|
||||
```typescript
|
||||
z.object({
|
||||
containerIds: z.union([z.literal("all"), z.array(z.string().uuid())]),
|
||||
daysAhead: z.number().int().min(0).max(30).default(0),
|
||||
});
|
||||
```
|
||||
|
||||
Default config: `{ containerIds: "all", daysAhead: 0 }`.
|
||||
|
||||
`resolveConfigOptions`: returns `{ containers: [{ id, name }] }`.
|
||||
|
||||
Render:
|
||||
|
||||
- Empty state if nothing is due: "All plants are on schedule."
|
||||
- Otherwise: compact list sorted by days overdue. Each row: plant name, care type icon, urgency badge ("X days overdue" / "due today"), inline "Log care" button (calls `logCare` action + revalidates — no navigation).
|
||||
- Truncate at 10 rows; "View all" link to `/garden`.
|
||||
- Rows with `next_due_at` within `daysAhead` days show as upcoming in a lighter style below overdue rows.
|
||||
|
||||
---
|
||||
|
||||
### Widget 2: `garden.overview`
|
||||
|
||||
**Title**: "Garden overview" | **Category**: "Garden"
|
||||
**Default size**: `{ w: 3, h: 2 }` | **Min size**: `{ w: 2, h: 2 }`
|
||||
|
||||
Config schema: `z.object({})`.
|
||||
|
||||
Render:
|
||||
|
||||
- Stat row: total plants, total containers, overdue care count.
|
||||
- "Next care" line: "Next: water [Plant] in N days" / "today" / "overdue".
|
||||
- Link to `/garden`.
|
||||
|
||||
---
|
||||
|
||||
### Widget component (`src/modules/garden/components/plant-widget.tsx`)
|
||||
|
||||
Server component implementing both widgets, following the pattern in `src/modules/lists/components/list-widget.tsx`.
|
||||
|
||||
---
|
||||
|
||||
### Search adapters
|
||||
|
||||
Implement `searchContainers` and `searchPlants` using `ilike` on name fields, returning `SearchResult[]`. Both appear in the command palette.
|
||||
|
||||
---
|
||||
|
||||
### Notification body improvement (optional)
|
||||
|
||||
If `tickReminders` in `src/modules/_core/reminders.ts` can be extended without structural changes — add a registry of entity-type resolvers and register `"garden.schedule"` to resolve to `"Time to water [plant name]"`. If it requires core changes, defer and note as known limitation.
|
||||
|
||||
---
|
||||
|
||||
### Playwright E2E test (`tests/garden.spec.ts`)
|
||||
|
||||
Happy path:
|
||||
|
||||
1. Log in as the seeded user.
|
||||
2. Create container "Living Room Shelf".
|
||||
3. Create plant "Pothos" in that container (manual entry, no species lookup).
|
||||
4. Add watering schedule: every 7 days.
|
||||
5. Log care: watered.
|
||||
6. Verify schedule `next_due_at` updates to ~7 days from now.
|
||||
7. Confirm plant appears grouped under "Living Room Shelf" on `/garden`.
|
||||
8. Add `garden.care-due` widget to the default dashboard.
|
||||
9. Confirm no overdue items (just watered).
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Health trend charts.
|
||||
- Weather-based watering adjustments.
|
||||
- Social/community features.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Both widgets register and appear in the widget picker.
|
||||
- [ ] `garden.care-due` shows overdue plants sorted by urgency; inline log button works.
|
||||
- [ ] `garden.overview` shows correct counts.
|
||||
- [ ] Both entities appear in command palette search.
|
||||
- [ ] All three quick-adds appear in the `+` sheet.
|
||||
- [ ] Garden nav link appears in sidebar and bottom nav.
|
||||
- [ ] Playwright happy-path test passes.
|
||||
Reference in New Issue
Block a user