Files
honeyDueWeb/docs/01-foundation.md
Trey t 5a50d77515 feat: complete Phase 3 — advanced features for Casera web app
Adds sharing (residence share codes, join, user management, .casera file
export/import), subscription status with feature comparison, notification
preferences with bell icon, profile settings (edit info, change password,
theme picker, delete account), onboarding wizard with create/join paths,
enhanced dashboard with stats cards, Recharts completion chart, recent
activity feed, and task report PDF download.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 09:31:29 -06:00

9.6 KiB

Phase 1 — Foundation

Scaffold the project, wire up auth, build the app shell, and establish the design system.

Checklist

  • Next.js 15 project init with App Router + TypeScript
  • Tailwind CSS 4 configuration
  • shadcn/ui setup (install CLI, add base components)
  • TypeScript types from API DTOs (all request/response shapes)
  • API client layer (typed fetch wrappers, token injection, error handling)
  • Auth flows: Login, Register, Verify Email, Forgot/Reset Password
  • Auth middleware (route protection, token cookie)
  • App shell: sidebar nav + top bar
  • Design system: color tokens matching mobile app
  • Theme switcher (11 themes, persisted to localStorage)

1. Project Scaffold

npx create-next-app@latest myCribAPI-Web --typescript --tailwind --eslint --app --src-dir
cd myCribAPI-Web
npx shadcn@latest init

Install core dependencies:

npm install @tanstack/react-query zustand react-hook-form @hookform/resolvers zod
npm install lucide-react recharts @dnd-kit/core @dnd-kit/sortable
npm install -D vitest @playwright/test

2. TypeScript Types

Generate TypeScript types matching the Go API DTOs. Source from:

  • myCribAPI-go/internal/dto/requests/ — request shapes
  • myCribAPI-go/internal/dto/responses/ — response shapes
  • myCribAPI-go/internal/models/ — entity shapes

Key types to define in src/lib/types/:

types/
├── api.ts          # ApiResult<T>, PaginatedResponse, ErrorResponse
├── auth.ts         # LoginRequest, RegisterRequest, UserResponse, TokenResponse
├── residence.ts    # Residence, ResidenceDetail, MyResidencesResponse, CreateResidenceRequest
├── task.ts         # Task, TaskDetail, TaskColumnsResponse, CreateTaskRequest, TaskCompletionRequest
├── contractor.ts   # Contractor, ContractorDetail, CreateContractorRequest
├── document.ts     # Document, DocumentDetail, CreateDocumentRequest
├── subscription.ts # SubscriptionStatus, FeatureBenefit, UpgradeTrigger
├── lookups.ts      # TaskCategory, TaskPriority, TaskFrequency, ContractorSpecialty
└── notification.ts # NotificationPreference, Notification

3. API Client Layer

Create typed fetch wrappers in src/lib/api/:

api/
├── client.ts       # Base fetch wrapper (token injection, error handling, base URL)
├── auth.ts         # login(), register(), forgotPassword(), resetPassword()
├── residences.ts   # getResidences(), getResidence(), createResidence(), updateResidence(), deleteResidence()
├── tasks.ts        # getTasks(), getTasksByResidence(), createTask(), updateTask(), completeTask(), etc.
├── contractors.ts  # getContractors(), getContractor(), createContractor(), etc.
├── documents.ts    # getDocuments(), getDocument(), createDocument(), uploadFile(), etc.
├── lookups.ts      # getStaticData(), getUpgradeTriggers()
├── subscription.ts # getSubscriptionStatus(), etc.
└── notifications.ts # getPreferences(), updatePreference(), etc.

Base client pattern:

// src/lib/api/client.ts
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'https://mycrib.treytartt.com/api';

async function apiFetch<T>(
  path: string,
  options: RequestInit = {}
): Promise<T> {
  const res = await fetch(`${API_BASE_URL}${path}`, {
    ...options,
    credentials: 'include', // Send httpOnly cookie
    headers: {
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  if (!res.ok) {
    const error = await res.json().catch(() => ({ detail: 'Unknown error' }));
    throw new ApiError(res.status, error.detail || error.message);
  }

  return res.json();
}

4. Auth Flows

Pages

Route Screen Components
/login Login form Email, password, "Forgot password?" link, social sign-in buttons
/register Registration form First name, last name, email, password, confirm password
/verify-email Email verification Code input, resend button
/forgot-password Forgot password Email input, submit
/reset-password Reset password New password, confirm, token from URL

Auth Token Strategy

Login → Server returns token → API route stores token in httpOnly cookie → Client redirects to /app
                                                                          ↓
                                                               All subsequent requests include cookie
                                                                          ↓
                                                               Next.js middleware reads cookie for route protection
  • Token format: Token <hex> (Django-compatible, same as mobile)
  • Cookie: httpOnly, SameSite=Strict, Secure in production
  • Next.js middleware in src/middleware.ts checks cookie existence for protected routes

Auth Store (Zustand)

// src/stores/auth.ts
interface AuthState {
  user: UserResponse | null;
  isAuthenticated: boolean;
  isLoading: boolean;
  login: (email: string, password: string) => Promise<void>;
  register: (data: RegisterRequest) => Promise<void>;
  logout: () => Promise<void>;
  fetchUser: () => Promise<void>;
}

5. App Shell

Layout Structure

┌─────────────────────────────────────────────────────┐
│  Top Bar: Logo | Search (optional) | Profile Menu   │
├──────────┬──────────────────────────────────────────┤
│          │                                          │
│ Sidebar  │              Main Content                │
│          │                                          │
│ 🏠 Home  │                                          │
│ 🏘 Resid │                                          │
│ ✅ Tasks │                                          │
│ 👷 Contr │                                          │
│ 📄 Docs  │                                          │
│          │                                          │
│ ──────── │                                          │
│ ⚙ Setti │                                          │
│          │                                          │
├──────────┴──────────────────────────────────────────┤
│  (mobile: bottom tab bar instead of sidebar)        │
└─────────────────────────────────────────────────────┘
  • Desktop (≥1024px): Collapsible sidebar + top bar
  • Tablet (768-1023px): Collapsed sidebar (icons only) + top bar
  • Mobile (<768px): Bottom tab bar (matches iOS app), no sidebar

Navigation Items

Icon Label Route
Home Home /app
Building Residences /app/residences
CheckSquare Tasks /app/tasks
HardHat Contractors /app/contractors
FileText Documents /app/documents
Settings Settings /app/settings

6. Design System

Color Tokens

Map the mobile app's color palette to CSS custom properties and Tailwind config:

/* src/styles/theme.css */
:root {
  /* Default theme (matches iOS appPrimary, etc.) */
  --color-primary: #07A0C3;       /* BlueGreen */
  --color-secondary: #0055A5;     /* Cerulean */
  --color-accent: #F5A623;        /* BrightAmber */
  --color-error: #DD1C1A;         /* PrimaryScarlet */
  --color-bg-primary: #FFF1D0;    /* Cream */
  --color-bg-secondary: #F0F4F8;  /* Blue-gray */
  --color-text-primary: #1A1A1A;
  --color-text-secondary: #6B7280;
  --color-text-on-primary: #FFFFFF;
}

[data-theme="dark"] {
  --color-bg-primary: #0A1929;
  --color-bg-secondary: #1A2A3A;
  --color-text-primary: #F0F0F0;
  --color-text-secondary: #9CA3AF;
}

11 Themes

Match the mobile app's theme system:

Theme Primary Secondary
Default #07A0C3 #0055A5
Teal TBD TBD
Ocean TBD TBD
Forest TBD TBD
Sunset TBD TBD
Monochrome TBD TBD
Lavender TBD TBD
Crimson TBD TBD
Midnight TBD TBD
Desert TBD TBD
Mint TBD TBD

Theme values sourced from MyCribKMM/composeApp/src/commonMain/.../ui/theme/ThemeColors.kt.

Spacing

--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 12px;
--spacing-lg: 16px;
--spacing-xl: 24px;

7. TanStack Query Setup

// src/lib/hooks/useResidences.ts
export function useResidences() {
  return useQuery({
    queryKey: ['residences'],
    queryFn: () => api.residences.getResidences(),
    staleTime: 60 * 60 * 1000, // 1 hour (matches mobile cache timeout)
    refetchOnWindowFocus: true,
  });
}

8. Lookups Initialization

Match the mobile pattern — after login, fetch /api/static_data/ and /api/upgrade-triggers/:

// After successful login
await queryClient.prefetchQuery({
  queryKey: ['lookups'],
  queryFn: () => api.lookups.getStaticData(),
  staleTime: Infinity, // ETag-based, manually invalidate
});

Deliverables

At the end of Phase 1, you should have:

  1. A working Next.js app with Tailwind + shadcn/ui
  2. Login and registration flows that authenticate against the Go API
  3. A protected app shell with sidebar navigation
  4. A design system with color tokens matching the mobile app
  5. Typed API client with error handling
  6. Theme switching (light/dark + 11 color themes)