Files
famapp/docs/tasks/74-garden-integrations.md
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.2 KiB

74 — Garden integrations (calendar + lists)

Goal

Wire the garden module into the two existing coordination modules:

  1. Calendar: schedule a plant care event on the household calendar from within a care schedule row.
  2. Lists: push all overdue care tasks to the household task list in one tap.

Depends on

  • 73 (care schedules + overdue queries)
  • 10 (calendar module — createEvent)
  • 11 (lists module — addItem)

Scope

Calendar integration

Server action (src/modules/garden/server/actions.ts)

scheduleOnCalendar(input: { scheduleId, calendarId, reminderMinutesBefore? }):

  • Look up the schedule + plant; household check.
  • startAt = next_due_at (reject with user-facing error if null).
  • endAt = startAt + 30 min; allDay = false.
  • Title convention: "Water ${plant.name}" / "Fertilize ${plant.name}" / "Repot ${plant.name}" / "${careType} — ${plant.name}" for other types.
  • Notes: "Scheduled from garden. Next due: ${next_due_at.toLocaleDateString()}".
  • Call createCalendarEvent(...) from the bridge below.

Bridge file (src/modules/garden/server/calendar-bridge.ts)

export { createEvent as createCalendarEvent } from "@/modules/calendar/server/actions";
export { listCalendars } from "@/modules/calendar/server/queries";

This keeps the cross-module dependency explicit and swappable without touching _core.

UI

In care-schedule-editor.tsx, add an "Add to calendar" button per schedule row. Clicking it opens a popover with: calendar select (from listCalendars) + optional reminder-minutes input + "Schedule" button. Show a success toast linking to /calendar.


Lists integration

Server action (src/modules/garden/server/actions.ts)

pushOverdueToTaskList(): Promise<{ added: number }>:

  • Call getOverduePlants(householdId).
  • Find the household default task list via the bridge below.
  • For each overdue (plant, schedule) pair insert a list item:
    • text: same title convention as the calendar integration.
    • notes: "Overdue by ${daysOverdue} day(s)".
    • dueAt: schedule.next_due_at.
  • Skip pairs where an identical text item already exists in the list (prevent duplicates).
  • Return { added: N }.

Bridge file (src/modules/garden/server/lists-bridge.ts)

export { addItem as addListItem } from "@/modules/lists/server/actions";
export { listLists } from "@/modules/lists/server/queries";

UI

Add an "Add overdue to task list" button to the /garden page header (available before the dashboard widget in task 75). Shows a toast: "Added N tasks to your task list." If N = 0, toast says "No overdue care tasks."

Out of scope

  • Bi-directional sync (checking off a list item does not mark the plant as cared-for).
  • Recurring calendar events (garden creates single events only).

Acceptance criteria

  • "Add to calendar" creates a calendar event visible at /calendar with the correct title.
  • "Add overdue to task list" pushes one item per overdue schedule, skips existing duplicates.
  • Items appear in the task list at /lists.
  • Both actions enforce household scope.
  • Both are graceful no-ops when nothing is overdue / due.