# Phase 4 — Demo Mode

Build a fully sandboxed demo experience with realistic mock data and no backend dependency.

## Checklist

- [ ] Mock data seeds (2 residences, 15 tasks across columns, 5 contractors, 8 documents)
- [ ] DemoDataProvider implementing same interface as real API client
- [ ] Session-scoped Zustand store (resets on close)
- [ ] All CRUD operations work against in-memory store
- [ ] Demo banner + "Sign Up" CTA
- [ ] Demo landing page with feature preview

---

## Architecture

```
User visits /demo
  → Server creates a session ID (cookie, no account needed)
  → App loads with pre-built mock data (realistic residences, tasks, contractors, docs)
  → All CRUD operations work against an in-memory store scoped to that session
  → No backend API calls — everything is client-side
  → Store resets on session expiry (or browser close)
  → Banner: "You're in demo mode — Sign up to save your data"
  → "Sign Up" converts session → real account (data NOT migrated, clean start)
```

### Context Switch

A `DataProvider` context determines whether the app uses real API calls or demo data:

```typescript
// src/lib/demo/demo-context.tsx
interface DataProvider {
  residences: {
    list: () => Promise<Residence[]>;
    get: (id: number) => Promise<ResidenceDetail>;
    create: (data: CreateResidenceRequest) => Promise<Residence>;
    update: (id: number, data: UpdateResidenceRequest) => Promise<Residence>;
    delete: (id: number) => Promise<void>;
  };
  tasks: { /* same pattern */ };
  contractors: { /* same pattern */ };
  documents: { /* same pattern */ };
}

const DataProviderContext = createContext<DataProvider>(realApiProvider);

export function DemoProvider({ children }: { children: ReactNode }) {
  const demoStore = useDemoStore();
  const demoProvider = createDemoProvider(demoStore);

  return (
    <DataProviderContext.Provider value={demoProvider}>
      <DemoBanner />
      {children}
    </DataProviderContext.Provider>
  );
}

export function useDataProvider() {
  return useContext(DataProviderContext);
}
```

### Layout Integration

```
src/app/(demo)/layout.tsx     → wraps children in <DemoProvider>
src/app/(app)/layout.tsx      → wraps children in <RealProvider> (or just uses default)
```

Both route groups share the same page components — the data provider abstraction means zero code duplication.

---

## 1. Mock Data Seeds

### Residences (2)

```typescript
const mockResidences = [
  {
    id: 1,
    name: "Maple Street House",
    address: "142 Maple Street, Austin, TX",
    residenceType: "House",
    taskSummary: { overdue: 2, dueToday: 1, dueSoon: 3, upcoming: 5, completed: 8 },
    contractorCount: 3,
    documentCount: 5,
  },
  {
    id: 2,
    name: "Downtown Apartment",
    address: "800 Congress Ave #412, Austin, TX",
    residenceType: "Apartment",
    taskSummary: { overdue: 0, dueToday: 1, dueSoon: 2, upcoming: 3, completed: 4 },
    contractorCount: 2,
    documentCount: 3,
  },
];
```

### Tasks (15 across columns)

Realistic tasks distributed across kanban columns:

| Column | Count | Examples |
|--------|-------|---------|
| Overdue | 2 | "Replace smoke detector batteries" (due 2 weeks ago), "Clean gutters" (due 1 month ago) |
| Due Today | 2 | "HVAC filter replacement", "Test garage door opener" |
| Due Soon | 3 | "Power wash driveway" (5 days), "Inspect roof" (2 weeks), "Service water heater" (3 weeks) |
| Upcoming | 4 | Various maintenance tasks due 1-3 months out |
| In Progress | 1 | "Paint master bedroom" (in progress since yesterday) |
| Completed | 3 | "Fix kitchen faucet leak", "Install new doorbell", "Reseal bathroom grout" |

Each task has realistic:
- Categories (Maintenance, Cleaning, Repair, etc.)
- Priorities (High, Medium, Low)
- Estimated costs ($25 - $500)
- Some assigned to contractors
- Completed tasks have completion records with notes

### Contractors (5)

```typescript
const mockContractors = [
  { id: 1, name: "Bob Martinez", company: "Bob's Plumbing", phone: "(512) 555-0101", email: "bob@bobsplumbing.com", specialty: "Plumbing", isFavorite: true },
  { id: 2, name: "Sarah Chen", company: "Chen Electric", phone: "(512) 555-0202", email: "sarah@chenelectric.com", specialty: "Electrical", isFavorite: true },
  { id: 3, name: "Mike Johnson", company: null, phone: "(512) 555-0303", email: null, specialty: "General Handyman", isFavorite: false },
  { id: 4, name: "Lisa Park", company: "Park Landscaping", phone: "(512) 555-0404", email: "lisa@parklandscaping.com", specialty: "Landscaping", isFavorite: false },
  { id: 5, name: "James Wilson", company: "Wilson HVAC", phone: "(512) 555-0505", email: "james@wilsonhvac.com", specialty: "HVAC", isFavorite: true },
];
```

### Documents (8)

Mix of warranties and general documents:

| Type | Count | Examples |
|------|-------|---------|
| Warranty | 4 | "Samsung Refrigerator Warranty" (expires 2027), "Roof Warranty" (expires 2035), "HVAC System Warranty" (expired 2024), "Dishwasher Warranty" (expires 2026) |
| Document | 4 | "Home Insurance Policy", "Property Tax Statement 2025", "HOA Agreement", "Electrical Inspection Certificate" |

### Lookups

Pre-loaded categories, priorities, frequencies, specialties — same as production static data.

---

## 2. DemoDataProvider

```typescript
// src/lib/demo/demo-provider.ts
export function createDemoProvider(store: DemoStore): DataProvider {
  return {
    residences: {
      list: async () => store.getResidences(),
      get: async (id) => store.getResidence(id),
      create: async (data) => store.createResidence(data),
      update: async (id, data) => store.updateResidence(id, data),
      delete: async (id) => store.deleteResidence(id),
    },
    tasks: {
      list: async () => store.getTasks(),
      create: async (data) => store.createTask(data),
      complete: async (id, data) => store.completeTask(id, data),
      // ... all task actions
    },
    contractors: { /* ... */ },
    documents: { /* ... */ },
  };
}
```

All operations are synchronous under the hood (wrapped in async for interface compatibility). They:
1. Validate input (same Zod schemas as real forms)
2. Generate auto-increment IDs
3. Update the Zustand store
4. Return the created/updated object

---

## 3. Session-Scoped Zustand Store

```typescript
// src/lib/demo/demo-store.ts
interface DemoStore {
  // Data
  residences: Residence[];
  tasks: Task[];
  contractors: Contractor[];
  documents: Document[];
  completions: TaskCompletion[];
  lookups: StaticData;

  // Auto-increment IDs
  nextIds: { residence: number; task: number; contractor: number; document: number };

  // CRUD operations
  createResidence: (data: CreateResidenceRequest) => Residence;
  updateResidence: (id: number, data: UpdateResidenceRequest) => Residence;
  deleteResidence: (id: number) => void;
  // ... same for all domains

  // Task-specific
  completeTask: (id: number, data: TaskCompletionRequest) => Task;
  markInProgress: (id: number) => Task;
  cancelTask: (id: number) => Task;
  archiveTask: (id: number) => Task;

  // Reset
  reset: () => void;
}

export const useDemoStore = create<DemoStore>((set, get) => ({
  residences: mockResidences,
  tasks: mockTasks,
  contractors: mockContractors,
  documents: mockDocuments,
  completions: mockCompletions,
  lookups: mockLookups,
  nextIds: { residence: 3, task: 16, contractor: 6, document: 9 },

  createResidence: (data) => {
    const id = get().nextIds.residence;
    const residence = { id, ...data, createdAt: new Date().toISOString() };
    set((state) => ({
      residences: [...state.residences, residence],
      nextIds: { ...state.nextIds, residence: id + 1 },
    }));
    return residence;
  },

  // ... other operations

  reset: () => set({
    residences: mockResidences,
    tasks: mockTasks,
    contractors: mockContractors,
    documents: mockDocuments,
    completions: mockCompletions,
    nextIds: { residence: 3, task: 16, contractor: 6, document: 9 },
  }),
}));
```

### Session Isolation

- Store is created fresh when `/demo` is accessed
- No `persist` middleware — store is pure in-memory
- Closing the browser tab or navigating away clears the store
- Each visitor gets their own Zustand store instance

---

## 4. Demo Banner

Persistent banner at the top of the demo app:

```
┌─────────────────────────────────────────────────────────────────┐
│ 🎯 You're exploring honeyDue in demo mode.  [Sign Up Free]      │
│    Changes aren't saved. Create an account to get started!     │
└─────────────────────────────────────────────────────────────────┘
```

- Fixed at top, doesn't scroll
- "Sign Up Free" links to `/register`
- Subtle, non-intrusive (light background, small text)
- Can be temporarily dismissed (per session only)

---

## 5. Demo Landing Page

### Route: `/demo`

Before entering the demo, show a brief preview page:

```
┌─────────────────────────────────────────────────────┐
│                                                     │
│        🏠 Try honeyDue — No Account Needed            │
│                                                     │
│   Manage your home maintenance, track tasks,        │
│   organize contractors, and store documents.         │
│                                                     │
│   [Start Demo →]                                    │
│                                                     │
│   ────────────────────────────────────────           │
│                                                     │
│   Preview:                                          │
│   [Screenshot of kanban board]                      │
│   [Screenshot of residence detail]                  │
│                                                     │
│   Already have an account?  [Log In]                │
│                                                     │
└─────────────────────────────────────────────────────┘
```

Clicking "Start Demo" initializes the demo store and redirects to `/demo/residences`.

---

## 6. TanStack Query Integration

In demo mode, TanStack Query hooks still work — they just call the demo provider instead of real API:

```typescript
// src/lib/hooks/useResidences.ts
export function useResidences() {
  const provider = useDataProvider();

  return useQuery({
    queryKey: ['residences'],
    queryFn: () => provider.residences.list(),
    staleTime: Infinity, // Demo data never goes stale
  });
}
```

Mutations also work through the provider:

```typescript
export function useCreateResidence() {
  const provider = useDataProvider();
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: CreateResidenceRequest) => provider.residences.create(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['residences'] });
    },
  });
}
```

---

## Key Design Decisions

| Decision | Rationale |
|----------|-----------|
| No backend calls in demo | Zero infrastructure cost, no rate limiting, instant responses, no auth needed |
| Session-scoped (no persistence) | Prevents demo data from accumulating, ensures fresh experience each visit |
| Same components as real app | Zero code duplication, demo accurately represents the product |
| Provider pattern (not if/else) | Clean separation, easy to test, no demo logic leaking into components |
| Data NOT migrated on signup | Avoids edge cases with ID conflicts, mock data quality issues |

## Deliverables

At the end of Phase 4, you should have:
1. Realistic mock data covering all 4 domains
2. Fully functional CRUD in demo mode (create, edit, delete tasks/residences/etc.)
3. Demo banner with sign-up CTA
4. Landing page with preview
5. Zero backend dependency for the demo experience
6. Same UI components used for both demo and real app
