cba44 607800e77f implement spec 03b — custom frequency, time-of-day, and dropdown time picker
Adds days_of_week/interval_days/due_time to recurring rules and due_time
to tasks (backend validation, generation, and PocketBase schema), surfaces
time-of-day on routine/task forms and task cards, and replaces the native
<input type="time"> with a shadcn-style TimePicker dropdown component.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 20:36:04 +02:00
2026-06-06 10:35:03 +02:00

Adaptive Household Support App

A cooperative household management platform for couples. Focuses on emotional fairness, adaptive workload balancing based on self-declared capacity states, light gamification, and real-time collaboration.

Features

Authentication

  • Register, log in, and log out with email and password
  • Sessions stored as httpOnly cookies — no tokens exposed to client JavaScript
  • Password reset by email
  • Protected routes redirect unauthenticated users to login automatically

Households

  • Create a named household on first login
  • Choose the household's planning day (the day of the week you review and plan) and timezone at creation
  • Invite a partner via a shareable invite code
  • Join an existing household by entering the code
  • View all household members with avatars and roles
  • Regenerate the invite code at any time
  • Owner can update planning day and timezone from household settings
  • Leave a household

Tasks

  • Weekly task board grouped by day, anchored to the household's planning day
  • Navigate to any past or future week with Prev/Next controls — catch up on and complete tasks from weeks you forgot to check off
  • Create one-off tasks with a title, optional description (up to 500 chars), due date, and optional assignee
  • Overdue pending tasks are highlighted with a red border and "Overdue" badge
  • Mark tasks complete or skip them (skip requires a confirmation tap)
  • Late completions (finished after the due date) are flagged in the completion record
  • View and edit a task's full detail page including description
  • Real-time updates — task changes made by either partner appear instantly without a page refresh
  • Dashboard task widget — today's tasks at a glance, last on the dashboard (after the positive/social widgets, so the page doesn't open with a wall of chores). Overdue tasks are collapsed behind a "Show N overdue" toggle rather than shown by default

Routines

  • Define recurring routines (daily, weekly, bi-weekly, or monthly)
  • Set the day of the week and an estimated duration per routine
  • Assign a routine to a specific household member or leave it unassigned
  • Edit or deactivate routines at any time; inactive routines stay visible and can be reactivated
  • Tasks are auto-generated from active routines each night by a background job — the household's planning day controls which night tasks are generated for the upcoming week
  • Opening the planner auto-generates tasks for the week if none exist yet

Weekly Planner

  • Plan the upcoming week together on the household's chosen planning day
  • The week always runs from the day after the planning day through the planning day itself (e.g. Friday planning → Sat Fri week)
  • Session starts as a draft — assign tasks to yourself or your partner using dropdowns
  • Summary bar shows how many tasks are mine, partner's, or unassigned at a glance
  • Add shared notes for the week
  • Confirm the session to lock in assignments and mark the week as planned
  • Real-time sync — partner's assignment changes and confirmations appear instantly
  • Browse past planning sessions and their confirmed status

Capacity States

  • Declare a reduced-capacity label for the week (lower capacity, busy week, exam period, in recovery, health day, or high stress)
  • Set a private reason only you can see — your partner sees the label only, never the reason
  • Choose a duration from 17 days; the state expires automatically overnight
  • Dashboard shows your active state with an "End early" button
  • Dashboard shows your partner's label and days remaining (no reason shown)
  • Both partners can have a state simultaneously — reassignment suggestions are paused when both are in low capacity
  • Real-time updates — partner's state appears on your dashboard instantly without a page refresh

Adaptive Balancing

  • When your partner declares a capacity state, a suggestions banner appears on your dashboard listing pending tasks due during their low-capacity window
  • Choose Take all to accept every suggestion, Choose to pick individual tasks via checkboxes, or Maybe later to dismiss for the session
  • Accepting reassigns those tasks to you immediately; your task list updates in real time via PocketBase realtime
  • The banner appears automatically when your partner activates a state — no page reload needed
  • Suggestions are scoped to tasks due today through the partner's state end date — only tasks you can actually act on appear
  • Tasks already assigned to you are excluded from suggestions

Support Mode

  • Either partner can activate support mode to signal they are actively supporting the other — for yourself or your partner
  • Dashboard shows a shared yellow banner on both screens the moment support mode is activated: "You're supporting Alex" / "Alex is supporting you" / self-support message
  • Choose a duration: 1 day, 3 days, 5 days, or 1 week — the event expires automatically at midnight after the last covered day
  • Either partner can end support mode early with the End early button; the banner disappears for both in real time
  • Only one support event can be active per household at a time; activating a new one replaces the current
  • Each event stores a support multiplier (1.5×) for the future Rewards engine

Rewards

  • Tasks carry a variable point value based on effort: Quick = 5 pts, Normal = 10 pts (default), Big = 20 pts — set per task or routine at creation
  • When support mode is active, the supporting partner earns 1.5× the task's point value (rounded); the supported partner earns base points
  • Create your own custom milestone rewards with a name and point cost — anything meaningful to your household
  • Either partner can create, edit (inline), or delete (2-click confirm) milestones directly from the rewards widget
  • When the household has enough points, a Claim button appears — claiming deducts the cost; the remainder carries over toward the next goal
  • Milestones are not one-time unlocks — they cycle and can be claimed repeatedly
  • Task completion shows a +N pts toast and inline badge so you always know what you earned
  • Redemption history shows the last 20 claims, recording who claimed what and when even if the milestone is later deleted
  • A streak counter tracks how many consecutive planning weeks you've confirmed together — visible as a 🔥 badge on the rewards widget
  • Real-time updates — partner's claims and point changes appear on your screen instantly

Mascot

  • A cat mascot lives on the dashboard and reacts to the household's current state with contextual messages
  • Role-aware support messages: the supporting partner sees "You're covering for {name} this week. Earning 1.5× pts! 💪"; the supported partner sees "Your partner has your back this week. Keep going! 💛"
  • Support and streak coexist: when both support mode and a streak ≥ 3 are active, both messages appear on separate lines simultaneously
  • Other message priorities: long streak ≥ 7 weeks (exclusive) → milestone just claimed (5 seconds, exclusive) → support/streak combination → default
  • "Milestone just claimed" message shows for 5 seconds then reverts to the next applicable messages

App Navigation

  • Every authenticated page shows a persistent nav bar with the household name, links, user badge, and log out
  • Household name displays to the left of nav links, truncated with a tooltip if too long
  • Nav links: Dashboard · Tasks · Planner · Routines · Settings — active link highlighted; Settings stays active for all /settings/* sub-routes
  • User badge: circular initials badge (up to 2 chars) next to the user's display name — truncated with a tooltip if long
  • Log out — ends the session, clears client state, and redirects to login; shows a toast if the request fails
  • Settings sub-navigation — all settings pages share a secondary nav listing Household and Capacity sections

Triggering routine tasks manually

To generate tasks immediately for your household without waiting for the nightly cron:

curl -X POST http://localhost:4000/recurring-rules/generate \
  -H "Cookie: auth_token=<your-token>"
# → {"generated": 3}

The response includes a count of newly created tasks. Already-existing tasks for the same date are never duplicated.

Tech Stack

Layer Technology
Frontend Next.js 14 (App Router) + shadcn/ui + Tailwind CSS v4
Client State Zustand
Form Validation react-hook-form + zod
Backend API NestJS + @nestjs/schedule (cron jobs)
Auth / DB / Realtime PocketBase
Realtime Client pocketbase npm package (direct browser → PocketBase subscriptions)
Toast Notifications sonner (via shadcn)
File Storage Backblaze B2
Push Notifications Firebase Cloud Messaging
Deployment Docker Compose

Getting Started

Prerequisites

  • Node.js 24 LTS
  • pnpm 11+
  • Docker + Docker Compose
  • curl and jq (for the PocketBase schema import script)

Development (Docker Compose)

# Install dependencies
pnpm install

# Copy and fill in the dev env file
cp infra/.env.dev.example infra/.env.dev
# Edit infra/.env.dev — set PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD, and PUBLIC_* URLs

# Start all services (run from infra/)
cd infra && docker compose -f docker-compose.dev.yml up

# Frontend   → http://localhost:3000  (or http://<host-ip>:3000)
# Backend    → http://localhost:4000  (or http://<host-ip>:4000)
# PocketBase → http://localhost:8090/_/  (admin UI)

Production (Docker Compose)

# Copy and fill in the prod env file
cp infra/.env.example infra/.env
# Edit infra/.env — set credentials, FRONTEND_URL, PUBLIC_* URLs

# Build from source and start (run from infra/)
cd infra && docker compose -f docker-compose.build.yml up -d

# Or pull pre-built images (if published to a registry)
cd infra && docker compose -f docker-compose.yml up -d

Fixed IP overlay (PocketBase superuser whitelist)

PocketBase supports a superuser IP whitelist. The NestJS backend authenticates to PocketBase as a superuser directly over the Docker network, so its container IP must be whitelisted alongside your own IP. Use the fixed-IP overlay to give the backend a stable, predictable IP (172.20.0.10):

# Run from infra/
docker compose -f docker-compose.build.yml -f docker-compose.fixed-ip.yml up -d

Then add 172.20.0.10 to PocketBase Dashboard → Settings → Application → Superuser IPs.

Running Without Docker

Step 1 — PocketBase

PocketBase is an external dependency. Choose one option:

Option A — Docker (recommended):

cd infra && docker compose -f docker-compose.dev.yml up pocketbase
# Admin UI → http://localhost:8090/_/

Option B — Existing PocketBase instance:

Set POCKETBASE_URL in backend/.env to point to your running instance:

POCKETBASE_URL=http://<your-pocketbase-host>:8090

Step 2 — Environment files

backend/.env — copy from backend/.env.example and fill in:

PORT=4000
POCKETBASE_URL=http://localhost:8090
FRONTEND_URL=http://localhost:3000
PB_ADMIN_EMAIL=<your-pocketbase-admin-email>
PB_ADMIN_PASSWORD=<your-pocketbase-admin-password>
ENABLE_DEV_TOOLS=true

frontend/.env.local — copy from frontend/.env.local.example:

NEXT_PUBLIC_API_URL=http://localhost:4000
NEXT_PUBLIC_PB_REALTIME_URL=http://localhost:8090
API_URL=http://localhost:4000

Note on realtime URL: The frontend subscribes to PocketBase directly for real-time updates. This URL must be reachable from the browser — use your host's IP address (e.g. http://192.168.1.x:8090) when accessing the app from other devices on the same network.

Step 3 — Backend and Frontend

Run each in a separate terminal:

# Terminal 1 — Backend (loads backend/.env automatically)
pnpm dev:backend
# → http://localhost:4000

# Terminal 2 — Frontend
pnpm dev:frontend
# → http://localhost:3000

First-time PocketBase setup

Do this once after PocketBase starts for the first time (or after wiping its data).

Step 1 — Import the collection schema

Run the helper script from the repo root (requires curl and jq):

# PocketBase running locally (default: http://localhost:8090)
./scripts/import-pb-schema.sh

# PocketBase on a different host
./scripts/import-pb-schema.sh http://<host-ip>:8090

The script reads PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD from your environment or auto-loads them from infra/.env.dev. It merges the schema without deleting any existing collections — superusers and the built-in auth collection are preserved.

Step 2 — Set the users collection view rule

PocketBase excludes its built-in auth collections from schema import, so this one step must be done manually in the admin UI:

  1. Open the PocketBase admin UI (e.g. http://localhost:8090/_/)
  2. Go to Collections → users → Edit → API Rules
  3. Set View rule to: @request.auth.id != ""
  4. Save

This allows authenticated users to read each other's name and avatar, which is required for household member listing.


Running Tests

# Backend unit tests
pnpm test:backend

# Backend e2e tests
pnpm test:backend:e2e

# Frontend component tests
pnpm test:frontend

Project Structure

household-management/
├── frontend/          # Next.js App Router application
│   ├── app/(auth)/   # Login, register, forgot-password pages
│   ├── app/(app)/    # Protected routes (dashboard, tasks, routines, settings)
│   ├── components/ui/ # shadcn/ui components
│   ├── lib/           # api.ts, pocketbase.ts, validations/
│   ├── stores/        # Zustand stores (household, tasks, capacity, support, rewards)
│   └── middleware.ts  # Auth redirect logic
├── backend/           # NestJS application
│   └── src/
│       ├── auth/      # Auth module + JwtAuthGuard + /auth/me + /auth/pb-token
│       ├── households/ # Household CRUD, invite/join
│       ├── tasks/     # Tasks, recurring rules, recurring engine (cron)
│       ├── planner/   # Weekly planning sessions
│       ├── capacity/  # Capacity states, cron expiry, engine stub
│       ├── support/   # Support mode activation, banner, cron expiry
│       ├── rewards/   # Points, milestones, claims, redemption history, streak tracking
│       └── pocketbase/ # PocketBase SDK wrapper
├── pocketbase/        # PocketBase binary and data
├── infra/             # Docker Compose config + .env.example
├── docs/              # Architecture and product spec
└── specs/             # Feature specs (spec-driven development)

Documentation

  • Architecture — system design, tech stack, layer responsibilities
  • Product Spec — features, user flows, development iterations

Specs (Iteration 1)

Spec Feature Status
00 - Project Setup Monorepo, tooling, Docker Compose ✓ Done
01 - Auth Registration, login, session management ✓ Done
02 - Households Household creation, invite/join ✓ Done
03 - Recurring Tasks Routines, task generation, completions ✓ Done
04 - Weekly Planner Weekly planning workflow ✓ Done
04b - Iteration 1 Addendum Overdue tasks, descriptions, planning day, auto-generate ✓ Done

Specs (Iteration 2)

Spec Feature Status
05 - Capacity States Declare capacity, privacy-filtered partner view ✓ Done
06 - Adaptive Balancing Reassignment suggestions when partner has low capacity ✓ Done
07 - Support Mode Explicit support activation, shared banner, reward multiplier ✓ Done

Specs (Iteration 3)

Spec Feature Status
09 - Rewards Engine Points per task completion, support multiplier, milestone rewards ✓ Done
10 - Streaks Consecutive confirmed planning weeks, streak counter ✓ Done
11 - Mascot Cat mascot with contextual messages based on household state ✓ Done

Specs (Enhancements)

Spec Feature Status
08 - App Navigation Shell Household name, user display, logout in nav bar ✓ Done

Specs (Iteration 4 — Usability Fixes)

Prioritized queue from user testing feedback. Implemented one at a time via feat/spec-NN-slug branches, in table order.

Spec Feature Status
13 - Tasks Page Week Navigation Prev/Next week navigation on the tasks board; catch up on tasks from past weeks ✓ Done
03b - Recurring Frequency and Time Multi-day weekly routines, "every N days" interval, time-of-day on tasks/routines
04c - Planner Effort Breakdown Per-person estimated-minutes rollup in the planner summary
12 - Dashboard Task List Today + overdue task list widget on the dashboard ✓ Done
14 - Mobile-Responsive Layout Hamburger nav drawer below 640px, spot-check dense rows at phone width
03c - Multi-User Task Assignment Assign a task to both partners — scoped now, deferred (touches capacity/rewards assumptions, has open questions) Scoped, not queued
S
Description
No description provided
Readme 643 KiB
Languages
TypeScript 98.4%
CSS 0.5%
Dockerfile 0.5%
Shell 0.4%
JavaScript 0.2%