- 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)
76 lines
3.9 KiB
Markdown
76 lines
3.9 KiB
Markdown
# 73 — Garden care tracking
|
|
|
|
## Goal
|
|
|
|
Implement the **care log** and **care schedule** systems. Users can log a care event for any plant (watered, fertilized, repotted, etc.), set up recurring schedules with an interval, and receive reminders when care is due.
|
|
|
|
## Depends on
|
|
|
|
- 70 (schema), 72 (plants exist to attach care records to)
|
|
- 41 (reminders system — `scheduleReminder` / `cancelReminder`)
|
|
|
|
## Scope
|
|
|
|
### Care log actions (`src/modules/garden/server/actions.ts`)
|
|
|
|
- `logCare(input: { plantId, careType, notes?, performedAt? })` — Zod-validated; `performedAt` defaults to now(). Insert into `garden_care_logs`. After inserting: call `updateScheduleAfterCare(plantId, careType)`, call `logActivity`, `revalidatePath`.
|
|
- `deleteCareLog(input: { id })` — household check, delete.
|
|
|
|
### Care schedule actions (`src/modules/garden/server/actions.ts`)
|
|
|
|
- `upsertCareSchedule(input: { plantId, careType, intervalDays, enabled? })` — upsert on `(plant_id, care_type)`. Recompute `next_due_at`: if `last_performed_at` exists use `last_performed_at + intervalDays`, else `now() + intervalDays`. Wire reminder: `scheduleReminder({ entityType: "garden.schedule", entityId: scheduleRow.id, fireAt: next_due_at })`.
|
|
- `deleteCareSchedule(input: { id })` — `cancelReminder("garden.schedule", id)`, delete.
|
|
- `toggleCareSchedule(input: { id, enabled })` — flip `enabled`; cancel reminder if disabling, reschedule if enabling.
|
|
|
|
### Schedule update helper (`src/modules/garden/server/care-schedule.ts`)
|
|
|
|
`updateScheduleAfterCare(plantId, careType)` — finds the schedule row, sets `last_performed_at = now()`, `next_due_at = now() + interval_days`, cancels old reminder, schedules new one.
|
|
|
|
Uses `entityType = "garden.schedule"` + `entityId = schedule.id` to satisfy the unique constraint on `(entity_type, entity_id)` in the reminders table — one reminder per schedule row, not per plant.
|
|
|
|
### Queries (`src/modules/garden/server/queries.ts`)
|
|
|
|
- `getCareLogs(plantId, limit?)` — most recent N logs ordered `performed_at desc`.
|
|
- `getCareSchedules(plantId)` — all schedules with computed `daysUntilDue` and `isOverdue`.
|
|
- `getOverduePlants(householdId)` — plants with at least one enabled schedule where `next_due_at < now()`, sorted most-overdue first.
|
|
- `getCareDueSoon(householdId, withinDays)` — plants with care due within N days.
|
|
|
|
### UI (`src/modules/garden/components/`)
|
|
|
|
**`care-log-form.tsx`** (client component) — plant picker + care type select + optional notes + optional performed-at datetime (defaults to now). Used as a sheet and inline in the plant detail Care tab.
|
|
|
|
**`care-schedule-editor.tsx`** (client component) — list of active schedules per plant (care type, interval, next due, enabled toggle, delete). "Add schedule" form with care type + interval days.
|
|
|
|
**`care-history-list.tsx`** (server component) — chronological log entries: care type icon, "X days ago", performed-by avatar, notes.
|
|
|
|
### Plant detail Care tab (update `plant-detail.tsx` from task 72)
|
|
|
|
The Care tab (previously placeholder) renders: `<CareScheduleEditor />` → "Log care" button → `<CareHistoryList />`.
|
|
|
|
### Quick-add (update manifest)
|
|
|
|
```typescript
|
|
{
|
|
id: "garden.log-care",
|
|
label: "Log plant care",
|
|
icon: "droplets",
|
|
url: "/garden",
|
|
}
|
|
```
|
|
|
|
## Out of scope
|
|
|
|
- Calendar/lists integration (task 74).
|
|
- Dashboard widget (task 75).
|
|
|
|
## Acceptance criteria
|
|
|
|
- [ ] Logging care inserts a log row and updates `last_performed_at` + `next_due_at` on the matching schedule.
|
|
- [ ] Creating a schedule with interval 7 sets `next_due_at` to 7 days from now; a reminder is scheduled.
|
|
- [ ] After logging care, old reminder is cancelled and a new one scheduled.
|
|
- [ ] Disabling a schedule cancels its reminder; re-enabling reschedules it.
|
|
- [ ] Deleting a schedule cancels its reminder.
|
|
- [ ] `getOverduePlants` returns correctly sorted results.
|
|
- [ ] Care tab in plant detail shows schedule editor, log form, and history.
|
|
- [ ] "Log plant care" appears in the quick-add sheet.
|