Files
honeyDueWeb/docs/plans/2026-03-03-advanced-features.md
T
Trey t e2172c20f2 Rebrand from Casera/MyCrib to honeyDue
Total rebrand across Web project:
- Package name: casera-web -> honeydue-web
- Cookie: casera-token -> honeydue-token
- Theme store: casera-theme -> honeydue-theme
- File sharing: .casera -> .honeydue, component/function renames
- casera-file-handler.tsx -> honeydue-file-handler.tsx
- All UI text, metadata, OG tags updated
- Domains: casera.treytartt.com -> honeyDue.treytartt.com
- Demo data emails updated
- All documentation updated

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 06:33:59 -06:00

26 KiB

Phase 3 — Advanced Features Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Build sharing, subscriptions, notifications, profile settings, onboarding wizard, summary metrics with charts, and task report PDF download.

Architecture: A shared foundation layer (hooks, settings hub, notification bell) is built first, then 5 domain agents run concurrently on independent feature areas. Each agent produces pages and components that compile independently.

Tech Stack: Next.js 16 (App Router), TanStack Query v5, React Hook Form + Zod 4, shadcn/ui + Radix, Tailwind CSS 4, Recharts (new), Zustand, Lucide icons.


Execution Strategy: 7 Tasks, 5 Run in Parallel

Task 1: Shared Foundation (sequential — all features depend on this)
    ↓
Task 2: Sharing (residence + contractor)  ─┐
Task 3: Profile Settings                   ─┤
Task 4: Notifications + Subscription       ─┤  ← 5 parallel agents
Task 5: Onboarding Wizard                  ─┤
Task 6: Dashboard + Task Reports           ─┘
    ↓
Task 7: Integration + Verification (sequential — cross-feature wiring)

Verification gate: Each task ends with npm run build. No task is complete until it compiles clean.


Task 1: Shared Foundation

Files:

  • Create: src/lib/hooks/use-notifications.ts
  • Create: src/lib/hooks/use-subscription.ts
  • Create: src/components/notifications/notification-bell.tsx
  • Create: src/app/app/settings/layout.tsx
  • Modify: src/app/app/settings/page.tsx (overwrite stub → settings hub)
  • Modify: src/components/layout/top-bar.tsx (add notification bell)
  • Modify: src/lib/hooks/index.ts (add new hook exports)

Step 1: Install Recharts

cd /Users/treyt/Desktop/code/HoneyDue/honeyDueAPI-Web
npm install recharts

Step 2: Create notification hooks

// src/lib/hooks/use-notifications.ts
"use client";

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import * as notificationsApi from '@/lib/api/notifications';
import type { UpdatePreferencesRequest } from '@/lib/api/notifications';

export function useNotifications(limit?: number) {
  return useQuery({
    queryKey: ['notifications', limit],
    queryFn: () => notificationsApi.listNotifications(limit),
  });
}

export function useUnreadCount() {
  return useQuery({
    queryKey: ['notifications', 'unread-count'],
    queryFn: () => notificationsApi.getUnreadCount(),
    refetchInterval: 60_000, // Poll every minute
  });
}

export function useNotificationPreferences() {
  return useQuery({
    queryKey: ['notifications', 'preferences'],
    queryFn: () => notificationsApi.getPreferences(),
  });
}

export function useUpdatePreferences() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (data: UpdatePreferencesRequest) => notificationsApi.updatePreferences(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['notifications', 'preferences'] });
    },
  });
}

export function useMarkAsRead() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (id: number) => notificationsApi.markAsRead(id),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['notifications'] });
      queryClient.invalidateQueries({ queryKey: ['notifications', 'unread-count'] });
    },
  });
}

export function useMarkAllAsRead() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: () => notificationsApi.markAllAsRead(),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['notifications'] });
      queryClient.invalidateQueries({ queryKey: ['notifications', 'unread-count'] });
    },
  });
}

Step 3: Create subscription hooks

// src/lib/hooks/use-subscription.ts
"use client";

import { useQuery } from '@tanstack/react-query';
import * as subscriptionApi from '@/lib/api/subscription';

export function useSubscriptionStatus() {
  return useQuery({
    queryKey: ['subscription', 'status'],
    queryFn: () => subscriptionApi.getSubscriptionStatus(),
  });
}

export function useFeatureBenefits() {
  return useQuery({
    queryKey: ['subscription', 'features'],
    queryFn: () => subscriptionApi.getFeatureBenefits(),
    staleTime: Infinity,
  });
}

export function useUpgradeTriggers() {
  return useQuery({
    queryKey: ['subscription', 'upgrade-triggers'],
    queryFn: () => subscriptionApi.getAllUpgradeTriggers(),
    staleTime: Infinity,
  });
}

Step 4: Create NotificationBell component

// src/components/notifications/notification-bell.tsx
"use client";

import { useState } from "react";
import { Bell } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Badge } from "@/components/ui/badge";
import { useNotifications, useUnreadCount, useMarkAsRead, useMarkAllAsRead } from "@/lib/hooks/use-notifications";

export function NotificationBell() {
  const { data: unreadData } = useUnreadCount();
  const { data: notifData } = useNotifications(10);
  const markAsRead = useMarkAsRead();
  const markAllAsRead = useMarkAllAsRead();
  const unreadCount = unreadData?.unread_count ?? 0;

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="ghost" size="icon" className="relative">
          <Bell className="size-5" />
          {unreadCount > 0 && (
            <span className="absolute -top-1 -right-1 flex size-5 items-center justify-center rounded-full bg-destructive text-[10px] font-bold text-destructive-foreground">
              {unreadCount > 9 ? "9+" : unreadCount}
            </span>
          )}
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end" className="w-80 max-h-96 overflow-y-auto">
        <div className="flex items-center justify-between px-3 py-2">
          <p className="text-sm font-semibold">Notifications</p>
          {unreadCount > 0 && (
            <Button variant="ghost" size="sm" className="text-xs h-auto py-1" onClick={() => markAllAsRead.mutate()}>
              Mark all read
            </Button>
          )}
        </div>
        <DropdownMenuSeparator />
        {(!notifData || notifData.results.length === 0) ? (
          <div className="px-3 py-6 text-center text-sm text-muted-foreground">No notifications</div>
        ) : (
          notifData.results.map((n) => (
            <DropdownMenuItem key={n.id} className="flex-col items-start gap-1 py-2"
              onClick={() => { if (!n.is_read) markAsRead.mutate(n.id); }}>
              <p className={`text-sm ${n.is_read ? "text-muted-foreground" : "font-medium"}`}>{n.title}</p>
              <p className="text-xs text-muted-foreground">{n.body}</p>
              <p className="text-xs text-muted-foreground">{new Date(n.created_at).toLocaleDateString()}</p>
            </DropdownMenuItem>
          ))
        )}
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Step 5: Modify TopBar to include notification bell

In src/components/layout/top-bar.tsx, add <NotificationBell /> before the profile dropdown. Import from @/components/notifications/notification-bell.

Add a <div className="flex items-center gap-2"> wrapper around the bell and avatar dropdown.

Step 6: Create settings layout with sidebar navigation

// src/app/app/settings/layout.tsx
"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";
import { cn } from "@/lib/utils";
import { User, Bell, CreditCard } from "lucide-react";

const settingsNav = [
  { label: "Profile", href: "/app/settings/profile", icon: User },
  { label: "Notifications", href: "/app/settings/notifications", icon: Bell },
  { label: "Subscription", href: "/app/settings/subscription", icon: CreditCard },
];

export default function SettingsLayout({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();

  return (
    <div className="space-y-6">
      <h1 className="text-2xl font-bold tracking-tight">Settings</h1>
      <div className="flex flex-col sm:flex-row gap-6">
        <nav className="flex sm:flex-col gap-1 sm:w-48 shrink-0">
          {settingsNav.map((item) => (
            <Link key={item.href} href={item.href}
              className={cn(
                "flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors",
                "hover:bg-accent hover:text-accent-foreground",
                pathname === item.href ? "bg-accent text-accent-foreground" : "text-muted-foreground"
              )}>
              <item.icon className="size-4" />
              {item.label}
            </Link>
          ))}
        </nav>
        <div className="flex-1 min-w-0">{children}</div>
      </div>
    </div>
  );
}

Step 7: Overwrite settings page stub → redirect to profile

// src/app/app/settings/page.tsx
import { redirect } from "next/navigation";

export default function SettingsPage() {
  redirect("/app/settings/profile");
}

Step 8: Update hooks barrel export

Add to src/lib/hooks/index.ts:

export * from './use-notifications';
export * from './use-subscription';

Step 9: Verify build

cd /Users/treyt/Desktop/code/HoneyDue/honeyDueAPI-Web && npm run build

Step 10: Commit

git add -A && git commit -m "feat: add Phase 3 foundation — notification hooks, subscription hooks, settings layout, notification bell"

Task 2: Sharing (Residence + Contractor)

Depends on: Task 1

Files:

  • Create: src/app/app/residences/[id]/share/page.tsx
  • Create: src/app/app/residences/join/page.tsx
  • Create: src/components/sharing/share-code-display.tsx
  • Create: src/components/sharing/user-management.tsx
  • Create: src/components/sharing/honeydue-file-handler.tsx
  • Create: src/lib/hooks/use-sharing.ts
  • Modify: src/app/app/residences/[id]/page.tsx (add Share button)
  • Modify: src/app/app/contractors/[id]/page.tsx (add Share/Export button)
  • Modify: src/app/app/contractors/page.tsx (add Import button)

Residence Sharing Components

share-code-display.tsx — Displays share code with copy button and expiry countdown:

  • If no active code: "Generate Share Code" button → calls POST /residences/:id/generate-share-code/
  • If active code: displays code in large monospace text, copy-to-clipboard button, expiry timer
  • Uses existing residencesApi.generateShareCode() and residencesApi.getShareCode()

user-management.tsx — Lists residence users with remove action:

  • Uses residencesApi.getResidenceUsers(id) to fetch user list
  • Each user row: name, email, role badge (Owner/Member)
  • Owner can click "Remove" → ConfirmDialog → residencesApi.removeResidenceUser(residenceId, userId)

.honeydue File Handler

honeydue-file-handler.tsx — Reusable component for both residence and contractor sharing:

  • Export mode: Takes data object, generates JSON { type: "residence"|"contractor", ... }, triggers browser download as .honeydue file
  • Import mode: FileUpload drop zone accepting .honeydue files, reads JSON, validates type, calls callback with parsed data
  • Uses URL.createObjectURL + anchor click for download
  • Uses FileReader.readAsText for import

Pages

Residence Share page (/app/residences/[id]/share):

  • ShareCodeDisplay for generating/displaying share codes
  • .honeydue export button → downloads file with share code embedded
  • UserManagement table
  • Only accessible to residence owner

Residence Join page (/app/residences/join):

  • Text input for manual code entry
  • .honeydue file import drop zone
  • On submit: calls residencesApi.joinWithCode() → redirect to residence detail

Contractor Sharing

On contractor detail page, add "Share" button → generates .honeydue file with contractor data for download. On contractor list page, add "Import" button → opens import dialog → reads .honeydue file → creates contractor via API.

Query hooks

// src/lib/hooks/use-sharing.ts
"use client";

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import * as residencesApi from '@/lib/api/residences';

export function useShareCode(residenceId: number) {
  return useQuery({
    queryKey: ['residences', residenceId, 'share-code'],
    queryFn: () => residencesApi.getShareCode(residenceId),
    enabled: !!residenceId,
  });
}

export function useGenerateShareCode(residenceId: number) {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: () => residencesApi.generateShareCode(residenceId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['residences', residenceId, 'share-code'] });
    },
  });
}

export function useResidenceUsers(residenceId: number) {
  return useQuery({
    queryKey: ['residences', residenceId, 'users'],
    queryFn: () => residencesApi.getResidenceUsers(residenceId),
    enabled: !!residenceId,
  });
}

export function useRemoveResidenceUser(residenceId: number) {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (userId: number) => residencesApi.removeResidenceUser(residenceId, userId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['residences', residenceId, 'users'] });
    },
  });
}

export function useJoinResidence() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (code: string) => residencesApi.joinWithCode({ code }),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['residences'] });
    },
  });
}

Verify build

cd /Users/treyt/Desktop/code/HoneyDue/honeyDueAPI-Web && npm run build

Commit

git add -A && git commit -m "feat: add residence sharing (share code, join, user management) and contractor .honeydue export/import"

Task 3: Profile Settings

Depends on: Task 1

Files:

  • Create: src/app/app/settings/profile/page.tsx
  • Create: src/components/settings/profile-form.tsx
  • Create: src/components/settings/change-password-form.tsx
  • Create: src/components/settings/delete-account-section.tsx
  • Create: src/components/settings/theme-picker.tsx
  • Modify: src/lib/api/auth.ts (add changePassword and deleteAccount functions if missing)

API additions needed in auth.ts

Check if these exist, add if missing:

export async function changePassword(data: { current_password: string; new_password: string }): Promise<MessageResponse> {
  const res = await fetch('/api/proxy/auth/change-password/', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-Timezone': timezone() },
    body: JSON.stringify(data),
  });
  return handleResponse<MessageResponse>(res);
}

export async function deleteAccount(): Promise<MessageResponse> {
  const res = await fetch('/api/proxy/auth/delete-account/', {
    method: 'DELETE',
    headers: { 'Content-Type': 'application/json', 'X-Timezone': timezone() },
  });
  return handleResponse<MessageResponse>(res);
}

Components

profile-form.tsx — Edit first name, last name, email:

  • Zod schema: first_name (required), last_name (required), email (required, valid email)
  • Pre-fills from current user (from auth store)
  • On submit: calls authApi.updateProfile() → updates auth store user
  • Success toast / inline success message

change-password-form.tsx — Change password:

  • Zod schema: current_password (required, min 8), new_password (required, min 8), confirm_password (must match new_password)
  • On submit: calls authApi.changePassword() → clear form on success

delete-account-section.tsx — Danger zone:

  • Red bordered card with warning text
  • "Delete Account" button → ConfirmDialog with "Type DELETE to confirm" pattern
  • On confirm: calls authApi.deleteAccount() → logout → redirect to login

theme-picker.tsx — Visual theme selector:

  • Grid of 11 theme swatches (colored circles/squares from theme-config)
  • Click to select → applies theme via useTheme() hook from existing theme store
  • Dark/light toggle using existing theme store
  • Read theme config from src/lib/themes/theme-config.ts

Profile page

// src/app/app/settings/profile/page.tsx
"use client";

// 4 sections stacked vertically:
// 1. ProfileForm (personal info)
// 2. ChangePasswordForm
// 3. ThemePicker
// 4. DeleteAccountSection (danger zone at bottom)

Verify build

cd /Users/treyt/Desktop/code/HoneyDue/honeyDueAPI-Web && npm run build

Commit

git add -A && git commit -m "feat: add profile settings — edit info, change password, theme picker, delete account"

Task 4: Notifications + Subscription

Depends on: Task 1

Files:

  • Create: src/app/app/settings/notifications/page.tsx
  • Create: src/app/app/settings/subscription/page.tsx
  • Create: src/components/settings/notification-preferences.tsx
  • Create: src/components/settings/subscription-status.tsx
  • Create: src/components/settings/feature-comparison.tsx
  • Create: src/components/shared/upgrade-prompt.tsx

Notification Preferences

notification-preferences.tsx — Toggle switches per notification type:

  • Uses useNotificationPreferences() to load current state
  • Each preference row: label, description, toggle switch (shadcn Switch component — install via shadcn if not present)
  • On toggle: calls useUpdatePreferences() with changed field
  • Preference types: Task Reminders, Task Completions, Residence Updates, Share Notifications, Marketing

Notifications page (/app/settings/notifications):

  • NotificationPreferences component
  • Section header "Notification Preferences"
  • Each toggle saves immediately (optimistic update)

Subscription

subscription-status.tsx — Current plan display:

  • Uses useSubscriptionStatus() to load tier, limits, usage
  • Shows current tier name (Free/Premium)
  • Progress bars for usage: residences (X/max), tasks per residence (X/max), contractors (X/max), documents (X/max)
  • If free tier: show upgrade CTA section

feature-comparison.tsx — Free vs Premium comparison:

  • Uses useFeatureBenefits() to load feature list
  • Two-column comparison table: Free vs Premium
  • Check/cross icons per feature
  • "Upgrade on App Store" button linking to App Store

upgrade-prompt.tsx — Reusable upgrade prompt dialog:

  • Shown when user hits a tier limit (e.g., max residences reached)
  • Takes feature prop (what they're trying to do)
  • Shows limit info + upgrade CTA
  • Uses shadcn Dialog
  • Can be imported by any domain page that needs feature gating

Subscription page (/app/settings/subscription):

  • SubscriptionStatus card at top
  • FeatureComparison below
  • If premium: show expiry date, management info

Install Switch component

npx shadcn@latest add switch

Verify build

cd /Users/treyt/Desktop/code/HoneyDue/honeyDueAPI-Web && npm run build

Commit

git add -A && git commit -m "feat: add notification preferences and subscription status with feature comparison"

Task 5: Onboarding Wizard

Depends on: Task 1

Files:

  • Create: src/app/onboarding/page.tsx
  • Create: src/app/onboarding/layout.tsx
  • Create: src/components/onboarding/welcome-step.tsx
  • Create: src/components/onboarding/choose-path-step.tsx
  • Create: src/components/onboarding/create-residence-step.tsx
  • Create: src/components/onboarding/first-task-step.tsx
  • Create: src/components/onboarding/join-residence-step.tsx
  • Create: src/components/onboarding/complete-step.tsx
  • Create: src/stores/onboarding.ts

Onboarding Zustand Store

// src/stores/onboarding.ts
import { create } from "zustand";

interface OnboardingState {
  currentStep: number;
  path: "create" | "join" | null;
  residenceId: number | null;
  isComplete: boolean;
  nextStep: () => void;
  prevStep: () => void;
  setPath: (path: "create" | "join") => void;
  setResidenceId: (id: number) => void;
  complete: () => void;
  reset: () => void;
}

export const useOnboardingStore = create<OnboardingState>()((set) => ({
  currentStep: 0,
  path: null,
  residenceId: null,
  isComplete: false,
  nextStep: () => set((s) => ({ currentStep: s.currentStep + 1 })),
  prevStep: () => set((s) => ({ currentStep: Math.max(0, s.currentStep - 1) })),
  setPath: (path) => set({ path, currentStep: 2 }),
  setResidenceId: (id) => set({ residenceId: id }),
  complete: () => set({ isComplete: true }),
  reset: () => set({ currentStep: 0, path: null, residenceId: null, isComplete: false }),
}));

Onboarding Layout

  • Clean, centered layout (no sidebar or app shell)
  • Progress indicator (step dots or progress bar)
  • honeyDue logo at top

Steps

Step 0: Welcome — "Welcome to honeyDue!" message, illustration, "Get Started" button

Step 1: Choose Path — Two cards: "Create a new residence" (House icon) and "Join an existing residence" (Users icon). Clicking sets path and advances.

Path A — Create:

  • Step 2a: Create Residence — Simplified ResidenceForm (name, address, type only). On submit: creates residence via useCreateResidence(), stores ID, advances.
  • Step 3a: First Task (optional) — "Add your first task?" Quick task form (title + due date only) or "Skip" button. On submit: creates task, advances. On skip: advances.
  • Step 4a: Complete — "You're all set!" Redirect to /app/residences/${id}

Path B — Join:

  • Step 2b: Join Residence — Code input (6-char) or .honeydue file import. On submit: joins via API, advances.
  • Step 3b: Complete — "Welcome to the residence!" Redirect to residence detail.

Onboarding trigger

After login/register, check if user has 0 residences → redirect to /onboarding. This is done via middleware or the auth store's login flow. For now, just mark localStorage.setItem('onboarding_complete', 'true') after completion, and check in the app layout.

Verify build

cd /Users/treyt/Desktop/code/HoneyDue/honeyDueAPI-Web && npm run build

Commit

git add -A && git commit -m "feat: add onboarding wizard with create/join paths"

Task 6: Enhanced Dashboard + Task Reports

Depends on: Task 1

Files:

  • Modify: src/app/app/page.tsx (enhance existing dashboard)
  • Create: src/components/dashboard/task-completion-chart.tsx
  • Create: src/components/dashboard/recent-activity.tsx
  • Create: src/components/dashboard/stats-cards.tsx
  • Modify: src/app/app/residences/[id]/page.tsx (add "Download Report" button)

Enhanced Dashboard

Replace the current simple dashboard with the spec's design:

stats-cards.tsx — Top row of stat cards:

  • 4 cards: Overdue (red), Due Today (orange), Active (blue), Completed (green)
  • Data from useTasks() kanban response total_summary field
  • Each card: count, label, colored icon

task-completion-chart.tsx — Area chart using Recharts:

  • Uses task completion data (from completions API or derived from kanban data)
  • Shows last 30 days of completions as an area chart
  • <ResponsiveContainer> + <AreaChart> + <Area> from recharts
  • Styled with theme colors via CSS variables
  • If no completion data available, show "No completion data yet" empty state

recent-activity.tsx — Recent activity feed:

  • Uses useNotifications(5) to show last 5 notifications as activity items
  • Each item: icon, title, time ago (relative)
  • "View all" link to settings/notifications
  • If no notifications: "No recent activity"

Dashboard page structure

// src/app/app/page.tsx (enhanced)
// 1. Welcome message with user name (from auth store)
// 2. StatsCards (4-column grid)
// 3. TaskCompletionChart (full width card)
// 4. RecentActivity (list)

Task Report PDF

On residence detail page (src/app/app/residences/[id]/page.tsx), add a "Download Report" button:

  • Calls residencesApi.generateTasksReport(residenceId)
  • The API generates a PDF and emails it (existing behavior)
  • Show success message "Report sent to your email"
  • Alternative: if API returns a PDF URL, trigger browser download

Verify build

cd /Users/treyt/Desktop/code/HoneyDue/honeyDueAPI-Web && npm run build

Commit

git add -A && git commit -m "feat: add enhanced dashboard with charts and task report PDF download"

Task 7: Integration + Verification

Depends on: Tasks 2-6

Step 1: Full build verification

cd /Users/treyt/Desktop/code/HoneyDue/honeyDueAPI-Web && npm run build

Step 2: Route verification

Confirm all new routes compile:

  • /app/settings/profile
  • /app/settings/notifications
  • /app/settings/subscription
  • /app/residences/[id]/share
  • /app/residences/join
  • /onboarding

Step 3: Cross-feature integration checks

  • TopBar notification bell renders and shows unread count
  • Settings hub sidebar navigates between profile/notifications/subscription
  • Residence detail has Share and Download Report buttons
  • Contractor detail has Share button, list has Import button
  • Dashboard shows stats, chart, and recent activity

Step 4: Commit

git add -A && git commit -m "feat: complete Phase 3 — advanced features integration verified"

Deliverables Checklist

At the end of Phase 3, verify:

  • Residence sharing: generate share code, copy, join with code, manage users (list/remove)
  • Contractor sharing: .honeydue file export from detail, import on list page
  • .honeydue files: download as JSON, import via drag-and-drop
  • Subscription: status display with usage bars, feature comparison table, upgrade CTA
  • Feature gating: upgrade prompt dialog available for tier limit enforcement
  • Notification preferences: toggle switches per notification type, saves immediately
  • In-app notifications: bell icon in TopBar with unread count badge, dropdown with recent notifications
  • Profile: edit name/email form, change password form, delete account with confirmation
  • Theme picker: 11 theme swatches + dark/light toggle in profile settings
  • Onboarding: multi-step wizard with create/join paths, stores completion state
  • Dashboard: enhanced with stats cards, completion trend chart (Recharts), recent activity feed
  • Task reports: "Download Report" button on residence detail, triggers PDF generation
  • Settings hub: sidebar navigation between profile, notifications, subscription
  • Build passes: npm run build exits 0