Files
famapp/docs/tasks/73-garden-care-tracking.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.9 KiB

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)

{
  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.