Files
famapp/STATUS.md
T
ginnoirandClaude Sonnet 4.6 d5a8bf9d95 Implement tasks 25, 26 + completion visibility setting
Task 25: Multiple dashboards — dashboards table + migration, /d/[slug]
route, root redirect, dashboard tabs in nav with create/rename/delete/
set-default. Task 26: Editable dashboard — react-grid-layout drag/resize
editor, widget picker modal with per-widget configurator auto-generated
from default config, save/reset server actions with Zod validation.

Completion visibility: server-side per-user setting (hours) replaces
localStorage timer; queries filter completed items by updatedAt cutoff;
Settings page dropdown persists preference via server action.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 15:52:59 -05:00

14 KiB
Raw Blame History

Status

Living progress tracker. Update at the end of each task. Codex and Claude Code both work on this project, so write status notes and next-step instructions for either agent to resume. Canonical briefs are AGENTS.md and CLAUDE.md; keep them synchronized. Task briefs live in docs/tasks/.

Done

  • 01 — Repo init & tooling (commit b89690a). pnpm 10 + TS strict + ESLint flat + Prettier. All acceptance criteria green.

  • 02 — Next.js app skeleton. Next.js 15 + React 19 + Tailwind v4 + shadcn/ui (button, card, input, dialog). pnpm dev serves placeholder, pnpm build produces .next/standalone/, pnpm lint clean. Added .npmrc with node-linker=hoisted for Windows symlink compatibility.

  • 03 — Drizzle + Postgres setup. drizzle-orm + postgres driver + drizzle-kit wired up. src/modules/_core/schema.ts declares users, households, household_members. docker-compose.dev.yaml starts Postgres 16. drizzle/0000_silent_magma.sql generated and applied. tsc --noEmit passes.

  • 04 — Module loader & registry. src/modules/_core/module.ts types (ModuleManifest, EntityTypeRegistration, DashboardWidget, etc.), registry.ts singleton with registerModule/getRegistry/getEntityType/getWidget, barrel _core/index.ts. Stub manifests for calendar, lists, notes. src/modules/index.ts loader. Root layout imports loader; AppNav reads registry for nav links. /debug/registry dumps full registry JSON (dev only). Uses zod v4 + built-in z.toJSONSchema(). tsc --noEmit, pnpm build, pnpm lint all clean.

  • 05 — Compose + Caddy. Dockerfile (3-stage: deps/builder/runner, pnpm fetch + offline install, non-root nextjs user, output: standalone), deploy/compose.yaml (famapp + famapp-db + full Authentik stack on famapp_net), deploy/Caddyfile.snippet, .env.production.example. docker build -t famapp . succeeds (~311 MB); docker compose -f deploy/compose.yaml config validates clean. Added .dockerignore and public/.gitkeep.

  • 06 — Authentik OIDC. next-auth@beta + @auth/drizzle-adapter wired up. src/lib/auth.ts configures OIDC provider (Authentik), database sessions, authorized callback guarding all routes except /login, /s/*, /api/auth/*. src/middleware.ts exports auth as middleware. src/app/api/auth/[...nextauth]/route.ts mounts the handlers. src/app/login/page.tsx has a single "Sign in with SSO" server action. getCurrentUser() available for server components/actions. Schema extended: users → added name/emailVerified/image; new accounts, sessions, verificationTokens tables; migration 0001_auth_tables.sql generated. deploy/authentik/README.md documents the manual Authentik bootstrap. tsc --noEmit and pnpm lint pass clean.

  • 07 — Household seed. pnpm db:seed inserts household "Home" idempotently (powered by tsx). signIn callback assigns owner to the first member of the household and member to all subsequent users. getCurrentSession() in src/lib/session.ts returns { user, household, role } and throws if unauthenticated or unmembered. /settings/household renders member list for all roles and a rename form for owner only. tsc --noEmit and pnpm lint both clean.

  • 08 — Theming infrastructure. CSS-variable multi-theme system (default + warm) × {light, dark, system}. users.theme + users.themeMode columns in migration 0002_naive_starbolt.sql. Theme registry in src/modules/_core/themes.ts (THEMES/THEME_MODES arrays — adding a third theme is one CSS block + one registry entry). Root layout reads session and sets data-theme/dark on <html> server-side; inline pre-paint <script> covers system mode and signed-out pages (no flash). useTheme() hook optimistically flips attributes, writes localStorage, and calls setUserTheme server action. <ThemePicker /> component mounts on /settings (select for theme, segmented buttons for mode). tsc --noEmit, pnpm lint, pnpm build all clean.

  • 10 — Calendar module. Added calendars and calendar_events schema + migration 0003_rainy_ravenous.sql, default Home/Personal calendar seeding, first-login default calendar creation, visibility-safe calendar/event queries, CRUD server actions, FullCalendar-backed /calendar UI with sidebar calendar management and event create/edit/delete/drag updates. Calendar manifest now registers share/reminder/search capabilities, two configurable widgets, and quick-add entries. Added Playwright happy-path spec in tests/e2e/calendar.spec.ts. pnpm db:generate, pnpm typecheck, pnpm lint, and pnpm build pass.

  • 11 — Lists module. Added lists and list_items schema + migration 0004_opposite_wraith.sql, default Shopping/Tasks seeding on first access/sign-in/seed, household-gated list and item CRUD server actions, reorder support, and Postgres LISTEN/NOTIFY to SSE bridge documented in ADR 0002. Added /lists grouped index, /lists/[id] keyboard-first item entry with checkbox toggles and swipe/delete, manifest entity/search/widget/quick-add registrations, and Playwright happy-path spec in tests/e2e/lists.spec.ts. pnpm typecheck, pnpm lint, and pnpm build pass.

  • 12 — Notes module. Added generic core reminders table plus household-scoped notes schema in migration 0006_new_hannibal_king.sql, notes CRUD server actions, reminder synchronization for notes.note, /notes index, /notes/new, /notes/[id] editor with safe markdown preview, manifest entity/search/reminder/share registration, notes.filtered widget registration, quick-add placeholder, and Playwright happy-path spec in tests/e2e/notes.spec.ts. pnpm typecheck, pnpm lint, pnpm build, and notes E2E pass.

  • 20 — Dashboard composition (single-dashboard MVP). Added default_dashboard_layout jsonb column to users + migration 0007_uneven_living_lightning.sql. Created src/modules/_core/manifest.tsx (core.activity placeholder widget) and registered it. Updated all three module manifests (calendar, lists, notes) with real async server component widget renders (data-fetching, empty states). Created src/lib/dashboard.ts (layout parsing + computeDefaultLayout greedy packer). Built src/app/page.tsx — 12-col CSS Grid, static smColSpan lookup for Tailwind class safety, per-widget <Suspense> for parallel loading, graceful skip for unknown widget IDs. pnpm typecheck, pnpm lint, pnpm build, and all 4 E2E specs pass.

  • 21 — Quick-add registry. Added url: string to QuickAddAction type (action is now optional). Added getQuickAdds() / SerializedQuickAddItem to registry (strips non-serializable action fn before crossing server→client boundary). Updated all three module manifests with navigation URLs. Built QuickAddProvider (context + cmd+k global shortcut), QuickAddFab (opens sheet, replaces plain button in dashboard), QuickAddSheet (bottom drawer / desktop popover grouped by module), and CommandPalette (cmdk-powered modal with arrow + enter + esc keyboard nav). Provider in root layout receives actions from getQuickAdds() at render time — adding a module's quickAdds automatically appears in both surfaces. Also added .claude/** to ESLint ignores to prevent stale worktree build artifacts from failing lint. pnpm typecheck, pnpm lint, pnpm build, and all 4 E2E specs pass.

  • 22 — Activity log. Added activity_log table to _core/schema.ts with index on (household_id, created_at desc). Migration 0008_activity_log.sql applied. logActivity() server function in _core/activity.ts reads current session and inserts a row. Added ActivityLogEntry type and optional renderActivity?(entry): string to EntityTypeRegistration in _core/module.ts. All three module manifests implement renderActivity for each entity type (human-readable, no hardcoded branches in the widget). Replaced core.activity widget stub with a real async server component that queries the last 20 rows via getEntityType(entry.entityType)?.renderActivity(entry). Wired logActivity() into every create/update/delete in calendar, lists, and notes server actions. Also added text to getAuthorizedItem select so toggle/delete log the item text. pnpm typecheck, pnpm lint, pnpm build, and all 4 E2E specs pass.

  • 30 — Share-link service. Added share_links table to _core/schema.ts + migration 0009_share_links.sql. Created _core/share.ts with createShareLink, resolveShareToken, revokeShareLink, and getActiveShareLinks. Token is 32 random bytes (URL-safe base64), stored as SHA-256 hash — raw token only returned at creation. createShareLink guards that the entity type is registered with canShare === true. resolveShareToken returns null for expired or revoked tokens. All three functions exported from _core/index.ts. /settings page gained a Share links card: lists active links (entity label, read/write capabilities, expiry) with a Revoke button per link (server action in settings/actions.ts). pnpm typecheck, pnpm lint, pnpm build, and all 4 E2E specs pass.

  • 25 — Multiple dashboards per user. Added dashboards table (migration 0012_dashboards.sql). Migrated each user's default_dashboard_layout into a "Home" dashboard row with is_default = true; dropped the interim column. Server actions: listDashboards, createDashboard, renameDashboard, deleteDashboard, setDefaultDashboard, reorderDashboards, saveDashboardLayout, resetDashboardLayout, resolveWidgetConfigOptions. / redirects to the user's default /d/<slug>. Dashboard switcher in AppNav renders tabs (active highlighted client-side) with a + button to create new dashboards and a kebab menu on the active tab for rename / set-default / delete. pnpm typecheck, pnpm lint, pnpm build pass.

  • 26 — Customizable layout + widget configuration. Installed react-grid-layout v2. Dashboard pages check ?edit=1 to enter edit mode, rendering a client DashboardEditor instead of the static grid. Editor uses react-grid-layout with gridConfig/dragConfig v2 API; each widget shell shows a drag handle, configure button (⚙), and remove button (🗑). WidgetPicker is a two-step modal: step 1 lists all registry widgets grouped by category; step 2 is a WidgetConfigurator auto-generated from the widget's default config — handles "all"|string[] multi-selects, booleans, numbers, and enums. resolveWidgetConfigOptions server action fetches dynamic options (calendars, lists). Save validates each config against its registered Zod schema. Reset to defaults calls computeDefaultLayout(). pnpm typecheck, pnpm build pass.

  • 31 — Public share viewer. Made actorId nullable in activity_log (migration 0010_nullable_actor_id.sql, onDelete: "set null") for anonymous share-page mutations. Added logShareActivity to _core/activity.ts (no session, explicit householdId). Added householdId to resolveShareToken return. Added renderSharedView to EntityTypeRegistration type. Each module implements loadForShare (bare DB queries, no session) and renderSharedView: calendar shows upcoming 90-day events or single-event details, lists shows items with optional toggle, notes shows title + body. toggleShareListItem server action lives in lists/server/share-actions.ts — validates token write capability, verifies item→list→household chain, logs share.toggle with actorId = null. /app/s/[token]/page.tsx resolves token, dispatches to loadForShare + renderSharedView, returns friendly error for invalid/expired tokens, sets noindex. Middleware /s/* exemption confirmed present. pnpm typecheck, pnpm lint, pnpm build, and all 4 E2E specs pass.

Next up

  • Next task in docs/tasks/.

Development login/testing notes

  • Local development can use the documented Dev login flow in docs/dev-login.md. It creates a database-backed Auth.js session for DEV_LOGIN_EMAIL when ENABLE_DEV_LOGIN=true and NODE_ENV !== "production".
  • The production cleanup gate is tracked in docs/tasks/09-production-dev-login-removal.md. Complete it before first production deployment.

Phase 1 remaining

  • 03 Drizzle + Postgres → 04 module loader → 05 compose/Caddy → 06 Authentik OIDC → 07 household seed → 08 theming infrastructure (newly added; multi-theme + per-user dark/light, must land before module work so module UIs adopt the token system from day one).

Recent architectural decisions

  • Calendars are first-class entities. Like lists, users can create as many as they want with private or household visibility. Both calendar entities are independently shareable. Updated CLAUDE.md data model + task 10.
  • Uniform widget contract — no singletons. Every dashboard widget declares a configSchema and defaultConfig; every placement is an independent instance. Same widget can appear N times on a dashboard pointed at different things. Updated task 04; tasks 11/12 widget sections updated to match.
  • Per-user customizable dashboards. Each user can have any number of named dashboards, switch between them, drag/resize widgets, and configure each placement. Tasks 25 (multiple dashboards) and 26 (customizable layout + widget configuration) added; phase 3 index updated.

How to resume in a fresh session

  1. Open the repo root in VS Code.
  2. Tell Codex or Claude Code: "Read AGENTS.md, CLAUDE.md, and STATUS.md, then complete the next task in docs/tasks/. Stop at the acceptance criteria."
  3. After it lands, append the result to the Done section here, bump Next up, and commit.

Environment notes

  • Repo: https://github.com/ginnoir/famapp (HTTPS remote on origin).
  • Local dev tooling installed: Node 22+, pnpm 10.33.3.
  • .env is not committed; copy .env.example.env when needed.
  • VS Code recommended extensions in .vscode/extensions.json; copy .vscode/settings.json.example.vscode/settings.json for the workspace defaults.
  • Memory files (cross-session, only seen by Claude): C:\Users\MattC\.claude\projects\C--Users-MattC-Documents-famapp\memory\.