# Tier 3: Garden empty-state component, toggle-group for notify channels ## Goal Create a reusable EmptyState component for garden (and potentially other modules), and replace manual checkbox patterns with shadcn ToggleGroup for notification channel selection. --- ### Task 3a: Garden empty-state component **New file:** `src/modules/garden/components/empty-state.tsx` **Purpose:** A shared empty-state component for the garden module — replacing ad-hoc "No plants yet." / "No containers yet." paragraphs scattered across garden list/detail pages. **Design (base on shadcn's `` from `src/components/ui/empty.tsx`):** The empty component already exists at `src/components/ui/empty.tsx` — it provides a generic EmptyState wrapper with icon + title + description slots. We should **use it directly**, not create a garden-specific one, unless the garden needs garden-themed defaults. **Garden-specific variants needed:** - "No plants yet" — icon: leaf/plant, title: "No plants yet", description: "Add your first plant to get started." - "No containers yet" — icon: box/pot, title: "No containers yet", description: "Add a container to group your plants." **Files to update:** 1. `src/modules/garden/components/plant-list.tsx` line 22: `

No plants yet.

` → `` 2. `src/modules/garden/components/container-list.tsx` line 39: `"No containers yet. Add one to start organising your plants."` → `` 3. `src/modules/garden/components/container-detail.tsx` line 169: `

No plants in this container yet.

` → inline EmptyState or keep as brief fallback (it's a sub-section, not a full page) **Import:** ```tsx import { EmptyState } from "@/components/ui/empty" import { Sprout, Container } from "lucide-react" // In plant-list: // In container-list: ``` **Acceptance for 3a:** - EmptyState imported from `@/components/ui/empty` in all garden list pages - Zero ad-hoc empty text paragraphs remain in garden component files (check plant-list, container-list, and any other garden page) - Garden empty states are visually consistent (same icon size, title weight, description color) --- ### Task 3b: Toggle-group for notify channels **File:** `src/components/notify-channel-toggles.tsx` **Current state:** Each channel is a native `` wrapped in a label with custom styling. No grouping or visual cohesion. **Change to ToggleGroup + ToggleGroupItem:** ```tsx import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group" export function NotifyChannelToggles({ push, inapp, ntfy, ntfyConfigured, }: { ... }) { const channels: { key: Channel; value: boolean; label: string; disabled?: boolean }[] = [ { key: "push", value: push, label: "Web push" }, { key: "inapp", value: inapp, label: "In-app inbox" }, { key: "ntfy", value: ntfy, label: "ntfy", disabled: !ntfyConfigured }, ]; const enabledKeys = channels.filter(c => c.value && !c.disabled).map(c => c.key); return ( handleToggle(v)}> {channels.map(({ key, value, label, disabled }) => ( {label} ))} ); } ``` **Important:** The ToggleGroup needs to integrate with the existing server action flow (`setNotifChannel`). The `onValueChange` handler should: 1. Detect which channels were added/removed from the selection 2. Call `setNotifChannel(channel, enabled)` for each change 3. Still trigger an optimistic UI update **Alternative approach** (safer — use individual ToggleGroupItems as independent switches): Since these are truly independent toggles (enabling push doesn't require disabling inapp), we keep the current per-channel rendering but swap `` to shadcn's `` component from `@/components/ui/switch`: ```tsx import { Switch } from "@/components/ui/switch"; // Each channel: ; ``` **Decision:** The `` approach is more appropriate here. ToggleGroup is for mutually-exclusive selection; Switch is for independent on/off toggles — which matches the notification channels use case perfectly. **Use `Switch` from shadcn, not ToggleGroup.** **Acceptance for 3b:** - Each channel uses shadcn `` component (not native checkbox) - Visual appearance matches the app's design system - Server action integration works correctly (toggle enables/disables on the server) - Disabled state handled properly for ntfy when unconfigured --- ## Acceptance criteria 1. EmptyState component from `@/components/ui/empty` used in all garden list pages 2. All ad-hoc "No plants yet." / "No containers yet." text replaced with EmptyState or kept as contextual inline text where appropriate 3. Notification channels use shadcn `` components 4. Visual consistency across garden empty states ## Steps 1. Use existing `src/components/ui/empty.tsx` EmptyState in plant-list.tsx and container-list.tsx 2. Replace Switch component imports in notify-channel-toggles.tsx 3. Verify server action integration for toggles 4. Run `pnpm lint` and `pnpm typecheck`