From 386e6f3cc72750022f1fb2ac5a765b1954a25984 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 14:51:19 +1100 Subject: [PATCH 01/34] fix: auto-detect port in auth client baseURL - Replace hardcoded localhost:3000 with window.location.origin - Enables auth to work on any port (3000, 3001, etc.) - Fixes issue when port 3000 is already in use - Maintains SSR compatibility with fallback env var --- src/modules/auth/utils/auth-client.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/auth/utils/auth-client.ts b/src/modules/auth/utils/auth-client.ts index c06fe9f..7923257 100644 --- a/src/modules/auth/utils/auth-client.ts +++ b/src/modules/auth/utils/auth-client.ts @@ -4,9 +4,7 @@ import { createAuthClient } from "better-auth/react"; // The baseURL will be automatically determined from the current origin export const authClient = createAuthClient({ baseURL: - process.env.NODE_ENV === "development" - ? "http://localhost:3000" - : typeof window !== "undefined" - ? window.location.origin - : "", + typeof window !== "undefined" + ? window.location.origin // Auto-detect from browser URL (works for any port) + : process.env.NEXT_PUBLIC_AUTH_URL || "http://localhost:3000", // Fallback for SSR }); From f389b91e6e7e3f373b8e94898ab7b786ebdd27e1 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 14:58:48 +1100 Subject: [PATCH 02/34] fix: correct todos navigation link path - Change /todos to /dashboard/todos to match actual route - Fixes 404 error when clicking Todos in navigation - Actual route is in app/dashboard/todos/page.tsx --- src/components/navigation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx index a6a3b0f..9595f27 100644 --- a/src/components/navigation.tsx +++ b/src/components/navigation.tsx @@ -22,7 +22,7 @@ export function Navigation() { Home - + From e5e4bb39e575415e044177bf10993edf9502480a Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:03:08 +1100 Subject: [PATCH 06/34] fix: add file size and type validation for image uploads - Validate image files are under 5MB before upload - Validate only PNG/JPG files are accepted - Show toast error messages for validation failures - Reset file input on validation error - Prevents large file uploads that would fail - Matches documented limits ("PNG, JPG up to 5MB") --- src/modules/todos/components/todo-form.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/modules/todos/components/todo-form.tsx b/src/modules/todos/components/todo-form.tsx index d26d514..9417c4a 100644 --- a/src/modules/todos/components/todo-form.tsx +++ b/src/modules/todos/components/todo-form.tsx @@ -5,6 +5,7 @@ import { Upload, X } from "lucide-react"; import Image from "next/image"; import { useState, useTransition } from "react"; import { useForm } from "react-hook-form"; +import toast from "react-hot-toast"; import type { z } from "zod"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; @@ -108,6 +109,22 @@ export function TodoForm({ const handleImageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { + // Validate file size (5MB = 5 * 1024 * 1024 bytes) + const maxSize = 5 * 1024 * 1024; + if (file.size > maxSize) { + toast.error("Image must be less than 5MB"); + e.target.value = ""; // Reset input + return; + } + + // Validate file type + const validTypes = ["image/png", "image/jpeg", "image/jpg"]; + if (!validTypes.includes(file.type)) { + toast.error("Only PNG and JPG images are allowed"); + e.target.value = ""; // Reset input + return; + } + setImageFile(file); const reader = new FileReader(); reader.onloadend = () => { From b0bcb4d28a7d6292ac54ad88c130aa27511f4063 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:08:37 +1100 Subject: [PATCH 07/34] fix: prevent double https:// in R2 public URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CLOUDFLARE_R2_URL environment variable already includes the protocol prefix (https://), so prepending another 'https://' results in malformed URLs like 'https://https://...'. This fix removes the hardcoded protocol prefix and uses the env var value directly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/lib/r2.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/r2.ts b/src/lib/r2.ts index 2fdc4f8..a3c1812 100644 --- a/src/lib/r2.ts +++ b/src/lib/r2.ts @@ -46,7 +46,8 @@ export async function uploadToR2( } // Return public URL of R2 (should be using custom domain) - const publicUrl = `https://${(env as any).CLOUDFLARE_R2_URL}/${key}`; + // CLOUDFLARE_R2_URL already includes the protocol (https://) + const publicUrl = `${(env as any).CLOUDFLARE_R2_URL}/${key}`; return { success: true, From b5c79d7c4fb4601e47f034d68408382e579890d4 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:11:27 +1100 Subject: [PATCH 08/34] refactor: replace hardcoded database ID with environment variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded D1 database ID in drizzle.config.ts with CLOUDFLARE_D1_DATABASE_ID environment variable for better configurability across different environments. Changes: - Add CLOUDFLARE_D1_DATABASE_ID to .dev.vars.example - Update drizzle.config.ts to use env var instead of hardcoded ID - Document the new env var in README.md with instructions - Add note about copying database_id when creating D1 database This allows developers to easily switch between different databases (dev/staging/prod) without modifying code. Note: wrangler.jsonc still requires the actual database_id value as it doesn't support environment variable interpolation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .dev.vars.example | 2 + PROJECT_BRIEF.md | 310 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 7 ++ drizzle.config.ts | 2 +- 4 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 PROJECT_BRIEF.md diff --git a/.dev.vars.example b/.dev.vars.example index b97e98a..6536148 100644 --- a/.dev.vars.example +++ b/.dev.vars.example @@ -4,7 +4,9 @@ NEXTJS_ENV=development # Drizzle Kit credentials for D1 # Get your Account ID from: https://dash.cloudflare.com/ (right sidebar) # Get your API Token from: https://dash.cloudflare.com/profile/api-tokens +# Get your Database ID from: wrangler d1 list (or create with: wrangler d1 create ) CLOUDFLARE_ACCOUNT_ID=your-account-id-here +CLOUDFLARE_D1_DATABASE_ID=your-database-id-here CLOUDFLARE_D1_TOKEN=your-api-token-here CLOUDFLARE_R2_URL=your-r2-url-here CLOUDFLARE_API_TOKEN=your-api-token-here diff --git a/PROJECT_BRIEF.md b/PROJECT_BRIEF.md new file mode 100644 index 0000000..4f9714a --- /dev/null +++ b/PROJECT_BRIEF.md @@ -0,0 +1,310 @@ +# Project Brief: Fullstack Next.js + Cloudflare CRM + +**Created**: 2025-11-08 +**Status**: Ready for Planning +**Purpose**: Learning exercise to understand Next.js 15 + Cloudflare Workers integration + +--- + +## Vision + +A lightweight CRM built on the fullstack-next-cloudflare template to learn modern fullstack patterns: Next.js App Router, Cloudflare D1/R2, Server Actions, and module-sliced architecture. + +--- + +## Problem/Opportunity + +**Learning objective**: Understand how to build production-grade features on Cloudflare's edge platform by implementing real-world CRM functionality. + +**Why CRM as the learning vehicle**: +- Multi-entity relationships (contacts ↔ deals) +- CRUD operations with validation +- Data modeling with foreign keys +- UI patterns (lists, forms, boards) +- Follows existing architecture (todos module) + +--- + +## Target Audience + +- **Primary user**: You (Jez) - exploring the stack +- **Scale**: Single user for learning (no multi-tenancy needed) +- **Context**: Educational project, not production SaaS +- **Data**: Can use synthetic/test data + +--- + +## Core Functionality (MVP) + +### 1. Contacts Module +**Essential**: +- ✅ Create, read, update, delete contacts +- ✅ Fields: firstName, lastName, email, phone, company, jobTitle, notes +- ✅ Search/filter by name, email, company +- ✅ Tag system (many-to-many: contacts ↔ tags) +- ✅ User-specific tags with colors + +**Deferred to Phase 2** (keep MVP lean): +- ❌ Activity timeline (calls, meetings, notes) +- ❌ Avatar uploads to R2 +- ❌ Email integration +- ❌ Import/export + +### 2. Deals/Pipeline Module +**Essential**: +- ✅ Create, read, update, delete deals +- ✅ Fields: title, value, currency, stage, expectedCloseDate, description +- ✅ Link deal to contact (simple 1:1 relationship) +- ✅ Pipeline board view (simple columns by stage) +- ✅ Fixed stages: Prospecting → Qualification → Proposal → Negotiation → Closed Won/Lost + +**Deferred to Phase 2**: +- ❌ Custom user-defined stages +- ❌ Drag-and-drop stage changes +- ❌ Deal probability/forecasting +- ❌ Multiple contacts per deal + +### 3. Dashboard Integration +**Essential**: +- ✅ Add navigation to /dashboard/contacts and /dashboard/deals +- ✅ Simple metrics cards (total contacts, active deals, pipeline value) + +**Deferred**: +- ❌ Charts/graphs +- ❌ Activity feed +- ❌ Advanced analytics + +--- + +## Tech Stack (Validated) + +Uses existing template stack - no changes needed: + +- **Frontend**: Next.js 15.4.6 (App Router) + React 19 + TypeScript +- **UI**: Tailwind v4 + shadcn/ui + Lucide icons +- **Backend**: Cloudflare Workers with Static Assets (@opennextjs/cloudflare) +- **Database**: Cloudflare D1 (SQLite) with Drizzle ORM +- **Storage**: Cloudflare R2 (not using for MVP - deferred avatars) +- **Auth**: Better Auth (already configured) +- **Forms**: React Hook Form + Zod validation +- **Deployment**: Cloudflare Workers (via GitHub Actions) + +**Why this stack works for learning**: +- ✅ Modern patterns (Server Actions, RSC) +- ✅ Edge-first architecture +- ✅ Type-safe end-to-end (TypeScript + Drizzle + Zod) +- ✅ Template already has auth, DB, migrations configured +- ✅ Follows module-sliced pattern (easy to extend) + +--- + +## Research Findings + +### Existing Template Analysis + +**What's already built** (from /home/jez/Documents/fullstack-next-cloudflare-demo): +- ✅ **Auth module**: Better Auth with email/password + Google OAuth +- ✅ **Todos module**: Complete CRUD example with categories, priorities, status +- ✅ **Database setup**: D1 + Drizzle + migrations working +- ✅ **R2 integration**: Image upload pattern (in todos for cover images) +- ✅ **Module architecture**: `src/modules/[feature]/` with actions/, components/, schemas/ +- ✅ **UI components**: 13 shadcn/ui components configured +- ✅ **Deployment**: GitHub Actions workflow ready + +**Pattern to follow**: +The `src/modules/todos/` structure is the perfect blueprint: +``` +todos/ +├── actions/ # Server actions (create, get, update, delete) +├── components/ # UI components (form, card, list) +├── models/ # Enums and types +└── schemas/ # Drizzle + Zod schemas +``` + +We'll replicate this for `contacts/` and `deals/` modules. + +### Technical Validation + +**✅ D1 Relational Data**: +- Drizzle ORM supports foreign keys and joins +- Template already has `todos → categories` relationship +- Contacts ↔ Deals will work the same way + +**✅ Many-to-Many Tags**: +- Need junction table: `contacts_to_tags` +- Drizzle example in their docs: https://orm.drizzle.team/docs/rqb#many-to-many + +**✅ Server Actions Performance**: +- Template uses server actions for all mutations +- Edge runtime = fast globally +- No API route boilerplate needed + +**Known Challenges**: +1. **Junction table queries** - Drizzle syntax for many-to-many can be verbose + - Mitigation: Study existing `todos.categoryId` pattern, extend to junction table +2. **Pipeline board UI** - Kanban layout without drag-drop library + - Mitigation: Simple CSS Grid columns, manual stage update dropdown (defer drag-drop to Phase 2) +3. **Search implementation** - D1 doesn't have full-text search + - Mitigation: Use SQL `LIKE` queries for MVP (good enough for learning) + +--- + +## Scope Validation + +### Why Build This? +**Learning objectives met**: +- ✅ Practice module-sliced architecture +- ✅ Understand Drizzle ORM relationships (1:1, many-to-many) +- ✅ Learn Server Actions data mutation patterns +- ✅ Explore D1 migrations workflow +- ✅ Build complex forms with validation +- ✅ Create dashboard visualizations +- ✅ Deploy to Cloudflare edge + +**Why NOT use existing CRM**: +- This is about learning the stack, not production use +- Building from scratch teaches architectural patterns +- Template provides 80% foundation (auth, DB, UI), we add 20% (domain logic) + +### Why This Scope? +**MVP is deliberately minimal** to focus on learning core patterns: +- 2 main entities (contacts, deals) = practice relationships +- Tags system = practice many-to-many +- Pipeline board = practice UI state management +- Dashboard metrics = practice aggregations + +**Deferred features** prevent scope creep: +- Activity logging (complex timeline UI) +- Avatars (R2 already demonstrated in todos) +- Custom stages (adds complexity) +- Advanced analytics (not core learning) + +**Time investment** = ~6-8 hours (~6-8 minutes with Claude Code) +- Realistic for learning project +- Can complete in 1-2 sessions +- Leaves room for experimentation + +### What Could Go Wrong? + +**Risk 1: Overcomplicating relationships** +- *What*: Trying to add too many foreign keys (deals → contacts → companies → industries...) +- *Mitigation*: Stick to MVP scope (contacts ↔ tags, deals → contacts). No nested hierarchies. + +**Risk 2: UI perfectionism** +- *What*: Spending hours on drag-and-drop Kanban, animations, etc. +- *Mitigation*: Use simple table/grid layouts. Focus on functionality, not polish. + +**Risk 3: Scope creep during build** +- *What*: "While I'm here, let me add email integration..." +- *Mitigation*: Strict adherence to MVP checklist. Document ideas for Phase 2. + +--- + +## Estimated Effort + +**Total MVP**: ~6-8 hours (~6-8 minutes human time with Claude Code) + +**Breakdown**: +- Setup (clone, configure D1, run migrations): 30 min +- Contacts module (schema, actions, UI, tags): 2.5 hours +- Deals module (schema, actions, UI, board): 2 hours +- Dashboard integration (nav, metrics): 1 hour +- Testing & seed data: 1 hour +- Documentation: 30 min + +**Phase 2** (optional extensions): +- Activity timeline: +2 hours +- Avatar uploads: +1 hour +- Drag-drop Kanban: +2 hours +- Custom stages: +1.5 hours +- Advanced search: +2 hours + +--- + +## Success Criteria (MVP) + +**Functional Requirements**: +- [ ] Can create, edit, delete, search contacts +- [ ] Can assign multiple tags to contacts +- [ ] Can create tags with colors +- [ ] Can create, edit, delete deals +- [ ] Deals link to contacts (dropdown selector) +- [ ] Pipeline board shows deals in columns by stage +- [ ] Dashboard shows: total contacts, active deals, pipeline value +- [ ] All data isolated to logged-in user +- [ ] Forms have proper validation (Zod schemas) +- [ ] UI responsive on mobile/desktop + +**Technical Requirements**: +- [ ] Follows module-sliced architecture (`src/modules/contacts/`, `src/modules/deals/`) +- [ ] Uses Server Actions (not API routes) +- [ ] Database migrations run successfully (local + production) +- [ ] Type-safe end-to-end (TypeScript + Drizzle + Zod) +- [ ] shadcn/ui components used consistently +- [ ] Deploys to Cloudflare Workers without errors + +**Learning Objectives**: +- [ ] Understand how to structure multi-entity features +- [ ] Practice Drizzle ORM relationships (foreign keys, joins, many-to-many) +- [ ] Learn Server Actions patterns for CRUD +- [ ] Experience D1 migrations workflow +- [ ] Build complex forms with React Hook Form + Zod + +--- + +## Next Steps + +### If Proceeding (Recommended) + +1. **Exit plan mode** and start implementation +2. **Clone project** to `/home/jez/Documents/fullstack-next-cloudflare-crm` +3. **Configure local Cloudflare**: + - Create new D1 database: `npx wrangler d1 create fullstack-crm` + - Update `wrangler.jsonc` with new database ID + - Set up `.dev.vars` with Better Auth secrets +4. **Implement in phases**: + - Phase 1: Project setup + database schema + - Phase 2: Contacts module + - Phase 3: Deals module + - Phase 4: Dashboard integration + - Phase 5: Testing & documentation +5. **Deploy when ready** (Cloudflare account setup) + +### If Refining Scope + +**Want simpler?** +- Skip tags (just contacts + deals) +- Skip pipeline board (simple table view) +- Reduces to ~4 hours + +**Want more ambitious?** +- Add activity timeline +- Add R2 avatar uploads +- Add custom stages +- Increases to ~10-12 hours + +--- + +## Research References + +- **Template repo**: https://github.com/jezweb/fullstack-next-cloudflare (forked from ifindev) +- **Local codebase**: /home/jez/Documents/fullstack-next-cloudflare-demo +- **Drizzle ORM relationships**: https://orm.drizzle.team/docs/rqb +- **shadcn/ui components**: https://ui.shadcn.com/docs +- **Cloudflare D1 docs**: via `mcp__cloudflare-docs__search_cloudflare_documentation` +- **Relevant skills**: `~/.claude/skills/cloudflare-d1`, `~/.claude/skills/drizzle-orm-d1` + +--- + +## Recommendation + +✅ **Proceed with MVP implementation** + +**Rationale**: +1. Scope is well-defined and realistic (6-8 hours) +2. Template provides solid foundation (80% already built) +3. Learning objectives are clear and achievable +4. No technical blockers (all patterns exist in template) +5. Can defer advanced features to Phase 2 without compromising learning + +**This is an excellent learning project** - complex enough to teach real patterns, simple enough to complete without frustration. diff --git a/README.md b/README.md index 333f7ab..0feb729 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ Edit `.dev.vars` with your credentials: ```bash # Cloudflare Configuration CLOUDFLARE_ACCOUNT_ID=your-account-id +CLOUDFLARE_D1_DATABASE_ID=your-database-id CLOUDFLARE_D1_TOKEN=your-api-token # Authentication Secrets @@ -263,6 +264,10 @@ wrangler d1 create your-app-name # Output will show: # database_name = "your-app-name" # database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +# +# Copy the database_id - you'll need it for: +# - wrangler.jsonc (d1_databases.database_id) +# - .dev.vars (CLOUDFLARE_D1_DATABASE_ID) ``` **Create R2 Bucket:** @@ -329,6 +334,7 @@ openssl rand -base64 32 ```bash # .dev.vars for local development CLOUDFLARE_ACCOUNT_ID=your-account-id +CLOUDFLARE_D1_DATABASE_ID=your-database-id CLOUDFLARE_D1_TOKEN=your-api-token BETTER_AUTH_SECRET=your-generated-secret GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com @@ -403,6 +409,7 @@ Go to your GitHub repository → Settings → Secrets and add: - `CLOUDFLARE_API_TOKEN` - Your API token from Step 2 - `CLOUDFLARE_ACCOUNT_ID` - Your account ID +- `CLOUDFLARE_D1_DATABASE_ID` - Your D1 database ID (from `wrangler d1 create` output) - `BETTER_AUTH_SECRET` - Your auth secret - `GOOGLE_CLIENT_ID` - Your Google client ID - `GOOGLE_CLIENT_SECRET` - Your Google client secret diff --git a/drizzle.config.ts b/drizzle.config.ts index b3c0c64..30fb1aa 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ driver: "d1-http", dbCredentials: { accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, - databaseId: "757a32d1-5779-4f09-bcf3-b268013395d4", + databaseId: process.env.CLOUDFLARE_D1_DATABASE_ID!, token: process.env.CLOUDFLARE_D1_TOKEN!, }, }); From ba9b4fc4ea4f8e4c675ad68cebd50393e9802ccc Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:12:49 +1100 Subject: [PATCH 09/34] fix: add NEXT_REDIRECT error handling to update todo action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add proper handling for Next.js redirect errors in updateTodoAction to match the pattern used in createTodoAction. Without this, redirect() calls from server actions could be incorrectly logged as errors. Changes: - Check if error message is 'NEXT_REDIRECT' before error logging - Re-throw NEXT_REDIRECT errors unchanged to allow proper redirection - Prevents false error logs in console for successful operations This matches the existing pattern in create-todo.action.ts for consistency. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/modules/todos/actions/update-todo.action.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/todos/actions/update-todo.action.ts b/src/modules/todos/actions/update-todo.action.ts index 99e4c6a..bbdf1d4 100644 --- a/src/modules/todos/actions/update-todo.action.ts +++ b/src/modules/todos/actions/update-todo.action.ts @@ -86,6 +86,11 @@ export async function updateTodoAction(todoId: number, formData: FormData) { revalidatePath(todosRoutes.list); redirect(todosRoutes.list); } catch (error) { + // Handle Next.js redirect errors - these are not actual errors + if (error instanceof Error && error.message === "NEXT_REDIRECT") { + throw error; // Re-throw redirect errors as-is + } + console.error("Error updating todo:", error); if ( From 3acc7410d2863af1b64f420599568f02148a9260 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:15:46 +1100 Subject: [PATCH 10/34] refactor: standardize createCategory error response pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update createCategory action to return success/error object instead of throwing errors, making it consistent with other mutation actions like deleteTodoAction and updateTodoFieldAction. Changes: - create-category.action.ts: Return { success, data?, error? } instead of throwing - add-category.tsx: Update to handle new response structure without try/catch - Added authentication error handling for consistency Benefits: - Consistent error handling across all non-form mutation actions - Cleaner client code (no try/catch needed) - Better error propagation to UI - Matches established pattern used by delete and update field actions This brings all programmatic mutations to use the same response pattern, while form actions with redirect() continue to use throw pattern (as required by Next.js). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../todos/actions/create-category.action.ts | 38 +++++++++++++++---- src/modules/todos/components/add-category.tsx | 21 ++++------ 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/modules/todos/actions/create-category.action.ts b/src/modules/todos/actions/create-category.action.ts index 48eaa2c..5890a19 100644 --- a/src/modules/todos/actions/create-category.action.ts +++ b/src/modules/todos/actions/create-category.action.ts @@ -10,7 +10,11 @@ import { } from "@/modules/todos/schemas/category.schema"; import todosRoutes from "../todos.route"; -export async function createCategory(data: unknown): Promise { +export async function createCategory(data: unknown): Promise<{ + success: boolean; + data?: Category; + error?: string; +}> { try { const user = await requireAuth(); const validatedData = insertCategorySchema.parse({ @@ -30,21 +34,39 @@ export async function createCategory(data: unknown): Promise { .returning(); if (!result[0]) { - throw new Error("Failed to create category"); + return { + success: false, + error: "Failed to create category", + }; } // Revalidate pages that might show categories revalidatePath(todosRoutes.list); revalidatePath(todosRoutes.new); - return result[0]; + return { + success: true, + data: result[0], + }; } catch (error) { console.error("Error creating category:", error); - throw new Error( - error instanceof Error - ? error.message - : "Failed to create category", - ); + if ( + error instanceof Error && + error.message === "Authentication required" + ) { + return { + success: false, + error: "Authentication required", + }; + } + + return { + success: false, + error: + error instanceof Error + ? error.message + : "Failed to create category", + }; } } diff --git a/src/modules/todos/components/add-category.tsx b/src/modules/todos/components/add-category.tsx index 1c90cbd..5a95a5d 100644 --- a/src/modules/todos/components/add-category.tsx +++ b/src/modules/todos/components/add-category.tsx @@ -59,23 +59,18 @@ export function AddCategory({ onCategoryAdded }: AddCategoryProps) { const onSubmit = (data: AddCategoryFormData) => { startTransition(async () => { - try { - const newCategory = await createCategory(data); - onCategoryAdded(newCategory); + const result = await createCategory(data); + if (!result.success) { + toast.error(result.error || "Failed to create category"); + return; + } + + if (result.data) { + onCategoryAdded(result.data); form.reset(); setOpen(false); - toast.success("Category created successfully!"); - } catch (error) { - console.error("Error creating category:", error); - - const errorMessage = - error instanceof Error - ? error.message - : "Failed to create category"; - - toast.error(errorMessage); } }); }; From fa233f37a0a3be9b78beeed120d9014cbede245a Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:28:38 +1100 Subject: [PATCH 11/34] docs: add comprehensive API endpoint documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add complete API reference documentation covering all REST endpoints and server actions. Contents: - REST API Endpoints (2 endpoints) - POST /api/summarize - Text summarization with Workers AI - /api/auth/[...all] - Better Auth endpoints - Server Actions (11 actions) - Authentication: signIn, signUp, signOut - Todos: getAllTodos, getTodoById, create, update, updateField, delete - Categories: getAllCategories, createCategory - Authentication Details - Better Auth configuration - OAuth providers (Google) - Utility functions - Data Models - Todo, Category, User, Session schemas - Validation rules and constraints - Error Handling - API error formats - Server action patterns - Security considerations Each endpoint/action includes: - Purpose and description - Request/response formats with TypeScript types - Authentication requirements - Error conditions and codes - Practical usage examples - Side effects (revalidation, redirects, R2 uploads) Total: 500+ lines of comprehensive API documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/API_ENDPOINTS.md | 872 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 872 insertions(+) create mode 100644 docs/API_ENDPOINTS.md diff --git a/docs/API_ENDPOINTS.md b/docs/API_ENDPOINTS.md new file mode 100644 index 0000000..ccbf78e --- /dev/null +++ b/docs/API_ENDPOINTS.md @@ -0,0 +1,872 @@ +# API Documentation + +Complete API surface documentation for the Full-Stack Next.js Cloudflare Demo application. + +## Table of Contents + +1. [REST API Endpoints](#rest-api-endpoints) +2. [Server Actions](#server-actions) +3. [Authentication](#authentication) +4. [Data Models](#data-models) +5. [Error Handling](#error-handling) + +--- + +## REST API Endpoints + +### POST /api/summarize + +Summarize text using Cloudflare Workers AI. + +**Purpose**: Generate summaries of text content with configurable length, style, and language. + +**Authentication**: Required (session-based) + +**Request Body**: +```typescript +{ + text: string; // 50-50,000 characters + config?: { + maxLength?: number; // 50-1000, default: 200 + style?: "concise" | "detailed" | "bullet-points"; // default: "concise" + language?: string; // default: "English" + } +} +``` + +**Response** (200 OK): +```typescript +{ + success: true; + data: { + summary: string; + originalLength: number; + summaryLength: number; + tokensUsed: { + input: number; + output: number; + } + }; + error: null; +} +``` + +**Error Responses**: +- `401 Unauthorized`: User not authenticated + ```json + { "success": false, "error": "Authentication required", "data": null } + ``` +- `400 Bad Request`: Invalid input (via zod validation) +- `500 Internal Server Error`: AI service unavailable + ```json + { "success": false, "error": "AI service is not available", "data": null } + ``` + +**Example Usage**: +```typescript +const response = await fetch('/api/summarize', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + text: 'Long article text here...', + config: { maxLength: 150, style: 'bullet-points' } + }) +}); +const result = await response.json(); +``` + +--- + +### Better Auth Endpoints + +All Better Auth endpoints are handled via `/api/auth/[...all]`. + +**Base Path**: `/api/auth` + +**Supported Methods**: GET, POST + +**Available Routes** (handled by Better Auth): +- `POST /api/auth/sign-up/email` - Email/password sign up +- `POST /api/auth/sign-in/email` - Email/password sign in +- `POST /api/auth/sign-out` - Sign out current session +- `GET /api/auth/session` - Get current session +- `GET /api/auth/get-session` - Alternative session endpoint +- OAuth routes for Google sign-in (configured) + +**Configuration**: +- Email/Password authentication: Enabled +- Google OAuth: Enabled (requires GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET) +- Session storage: D1 database via Drizzle adapter +- Cookie handling: Next.js cookies plugin + +**Example - Get Session**: +```typescript +const response = await fetch('/api/auth/session'); +const session = await response.json(); +// Returns: { user: { id, name, email, ... }, session: { ... } } +``` + +--- + +## Server Actions + +All server actions use Next.js Server Actions (`"use server"`) and return structured responses. + +### Authentication Actions + +#### signIn() + +**Path**: `src/modules/auth/actions/auth.action.ts` + +**Purpose**: Authenticate user with email and password. + +**Parameters**: +```typescript +{ + email: string; // Valid email format + password: string; // Minimum 8 characters +} +``` + +**Returns**: +```typescript +{ + success: boolean; + message: string; // "Signed in successfully" or error message +} +``` + +**Authentication**: No (this creates the session) + +**Usage**: +```typescript +import { signIn } from '@/modules/auth/actions/auth.action'; + +const result = await signIn({ email: 'user@example.com', password: 'password123' }); +if (result.success) { + // Redirect to dashboard +} +``` + +--- + +#### signUp() + +**Path**: `src/modules/auth/actions/auth.action.ts` + +**Purpose**: Create new user account with email and password. + +**Parameters**: +```typescript +{ + email: string; // Valid email format + password: string; // Minimum 8 characters + username: string; // Minimum 3 characters +} +``` + +**Returns**: +```typescript +{ + success: boolean; + message: string; // "Signed up successfully" or error message +} +``` + +**Authentication**: No (creates new user) + +**Usage**: +```typescript +import { signUp } from '@/modules/auth/actions/auth.action'; + +const result = await signUp({ + email: 'user@example.com', + password: 'password123', + username: 'johndoe' +}); +``` + +--- + +#### signOut() + +**Path**: `src/modules/auth/actions/auth.action.ts` + +**Purpose**: End current user session. + +**Parameters**: None + +**Returns**: +```typescript +{ + success: boolean; + message: string; // "Signed out successfully" or error message +} +``` + +**Authentication**: Required (implicitly via session headers) + +**Usage**: +```typescript +import { signOut } from '@/modules/auth/actions/auth.action'; + +const result = await signOut(); +if (result.success) { + // Redirect to login +} +``` + +--- + +### Todo Actions + +#### getAllTodos() + +**Path**: `src/modules/todos/actions/get-todos.action.ts` + +**Purpose**: Fetch all todos for the authenticated user with category information. + +**Parameters**: None + +**Returns**: +```typescript +Todo[] // Array of todos (see Data Models section) +``` + +**Authentication**: Yes (via requireAuth()) + +**Database Query**: +- Joins todos with categories +- Filters by authenticated user ID +- Orders by creation date + +**Error Handling**: Returns empty array on error + +**Usage**: +```typescript +import getAllTodos from '@/modules/todos/actions/get-todos.action'; + +const todos = await getAllTodos(); +// Returns: [{ id, title, description, categoryName, ... }, ...] +``` + +--- + +#### getTodoById() + +**Path**: `src/modules/todos/actions/get-todo-by-id.action.ts` + +**Purpose**: Fetch a single todo by ID for the authenticated user. + +**Parameters**: +```typescript +id: number // Todo ID +``` + +**Returns**: +```typescript +Todo | null // Todo object or null if not found +``` + +**Authentication**: Yes (via requireAuth()) + +**Database Query**: +- Joins with categories +- Verifies todo belongs to authenticated user +- Returns null if not found or unauthorized + +**Usage**: +```typescript +import { getTodoById } from '@/modules/todos/actions/get-todo-by-id.action'; + +const todo = await getTodoById(123); +if (todo) { + // Display todo details +} +``` + +--- + +#### createTodoAction() + +**Path**: `src/modules/todos/actions/create-todo.action.ts` + +**Purpose**: Create a new todo with optional image upload to R2. + +**Parameters**: `FormData` object with the following fields: +```typescript +{ + title: string; // Required, 3-255 characters + description?: string; // Optional, max 1000 characters + categoryId?: number; // Optional category ID + status?: "pending" | "in_progress" | "completed" | "archived"; + priority?: "low" | "medium" | "high" | "urgent"; + completed?: boolean; // Default: false + dueDate?: string; // ISO date string + imageUrl?: string; // Optional image URL + imageAlt?: string; // Optional alt text + image?: File; // Optional image file for upload +} +``` + +**Returns**: Redirects to todo list on success + +**Authentication**: Yes (via requireAuth()) + +**Side Effects**: +- Uploads image to R2 if provided (bucket: "todo-images") +- Revalidates `/dashboard/todos` path +- Redirects to todo list after creation + +**Throws**: +- Zod validation errors for invalid data +- "Authentication required" error if not authenticated +- Generic error message for other failures + +**Usage**: +```typescript +import { createTodoAction } from '@/modules/todos/actions/create-todo.action'; + +const formData = new FormData(); +formData.append('title', 'New Task'); +formData.append('description', 'Task description'); +formData.append('priority', 'high'); +formData.append('image', fileInput.files[0]); + +await createTodoAction(formData); +// Automatically redirects on success +``` + +--- + +#### updateTodoAction() + +**Path**: `src/modules/todos/actions/update-todo.action.ts` + +**Purpose**: Update an existing todo with optional new image. + +**Parameters**: +```typescript +todoId: number // Todo ID to update +formData: FormData // Form fields (all optional, partial update) +``` + +**FormData Fields** (all optional): +```typescript +{ + title?: string; + description?: string; + categoryId?: number; + status?: "pending" | "in_progress" | "completed" | "archived"; + priority?: "low" | "medium" | "high" | "urgent"; + completed?: boolean; + dueDate?: string; + imageUrl?: string; + imageAlt?: string; + image?: File; // New image file +} +``` + +**Returns**: Redirects to todo list on success + +**Authentication**: Yes (via requireAuth()) + +**Database Query**: +- Verifies todo belongs to authenticated user +- Only updates provided fields +- Sets updatedAt timestamp + +**Side Effects**: +- Uploads new image to R2 if provided +- Revalidates `/dashboard/todos` path +- Redirects to todo list + +**Throws**: +- "Todo not found or unauthorized" if todo doesn't exist or belongs to another user +- Validation errors for invalid data + +**Usage**: +```typescript +import { updateTodoAction } from '@/modules/todos/actions/update-todo.action'; + +const formData = new FormData(); +formData.append('title', 'Updated Title'); +formData.append('status', 'completed'); + +await updateTodoAction(123, formData); +``` + +--- + +#### updateTodoFieldAction() + +**Path**: `src/modules/todos/actions/update-todo.action.ts` + +**Purpose**: Update specific fields of a todo (optimized for checkbox toggles). + +**Parameters**: +```typescript +{ + todoId: number; + data: { + completed?: boolean; // Currently only supports completed field + } +} +``` + +**Returns**: +```typescript +{ + success: boolean; + data?: Todo; // Updated todo object if successful + error?: string; // Error message if failed +} +``` + +**Authentication**: Yes (via requireAuth()) + +**Side Effects**: +- Auto-updates status to "completed" or "pending" based on completed field +- Revalidates `/dashboard/todos` path +- Does NOT redirect (returns data for optimistic UI updates) + +**Usage**: +```typescript +import { updateTodoFieldAction } from '@/modules/todos/actions/update-todo.action'; + +const result = await updateTodoFieldAction(123, { completed: true }); +if (result.success) { + // Update UI optimistically +} +``` + +--- + +#### deleteTodoAction() + +**Path**: `src/modules/todos/actions/delete-todo.action.ts` + +**Purpose**: Delete a todo by ID. + +**Parameters**: +```typescript +todoId: number // Todo ID to delete +``` + +**Returns**: +```typescript +{ + success: boolean; + message?: string; // Success message + error?: string; // Error message if failed +} +``` + +**Authentication**: Yes (via requireAuth()) + +**Database Query**: +- Verifies todo exists and belongs to authenticated user +- Deletes todo record + +**Side Effects**: +- Revalidates `/dashboard/todos` path + +**Usage**: +```typescript +import { deleteTodoAction } from '@/modules/todos/actions/delete-todo.action'; + +const result = await deleteTodoAction(123); +if (result.success) { + // Show success message +} +``` + +--- + +### Category Actions + +#### getAllCategories() + +**Path**: `src/modules/todos/actions/get-categories.action.ts` + +**Purpose**: Fetch all categories for a specific user. + +**Parameters**: +```typescript +userId: string // User ID +``` + +**Returns**: +```typescript +Category[] // Array of categories (see Data Models section) +``` + +**Authentication**: No (but requires userId parameter) + +**Database Query**: +- Filters categories by userId +- Orders by creation date + +**Error Handling**: Returns empty array on error + +**Usage**: +```typescript +import { getAllCategories } from '@/modules/todos/actions/get-categories.action'; + +const categories = await getAllCategories(user.id); +``` + +--- + +#### createCategory() + +**Path**: `src/modules/todos/actions/create-category.action.ts` + +**Purpose**: Create a new category for the authenticated user. + +**Parameters**: +```typescript +{ + name: string; // Required + color?: string; // Optional, default: "#6366f1" + description?: string; // Optional +} +``` + +**Returns**: +```typescript +Category // Created category object +``` + +**Authentication**: Yes (via requireAuth()) + +**Side Effects**: +- Revalidates `/dashboard/todos` and `/dashboard/todos/new` paths + +**Throws**: +- Zod validation errors for invalid data +- "Failed to create category" if database operation fails + +**Usage**: +```typescript +import { createCategory } from '@/modules/todos/actions/create-category.action'; + +const category = await createCategory({ + name: 'Work', + color: '#ff6347', + description: 'Work-related tasks' +}); +``` + +--- + +## Authentication + +### Better Auth Configuration + +**Provider**: Better Auth library with Drizzle adapter +**Database**: Cloudflare D1 (SQLite) +**Session Storage**: Database-backed sessions + +**Authentication Methods**: +1. Email/Password (enabled) +2. Google OAuth (enabled) + +**Environment Variables Required**: +- `BETTER_AUTH_SECRET` - Secret key for signing tokens +- `GOOGLE_CLIENT_ID` - Google OAuth client ID +- `GOOGLE_CLIENT_SECRET` - Google OAuth client secret + +**Session Management**: +- Sessions stored in database with expiration +- Cookies managed by Better Auth Next.js plugin +- Automatic session refresh + +### Auth Utility Functions + +All utilities in `src/modules/auth/utils/auth-utils.ts`: + +#### getCurrentUser() + +Returns the current authenticated user or null. + +```typescript +const user = await getCurrentUser(); +// Returns: { id: string, name: string, email: string } | null +``` + +#### requireAuth() + +Returns the current user or throws "Authentication required" error. + +```typescript +const user = await requireAuth(); +// Throws if not authenticated +``` + +#### isAuthenticated() + +Check if user has valid session. + +```typescript +const authenticated = await isAuthenticated(); +// Returns: boolean +``` + +#### getSession() + +Get full session object including user and session metadata. + +```typescript +const session = await getSession(); +// Returns session object or null +``` + +--- + +## Data Models + +### Todo + +```typescript +{ + id: number; + title: string; + description: string | null; + categoryId: number | null; + categoryName: string | null; // Joined from categories table + userId: string; + status: "pending" | "in_progress" | "completed" | "archived"; + priority: "low" | "medium" | "high" | "urgent"; + imageUrl: string | null; + imageAlt: string | null; + completed: boolean; + dueDate: string | null; // ISO date string + createdAt: string; // ISO date string + updatedAt: string; // ISO date string +} +``` + +**Validation Rules**: +- `title`: 3-255 characters +- `description`: max 1000 characters +- `imageUrl`: valid URL or empty string +- `status`: defaults to "pending" +- `priority`: defaults to "medium" +- `completed`: defaults to false + +--- + +### Category + +```typescript +{ + id: number; + name: string; + color: string; // Hex color, default: "#6366f1" + description: string | null; + userId: string; + createdAt: string; // ISO date string + updatedAt: string; // ISO date string +} +``` + +**Validation Rules**: +- `name`: required, minimum 1 character +- `color`: optional, defaults to indigo +- `userId`: automatically set from authenticated user + +--- + +### User (Auth) + +```typescript +{ + id: string; + name: string; + email: string; + emailVerified: boolean; + image: string | null; + createdAt: Date; + updatedAt: Date; +} +``` + +**Public Interface** (AuthUser): +```typescript +{ + id: string; + name: string; + email: string; +} +``` + +--- + +### Session + +```typescript +{ + id: string; + expiresAt: Date; + token: string; + createdAt: Date; + updatedAt: Date; + ipAddress: string | null; + userAgent: string | null; + userId: string; +} +``` + +--- + +## Error Handling + +### API Endpoint Errors + +REST endpoints return standardized error responses: + +```typescript +{ + success: false; + error: string; // Human-readable error message + data: null; +} +``` + +**Common HTTP Status Codes**: +- `400` - Bad Request (validation errors) +- `401` - Unauthorized (authentication required) +- `404` - Not Found +- `500` - Internal Server Error + +### Server Action Errors + +**Pattern 1: Return Object** (for UI handling) +```typescript +{ + success: false; + error: string; +} +``` + +**Pattern 2: Throw Error** (for form actions with redirects) +```typescript +throw new Error("Specific error message"); +``` + +**Pattern 3: Redirect on Success** (form actions) +- Uses Next.js `redirect()` after successful mutation +- Throws `NEXT_REDIRECT` error (normal behavior) +- Revalidates paths before redirect + +### Authentication Errors + +All protected actions check authentication: + +```typescript +const user = await requireAuth(); +// Throws "Authentication required" if not authenticated +``` + +Caller should handle: +```typescript +try { + await protectedAction(); +} catch (error) { + if (error.message === "Authentication required") { + // Redirect to login + } +} +``` + +--- + +## Security Considerations + +1. **Authorization**: All todo/category operations verify userId matches authenticated user +2. **File Uploads**: Images validated and uploaded to R2 with scoped paths +3. **SQL Injection**: Prevented by Drizzle ORM parameterized queries +4. **XSS**: React escapes output by default +5. **CSRF**: Better Auth handles CSRF tokens automatically +6. **Rate Limiting**: Not implemented (consider adding for production) + +--- + +## Development Notes + +### Revalidation Strategy + +Actions that modify data revalidate affected paths: + +```typescript +revalidatePath('/dashboard/todos'); // List page +revalidatePath('/dashboard/todos/new'); // New todo page +``` + +### Redirect Pattern + +Form actions redirect after success: + +```typescript +revalidatePath(todosRoutes.list); +redirect(todosRoutes.list); // Throws NEXT_REDIRECT +``` + +### Image Upload Flow + +1. Check if FormData contains image file +2. Upload to R2 bucket ("todo-images") +3. Store returned URL in database +4. Generate alt text from filename if not provided +5. Log errors but don't fail todo creation + +### Database Adapter + +- Uses Drizzle ORM with D1 adapter +- Better Auth uses Drizzle adapter for session storage +- All queries are type-safe with TypeScript + +--- + +## Quick Reference + +### Most Common Operations + +**Create Todo**: +```typescript +const formData = new FormData(); +formData.append('title', 'Task'); +await createTodoAction(formData); +``` + +**Update Todo Checkbox**: +```typescript +await updateTodoFieldAction(todoId, { completed: true }); +``` + +**Delete Todo**: +```typescript +await deleteTodoAction(todoId); +``` + +**Get All Todos**: +```typescript +const todos = await getAllTodos(); +``` + +**Sign In**: +```typescript +const result = await signIn({ email, password }); +``` + +**Check Authentication**: +```typescript +const user = await getCurrentUser(); +if (!user) redirect('/login'); +``` + +--- + +**Last Updated**: 2025-11-08 +**API Version**: 1.0.0 From f355e686951f78dfccdb3d0e2cb923da14960f39 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:39:59 +1100 Subject: [PATCH 12/34] fix: replace alert() with toast in toggle-complete component - Import react-hot-toast for consistent error feedback - Replace alert() with toast.error() on line 35 - Simplify error message to match other components - Consistent with PR #14 (delete-todo.tsx) Provides professional, non-blocking error notifications when toggling todo completion fails. --- src/modules/todos/components/toggle-complete.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/todos/components/toggle-complete.tsx b/src/modules/todos/components/toggle-complete.tsx index a652ec2..5b62cb5 100644 --- a/src/modules/todos/components/toggle-complete.tsx +++ b/src/modules/todos/components/toggle-complete.tsx @@ -1,6 +1,7 @@ "use client"; import { useState, useTransition } from "react"; +import toast from "react-hot-toast"; import { Checkbox } from "@/components/ui/checkbox"; import { updateTodoFieldAction } from "../actions/update-todo.action"; @@ -31,8 +32,8 @@ export function ToggleComplete({ todoId, completed }: ToggleCompleteProps) { console.error("Error updating todo:", error); // Revert the optimistic update setIsCompleted(!checked); - alert( - `Error updating todo: ${error instanceof Error ? error.message : "Unknown error"}`, + toast.error( + error instanceof Error ? error.message : "Failed to update todo", ); } }); From 1f0d87ed516c3e93ea22f7fced3d5055cada317d Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:41:25 +1100 Subject: [PATCH 13/34] feat: add success feedback for todo create/edit operations - Import react-hot-toast and useRouter - Use toast.promise to show loading/success/error states - Show 'Creating/Updating todo...' during operation - Show success message before redirect - Provides clear user feedback for save operations Enhances UX by confirming successful todo creation/updates before redirect happens. --- src/modules/todos/components/todo-form.tsx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/modules/todos/components/todo-form.tsx b/src/modules/todos/components/todo-form.tsx index d26d514..34f3680 100644 --- a/src/modules/todos/components/todo-form.tsx +++ b/src/modules/todos/components/todo-form.tsx @@ -3,8 +3,10 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { Upload, X } from "lucide-react"; import Image from "next/image"; +import { useRouter } from "next/navigation"; import { useState, useTransition } from "react"; import { useForm } from "react-hook-form"; +import toast from "react-hot-toast"; import type { z } from "zod"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; @@ -72,6 +74,7 @@ export function TodoForm({ categories: initialCategories, initialData, }: TodoFormProps) { + const router = useRouter(); const [isPending, startTransition] = useTransition(); const [imageFile, setImageFile] = useState(null); const [imagePreview, setImagePreview] = useState( @@ -162,12 +165,21 @@ export function TodoForm({ formData.append("image", imageFile); } - if (initialData) { - await updateTodoAction(initialData.id, formData); - } else { - await createTodoAction(formData); - } + // Show success feedback before redirect + const actionPromise = initialData + ? updateTodoAction(initialData.id, formData) + : createTodoAction(formData); + + await toast.promise(actionPromise, { + loading: initialData ? "Updating todo..." : "Creating todo...", + success: initialData + ? "Todo updated successfully!" + : "Todo created successfully!", + error: (err) => + err instanceof Error ? err.message : "Failed to save todo", + }); } catch (error) { + // Error already shown by toast.promise console.error("Error saving todo:", error); } }); From 821c4ac60cd390f8440e44ea10f38208a1b9fa13 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:44:24 +1100 Subject: [PATCH 14/34] feat: add warnings when image upload fails during todo operations - Import cookies() in create-todo and update-todo actions - Set 'todo-warning' cookie when R2 upload fails - Create WarningToast component to display cookie-based warnings - Integrate WarningToast into todo-list page - Clear warning cookie after displaying Now users see: 'Image upload failed, but todo was created/updated successfully' when R2 upload fails but todo operation succeeds. Prevents silent failures. --- SESSION.md | 163 ++++ docs/DATABASE_SCHEMA.md | 755 ++++++++++++++++ docs/IMPLEMENTATION_PHASES.md | 836 ++++++++++++++++++ .../todos/actions/create-todo.action.ts | 7 + .../todos/actions/update-todo.action.ts | 7 + .../todos/components/warning-toast.tsx | 21 + src/modules/todos/todo-list.page.tsx | 13 + 7 files changed, 1802 insertions(+) create mode 100644 SESSION.md create mode 100644 docs/DATABASE_SCHEMA.md create mode 100644 docs/IMPLEMENTATION_PHASES.md create mode 100644 src/modules/todos/components/warning-toast.tsx diff --git a/SESSION.md b/SESSION.md new file mode 100644 index 0000000..433ee88 --- /dev/null +++ b/SESSION.md @@ -0,0 +1,163 @@ +# Session State + +**Project**: fullstack-next-cloudflare (Open Source Contributions) +**Repository**: https://github.com/ifindev/fullstack-next-cloudflare +**Fork**: https://github.com/jezweb/fullstack-next-cloudflare +**Current Phase**: UX Improvements +**Last Checkpoint**: fa233f3 (2025-11-08) + +--- + +## Completed PRs ✅ + +### Phase 1: Quick Fixes (PRs #11-16) +**Status**: Complete | **Date**: 2025-11-08 + +1. **PR #11** - Auto-detect port in auth client + - Fixed hardcoded localhost:3000 to use window.location.origin + - Prevents port conflicts in development + +2. **PR #12** - Fix navigation link + - Changed /todos → /dashboard/todos (404 fix) + +3. **PR #13** - Fix typos in method names + - buildSystenPrompt → buildSystemPrompt + - styleInstructructions → styleInstructions + +4. **PR #14** - Replace alert() with toast + - delete-todo.tsx: alert() → toast.error() + +5. **PR #15** - Add ARIA labels + - Added aria-label to delete button for accessibility + +6. **PR #16** - Add file validation + - File size limit: 5MB + - File types: PNG, JPG only + - Toast error messages + +--- + +### Phase 2: Medium-Difficulty Fixes (PRs #17-20) +**Status**: Complete | **Date**: 2025-11-08 + +7. **PR #17** - Fix R2 URL double https:// + - File: src/lib/r2.ts + - Removed hardcoded https:// prefix (env var already includes it) + +8. **PR #18** - Database ID environment variable + - Files: drizzle.config.ts, .dev.vars.example, README.md + - Added CLOUDFLARE_D1_DATABASE_ID env var + - Replaced hardcoded database ID + +9. **PR #19** - NEXT_REDIRECT error handling + - File: src/modules/todos/actions/update-todo.action.ts + - Added NEXT_REDIRECT handling to match createTodoAction + +10. **PR #20** - Standardize error responses + - Files: create-category.action.ts, add-category.tsx + - Changed from throw pattern to { success, data?, error? } pattern + - Consistent with other mutations + +--- + +### Phase 3: Documentation (PR #21) +**Status**: Complete | **Date**: 2025-11-08 + +11. **PR #21** - Complete API documentation + - File: docs/API_ENDPOINTS.md (872 lines) + - REST endpoints: /api/summarize, /api/auth/* + - Server actions: 11 actions documented + - Data models, error handling, examples + +--- + +## Current Phase: UX Improvements 🔄 + +### Planned PRs (Next 5) + +**PR #22** - Replace alert() with toast (remaining instances) +- Files: toggle-complete.tsx +- Estimated: 15 min + +**PR #23** - Add success feedback for todo create/edit +- Files: todo-form.tsx, create-todo.action.ts, update-todo.action.ts +- Add toast.success() before redirect +- Estimated: 30 min + +**PR #24** - Image upload failure warnings +- Files: create-todo.action.ts, update-todo.action.ts +- Show toast when R2 upload fails +- Estimated: 20 min + +**PR #25** - Loading state for image uploads +- File: todo-form.tsx +- Add loading indicator/progress +- Estimated: 45 min + +**PR #26** - Theme-aware colors (optional) +- Files: todo-card.tsx, dashboard.page.tsx +- Replace hard-coded colors with semantic theme colors +- Estimated: 45 min + +--- + +## Key Files Reference + +**Actions**: +- `src/modules/todos/actions/create-todo.action.ts` +- `src/modules/todos/actions/update-todo.action.ts` +- `src/modules/todos/actions/delete-todo.action.ts` +- `src/modules/todos/actions/create-category.action.ts` + +**Components**: +- `src/modules/todos/components/todo-form.tsx` +- `src/modules/todos/components/todo-card.tsx` +- `src/modules/todos/components/delete-todo.tsx` +- `src/modules/todos/components/toggle-complete.tsx` +- `src/modules/todos/components/add-category.tsx` + +**API Routes**: +- `src/app/api/summarize/route.ts` +- `src/app/api/auth/[...all]/route.ts` + +**Config**: +- `drizzle.config.ts` +- `wrangler.jsonc` +- `.dev.vars.example` + +--- + +## Development Setup + +**Dev Servers Running**: +- Wrangler: Port 8787 (Bash 23e213) +- Next.js: Port 3001 (Bash d83043) +- Additional: Bash bc259d + +**Environment**: +- Account ID: 0460574641fdbb98159c98ebf593e2bd +- Database ID: 757a32d1-5779-4f09-bcf3-b268013395d4 +- Auth: Google OAuth configured + +--- + +## Contribution Stats + +**Total PRs**: 11 submitted +**Lines Changed**: ~1,500+ lines +**Documentation Added**: 872 lines +**Issues Fixed**: 15+ + +**Focus Areas**: +- Error handling consistency +- Environment configuration +- API documentation +- User experience improvements + +--- + +## Next Action + +**After context compact**: Continue with UX improvement PRs (#22-26) + +Start with PR #22: Replace remaining alert() calls with toast notifications in toggle-complete.tsx diff --git a/docs/DATABASE_SCHEMA.md b/docs/DATABASE_SCHEMA.md new file mode 100644 index 0000000..74a0e4d --- /dev/null +++ b/docs/DATABASE_SCHEMA.md @@ -0,0 +1,755 @@ +# Database Schema: Fullstack Next.js + Cloudflare CRM + +**Database**: Cloudflare D1 (distributed SQLite) +**Migrations**: Located in `drizzle/` +**ORM**: Drizzle ORM +**Schema Files**: `src/modules/*/schemas/*.schema.ts` + +--- + +## Overview + +The CRM extends the existing template database (users, sessions, todos, categories) with 4 new tables for contacts and deals management. + +**Existing Tables** (from template): +- `user` - User accounts (Better Auth) +- `session` - Active sessions (Better Auth) +- `account` - OAuth provider accounts (Better Auth) +- `verification` - Email verification tokens (Better Auth) +- `todos` - Task management +- `categories` - Todo categories + +**New CRM Tables** (added in Phase 2): +- `contacts` - Contact profiles +- `contact_tags` - User-defined tags +- `contacts_to_tags` - Many-to-many junction table +- `deals` - Sales pipeline deals + +--- + +## Entity Relationship Diagram + +```mermaid +erDiagram + user ||--o{ contacts : "owns" + user ||--o{ contact_tags : "creates" + user ||--o{ deals : "manages" + + contacts ||--o{ contacts_to_tags : "tagged with" + contact_tags ||--o{ contacts_to_tags : "applied to" + + contacts ||--o{ deals : "associated with" + + user { + integer id PK + text email UK + text name + text image + integer emailVerified + integer createdAt + integer updatedAt + } + + contacts { + integer id PK + text firstName + text lastName + text email + text phone + text company + text jobTitle + text notes + integer userId FK + integer createdAt + integer updatedAt + } + + contact_tags { + integer id PK + text name + text color + integer userId FK + integer createdAt + } + + contacts_to_tags { + integer contactId FK + integer tagId FK + } + + deals { + integer id PK + text title + integer contactId FK + real value + text currency + text stage + integer expectedCloseDate + text description + integer userId FK + integer createdAt + integer updatedAt + } +``` + +--- + +## Table Definitions + +### `contacts` + +**Purpose**: Store contact information for CRM + +| Column | Type | Constraints | Notes | +|--------|------|-------------|-------| +| id | INTEGER | PRIMARY KEY AUTOINCREMENT | Unique contact ID | +| firstName | TEXT | | Optional - at least one name required | +| lastName | TEXT | | Optional - at least one name required | +| email | TEXT | | Optional but must be valid format | +| phone | TEXT | | Optional, freeform (no format validation) | +| company | TEXT | | Optional company name | +| jobTitle | TEXT | | Optional job title/role | +| notes | TEXT | | Optional freeform notes | +| userId | INTEGER | NOT NULL, FOREIGN KEY → user(id) | Owner of contact | +| createdAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | +| updatedAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | + +**Indexes**: +- `idx_contacts_user_id` on `userId` (filter by user) +- `idx_contacts_email` on `email` (search by email) +- `idx_contacts_company` on `company` (search by company) + +**Relationships**: +- Many-to-one with `user` (each contact owned by one user) +- Many-to-many with `contact_tags` (via junction table) +- One-to-many with `deals` (contact can have multiple deals) + +**Business Rules**: +- At least one of `firstName` or `lastName` must be non-empty (enforced in app, not DB) +- Email must be unique per user (optional constraint, not enforced in MVP) +- Deleting a user cascades to delete their contacts + +--- + +### `contact_tags` + +**Purpose**: User-defined tags for organizing contacts + +| Column | Type | Constraints | Notes | +|--------|------|-------------|-------| +| id | INTEGER | PRIMARY KEY AUTOINCREMENT | Unique tag ID | +| name | TEXT | NOT NULL | Tag name (e.g., "Customer", "Lead") | +| color | TEXT | NOT NULL | Hex color code (e.g., "#3B82F6") | +| userId | INTEGER | NOT NULL, FOREIGN KEY → user(id) | Tag owner | +| createdAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | + +**Indexes**: +- `idx_contact_tags_user_id` on `userId` (filter by user) +- `idx_contact_tags_name_user` on `(name, userId)` (prevent duplicate tag names per user) + +**Relationships**: +- Many-to-one with `user` (each tag owned by one user) +- Many-to-many with `contacts` (via junction table) + +**Business Rules**: +- Tag name must be unique per user (enforced in app) +- Color must be valid hex format (enforced in app with Zod: `/^#[0-9A-Fa-f]{6}$/`) +- Deleting a tag removes all tag assignments (via cascade on junction table) + +--- + +### `contacts_to_tags` (Junction Table) + +**Purpose**: Many-to-many relationship between contacts and tags + +| Column | Type | Constraints | Notes | +|--------|------|-------------|-------| +| contactId | INTEGER | FOREIGN KEY → contacts(id) ON DELETE CASCADE | Contact being tagged | +| tagId | INTEGER | FOREIGN KEY → contact_tags(id) ON DELETE CASCADE | Tag being applied | + +**Composite Primary Key**: `(contactId, tagId)` + +**Indexes**: +- Primary key index on `(contactId, tagId)` (prevent duplicate assignments) +- `idx_contacts_to_tags_tag_id` on `tagId` (query contacts by tag) + +**Relationships**: +- Many-to-one with `contacts` +- Many-to-one with `contact_tags` + +**Business Rules**: +- A contact can have multiple tags +- A tag can be applied to multiple contacts +- Same tag cannot be assigned to a contact twice (enforced by composite PK) +- Deleting a contact removes all its tag assignments (ON DELETE CASCADE) +- Deleting a tag removes all its assignments (ON DELETE CASCADE) + +--- + +### `deals` + +**Purpose**: Sales pipeline deals linked to contacts + +| Column | Type | Constraints | Notes | +|--------|------|-------------|-------| +| id | INTEGER | PRIMARY KEY AUTOINCREMENT | Unique deal ID | +| title | TEXT | NOT NULL | Deal name/description | +| contactId | INTEGER | FOREIGN KEY → contacts(id) ON DELETE SET NULL | Linked contact (optional) | +| value | REAL | NOT NULL | Deal value (e.g., 5000.00) | +| currency | TEXT | NOT NULL DEFAULT 'AUD' | Currency code (ISO 4217) | +| stage | TEXT | NOT NULL | Pipeline stage (see enum below) | +| expectedCloseDate | INTEGER | | Expected close date (unix timestamp) | +| description | TEXT | | Optional deal notes | +| userId | INTEGER | NOT NULL, FOREIGN KEY → user(id) | Deal owner | +| createdAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | +| updatedAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | + +**Stage Enum** (enforced in app): +- `"Prospecting"` - Initial contact +- `"Qualification"` - Evaluating fit +- `"Proposal"` - Proposal sent +- `"Negotiation"` - Negotiating terms +- `"Closed Won"` - Deal won +- `"Closed Lost"` - Deal lost + +**Indexes**: +- `idx_deals_user_id` on `userId` (filter by user) +- `idx_deals_contact_id` on `contactId` (filter by contact) +- `idx_deals_stage` on `stage` (filter by pipeline stage) +- `idx_deals_user_stage` on `(userId, stage)` (pipeline board queries) + +**Relationships**: +- Many-to-one with `user` (each deal owned by one user) +- Many-to-one with `contacts` (optional - deal can exist without contact) + +**Business Rules**: +- Contact link is optional (`contactId` can be NULL) +- Deleting a contact does NOT delete deals (sets `contactId` to NULL) +- Stage must be one of the 6 valid enum values (enforced in app with Zod) +- Value must be positive (enforced in app) +- Currency defaults to AUD (user can change in form) +- Deleting a user cascades to delete their deals + +--- + +## Data Types & Conventions + +### Timestamps + +**Storage**: INTEGER (unix timestamp in milliseconds) + +**Creation**: +```typescript +const now = Date.now() // JavaScript +``` + +**Display**: +```typescript +new Date(timestamp).toLocaleDateString('en-AU') +new Date(timestamp).toLocaleString('en-AU') +``` + +**Drizzle Schema**: +```typescript +createdAt: integer('created_at', { mode: 'number' }) + .notNull() + .$defaultFn(() => Date.now()) +``` + +### Foreign Keys + +**Syntax in Drizzle**: +```typescript +userId: integer('user_id') + .notNull() + .references(() => userTable.id, { onDelete: 'cascade' }) +``` + +**Cascade Behavior**: +- `onDelete: 'cascade'` - Delete child records when parent deleted +- `onDelete: 'set null'` - Set foreign key to NULL when parent deleted + +**Applied**: +- User → Contacts: CASCADE (delete user's contacts) +- User → Tags: CASCADE (delete user's tags) +- User → Deals: CASCADE (delete user's deals) +- Contact → Deals: SET NULL (keep deal, remove contact link) +- Contact → Junction: CASCADE (remove tag assignments) +- Tag → Junction: CASCADE (remove contact assignments) + +### Enums + +**Deal Stage** (not enforced in DB, only in app): +```typescript +export const dealStageEnum = [ + 'Prospecting', + 'Qualification', + 'Proposal', + 'Negotiation', + 'Closed Won', + 'Closed Lost', +] as const + +export type DealStage = typeof dealStageEnum[number] +``` + +**Validation in Zod**: +```typescript +stage: z.enum(dealStageEnum) +``` + +### Text Fields + +**SQLite TEXT type** (no length limits): +- Short fields: email, phone, name, color +- Long fields: notes, description + +**Encoding**: UTF-8 + +**Collation**: Use `COLLATE NOCASE` for case-insensitive searches: +```sql +WHERE email LIKE '%query%' COLLATE NOCASE +``` + +### Numeric Fields + +**INTEGER**: Whole numbers (IDs, timestamps, foreign keys) +**REAL**: Floating point (deal values) + +--- + +## Migrations + +### Migration 0001: Initial Schema (Template) + +**File**: `drizzle/0000_*.sql` + +**Creates**: +- `user` table (Better Auth) +- `session` table (Better Auth) +- `account` table (Better Auth) +- `verification` table (Better Auth) +- `todos` table (template feature) +- `categories` table (template feature) + +**Status**: Already applied (template setup) + +--- + +### Migration 0002: CRM Schema (Phase 2) + +**File**: `drizzle/0002_*.sql` + +**Creates**: +- `contacts` table with indexes +- `contact_tags` table with indexes +- `contacts_to_tags` junction table with composite PK +- `deals` table with indexes + +**SQL Preview**: +```sql +CREATE TABLE contacts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + first_name TEXT, + last_name TEXT, + email TEXT, + phone TEXT, + company TEXT, + job_title TEXT, + notes TEXT, + user_id INTEGER NOT NULL REFERENCES user(id) ON DELETE CASCADE, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL +); + +CREATE INDEX idx_contacts_user_id ON contacts(user_id); +CREATE INDEX idx_contacts_email ON contacts(email); +CREATE INDEX idx_contacts_company ON contacts(company); + +CREATE TABLE contact_tags ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + color TEXT NOT NULL, + user_id INTEGER NOT NULL REFERENCES user(id) ON DELETE CASCADE, + created_at INTEGER NOT NULL +); + +CREATE INDEX idx_contact_tags_user_id ON contact_tags(user_id); +CREATE UNIQUE INDEX idx_contact_tags_name_user ON contact_tags(name, user_id); + +CREATE TABLE contacts_to_tags ( + contact_id INTEGER NOT NULL REFERENCES contacts(id) ON DELETE CASCADE, + tag_id INTEGER NOT NULL REFERENCES contact_tags(id) ON DELETE CASCADE, + PRIMARY KEY (contact_id, tag_id) +); + +CREATE INDEX idx_contacts_to_tags_tag_id ON contacts_to_tags(tag_id); + +CREATE TABLE deals ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + contact_id INTEGER REFERENCES contacts(id) ON DELETE SET NULL, + value REAL NOT NULL, + currency TEXT NOT NULL DEFAULT 'AUD', + stage TEXT NOT NULL, + expected_close_date INTEGER, + description TEXT, + user_id INTEGER NOT NULL REFERENCES user(id) ON DELETE CASCADE, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL +); + +CREATE INDEX idx_deals_user_id ON deals(user_id); +CREATE INDEX idx_deals_contact_id ON deals(contact_id); +CREATE INDEX idx_deals_stage ON deals(stage); +CREATE INDEX idx_deals_user_stage ON deals(user_id, stage); +``` + +**Apply**: +```bash +# Local +pnpm run db:migrate:local + +# Production +pnpm run db:migrate +``` + +--- + +## Seed Data (Phase 6) + +### Contacts + +10 sample contacts with variety: +- 3 contacts with multiple tags +- 2 contacts linked to deals +- Mix of complete and minimal profiles + +Example: +```typescript +{ + firstName: 'Sarah', + lastName: 'Chen', + email: 'sarah.chen@example.com', + phone: '+61 412 345 678', + company: 'TechCorp Australia', + jobTitle: 'CTO', + notes: 'Met at DevConf 2024. Interested in AI features.', + userId: 1, + createdAt: Date.now(), + updatedAt: Date.now() +} +``` + +### Tags + +5 common tags: +- Customer (#10B981 - green) +- Lead (#3B82F6 - blue) +- Partner (#8B5CF6 - purple) +- Inactive (#6B7280 - gray) +- VIP (#F59E0B - amber) + +### Deals + +5 deals across all stages: +```typescript +[ + { title: 'Enterprise License', stage: 'Prospecting', value: 5000 }, + { title: 'Consulting Package', stage: 'Qualification', value: 12000 }, + { title: 'Annual Support', stage: 'Proposal', value: 25000 }, + { title: 'Cloud Migration', stage: 'Closed Won', value: 50000 }, + { title: 'Training Program', stage: 'Closed Lost', value: 8000 }, +] +``` + +--- + +## Query Patterns + +### Fetch Contacts with Tags + +```typescript +// Using Drizzle ORM +const contactsWithTags = await db + .select({ + id: contacts.id, + firstName: contacts.firstName, + lastName: contacts.lastName, + email: contacts.email, + company: contacts.company, + tags: sql`json_group_array(json_object('id', ${contactTags.id}, 'name', ${contactTags.name}, 'color', ${contactTags.color}))`, + }) + .from(contacts) + .leftJoin(contactsToTags, eq(contacts.id, contactsToTags.contactId)) + .leftJoin(contactTags, eq(contactsToTags.tagId, contactTags.id)) + .where(eq(contacts.userId, userId)) + .groupBy(contacts.id) +``` + +### Search Contacts + +```typescript +// Case-insensitive search across name, email, company +const results = await db + .select() + .from(contacts) + .where( + and( + eq(contacts.userId, userId), + or( + sql`${contacts.firstName} LIKE ${`%${query}%`} COLLATE NOCASE`, + sql`${contacts.lastName} LIKE ${`%${query}%`} COLLATE NOCASE`, + sql`${contacts.email} LIKE ${`%${query}%`} COLLATE NOCASE`, + sql`${contacts.company} LIKE ${`%${query}%`} COLLATE NOCASE` + ) + ) + ) +``` + +### Fetch Deals with Contact Info + +```typescript +// Join deals with contacts +const dealsWithContacts = await db + .select({ + id: deals.id, + title: deals.title, + value: deals.value, + currency: deals.currency, + stage: deals.stage, + contactName: sql`${contacts.firstName} || ' ' || ${contacts.lastName}`, + contactEmail: contacts.email, + }) + .from(deals) + .leftJoin(contacts, eq(deals.contactId, contacts.id)) + .where(eq(deals.userId, userId)) + .orderBy(deals.createdAt) +``` + +### Dashboard Metrics + +```typescript +// Count contacts +const totalContacts = await db + .select({ count: sql`count(*)` }) + .from(contacts) + .where(eq(contacts.userId, userId)) + +// Count active deals +const activeDeals = await db + .select({ count: sql`count(*)` }) + .from(deals) + .where( + and( + eq(deals.userId, userId), + notInArray(deals.stage, ['Closed Won', 'Closed Lost']) + ) + ) + +// Sum pipeline value +const pipelineValue = await db + .select({ total: sql`sum(${deals.value})` }) + .from(deals) + .where( + and( + eq(deals.userId, userId), + notInArray(deals.stage, ['Closed Won', 'Closed Lost']) + ) + ) +``` + +--- + +## Performance Considerations + +### Indexes + +**Query Patterns**: +- Filter by userId (every query) → Index on `user_id` for all tables +- Search by email/company → Indexes on `email`, `company` in contacts +- Filter deals by stage → Index on `stage` +- Pipeline board query → Composite index on `(user_id, stage)` + +**Trade-offs**: +- Indexes speed up SELECT queries +- Indexes slow down INSERT/UPDATE/DELETE (minimal impact for CRM scale) +- SQLite handles small indexes efficiently + +### N+1 Query Prevention + +**Anti-pattern**: +```typescript +// BAD: N+1 queries +const contacts = await getContacts(userId) +for (const contact of contacts) { + const tags = await getTagsForContact(contact.id) // N queries! +} +``` + +**Solution**: +```typescript +// GOOD: Single query with JOIN +const contactsWithTags = await db + .select() + .from(contacts) + .leftJoin(contactsToTags, ...) + .leftJoin(contactTags, ...) + .groupBy(contacts.id) +``` + +### Pagination + +**For lists >50 items**: +```typescript +const pageSize = 50 +const offset = (page - 1) * pageSize + +const contacts = await db + .select() + .from(contacts) + .where(eq(contacts.userId, userId)) + .limit(pageSize) + .offset(offset) +``` + +**Not needed for MVP** (small data sets), but good practice for Phase 2. + +--- + +## Security + +### User Isolation + +**Critical**: Every query MUST filter by `userId` to prevent data leakage. + +**Pattern**: +```typescript +// Always include userId in WHERE clause +await db + .select() + .from(contacts) + .where(eq(contacts.userId, currentUserId)) +``` + +**Enforcement**: +- Server Actions use `requireAuth()` to get `currentUserId` +- Never trust client-provided `userId` parameter +- Always re-fetch user from session token + +### Ownership Verification + +**Before UPDATE/DELETE**: +```typescript +// 1. Fetch record +const contact = await db.query.contacts.findFirst({ + where: eq(contacts.id, contactId) +}) + +// 2. Verify ownership +if (contact.userId !== currentUserId) { + throw new Error('Forbidden') +} + +// 3. Mutate +await db.update(contacts).set(...).where(eq(contacts.id, contactId)) +``` + +### SQL Injection Prevention + +**Drizzle ORM handles parameterization automatically**: +```typescript +// Safe - Drizzle uses prepared statements +const results = await db + .select() + .from(contacts) + .where(sql`email LIKE ${`%${userInput}%`}`) +// userInput is safely escaped +``` + +**Never use string concatenation**: +```typescript +// UNSAFE - DO NOT DO THIS +const query = `SELECT * FROM contacts WHERE email = '${userInput}'` +``` + +--- + +## Backup & Recovery + +### Local D1 Backup + +**Location**: `.wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite` + +**Backup command**: +```bash +cp .wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite backup-$(date +%Y%m%d).sqlite +``` + +### Production D1 Backup + +**Export**: +```bash +npx wrangler d1 export fullstack-crm --remote --output backup.sql +``` + +**Restore**: +```bash +npx wrangler d1 execute fullstack-crm --remote --file backup.sql +``` + +**Frequency**: Manual for MVP, automate with GitHub Actions for production. + +--- + +## Future Enhancements (Phase 2) + +### Activity Timeline + +Add `contact_activities` table: +```sql +CREATE TABLE contact_activities ( + id INTEGER PRIMARY KEY, + contact_id INTEGER REFERENCES contacts(id) ON DELETE CASCADE, + type TEXT NOT NULL, -- 'call', 'email', 'meeting', 'note' + subject TEXT, + description TEXT, + timestamp INTEGER NOT NULL, + user_id INTEGER REFERENCES user(id) ON DELETE CASCADE +); +``` + +### Custom Deal Stages + +Add `deal_stages` table (user-defined): +```sql +CREATE TABLE deal_stages ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + order_index INTEGER NOT NULL, + color TEXT, + user_id INTEGER REFERENCES user(id) ON DELETE CASCADE, + UNIQUE(name, user_id) +); +``` + +Modify `deals.stage` to reference `deal_stages.id` instead of enum. + +### Soft Delete + +Add `deleted_at` column to contacts and deals: +```sql +ALTER TABLE contacts ADD COLUMN deleted_at INTEGER; +ALTER TABLE deals ADD COLUMN deleted_at INTEGER; +``` + +Filter deleted records: `WHERE deleted_at IS NULL` + +--- + +## References + +- **Drizzle ORM Docs**: https://orm.drizzle.team/docs/overview +- **SQLite Data Types**: https://www.sqlite.org/datatype3.html +- **D1 Documentation**: https://developers.cloudflare.com/d1/ +- **Better Auth Schema**: https://www.better-auth.com/docs/concepts/database diff --git a/docs/IMPLEMENTATION_PHASES.md b/docs/IMPLEMENTATION_PHASES.md new file mode 100644 index 0000000..d38876a --- /dev/null +++ b/docs/IMPLEMENTATION_PHASES.md @@ -0,0 +1,836 @@ +# Implementation Phases: Fullstack Next.js + Cloudflare CRM + +**Project Type**: Learning Exercise - CRM Features +**Stack**: Next.js 15 + Cloudflare Workers + D1 + Drizzle ORM + Tailwind v4 + shadcn/ui +**Estimated Total**: 6-8 hours (~6-8 minutes human time with Claude Code) + +--- + +## Phase 1: Project Setup + +**Type**: Infrastructure +**Estimated**: 30 minutes +**Files**: `wrangler.jsonc`, `.dev.vars`, `package.json` + +### Purpose + +Clone the project to a new directory, configure Cloudflare D1 database for your account, set up environment variables, and verify the development environment works. + +### File Map + +- `wrangler.jsonc` (modify existing) + - **Purpose**: Cloudflare Workers configuration + - **Modifications**: Update D1 database ID to your account's database + - **Used by**: Wrangler CLI for deployment and local dev + +- `.dev.vars` (create new) + - **Purpose**: Local development secrets + - **Contains**: Better Auth secrets, Google OAuth credentials + - **Used by**: Local development server + +### Tasks + +- [ ] Clone project from `/home/jez/Documents/fullstack-next-cloudflare-demo` to `/home/jez/Documents/fullstack-next-cloudflare-crm` +- [ ] Install dependencies with `pnpm install` +- [ ] Create new D1 database: `npx wrangler d1 create fullstack-crm` +- [ ] Update `wrangler.jsonc` with new database ID (replace `757a32d1-5779-4f09-bcf3-b268013395d4`) +- [ ] Create `.dev.vars` file with Better Auth secrets (copy from demo project if available) +- [ ] Run existing migrations: `pnpm run db:migrate:local` +- [ ] Start dev servers (two terminals): `pnpm run wrangler:dev` and `pnpm run dev` +- [ ] Verify app loads at http://localhost:5173 +- [ ] Create git repository and initial commit + +### Verification Criteria + +- [ ] App loads without errors in browser +- [ ] Can navigate to /dashboard (after login) +- [ ] Existing todos feature works (proves D1 connection) +- [ ] No console errors +- [ ] Git repository initialized + +### Exit Criteria + +Development environment is fully functional with new D1 database configured. Can run both Wrangler and Next.js dev servers simultaneously. Existing template features work correctly. + +--- + +## Phase 2: Database Schema + +**Type**: Database +**Estimated**: 1 hour +**Files**: `src/modules/contacts/schemas/contact.schema.ts`, `src/modules/contacts/schemas/tag.schema.ts`, `src/modules/deals/schemas/deal.schema.ts`, `drizzle/0002_crm_schema.sql` + +### Purpose + +Create database tables for contacts, tags, and deals with proper relationships. Follows the existing pattern from `todos` and `categories` tables. + +### File Map + +- `src/modules/contacts/schemas/contact.schema.ts` (new ~60 lines) + - **Purpose**: Drizzle schema for contacts table and tags + - **Key exports**: contactsTable, contactTagsTable, contactsToTagsTable + - **Dependencies**: drizzle-orm/d1, src/modules/auth/schemas (user relation) + - **Used by**: Contact actions, migration generation + +- `src/modules/deals/schemas/deal.schema.ts` (new ~50 lines) + - **Purpose**: Drizzle schema for deals table + - **Key exports**: dealsTable, dealStageEnum + - **Dependencies**: drizzle-orm/d1, contacts schema (foreign key) + - **Used by**: Deal actions, migration generation + +- `src/db/schema.ts` (modify existing) + - **Purpose**: Aggregate all schemas for migrations + - **Modifications**: Export new CRM schemas + - **Used by**: Drizzle Kit for migration generation + +- `drizzle/0002_crm_schema.sql` (generated) + - **Purpose**: Migration file to create CRM tables + - **Creates**: contacts, contact_tags, contacts_to_tags, deals tables + - **Generated by**: `pnpm drizzle-kit generate` + +### Data Flow + +```mermaid +erDiagram + users ||--o{ contacts : "owns" + users ||--o{ contact_tags : "owns" + users ||--o{ deals : "owns" + contacts ||--o{ contacts_to_tags : "has" + contact_tags ||--o{ contacts_to_tags : "tagged" + contacts ||--o{ deals : "linked to" + + users { + integer id PK + text email + text name + } + + contacts { + integer id PK + text firstName + text lastName + text email + text phone + text company + text jobTitle + text notes + integer userId FK + integer createdAt + integer updatedAt + } + + contact_tags { + integer id PK + text name + text color + integer userId FK + integer createdAt + } + + contacts_to_tags { + integer contactId FK + integer tagId FK + } + + deals { + integer id PK + text title + integer contactId FK + real value + text currency + text stage + integer expectedCloseDate + text description + integer userId FK + integer createdAt + integer updatedAt + } +``` + +### Critical Dependencies + +**Internal**: +- Auth schemas (`src/modules/auth/schemas`) for user foreign keys +- Existing D1 database from Phase 1 + +**External**: +- `drizzle-orm` - ORM for type-safe queries +- `drizzle-kit` - Migration generation + +**Configuration**: +- `drizzle.local.config.ts` - Points to local D1 database + +### Gotchas & Known Issues + +**Many-to-Many Pattern**: +- Junction table `contacts_to_tags` requires composite primary key `(contactId, tagId)` +- Drizzle syntax: `primaryKey: ["contactId", "tagId"]` +- Querying requires joins - study Drizzle docs for many-to-many queries + +**Foreign Key Constraints**: +- SQLite (D1) supports foreign keys but they must be enabled +- Use `.onDelete("cascade")` for tags (deleting tag removes associations) +- Use `.onDelete("set null")` for deals.contactId (deleting contact keeps deal) + +**Timestamp Pattern**: +- Store as INTEGER (unix timestamp in milliseconds) +- Use `.$defaultFn(() => Date.now())` for createdAt +- Use `.notNull()` for required fields + +**Deal Stages as Enum**: +- Fixed stages for MVP: Prospecting, Qualification, Proposal, Negotiation, Closed Won, Closed Lost +- Stored as TEXT with enum constraint +- Phase 2 feature: Custom user-defined stages (requires separate table) + +### Tasks + +- [ ] Create `src/modules/contacts/schemas/` directory +- [ ] Create `contact.schema.ts` with contactsTable definition (firstName, lastName, email, phone, company, jobTitle, notes, userId, timestamps) +- [ ] Add contactTagsTable definition (id, name, color, userId, createdAt) +- [ ] Add contactsToTagsTable junction table (contactId, tagId, composite PK) +- [ ] Create `src/modules/deals/schemas/` directory +- [ ] Create `deal.schema.ts` with dealsTable definition (title, contactId FK, value, currency, stage enum, expectedCloseDate, description, userId, timestamps) +- [ ] Update `src/db/schema.ts` to export new schemas +- [ ] Generate migration: `pnpm drizzle-kit generate` +- [ ] Review generated SQL in `drizzle/0002_crm_schema.sql` +- [ ] Run migration locally: `pnpm run db:migrate:local` +- [ ] Verify tables created in D1: `npx wrangler d1 execute fullstack-crm --local --command "SELECT name FROM sqlite_master WHERE type='table'"` + +### Verification Criteria + +- [ ] Migration generates without errors +- [ ] Migration runs successfully (local D1) +- [ ] Can see 7 new tables in D1: contacts, contact_tags, contacts_to_tags, deals (plus existing user/session/account/verification/todos/categories) +- [ ] Foreign key constraints are correct (userId → users, contactId → contacts, tagId → contact_tags) +- [ ] Composite primary key exists on contacts_to_tags (contactId, tagId) +- [ ] Deal stage enum constraint is enforced + +### Exit Criteria + +All CRM database tables exist with proper relationships and constraints. Can manually insert test data via Wrangler CLI to verify schema. Migration is version-controlled and ready for production deployment. + +--- + +## Phase 3: Contacts Module + +**Type**: UI + Server Actions +**Estimated**: 2.5 hours +**Files**: See File Map (8 files total) + +### Purpose + +Implement complete contacts CRUD functionality with search, filtering, and tag management. Follows the existing `todos` module pattern. + +### File Map + +- `src/modules/contacts/actions/create-contact.action.ts` (new ~40 lines) + - **Purpose**: Server action to create new contact + - **Key exports**: createContact(data) + - **Dependencies**: contact.schema.ts, getDb(), requireAuth() + - **Returns**: Created contact or error + +- `src/modules/contacts/actions/get-contacts.action.ts` (new ~60 lines) + - **Purpose**: Server action to fetch contacts with search/filter + - **Key exports**: getContacts(searchQuery?, tagId?) + - **Dependencies**: contact.schema.ts, getDb(), requireAuth() + - **Returns**: Array of contacts with tags + +- `src/modules/contacts/actions/update-contact.action.ts` (new ~50 lines) + - **Purpose**: Server action to update contact + - **Key exports**: updateContact(id, data) + - **Dependencies**: contact.schema.ts, getDb(), requireAuth() + - **Returns**: Updated contact or error + +- `src/modules/contacts/actions/delete-contact.action.ts` (new ~35 lines) + - **Purpose**: Server action to delete contact + - **Key exports**: deleteContact(id) + - **Dependencies**: contact.schema.ts, getDb(), requireAuth() + - **Returns**: Success boolean + +- `src/modules/contacts/actions/tag-management.actions.ts` (new ~80 lines) + - **Purpose**: Server actions for tag CRUD and assignment + - **Key exports**: createTag(), getTags(), assignTagToContact(), removeTagFromContact() + - **Dependencies**: tag.schema.ts, getDb(), requireAuth() + - **Returns**: Tag operations results + +- `src/modules/contacts/components/contact-form.tsx` (new ~120 lines) + - **Purpose**: Reusable form for create/edit contact + - **Key exports**: ContactForm component + - **Dependencies**: React Hook Form, Zod, shadcn/ui (Input, Textarea, Button, Form), actions + - **Used by**: New contact page, Edit contact page + +- `src/modules/contacts/components/contact-card.tsx` (new ~80 lines) + - **Purpose**: Display single contact with actions + - **Key exports**: ContactCard component + - **Dependencies**: shadcn/ui (Card, Badge), actions (delete, tags) + - **Used by**: Contact list page + +- `src/app/dashboard/contacts/page.tsx` (new ~90 lines) + - **Purpose**: Contact list page with search and filter + - **Route**: /dashboard/contacts + - **Dependencies**: getContacts action, ContactCard, shadcn/ui (Input for search) + - **Features**: Search by name/email/company, filter by tag + +### Data Flow + +```mermaid +sequenceDiagram + participant U as User + participant C as ContactForm Component + participant A as createContact Action + participant D as D1 Database + participant R as React (revalidate) + + U->>C: Fill form + submit + C->>C: Validate with Zod + C->>A: createContact(validatedData) + A->>A: requireAuth() - get userId + A->>D: INSERT INTO contacts + D->>A: New contact record + A->>R: revalidatePath('/dashboard/contacts') + R->>C: Trigger re-render + A->>C: Return success + C->>U: Show success toast + redirect +``` + +### Critical Dependencies + +**Internal**: +- Contact schemas from Phase 2 +- Auth utilities (`src/modules/auth/utils/server.ts` - requireAuth, getCurrentUser) +- DB connection (`src/db/index.ts` - getDb) + +**External**: +- `react-hook-form` - Form state management +- `zod` - Validation schemas +- `@hookform/resolvers/zod` - RHF + Zod integration +- shadcn/ui components: Card, Input, Textarea, Button, Form, Badge, Select + +**Configuration**: +- None - uses existing D1 binding from Phase 1 + +### Gotchas & Known Issues + +**Ownership Verification Critical**: +- UPDATE/DELETE must check `contact.userId === user.id` +- Without check, users can modify others' contacts (security vulnerability) +- Pattern: Fetch contact first, verify ownership, then mutate + +**Search Implementation**: +- D1 (SQLite) doesn't have full-text search +- Use `LIKE` queries for MVP: `WHERE firstName LIKE '%query%' OR lastName LIKE '%query%' OR email LIKE '%query%'` +- Case-insensitive: Use `COLLATE NOCASE` in SQL +- Performance: Acceptable for <1000 contacts per user + +**Many-to-Many Tag Queries**: +- Fetching contacts with tags requires LEFT JOIN on junction table +- Drizzle syntax is verbose - see examples in Drizzle docs +- Consider using `.with()` or raw SQL for complex queries + +**Tag Color Validation**: +- Store as hex string (#FF5733) +- Validate format with Zod regex: `z.string().regex(/^#[0-9A-Fa-f]{6}$/)` +- Provide color picker in UI (use shadcn/ui Popover + color grid) + +**Form Validation**: +- Email is optional but must be valid format if provided: `z.string().email().optional().or(z.literal(''))` +- Phone is optional and freeform (no format enforcement for MVP) +- At least one of firstName or lastName required + +### Tasks + +- [ ] Create actions directory and implement all 5 action files +- [ ] Create validation schemas for contact create/update (Zod) +- [ ] Implement getContacts with search and filter logic (LIKE queries + LEFT JOIN for tags) +- [ ] Implement tag CRUD actions (create, list, assign to contact, remove from contact) +- [ ] Create ContactForm component with React Hook Form + Zod validation +- [ ] Create ContactCard component with delete button and tag badges +- [ ] Create /dashboard/contacts page with search input and tag filter dropdown +- [ ] Create /dashboard/contacts/new page with ContactForm +- [ ] Create /dashboard/contacts/[id]/edit page with ContactForm (pre-filled) +- [ ] Add navigation link in dashboard layout +- [ ] Test all CRUD operations manually + +### Verification Criteria + +- [ ] Can create new contact with valid data (redirects to contacts list) +- [ ] Form validation catches invalid email format +- [ ] Can search contacts by firstName, lastName, email, company (case-insensitive) +- [ ] Can create new tags with name and color +- [ ] Can assign multiple tags to a contact +- [ ] Can remove tag from contact +- [ ] Can edit contact (form pre-fills with existing data) +- [ ] Can delete contact (shows confirmation, removes from list) +- [ ] Cannot edit/delete another user's contacts (403 error) +- [ ] Contact list updates immediately after create/update/delete (revalidation works) +- [ ] UI is responsive on mobile and desktop + +### Exit Criteria + +Complete contacts management system with CRUD, search, and tagging. Users can create, view, edit, delete, and organize contacts. All operations respect user ownership. Forms validate inputs properly. UI follows shadcn/ui design patterns. + +--- + +## Phase 4: Deals Module + +**Type**: UI + Server Actions +**Estimated**: 2 hours +**Files**: See File Map (7 files total) + +### Purpose + +Implement deals/pipeline management with CRUD operations, contact linking, and simple Kanban-style board view. + +### File Map + +- `src/modules/deals/actions/create-deal.action.ts` (new ~45 lines) + - **Purpose**: Server action to create deal + - **Key exports**: createDeal(data) + - **Dependencies**: deal.schema.ts, getDb(), requireAuth() + - **Returns**: Created deal with contact info + +- `src/modules/deals/actions/get-deals.action.ts` (new ~70 lines) + - **Purpose**: Server action to fetch deals with filters + - **Key exports**: getDeals(stage?, contactId?) + - **Dependencies**: deal.schema.ts, contact.schema.ts, getDb(), requireAuth() + - **Returns**: Array of deals with JOIN to contacts table + +- `src/modules/deals/actions/update-deal.action.ts` (new ~55 lines) + - **Purpose**: Server action to update deal (including stage changes) + - **Key exports**: updateDeal(id, data) + - **Dependencies**: deal.schema.ts, getDb(), requireAuth() + - **Returns**: Updated deal + +- `src/modules/deals/actions/delete-deal.action.ts` (new ~35 lines) + - **Purpose**: Server action to delete deal + - **Key exports**: deleteDeal(id) + - **Dependencies**: deal.schema.ts, getDb(), requireAuth() + - **Returns**: Success boolean + +- `src/modules/deals/components/deal-form.tsx` (new ~110 lines) + - **Purpose**: Form for create/edit deal + - **Key exports**: DealForm component + - **Dependencies**: React Hook Form, Zod, shadcn/ui (Input, Select, Textarea), getContacts action + - **Features**: Contact dropdown selector, currency input, date picker for expectedCloseDate + +- `src/modules/deals/components/deal-card.tsx` (new ~70 lines) + - **Purpose**: Display deal in board column + - **Key exports**: DealCard component + - **Dependencies**: shadcn/ui (Card, Badge), currency formatter + - **Used by**: Pipeline board + +- `src/app/dashboard/deals/page.tsx` (new ~100 lines) + - **Purpose**: Pipeline Kanban board view + - **Route**: /dashboard/deals + - **Dependencies**: getDeals action, DealCard + - **UI**: CSS Grid with 6 columns (one per stage) + +### Data Flow + +```mermaid +flowchart LR + A[Pipeline Board] --> B{Stage Columns} + B --> C[Prospecting] + B --> D[Qualification] + B --> E[Proposal] + B --> F[Negotiation] + B --> G[Closed Won] + B --> H[Closed Lost] + + C --> I[DealCard] + D --> I + E --> I + F --> I + G --> I + H --> I + + I --> J[Edit Click] + I --> K[Stage Dropdown] + + J --> L[DealForm] + K --> M[updateDeal Action] + M --> N[Revalidate Board] +``` + +### Critical Dependencies + +**Internal**: +- Deal schemas from Phase 2 +- Contact schemas (for foreign key and dropdown) +- Auth utilities (requireAuth, getCurrentUser) +- DB connection (getDb) + +**External**: +- `react-hook-form` + `zod` - Form validation +- shadcn/ui components: Card, Input, Select, Textarea, Button, Form, Badge +- Date picker: Consider shadcn/ui date picker or simple HTML date input for MVP + +**Configuration**: +- None - uses existing D1 binding + +### Gotchas & Known Issues + +**Contact Dropdown Performance**: +- Load all user's contacts for dropdown selector +- If user has >100 contacts, consider search/filter in dropdown (use shadcn/ui Combobox instead of Select) +- For MVP, simple Select is fine + +**Stage Enum Constraint**: +- Fixed stages defined in schema: `["Prospecting", "Qualification", "Proposal", "Negotiation", "Closed Won", "Closed Lost"]` +- Enforce in Zod validation AND database constraint +- Changing stage via dropdown updates deal immediately (no drag-drop for MVP) + +**Currency Formatting**: +- Store value as REAL in database (e.g., 5000.00) +- Store currency as TEXT (e.g., "USD", "AUD") +- Display formatted: `new Intl.NumberFormat('en-AU', { style: 'currency', currency: 'AUD' }).format(value)` +- For MVP, default to AUD (hardcode or make it user preference in Phase 2) + +**expectedCloseDate Handling**: +- Store as INTEGER unix timestamp +- Use HTML date input (returns YYYY-MM-DD string) +- Convert to timestamp: `new Date(dateString).getTime()` +- Display formatted: `new Date(timestamp).toLocaleDateString('en-AU')` + +**Board Layout Without Drag-Drop**: +- Use CSS Grid: `grid-template-columns: repeat(6, 1fr)` +- Each column filters deals by stage +- To change stage: Click deal → Edit form → Change stage dropdown → Save +- Phase 2 feature: Add drag-drop with `@dnd-kit/core` + +**Soft Delete for Deals**: +- Not implemented in MVP +- Hard delete is fine for learning project +- Phase 2: Add `deletedAt` column for soft delete + +### Tasks + +- [ ] Create actions directory and implement all 4 action files +- [ ] Create Zod schemas for deal create/update (validate currency, stage enum, dates) +- [ ] Implement getDeals with JOIN to contacts table (include contact name in results) +- [ ] Create DealForm component with contact Select dropdown (load from getContacts action) +- [ ] Add currency input field (number input + currency dropdown) +- [ ] Add expectedCloseDate field (HTML date input) +- [ ] Add stage Select dropdown with 6 options +- [ ] Create DealCard component with formatted currency, contact name, stage badge +- [ ] Create /dashboard/deals page with 6-column grid layout +- [ ] Filter deals by stage and render in appropriate column +- [ ] Create /dashboard/deals/new page with DealForm +- [ ] Create /dashboard/deals/[id]/edit page with DealForm (pre-filled) +- [ ] Add navigation link in dashboard layout +- [ ] Test all CRUD operations manually + +### Verification Criteria + +- [ ] Can create new deal with title, contact, value, currency, stage, expectedCloseDate +- [ ] Deal appears in correct stage column on board +- [ ] Can edit deal and change stage (moves to new column after save) +- [ ] Can delete deal (removes from board) +- [ ] Contact dropdown shows user's contacts only +- [ ] Currency displays formatted (e.g., "$5,000.00 AUD") +- [ ] expectedCloseDate displays formatted (e.g., "15/03/2025") +- [ ] Cannot edit/delete another user's deals (403 error) +- [ ] Board layout is responsive (stacks columns on mobile) +- [ ] Validation catches invalid data (negative value, invalid stage, etc.) + +### Exit Criteria + +Complete pipeline management system with CRUD and visual Kanban board. Users can create deals linked to contacts, track progress through stages, and view pipeline at a glance. All operations respect user ownership. + +--- + +## Phase 5: Dashboard Integration + +**Type**: UI +**Estimated**: 1 hour +**Files**: `src/app/dashboard/page.tsx`, `src/components/stat-card.tsx`, `src/modules/dashboard/actions/get-metrics.action.ts` + +### Purpose + +Enhance dashboard home page with CRM metrics and navigation links. Provides quick overview of contacts and deals. + +### File Map + +- `src/modules/dashboard/actions/get-metrics.action.ts` (new ~60 lines) + - **Purpose**: Server action to compute CRM metrics + - **Key exports**: getDashboardMetrics() + - **Dependencies**: contact.schema.ts, deal.schema.ts, getDb(), requireAuth() + - **Returns**: Object with totalContacts, activeDeals, pipelineValue, contactsByTag + +- `src/components/stat-card.tsx` (new ~40 lines) + - **Purpose**: Reusable metric display card + - **Key exports**: StatCard component + - **Dependencies**: shadcn/ui (Card) + - **Props**: title, value, icon, trend (optional) + +- `src/app/dashboard/page.tsx` (modify existing ~40 lines added) + - **Purpose**: Dashboard home page + - **Modifications**: Add CRM metrics grid, navigation cards + - **Dependencies**: getDashboardMetrics action, StatCard + +- `src/app/dashboard/layout.tsx` (modify existing ~10 lines added) + - **Purpose**: Dashboard sidebar navigation + - **Modifications**: Add links to /dashboard/contacts and /dashboard/deals + - **Dependencies**: None + +### Data Flow + +```mermaid +flowchart TB + A[Dashboard Page] --> B[getDashboardMetrics Action] + B --> C{Query D1} + C --> D[COUNT contacts by user] + C --> E[COUNT deals WHERE stage NOT IN closed] + C --> F[SUM deal values] + C --> G[GROUP contacts by tags] + + D --> H[Metrics Object] + E --> H + F --> H + G --> H + + H --> I[Render StatCards] + I --> J[Total Contacts] + I --> K[Active Deals] + I --> L[Pipeline Value] +``` + +### Critical Dependencies + +**Internal**: +- Contact and deal schemas +- Auth utilities (requireAuth) +- DB connection (getDb) + +**External**: +- shadcn/ui Card component +- Lucide icons for StatCard icons + +**Configuration**: +- None + +### Gotchas & Known Issues + +**Metric Computation Performance**: +- Use COUNT(*) for counts (fast) +- Use SUM(value) for pipeline value (fast) +- Avoid fetching all records then counting in JS (slow) +- Add indexes if queries are slow: `CREATE INDEX idx_deals_user_stage ON deals(userId, stage)` + +**Active Deals Definition**: +- Active = stage NOT IN ('Closed Won', 'Closed Lost') +- SQL: `SELECT COUNT(*) FROM deals WHERE userId = ? AND stage NOT IN ('Closed Won', 'Closed Lost')` + +**Pipeline Value Calculation**: +- Sum only active deals (exclude closed) +- Handle multiple currencies: For MVP, assume all AUD and sum directly +- Phase 2: Convert currencies to base currency using exchange rates + +**Dashboard Layout**: +- Use CSS Grid for metrics cards: `grid-template-columns: repeat(auto-fit, minmax(250px, 1fr))` +- Responsive: Cards wrap on mobile + +**Navigation Cards vs Sidebar**: +- For MVP, add simple navigation links in sidebar +- Phase 2: Add quick action cards (e.g., "Add Contact" button with icon) + +### Tasks + +- [ ] Create getDashboardMetrics action with COUNT and SUM queries +- [ ] Compute totalContacts (all contacts for user) +- [ ] Compute activeDeals (deals with stage not Closed Won/Lost) +- [ ] Compute pipelineValue (SUM of active deal values) +- [ ] Create StatCard component with props: title, value, icon +- [ ] Modify /dashboard page to fetch metrics and render 3 StatCards +- [ ] Add CSS Grid layout for metrics cards +- [ ] Modify dashboard layout to add navigation links (Contacts, Deals) +- [ ] Add Lucide icons to sidebar links (Users icon for Contacts, Briefcase icon for Deals) +- [ ] Test metrics update after creating/deleting contacts/deals + +### Verification Criteria + +- [ ] Dashboard shows correct total contacts count +- [ ] Dashboard shows correct active deals count +- [ ] Dashboard shows correct pipeline value (formatted currency) +- [ ] Metrics update immediately after CRUD operations (revalidation works) +- [ ] StatCards are responsive and wrap on mobile +- [ ] Navigation links work (click Contacts → /dashboard/contacts) +- [ ] Sidebar highlights current route +- [ ] Icons render correctly + +### Exit Criteria + +Dashboard provides useful CRM overview with metrics. Users can navigate to contacts and deals from dashboard. Metrics accurately reflect current data and update in real-time. + +--- + +## Phase 6: Testing & Documentation + +**Type**: Testing + Documentation +**Estimated**: 1 hour +**Files**: `src/lib/seed.ts`, `docs/DATABASE_SCHEMA.md` (update), `docs/TESTING.md` (new), `README.md` (update) + +### Purpose + +Create seed data for testing, verify all features work end-to-end, and document the CRM implementation. + +### File Map + +- `src/lib/seed.ts` (new ~100 lines) + - **Purpose**: Seed script to populate D1 with test data + - **Key exports**: seedCRM() function + - **Dependencies**: Contact/deal schemas, getDb() + - **Creates**: 10 contacts, 5 tags, 5 deals across all stages + +- `docs/TESTING.md` (new ~80 lines) + - **Purpose**: Test plan and manual testing checklist + - **Contains**: Feature checklist, edge cases, security tests + +- `docs/DATABASE_SCHEMA.md` (update existing) + - **Modifications**: Add CRM tables documentation from Phase 2 + - **Already created in Phase 2 planning** - just verify it's accurate + +- `README.md` (modify existing ~30 lines added) + - **Modifications**: Add CRM features section, setup instructions + - **Contains**: Feature list, screenshots (optional), usage guide + +### Seed Data + +Create realistic test data: + +**Contacts** (10 total): +- 3 with multiple tags +- 2 with deals +- 5 with just basic info +- Mix of complete and minimal profiles + +**Tags** (5 total): +- "Customer" (green) +- "Lead" (blue) +- "Partner" (purple) +- "Inactive" (gray) +- "VIP" (gold) + +**Deals** (5 total): +- 1 in Prospecting ($5,000) +- 1 in Qualification ($12,000) +- 1 in Proposal ($25,000) +- 1 in Closed Won ($50,000) +- 1 in Closed Lost ($8,000) + +### Testing Checklist + +**Contacts**: +- [ ] Create contact with all fields filled +- [ ] Create contact with only firstName +- [ ] Edit contact and change email +- [ ] Delete contact (verify deals set contactId to null) +- [ ] Search for contact by firstName +- [ ] Search for contact by email +- [ ] Search for contact by company (case-insensitive) +- [ ] Create tag and assign to contact +- [ ] Assign multiple tags to one contact +- [ ] Filter contacts by tag +- [ ] Remove tag from contact +- [ ] Delete tag (verify junction table cleaned up) + +**Deals**: +- [ ] Create deal linked to contact +- [ ] Create deal with no contact (contactId null) +- [ ] Edit deal and change stage (verify moves to correct column) +- [ ] Edit deal and change contact +- [ ] Delete deal +- [ ] View pipeline board (all 6 columns visible) +- [ ] Verify currency formatting displays correctly +- [ ] Verify expectedCloseDate displays formatted + +**Dashboard**: +- [ ] Metrics show correct counts +- [ ] Create contact → metric updates +- [ ] Delete deal → pipeline value updates +- [ ] Click navigation links (Contacts, Deals) + +**Security**: +- [ ] Cannot view another user's contacts (if multi-user test) +- [ ] Cannot edit another user's contacts +- [ ] Cannot delete another user's deals + +**UI/UX**: +- [ ] Forms validate before submit (invalid email caught) +- [ ] Success toasts appear after create/update/delete +- [ ] Responsive layout works on mobile (test at 375px width) +- [ ] No console errors in browser DevTools + +### Tasks + +- [ ] Create seed script in src/lib/seed.ts +- [ ] Add npm script to package.json: `"db:seed": "tsx src/lib/seed.ts"` +- [ ] Run seed script locally: `pnpm run db:seed` +- [ ] Verify seed data appears in dashboard +- [ ] Create TESTING.md with manual test checklist +- [ ] Run through entire test checklist, check off items +- [ ] Fix any bugs found during testing +- [ ] Update README.md with CRM features section +- [ ] Add setup instructions for new developers +- [ ] Verify docs/DATABASE_SCHEMA.md is complete +- [ ] Take screenshots of key pages (optional but helpful) +- [ ] Create git commit for all testing/docs changes + +### Verification Criteria + +- [ ] Seed script runs without errors +- [ ] All 10 contacts, 5 tags, 5 deals created +- [ ] All items in testing checklist pass +- [ ] No console errors during testing +- [ ] README.md accurately describes CRM features +- [ ] TESTING.md documents all test cases +- [ ] Documentation is ready for handoff/deployment + +### Exit Criteria + +All CRM features tested and verified working. Seed data available for demos. Documentation complete and accurate. Project ready for deployment or Phase 2 feature additions. + +--- + +## Notes + +### Testing Strategy + +**Per-phase verification** (inline): Each phase has verification criteria that must pass before moving to next phase. This catches issues early. + +**Final testing phase** (Phase 6): Comprehensive end-to-end testing with seed data. Ensures all features work together and no integration issues. + +### Deployment Strategy + +**Local development first** (all 6 phases): Build and test everything locally before deploying to Cloudflare. + +**Deploy when ready**: After Phase 6 verification passes: +1. Create production D1 database: `npx wrangler d1 create fullstack-crm` +2. Update wrangler.jsonc with production database ID +3. Run production migrations: `pnpm run db:migrate` +4. Deploy: `pnpm run build && npx wrangler deploy` +5. Set up GitHub Actions for future deployments (optional) + +### Context Management + +**Phases are sized for single sessions**: +- Phase 1-2: Quick setup (can do together) +- Phase 3: Largest phase (~2.5 hours), may need context clear mid-phase +- Phase 4-6: Moderate phases (can each fit in one session) + +**If context gets full mid-phase**: +- Use SESSION.md to track current task (see Session Handoff Protocol) +- Create git checkpoint commit +- Resume from SESSION.md after context clear + +### Phase 2 Feature Ideas + +After MVP is complete, consider adding: +- **Activity Timeline**: Log calls, meetings, emails on contacts +- **Avatar Uploads**: R2 storage for contact photos (reuse todos pattern) +- **Custom Deal Stages**: User-defined pipeline stages (requires new table) +- **Drag-and-Drop Kanban**: Use `@dnd-kit/core` for pipeline board +- **Advanced Search**: Full-text search with Vectorize (semantic search) +- **Email Integration**: Cloudflare Email Routing + Resend for sending +- **Export/Import**: CSV export of contacts/deals +- **Analytics Dashboard**: Charts with Recharts (conversion rates, pipeline trends) diff --git a/src/modules/todos/actions/create-todo.action.ts b/src/modules/todos/actions/create-todo.action.ts index 3024614..f306b5d 100644 --- a/src/modules/todos/actions/create-todo.action.ts +++ b/src/modules/todos/actions/create-todo.action.ts @@ -1,5 +1,6 @@ "use server"; +import { cookies } from "next/headers"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { getDb } from "@/db"; @@ -60,6 +61,12 @@ export async function createTodoAction(formData: FormData) { } else { // Log error but don't fail the todo creation console.error("Image upload failed:", uploadResult.error); + // Set a cookie to notify the client about the upload failure + const cookieStore = await cookies(); + cookieStore.set("todo-warning", "Image upload failed, but todo was created successfully", { + path: "/", + maxAge: 10, // 10 seconds - just enough for redirect + }); } } diff --git a/src/modules/todos/actions/update-todo.action.ts b/src/modules/todos/actions/update-todo.action.ts index 99e4c6a..1c29390 100644 --- a/src/modules/todos/actions/update-todo.action.ts +++ b/src/modules/todos/actions/update-todo.action.ts @@ -1,6 +1,7 @@ "use server"; import { and, eq } from "drizzle-orm"; +import { cookies } from "next/headers"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { getDb } from "@/db"; @@ -52,6 +53,12 @@ export async function updateTodoAction(todoId: number, formData: FormData) { imageAlt = validatedData.imageAlt || file.name; } else { console.error("Image upload failed:", uploadResult.error); + // Set a cookie to notify the client about the upload failure + const cookieStore = await cookies(); + cookieStore.set("todo-warning", "Image upload failed, but todo was updated successfully", { + path: "/", + maxAge: 10, // 10 seconds - just enough for redirect + }); } } diff --git a/src/modules/todos/components/warning-toast.tsx b/src/modules/todos/components/warning-toast.tsx new file mode 100644 index 0000000..06be804 --- /dev/null +++ b/src/modules/todos/components/warning-toast.tsx @@ -0,0 +1,21 @@ +"use client"; + +import { useEffect } from "react"; +import toast from "react-hot-toast"; + +interface WarningToastProps { + warning: string | null; +} + +export function WarningToast({ warning }: WarningToastProps) { + useEffect(() => { + if (warning) { + toast.error(warning, { + duration: 6000, // Show warning for 6 seconds + icon: "⚠️", + }); + } + }, [warning]); + + return null; // This component doesn't render anything +} diff --git a/src/modules/todos/todo-list.page.tsx b/src/modules/todos/todo-list.page.tsx index 0939b82..53bca45 100644 --- a/src/modules/todos/todo-list.page.tsx +++ b/src/modules/todos/todo-list.page.tsx @@ -1,15 +1,28 @@ import { Plus } from "lucide-react"; import Link from "next/link"; +import { cookies } from "next/headers"; import { Button } from "@/components/ui/button"; import getAllTodos from "@/modules/todos/actions/get-todos.action"; import { TodoCard } from "@/modules/todos/components/todo-card"; +import { WarningToast } from "@/modules/todos/components/warning-toast"; import todosRoutes from "./todos.route"; export default async function TodoListPage() { const todos = await getAllTodos(); + // Check for warning cookie + const cookieStore = await cookies(); + const warningCookie = cookieStore.get("todo-warning"); + const warning = warningCookie?.value || null; + + // Clear the cookie after reading + if (warningCookie) { + cookieStore.delete("todo-warning"); + } + return ( <> +

Todos

From 2f3811d7c102e0d4c4f6973c16167db6582cf8f7 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:47:40 +1100 Subject: [PATCH 15/34] feat: add loading indicator for image uploads in todo form - Add spinning loader icon to submit button during submission - Show specific text when image is being uploaded: - 'Creating with image...' for new todos with images - 'Updating with image...' for editing todos with images - Generic 'Creating...' / 'Updating...' when no image - Disable cancel button during submission - Visual feedback improves UX during potentially slow image uploads Users now see clear indication that image upload is in progress. --- src/modules/todos/components/todo-form.tsx | 43 ++++++++++++++++++---- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/modules/todos/components/todo-form.tsx b/src/modules/todos/components/todo-form.tsx index d26d514..e0a4c8a 100644 --- a/src/modules/todos/components/todo-form.tsx +++ b/src/modules/todos/components/todo-form.tsx @@ -499,17 +499,46 @@ export function TodoForm({ type="button" variant="outline" onClick={() => window.history.back()} + disabled={isPending} > Cancel
From ecb46992d77e788c2119831a288ba1c195906b8d Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 15:53:34 +1100 Subject: [PATCH 16/34] refactor: replace hard-coded colors with semantic theme colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - todo-card.tsx: - text-gray-500/400/600 → text-muted-foreground (with opacity variants) - text-blue-600 → text-primary (image badge) - text-red-600 → text-destructive (overdue indicators) - todo-list.page.tsx: - text-gray-600/500/400 → text-muted-foreground (with opacity variants) Benefits: - Theme-aware: Colors adapt to light/dark mode automatically - Consistent: Uses shadcn/ui semantic color system - Maintainable: Change theme by editing CSS variables - Professional: Avoids arbitrary hard-coded colors Note: Priority/status badge colors (green/yellow/red/etc) kept as-is since they're functional semantic indicators, not UI colors. --- src/modules/todos/components/todo-card.tsx | 10 +++++----- src/modules/todos/todo-list.page.tsx | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/modules/todos/components/todo-card.tsx b/src/modules/todos/components/todo-card.tsx index 561ba94..db45442 100644 --- a/src/modules/todos/components/todo-card.tsx +++ b/src/modules/todos/components/todo-card.tsx @@ -68,13 +68,13 @@ export function TodoCard({ todo }: TodoCardProps) {

{todo.title}

{todo.description && (

{todo.description}

@@ -120,7 +120,7 @@ export function TodoCard({ todo }: TodoCardProps) { {todo.categoryName} )} {todo.imageUrl && ( - + Image @@ -129,12 +129,12 @@ export function TodoCard({ todo }: TodoCardProps) { {todo.dueDate && (
Due: {formatDate(todo.dueDate)} {isOverdue && ( - + (Overdue) )} diff --git a/src/modules/todos/todo-list.page.tsx b/src/modules/todos/todo-list.page.tsx index 0939b82..e852a33 100644 --- a/src/modules/todos/todo-list.page.tsx +++ b/src/modules/todos/todo-list.page.tsx @@ -13,7 +13,7 @@ export default async function TodoListPage() {

Todos

-

+

Manage your tasks and stay organized

@@ -27,11 +27,11 @@ export default async function TodoListPage() { {todos.length === 0 ? (
-
📝
-

+
📝
+

No todos yet

-

+

Create your first todo to get started

From 12b8e2b4b56338512d4f25bb0e3f4f29b4970955 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 18:41:37 +1100 Subject: [PATCH 17/34] feat: implement dark mode with complete semantic color system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Install next-themes package - Create ThemeProvider component with localStorage persistence - Create ModeToggle cycling button (Light → Dark → System) - Update layout.tsx: - Wrap app with ThemeProvider - Add suppressHydrationWarning to html tag - Replace bg-gray-50 with bg-background - Update navigation.tsx: - Add ModeToggle button next to LogoutButton - Replace bg-white with bg-card - Remove hard-coded text colors **Complete semantic color audit (54 violations fixed):** - dashboard.page.tsx: Replace all gray/blue/green/purple with semantic - todo-card.tsx: text-muted-foreground, text-primary, text-destructive - todo-list.page.tsx: text-muted-foreground variants - delete-todo.tsx: text/bg-destructive for delete actions - todo-form.tsx: border-border, text-primary for upload UI - edit-todo.page.tsx: text-muted-foreground - new-todo.page.tsx: text-muted-foreground Priority/status badge colors kept as-is (semantic data visualization). Dark mode now fully functional with proper theme-aware colors. --- package.json | 1 + pnpm-lock.yaml | 14 +++++++ src/app/layout.tsx | 16 +++++-- src/components/mode-toggle.tsx | 44 ++++++++++++++++++++ src/components/navigation.tsx | 10 +++-- src/components/theme-provider.tsx | 8 ++++ src/modules/dashboard/dashboard.page.tsx | 24 +++++------ src/modules/todos/components/delete-todo.tsx | 4 +- src/modules/todos/components/todo-card.tsx | 10 ++--- src/modules/todos/components/todo-form.tsx | 8 ++-- src/modules/todos/edit-todo.page.tsx | 2 +- src/modules/todos/new-todo.page.tsx | 2 +- src/modules/todos/todo-list.page.tsx | 8 ++-- 13 files changed, 115 insertions(+), 36 deletions(-) create mode 100644 src/components/mode-toggle.tsx create mode 100644 src/components/theme-provider.tsx diff --git a/package.json b/package.json index 7c5f3e8..5f76957 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "drizzle-zod": "^0.8.3", "lucide-react": "^0.544.0", "next": "15.4.6", + "next-themes": "^0.4.6", "react": "19.1.0", "react-dom": "19.1.0", "react-hook-form": "^7.62.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11a3696..b14f415 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,6 +62,9 @@ importers: next: specifier: 15.4.6 version: 15.4.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: 19.1.0 version: 19.1.0 @@ -3220,6 +3223,12 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + next@15.4.6: resolution: {integrity: sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} @@ -7423,6 +7432,11 @@ snapshots: negotiator@1.0.0: {} + next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + next@15.4.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 15.4.6 diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c4406ed..6ae61e4 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { Toaster } from "react-hot-toast"; +import { ThemeProvider } from "@/components/theme-provider"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -27,12 +28,19 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + -
{children}
- + +
{children}
+ +
); diff --git a/src/components/mode-toggle.tsx b/src/components/mode-toggle.tsx new file mode 100644 index 0000000..eb4d819 --- /dev/null +++ b/src/components/mode-toggle.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { Moon, Sun, Monitor } from "lucide-react"; +import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; +import { Button } from "@/components/ui/button"; + +export function ModeToggle() { + const { theme, setTheme } = useTheme(); + const [mounted, setMounted] = useState(false); + + // Avoid hydration mismatch by only rendering after mount + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return ( + + ); + } + + const cycleTheme = () => { + if (theme === "light") { + setTheme("dark"); + } else if (theme === "dark") { + setTheme("system"); + } else { + setTheme("light"); + } + }; + + return ( + + ); +} diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx index a6a3b0f..04535c3 100644 --- a/src/components/navigation.tsx +++ b/src/components/navigation.tsx @@ -2,16 +2,17 @@ import { CheckSquare, Home } from "lucide-react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; import LogoutButton from "../modules/auth/components/logout-button"; +import { ModeToggle } from "@/components/mode-toggle"; export function Navigation() { return ( -
diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx new file mode 100644 index 0000000..fba8ad6 --- /dev/null +++ b/src/components/theme-provider.tsx @@ -0,0 +1,8 @@ +"use client"; + +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { type ThemeProviderProps } from "next-themes"; + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children}; +} diff --git a/src/modules/dashboard/dashboard.page.tsx b/src/modules/dashboard/dashboard.page.tsx index 204e4da..5e79f3b 100644 --- a/src/modules/dashboard/dashboard.page.tsx +++ b/src/modules/dashboard/dashboard.page.tsx @@ -13,10 +13,10 @@ export default async function Dashboard() { return (
-

+

Welcome to TodoApp

-

+

A simple and elegant todo application built with Next.js 15, TailwindCSS, and shadcn/ui components.

@@ -65,34 +65,34 @@ export default async function Dashboard() {
-

+

Features

-
- +
+

Task Management

-

+

Create, edit, and delete todos with ease

-
- +
+

Categories

-

+

Organize your todos with custom categories

-
- +
+

Rich Features

-

+

Priorities, due dates, images, and more

diff --git a/src/modules/todos/components/delete-todo.tsx b/src/modules/todos/components/delete-todo.tsx index 933ebca..910f0f0 100644 --- a/src/modules/todos/components/delete-todo.tsx +++ b/src/modules/todos/components/delete-todo.tsx @@ -50,7 +50,7 @@ export function DeleteTodo({ todoId }: DeleteTodoProps) { @@ -68,7 +68,7 @@ export function DeleteTodo({ todoId }: DeleteTodoProps) { {isPending ? "Deleting..." : "Delete"} diff --git a/src/modules/todos/components/todo-card.tsx b/src/modules/todos/components/todo-card.tsx index 561ba94..db45442 100644 --- a/src/modules/todos/components/todo-card.tsx +++ b/src/modules/todos/components/todo-card.tsx @@ -68,13 +68,13 @@ export function TodoCard({ todo }: TodoCardProps) {

{todo.title}

{todo.description && (

{todo.description}

@@ -120,7 +120,7 @@ export function TodoCard({ todo }: TodoCardProps) { {todo.categoryName} )} {todo.imageUrl && ( - + Image @@ -129,12 +129,12 @@ export function TodoCard({ todo }: TodoCardProps) { {todo.dueDate && (
Due: {formatDate(todo.dueDate)} {isOverdue && ( - + (Overdue) )} diff --git a/src/modules/todos/components/todo-form.tsx b/src/modules/todos/components/todo-form.tsx index d26d514..81e8eae 100644 --- a/src/modules/todos/components/todo-form.tsx +++ b/src/modules/todos/components/todo-form.tsx @@ -443,12 +443,12 @@ export function TodoForm({
) : ( -
- +
+
@@ -460,7 +460,7 @@ export function TodoForm({ onChange={handleImageChange} />
-

+

PNG, JPG up to 5MB

diff --git a/src/modules/todos/edit-todo.page.tsx b/src/modules/todos/edit-todo.page.tsx index 9b63f8d..c3644ed 100644 --- a/src/modules/todos/edit-todo.page.tsx +++ b/src/modules/todos/edit-todo.page.tsx @@ -39,7 +39,7 @@ export default async function EditTodoPage({ id }: EditTodoPageProps) {

Edit Todo

-

Update your task details

+

Update your task details

diff --git a/src/modules/todos/new-todo.page.tsx b/src/modules/todos/new-todo.page.tsx index 2f2c87e..ff8d3ec 100644 --- a/src/modules/todos/new-todo.page.tsx +++ b/src/modules/todos/new-todo.page.tsx @@ -20,7 +20,7 @@ export default async function NewTodoPage() {

Create New Todo

-

+

Add a new task to your todo list

diff --git a/src/modules/todos/todo-list.page.tsx b/src/modules/todos/todo-list.page.tsx index 0939b82..e852a33 100644 --- a/src/modules/todos/todo-list.page.tsx +++ b/src/modules/todos/todo-list.page.tsx @@ -13,7 +13,7 @@ export default async function TodoListPage() {

Todos

-

+

Manage your tasks and stay organized

@@ -27,11 +27,11 @@ export default async function TodoListPage() { {todos.length === 0 ? (
-
📝
-

+
📝
+

No todos yet

-

+

Create your first todo to get started

From 9f73848281c66caa04de15d9b824aaa753800431 Mon Sep 17 00:00:00 2001 From: Jez Date: Sat, 8 Nov 2025 19:16:28 +1100 Subject: [PATCH 18/34] refactor: use route constants and semantic colors in navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Import dashboardRoutes and todosRoutes for type-safe navigation - Replace hardcoded paths with route constants (dashboardRoutes.dashboard, todosRoutes.list) - Fix dark mode colors: bg-white → bg-card, remove text-gray-900 - Improves maintainability and dark mode support --- src/components/navigation.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx index 9595f27..fb71dbf 100644 --- a/src/components/navigation.tsx +++ b/src/components/navigation.tsx @@ -2,27 +2,29 @@ import { CheckSquare, Home } from "lucide-react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; import LogoutButton from "../modules/auth/components/logout-button"; +import dashboardRoutes from "@/modules/dashboard/dashboard.route"; +import todosRoutes from "@/modules/todos/todos.route"; export function Navigation() { return ( -
+ ); +} diff --git a/src/modules/layouts/components/app-sidebar.tsx b/src/modules/layouts/components/app-sidebar.tsx new file mode 100644 index 0000000..e08c755 --- /dev/null +++ b/src/modules/layouts/components/app-sidebar.tsx @@ -0,0 +1,102 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { Home, CheckSquare, type LucideIcon } from "lucide-react"; +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar"; +import dashboardRoutes from "@/modules/dashboard/dashboard.route"; +import todosRoutes from "@/modules/todos/todos.route"; +import type { NavGroup } from "./types"; + +// Navigation configuration +const navGroups: NavGroup[] = [ + { + title: "Main", + items: [ + { + title: "Dashboard", + url: dashboardRoutes.dashboard, + icon: Home, + }, + { + title: "Todos", + url: todosRoutes.list, + icon: CheckSquare, + }, + ], + }, +]; + +export function AppSidebar() { + const pathname = usePathname(); + + return ( + + + + + + +
+ +
+
+ TodoApp + Starter Kit +
+ +
+
+
+
+ + {navGroups.map((group) => ( + + {group.title && {group.title}} + + + {group.items.map((item) => { + const isActive = pathname === item.url; + const Icon = item.icon as LucideIcon; + + return ( + + + + {Icon && } + {item.title} + + + + ); + })} + + + + ))} + + + + + + + View on GitHub + + + + + +
+ ); +} diff --git a/src/modules/layouts/components/header.tsx b/src/modules/layouts/components/header.tsx new file mode 100644 index 0000000..8e139df --- /dev/null +++ b/src/modules/layouts/components/header.tsx @@ -0,0 +1,24 @@ +import { SidebarTrigger } from "@/components/ui/sidebar"; +import { Separator } from "@/components/ui/separator"; +import { ModeToggle } from "@/components/mode-toggle"; +import { UserNav } from "./user-nav"; +import type { AuthUser } from "@/modules/auth/models/user.model"; + +interface HeaderProps { + user: AuthUser; + title?: string; +} + +export function Header({ user, title }: HeaderProps) { + return ( +
+ + + {title &&

{title}

} +
+ + +
+
+ ); +} diff --git a/src/modules/layouts/components/types.ts b/src/modules/layouts/components/types.ts new file mode 100644 index 0000000..e7144be --- /dev/null +++ b/src/modules/layouts/components/types.ts @@ -0,0 +1,19 @@ +import type { LucideIcon } from "lucide-react"; + +export interface NavItem { + title: string; + url: string; + icon?: LucideIcon; + isActive?: boolean; + items?: NavSubItem[]; +} + +export interface NavSubItem { + title: string; + url: string; +} + +export interface NavGroup { + title?: string; + items: NavItem[]; +} diff --git a/src/modules/layouts/components/user-nav.tsx b/src/modules/layouts/components/user-nav.tsx new file mode 100644 index 0000000..f9371e7 --- /dev/null +++ b/src/modules/layouts/components/user-nav.tsx @@ -0,0 +1,71 @@ +import { User, LogOut, Settings } from "lucide-react"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { + Button} from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import LogoutButton from "@/modules/auth/components/logout-button"; +import type { AuthUser } from "@/modules/auth/models/user.model"; + +interface UserNavProps { + user: AuthUser; +} + +export function UserNav({ user }: UserNavProps) { + // Get user initials for avatar + const initials = user.name + ?.split(" ") + .map((n) => n[0]) + .join("") + .toUpperCase() + .slice(0, 2) || user.email.slice(0, 2).toUpperCase(); + + return ( + + + + + + +
+ {user.name && ( +

{user.name}

+ )} +

+ {user.email} +

+
+
+ + + + + Profile + + + + Settings + + + + + + + Log out + + +
+
+ ); +} diff --git a/src/modules/layouts/hybrid/hybrid.layout.tsx b/src/modules/layouts/hybrid/hybrid.layout.tsx new file mode 100644 index 0000000..f79381e --- /dev/null +++ b/src/modules/layouts/hybrid/hybrid.layout.tsx @@ -0,0 +1,48 @@ +import { redirect } from "next/navigation"; +import { cookies } from "next/headers"; +import Link from "next/link"; +import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; +import { ModeToggle } from "@/components/mode-toggle"; +import { getCurrentUser } from "@/modules/auth/utils/auth-utils"; +import authRoutes from "@/modules/auth/auth.route"; +import dashboardRoutes from "@/modules/dashboard/dashboard.route"; +import { AppSidebar } from "../components/app-sidebar"; +import { UserNav } from "../components/user-nav"; + +interface HybridLayoutProps { + children: React.ReactNode; +} + +export default async function HybridLayout({ children }: HybridLayoutProps) { + // Check authentication + const user = await getCurrentUser(); + if (!user) { + redirect(authRoutes.login); + } + + // Read sidebar state from cookie (server-side) + const cookieStore = await cookies(); + const sidebarState = cookieStore.get("sidebar_state"); + const defaultOpen = sidebarState?.value !== "false"; + + return ( + + + + {/* Top header bar */} +
+ + TodoApp + +
+ + +
+
+ + {/* Content area */} +
{children}
+
+
+ ); +} diff --git a/src/modules/layouts/marketing/marketing.layout.tsx b/src/modules/layouts/marketing/marketing.layout.tsx new file mode 100644 index 0000000..cada16b --- /dev/null +++ b/src/modules/layouts/marketing/marketing.layout.tsx @@ -0,0 +1,107 @@ +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { ModeToggle } from "@/components/mode-toggle"; +import authRoutes from "@/modules/auth/auth.route"; +import dashboardRoutes from "@/modules/dashboard/dashboard.route"; + +interface MarketingLayoutProps { + children: React.ReactNode; +} + +export default function MarketingLayout({ children }: MarketingLayoutProps) { + return ( +
+ {/* Marketing header */} +
+
+
+ + TodoApp + +
+ + + + + + + +
+
+
+
+ + {/* Full-width content */} +
{children}
+ + {/* Footer */} +
+
+
+
+

Product

+
    +
  • + + Features + +
  • +
  • + + Pricing + +
  • +
  • + + Dashboard + +
  • +
+
+
+

Company

+
    +
  • + + About + +
  • +
  • + + Blog + +
  • +
  • + + Contact + +
  • +
+
+
+

Legal

+
    +
  • + + Privacy + +
  • +
  • + + Terms + +
  • +
+
+
+
+ © {new Date().getFullYear()} TodoApp. Built with Next.js + + Cloudflare. +
+
+
+
+ ); +} diff --git a/src/modules/layouts/sidebar/README.md b/src/modules/layouts/sidebar/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/layouts/sidebar/sidebar.layout.tsx b/src/modules/layouts/sidebar/sidebar.layout.tsx new file mode 100644 index 0000000..e001b4b --- /dev/null +++ b/src/modules/layouts/sidebar/sidebar.layout.tsx @@ -0,0 +1,38 @@ +import { redirect } from "next/navigation"; +import { cookies } from "next/headers"; +import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; +import { getCurrentUser } from "@/modules/auth/utils/auth-utils"; +import authRoutes from "@/modules/auth/auth.route"; +import { AppSidebar } from "../components/app-sidebar"; +import { Header } from "../components/header"; + +interface SidebarLayoutProps { + children: React.ReactNode; + title?: string; +} + +export default async function SidebarLayout({ + children, + title, +}: SidebarLayoutProps) { + // Check authentication + const user = await getCurrentUser(); + if (!user) { + redirect(authRoutes.login); + } + + // Read sidebar state from cookie (server-side) + const cookieStore = await cookies(); + const sidebarState = cookieStore.get("sidebar_state"); + const defaultOpen = sidebarState?.value !== "false"; + + return ( + + + +
+
{children}
+ + + ); +} diff --git a/src/modules/layouts/top-nav/top-nav.layout.tsx b/src/modules/layouts/top-nav/top-nav.layout.tsx new file mode 100644 index 0000000..974c35b --- /dev/null +++ b/src/modules/layouts/top-nav/top-nav.layout.tsx @@ -0,0 +1,111 @@ +import { redirect } from "next/navigation"; +import Link from "next/link"; +import { Home, CheckSquare, Menu } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from "@/components/ui/sheet"; +import { ModeToggle } from "@/components/mode-toggle"; +import { getCurrentUser } from "@/modules/auth/utils/auth-utils"; +import authRoutes from "@/modules/auth/auth.route"; +import dashboardRoutes from "@/modules/dashboard/dashboard.route"; +import todosRoutes from "@/modules/todos/todos.route"; +import { UserNav } from "../components/user-nav"; + +interface TopNavLayoutProps { + children: React.ReactNode; +} + +export default async function TopNavLayout({ children }: TopNavLayoutProps) { + // Check authentication + const user = await getCurrentUser(); + if (!user) { + redirect(authRoutes.login); + } + + return ( +
+ + + {/* Full-width content */} +
{children}
+
+ ); +} From 47a5958c19af1cba62ce1fe10af8a35f15ed4958 Mon Sep 17 00:00:00 2001 From: Jez Date: Sun, 9 Nov 2025 07:59:49 +1100 Subject: [PATCH 30/34] fix: add account_id to wrangler.jsonc to resolve account selection error --- wrangler.jsonc | 1 + 1 file changed, 1 insertion(+) diff --git a/wrangler.jsonc b/wrangler.jsonc index 525553b..94eaa37 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -5,6 +5,7 @@ { "$schema": "node_modules/wrangler/config-schema.json", "name": "next-cf-app", + "account_id": "0460574641fdbb98159c98ebf593e2bd", "main": ".open-next/worker.js", "compatibility_date": "2025-03-01", "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], From 2daa95cb748ef02cd55010989fd1f21e13952868 Mon Sep 17 00:00:00 2001 From: Jez Date: Sun, 9 Nov 2025 12:10:02 +1100 Subject: [PATCH 31/34] feat: add layout demo pages with screenshots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add interactive demo pages for all 5 layout variants: - Sidebar layout demo (collapsible sidebar with keyboard shortcuts) - Top nav layout demo (horizontal navigation) - Hybrid layout demo (top header + sidebar) - Centered layout demo (max-width content) - Marketing layout demo (public landing page) Features: - Each demo showcases layout-specific features - Cross-navigation between all demos - Dashboard now includes "Layout Demos" card - Screenshots included for PR documentation Demo URLs: - /layout-demo/sidebar - /layout-demo/top-nav - /layout-demo/hybrid - /layout-demo/centered - /marketing-demo 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- screenshots/layout-centered.png | Bin 0 -> 137344 bytes screenshots/layout-hybrid.png | Bin 0 -> 106742 bytes screenshots/layout-marketing.png | Bin 0 -> 102830 bytes screenshots/layout-sidebar.png | Bin 0 -> 115324 bytes screenshots/layout-topnav.png | Bin 0 -> 96683 bytes .../(demo)/layout-demo/centered/layout.tsx | 9 + src/app/(demo)/layout-demo/centered/page.tsx | 99 +++++++++++ src/app/(demo)/layout-demo/hybrid/layout.tsx | 9 + src/app/(demo)/layout-demo/hybrid/page.tsx | 115 +++++++++++++ src/app/(demo)/layout-demo/sidebar/layout.tsx | 9 + src/app/(demo)/layout-demo/sidebar/page.tsx | 106 ++++++++++++ src/app/(demo)/layout-demo/top-nav/layout.tsx | 9 + src/app/(demo)/layout-demo/top-nav/page.tsx | 106 ++++++++++++ src/app/marketing-demo/layout.tsx | 9 + src/app/marketing-demo/page.tsx | 160 ++++++++++++++++++ src/modules/dashboard/dashboard.page.tsx | 24 ++- 16 files changed, 653 insertions(+), 2 deletions(-) create mode 100644 screenshots/layout-centered.png create mode 100644 screenshots/layout-hybrid.png create mode 100644 screenshots/layout-marketing.png create mode 100644 screenshots/layout-sidebar.png create mode 100644 screenshots/layout-topnav.png create mode 100644 src/app/(demo)/layout-demo/centered/layout.tsx create mode 100644 src/app/(demo)/layout-demo/centered/page.tsx create mode 100644 src/app/(demo)/layout-demo/hybrid/layout.tsx create mode 100644 src/app/(demo)/layout-demo/hybrid/page.tsx create mode 100644 src/app/(demo)/layout-demo/sidebar/layout.tsx create mode 100644 src/app/(demo)/layout-demo/sidebar/page.tsx create mode 100644 src/app/(demo)/layout-demo/top-nav/layout.tsx create mode 100644 src/app/(demo)/layout-demo/top-nav/page.tsx create mode 100644 src/app/marketing-demo/layout.tsx create mode 100644 src/app/marketing-demo/page.tsx diff --git a/screenshots/layout-centered.png b/screenshots/layout-centered.png new file mode 100644 index 0000000000000000000000000000000000000000..251c83a7f4110c294597a848a96c88e05c8e8355 GIT binary patch literal 137344 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%0DM!bO4ns3pHpXD zzF|x-=AUF(D%=m1eYd;%`xiW8;Q>+-Jrl*om;a(!cI$=fT<5

)*2Feek5U)b-7H z>F?JA5G6pT#y-F!E;or?$ZKD$_t+=8wyP(yD_z@C(Wg2tQ-)oW_k`R3-W0~v%cFn? zmjB{S&ydW?AhPH2GbE9;;FB+KHRxCG&g%Xt-Ww1+y&n!l*+$XYZR$yUIr3fe zO%t+UIOhYB<-UGwykS@WpkV7OQ6Itx-t_&s(t;FAw~hrD@=?0t$feG+Z(Zm?bi;^A z|C2N~VSFB?Q_QEKR>sLg*`89RD5gD(TE6~Ui%G}0#vicB?03%P()aW&t6RVyEwxj) JB30{K{{zSsjt&3- literal 0 HcmV?d00001 diff --git a/screenshots/layout-sidebar.png b/screenshots/layout-sidebar.png new file mode 100644 index 0000000000000000000000000000000000000000..103bf3a7f006bb4b86029cb374d5feae0a0be5e5 GIT binary patch literal 115324 zcmZ^Lby$?^`n4h|r7)y)4h$tN;h;#*AmShrf*>I%oicO!HLUHWau`?)5Ro!F9PQs5J zJMoO{6!<^dZRX6!j&U7Bsb0~;nl9CzOg2$tsW&zx3;0$;s zMDnjc0~E-ko*|3`YQ~X(oq4$wYn;-ME@GxJu&dq z7VQr#xikWL{v1k{CUAsc^ljb@;KlEwqe6%zn)T~y^a^bCY2?FzbEP`*u|2aQyXnrPod3*s#QhNC2 z^^)lSF(~#cVCcLrhVw{S9zjbbf>Ze)UnLlDmJaSz`?KG9{NZ-+fT@pY&Ch;g^B?nl zGTcjFE1i;ET9_M2C`D-WO}%D{8cwl(cK6k|aQJJnuu)rfl%7A+ccZ9Joz-Ld<&rT8 zyE^#E#@^xLKZcWK4BJ_*948JITTW5eAAyg%_)f)KbUbI1=0e0yMKk&D)RyKuV>U8; zR>(EjTvV|~2iuhCjh#|GY$0daS)Q2r?JDoR$t$(Ve0YH(=c|3Gw%k;d)b|CVC139h zScG@JXK!fB z+|-_{C?<{I$wC;>qxRze_<46R60Gv!k54*%!X+GymbA+hIv-+6h^p;UJxHlEXCgVf zOgG~Jhwoa0d931An@7R3*h>n?%}%Le(Lk>3frGtm>W9C6gcqj!AMQ?2`|qd})agFk z3d3Dot#PZ`8n&}E2<7y{4*DM**eO&-!>`jrXGe25_RrPdqj@hyOWn>x6^lX&TqIJ! z;H&jVhvN5ov)+CgxFryB%V!}cDdeWUQBJPPz-`abwKm?^Ab|`l4_JR>j^*WpAD`9+ z>JMc`z#2*T`K5dGlecpC*mp9AXAALGxpq6%A6)2^88E?o>kM(?)jM2v@{c5$$KSZd zT=CWMUD3GTUio$W(RTe&a4_R7M1FA#%f!pPjEI6Fv6?kyq`=mg&uVb(_SnUmjSs>T zn?09fP4SC`^?q_2ZM-MsFBIRnCJ#4p>ynW`a(qcmOARag@e=j_zmHjabUW*^RM3NUS4K|gE{Xdo2^g9UOw;Z?BFq2XjZ@W?J>2w!N(eX6uv1j(b+cW zeKV68vSlciO5yCB#mug}WD8DYN#*j0S7IPj0x0yKn_t%{_+66*i7uG4{m-pW)Zcos;#H?@j z@Td5w`={p3nqS{v2h^RWI!B`(bq>~wBlfkwk?&KbYqn6ne6;UYwL645h2*9C8FvEUF9**!;KPr!$9 zh;}=vPH`1_InUwyd58$zb&~`oQxHSFP(#=LFJb*R?)BD^(=(Th4quHtJL6c1s0OD- zZM{I4ihF&jw5R*wcQk>|i(Rl+#j(9hb{x6o(QjlE+<5k)2%B4{g!$UH225C*Q{67n zGFFjsT&zbhJd|f*gubBRG2K_&*KdgFo@2~p4rcCsYKe5#F8p&xFL1`5uHc%O8$5 z=U^RzXUu(i1{^W8S61Fsj@hoteOK^{k)I7Cn3=xjL*q`<#&%Giui~<=7_u%FKAiPX zZ3iLok~bgERoedOa(*-|$t7^nS}zzoR^O#Mx<=sRz)uI}Td9Olk^3x{8>nG6(%ibk zch8gW<$lLfj9<&Z{m}U7j6`1E`S!R5VJ{E>(pYiou~pj|0;=%Qwuh+t%B_#8YgrRL z{)cX&p~L$BT=utd?+wfzGW2A8$Gf*&SC8cwWzG{!=Vgd>Tt$5qMR$V}SiGZUtiB^T z$x#jaDg?jghendWj7x7t90n$tL$l(c=Q%goj2*h0)GTf{toUpGSq?Hf=r)~Wy7G$- zwRXkr5vdTrO!to{3Hmgm{Pc6H8(f0~t{(ZhwBjOd{j~QT@oRSHT z)zZEj;V{qC6KfI{ z{Y9S6G=|)28~VqxC=JjWf><+k?C@uyzsCoqsv~~)cT#TZ6tz`{->LJCm_} zv98QYhgdz*TarM^``mYTCP`w>ilVedg*Fyb!vG>n>i0pPrDBa8uoN84l&VAEmR^7Y zk7({=EXtfs6*a4)xlKxUm8P}H>l|eufA&;O1`-ak;+FXKh+FFb%mwzCjCp`BE=j2m ztSRf}b@O(35>>7D&xp(uQJ+uGw4B_mKRR%d`}z5X<+ab=Z!|GD0wyoh&l~&wW!|KX z?W>o$5f+y-A_P5`AAEISDyZEWmNTj0TJLx4Vll$=Wr}XLYH~M}=VQ)r?-C-B z8GsLI3J(V(A2rl3^+CDmI28-H9a0`s-aeJ=_~HIz0lr;fm{*EM+UETYv z*%lFkxnYHz*lz)STCR(4Dq~EaVAXgOtj`p0f%q1c_!i)w<()$abTk`ZkX2q-n`k`2 z!}mlLwV|h*&Z;9+(?#PfL;hJg>mCND82ciVLN=#pO1PkkFj-VFXc5`_&U1A>+oX^I zj+UHde=wLmDUSW-ytiE0y1jObzfzc5bgjfuI5vb}Rr~tZl8XlCL-(2%o)}&^1F8NU z9sgf9UU%`QSmDI&2aDnkpJCoy9{(M3KTSFoAE?@-dzx=SQcmtpY1akgeWp34j~f%> zSK=oLZ)&r@e!429ebub3)=^YnmCQ3Q42NAe53_M#Ht!snPtZ71Sif4gS0EI7OSSz~ zx`)eS^u*U0l~?#1C6S@a3#@phKEm|-fD^pr%1?@I307Wv-r|gK^kg(ZO`IA%8XWX- z8c*uHt~VsLoMN@VzPUyZw^Q%uK3%4Pe|~JZX0x01($W6rd}8-FQe^Ta7j+6Y?)mQ^ zS26Y2HJTvXakm~tiA3hUqI27{awYj7?MrBXJARt&y4(vMijmL4Xhs-u)Ik+RirV1E zJ%&-gszDbLtR{Sh&-#!7kaP5|H^}xz1Bc3c+r>yixk>EvTXNrDJR^p|cnSBKlc>-C z@MTV%!%si_s4(KY*(HHI$BjoOVoe3K<{A?4cv#Te&AXrK{g0fd<272+p0wA2EURQF z-(1*c)4}u?+Es}wB`}-CVSVett)2k5EW-3nTB>i-{lk|%w}_SNWUcSw*a-ALxjVf; z_K}!oU5|0g6>u~e&r5ZDi)E?Hmlqpb6Ug*+ec1(o3A#URac6=~xsv z`KDBbEt&}pkKX`P@S6Trx@!AItjsQUy;H2xe*F0tL#uc-v^D^!nq6-beN1t6C!Bt1 zov-E#@!M>UX@#qOS}VBPG7N=&iRRObYPZf3<-WBILP)-i);!IlIr5TS&|R(=+$3qw zL*iwuq|cyvXyV{joL2Iuzn1|@l|0TMZL7y>uJje51k$C|jXbQ@bqV&>mA0{8gweW| z0h*IrhTtHLyfA;aTw%PQv}SecXzQy}YrPw0S@DXF~br*MEkxa(>GMm$vJHM zdIck_D|aU>@%d9F8j;F(t92Je-Yu1^-~FhGXBwHHP*uw=z5pAW5R`%2x=qM!1|C@YA)3Q(@vR*Ik!ZtyG~_l&)A6!r#EyZha z2);RS)K|5To9fpZ!1g-dfa0eBwG_eHbjo+(U4mtamv-sHxVeq&P!7rQyQW1I34a6k zN&%$KBJURh_IOue5Jm@E(PHvXr z0%v3mUcAMAM1ml_?o<=G5#l4YZcPjSS{lb8{8yEFc+^y)Ojn5e%CM@2>}``u%?Aid zg{fvlT)7@q{HRyMMMSkCAAyR2AxCzU33tw#-I^YOACZ{`JJqbK(;DQ>b-e}a_ zsu47*bY0a_J~{i;t{}OGvB9lFv@*)ei#)#d^;sKjM(WFKG~;bPk=)Xg>{Xz!OC;{4NO@_|JX$AhUL#e@;@kcJB0EEF`h(%* z@gx$NwQTl`gr6YH6!P%!Pjw_*>UblngBr$JKSn+~M{;2-F35E=UEMz_)r*IQM~{aw zK{k=C-H;Xzvp3S&B3o3u_$#M~#%yK!Uh|$u!$~G8vZ`|g8}APhr{P|r_f>4^Gg?Zv zOWMo)W#!F&r#mRPmE7sKeFvFs(qO?FAN}H6FERM!>b!>VQoQ*->z z0FOjG_yi~LP1w$*dzwbQZmsfqMj}QbmWwcI>Mx@vFVhB9E1a{Y9QI*H5*2bR687zYe-W9KxHzrA=UmaM+r(5|1Y zKWJ75x!z=Y_*(@LaF#EQ0WN5z;(!NLe<1-8Udb14j>12GC=~cy>}U0@Np?sIaQOl^Mpvk&g1yf*!{;?5`9|A^&eA|_qInp zV{c|8Bu3)GV!~n_j6U2SdWvX2!L|XncwU*ML``6ygKNOeqQVTP34A?w^=rN(Fto7f zB6}p;x^F0x4<&CqSeUGQ8sFpy!q)ti@CLdoOcL}LoG0(wm*j6- zZ?)U+71D0~&iG7ozptgm0HSOJVOp6|lzwvxw;e0KW;H{Y24%XTP=26c zSeua}7B%TH@DzbsXHxzk;cB?yE;=LG6b ziuB5IaE4*;)w$_Np8;+lAD>rbg1@Sh1*!`6`WHCOoA>G8gyZN9uk19@(XPh|+z5+9 z25S)o57MFHfbZ=ywRS)tNQJ<7Gq_)w8|e^1tV2jwD=`=`L0wD22s*EgPj>$}x8{6P@aty5R@axi9qXE8cQQT)#P zN6D)tqIR)}6k=0HW2KSLt9@9&qn-3OTeltR>cswAhl1l!7W*E+K1+pM7_WL0;*Kkd zXAq;AxW`F+8y>-vbF;6d_6|BYInQaO57a5IvD7MfRvGHBD&AkOa8p-}AL8;ZGyLdu zTR}T0!1MULdJSnPxTYl>+l<;2w+5ftZdx$a#kaeO=#SsB z6>5ujolX=xHqjJ@Ky|*d380z%^>fjnCtEbeTqKAl;c+~Vbl5feBJLnYE%UdDP0AD= zId-gsK|r6dt2pc?1+@(I-gccMhQ1mIv$M-DR?pXO@re39E*NW}&;y7q#>mp%5WC~@ zr*?@GT0wF5>mDH-8|!@=CKxo$GkDZcv&GGf#``f6JR+v4L3rL4*ro0oV>hEV^5(O=0mYC2@${Y;J^}me?3KfQWfK z3Ya~KJl<1G8?rnj@rF-dEzW?%zy2iWTpy@d7=@!YZ>wiqt;4>w${wVmjtX?Z z`j*5ZuucA&A9ua#6~WpFEpR@&!xro~pSj6Rm}_SmUZIX;|GgB4lL$VI-eX+R5MN;y zSG^qdSxYY0xe&)D5uqA3rDdvupEkwMOMPc|UJa(}7J8kJtBllr*Kf=A`6C8GHgPT| zWL&4)Oe`Bu1Y;jKZoM{=!Qj#oM_#x|MRGsT6=afl#P=+e{efYZt@uG)B_%wKNVzOS z3Uji%C^nI<@oRLC zyz=c@{UAh&5TX3SshYK_JDYcMK%SSe23THrCQD>hKp2fPpw+pkTI|}vn1h;oDb24O zv^%L%)Tq02q5g(a2zqeZS(WQN@kWWvdEXT7qTIZPYYldEE{U8Xi-PUrL&$Klr+RO? zSqAQB@Zj>RN2(mB1fPm9Y`&DP12;;|Ij9pqCt0qe7W^T$CjyuFOP#buN`iG*>AdbC z|LWap?mAb;8slQXGP3DpwDUc&>1n>9y`{FX;@~jqd;2#60TFYo`}p%a9~5|KFd>*i zF6J1jkwxboy)p*OQf|Kl1I8=W4Ny{i{*1B@9hei9*12vy7`prV?E@a-1hYG+O7V{N z#*3+`PLtzbB^^Jm#WcJQPE|qL8H&@})jU{x^uhc4^lh?N005RWpW5Is9y>3L{>XV! zWQ|tH^Rehg+RvCTQ%^&o>oag%S8OdsWnUwXC{+E3_s;rmV`E&83$)VVk6KR96u_n} znzU$LVu>QyjEUm(SIm`+`duEBJ!n-0+&L+K;F6z4VD=8_CSN6Q(uIW6Syk9C8E;3IEv~nvmSO}0X6PYs}VNEww&%Ung)wZ)s zAk}D6EEvGbxjuxupEIZGxq+QvcuLjNFl3Xd$TdCLkX)BvI(Mf#?29k;T9eb5*Ie|j z0<(XAvcSzeql+6hlO7#R$VGLz-ER$#wYto(6T#=ks0{ieB)FXUdp_IOBljEV7>=j7 z6&aw=T@qz;msx`XlUVJ#PvrO?Ze~ifBGifV5QP~-S7?x5OW>PowM;nrOk>?}Ycnp| z{g@i|%)p+}54qo}@oz`8TmAeQ?Hn)M@6Z+`pOC%X+V8y<254e@Ej0nD<^Kb>?`FARRW+*CO+T zSqkyEr@E9GY-~yyeCzx zU`^_pFHy)#bn;D*#+flQw%Aa5Q~e-2TH;fpQAcY>Rh3Jg6B1r3`+bIBg&9lvGWg4H z6Oz~tWme8gHNB=@?Y2U)@pO8DW4&~?(T(cwNfiKBmJB5uZ!N|dxDkl#ZI&5<((J{= zktH6+SqY&^U6JFlEuXGk6rFVvVS>YG*U>-{IBl5}^b2DZV&debilTkOgXUjVbERCW znu?LY7Xc)-M-~(0WUWHM0N>0naQMy*LxPsAVGF*T#aMpZWkBNuV z=!0PRdvu1>t6^Mblu#ZdKNwo4-1QSc>rx%3?z6v!3c7}r6?}Ew@rx>oV)XjYd*9v% z6tA6brYg!u7%ilgcaB!jwm}T)<=?|Lv`9}w-^X4)Jm)s$85D0_G*2H0c>0ui=qw|? zdF0-H`16bT`$xxb>V${#O$?*jYeA|0Y@7E8r{bD)`+?w$=X1?wh*6KEz2gv$6RZ89 zOE0Stbq>RF!b_l?992vm@j2^y9`XrTL{)8c>(PRae?&EJ`|T74xNv%@&@qsK832lL zJoDRyfP^_EH`qLxc?mBm>ID-I`Xem+^R(jDzN#)b)zN*R3KZ+*aUPtp? z-Ftt6I_irSdEe~z!>u8KSzZ}dzYzDyCsfdR8K)*vzPBWHNe>3FTA4u6m)#(5R&+J z^Kr`})kIa4g5qL+DX}+O<)ZuDvkLQBMROTG59Z~UOh^<105;VzygP^Z4dZ}qP>erj zKJwh8so@RBGcu{OK3LQPJcAHgvh6enmW$1MK6~O|dtA&N5`f(3k>oDmBn50iC;qaE z&+nBObi~|kNS-q`xs@>Tew54M&7Y~d^_B5FI`{a9_u`w0y1f<3=_`Cala=Fs#-9d% z{aBzwScC7~3`#UGmA40~q71u}B{-W7U?)~+{@jTku1BrY|B?>#0_CvElME;adOt`p zY*cOUe)?z*Xm8(vh-<_t8;ko}rT%|^W*q>5@M}*m2VZX-_>0wbh^_3;B^89E+h$1x za`MmDKtcGxnuGhFYX%O51o(o;iF;zS_vFA}&lv4kbo|>3P-%76@V5jBEmN;F_&}<) zV0sv-Ei^(_F=MU&A^Wm{ot`utP9SB802`(vn)EpFk9kNlfQNE#T@&C9*au&_Sz40w z$1IdcnjtOvNnR}~%N&|er>^|dNS!SuqZ5?{ZmoZ%z6zb^?<>vM>ya-WMgQi{I>9?YOe@67N|{q^TtYn z{=c>K)ALUQKtKT>_YqHpsgGXlsEZvE9s!EBs9AV(Y^=(hYEkI<-DL_+Rsb*phpMErEbjAm2~#^Zt)PX#k(c64|RQ!GKfH;vS2fruiRVg@%@jssBDIiv@g@ zZIG|oA0y`qhyfz}5XNfRfMhfctb(i=SE#@rzj)6A%w4;t>Y6r*16Vb8|39-4U++z@ z2=5|l<`R{dK(t$$Jh}Gck5vfZxC#b!TCDEI>JWI`XY1ERpFelT04(AX`d5ZA$!%yk zC?|Yu|9psA01UiQ_R*)+YUrC*hA+DP`H%rLlJ5*_`?T{sU<$WM$3CI|3?el2@KI9Q z`E2NW=7|>_{`?+Uu3k}9Aou!8LUNe-&f0|eh)e78DMPkE(x(uMu~}Fw_2(UTHy{ZP z!`Y;J=wktIO(YjcqiF%O+eZ_iVt;Y|jL`@N`j^aA*-`g5l32j|{3Wthj6V3f@}b=Y zm9+pJ-cg(a_n!l5q(P|Skt{cJK!R+S<M7gg43qc520*Q)LAVsG>fTbmqz716J1XdbAHaWL(hX#q& z?@QX3f6+G|EHd}VaohPie#i*v%h=l8$w)EcT&6#V_x8$dgvGDfUoG`OytW<$6-eXm zT&5Yw+*kjcNGCKAPoOp;kghlU0D#aqz78Z8%7=Sf(Xz{c;x#b@+wn}c4~Tgz{D4() zZe}nDf<#CzE-N*ZDUj5ciHCcuoQkoIK=43VaJV$Vx@yjLYm=m3%eokjBa267eY>J6 z$)V?b`{kEepqlPn=QYv@!yO%N*EeP!{>b|%f9fpT3xv_4@{s922k0#wpsb2=y~5$Q zGa(@cW$P2N^JIwv>F&B!fFLA--5i}f#W5M=F~uB?H3Iai7ouGYRqz3b8qYPv@CiV$ zBvnwx?*qY~c+=oE1I_Xnpby@U*B@*dC;M)+Hv(r2-IH6kFN-EZSVJx!uE&bzZbB}d zyGgezn(eT61~$c&F(rrTu7KtZ0s3Is=LU#}tG!nKi;Syelf9);-tX7;DrY|s`F<&E zBK~n4wbipm3Dbr3hb*-zG+Z#wHD42;rVxQr%vsB>11vD}>tWY-KL{5#34H~tYJLuE z=CeoPgM&{HC}Y=~hf|zKWOP@>uOv)(D$vd=Kt+tNIso%w1=f(4r3x55w7O`VXx^(w zv)uGv1_}zb@S8H-@&do@I)YVK8~QY`s`V6njJL_~vfjVS$BTQkDy99;Wmdjdi9p@^ z8FO*vT-7cit7#l;&KeMy|8hf!RDBDScpmSYaB;cp#!iRXA#J~*W%MJib>M+&-N$`V zZI8Y53lWi;Waj5J|K)X(9Y}igs#!n+4m|^*`jvQylFr#LUC0_R^H)p*>Qhb{&M3Ba zg#D=g3)6drKR83>7E}6s3HUr0ms5yLAl-$ zO`|R3LPrcixIjJyq#NBeIdgjWEn)WrSM+zmq5afe<_Bj22&(NM9?0l-TA$cC+R|EAutCw(q?1{Z7iH+WfpC;zP3_NuG!glo1tddrm3-DK|W-iSP46RhLOFqx3 zhd}u88(`$na|u1;6@xl138ehcoFr3)4M^#{bypJjkAnK1X|!(3$55=#w4A60PNa7~ zBrQMa-vy4Iq2cznMs&vA-#0o=Ne4>&3Ho2F(OfFfnnMv02<~Pt=YY0%KE%dgBhAt7 z_(m9xzKQ$tGB6t?`-lQc6I}COMS!Lb0?xIHFLi74$ld7#OET~N4*+63=5BS=uj;CR zynsZI3cgq~k)@G6oSEdGoCABxda|t|s-`}y^-De(hE~jT_Un^kS3Vrj-gje zVMW+U$RSd{XuDE97h~aG0_vWN`@q46IM6;`0c?-w*+A@Yht13$oV^7f{NOFKvPezB!mEMwT9R8 zt3##90oPTYth;aYF*wUu`PpQ~Nr&&T^o;ZeudK1!mkxKPL`ij3hoOWyoZXk|1U~U* zxB%>`+0ie9K@f3YXHas-?OHYNCmC9VuYq`nkEsQmj>^&Df$@)<=S?>*EtjbG!6U?G zB#;=$V`dm9Y6omx^F!*t2P-A0>{_&$i>JMJ51N4zf{C6k8F(izMVJTOG}uhb=SPpZ zc8Hn-Cx&B#Z{kgTaxBZ%@>kh!q8dBWJomv~plamExm!BKnX`wX7@B-1keqNmq>?kx zOfsm#9^qMykOxVkx#%{r&L-JBWNjjpvt`Ydw9E=?j5A1Do5<%T9dl`A$GlH>{UB(W zyhVL^pJvnb<7w`hcADWkDfuRND8^aFm?`%PB2?w%xH};yXfs>p3~Ln`#`W_Hj6~>~ zMWiTWtz2HRMFf_G_zAKuF=qb^GNr@=XDkvx-8U8D2P z3W~8%2>v*k4EaX$L9`3-uvj+i$v?xcAiY#QmmzApNQS}1t&3KUl(6|IH4XfOtN9@f zPJp6Ab9y1^;bjuF2;B5p6W{}D1hytjyx1-bMh`;5IAZu_i7kPT@DVuMt}D<7%z?{) zblU(s#!bO5%mnYK6@@iG{z}FJnrqR6DO7#5S454f5$z*wx07WyUo_Tc;L!rA3R&*caj_*VsaqJ$S4d0`7#qxuW&kULj7x~*nb zH@&^8E_!Ls0VgDuOj%^|{4Q?-_eTzo&lu5=V6yNzTgj#yTaFhBEdh*}A+a5$oC)|X zYXJTSGAGlC5N^aNDUwFe!{smYuvzqg5OWSOf@xnR`_)fpEQ}Y8XhxN$;SBxv5 zq$U7bI2y%wG;2ppTz7{TN;tKoQkiePT`Ukj7`;c}JE7sO>f5yDd;pT_0Lf`DcqvD& zQD6Kn@q~`9{nn68Y(`f5ok+P1s`w{viG^YW#GKb=m#mgU`wcY0Q&O$H=H2IiR!sk4 zC6pTJ5V!DGm0qxECreE7alcEnfBmK+V%6r2C}v|JFEfUFpq&O!=2Tz>Xe^-As&Y+V zWG2i4ht;iOP9)n)mT2y*4IiPmI0O9iu(QGbC378&_a;0@C6V;)c5O)50?jCd`3xIU zZTs8FLVFmS7YYb`W3fS)M%F^LR{?alV#uyGyB&6~BrTC#H=GaSjX~7nf4Ymz#S93J z<+ZmRgwLnv%Q)Q{%R5JH{;U1PK6L4m4)yW*JH=~izhycll3=JZrOIJPWfhaT;}I!2 zYY-3vrXJY{e0zsPZEP!NhJX7oExe;KV*Q@o8xl`r!Zfh5=}Y99Wh6eMm6qo$t|9O_ zb;DX6B60$#Bl!qs9v)wp`Fskm=9K1lVhBuZb7W01v_FK!GpH>mL>ZIO~ ze;m5+kY}6e0yR!)if*0?BZ*N?dmcx%%btMA-sL>n(O6i$rHUHXWy{5t!_lst)~OEq z1CtG}buurl4}x)<#!Y{^Do+pVU#q%0(IgJ#aO0@Z{V(OcI0D3TAVZyTnqIx@xh2W{j$5M#3g3mX5RjAAF7(^nBOvOVd zdXPS5hm zSPu}M5^y85sc_mdU-TrTQSvz5brY7x!4nCK9Lj z;c(IH=oSkzM*zJ9svkgbM|C2$HH!I$?NR2@zJN$+r(}KVePiHZyF4Anw+eqaVBw{P z67t~$?#i4317nAB-05#%cpNH~2{&nbS%CdJLEH-~nFqqV6c>MzF$YMWPfT3fb+73X zN7semtNbBzJZiRU)}6O}C`L!zKWUps%k(2Wl0@4YBd5i?BHC(zBELU(`H1-eD5`Zc zXU3Atm*l_xqfozh0R;7~3PQIAi99*IAbRq6#hCYE@ZrIp0dfx$rIln$6c*3$xg=o+L=j>T-L*jr zQ!mwafNex5?!VNPW;X%0=bW@BL40(?jQq6Ig|a|zM7C$9MDZxoSHIN1TZ_x%dl(n$ z4yvYts>}<6cOF++&!{IO7c)iq^-QTkEk8sqa5?h0TDPKO+U>mq8XliMjg%+_lT~f& z)c?_v@B0A)q=-^W0mSr_X1# zmMOB7>#Fr~Am#b6>Jte!f%Es3>R=~hC-<~hszXIMcMnz(S4!K{WPf6tbpT2Z=h@ZQ z8G}AZKLJ&=tO#J{MSnFXxF={|2*3F%g?j2Q$DM=1MPJt9vl0bQmjUd@#08B*<_+6A zV1){XnEjR0HS91f%(nFA5P*^9r!zNfXWV1Xb@{!t((@PxvoWqw8{7sdg*9@f=t1R} zw_W6RC}?2W0`)IrA@?KRAe-pjd?@K&=J%nR_yKuwU!uIOp;YfSt>lM_aU}ofxO7g& zR`17qRX(_h$v>s81VE;2J^%8cfVPbWtSS=19pK2`pfgsz8kD2A7X0{iIIfKQG*|U8 zyJA-e0$x7cSp%IHiUzWY#5iEdwq@tG!1SJ4m$ai-%Py?xA0k+dxl9~{a>@JdtU2#Y zgw837RM=_br)xJXq-*p!w#*i+X2yyf&X5JL#M3rN+Cc@mq2KPz2`|mfTL4M8Oyl_~ zt&H^rx2?zAhX3L1S9%6mF#FGmZX>k!1fUWN)G@Lla2v&QUs+g!b;us4Tj5Z*q&s-g zuE)&ZVFA4{>RC;uM+=u<12joYAf`X^jc#bz&7u#C@-Nl!d}-&3DJ0&EKt_2b#70{@ zuUl8nyGYzp0EkOD7WX{t=HzC1!F8*U$UrB0 zx-3Xx902ACfcwrsjVx4Oa$itYZ{`A|XsBr&%%D#eRBzoFU#ikAjCQ3m?*g4Qw|_YE zQaz362@4I1qC2mJ5aAxBCmP+)NxTJrJP&E&L7-?Bn|-$ruD_+}m6ofAmnl-O?^^+% zW%R`7cF0V#0?s8u8dTYK5}e$V0&e{DY2pu5+ul?R3+YEW3Wsu$uqZDAC#;3}xnJCu zd6kXE>mHs+Y_S1%Nsbe_UGk-|!|fWa?I8en{Z({_N3)(lgI;}gcWyz%3!PiBJ?`&c z1*%Dlor^vv&IdU`t}%(zh>4N41~+NrrM!I;tgtS(Vi!3_(RrpaV^&@t*m}yt-GqYhB{q8rIQp91UojH|xhL|er{HgyhdMbWVUr~5k(Q$e~IZ!%iP{S5;jXB2qw=8(x#p$fldHmTyjHjoDYZ~ZMzs@ zU@uw287ntQcK;vy6{kc82xj|0HFpZy7f?5&;%8*;Gcp>YAAlJe)aV-1^@^GwAGNej zhJ#}H>3ndcZ2rGgfWQ5Cv<{$d*9Ur;HM7ox?e{R>%sR`XRL};RPx8Nvk-G>S<^J)R zt}9t8YN&`SaXyhqf-ah946uc=8YjI3o1dwmtTJVC1CsitLk0?r>OW&{GVf2%W}rx zUX=m9BWCw~H{byfgk@0lnuGEz{O0w&9}Az}ka0M_KVGc6;Vxp<VRad_gA66x=x3| zbQcc))FmOoh@Kx^2k}Nz(XwZ6T$NCxY7l1PfLPojKoNvMJnaa$#pWPlbSgqkTe1;x z(`>x?t014A6-aYdPPQ*;Cjz46)A`i~TY_czwwn%t9k5|vkN2n;1H82``~Ue}9&{b( zXETTH?r!~g;AHNtbQ+J?1PZnj@xoVox(vW-{?&E~woPE51_lQ_!QQ+>$`W93BTl;0 zC;(i!y0ZaD5*9?*Uo0^ zkqZ~rxK@Gg&@*bq~@M!UB0UVyHkm_N4zhEUBtM+6fG3 zA`wAekonmqD3JHmB>J$0?MsvTHZ(C+)x27@oKW)uZ3q!2;v;nCU(RjVWBFw_Wnd8 zSO?nRHmE@_CVV^ZsqL%5zvwS(5U=Fw&d834FV!C%_HEK}<6MxSy+Oi;Z(;$Y6o-7t zJwW1WxVVA-KI)@i1NB5eEaxCnY)S|DWr0*R^1}WP)g36v4G@oC2>`R4NDW^xsDOkh zY_V_)>@^O+y<)8!e(uiOiLYNCgsl!hA}xn4$N!S*V5ADF_@+18{5As2hOm zLO!M%n0AW+%V!`G?p-MP_i#3L76ab&Vn{+I=qKE`wsT=D32STN zjjpf^^||*N5OSkz|HwGd>bWxnguvkKLv6s8o!yIN)71?5FaR_zKl)#7t6``47|LGW ziLMG|f;W5Cfrje&UCl#v)kaVWgo4Jc8Ctktn##3K5MJa>eAk;LMD_q8if+CyH_jc( zM!R``GR14Te4wS6J7nXMDM4>nJ^48qb;i@_*Y?_YD_%Ar7)^jiiwKA06PBic3l?$z zbTbpjuLwGR63*v;`qR)MeGHPCC~CiTc>;pERnUNPK%GieQ}tViC6s5_BX|9hbgZco-C89fjB!RVS|DJkD| z=+zD0?OthrJf=jl3~>#nfgEB1r{KNpYVDl<`x0RLnX=95{o-wR$8QICFAWyEuuZ>o z=z7~I$INQFSdPt_OEESO-%cy1%c^39QB0#93yU)s>_Et{FBk3(E&q~RJN0622P8sK zU(n=ZjzjUkoO6zCM`g7gJh|5XGt3V_kOVh+#b?aBp9RY_T{Qly2uQPn!CD(*=6D0r zNZ&p~>_QR_;X2eio2IrqE}z$5U`_vxkk!whYEQeDJ;>k>$pCM*zV=OtaNfuX*kAQO ziac3@C`+v5($A;{FXV{*1YRC@WfdyZ9i_T%Pei zh4yu*8BSSzIGmIv7V^O|d|w5}W(}M~)xxtuH4v=GubzGC;LU8^?r|Ek0SM7(i;sZP zXA9km_21{cdTDq1wh;PB?8}diuQl+UQkCk%Lr}KcuE$)r?xGP)7;x~JT?Z}lEs7ZJ z>2FfbAZe&xae}lZ;=FxnSAWKhxl3DsOC*B~dz3JEtityfMF8Mb3ZiH4C-|(LyfDoY zh`9Y6u?uMFP|)xw9R<)I#UVByGw1{Xql|~3F!%fB9T6(%RSJt^G{sI7fnNF$a{lBj z>34Po)%?ByZkU6bGcx1F%ob1qT0$wHA_54TG8E5seGSxBDy0>gD(~l|SKJ1S=M#D} z6Hrf4?26Fq6l`$-(b8rX_U{`beHJWOi;=K~P=GR2M(~i{2EFl^vI*<-UKFyqDrFpM z7gwdz#@j88UcrlS-s6}kc%}&7e18K^H38{)TB7B8kYiwzt7CK)qAVsS!AJUMC3w}N z$jtl?JboB1*$eCPec+Xwz5i*5O3n=Ii}EC7Z-B%)NYhj;Gze-2W!7bT0ID$97CUDk zfSiSTr1)ED(gfO8DLw^XVV zxzZLa4;ow%cP8&w0~Ev%o!fgDG2#R|NyMTFR@ue_-Tw}f^VT3=y&DRYVC&ZN;)Hm?Vo`nlXn@ZV7IUWpg(nj9F~o*by64?)waR|65bd`;o)dJAhK z)P$+nU)3Pso3u%T&Y)^|^Z+P-50t3>!h~3*z8KdY&BNSBR9hf*H7oN38Q`|5T#mn< z7DO4&L2pW^%_r;l>lFhntWG1Z1`w9H2awM~<|i|cuxv{oz8kt z{yT$YsUEU_R-@@DFqa6-#%r~B7iruZA5u?sG-8rXEC@T#Dp@DSvAf+sXFiVE0MSMGXGhhL*vu)N7@e)q%lq7H zpb>h#2e@mbwt8$%>R9v#u8o&5I&{6>N0Rjh{eZ^ea=8oauS;e_!1!i9a}+1=h3?2_ zhtcivDC#}p-JDZ2e2(-tDo(l}K9^0JDKJb|V6u|)A>&Ry)RZ{FN-0vL1PH}5w|vij zA2rJt-n|Ht#QJBfg501 znia{VwrCxIW94!=;tt-X{%$KVxkq6!e+{tk&GZz$A*h#e3=Z;~n=`k%f5tB|W!iLS zx}r-8_MO?EWzr9KW$=0m`$S4}FO32SS|c6g+Q8;#Vs^hL19ecgwZ^kr{vA~Clt;Tg z{*yAHlSp5`s;!(JX~Ko6IhHdCP#3MU_N}{#H*bmDE?;!7Mhs;r-gfBOsrye7owo;E zLb~?ICMqlSFB%WzgQ;j~`I=$LjRWo=d!VXfNLBTegLs!Df+c5R@ajJGFl8EF5GW?k z99}uiPVdy8K%?|x21KB9N9y?#Bhz2CNamql$ggfvS}}4#CDI`PEKfWo)0FC#v!vIP z7nrE|5uO;E%4+6H={glX$6+~hip~5EV0{nS`G+TFG!C^@X-Fl5g80M}V;6uimpsYD z6~%UWlTR-r)qVDG^xV6A8Z83A^{bB8OJ%at2FDHu8*QXG>QG4P%)Al9R4(3nhd=_dqmC>X`F-2`U_%~& z_u~D=kHPiO4bT;snW(@VCmQ@FvrkW~FFl-X9<$=K0J4*RsZO>2MoNBJX6aOq&l>`} zgdfC~9 zS^0F?pPro(t$IJ6i*SAd?+#*d>MS)U&P;qAc`+3n^eqXg(7x^kj31|!#Xf1fl~>;! ze&v;%#F^yy16EvpMJME3*u6mUK*Pj-vC*>9R_K*dwUIO;w-!SWh2A{OxU^0n$er3d z;gsvz!CE(Z>kxyTa0x$_KqZmvVYSLTRt63kmSzJs1?wnj)_oQH$$EgQF9}(scdF1T zQLbbhP!fH(B!ip_VVz;mqJoRLxf(89)K}F*(!iW(6c?e;SIu^snq!2=q#o(}?oE%k zaguq^G*qcwzsuH8AOnBYTOfE5Y@XPxT0W8L=nqE3eqlD49B!!;t zJo63^md|!t8q>B|>;jyhTsjKFnJ|@RPm}p$4P4(Szklrsf?b4jt4c5*o9k_5dK(9t zgM>t;w=aKqO*Wje@ZGpHW5`kWExV)f1$rO=xhH>d3_0)$ZP)OW*E%n)B(WvdS?36; z^nSx64`>D6+wMNwv914ctOvwEMqQZR@(q$RN&rmYShbs4?9V;e{Q+C3x}qifcHv>o zHq9jNdXfg7PcWjm{WszFhN}Dg6+1h&mF#4H{Wv4mFB)FGL{~G4yA8~)n6{?HGJTAs zPG%Zse37o^1^x@)bH6eLYrjaJpcOom<>)lLWdGR(`A~oN?I}7s!?7uyx z>)roj@5`g1?%(%IDoe@8nv!J(W66>|WEqS>Wt5!~vX?zui?R&aW*WOx8cHE+5pBp4 z5|z+u$&w{YDN_CJSD#PM*7NjkS^4gxso)byh}!qPPk*>dpT8aCk+--w;&oE1iU6eR z;1tI=V@79{4AS zXSTIN!z*(x&T^^fW{|+HXwL-*XO?GP`bKaGV)8f{y+%>*=iiECpHcjjt{L5`5Z_CP z+$_ZXw(8+%=3XxD#(^qqpvb+!0n;mT;rYd0K3N-w?p;e+yIE6nKqK}tHaF~JA}E<_ zwC^c7?4O&w>7PV-6eU%0xjF{Jlw_pze7_K*av>CYWpgK)%vC3FNp2GJCa)RL?w$LV zb51|ki)Wxe#GT79X+?)^S#yS$T^_bZSN&-J&Crr3pj*2geyjM~boj4g=>B~6f|o=tu5(s&g8kbZ zmIQbob9JH)7w9S5$cGSIZ?9;(DB|xM@$kOk*U1dp=oa;PDBgi=)z}bcAN(%b$US+K z%kfBY9IdbBSzBF%BRozq67|SNIdZ)QW(!&Sw)a-~(+0pa&D9=ABpTgmBgMGKXPmm% zleTX#v*(Q7o{%`#^>RB2>w6@xOUAkJF!?-W&su7Or{5#9rMZrE{J|5s(0EI(m6(d& zY~VV)6KeGdCDC@L$UlcisU+mjD%$gQUv}e;k*Btwrby>Md8Qv&GF--8cJi{sU&iXm z>8W__60()acPH}P2u*ww>V7gd@S$8>Z?uGSeyLvxD@#}uUd;5S>v%+8AS=uLVgGh9 z(C2;9{8}8bSu^6`c^CFpMnqOb5@&NZBWq5Z35hSt=Umc!KHhWn$n%QJ*QP@MLI*sD6c-h7;o7W$8xfB#s?_gNo0)s4 zwSSfrU|0eL8ompL%KKJ~#H4bX&Y29ZG~m!ET0X)|tiSHXgKNELR3_Mzvi_6lP(wvr zwyPe~JcWj@+#b3yHTkUIZoNseUz%3QU4ABXqj#SFZ1Wc=yPFJ zb;pb2>X5Zm&hFqh;>p#Ov2*dXkhSG^TX?c~hRWgbDz}y6o~?+sBvTn-2CZ{1i$gw( zbOVGI$0s604{fsb(2O#k4;7bD<>7VO1?weMd*YdPddMA&=b0ILG3R-$k}5sbNdacV zDB}`!`4Nq?dY8vfuN7t4OR+9g+n5_1m0&ej^p3yj^wC);sb7!GSUl=pfMv2^x2VIv z1FuDn-1-_+(D+zs$GN^!)QGkgj0)bEMmZ39;AE-(C+qm2w-;kn zow&BM`ZRc*k>4q`GW|y)rPWN`z;kTX5$S#lO^FG7+<{nCjH*9nWypx{Yi^Z93ME}6 z_h^Vx_gbfC372S+aYk&8{(A$OUQqnRqOjDf5jj!MFa=b|-%+ROfhIz{Xr%;g^?G(p51 zovUMRMR7!uabSaVitpFG&oVj5hbJwB%zFJRcCUWDcYT&JDf);f4^F_SO1t9O$-lqHue%5L{(nU7~YK0Xp7A;H9cJzw)EP;?+MPKes7=MkD3{^-6C!CGQC<91UJ zlSs0%(cWSe%ig`4HeBC+cuD^)_T9>%w-t%}=dT{Q`N_s;v$GFA&7w6#ZH1NgynS=^ zp|^dM-j+{iVwqsw*#BVZ$X_3x?j=&MI=+o|KetoXD@`bkG2>BxtM%L$=y)}2H3yy4 zX8rbV-sJecFN1}R9D7HNI=Z=oB!d|ABjYrE6O5wDJxmLUsj4P=ac$b_@kTx8qS2=`#QYW#Hsyb z>w1NHp}ZBUp?m!e-s?ot5?7kc&1YB_Z|T2Flu;wO!j^ z`MMot7iFuL{2^K-d{y*7zWP3{TK=7<*8Nc2#;jC-y0tc=Y4s}2b2G;Y_guGdzG{h# zd6#MDI0CLhEK2ga)`s*0=dmV>-B;L56~v;P8;$GjFy0dw+_g`cJ?x#@jmlB{QbJen3LX?3WEp``uY3%-c^@Jo;|ry(_gu+Tp&y)_PoxW6h!R^e>x57RrvE zD)mY%E$@eLPX!Hl$?mg-6Vuu6!Gy{Huz zk*dA>oGj@^Q1QV@O{{aW*pmPH0=;9T`8^)XP~ntb3qCh8pUWmj{d}YN!}Xqp)a!o< zLr5N1TaqwD(^-ocrju_6MyBs5U%&ctOK+(@%Tv)?K`kpL_cFOMa~_)2k?!>AmAo)> z54U`da|(M{^dxPVjTHX_V*U2@Eq&T$h@aqWi_ zwzUd5IAmaCyRuk~$?;mev&ZgVe^2~Vr)Tyb?cR;-U2THj4&Rd33bBrQveDCoC0uNi z)2pbC%MnG1YO&L5hpz8?<+pG1*QtopBN?-p_( zn!an8w1;U6l2huJ_80#8fIrSfhe;j!E+Z#hHnJ*-q3(eFzESKSAMp!lbYBN>Ok&xt zBr;PpG+AUe7jF3nZ4Y($Q_kv$?~TPXu{offZ~}Y57WKL$ z5AbWe91U)|IGM`FdRqdR!c%otAu7sA1Aoe2PVGNG0%sGNH=$hye9=E!2_K-H@c!8~ zv!8CCtP~NTD(&Ta$n(0j(+LABBGQ(y)VP0f?eDOTKL2ShJPPxbx1vlp3bbb-^{ zAL`-_p!#REviCFCcM2E}{=J|&ZvaSs>tR6F5iLO_*#@kx8bEJHF6#g%KF;q8H}CG# zatmE$(3(XJ)!B$>T=fP)q~FhfjR>574M34ijjI{71`o`={gbcy=Y#$4Q-K2W3333e69N$VkuP*cSI?RBc!IGe zctPWk@#_HCXt4pk&Dk2!;t}x_1RIv`bLIW!h{vH35su4;IG!Q;ZTaSF?h}WYL_`>AE3uxX>2Ad3?{Iyj6 zq$^SGL%+25*-7!cJ`^oqjfkx`d6*a)%WcIfcB|&>M)(QHY8{mI02zQUL#4ujydK!G zhy`m6)UrW-$t#FNZ7{27V@W3zHR}5VDuuuO17O8yaO2h3LtlH}ABbPct3!dxe_qnx zlgoV#BJ`EUKhph;s`NMm5gwQgxWrkXh;xUXVilLfNRj9fz`f4P0L5P)vW^CzFBU%P zHrS@UC4O(|AsBNk0UfIW!0CX!`0Ky80GoF~n~k5gOHy^d8~eBgdK!YEpElS27OO6l zu?A*H_VK2{Rca2r2r)oZ#V|ARwlES0bY_GfAtrVH=Zb+BeW80k9vX<|;{ayxPOnF? zeZ4OlyHx+z__kh_bP6a>8#u1l&5>jR_3w}fO^61Yz^@=$au3=}Ji)-d^hLhPJeJlX z2|`*L-gUE017LKWFX&nExas2Tr>kM&${J0LOQ-OIdz-!||wZ}?gPa6$!?3hfs zb~_OmYlHCDyq!;les$CSeE-2Z0b(odWfF1~Eb>nrJ!lsbi*Zs-2wD$EIyV1{XTA-@b(J=f#s=1>NI-6QK;`dtus<*4f;=qZ z=!=zNtV+D7xoInB^t%CoyEt0um06Drn3UnE6s=4%`B~S(*t9C<((l@G;)T_b zu&giZ$H5*Z>9`|Z=QHlHd7euRr_I!dX_FS}KS`Y@nZwpZ`mZeSe`B!6oGx=J?-=m{ zs90r5mRwt*m;0O;^SS$L*lv}#hx}UpxmP`Y%1mQvy*vIH*flplhG#`kU!Mg>fZO>w zIn1^adl-_LJex40XWPkONzuNJgamJpy+wvB3uEf7M zeXy>ao;bU)3mM~l8F+mhNDy(NxP!RQ^UK_LQz?>4>l*(}Tvt+cFkt?O&1Sdc!WscH zSk4{dE)w=y73=@2p@akR08By{0A?9@6hfE#sa{5VOxR5~%73STxF2Ae0$&_fntkO= z($DD+(Bu`&Ncxw_AH4cC=3KdHW~KOWPcMhSreE9)-!${#T9bg7t_zpWnI=B6nD5mJda(#ic$$34i zl)r=%ZDY@nY@uY|MHAOiuCY7J067lI#!1Er8}}~2@B@$3sp;CMNx#1zz}v{7Jzj~t zo-_7y`ElvOa^b8C7LCl+y&z<+7=2ibNZZY2J_r214Tx#jd2&SN_xDvR=N@8efF9ue zGil2q!;D5x9`R4~$pOLE@z3_?Nn)_Axcd)c+3f%AQFQEnix$E`Y%Z=~cBJT}gU3ge7478hh;y-#lda^zvAL`f0{zr3&}h%cOg-)QbGsOEd%%;LPppunk3 zi#S+4bCT^MCEw663%HTq+;6wl4AV-%Fd-Tkwc0`_z<4GG(F56{Sc2an(CUYoN*@*} zew;wm!49JwY`#F|k;fkbHSdFM7|d5NEz$1~*vRKM`P5=};@|-;TBVRiz$v;h%Ejsr6Yq;Kp|KdnGA z(Y$~V$YARMP7pw@8@E1Zt^H@#2Ud9SCYO8)RN)%ii*A6p4nie9evwx7H>G)TeIOF_ z>Uef*j5AJ--2j$i(`Gt%V;@K)e|olnZZWg>0bJhJX=p=A-tmFz`0m8bsvO$z@d&82 z4}LY^wZVodG1_!^K}j&H^{peDSQboJ=?-Y=F3gctA9UNzP}c5|rGhH_p;jDiPyoXqLj*vX5}ZLiM)9 zKNNMsk$Mn7cu#fh!am;4ss!F77c&u)HQpse3EtWpvb;yY`IQB^FWry5A29XD zV9myY7p_PhXdTR5pwjHChc{qLE=+3rqOmh7SnQz)OPZb6vzuo_zJN#o(bFVj4uE^o z>-;_L%#WeO!R3IQFuzRnAUR_cR21uWF3;%mYe$59jE{go#11yOk4!~gw8xfC!_w7i z2d;-X-7Kbg9EvtR{kGYM*t%~Zx{njx=JYPa(et0LwQ!`&N(1B1rQe9gcep2}UEBMDj&zF6ASIXP_TGd{@qrz)X?ZEyBpV(1#;_^84TvcYD-G>2e z-*`#Mt$nY)E}R=Jyqc*aMlvQRKxRDe3$7vtv(I6If@CMzX}yuk9lR95tRQe4iICz+ zqLOjzuv1x;M|uv*NTqh)gcaR4zqU|>ak0YKPrDN)6iU(uz-cV8)W zJtozAMxTy5!Rr%fK_ZDdQOnNAloMSnOGyD(c{PK@)I1irVJO2a?MxpNMxGbh+7lYMl3 z$!;VkH=kCt@I^zwfyx;xH9pNaYWHhH`ZHPL+yiARl+RhLO67FMla}p`gDAHaTY*CU z(PVL^If!sc_9+PjanV=ZO>(UK)DskSHZP0vY3D++G0FcmB3(vf^)P*^5NMm-jTmBr zZ8HJg*W<3bSb3>r>8h3Y?V{11o@OSjA*&^6^vcBEDRoxEoZ}8|E;vFoV3wfL>0~Nc zFfro=XZKK=2*c$gBM8{Es73Ha>DyE6O|Kei77*Qbv3zku_6nQ-^)hbJ8AIOP<(=AJ z39M=`IIX%tnJckM5Ll!O)u#iKg|zAsI%Yw|L=_6l8(%iGraj8}CbOYx6TVd)?8gqg zoXTcthvu7f&l{K$!>?|<2xv?_kkB?C9Wo@+jw5AW^WMWf=S(*Q4~uo3=N6U6TzP6R z{0=z5LTg|?*d5p$`E`vyVg)Kc$USPX4LH>d;UB2NREA49 zm&C1*p=dv}`7XK^^__0W+IzaLb^ zwFf3gKAMLMtG+YHZDHCa#pTVp9U0TXN{#~9SqyF2#U+>a@qIk-F9^^3Bsk&en8r26 z7yMWxae{6|-oN)JF66?mCOnk9&oFOFfaCA4X&?FL?hD?gQShr>Yw8aXFJM^Vqx_zF zgEr$I#WlbYlDOLA>%1eGH>JVxPO*<-{w0T}!>>$c!~Iy5R3ZU!*1 zQ}X9kK@i!a9dCyPy)V#RW(ozqX+4{C(c{Arpg$;-hZ#FDh-m%leWmC>ulUMNCQ|E? z;6^)aWQ@q;N%9<|XRLyUtDQ0c9JbREM`;az=rFYbY^`3u5AagIDC<)^5{8WL5NEs| z`25fOzaWq=7q$zaBJAL{oCT~r2JFzpCt-}>GkZ}X6wzWq8XznKVzYhcpJ3MVe!3}E z@+3=RqRgz=<1Sjde_iWfI!w~o`ky!yPmh=^Yp!3rHG&g)6UHCA4QRA;;4q@l_KPl| zDOU0TTtw$;gBfoPI?bZ}arc z)g4{BYkQotHLhH<{Ck}ixC(B{VfmI|bEYi@7t9&M5NHLFbk`T=L{wcsXd_Lq&Vsc(5Xg~6}S6&QfXD~wPA7eBBPbo$v#(I6SR!}c7-V`;8l7A!?i9P zVoDUnVN%dgDK!Y04S?JVef^4jS-@N(7R}2tG$gA{L;AZOO_eNsPS~2HHb@zOQ#y0v z5#;es@X{uG36Y6rhhDY5Iq^fQWJjwT{#iD$6*1})l)8zt)_Ln-U}qC=4r03+ewC? zUHECi2v(Hx$V{Q^72P}RXw`Et6l@v;2tN7P$*r1ALgAB=nc*MDFkrO!s@oE~-OC(x z?)+pt1og6JZT(wuVs9{p{31e~5oq{l10Z7DKfa-Hh)-uB*vfscy>hqk!}uvA@v4UY zRy{O(8sNrkd7-~|3=M+(aSW*XG2jiTQ8C!k5u_Ab>3V2`@Az9rXZ3I z-sbWu=L+m&7UoR`q@Nq9u#&b#doJT?CVCbqf>nJ|2fR>*($=FIPNA^@H{Ylj?K&9Zk6Kyl~ ziN{C=B<K>A~sw{u*2 z?rdFW2cKhz6L$nRL$Hg=qCmd5h~~v?Kq_xDvf_)hy*u&G;hjGA zU_t~JcM3hx2Jkj{p~&95db4@PE9LABaq(EvSr+8tiDfehuFHjDo84_s6?qQnlgL5P z*50yqPf3+YaR#~}1REnE^-n08$u63O`j+nc!Knh%d7+~5lsRW<-VplH9J zlR|OtZim|tw~YOi`|-zKDdSd>vw7iNLsIZyLx;)V0ZG7j@O=Z6+hCxOt|Mk_j2%W6 zqDi1e<2P@%jbg~(1>UsZlo2^-(HrE*3ElCuWIDF02BeDS?z2Zl1#k8p%I1%ZhaG+Js?9{EHnt>ddqmD^w6fUzWa z>aYBsPE|1X$DwQP!D7t><68Qo2rool(@b3)2vK}pfra$5H19il^kINl`u#P{X$-ftbK_wfD zJO~Pc0+u24ZhZ3OO!rO9LvQWIS;j*#MSLjQKV>+Zh5+Ifn%{^Ia|v<*J)~CB^R^qQ4)-wy6k>Pf_}0P+hRR0P z)^t$Wksl4yy!LCv*-dK?U*2sq;;LLK4>Q$(WdtxuG%uA?An>doM zqC9HR64=1pd#@@e7hv!!VVEjekHZMV*}R83~ZzhhuSFeey% z6|Y4E-<8fQ2MI5xSOS~G>}6o|r_G2~l3|-e^2&F~pLq8o4AD_wvk;#iNtAb8wYmFg z-|DR3@?3F`9%xF1n7$B?Pi}bx&IyX&)8$(1ml_JxSWL)QF^SL6{?IV}jG#}ee_i3$ z$?vBTGV@ka2y>j$G^vqPxmKNc!n#?Jr>E4VLeND_ks9bvUa+v$7}9T?1ou@Q%C;#m z0X03bALN|}c?`Z8j2&WsKC%fA8Nbc*aT2*|KY2Hic4?X4F`fpytVCgy zWn=#F2+)UuBR#pC*E1I!B_wg@R{in)eZX`aAJyhr@^Gx`6I=U>+B|9?FH?{AOyUG*8j zpra9M2Hfh3^SkrTSH0ocQBZLH_!2$=IAdEZYN~?OBT=(;88T6)7=c4qYGhHLR`V#7 z#w9fzyVpgAPa!KHq@`?}ML-9Fs*zWo6EwSI&Wr)(%zD;T(YWE@G7_sp)y`um;7Z>% z(SY!8#Pi!$dtA#g{68=5KjOxXP6Fbz)uXxW2yQQZ z>EHH%bmn-Eww3h$HUxemSE!$s0MJa45vl_gTojnC#Gr%|O^1!Ztgwt<6AA`Td5tS z=qB0|Z+CzLbu9G9YEAl|@8i3T7@v?U@(Y3FT19yP?(TX3KlUTXnhfBYzSwJJl=Jf0 z{B^M0GEaX$7oO2F(utIe>?dk_fet6N0W0(%lQi-Zh~C z)R%Zz0hw8a0g3lp(R;QRzQW~&P&p~Eu@)NHc$Yn>hay4S{_9*i>SHv3u2%5GOLOuZ zFv5}&;+lYLvAY*Q8@BKzD6Jda%MdstZ}=>26^EK|4misVKsLS3XiL!Es>?yWeo*OK zhCohe(sLK;=@@AETy7}Yzt=CE*ou05{)ey8QNNxZLL4@fh12)84tln4)iK@jfUUA% zRk|%P*?#~HY6eF2hSdr z+Mj^QJO(wP!OGo%?@_Z64fiYs!0f2Nt&-dm4n2Ny|LwPrt$_WM<?Y4aiOhzqJ&@`y5;D)4b*Ay7n4aF_-s{xK`c-I!QEg75M2*0_%Uhg_-ic zYcF0oN(e6lYLqNly`y$7jdt!;Zdi?DIMMjdIx)5d5W%mDh`i7L0bn@4N3(T90xie% zE0sGhl}I_L|G0Iv6PDc1HTW0<``;GbXBxQ2W-LnC^v%36(HP*UkfJ{^h_aV{Fw!tK zEw>Ic%xGb)wxQteaVX!@R{OdHQ~_gQG}<#AA6|Uslow5yFlMUMMR;~#EXA2ln>v%~ zZq5>~nH=urP%sK%sF&#>&@I%E^&-`Je*nWNv8=bh`&w3|^2S;?XuKVgP+>k{2J zbK@_HZX1PZoN@)I?#y7uiDZa;Wz+OKTFA14#h`mFVgmHOE^-+$K$8eKmYG}wFirG{ z>t#)w%7hw40-~zqJOrDC0}t%+!b{*FwhVoJ1GJF9MO`0sUyk#@oiW^6ey_8+sotI&-yF&3%FV zIH4;T7OtDlW7jt5Xb3jPhH8O5;A*l)#b-0^+gq9#f!&6p-^p0vAvNe@ukKd|%alfw zQtlOsDIY_YejwOTCE$dDV|(Y%upX0|->(N{u=nw`BaF6sg z1&jbrY7ya(c%e2eM-V31O#9jk5*{l7<0iHE3 z{ptaGr*o+tn!BGYLr+j)a6V0$AOGxS!?PR8eiu54hej{~F{2{iPKgaBFFriv$#@SB zVX@(x(6s_B&1=Yq#TPkdjqVIcrw5H34>oHhefB&4($X>XT`2(lbO$;5=<3g6&kHJD zzgIf$j`#!RUBmVI}&dkOV!!2(VZ9H_hBM(C4TYCW{Iep<}Y`HFl{`J zS-qTxfK8B8e{t^sE z;Ql)5zRC^8`kq31Zy}+Pv){2H?w&1`B_9C+)AFYK-mhRq930{RCJ`{D>1Ar>|f@%9#v7LDR(B-g!QU9{MRTr zc;RulI9|P!Jv-&zx3fm^&aX4hRM{-5m*h9H-H|c=17JTPaJ0^-#&fP-85N>CSn9Zy zi+z#fI$bn7omM)sL?{vo|FVxj+@o0vV2LG1z2oUBQVl{rcss zdeld~E8w?EG!{+6$Z?PG2e8k!%z9XPs(1H*&99;c6XN>ojDrCzmD@eL(Sq1 zGLo4%^m?yGOsF5AmraZJt1EKxKH?8Hk6Y}mzMOg4!x`6f<|{*tfh|9~zKH)TxxSi6 z@=sP+o04E$H~N*9cQ($~*);P6iqb&mI-4iO|epVdy^l9qwF<39w*x%nQ2E1RTR zKiz1*YpdQ>O`5;%OJ36dRVBuVL4~XLvjTKqgYP@ha-sB9?6>;c6+%2=N%JQ#v8in1 z$wYa_rjU!W`zRzaPZY#fzgiZUJU=k;b@xP4 zIdOGw=gE)QmAfnnLKxkqHU;xQN@h{7Og!D?sHfuy)tAN8MN%|A{fQ_(b1C`CrNl}t zgG^B`i;vD}Tl08BV-rX_CLnkzIXjZLWDS|SfZDS~EVp--H`4fPIhjlM!L5WAk*Jma zba$;`8FE7BS-ML*(ZYZr@`#gjCVvBW-cG00I~s9bT3mi>*M?y6ttLVJoXj>YZDBWj zx5*7~e{p;MrLD7&Os37g!=2NrdqPRLV2n@>%UmN7RUlI)2DAK0RvK=dZtmUDmsEUV4KhK?~ zS+oS6Z?6IP8hSS_#t@4Gt5`9u2~$B1-qlzi~1%J}6<}SFq&u zF6MySuDN#O)%V-xlNM9ix{5WQP;83|gylkmFSFF<)ldkAgWHctYD#1z_g!fwjlWp* zd)KyM*t09$8p31cCA8MK6IrPxnwKb>c14dJXguD}U6i#jv*RSuGOww^Jb&@yl;`x2 zcecge?Oa)Jvis`!yq0&ptubb3;9nL(Jbd;r&g*9I_kG)qwEjEjrhJwRo>)tux0lZtkPeT38(W zL+(Gi^O&nO`hck*X|FU6V^=pk&t;IcO)i7Tq;f(xfyY3dOCXM-H(6%L=VMgP8A;(E zHrQ#9yYhlP!zK9W49JMo40}06RDIvx(?d(FnrJRZxFnKFnZd^6_fxvXQqM~6!itFE z`oqAqd&Bbt zfcTmC#-lzu9nMD-j$={mT$yjzHDxB%k^RzsuVEBs$hmG zUOZk_tB!Bdlvw17{F=_<{l0*^!{83(tu;ZN+TEfsVyN=&wlbNk=3zvEoWYK3RskEV zp7cb#In%_5HFGiE67H*|e6UO`s;RNH+l$4B+E zY-j${v`M#Rj!U~={vd`=pZoaIqiy+G>PDYyND=p?=W6%h;=&g<{zz=<*4_;s`w}M{pC_MQW9IEC`Jgw@nE2?BAs%j4|Zy4|tbqWoT|) zOdmElyVp^49Y(`|N-O+f5W$(a%=C~iZwJQdV;s>yrBsJ3$|easEGcXk#il6~AD5w% zlPZOOUqB?$4rb=D#|>W8`gqNK*7u-UJ651#N}u_Op{P)eTioH1GuUb8-2HS2EpOdkQo2panQ}s8 z4b{;qLsHsRv8t*pw1JH@SGt=XsNbGVoUK&fV|$H0s?hn)v)|%j=v}T z1H<>dl3ncUp$@*VJ3&M>_xPZO<7bPXz5gzDgwuvW5ECq?M)W--ODPum5$yBHCJd}# zudoi)t1ctJr~7bRP7xLtnmK|~$`5_Qf#s<`ogU^m8RFsAaoaN=>vPeJLUx=v!x#o< z$Snqva*KV$Tb?Jyayj)VtXMv#IQQl{Iw6e{lb*+2xI+$SF)TDF_pMvnQ=dx5`X{^Z zVF;e$ih7H3h5@cG7ErFwLHD@Hf=aFf% zOFp*CmypkOk<{eojuMT>4EIUVJx}Fgn zM%k%xsnbr6@VAbX`*b|FQxacPr=PQuagEd|D$s)9xIZD+b(f3SwLimzv+>T%OA+ek zS2BwC-gR3Mc#d#;`OOH5*82~y(b!lc^i8r{Y|!f|!KTd~ zm8IVrrK9dxY}o2xDa+DxiZC{Fu<_AtidK|>m&0uU7R)24{Y~r&=9YD1vwHE8#vYx7 zNMTcAx`{_!pZneAPR{H)Eb#uO{lvJ~sPT37qxo2`S?VD16+uVzN{T1Jro2RCYPY!^ zVVT<0aYndH=NZwMoE67wQ2m&L__87+a7CBJt4Y zh~pL)H=|oWl8-H%AUJnfb)Q|-l*3W82(QgmhiJk2hmPsF@OSys>*&}A)I?c|wTah( zf*?hkt=o!+GJ2{e+3<1vhULT0;pL?@C49{5Xx~a;^O!A!^%J0yUsbP}3Tu;4nr3gC zO-=V#sn+qdw2L2R`^-5XU1Ojj*;Px~ls7}-iBkF|qNTc@_(Y~;M-6Hz!KG?McOu#U>L11$k3>0v$2X1KcrF3O+%a@TU5C$Z#VY$tnTh~3D3JBCZmqSe&=oxB>!k7t%J z@qIy?;(Bz6-G@b24y@fFI@Nth$(Y2oxJs}$pYF62*Go9qEhlQ$=a9h{&`%kgV#Bv; z5x0g!x;+wph>5A(ViWy%(5+=bGgNIcr$pD@0hyUuGO4V@Pom)Otc#2`Yifww_;WcX#_vNu9ft)yW~BZ+&QVI z++2B?PM8ZgLCuT*a%pQ(elvf5hYM%AjEF7)H*tvTRr9e`e0ls881Ijf>SykW94vSD z(NDtDXv;b32Jt(*v_vJTF`7qdEwZ?1&v%xIbeHK;^^JC@=)eQ^v5^BT|zo9e15hdK0dSBtA*1 zuB381R*Qs;33jQq*f%NLZD=2tFn6KYitff3QffS()4e6 zt359e)9#jQ-oBezVH73AFM6KF*t*(Pb#f=2mJ8r8Z<*sK9-I~&@sWa5XF22D^NsIn z>W@)YjI#4G)SkaJVQ|mBKs(!I&yoEDs~&It#n1ff#&$HFrhIzh!p0qXF+qDQif^1@ z<$M-3EW9LJYw38$hG5P$n~}QC;boxQn>9=fMF7>gwi~G3ym)JyR%(zbrpf~!xy>hm z?}#c>1T}hcgq^AJ$Zg)y{kM}6rhbY@BMkup@UFyE+~&F>hPwHi5`HKf;BEE~neQ$Z z5tW{DH)9BTq!ENMiRmhrL}+yGBck?kAuik5MJ3``4r=W+3E&ha`=aSvn0tw~#18Si zoH6f_qWl^b&dfymumC|R=r#|0VIpPfCk-Id5a&q!hlX+WCPvjrDap@Ct zuY3GEKBB=cK~mM7v=Xh;K`J(UXWwzjbQqodv^(AAP-gWva({4Ug{K9a&}fp9a}q1ay%ci1~fso~2ymA@sCHx~l#@ z&KRA3WD=~LNwph2GLZ#{SH2{2{5J$H@(?e4MF+hpyE*#%K{wz=-H6T|C0)pd!-MBp4}AWe2d@l& z-^23g3EU1&ptLZ-j!?lG0Ua18XpPC%O(Lt15kR+*pdy%9fyP730KSgYQ5W^!A6X#% zfGI|3jBM0uUTfh5U>tY6cK9ms=f&@d1TqC4R099ug50t^7lNimgA`91`5+GfAMwbu znsa_L;Jm#VaZKd_@c2n3eNRK&W90r(4{d1yG}~4l*qk@tM{JmOWQO4<#c1?#EE0h2 z42Z5#LKB9eeO&ZxzSK4zd7Wf{wxt17hE`K}p>&aZ$L_R)S~=@}bA z&m)4V_ZVwsw9e5~+P9@o?8r7|tgphY8veqT6VXfuP{SAvm*!V;-@}caGDj2beSyTa z`u$Ge&i7w>7+mZOjMxpJJ~aygixyki)5zXM9{j?a7P#XI7Cgt0Q#=9~BrCzDCUfEH zjr?3hFh))&P6-N4ENEnX_WKin^Va8vPYYa`N1b7iGzMoNawfu|CrWOzR6v!aiNx{) za1iqA@X2t`i>pVYY(G$ed|hiMGbbTYxKGInNlTJIx5Xcq&KB^Kw2we(=mhSp2hjuO z=LDM1HM@_3TB;OCYF1iDyr?uy_8ah-_kIj)0=X$aXgm)pjY8<)f_`kad_9X8x8k7s z_EzlKMme#5TcL=V`z>#wx8d!z&qrfFzE3 z>3;-bJK4eSux1lJ4+Gbny9fMHE+Tm++l`WKh^e0eO(18DZ6@mi4Vm5W_*RW%f_Q)&;=&Jr zF-KBY77~f|0{w!N_g)5H1M9iTuZ=hFJm&18->zoBIKyJXkNp{FBa=|@bg%D`ixd}s zY)YP^bY5q_yZ+nu9F-0g9wQlP994*M`a6&w2U7Go@b>f6D>B`;Y_|J>6F^dzIo>w; z(|B+75Uf=W#IC4Q@kj#|bW&jf=$0IbWZI`q#-pwkiLm@}n}PkHK$5j1^0?lvO?M2# zCn)z=m+d0ko^4!$DANGaw=MbWCvJ(jS~>(h<1aQC=6?q)K^8Pr0^FQb>(NNfOP>Jn zzTTSz62tAtH%j6C3~)H&nJ9raj6KjiCHoks-v$$J(aX@v(~P$ZaeZe&|!&{WwWO@7S~U(HdF!>|Jr-_gVZ25{u4Ni6PM*}nb}?m-55y25)ulCG#F zXI^T*L|(mS`7?CFniuu=;(XjV9HD7g_J8<#^LVKG|9w0ngD``#FEeH=Wf!tCdzPsO#`~Cg>)8jmvGv=K0 zI_Gtsuj_hV*Y&g8ci52NT?h9Zf^zLar>*`hY2RdC@Q+PMWH-(2olygxte3uoI8({9 zmzBK~4^4rk{W^;?OiR-SWNFyZxiuJe0@V?7X2OT}Pm0(Ya|!b$E84;Q)1F+&d3GC1^h!Kc7ay9$eME@n#r!RTZ~( zaACPzhs{$w`HghoRr+WmLlMrwDzHHdG~kSPY0WX9lE{<^^??#S7&iW;QmO;_y`eT*aJ(M0e!P@3=#Za+EJY}F9WGPw>!>@a zHWk~X4xCTwflv_Rg$>|(Jy|~!*z0YyL z5l2APL)l>#0=t%GsfG7}2FJ4!#GgI%+?y;#58f=OrRf3rFo*rX%TJuK>rh*c&y@k* zQV+8?ZT*13bNi|W8jc?Vh(vmV-^R28$P_WkJ+@cmMX5JW)@#9oWDVwDBu^jMQrTiR zPNCp(aYzheaXr2HX-`fLXQf_#_vujmVYT+^Itks}RWRwb)p=>OMY#%JxeCNphY{UR zji11`)b(yB+=wGB;*LDl!NAV{{GbmVR}BiFUtj^o)+ItR_}qMPctL zc#pmuPvPp&BDk=P(W}I|84iw{QIF7alw+xUeKbYk*s^ z2+UDn6Rk^9VkyS;ny=FgpG=TD~ z_ybilO6aHV>WdP^RC4+N#bN%tOgbu@l1awqA<;Z+Q;Z0A&f#d78KSP-)Zx%P$G?2i)X=-y?sY-wYk6%OCV~x_e&e>tdX~A2|mumC6jQ z#rHiLdLDCI13g8{ED4|HT20dQ`b9Zf+V?17*jNJq=%}>-))OhSoKt)e88^b?31AX# zdh?YThRS&-s?y(?C2|SzdYkBCf~QkNJoU&20gUHN$Fs3d>Se@_R@U3`&w=Xj?j-k= z#Flfs>7)@|6WZ2#rIaQ*C}-Lpyx+1sLyBd#nYiedW~qgm?T-`%yncWozXPeA~FW~utb`$jSaO<+Un4uR^a zSoqZ~K8`qjA7pb>=I$xOOX$AT%x%t<81Z0WCQ;Hjs@2L=-rbQ7L>%}KOmt@E_18F$ zTRuj`Ob8cFGzTJkD8G4|znpZeG!)J7Qy4?>=_0yXkYkbzUoELxJ8!uckHE>b$#w9j znGRNO7e4}r9b5IoO;`_e!-XlXGfF)I&M)+ZWerfxwf&}_C&4nG4xt;U?B0_J@JlQ+ zJ0gRS+JV~?t|lE8`Ii2Rn`?pv5i>*@1W@TFd9UEc&B=2ehLuKWl(!Qo=x(gKfC5O! zR6l)3vNziB1on^-H{~cUBDqQUZOXZFxOw|=kpXU_;&W*orVz=3`j&OJHoy64q>ljs zwfQO?piLvn>(LS7)1i|p&5a`Q**mf}V5Y-L70USyHBT9iwBw8pP{?F#(JH@PlS3EO zDcK$)gMt@yOEm3vT!DdGkDAG0lKm;G49vMdU|X%C4{3NhR~aGu$f1?Bzj=JH{Ahy~ zYV#`(HrQu~kk*mmr~2{r(4E^Jn?4_71w^gonNV%Z zBNBzr9l_C{7`ByNe<6cOFe)cr*KgUxxfu(naRD^}5z89a{%RE_P#_F|`av$9euB;1 zdwUhM3u1Ln69~6zy{M=@R62pk&KO^d(>!rm*)HRDJK*`T2ij3X;uY8M!$(uoIwYCo z50@L^=Z*}ZFeg8iqBP>ZqTCyE6fs9}1o<=28h}|XN_w3s(;%y_M~UtI=V+MhLfG=nDe zp2j|cYmyuLZS}QI0)!EJUjAyTMY*tYgV4!u-sN=ltqt`D3z8VjG+4)p?|sdYQg0SBR%vjDI%#S+#19gGHv&Yl|TwW4{2zk%#ep zZ%MFyx@F2#X8OQ)M&omE(e(M)sK{Fu%Ja&W+lF^gGuxjYJ(wNAR+eUZp)yiXR7nu% zRAzA5B;n5wuY4eVW>R`~Cfp!fECB1vHDA8eb5k>e;eZthoS1Nf4XyoIAo-c>9 z48sV3nH6Jv-5(1!1lsj^NFu~{cpHN$;lpdFYD2;;P7gZn<_uj2iPZD&v4omU6d$$f zwVh^j;%&*SOC_=o#v|R3F6lGGwpj9#g0DBrz*e_G{=>~SsHdf=00q*W%3!K}L(bRg zgu7}a9tX#SQ9oe^pr)-B0h>hyR)+6Pd!dlGahO{k`Jkd7-|#?yDk=80+4Ta5ytyq+N1F^%ykt&Z*3xb0 z*H`9m*}W7OJuiB!T%h96s|S%Y!){0-xpuvlU++hvA-+|-H#r#jvf$JLXq9H&LZrsa zUTSh+wow(n7a)q>E)+@ttcy#wl7bkq9F3Xf8|;-)%HEG(o5q}X=N^yQSR%3N=~vUR z%ylF3&)xupWQvc!-zu&`*na48wD?gJ)-w0L_&50*66Lo^JW06c)K9Ug`VtRw<+R&` z(=LnIN2z1vIgyc#kw?8`zBr*yAmsBB)-hu;PyC!7dMAtzqk!s4_Z;Hk855)(S9Z=9 z^%(E2khYZymQJhgTh!_MmY{rL9k9k|P;tvRaKrPiLYh&%8$MZ$!6*%)tMkD>dK^%?kgi;)(oc6b8y{c&T6DMjPnh!mM*~z z+Ssyx07rsH72fB2@;12tv0j@J1~X@pXDt`|3_B!;3U5^} z+x2K-d%&_^@FkadfCLjPHKrC@P-@~pJr0`Ka&cstDcw+?vFLya|!PH)xJ-L)gxUT>8 zE$f7cE0#T-wClj4J!}C;kDo@UsgyEV>|<|I|@~0s3jW0jW?q5 z&vDB;t|vSuaYkw&D0tpf$5+?a!^uC4&vP@pE^)7LHl!0$vw2ikdzyf9N1W`|!165M zj72UW%)05P;1N7~FdSMiwQN%J=r;!Q`;u(hM>G?;jT|r=DUQi+?wuy|i59~eWKzNt z)y6ycO=;8R94bXNSLw42T#oUfswLRQ2|v^kk->4#8_~Y`)eeGHG>GwbMI$`Em}6T$ z>{IzWz@0q?uVZ&n7lltUi3}qRY(sKS#8Ggh;S*^fwynP%N-N;q!iJBOZ4b+=d zYNEuJyp7ugv-$mzC95>8@`TN1*7AeBwL6=TbaikXYgl%}@w;+2>DZJy3R|AgzgYX0 z#g~0MZo;9vzL4bRC^ZcJV@qp`K4jdIo(*NxAQV^SU|gSRA(G))^%sK-Ajo6N0CDtA zzW+20xEEf=N(>2V#{*p|`$FW1?sD}u!(1s6mdb=Zf{-IwI4Gaf>{?VeW)dey&NS{$ z2~ziqC`%C%Q;T9*X+uJrG z-<(5(CqEI1D?=6=3Y7JBq`4vUIaeYZjnvMGa5-}jD~1&@eo+E$YmJH2F1p^C&A_$+uj61#!WbSSdRdr?l!15K}xy zv$dlH$XOU#3+bECx3ZKInXjYjc(~ef`#l$V7BPOty=69Kb!V5$(* z3Daqs(WdkIr4WBY}ACHVmXp)*4-9V(6i zfN{T)ma$&1!3wNpBhv8Fx39zMck8E9@6PAtGs*LY?k0xz8AORrQ+FnSD|TG3c$4u> zf!f#=VHfslCN1vF^U04MZc+XuHUU^w3=DrrQo=p5wjLbx4b^BcFf_z?7^afl^1?@; z8)cFaj)?CU=~H}Co;;!&yU&Iqdq6V(#2q*;E^CSzjw1b+f6PBTH2^QD&t-;=CmUk+ zCQ9=;^_5Oa9NKtacQ>GpE+=81^AL8tT9jI9&qy_VSAHw!h-Min3Z0%$dR2?$a$ere zbK;$y0qyPPiBqDys{lJ)_;BFdKYWq3^#Jy)G*LBlnX#V%oW#dAn$QYngL7iJJqEP?LV}T>ngkKxbeH zSfZY_5`F$pQ<`vf&}8BLyU7Bq0Udn`{+-%AGXUYgf!`Mz>4x>e3sEVwKbB_{}e(Hp#Z*elMlk_x^oQo#01ugTodI=v2vICz086kN&+=0JPIJw}osv)jY5ha8vx(B}mae2@Tm~ z)vyGk4=XslX^+h#|2{oOh>@#9Jk(;J!Gdc5EFMe$rf>dpowR99u;3bCBc0Ep-wc6s z0BMPH|Gow}k>E&F+TL12PP+i8q6$zwh4+7fAPax?2AN={RAHM7W zjznKjvb_&hm8fN4 z=+>G8sD^pg#}V%dUw?7@_J#l!JAIXg5H2kXyxwDgkmBxzxt=phkf#uUb7O&X;hjj|hTh6@Sa#Kzr_9?@OL!2%Jk)8omq#X)^&R z^UAbhRsQ{2AZz*UpEClu5Xg|;ZF=}#r8TS@WS#%!Dj@jv>9O*Uwc~&ozX<^k^e*=X!pZTQ3_E%qpHtmKf5E?IuIP9QLv_oPs$nLKNj1L#i zyb1EUS3KK%++%sRRZ zm;}Xii|xM@dN`(LQou66*Y$Z`_cE~Ze|R%qL7DIW=u)2`1(ZsVsS7K_r3@+-r&Ssc zZ$e0&HCW_5&l5S6Q+TNh0GCJITM>{8r|V2mlS`H0 zU-OOjBT!+^A8v6I!@QBB%P}FaGPg9te(l(lMsTB8Qufjgd;kczc%or=Ft~%pe)wY_ zJ#;G9C)7=E@ZPHgHdNhr0j~Yvk_F^Bb9^g>4B0D{u$(mtN+(}X_8CzCq9oHtBqHWV zYN_z65KAm|9Nd1~tib;B3=6>tlwlYEkWNA3mRQg~_3+LO!9O?Qw{&n5S{t6eSZ4CT1h@KI!u2&5qQ{J6o}N-7F9C-G7=-LYR1>iHGfW&90#AfJ zz(0w1zYx6gS1-Uh$fhX6%Bz#RR4*{F4s2jNO`d}TIlk;= zN_`83SFv}toO1<}%t?Phlh`@1cS{2SM1ANdh~g62_Y5#g9Vrh*02kM0p-~2!x8`tv{6?1+pXsy|I!N;(*T}L(h>r^OAB~14^O)(^=$&t*9~vS<*zSR{E4;? zyt6zfwEDU&??TTUi-BMONPV0K>5g%0_8?J(H0=(&x1_{V$QnVL2cb9*2Qywd=h^L< zr-Z?0V1^sZD-B6Rgg8l8*Bu$83aq=|dpN)Zp20m2y%!;j2|)bmRWE)%>Jt=lkm+>u z4FfSW+o&W_o?vxk6gVlljdp3kIHt_%CMJcfvaa?UXmn>DyWSVjUk&LB!PBNV(9R_Q zIVtgLwVpaM`Rz{Y!Ofw|FThC3z2;g}oZ7#&f6y9OsL}R9iR)!!zDM(oLK0(PoTT}Tehe6H?nNu8VUfc`y=YrHDmQzY8pyyxJRQ+WyDN@~sbUJe5c7(&FQEw!PHD3nz?H&UcGkI}w~ZZN#icvHa%US+5>5RavzEFnwg51RFky+%Yb*H5o@ zV5VZU3;CMOg(1jyc@LEL)-t>`3>5Qn2fYY4yeLf~2QOx$4-q3imBdx(9$01e1qm>u zkO_shwBGV@D3u2C=zrOeu62__p;`}ctiyH zdv3p=-Zlvszhu`GAavqErElYY`-XIAf{ur=!bBnV1wgOIjT_mroSuTMM*B+hC`a+2 z+=16!T=|rT9vo#$re12yG8-{+3)K6eF~-Ii6Xv|IG0Wh6!xzsJRGCJ1gfCxKpF|DT zVe(`{brW?2k*{VLw)WYP_B;-5&47vkzT~vXs8n$045817j(e~qe`IaB^DtUUKBdRx zc)?s1>lh@R=Rw9ulzzM8*nK?H(^Q>Xc}it9TZZ@hYS{EEaN69;&vjN6?sr{y)YnjM z<5t>qAFH+|P@c$r@77hzjFCt4^*1hB0|JhD>BZ-#LG9xB zg8ijTy~<6uzgiUFYV%jLZ=(?Ja>&8iEt%%(xmv)3S)4f9e6B^E50N{>68$w%`#KPL zk59wS7Cci|t~ZDyO${09^si#r4odzhZg@a(lWWVl5J~r#q^)4sxurZYcYT0 z_8afK(UN^ew)yr+Dt1_YbDV@%*^Gttu{gW&Nix^#+IO%Z308bF#gp{gYOfA4iszeUqm5V@0q=0TT48zS&m`S? zBoE2$mA-+Tb)wLi{Gll0P=Pl9nYVk9;fcJH;H!*Cwu*E``eiv$p!SU<^LTC?fxRLA zlrjB_7^?;#F+Lv};{h4!*&P$7N3?%;6ge0-RY-@+JZRR#+w;;jz^pS|$Rz;t>ul9|e)XCr|Z*PoNUM;lm;|8VM)l}x@ zxS75I!%jl(4m!Z#7}A_v4!JAKjZvy4U)~>TYv3#@1Ip#4z!@+(SNIHcryWZ zES|C(GGjBET4>F7chkb-IFW5{-Nj<=lV;H`V}_{rL6WBwaeV>!r;zqx2u)^LNlRBW zq{j-$WMREmn$2|RFcPtx+Ds^ToNO190aYk9cs?)pQ=Fm<1|LMd(#q;3-pN_nm2lh+ zjcd82R>-6;~Y>y>4APZUn%rO*_m1T!`s-tdDMgYEIJak}p(`X=e5Wn>$#nTpQyL^&{l#Nm*G}JJc%QD_YEDbm38sw^8SrrWu`Gc_;tRppBGg@uORL z-PIv)?(=PA8D281JJgh-(4G==p>kPLUwmp>+nN9v?+4~bP%pRbVsl1x%kqC|I2UaL zTsm1aQF#iVl5FqBWw;;GL6wo1(DZ2}AfB30Exwy#fXyh?J;f?hF)wT4!{dCN`VOT{ zM7~eqTC25pJ9GS&y@jhJPGRK%09nh1C`5~X{Z`%xDVumYfrzlot4ymDm^>=BQWJ-1UAayY1NA{Gf$hjYyGd>VKx z*H6cTaWi(TeUB=H3A4L~Hej65ccd9#^qZorXYI+4adwH18-!9o$Huw)g+F@LabHE= zH*mdEE!pteFO3)1o(>L)R08p?lyIh>Ct%foxo7C9F<-ld1m3*Wk zCZOCtet<$vCNZLT@Ts?Cg)Ht$KZ|27L}nR20Bj!2koahgNcN@UVMZy$wV_`zOQcD4 zM{|~2qA}UOs9laFQQJIQCImAncKbw;@woYM>++2sLv_umaucD*#|ko^j%UEkGej@O zIyz|8h%+KlnAGr&TM#+4ff>yzj!W%vcE=|5T=jW(SOQO#w$JaomhkP4Uw8a#97(R% zwosM8_(t`q8)!sk0f@ zfwSMEwa>{Y3*@Gg)gQ$r^?o)}|5ERiMnMPWJoeAky259)(OE9{=SC+19Lj(y7fU@~srLdY7J);VVyf*ZGU3hlZ zzxZp4uA5IpZBa63aq=D86E`SWb!2%8m!Yb|0aQ9kw2kHf)dt0sY5;?48pKRQTR-%^ z2l%`Wz9^=(`1KPG^br+#==(tbXG&GpWyM?H0Sc3eP!x&{Vfw}8X=u~Ek-}~jB}-fT z++f?qn(7{47AsIfn_2_NW{G#C(%l^L$di$h^}_XwZT!e|2f69GGH4tWE%D>GDC~Lf zWsj<6YA$;dZ@b{RG9_tvCUe&17bNb;U`lqCI&P8HJm|JO?6 zGLhZ5LL}g0ij&^6U#0GkN>c(&zIpqVkWUHq#Ty=u;;gyBX-9`N*EbX;_-PQVWi8i| z$5moZ;fWRja?|wO;n`eCBwcH_xF5Qwmc1?j_G9%R6936i@7r`~;pJwcTD_=_f!c#7 zhHFc;wk+43+#hmg$67i3R2Y7~E%ERPh2p@1S30CK_8e{v6aUer8nwN4M8FK~a;_{z zll(be*umhN3->7z3=aotisVxaUx^LF6thYjI_?$tSr~9~D7IU8-simMbk>l)7GQEU}Bx; z7egJa0;H=0IM-xp3DH1?TMDLXd z%ca?W0BiYzS!(v0+xJu{Y;(#Kfr_EE1C42kG7}OSb%w{iBxRl(YG9eQ6Nwu1=p;EeJ&*JB>8lZwX-YZ}nInk-M!==#y?sS-~bgj~2HW zPUNRj1>VI`#w=PnXSY3c?ds}4YfFD*y={*k(H-$RQAM7+x;>y`+K}@Mdn>pPDx*jA z4x`H8!QBi4hb}dpbV(v))daWG-%GwQ|aD0P91T-#9F3u=Gb8@ zTqMZy=wZtoscug!J3G0d3HWTcU*f5m3tQzk3>oX9*DNu^UYBo|xR*}uzVHo)MXAM| zF)g*ake-#U%5}!gE{YRvlBbfChdp)6iEwZF%u&oqzLcs85k`ZiEJOK9;+IwX^R<3Q z+J!me9D1bhn=cAZ2D}V0SV#3bH2Gh&%982VLlSa=qDg^GCv-==1q#f{moc<8sZRTC zJW88)CxRDknyhpjPIB3>RvYgdnGI56e*_dk|Hwhw0NA~yS&8j=^qY%xkDpN7sz&cT zZooxUeoNChLCazsM)Ay`)hwMvd5h%P@G7y@n!C(4#li}R9h?y( z=fnFHi-m7Z{(riU-$34#b#K_7v-XAY3Tyt+w?oC-G|7j50fGjC{ zOYA?QPfUZ?lCP$=qLP_}rO0tJS3_FJ_Q!b5@t!d;3LtzRe$D?F84@^2sMA^jE!JZC zFKSq&o!{kOu;2~5BypLciy(}DQ2uLjSdXv;mVt*e!~gKI;f_!W!L7HoGVC)D^BnEIO;4u1@Ij*7X_YESmtSE2m{h0gy&m+>D~>-Jgb zR|I%IV!{tVZ3JODHUA1Q5e`=cMxqrhI@Ngg%bZ|;eIALA|IRhL4G~^Hc-hX2gvUT9 ze|xm1`hOf1WagP$RE1=}tPb{X<5M{Q2h077t@+Kq1ai}V6b}DG$^0b~`dd=}hsgQE z?)>Xo&8UK3skC28$GlkvHWNpCL>ANsq-`OoD{(H4UUK$M#dCj1W#_OBy<4T*~QUt8m7P4IvQqqMk` z{dMYp_Wh^nHCP_+wQYh`DHCL?4~5hGRD6B-Vcc`bg$u&bApOFl@&ZTIZP4=1%3*TM ze7AS?Ivp@5%=|tLLIEx)lK;yX@<*Nv>x5fvOegaP{1k{AIAO8dA^1fQaoIr-W@CL>lytc1p|Z#h!eS(;#N%eEgjM|L4qnHrUfWM4ATcBJ0tSO9ru8!M=IsY z6QHGHptEx&C=yqJ`#%d5N6ih|*IU5w@|NUN$oGnNueXPSHrP?19{L@S@d0EEoU_qU z%MZ5@tyuthH%T?w$9AEvy@Mc3jvq*$R*vLLU;u5h8-~7M7l#=xj}TW7| z`vLI5@avYW`A2^4e|uaGQ75WoB@>sQ{W^UF(vD<-G?8%V8 zb!WUe0&ej$qpKj}McC+OHgy6~Xu}oC_rvM9dye}N)>Xj!9RdLp0+8e9GB}ke;FhSa z4lvOb^*-h}GGvoF=ET1S2Kr%tXD_LH4zNmz5#t{K1*BZbkWBz;@NLjpN{3S50s+KX zj@^MSoZk%miH0I##0Qk$p(SJIJi)W+1qrR1!QIRAOXKzNan8*Rz=4q@GCR%|YYt&2 ziW8*cXUVnEFcZx(mfBdu9+2x$URntZ^u#t-0Gkj$c=4X0^ddPu}1 z2`sHfNg_!=GN!rXv49Lr@oNG|qukRRL*2@Jr9kBApSMernB4qy)oitZ)koW9y&qXj zh41vgi4^-lp+gXB4B2$r-`Rl_dKY`RYXpUSKY<8lsoDGBi1neAaC;kgEJV4S=09Re z?WUm;)|eZ}lwd%9G`}g*Yk^cc%4|bGiq`@R5S2$tp`^GD7ELHw`uKcROH8f}ztt=^ z=mQ)uj$R3OHMhO8J~t>63}{fPUCX*K-H+c@)-=v%-KBoL0yJoz_M!vF(eE>zUP1lm zrWzalvcO=mHqQ&t?ht@^dM zlbJ{B0Wk$Fkn`wAJXR4=4Mo9QnPmh;_HRSdM(Lgug5tuc<{S_Z{?6Zn0wPFLjw*4r z2lx3_TT21y16`>&k-WzbbOkHi0~MbDdo~tAxsax8kzTRv(}+C zG5Q~}nelY&nfumHe+S!5_5#M73q!ja!Gedv-k*!IaazA=2Nb$^nqf}}m?y^*G|0?F zhJeQPK1bDoEDkaQCnf=3&jH7Qz+XEk%JOao84AkaBw7W5`98tDNSaRMr}&ZCA06!0 z%REl4bU*bbR<8B6Av9p>SfN4WdoDy={b(Re?nhk5xYm!L=QIph3H+xH(+TEP=2?=u zi5xheh4_L|GE-UVjq`iq?DwS4%OaS^5(nqvP4`+Il(EYIWo?hgBcH)-3f``{$srF5)TMLElc5$O&O#a_UV7Y(%`Uc?4O3 z9ZQ3t*MZZaCB%6+&Ao`)@+5IuS`>jbeid|t@Juc&Hv{;AdVUZukVD@&wvh5&pI1V zq4ti%ia+TT&80JaVe0F_R&b6FU;bAwfU8GKE~^fyAx2qLYy6AsRZdQX)M)g+SOb3d zmUo<hU1u0&w|!X6`lH)<#J?E{Gl%=GNVamgf`C+R-u5&6HO4E#dL71> zz{8o`dsTAT-ffm)jhRM#t|y19iN!3-<1%Bkiouhb0_&=qy|X?ms6oz7iMV}M>;U){tAsHs&AyZyq96QpYY8$<*97y5Es%j?|*KR)@tF?kaB zu_v>wA%G~)JgxW_l@s`{tuShpIBf7Hlfk>ZJ5p+=4? z!5g2e296osII7whXlAih)Y2TVw!kq^;5gfCAGyW$`68sQR%hf{(y(4c;%o)Eu7#ES)Zvu#aHQq(~vN(-dJ*2wfXMVPa{p!bZe8{N)~k}Zh5 zCMKBR@2iyIs3LW8hw)JxA6Zuo{)D(_j6Fz)k5V?pv_D}vR|J9KN+rg$I6v zI<-o~Ny?5#f*)~TJANWQo~+K7tfG#^r)z7$-vTXTquh$0!+2Ok7JUmP6saa!n|UJ) zU0K%4Ez~@ec6{)P)oPo{eXS2PQCjL%Cr5*DeA&IYk7;MGG2NSf62o;#&7571?W&9V zm6P}eAB6t{lvJTYJ6L$H&2;7-8UbnDsExWDnH%0TYGEGT-<4w0xw7Yizbr3{PBh6$ zYF!=K&HlU7Aw~Ndm?Qi$%O2t{-h`dPQ5q*7?zHf-@pFf2ZUL7)p20->^{qi6Mw|Ly zVII39YBBd5EgqJ5ii}Wpll&hDfV{eutF5w8hpqQObu`bN5yx=2c>t=hZGsugUV1aL zX*LcLUz}v15fjKt6JtUpe#OT)i3ld)u$@r56ccj~!6j&Xx(D0Yzt{Lv8j%<4 zV1Xs5uQ2zTsxu)HK|?=_pp9j?Xmq>C=HZ4IKWMPmmd}-j3`K+)@xk(OjryD~y~*1h zPX7IJ8Nph&_mAdGUt*5vQ=O~eXpbXeDXo0z!4zhx+7s(ln30?%KK_-764}9JELCbk z=#sT?-)O@}3wTm_8cVoak!hpTHY;)XhSBHa(;EylT*++}WyqqJ-7VnisWA=VhSSa} zd+%%WKmMTnjVUDxNo%ikW1nD!M{CQWw<_nk4d}Yt^q%tmJ=jG<-VK#AR!;ngj$Uv) z^(qk|j*@?x3AAZN%(TtDCU|z}82dA{Ph;^1zP`#_I>@IPj6Lr1`d$m~Ng2eM#;eb& zmMZw7o;vP5WAO4JcE}j|yD{lZVFZ>mm;|w;V^NGEiHNp*RuiNRKSSs|Fh?`*H!dZW zp^Gy8ol2`_P{j_bZpBiaAGl_n6wKL|6j|$wep#~JX;e*Pz%(2{Op@WbVg#?}blXBJ zb>o_JjXoXtfs^bn4uqGdmpQOw-YJ&5lY%GaTlkpRl%sI!pI5P+=ur94s9}Xr7>1Na zF+I*X`e{CAViw1Ta9(Sb(bNkoF^j5^{{@@W-|am7VbwC#wVq9f?F9$TT}$U_i2x90 z)fyZUg%nv8M!z#{gWoq{67*Z12-KVs4Hb}zV~ZRvDj`(pc_BcPpov!u~jS|bmzn%))AKz>cs9}p$lj|>o43^ z$AQ`2d&Amxt6Da>rl2}FsT~!2cyxtuC-&4|!_=Ru!o^255p3ei^4xMRFvtz~+){b6_-4Ie$H&)tfwcX)4%NE;PeW|=@ zd?;EOU97LV9-ismil^gRtu*hjX%9b&e}nSLePh$uaeV$jk!bWKjynW@*)m_{_z038 z>?q7H>KplioEgQ~-H=wez{LOcs5@y($hL*QRVP#j%hcSpMgYTiUs1PaB_4ZQZ(p%T zge>Z?ugKoARdazv=UvXxFpm{90rdv;+(VdE@S?vuGHkRHhn(l{)l?@SlvX@M5_w)^ zT2Wxl_tY_I;`sA$m6HXUUVQ9rH>z|lU$GW|Ws%NId})1>CRZf2Ya-EH)Zgk-FTsfK z9IqmMkLD6@FwKtbA*8xy<@(PcV-$~0?0-3znlfII(pJyzViq69`6>Ypgql%ad!}Fb z=)~J-%@h0-kM>~kx(ke*qSG$J{KE73faB%BFutD(>NqaH^*wa*&=l%(CoGNhuH56C z@|V#G9ZLr*-Z>c&!eMta>rbSW^~@%D(Hq6+2$Vg((v0bgjCuxQCV`VqdvRK=pfzaG zl@pUN@X@X5gj3!15A+3Hj*Kc!d`77a1y32wPO-Mw?H5`4T7r^n{Ez?bZ!G9I$d4bZ zXaxA=W#JQw=Hw}xEpx$6Bb!F4yk+wb00v@K|9lcJOf3Ldj7Urr`vAT}1|yK7tEq*B zTl*sh>RHW1)IFD97ky=U8O~2ZGm-6+Px`OW>RPqzu*jvob6dmW)nyUd2m!b#!s@bE z1bS;6->MLhD%LJ!ch=fjl)xU?m7qGtMvqL_Oua5A5FdF$@3>siJWP*7A_e;gqNYU6 z$vX(;hCN<>9%Y<7|iMNPRdS%`T>Lu$eMVX#u`Q0)FxB}C*O44^+lX+!J?jfYw4Vp1y_JMe- zG%p;=5y`LcWfd#rRWhb{P^eYkOND&(kX-zV5&c_4QdN72T!;Zk$W)nNkSpAu#O+Nd z$I4GA%@*%ekKq|prj@r2tL0cS|M8lJpsqY8k=|6e8#sw9Ym%}ztY`B=*j4yUjJ`fe0>Mymz{<=t@he>7)Eb2Xj%?;2+ z!q1WZE8bD;SWecW7YgEsq6&bB8x{E7QzK%Z9j(k4SO5j&(yeG`wW@KLJ!Db(AZ(rB z1Qz=NM-iKF0l2|RUN>~-SK>}J=fK7MbT^aY|HwsM^mdh(OFcbw^uBL?U4PcgNRY2K#e&TRNnmXs7HLd^b9Y)o;DD=9Hd}Q zZW_H5e=~>Gt+U3Zz35#)2CLPJAccntgACgI`NK zN2V@vMu~gLu-D|BT~5-=!g|nKm>FOfWN8Qo)c63!eo(QvmPvGB~vz7JYaMorg(ZMLsZ9rupiEG7C_ z)%a#2Fb&TPgQ~tx}M=auYPIhV3^jI>)(2&<|T<&?{4>XB)Mj50q>K*ZV`Ob88_2ai>r!*)y!@g z%NOtklj{h%;Xf@lXxUXTM;jDWo26*GGakGnZuyaBL8pDyi?I2%T2q&H9Ij0f$=Cf{ z{;~MC&)VOJS)d-Myp{7p;^|!QU@N0VwK$(h;WuwQ^EqN)!|4_Nr=Z#Fw^0wS(?>h~ z>pzNipz{9Aae^|h;aw;mJ`6uWspvVeSs@Zn*3essJ3 z&qG__V1s%`#0WQ)z?Q25#Si;;|H$7^-bq^Ur(Q0!pijtvtqoqYpnm{;|3TU*VBdmC$WA&jQV}B0V z>0a#M>;JJ+BG`#k^w5s6zYy&7v;BkMzwN{HBfw6ECmxzI9)y8oqT#vxA zdjCUJ#)Ir&ONZBe=ieS+a8hkX$z8=bWP!?>_2nIiq{`(&IYfQNhfRAjSubp8$s0y}p3TZZN{xvKolE8_p zT=|TlVGPDHVi2a1ppgag4~I1;z+#)V z@O!IRGg$WwZl06*=TU)ZZfKd=WbfLGmK_7X8XNl^f@*e$ApLhZgCO_OuAQOfg4OFw zjuj9y0I@K!P@t#@2x{N-rEAQj}5qfd3=9=KlJ_T+ux8WXvlAQ_Uq@;a^bIC zF=N>mbe}HY@m)uRgJqC{WK9QZR7`6d53R&<>*k?!7Rc`?@BF=2kOh(ET6S*~0Q>#; zkxj{uN78Nq$%26G(~SXU5Sne21%wiTtHwhAc+iEzLq%?i4_y_p3qqej(ncO36aeth z7Kjvj1j-McwUQH_KP2&GZ}U+LB#vp?`Lx6uM-I?BN<90-6$-OnPQM?Fv5tX9$WqRl zsx8zm-@6)ev;s5_j$V7Vb3xm`5emt={%hyUa*NQON8WiT6-ySFa=k2k3aIT6F+!b7 zkeYRVFbE3zrSbe4$em$?gh^tLLg&x5{epBo0FHC#OnESQ|N2i?K%OWTJGtL~1!ZX~ z0g6HUNz-z)%(0MpAP?u9#e5gX}Qu`a6SH<@Yh`n4e^^LFq}IfQEr8FKeC?ROdvxcr&%Bcy`85B z2!NF06>tO@TqmR~;s*w2w#9Lx@y%^na`s-iAH@Gpwgke55(?6zJ~ufy%e$(G=9qb zw0Vu?_4w7YvCu}EMORR0T!LNr$ zJ5Sbd-0x^q4U!<#&7J&VWnLp*{_EG{D+%OlpM1~yLpZfe=t=^&U?F&mPust{r^bnS zg6j+X=jB-b1?FWRB`}kgGWO#s>R&sPbxw*=X*UUB_J>l{wgcO%S-&JlM4P zaOA|?v)xGqlCKQ{lC3p%pzuyKBsY1LLq{#Ui0IyGb8K~%C)!JRQ;3_@+aXWAMN~6JwTdB;1X=}oIg3Yd7<1dIp+16YFB;azl zLho;3NHVBphWN7HnjK&_2IWX z&gKA)a260HqP2NhGZup!h{`F85SwQUU5=~*fZ)NOZjgKhipGLOYU7PRCuOelG=YoK z-mB6d6VS%30Ww)2EnnjJ9yn;0=&K@QfS&Z#+X&fPD`Y!s<%u&aAVZ3hddNWR!@CU7 zLN6Np?PQ@#d_uK@+urW97DHob_Ejj)uG`2mipE_7ND7NTbk(=~cB=!izguq~xiQL6`tT-WP0<76i&#?6AXBV}4YXTS3_V`xvF0mk7-O1_HD z_Db?~{^)m&x~5%FC>mf=)xzQaQ5ZVy=9bQqj;H(;x;o;x`Qns9xMy61cHD`!pX)bV zKLd{HgHo$Bw&;wz!3SOc5~iq1j zTBg1O@jh;yZ>dnHWK)h1{E{8Y^%CaBJb2h(Jr)YnE{IYj=3c!lV z8iGroO@Qc2ygP5tLx6S|X#Nh}HxS3x>YX?b!lpDu30`paSYZKhy**7wz>_lSq_tz2 zrzKH_UN{E`)#55bxZJ@s?)GainsiU6ift7;!SNvrxF$jxe?wc1=W3-HtqtM;Y_eO~ z>xKN6j{Wg4!za)7Htx4q7XUJ~xatuQSm}pn(zB2DQ5=81c)-n?$n|r4NAH<9Y|;6% z@GaCbO7*qkpp_5@h9eUq16HTDZ&(q(Z-VJkGl~?T<~RQ#!+ptzHSp;lR4$Az5C>kH zXQ?4cbS{QBS&JtEWInUk19vl+in&!Gm4ikoR=V6J<2G%W!@*j3*eTSoW^l+4P zvIE2@o|7$MHDMI9xKlhIquvLPwc8DpeV$5k=DjQ+J00Wl0}*GSB+o_D9n(WE);tH4&uLkpbU!~QsX=P)Zio3{sm~6gNe=SZ-e<4Q zy<9u{_k1h|-iudBM>^_{Pa)ma96QHfQ9 zFyjCN>GuwGRC{x{rlx8xeX4{L+xd*dCf+8)J8+>#ud(&lc~et%DXog_5`+^QQxM;I zGauoa!U(yrzr@a{DA^b1yVnk!-&{Gx$z4oq%59VUi;qb}U|>|q9|o6?9mDpkOrY&V zodwC7dFqZmoURz>cCjh=pMLM5iO(oRYRThreCioJ@4jeaT$PT7wM89n#&aPABX6dMyUOb$Q$+sWsew(HtC_z^cU?0EC|uZIKDUj74FEN5gUH#{x>#!EPi$yS@Z~CXpdMQuu55` zR~oKMd?+i7@fb+56DY3O;NrrXt7v5CGzPFc7Pmn)VtJgee@UCukIk$&dl<4d{<-}k zMvhGDwr=ek^})|2{1n*;Y<6taDdOS%pfC>|;nrW-a$sRPSKp?%#5wxr!hyO+1N{Iq8@e`lJp$Hq*9QHf7|Z zX~#ju=WN#-kLeyS!B(9w{bm7C>E)YCawKuCS^VI!u4ePY5#%4YVP{!WFVGTFw|~1| zYZbNaq^%gdC+_c|mofzo6-~UascmAf#p1`cu3;~yP7k>pXB|=)(Xcu`Y(LIcl%k00 z>`TjSK?1d#Y#pq(mMs_=+9Js=AaFF%d5oUNfOr_ED)v?%h2 zPqBzxvWk6=hPz31ZGfflo%T&LlH*UUC1X0ZW50}xr(H+(M;8wa&J$T@j5M1hd1OQz zx9c-EIbfeoTo}2t`0um!)}fpSa%lhK-Hy6hc{C3&dWUIFReAZI-n>&=Yd>VnUz+Lp z+hd8}GI37|0@mv^oL;X=eIF6^wN(h6h4n=XQIxPeKpSq-xi$8Wd)^VH2~Rp$lxP{g z-W9@lTko`W?${Ve*E?vaq{c?i5QVbuL1FRqL_n<+lEq0}=I-p+-A71TBz*lrE~7Kz zl&X#!hXdZ(>?7$7`RRr$OKhV*Bt}Vhly>C(b)qO6)26dD5_iYt-WAC6%XF>uM*>SJ zY*>a~u}sXeX0hXoaE05opl2#%^m=oRR2|6k@Q18 z=Ko`7y_k*BQ2RR?3q>5wF};C2`m<}VhhoT@O_#eERckqz++S8?9=jx7PQ!K=7cNRq z{y<~})~DJCgUc|NW^^qzyA2pkZtASPWxUP57A&NR|11v3J99O;=ibjO32mnm5mc3+V;19-dLq zqB*cnFOS-ZAdnv@n#Oi9ohvu;-7|-5jL4CUu<)yR3aJTM{C4MeK4;6@?W8OR@XpQ)o7H9P0I)S zg@a`;XgWTPT?x+@8$4KZ>ZhXZ9`$pkrsRz@ppAO2v0efyMsbD5kk8yxK&b;rPdT{L zT0b^z4&9GvklZwW`NNzs-hDxd`RqVD-%@_CteCoJ*s{?3y!SZB!Yd@$`dI1Mau}BL zT!%Mugjz3+uKH_9=JyPL-9;Mf5vIOUe!Zh`z-b74ZimW( z)V3-j7+Wdtv?;M*$A=s3p%pL@%4W2``v;q}`EqO*d}{nt^0s*qNLQ7!N0572r>qdO z9rc-KufC?udH+4?M{~pb=n$Fm#+pzgjN_B_*m&^YbJDHjuJs9YiryC}c#wDzPP~s6 zmb(APDP=3XRXeenQ1}nbs+^)aCIpDmjpHRue}DB`5E0Q7{!5;*Js_9nr#fW22SuG# z|9+zW19H8>0`g+nr(#I;Oa!jPKKd+PlKR3Snq9BI20)Kk+pW7Zt(c|U<|6|t!8~8bX8TI{f)!)#EzWm3o2LtGKnU$w*RbNLRpZJON;E`XL1|L>q>JD2G!B3>h3j$|+xs0E}b z_n+`=T=h2AC2$<5MYJxl%xkk-fFWwC9u4^a-b>VWV5pqVLU%ps95~ACp{M+&)GbxO zK_B@=L;#%bS@2ma)aE15;yM|cQ;NT7b)nUB?dReP9q5Jg$hcE0^7W3@LxcK1yi+;6B z|Lt}2?_bx=nxY4f=lz9-TPPZMt;|!#U$PY)XCL#Xs%66hpLjh^6Yrx76uCT=smHAe z|78R9-AwTeE~>v-Y6 ze)zxN{5%g4DBuSpwVF2eh=Q7-QrtwFl4~9ww}$T{(gr>_8q=ni=a26B^rk-?bEWu<_Zf!=ij_$*f9KsZ@xn<@E*o0%Ja12wwH`BnNi zy4$~RgSf)_EqxPEHhzUJvArJVpY+X4i&VU8Gjy};sA{>e3C}S#7(cAUcrwnwM~SoV zpYw9ek_sPZVAH#%{F;WNu3&%BEdSMeI z0INg2ZmzMe(EL53{)vU*?7WITF5g(xF=x~kJosku+MY9_2eJ$fU3-bJc(q;hFt_T2 zgbilJ)9YI|?c$cud3)i+n`fmr zpT13i0pZ1=DYN}n1(q)aUxz>YvgtqsTdjwu2v2>~rtz}a4#v{-f0}^JOAsj;F}k{a zL!HFZiiWvZ;|<-undQy{$6@%4#c#Bd)uia*P~;+JkVowyeYT0NAxMJ z$K{jjCZ7G{ak5ky>{%W=b4nB+m`oJ9kL%8yt3HX(5~q1_Krc^Q_UiG{RbOL;UmE}1 z!q}bkvP@lE7N^{@KA0EraX!|d;=)LqW!rOy*z|Cv@yb~I9bwp7R?}=mx2sws+yA0m zzKyq5W0+T`@SBwQSpMUKLPS)}OBZ41HG%pMwZim&_)I$;Lh7wqU4m(}Z$O>Q$z(C* zKP+A~2-1E+#O<)*%4bo#KmTW|n*jJo5zCI(V!GBx(eAe6O6EVSY;oJr4blDY4gH@t zLNJTKK8!^t=aPy!%p|oohUnc#@kWN1s*j@WWpbAmr{aIwD6T#*kvAKDe2R*F3`_Kp z@yR`(Y<~V(`78b_nNYNh-a+grcpgd4IrJWWI6y6{H`~5XeFE(%%HNCU%cYtEq?cr% z(JwDIvBY|4YJL+u!TQ=a^P6>kYFYRzJ{p*F0B1;Bnv!xZKIzo`+Zj$IRy%cBYBe<* z@$ela4zP{o;Zg#QYlj<%V1lZ^J8z~9;W&M0*HjU-`t=Geoa!-F>%!1&^3Vk-3?&Q( zas#9pV|HBUs3_LpqPvdcmw*S*QB(nbC8zdlK9oRj=vbGGKh7Th)_TlelBoL)1P}~I z0G=#xK3XXxsvx2Q{q6TE%2ZYuA8x|T7+;MSl$g3><6F;&VnB)T^;Ug8N%vR3Pkv+Z zaYsg7B3N5&X{_p3Zn0<2jf@#|t=s9Hyy^aQv64PRqy4sTpI+>zZ~zyh4-?%8dJY;z zeiFvP!^lA8d{2Ig!)@sfO^z5Zb z9&tnT`ntYMT-@aFA^Ed5X8yfFcCJwSBX)>qweDR%R6q-ZY0b(j;m@QD#qVT|1v4r^ zQE7hDqK{0o$SZK}p#>U&og&+19Fc8anSAFixQt?Waia=M94!VX?UZNX)u5!s62bWi#yba}wNN71~`c71R^Z;yBz=H}SZd^dLNv*+jd zg&qR&;G&jVs^y4 z#I#VGqE~_nVC|V9cnNNgu90b05~~3=DTPjA6v2uwlD3dZBV;y zl*mgK8A;Mi(~KL}q8+1Iy{{XmXLPhfbLOZU39qLJ@5S<_ytNd(-afYW zk%ic2#)!>) zshR(;7W7?{=<71idBvZiKTDsbx&XH~?9t?3MRQRdGN%-E26?|f3o1V$s4$)1sjI_C#Gc zjPBH-T!VrfRW{~MFIt->a)bAL8pB|kv5#p`5#*6pYlagNTILD~@+p80dwNy)Po#n* zc>ch4SGRuV7&P1$wX+_<#Cg=MS)j;{)3+|m#x%J$#O~#DfQo8qc}H_;$10jRC(^eK zipcxQh4_>4ViH)ri|!9qm|Uu0;_QdGxkZYJtIp!UU2^Bb(glok1u?9tQhjB|B3FnM zSdj1*n5Z^KH`g+g>93V-%@Q#*OO)YO{I#uw(VzKfno($Ti+j$DiVPN}Wb@|?;=^%# zO4tDWlh^hIaFnhNXV+ztnbl*jY<(@OWz_$vFLtcpG_(~kF^BuO&HU&q4E{>2+HQ#7 z-M+O>H&9%IlL)(!t`Qzi`MXs!;cRsxY#URl%aTt%+(@Z{ovS|f|Gvg?cIZLu+`9A` zO3*=^JDvw6wPTFL!|?O-7-P8Dlbtti!9P-~tEDOmQM<~*MNGv_>QO9PDd^tDFi!`# z!Uiv$B|P1SOtlo+R5%=shA6`+NIUepjIVv!nTIIqXj5H)V4FLqOT`|JQV4pKqg>U< zY6&(3x|H3JIecaXrsyYkgNb+;@ytKtw85`$i6v&BgS5PtAQ*1$#G#u5!cDp)N``o4 zw!2Oiu$>{I-%$Va9d)Nyx>Uc#0Z-UGR0?kXYC){HVrz5C_|3v%F8J$jh|3RU9ZF9_ z6c8gGM)U2qo$Etv`xQvC&gonXLAU0`K(Q#0&{If36N4g})q0?z^(FfAWxcFu6on{7 zpY;LP1+tYwfsa0nnj+N&ZWlO4$VSV+L*Ao@m#(#=*#YLotv>^HGO%j`e2WoHN0I+A zT4ieg`p(3?71T6>c#W#(tRu>`iG8eFFUS9gjEWXj0Fb+jzOcT6`t0^XYgj@DPZ52A zOGT}-HGE#*@*?&o!j(1EQhTv+ClRSDYuDC;y+>h66CNwwn6yjwi}YD=Ga5j3^YcqJ zJPJ>%72&6d7!!xrCw)z4K?c1`b-Bs71`6TasKecvHm1P-C#5|XJ&*hX-5-m&;jzCS zuU;C(NcW&^RGCTz*i5{fTW@watA%-00`k=dB6_G+hYWI^x>J#G19hEqZCHcGWZ_o~ zp?r-DSl#>shG@ZX^l};K1%Vec9R|lxc|;0Gd6Qvv9b!XR>r0}F~X{p9DF-^gclsrghT`_xNh2rtaQS7_J+@9C00QOTMYj zraC@q)FT7wfIU)Rd_nfS$l+di3$}nj8NQkM`{?r5sX!jo!>u%nEZz7PX}S9sR_d8J zv5MLw@XXaZG|iG-E5|8kq3%-ycfTkXioCq#faIgNr|qQ7BGgfs5Vf%j*g4uKa`kI2 zUR(l#6c3^SN&ma`qxgL}lr=TF0bPd?T_vaS641r8W7TfCOoG=fJ7Q$f+XXX-g4lLtfH&q;?zTXg ztGa`GgFcw*yh+-i6TBw95YjIWgP9db^T6Ia0S98FQ=v$sssu)@16eni83BuMLk61E z0;8yuy}qMK0M^7ap{?Nb$#!glL=$S96`PHbs;*jkBbB|#6jcGOy_7;nXV~@dQjHc@ zQ(cPia=`wmK_!3QeRD7(vK25HY=97TQ>w6k`JFFS#xaVUzn;o;HFd|OP!AO)N1!-7 zDPui%>G89{xoO5XFGqrz4$_lCrGS<;vx103Gu}1V0-?eihD7%V&y+iWRzFt$?bsX; zuml5vbb2XhsjG(38PlNW>W-vSzo2Y|cUF(O0Z`M}D zD$O=tZ&>LxR^96W3=b#$!t}>32TPZes@5(nP5M^x1uoSJ*2n!s=!{BKoExLhvUld{ zS)`q>g_m3@fu4^1#FM>lfE#WwttVA`V{8y7jq~X5^&%IrwbY zxx6$WNqN3nLp(uiQD4v>C)?t>IbHUNNR&9uq}2`??nHuEq0n$hdg*H$WDy}^-)w`=Y`lf<0!C5Awe!G^vOxl2sa2&g zo^IeYe)s|H{^pZmbM1ACB)fz|_NHWtvr7m|G9MT66Otd>&())=Qu6R=rNTyd(#Jx( zhET+u^Q69etF<+fOz%mbQnlRbKFD++tlxx{9q`hrJ3MFQY*z_3qZVEGjKv7!Ya*d3 z#t`-d`7Fk9QnM8$o8RbE!+i>7vQk8T=Os-bdfwHWBCM!6=s;W0hW9mx{iz`K5jp%m z3HW3(p>gI?UuMG9J5!1#)wII&X51dG04D>wq~%8j!i z-@f@v>r-Ht<$Ggm{|xYtDH|F3rsb7h>^b+tI7q=Qw|J!1!8)gVTKlxY?}Fv^&diOY zpIVE%4YIAny2rB$j(b63sO?|3!l9^juA9HQNem=9QA$sx#A0F~_UN&}2FfRxi?d*S zC5z8mq4_y1(KHwp!pn55g3*rwHM|^rGKs9d)Ghey4@w`?*H|r&e;M(RCC6I^@Ms!td%LL9JsQ~W@z!8JGwUE9u)O^?wS{4Cg{m+ zKAXw=c`4Pmze7iGud@E~S2e45K5dhe2Bk6# zCvLzF;dYW@pF{Q3b$n;(HoiBnBV2SPK@oiDis|f%L(XkZzDGp)qtR_Mk`1+K?Nf+Q z_T|dwb2i|O9mzMNIqETi+z38r5S;9E3ZC=dtIcCh9eSjbfJhH?IiC zSa5Xxmf<=}pNAy;2#zn6ephH$WN99FL&lf}S=-_V}&rhp=$vE;40 zc3&ZI8|ANB%+4C#6R?H7xNo;3URDcFW8(9Aru4WyCO1^edLOdnK6`@wcB4o_4bwiU zVS-MPt}8ctROxm7jO!LG z%9=;j=L@PxzmFvvY2sZD=KI_66}ndl&56W37ZVO8R1e*ue+$I5qoIe)3ELfqb{tPg z8m@s7q z*l=nLkD1Lk+SmXwkAp?hocbf^WmVwRY~~pg8zg^Eko4cE7sh zI1EdDn1=ylK$g?gLnkhmIbHU9?ChOa%Po|_T<%izAdp`>_wcq|u8`^;J3j8hRe5gSPgI`g5jwb7pk&G} z3N$7@(T~ni1?XoPW!qn!!LrFQyfzok97fD@2wB3rfKqOL8uBu`E}MPh%yS}*`f@g% zq(YB^ZA{fL`+kbHu?PncSmMVNxwxY$TJk&^=e?~iGjtqhy|6un^cQotaPCd%<28g? zz5S+#&#|zuCvxQ77-ge!>am&XZE^-R$PsjM$-(N0O(mVwxV|W7HQZ!$VNT6q>XL(Z zdETLGshI=a@AT!QDk@G4X(b07YIw=Noq088uau?P*yXD!letz(iL8dsJa%;>>={vz zn%LUqlqJ`^GH+&^99?AQI|V91(6LPfbfB0L82R?qk~ z2LgXlwI1Dfk9#}AL|8)`-ip}VsG!V>=pGcTQ&cavC;IP^#>Y{gSxcjlRRJv^z2F)hM|Z@@5wYc8s|@=Z=^khvMJ+-4BaF|>P=WRB~m$B&<2 zGd=9*E^>Qz^N}N;7DSqd?^=rP!Z3)!DYdkaSqndUi)IG}g_+Pe2r8#Jt>S(NM#<6W zk@Oe^q9uP4I_#dO@Yq>h^xN*Zz0v5XR66Os@JX_e(tenKP$(`Uz4~FO)t7_368Pl zpjG_XGScVu_CR?y=E}F-8MEd^hZ2k;z8@LKT(QfPa1n}6O7ghq638yqxy??eFl5*< z*Lu}#NmfKCL!JJ<)=%u-9fQJEM+)BXpRAvvQo=5$cQlX8u4b81hPTmMtuaLDD1Gx~ zuW;hwb6zvrw@>fll>f%91!90v);<}YXuo`S?`q!EepFcgqUl&18Y4)9A3vfl;^$K+ z=u*bID}mAH#{e-Nw0;aQvd(dP53Y`21w_hQ7iZir3=y(GEAG$d3xrWjp>_rQhvN_o9~$px3G#ewQ4+RM)F4Ag)e~LAL@`R_aoRi zyvCoFp_@d4pcw)6OABy#7>jSBVix9N!A^>P<_+OFu@N(h6?dNVrlD4UWkHhRz;^E* zrM=zB?zdZ~-Ja+Nk_Qqb+XOYoim!>)V_T=GU8d}O%}f}pFkevrG+d9jrDD5D_7b+W zV`XO>pZ2IT8ZeExj(2wOo>Rh>3s^RcuvsN^;VZg|Ls~_Zf>>qzWJ(kGolx%#POW0v z4oW{mZsWH%yDi^FDJ{hHQv<3o-2Q#y=Y^ptlS@5eJmP)JAC8~z?f&9Wdb?lo!`mrN ztLY?Y;ErSbri_;m(JS>|7;C+nDM{=QHQ`ESx}#*uzS^Kt1-XxJ0vtwse@hG z<9zqSrhDqW4_h_{MNJHuNPDS*L$#e0!{hpd_+y9tE_OSKW9hc2WSaYI-gbX+oTKGy zso!Q$EWZJqrd4)L$Jv|Ni}Yu_#oV2{k6?BVMy z315={8UGJ#ma2aB$!Y+OY%v$|VeSibl0=1DH;;e;~SMmY2|u$`^`g zK4Ce}5a{UiukY;rOBHSl6KAZJ`l|)6I_#Y^#ZrvEQg2i|#%^vuQg7{WtAoLGDxpgUMMFgnc<3DU7DF1GK zV_iNG+Ndi7cG&GbtYQM&w8oV!*bj`T6g$km1yNo; z4CBR)(}9-~n~ESl_A$yIm{;P$)1<#so7#QS@@A zdVH2I#6tgu9&y+sZWwOGXs?3N=vT0JHgnu7YW<5MsXEyQ{GIuXKS6iZb2fr~OrPp6 z4lGo5COnXVZ2#Jbtmo(wfZ{s=TMk%iaa7?zdW-__c)e1${Pz5YR~g{MseMO)cSy_o zY(Host>u2+%Ne|YtSOfeUjFU?vNx22?&q~gsU&c9?9MdQiW+?2p9gKAn!|K{wdZq> za!nfq`bZAg<$j5qvZuZTm|Z8e3?!cxS;unkn28_8qO0-QVm_|vX=*F*~khON$E z$XQn;N^-Vvr637aILe@>U=4?{65CV?ROyG5!CA#_Y1~oO^4_ksNo2AcI=+qo-oV9| z{ai0TBWlA%_InRnUx5p1_AAh3<=5;y(!z?|aSN)cm`JodXQdHSX2KfT=-L1|g&8>z zRxAKSiInNna*L=P;6S?ui>t+FK$=Gj0CFe|5a2lrI`sjhKL;VRCBlenDCS1HRn~tP zhast(3z&x4Ahk(;21pZS(*WZf0Gm=q_=~S~I`U2nL$cBBt4Mem2J-EaiQ=$T|N9fv z6(|YNM40p=LJJc4*xbqYgQ}>>eqg&Z&Tn(xqQxYTnCKA>(~R(tKN`q6lt3go-vdcu+J}h7)gI| zKBfBrfdXy5m~JlX4CGm$Sd>2ah0b z@hcxVC_i)JF;9V-fi3f~@q;0|aFeS}LrN{mi(hQO6*=n4^!s!dl`5t(M^I^AQjkDZ zxUMi4Q_7EBGcY@o_U)?efP0#(hVB1Z@3q7?A>p&t+h~O8Ay?e;mZ!1JFw| zW+5YR?wX5V>BJUl42&w(g(bH&tDp7)e$YL)9YvJh(+{)l{oh#$6VCuRhFgZ(aR`)3 z4~AYTpuY`qYbY6n)|(L-;Swz?FZia^1kcl%f-H9<6Q{F;*RXj9Y8+(tcfGG@1NchhX2Dxd znJ`rUGfN0F%mMptY|mwx>*U8`h8@rnTdt%Ye7`T)h5PP9KCEBHS6~habluBlb*qFT zcNmHsGyRX|@W0~^d|i8W;z4a6@Khl2hk3i-jB?ovQ9)1ODfZ!~lfNHGqwg1O!-UW> zzkI62$fR1qKa*_#`ZIn&o|I#<09Cd->4Qq3UU`KK1E_^ZDMT03E_?`P51hD$!~(jT0`8=oy-%%^Ra#wnyb~vNXk5EG}P`J z0H(XJ6`W(Z3whAN4a%^^!6G&Md~)d&;G;?8HD*~0T6E^EjexXrnFz)m^#o<%F24-2 zRwj$1`dhv^Z7A4P#SDy5ppvPKJfU45qN+DFl^9kk#O!&H)H7FT4fC&qR1DXCJ#O_0 zE%tkY#0L@NR&DE4*U|`vVu@kjwI<`h_dW&Fm+j*Y z`XQt@YilU6Pp{}DqL_z)5I~|&C%FYGUFXVnSpje4MAW~^X9Ns6$`f1&=#dw~H)|63 zDI8V@lQ0>+12o1J4mdy(2ITy(bCUyS)HxIKF;buW==$0xFDLI3$aGP=yOt11{#L#(8HK6ZdzrN_}S_Q0$BLap+S+trI+E`b`h}<+k zh4f%2{a~iQ3;P>&!?45QU)I!!g`~t~hSBboZ*6p)Llz{FQGT=zU&8K1jJBgnshU8h zNH&J)XvW(oN0)KtW3u^tUS|ejx9WS+9SBx6Cn*0KkeK9dxgVvt2w%c%blnEqgQU^I z8>6gY!_)8%j1=T{4jh^)*g?`$BH{=_>oBEb`{n^oSfA~3VW6O*UX+e-MhNHowQuV& za?4Oj%)ZdJy!W_~?kCEyj!qngN6K01Oa%GS`KI$t%DLROt#5zy<<=+r_W9|z3oLTa zZg?eQGH!AzsTK~!&|1dOsa(3~^|&8O$TRFcwZ~=C6#5=S*G=8E@$F;8NJDnh)vIj) zTxdDZ@gf=_lPjiV4HkNT2Vo6i9*ZI3&Q|ReSc;9xUqXLb?txo!-h_&2SS2^ZIHOfWO zW8U>P5ux!OEBk~4yMqZ$KT3v$N2+O=>j!90f&ESY0e>-~(pFfOn9Xryal`OmMSe4= ze_SWWbMW47!C9HfnXaz(Zw9Vyo&*JCGAKQ68{I}|*vL=UD~1nuH%vW7ido`45k|g^ zG+f5>?whADeevBj+Z_ny`7w7-wwY1pUrHfx0l=M7a4}J>e}7UpYn0}Jq#nE5EV$H=Pv`iF8tpQI9Jw>z{>wOb!mklvn(&Su_Ic2BDv|My__&C9QThHbQ9U!B z@&gQ(b(UKLkbnl@oECmuWiv51wE=(`V*}qMIAAVzE*O-kyj?m^`;&LW`nc#@mkqQ!hJFP<63e)MKP^_v}OFB zfK|6!G+^3fbjn2-yCWl#o|MihbCcCHL8xh+WX+M0aSD{qPilp?wOy-gJ*j0{l+ZDA zh9R1N3x+%vNfwn=`6NlK6f^nDluXMvASNM6qUj~Mio3r#+}dLIaEcu-SM=)0EN0%pf z$E;VHFX&P|ra#d!GAIw!iTnmb@1D#9h?EpviWmM$(h!7F+zh+fiMR(Mw|oYAW4@qt z4M*jW6N8!{Bpa7G9gq9soSz=*wQTE;x14|WG~n?Ct}i-IzQGnPx3G%3KEKGI_Lqw; zvM{AcDkxZq15bEsLg<1ST|;9JwjIi6AUzamW^!#-yde~kzq8!qg=v?c71ZTxAe&`< zI5cm;Z2@f?0_Ml>w8-?j6z;k|U6jUr!aC`8tvh{#5kqC_GbYa~khTago*#bmLbps$ zcEx!wDq0{+{G%a7Gp?@Q#!*6xwy8e8c_g|nX9RJ(?hxtrUN%{R;{6^eagyTqT_gR& zbA~^TbU6)PKT^|xHmXG*k)@-6+=^*`IG0Gc4ZTn9*!FMrii#cB@`G_hx&X%Z7;)$DQvMcS_Ff z{o(C$B-NO+;-LA#p^8;5Y?Rq=1%_zJt*N60Q%6zz(?Mb-7Wp(L(6Uj(QQ=c#NpG92dTf-9 zv5MtaPpqMN%Seu1fxg9pdABb`OUb}g7@g{<~b6Hg}OC!EYrwvFU#7#8On+Uk^+$ZNUy z7vOW%W5n)d;|A0qjn8bshIJ<0!3`Ih@#Sy0ElH3v+;{#9T+Af=GihGvqr;605unM2 zx*m+lZj`Mz$ri(TOp|3dqhfwH>rbOQ39@1Q@%)Dq7^${J-5+Nt;LqrE8~D609}}fx zRf#vtP{t+(e%qa*tQ*$J!_ZLpU+b3#ylJ&)G#J$}gTv5%yU#0*;dgy8onrc|x)-%g z6mnlM=shl+z9ZXy;dVu2tAs_>6h)b=2B)i{a*3i)YMVW-yMLKw``~i#>(X_V{PcgA zMh;r(%(RWXL*Y^WSZfnYvP5p?GdhoquXOulKCU~qBvU_27VTm6i3lQngwR$X-vKFJmtjC0^(isW;1{Zm+83gv zz<*G~X7twAvm|x)Q15+0>f-p?nPD4KtM6JouRhQ*1(a z+xp9W6Z5YTS;zZHth!})2>}+~g)rX-VtTd>U8Flg{COUgE_%^Wo4`s|t<9Dzu}S(d z+#QEwUWZ1ghnCllx!qxEamI453cU9Ez4rmRW$94PA7*gYNt6Q4km^LqzsI27C5z$p zT6zmLcaG2ZIK9#mepQ@<(kxlw#<4Ltb#QGKsA!F0rrh?0hqwp`2b;OUY1q@KU92W7 zp;|sGUV3V)1E{CWYVm}1>EZ@CqykH z$e3!oZkFAS?g?qGCGVcSXB5OHqsbwEX8TJaRCZP&@;q#aSK>Kbz zz0Aolzn8c#f_&i{+YYjl7#pv}l7|{PEXAok-C`3b;4DZyKcwYIp*2zN3m%~&i~d{X zE;lv+)6PNTrP}qyenFrkXYh(B_t3tkP&?sDVk#(Gbl(VcAl5ZDeZP#f6ed8WMt*7v zt$SGTO4tL4O|E?drB<-cDGzx{`G3C2GkJK{I_J<1q}1Z&r~0e3d|ga0a8$9M0o5GS zlz>6R;EtNIW#NAC!}miCvnIh)XGaFWSMeOnxaU0=AKDx#-YP>~pg*xpTdo_#(11EG z58mZ>@rE6ftGx9Rs(D4z6ZiFg2?Q55sbysT;3+a;8qc3BdidT+&C%7t9lFH z+k0>f(MrS@QRLw}$g^;8`(ep!gucXw{p3KG8b&nwhva;Wd=QJ`cmWcT)FqtZIpZWe zx<$V?6*ciq?cHAhD(RTzw!&X1oV5fvqsNFtNeZ+i4TBY@iykFlxF8i=8&!}MVeAW3 zkAZK)LIm3@A~qjIZIz2@0R`|M+v#^kwoZI>5;gL|4VQEp^*+>D;fH-oP8q1C(k1 zKdxHU&%j}t5%_Xea6V_Lfmp^V)8mx$ZXeF`H&oSmw`@BBeMu~PS>;v^r_Wjl{imWT z=XoqF&1q4=j-Of=X@4#gAsiBWm>l5y^vXwJ21Ah&EMRn^fZASJDs9d+ZQe9!k zW)cP?JRq?SL-bag_jvJ`djdev^gs-BWJS5fsnFCn(Q&(|xTr5#$5Zw^$BWk=Nj?_h7@nTz+`ShPA<0s;FDg%gt;ax2@b zoUrje3DKi-R$p8}y`=Vl?&@{{ED`3v{NN@+&@qnj1vkUaJqWN01lA~uxTuc-Tl|76 zWJ3cSnM;}k=nb~j9?n&~W)Q%!Ww|<+N7lZm?bXd)@{4JZZP`41wZI4fv{hSP9Xe?s zUboL$h0@DUp4i&50brVkS2Z95pVLfLwQl9#6AK`-+L?3G{g#?C!-v-+rDqy8z%Y>k zKo)lbP|Ry-I1E^2Kzb&AGCEZ)tsJ+=?_ zulip|Hk|QcnE%8382Dqju$nC^169^B3F3=z@|Ll+utVW9=n1hif^4j^Y=1UO*i1wY zjr0J-m}n*$D-r@`rU5b!K?L-U&S9{S`wR8Ss)gL(mLhY0nwQ?$mKmzPhi1SkjlbG* zsDT8&$WKoh?j4nf6P#epBfzO+uS0=(pT>a3Dhb8Ust9sP7|+wGYom&$y#A1u4(?#F z3PVSkWY^N}>}^fH>6ywRf| zfH3)XANM3@esDuF4 zp#nby2rm(!xfTg}XxiygTCD0~XAm#f1E?%@f}4XpRrIJdGjwq7tyC315~+CrGXExh z#36*o)v8$>VzM-x2y~>>YRYGLYxuljU*09?I!ohY(hr)CrDEjJx^V{;Q%tvA&`E7W zJ%q-7sHbyQoI|G+Pk~G>EWp3ZV-YlTO2QV`2Cp=#gOW}vMseW7OJKX|xZl0OtbkLw zQ3PmbC;-rQ_a=>cW}jEBAD9kX5eMDG(%GJ62-2Y_%bC@tNr%Z>`x#^uW#7JkUx%J= zu~KaWKar6d7M1C%bFH#D+tyOrt~u0=mb1h`o0LCD`78vB@WH`44r6{c+w%o?2)hTn zmjHhbK}7bRO1Niv##{OT(8mQ_n4>dpcIfNqszL)}+LDVpCKd`0 zXz_7~IYuQ-C9bed$Ck>jFGEp?Z^~V=J4vUfEtFeo2}rbT61l0>R(4Mjk1hlO1D~H< z_~kIfbg(2{202Z3sLZX>?U=vW)~$Kzw?EaB4;@b8er)xX@AFVr)dFgf0eH+SUvNXd z>YsKj)}}q*#m;L;^;dDZ3t1H+s14+QO>YvEh&3`{OgofZ}p4Fz)jF~#0ZcH z27!~_nM{BLtLD5q4R6-#*Sq33gg16KEsEKfYgUqfO;e29Hj(rQvac{VR&LWy{o_v~A(+ z)=Ka9r28^O{Rc`UEaLl@0GjlP^4q-zvyE4(3aPLVXPWlevrlLC$Jy;

T31$=d8u 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 O{#lspGOohykNY<-u4MoK literal 0 HcmV?d00001 diff --git a/screenshots/layout-topnav.png b/screenshots/layout-topnav.png new file mode 100644 index 0000000000000000000000000000000000000000..d6cefef8f61e111f54c961c2d84ff55afbeceac0 GIT binary patch literal 96683 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

NSegv77@*%^b@3O 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 z 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

}SLwVdQ9kB6kd|jz`(Fhqi zuZ0BiEZ#+XcmpV^M;Kf8c;jKfMpp0Oa;DS{gReqjLM`V^#yOW8UOI?E&X3IW6RIoi zMzU{Y-%3OFy7~y;r{bOX!vZGft?(T3})kD(GBCG&yDgby$46 zF;zR~NOI`!aGp-p{>Qfu>TIqclpGGIvFuLG6bkzre@D}WU77L5Q^V`Wr z%^di}1*pgaOwu7>>si#b(8`%4?l~;{969-n!8spF=J7Od-4MAgWdlm}t#>g%J9HWF zbO8l6JNJ9S0T!|eMgz6CuKZ?=_^{KMoswHjkExph=5XWedsgw+WaL0`eJh;@`B6a; zfD+YO>d!^l|M)$X-DpGU^OB~ z%v5Vg&*s{Evl5TN`Zt;kJ4PU~E=up~IE$^TK9R^bAg>SLp&=*;@p7@tQTD@dj&W@b zhz+dU4?epp&VscfXBXXYDU5d#-8&#BSSGTA{Rc;pj})G8Zx&!amt3J$nv2i}d(e3~ zZ!d;Gz97h?E56pz;HV*da(kXNC7`$Ei=0bKjs7a+*LF+7q z#tOA_;-9~ccZ!a=@3h0NQg~4*l3({OV@uioh*F9^-p{bQ;#` zjM>jf_P_3J2cAX%TBVgZAAa2|9Fl$a17~_iwk=?ekwf6~M&!j=GKi--b=-P#wgzkr zP4Uei!P(8mqWc<|BTgAXu%#^`JY@0A4(CC&0@6M@Wt{f8+amOL`+SA~PNk0+X~S3( zC>+THBkLe=qJBhtsVE0Pwd&bzwiFP0Zmu;N+2L_D0Wjr}1(p>y)L4MM8eRYzLM;Mn zbs9ie`6CZ5`WU4%^8jaW_I0r5%ZnRhN(O5p8xJv4mrPQ&;>MU2ocUS9+pqm@OS8z_ zv}Ws|M-QI@;8N}e$lUy&1^QXNT=Hp6x@~rD_&dM#rqY+eW3Iqmx1kD^2U*3ZY~TAf z_bG7E*Utl2CpL!w4`4|zR?Wq%Y7Bgtisii6K7&`+8dd_gu1^8(Ox^hER4s5DKi`u* zpaJ6Kur!{N>+8O*ehNH}K^M5GaP#WgZ_%{wAIQfV+t|0nauI zJ(!es`rLKUBKK0sdf-_YT91Is0ZweM`1okk$79mr@hf}wJe!q$3N*aUsXhms0ykw` zT$J_Kk8w#rQB&0FU@X~=qtTLEb#ZXGciAkC=fDDMecH)=uU4%FFZwsTQ*c2o*q-=xf57Yp9aP(@Jy?lz+)CxuLn-!ZJl+{I@wJxc2|jR?skKX_g{G| zd@^H8SUYAT&OneU+xg_T4I8f4m#?bJxxehB)z+l-k~^z??TkGHZUPraq|Z4~cWrKm z*b@~jh1eHOgJj^2j0qb(1#ap#y!rMy&+-0-rW1L;SL*)el=>aH{d{(6DMlU0$Rw<2 z@C7*Hdg6^(z_#|eniVT_=PFeGj8OErc|q@ ztk=7J*Dv$JhQeh6o5IaM&YThId2XHHtuBl@Bw-hem~8hXf&H`#$BfKaTorUJe;RF;f;vWw_D$H z`qo}ItAkG%!`j*k4hgrwRq4UUK8`8NCAU`hYTnddkaMebcle4M`BPJtKPXzpw&?e_ zEqiO{=wsLkoP%r=@;WCHRK_i1-8%j69EbhMj^Cp8M=!YHf8oY?U`J|oDQ20(;WG(1 z5^?fd!^Y?pH?%w6gc@32u1jZn7s?x%fBXE5$v2n1tov=d%`;2q9G3Z|0|qQo;D&qL zeq($yS2OTz0YAf zp>{A$|D76R*818XwiuoRZlMr(p*=;W4A?YuxT*a^I!s~a<5yp!-u5oO|8@DoLQ8in zkv^AY7w~{SrV}^WCL6!azuxet!c P3_#%N>gTe~DWM4f6}5eu literal 0 HcmV?d00001 diff --git a/src/app/(demo)/layout-demo/centered/layout.tsx b/src/app/(demo)/layout-demo/centered/layout.tsx new file mode 100644 index 0000000..9f85b82 --- /dev/null +++ b/src/app/(demo)/layout-demo/centered/layout.tsx @@ -0,0 +1,9 @@ +import CenteredLayout from "@/modules/layouts/centered/centered.layout"; + +export default async function Layout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/src/app/(demo)/layout-demo/centered/page.tsx b/src/app/(demo)/layout-demo/centered/page.tsx new file mode 100644 index 0000000..51d0e62 --- /dev/null +++ b/src/app/(demo)/layout-demo/centered/page.tsx @@ -0,0 +1,99 @@ +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; + +export default function CenteredLayoutDemo() { + return ( +
+
+

Centered Layout

+

+ Max-width centered content for documentation, blogs, and forms +

+
+ + + + Key Features + What makes this layout great + + +
+ +
+ Max-width centered - Optimal reading width (768px) +
+
+
+ +
+ Top navigation - Simple header with user menu +
+
+
+ +
+ Clean & distraction-free - Focus on content +
+
+
+ +
+ Prose-friendly - Perfect for reading and writing +
+
+
+ +
+ Best for - Documentation, blogs, forms, wizards +
+
+
+
+ +
+

Example Documentation Content

+

+ This layout is perfect for content-heavy pages where readability is paramount. + The centered column with optimal width prevents eye strain and makes it easy + to scan through long-form content. +

+

+ Notice how the content stays within a comfortable reading width, even on + ultra-wide monitors. This is the same pattern used by popular documentation + sites and blog platforms. +

+

Code Examples

+

+ This layout also works great for technical documentation with code examples: +

+
+					{`export default function MyComponent() {
+  return 
Hello World
; +}`} +
+
+ + + + Try Other Layouts + Explore all 5 layout variants + + + + + + + + + + + + + + + + +
+ ); +} diff --git a/src/app/(demo)/layout-demo/hybrid/layout.tsx b/src/app/(demo)/layout-demo/hybrid/layout.tsx new file mode 100644 index 0000000..4dddff3 --- /dev/null +++ b/src/app/(demo)/layout-demo/hybrid/layout.tsx @@ -0,0 +1,9 @@ +import HybridLayout from "@/modules/layouts/hybrid/hybrid.layout"; + +export default async function Layout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/src/app/(demo)/layout-demo/hybrid/page.tsx b/src/app/(demo)/layout-demo/hybrid/page.tsx new file mode 100644 index 0000000..e1d4be0 --- /dev/null +++ b/src/app/(demo)/layout-demo/hybrid/page.tsx @@ -0,0 +1,115 @@ +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; + +export default function HybridLayoutDemo() { + return ( +
+
+

Hybrid Layout

+

+ Top header + sidebar for complex enterprise applications +

+
+ + + + Key Features + What makes this layout great + + +
+ +
+ Top header bar - Branding and global actions +
+
+
+ +
+ Left sidebar - Main navigation menu +
+
+
+ +
+ Most polished - Linear/Notion style appearance +
+
+
+ +
+ Separates concerns - Branding separate from navigation +
+
+
+ +
+ Best for - Complex SaaS, enterprise apps +
+
+
+
+ +
+ + + Organizations + + +
8
+

3 active

+
+
+ + + Workspaces + + +
15
+

12 configured

+
+
+ + + API Keys + + +
23
+

5 expiring soon

+
+
+ + + Integrations + + +
12
+

All active

+
+
+
+ + + + Try Other Layouts + Explore all 5 layout variants + + + + + + + + + + + + + + + + +
+ ); +} diff --git a/src/app/(demo)/layout-demo/sidebar/layout.tsx b/src/app/(demo)/layout-demo/sidebar/layout.tsx new file mode 100644 index 0000000..76eb463 --- /dev/null +++ b/src/app/(demo)/layout-demo/sidebar/layout.tsx @@ -0,0 +1,9 @@ +import SidebarLayout from "@/modules/layouts/sidebar/sidebar.layout"; + +export default async function Layout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/src/app/(demo)/layout-demo/sidebar/page.tsx b/src/app/(demo)/layout-demo/sidebar/page.tsx new file mode 100644 index 0000000..91557c7 --- /dev/null +++ b/src/app/(demo)/layout-demo/sidebar/page.tsx @@ -0,0 +1,106 @@ +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; + +export default function SidebarLayoutDemo() { + return ( +
+
+

Sidebar Layout

+

+ Collapsible sidebar perfect for dashboards, CRMs, and admin panels +

+
+ + + + Key Features + What makes this layout great + + +
+ +
+ Collapsible sidebar - Toggle between full (16rem) and icon mode (3rem) +
+
+
+ +
+ Keyboard shortcut - Press Cmd/Ctrl+B to toggle +
+
+
+ +
+ Cookie persistence - Remembers your preference (7 days) +
+
+
+ +
+ Mobile responsive - Sheet overlay on mobile devices +
+
+
+ +
+ Full-width content - No width constraints +
+
+
+
+ +
+ + + Total Users + + +
2,543
+

+12% from last month

+
+
+ + + Active Sessions + + +
1,234
+

+8% from last month

+
+
+ + + Revenue + + +
$45,231
+

+23% from last month

+
+
+
+ + + + Try Other Layouts + Explore all 5 layout variants + + + + + + + + + + + + + + + + +
+ ); +} diff --git a/src/app/(demo)/layout-demo/top-nav/layout.tsx b/src/app/(demo)/layout-demo/top-nav/layout.tsx new file mode 100644 index 0000000..dadd04e --- /dev/null +++ b/src/app/(demo)/layout-demo/top-nav/layout.tsx @@ -0,0 +1,9 @@ +import TopNavLayout from "@/modules/layouts/top-nav/top-nav.layout"; + +export default async function Layout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/src/app/(demo)/layout-demo/top-nav/page.tsx b/src/app/(demo)/layout-demo/top-nav/page.tsx new file mode 100644 index 0000000..797c1b1 --- /dev/null +++ b/src/app/(demo)/layout-demo/top-nav/page.tsx @@ -0,0 +1,106 @@ +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; + +export default function TopNavLayoutDemo() { + return ( +
+
+

Top Nav Layout

+

+ Horizontal navigation for simple apps and tools +

+
+ + + + Key Features + What makes this layout great + + +
+ +
+ Horizontal navigation - All links visible in top bar +
+
+
+ +
+ Full-width content - No sidebar taking up space +
+
+
+ +
+ Mobile responsive - Hamburger menu with Sheet drawer +
+
+
+ +
+ Sticky header - Navigation always accessible +
+
+
+ +
+ Best for - Simple apps, tools, utilities +
+
+
+
+ +
+ + + Projects + + +
24
+

+3 this week

+
+
+ + + Tasks + + +
156
+

42 completed

+
+
+ + + Team Members + + +
12
+

2 online now

+
+
+
+ + + + Try Other Layouts + Explore all 5 layout variants + + + + + + + + + + + + + + + + +
+ ); +} diff --git a/src/app/marketing-demo/layout.tsx b/src/app/marketing-demo/layout.tsx new file mode 100644 index 0000000..17096a0 --- /dev/null +++ b/src/app/marketing-demo/layout.tsx @@ -0,0 +1,9 @@ +import MarketingLayout from "@/modules/layouts/marketing/marketing.layout"; + +export default function Layout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/src/app/marketing-demo/page.tsx b/src/app/marketing-demo/page.tsx new file mode 100644 index 0000000..47e4b22 --- /dev/null +++ b/src/app/marketing-demo/page.tsx @@ -0,0 +1,160 @@ +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; + +export default function MarketingLayoutDemo() { + return ( +
+ {/* Hero Section */} +
+
+
+

+ Marketing Layout +

+

+ Perfect for landing pages, pricing pages, and public marketing sites. + No authentication required. +

+
+ + + + + + +
+
+
+
+ + {/* Features Section */} +
+
+

Key Features

+

+ What makes this layout great +

+
+ + + No Authentication + + +

+ Public-facing pages don't require login. Perfect for landing pages. +

+
+
+ + + Header with CTA + + +

+ Sticky header with Log in / Sign up buttons for conversions. +

+
+
+ + + Full-Width Content + + +

+ Support for hero sections, grid layouts, and wide content. +

+
+
+ + + Footer with Links + + +

+ Comprehensive footer with navigation, legal, and company links. +

+
+
+ + + Theme Toggle + + +

+ Dark/light mode support for better user experience. +

+
+
+ + + Mobile Responsive + + +

+ Fully responsive design that works on all devices. +

+
+
+
+
+
+ + {/* Stats Section */} +
+
+
+
+
5
+
+ Layout Variants +
+
+
+
100%
+
+ Responsive Design +
+
+
+
0
+
+ Additional Dependencies +
+
+
+
+
+ + {/* CTA Section */} +
+
+ + + Try Other Layouts + + Explore all 5 production-ready layout variants + + + + + + + + + + + + + + + + + +
+
+
+ ); +} diff --git a/src/modules/dashboard/dashboard.page.tsx b/src/modules/dashboard/dashboard.page.tsx index 5e79f3b..3ef3617 100644 --- a/src/modules/dashboard/dashboard.page.tsx +++ b/src/modules/dashboard/dashboard.page.tsx @@ -1,4 +1,4 @@ -import { CheckSquare, List, Plus } from "lucide-react"; +import { CheckSquare, List, Plus, Layout } from "lucide-react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; import { @@ -22,7 +22,7 @@ export default async function Dashboard() {

-
+
@@ -62,6 +62,26 @@ export default async function Dashboard() { + + + + + + Layout Demos + + + Explore 5 production-ready layout variants + + + + + + + +
From 3dca156e59944dffb40cfe30a57c1d03e1661eb6 Mon Sep 17 00:00:00 2001 From: Jez Date: Sun, 9 Nov 2025 12:21:29 +1100 Subject: [PATCH 32/34] fix: address CodeRabbit feedback on PRs #28, #29, #20 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Phase 1: Remove CRM Content Leak** - Delete docs/DATABASE_SCHEMA.md (CRM contacts/deals/tags from separate project) - Delete docs/IMPLEMENTATION_PHASES.md (CRM implementation phases) **Phase 2: Critical Accessibility Fixes (PR #28)** - Add ref forwarding to Popover components (PopoverTrigger, PopoverContent, PopoverAnchor) - Prevents accessibility issues with asChild pattern - Follows shadcn/ui standards with React.forwardRef + displayName **Phase 3: UX Improvements (PR #28)** - Add visual validation feedback to color picker input - Show red border when hex color is invalid - Allow partial input while typing - Remove redundant backgroundColor from button (keep only on inner div) **Phase 4: Markdown Fixes (PR #29)** - Add 'plaintext' language identifier to code block (line 67) - Fix hyphenation: "export to PDF" → "export-to-PDF" (line 1030) **Note on PR #20:** - createCategory already uses correct discriminated union pattern - No changes needed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- MODULE_TEMPLATE.md | 4 +- docs/DATABASE_SCHEMA.md | 755 -------------------------- docs/IMPLEMENTATION_PHASES.md | 836 ----------------------------- src/components/ui/color-picker.tsx | 13 +- src/components/ui/popover.tsx | 72 +-- 5 files changed, 54 insertions(+), 1626 deletions(-) delete mode 100644 docs/DATABASE_SCHEMA.md delete mode 100644 docs/IMPLEMENTATION_PHASES.md diff --git a/MODULE_TEMPLATE.md b/MODULE_TEMPLATE.md index dbbec0b..f33f844 100644 --- a/MODULE_TEMPLATE.md +++ b/MODULE_TEMPLATE.md @@ -64,7 +64,7 @@ mkdir -p src/modules/invoices/{actions,components,models,schemas,utils} ``` **Result:** -``` +```plaintext src/modules/invoices/ ├── actions/ ← Server actions (create, read, update, delete) ├── components/ ← React components @@ -1027,7 +1027,7 @@ Before deploying your new module: - Add search/filter functionality - Implement pagination - Add sorting options -- Create export to PDF feature +- Create export-to-PDF feature - Add email notifications - Implement recurring invoices diff --git a/docs/DATABASE_SCHEMA.md b/docs/DATABASE_SCHEMA.md deleted file mode 100644 index 74a0e4d..0000000 --- a/docs/DATABASE_SCHEMA.md +++ /dev/null @@ -1,755 +0,0 @@ -# Database Schema: Fullstack Next.js + Cloudflare CRM - -**Database**: Cloudflare D1 (distributed SQLite) -**Migrations**: Located in `drizzle/` -**ORM**: Drizzle ORM -**Schema Files**: `src/modules/*/schemas/*.schema.ts` - ---- - -## Overview - -The CRM extends the existing template database (users, sessions, todos, categories) with 4 new tables for contacts and deals management. - -**Existing Tables** (from template): -- `user` - User accounts (Better Auth) -- `session` - Active sessions (Better Auth) -- `account` - OAuth provider accounts (Better Auth) -- `verification` - Email verification tokens (Better Auth) -- `todos` - Task management -- `categories` - Todo categories - -**New CRM Tables** (added in Phase 2): -- `contacts` - Contact profiles -- `contact_tags` - User-defined tags -- `contacts_to_tags` - Many-to-many junction table -- `deals` - Sales pipeline deals - ---- - -## Entity Relationship Diagram - -```mermaid -erDiagram - user ||--o{ contacts : "owns" - user ||--o{ contact_tags : "creates" - user ||--o{ deals : "manages" - - contacts ||--o{ contacts_to_tags : "tagged with" - contact_tags ||--o{ contacts_to_tags : "applied to" - - contacts ||--o{ deals : "associated with" - - user { - integer id PK - text email UK - text name - text image - integer emailVerified - integer createdAt - integer updatedAt - } - - contacts { - integer id PK - text firstName - text lastName - text email - text phone - text company - text jobTitle - text notes - integer userId FK - integer createdAt - integer updatedAt - } - - contact_tags { - integer id PK - text name - text color - integer userId FK - integer createdAt - } - - contacts_to_tags { - integer contactId FK - integer tagId FK - } - - deals { - integer id PK - text title - integer contactId FK - real value - text currency - text stage - integer expectedCloseDate - text description - integer userId FK - integer createdAt - integer updatedAt - } -``` - ---- - -## Table Definitions - -### `contacts` - -**Purpose**: Store contact information for CRM - -| Column | Type | Constraints | Notes | -|--------|------|-------------|-------| -| id | INTEGER | PRIMARY KEY AUTOINCREMENT | Unique contact ID | -| firstName | TEXT | | Optional - at least one name required | -| lastName | TEXT | | Optional - at least one name required | -| email | TEXT | | Optional but must be valid format | -| phone | TEXT | | Optional, freeform (no format validation) | -| company | TEXT | | Optional company name | -| jobTitle | TEXT | | Optional job title/role | -| notes | TEXT | | Optional freeform notes | -| userId | INTEGER | NOT NULL, FOREIGN KEY → user(id) | Owner of contact | -| createdAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | -| updatedAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | - -**Indexes**: -- `idx_contacts_user_id` on `userId` (filter by user) -- `idx_contacts_email` on `email` (search by email) -- `idx_contacts_company` on `company` (search by company) - -**Relationships**: -- Many-to-one with `user` (each contact owned by one user) -- Many-to-many with `contact_tags` (via junction table) -- One-to-many with `deals` (contact can have multiple deals) - -**Business Rules**: -- At least one of `firstName` or `lastName` must be non-empty (enforced in app, not DB) -- Email must be unique per user (optional constraint, not enforced in MVP) -- Deleting a user cascades to delete their contacts - ---- - -### `contact_tags` - -**Purpose**: User-defined tags for organizing contacts - -| Column | Type | Constraints | Notes | -|--------|------|-------------|-------| -| id | INTEGER | PRIMARY KEY AUTOINCREMENT | Unique tag ID | -| name | TEXT | NOT NULL | Tag name (e.g., "Customer", "Lead") | -| color | TEXT | NOT NULL | Hex color code (e.g., "#3B82F6") | -| userId | INTEGER | NOT NULL, FOREIGN KEY → user(id) | Tag owner | -| createdAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | - -**Indexes**: -- `idx_contact_tags_user_id` on `userId` (filter by user) -- `idx_contact_tags_name_user` on `(name, userId)` (prevent duplicate tag names per user) - -**Relationships**: -- Many-to-one with `user` (each tag owned by one user) -- Many-to-many with `contacts` (via junction table) - -**Business Rules**: -- Tag name must be unique per user (enforced in app) -- Color must be valid hex format (enforced in app with Zod: `/^#[0-9A-Fa-f]{6}$/`) -- Deleting a tag removes all tag assignments (via cascade on junction table) - ---- - -### `contacts_to_tags` (Junction Table) - -**Purpose**: Many-to-many relationship between contacts and tags - -| Column | Type | Constraints | Notes | -|--------|------|-------------|-------| -| contactId | INTEGER | FOREIGN KEY → contacts(id) ON DELETE CASCADE | Contact being tagged | -| tagId | INTEGER | FOREIGN KEY → contact_tags(id) ON DELETE CASCADE | Tag being applied | - -**Composite Primary Key**: `(contactId, tagId)` - -**Indexes**: -- Primary key index on `(contactId, tagId)` (prevent duplicate assignments) -- `idx_contacts_to_tags_tag_id` on `tagId` (query contacts by tag) - -**Relationships**: -- Many-to-one with `contacts` -- Many-to-one with `contact_tags` - -**Business Rules**: -- A contact can have multiple tags -- A tag can be applied to multiple contacts -- Same tag cannot be assigned to a contact twice (enforced by composite PK) -- Deleting a contact removes all its tag assignments (ON DELETE CASCADE) -- Deleting a tag removes all its assignments (ON DELETE CASCADE) - ---- - -### `deals` - -**Purpose**: Sales pipeline deals linked to contacts - -| Column | Type | Constraints | Notes | -|--------|------|-------------|-------| -| id | INTEGER | PRIMARY KEY AUTOINCREMENT | Unique deal ID | -| title | TEXT | NOT NULL | Deal name/description | -| contactId | INTEGER | FOREIGN KEY → contacts(id) ON DELETE SET NULL | Linked contact (optional) | -| value | REAL | NOT NULL | Deal value (e.g., 5000.00) | -| currency | TEXT | NOT NULL DEFAULT 'AUD' | Currency code (ISO 4217) | -| stage | TEXT | NOT NULL | Pipeline stage (see enum below) | -| expectedCloseDate | INTEGER | | Expected close date (unix timestamp) | -| description | TEXT | | Optional deal notes | -| userId | INTEGER | NOT NULL, FOREIGN KEY → user(id) | Deal owner | -| createdAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | -| updatedAt | INTEGER | NOT NULL | Unix timestamp (milliseconds) | - -**Stage Enum** (enforced in app): -- `"Prospecting"` - Initial contact -- `"Qualification"` - Evaluating fit -- `"Proposal"` - Proposal sent -- `"Negotiation"` - Negotiating terms -- `"Closed Won"` - Deal won -- `"Closed Lost"` - Deal lost - -**Indexes**: -- `idx_deals_user_id` on `userId` (filter by user) -- `idx_deals_contact_id` on `contactId` (filter by contact) -- `idx_deals_stage` on `stage` (filter by pipeline stage) -- `idx_deals_user_stage` on `(userId, stage)` (pipeline board queries) - -**Relationships**: -- Many-to-one with `user` (each deal owned by one user) -- Many-to-one with `contacts` (optional - deal can exist without contact) - -**Business Rules**: -- Contact link is optional (`contactId` can be NULL) -- Deleting a contact does NOT delete deals (sets `contactId` to NULL) -- Stage must be one of the 6 valid enum values (enforced in app with Zod) -- Value must be positive (enforced in app) -- Currency defaults to AUD (user can change in form) -- Deleting a user cascades to delete their deals - ---- - -## Data Types & Conventions - -### Timestamps - -**Storage**: INTEGER (unix timestamp in milliseconds) - -**Creation**: -```typescript -const now = Date.now() // JavaScript -``` - -**Display**: -```typescript -new Date(timestamp).toLocaleDateString('en-AU') -new Date(timestamp).toLocaleString('en-AU') -``` - -**Drizzle Schema**: -```typescript -createdAt: integer('created_at', { mode: 'number' }) - .notNull() - .$defaultFn(() => Date.now()) -``` - -### Foreign Keys - -**Syntax in Drizzle**: -```typescript -userId: integer('user_id') - .notNull() - .references(() => userTable.id, { onDelete: 'cascade' }) -``` - -**Cascade Behavior**: -- `onDelete: 'cascade'` - Delete child records when parent deleted -- `onDelete: 'set null'` - Set foreign key to NULL when parent deleted - -**Applied**: -- User → Contacts: CASCADE (delete user's contacts) -- User → Tags: CASCADE (delete user's tags) -- User → Deals: CASCADE (delete user's deals) -- Contact → Deals: SET NULL (keep deal, remove contact link) -- Contact → Junction: CASCADE (remove tag assignments) -- Tag → Junction: CASCADE (remove contact assignments) - -### Enums - -**Deal Stage** (not enforced in DB, only in app): -```typescript -export const dealStageEnum = [ - 'Prospecting', - 'Qualification', - 'Proposal', - 'Negotiation', - 'Closed Won', - 'Closed Lost', -] as const - -export type DealStage = typeof dealStageEnum[number] -``` - -**Validation in Zod**: -```typescript -stage: z.enum(dealStageEnum) -``` - -### Text Fields - -**SQLite TEXT type** (no length limits): -- Short fields: email, phone, name, color -- Long fields: notes, description - -**Encoding**: UTF-8 - -**Collation**: Use `COLLATE NOCASE` for case-insensitive searches: -```sql -WHERE email LIKE '%query%' COLLATE NOCASE -``` - -### Numeric Fields - -**INTEGER**: Whole numbers (IDs, timestamps, foreign keys) -**REAL**: Floating point (deal values) - ---- - -## Migrations - -### Migration 0001: Initial Schema (Template) - -**File**: `drizzle/0000_*.sql` - -**Creates**: -- `user` table (Better Auth) -- `session` table (Better Auth) -- `account` table (Better Auth) -- `verification` table (Better Auth) -- `todos` table (template feature) -- `categories` table (template feature) - -**Status**: Already applied (template setup) - ---- - -### Migration 0002: CRM Schema (Phase 2) - -**File**: `drizzle/0002_*.sql` - -**Creates**: -- `contacts` table with indexes -- `contact_tags` table with indexes -- `contacts_to_tags` junction table with composite PK -- `deals` table with indexes - -**SQL Preview**: -```sql -CREATE TABLE contacts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - first_name TEXT, - last_name TEXT, - email TEXT, - phone TEXT, - company TEXT, - job_title TEXT, - notes TEXT, - user_id INTEGER NOT NULL REFERENCES user(id) ON DELETE CASCADE, - created_at INTEGER NOT NULL, - updated_at INTEGER NOT NULL -); - -CREATE INDEX idx_contacts_user_id ON contacts(user_id); -CREATE INDEX idx_contacts_email ON contacts(email); -CREATE INDEX idx_contacts_company ON contacts(company); - -CREATE TABLE contact_tags ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - color TEXT NOT NULL, - user_id INTEGER NOT NULL REFERENCES user(id) ON DELETE CASCADE, - created_at INTEGER NOT NULL -); - -CREATE INDEX idx_contact_tags_user_id ON contact_tags(user_id); -CREATE UNIQUE INDEX idx_contact_tags_name_user ON contact_tags(name, user_id); - -CREATE TABLE contacts_to_tags ( - contact_id INTEGER NOT NULL REFERENCES contacts(id) ON DELETE CASCADE, - tag_id INTEGER NOT NULL REFERENCES contact_tags(id) ON DELETE CASCADE, - PRIMARY KEY (contact_id, tag_id) -); - -CREATE INDEX idx_contacts_to_tags_tag_id ON contacts_to_tags(tag_id); - -CREATE TABLE deals ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - title TEXT NOT NULL, - contact_id INTEGER REFERENCES contacts(id) ON DELETE SET NULL, - value REAL NOT NULL, - currency TEXT NOT NULL DEFAULT 'AUD', - stage TEXT NOT NULL, - expected_close_date INTEGER, - description TEXT, - user_id INTEGER NOT NULL REFERENCES user(id) ON DELETE CASCADE, - created_at INTEGER NOT NULL, - updated_at INTEGER NOT NULL -); - -CREATE INDEX idx_deals_user_id ON deals(user_id); -CREATE INDEX idx_deals_contact_id ON deals(contact_id); -CREATE INDEX idx_deals_stage ON deals(stage); -CREATE INDEX idx_deals_user_stage ON deals(user_id, stage); -``` - -**Apply**: -```bash -# Local -pnpm run db:migrate:local - -# Production -pnpm run db:migrate -``` - ---- - -## Seed Data (Phase 6) - -### Contacts - -10 sample contacts with variety: -- 3 contacts with multiple tags -- 2 contacts linked to deals -- Mix of complete and minimal profiles - -Example: -```typescript -{ - firstName: 'Sarah', - lastName: 'Chen', - email: 'sarah.chen@example.com', - phone: '+61 412 345 678', - company: 'TechCorp Australia', - jobTitle: 'CTO', - notes: 'Met at DevConf 2024. Interested in AI features.', - userId: 1, - createdAt: Date.now(), - updatedAt: Date.now() -} -``` - -### Tags - -5 common tags: -- Customer (#10B981 - green) -- Lead (#3B82F6 - blue) -- Partner (#8B5CF6 - purple) -- Inactive (#6B7280 - gray) -- VIP (#F59E0B - amber) - -### Deals - -5 deals across all stages: -```typescript -[ - { title: 'Enterprise License', stage: 'Prospecting', value: 5000 }, - { title: 'Consulting Package', stage: 'Qualification', value: 12000 }, - { title: 'Annual Support', stage: 'Proposal', value: 25000 }, - { title: 'Cloud Migration', stage: 'Closed Won', value: 50000 }, - { title: 'Training Program', stage: 'Closed Lost', value: 8000 }, -] -``` - ---- - -## Query Patterns - -### Fetch Contacts with Tags - -```typescript -// Using Drizzle ORM -const contactsWithTags = await db - .select({ - id: contacts.id, - firstName: contacts.firstName, - lastName: contacts.lastName, - email: contacts.email, - company: contacts.company, - tags: sql`json_group_array(json_object('id', ${contactTags.id}, 'name', ${contactTags.name}, 'color', ${contactTags.color}))`, - }) - .from(contacts) - .leftJoin(contactsToTags, eq(contacts.id, contactsToTags.contactId)) - .leftJoin(contactTags, eq(contactsToTags.tagId, contactTags.id)) - .where(eq(contacts.userId, userId)) - .groupBy(contacts.id) -``` - -### Search Contacts - -```typescript -// Case-insensitive search across name, email, company -const results = await db - .select() - .from(contacts) - .where( - and( - eq(contacts.userId, userId), - or( - sql`${contacts.firstName} LIKE ${`%${query}%`} COLLATE NOCASE`, - sql`${contacts.lastName} LIKE ${`%${query}%`} COLLATE NOCASE`, - sql`${contacts.email} LIKE ${`%${query}%`} COLLATE NOCASE`, - sql`${contacts.company} LIKE ${`%${query}%`} COLLATE NOCASE` - ) - ) - ) -``` - -### Fetch Deals with Contact Info - -```typescript -// Join deals with contacts -const dealsWithContacts = await db - .select({ - id: deals.id, - title: deals.title, - value: deals.value, - currency: deals.currency, - stage: deals.stage, - contactName: sql`${contacts.firstName} || ' ' || ${contacts.lastName}`, - contactEmail: contacts.email, - }) - .from(deals) - .leftJoin(contacts, eq(deals.contactId, contacts.id)) - .where(eq(deals.userId, userId)) - .orderBy(deals.createdAt) -``` - -### Dashboard Metrics - -```typescript -// Count contacts -const totalContacts = await db - .select({ count: sql`count(*)` }) - .from(contacts) - .where(eq(contacts.userId, userId)) - -// Count active deals -const activeDeals = await db - .select({ count: sql`count(*)` }) - .from(deals) - .where( - and( - eq(deals.userId, userId), - notInArray(deals.stage, ['Closed Won', 'Closed Lost']) - ) - ) - -// Sum pipeline value -const pipelineValue = await db - .select({ total: sql`sum(${deals.value})` }) - .from(deals) - .where( - and( - eq(deals.userId, userId), - notInArray(deals.stage, ['Closed Won', 'Closed Lost']) - ) - ) -``` - ---- - -## Performance Considerations - -### Indexes - -**Query Patterns**: -- Filter by userId (every query) → Index on `user_id` for all tables -- Search by email/company → Indexes on `email`, `company` in contacts -- Filter deals by stage → Index on `stage` -- Pipeline board query → Composite index on `(user_id, stage)` - -**Trade-offs**: -- Indexes speed up SELECT queries -- Indexes slow down INSERT/UPDATE/DELETE (minimal impact for CRM scale) -- SQLite handles small indexes efficiently - -### N+1 Query Prevention - -**Anti-pattern**: -```typescript -// BAD: N+1 queries -const contacts = await getContacts(userId) -for (const contact of contacts) { - const tags = await getTagsForContact(contact.id) // N queries! -} -``` - -**Solution**: -```typescript -// GOOD: Single query with JOIN -const contactsWithTags = await db - .select() - .from(contacts) - .leftJoin(contactsToTags, ...) - .leftJoin(contactTags, ...) - .groupBy(contacts.id) -``` - -### Pagination - -**For lists >50 items**: -```typescript -const pageSize = 50 -const offset = (page - 1) * pageSize - -const contacts = await db - .select() - .from(contacts) - .where(eq(contacts.userId, userId)) - .limit(pageSize) - .offset(offset) -``` - -**Not needed for MVP** (small data sets), but good practice for Phase 2. - ---- - -## Security - -### User Isolation - -**Critical**: Every query MUST filter by `userId` to prevent data leakage. - -**Pattern**: -```typescript -// Always include userId in WHERE clause -await db - .select() - .from(contacts) - .where(eq(contacts.userId, currentUserId)) -``` - -**Enforcement**: -- Server Actions use `requireAuth()` to get `currentUserId` -- Never trust client-provided `userId` parameter -- Always re-fetch user from session token - -### Ownership Verification - -**Before UPDATE/DELETE**: -```typescript -// 1. Fetch record -const contact = await db.query.contacts.findFirst({ - where: eq(contacts.id, contactId) -}) - -// 2. Verify ownership -if (contact.userId !== currentUserId) { - throw new Error('Forbidden') -} - -// 3. Mutate -await db.update(contacts).set(...).where(eq(contacts.id, contactId)) -``` - -### SQL Injection Prevention - -**Drizzle ORM handles parameterization automatically**: -```typescript -// Safe - Drizzle uses prepared statements -const results = await db - .select() - .from(contacts) - .where(sql`email LIKE ${`%${userInput}%`}`) -// userInput is safely escaped -``` - -**Never use string concatenation**: -```typescript -// UNSAFE - DO NOT DO THIS -const query = `SELECT * FROM contacts WHERE email = '${userInput}'` -``` - ---- - -## Backup & Recovery - -### Local D1 Backup - -**Location**: `.wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite` - -**Backup command**: -```bash -cp .wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite backup-$(date +%Y%m%d).sqlite -``` - -### Production D1 Backup - -**Export**: -```bash -npx wrangler d1 export fullstack-crm --remote --output backup.sql -``` - -**Restore**: -```bash -npx wrangler d1 execute fullstack-crm --remote --file backup.sql -``` - -**Frequency**: Manual for MVP, automate with GitHub Actions for production. - ---- - -## Future Enhancements (Phase 2) - -### Activity Timeline - -Add `contact_activities` table: -```sql -CREATE TABLE contact_activities ( - id INTEGER PRIMARY KEY, - contact_id INTEGER REFERENCES contacts(id) ON DELETE CASCADE, - type TEXT NOT NULL, -- 'call', 'email', 'meeting', 'note' - subject TEXT, - description TEXT, - timestamp INTEGER NOT NULL, - user_id INTEGER REFERENCES user(id) ON DELETE CASCADE -); -``` - -### Custom Deal Stages - -Add `deal_stages` table (user-defined): -```sql -CREATE TABLE deal_stages ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL, - order_index INTEGER NOT NULL, - color TEXT, - user_id INTEGER REFERENCES user(id) ON DELETE CASCADE, - UNIQUE(name, user_id) -); -``` - -Modify `deals.stage` to reference `deal_stages.id` instead of enum. - -### Soft Delete - -Add `deleted_at` column to contacts and deals: -```sql -ALTER TABLE contacts ADD COLUMN deleted_at INTEGER; -ALTER TABLE deals ADD COLUMN deleted_at INTEGER; -``` - -Filter deleted records: `WHERE deleted_at IS NULL` - ---- - -## References - -- **Drizzle ORM Docs**: https://orm.drizzle.team/docs/overview -- **SQLite Data Types**: https://www.sqlite.org/datatype3.html -- **D1 Documentation**: https://developers.cloudflare.com/d1/ -- **Better Auth Schema**: https://www.better-auth.com/docs/concepts/database diff --git a/docs/IMPLEMENTATION_PHASES.md b/docs/IMPLEMENTATION_PHASES.md deleted file mode 100644 index d38876a..0000000 --- a/docs/IMPLEMENTATION_PHASES.md +++ /dev/null @@ -1,836 +0,0 @@ -# Implementation Phases: Fullstack Next.js + Cloudflare CRM - -**Project Type**: Learning Exercise - CRM Features -**Stack**: Next.js 15 + Cloudflare Workers + D1 + Drizzle ORM + Tailwind v4 + shadcn/ui -**Estimated Total**: 6-8 hours (~6-8 minutes human time with Claude Code) - ---- - -## Phase 1: Project Setup - -**Type**: Infrastructure -**Estimated**: 30 minutes -**Files**: `wrangler.jsonc`, `.dev.vars`, `package.json` - -### Purpose - -Clone the project to a new directory, configure Cloudflare D1 database for your account, set up environment variables, and verify the development environment works. - -### File Map - -- `wrangler.jsonc` (modify existing) - - **Purpose**: Cloudflare Workers configuration - - **Modifications**: Update D1 database ID to your account's database - - **Used by**: Wrangler CLI for deployment and local dev - -- `.dev.vars` (create new) - - **Purpose**: Local development secrets - - **Contains**: Better Auth secrets, Google OAuth credentials - - **Used by**: Local development server - -### Tasks - -- [ ] Clone project from `/home/jez/Documents/fullstack-next-cloudflare-demo` to `/home/jez/Documents/fullstack-next-cloudflare-crm` -- [ ] Install dependencies with `pnpm install` -- [ ] Create new D1 database: `npx wrangler d1 create fullstack-crm` -- [ ] Update `wrangler.jsonc` with new database ID (replace `757a32d1-5779-4f09-bcf3-b268013395d4`) -- [ ] Create `.dev.vars` file with Better Auth secrets (copy from demo project if available) -- [ ] Run existing migrations: `pnpm run db:migrate:local` -- [ ] Start dev servers (two terminals): `pnpm run wrangler:dev` and `pnpm run dev` -- [ ] Verify app loads at http://localhost:5173 -- [ ] Create git repository and initial commit - -### Verification Criteria - -- [ ] App loads without errors in browser -- [ ] Can navigate to /dashboard (after login) -- [ ] Existing todos feature works (proves D1 connection) -- [ ] No console errors -- [ ] Git repository initialized - -### Exit Criteria - -Development environment is fully functional with new D1 database configured. Can run both Wrangler and Next.js dev servers simultaneously. Existing template features work correctly. - ---- - -## Phase 2: Database Schema - -**Type**: Database -**Estimated**: 1 hour -**Files**: `src/modules/contacts/schemas/contact.schema.ts`, `src/modules/contacts/schemas/tag.schema.ts`, `src/modules/deals/schemas/deal.schema.ts`, `drizzle/0002_crm_schema.sql` - -### Purpose - -Create database tables for contacts, tags, and deals with proper relationships. Follows the existing pattern from `todos` and `categories` tables. - -### File Map - -- `src/modules/contacts/schemas/contact.schema.ts` (new ~60 lines) - - **Purpose**: Drizzle schema for contacts table and tags - - **Key exports**: contactsTable, contactTagsTable, contactsToTagsTable - - **Dependencies**: drizzle-orm/d1, src/modules/auth/schemas (user relation) - - **Used by**: Contact actions, migration generation - -- `src/modules/deals/schemas/deal.schema.ts` (new ~50 lines) - - **Purpose**: Drizzle schema for deals table - - **Key exports**: dealsTable, dealStageEnum - - **Dependencies**: drizzle-orm/d1, contacts schema (foreign key) - - **Used by**: Deal actions, migration generation - -- `src/db/schema.ts` (modify existing) - - **Purpose**: Aggregate all schemas for migrations - - **Modifications**: Export new CRM schemas - - **Used by**: Drizzle Kit for migration generation - -- `drizzle/0002_crm_schema.sql` (generated) - - **Purpose**: Migration file to create CRM tables - - **Creates**: contacts, contact_tags, contacts_to_tags, deals tables - - **Generated by**: `pnpm drizzle-kit generate` - -### Data Flow - -```mermaid -erDiagram - users ||--o{ contacts : "owns" - users ||--o{ contact_tags : "owns" - users ||--o{ deals : "owns" - contacts ||--o{ contacts_to_tags : "has" - contact_tags ||--o{ contacts_to_tags : "tagged" - contacts ||--o{ deals : "linked to" - - users { - integer id PK - text email - text name - } - - contacts { - integer id PK - text firstName - text lastName - text email - text phone - text company - text jobTitle - text notes - integer userId FK - integer createdAt - integer updatedAt - } - - contact_tags { - integer id PK - text name - text color - integer userId FK - integer createdAt - } - - contacts_to_tags { - integer contactId FK - integer tagId FK - } - - deals { - integer id PK - text title - integer contactId FK - real value - text currency - text stage - integer expectedCloseDate - text description - integer userId FK - integer createdAt - integer updatedAt - } -``` - -### Critical Dependencies - -**Internal**: -- Auth schemas (`src/modules/auth/schemas`) for user foreign keys -- Existing D1 database from Phase 1 - -**External**: -- `drizzle-orm` - ORM for type-safe queries -- `drizzle-kit` - Migration generation - -**Configuration**: -- `drizzle.local.config.ts` - Points to local D1 database - -### Gotchas & Known Issues - -**Many-to-Many Pattern**: -- Junction table `contacts_to_tags` requires composite primary key `(contactId, tagId)` -- Drizzle syntax: `primaryKey: ["contactId", "tagId"]` -- Querying requires joins - study Drizzle docs for many-to-many queries - -**Foreign Key Constraints**: -- SQLite (D1) supports foreign keys but they must be enabled -- Use `.onDelete("cascade")` for tags (deleting tag removes associations) -- Use `.onDelete("set null")` for deals.contactId (deleting contact keeps deal) - -**Timestamp Pattern**: -- Store as INTEGER (unix timestamp in milliseconds) -- Use `.$defaultFn(() => Date.now())` for createdAt -- Use `.notNull()` for required fields - -**Deal Stages as Enum**: -- Fixed stages for MVP: Prospecting, Qualification, Proposal, Negotiation, Closed Won, Closed Lost -- Stored as TEXT with enum constraint -- Phase 2 feature: Custom user-defined stages (requires separate table) - -### Tasks - -- [ ] Create `src/modules/contacts/schemas/` directory -- [ ] Create `contact.schema.ts` with contactsTable definition (firstName, lastName, email, phone, company, jobTitle, notes, userId, timestamps) -- [ ] Add contactTagsTable definition (id, name, color, userId, createdAt) -- [ ] Add contactsToTagsTable junction table (contactId, tagId, composite PK) -- [ ] Create `src/modules/deals/schemas/` directory -- [ ] Create `deal.schema.ts` with dealsTable definition (title, contactId FK, value, currency, stage enum, expectedCloseDate, description, userId, timestamps) -- [ ] Update `src/db/schema.ts` to export new schemas -- [ ] Generate migration: `pnpm drizzle-kit generate` -- [ ] Review generated SQL in `drizzle/0002_crm_schema.sql` -- [ ] Run migration locally: `pnpm run db:migrate:local` -- [ ] Verify tables created in D1: `npx wrangler d1 execute fullstack-crm --local --command "SELECT name FROM sqlite_master WHERE type='table'"` - -### Verification Criteria - -- [ ] Migration generates without errors -- [ ] Migration runs successfully (local D1) -- [ ] Can see 7 new tables in D1: contacts, contact_tags, contacts_to_tags, deals (plus existing user/session/account/verification/todos/categories) -- [ ] Foreign key constraints are correct (userId → users, contactId → contacts, tagId → contact_tags) -- [ ] Composite primary key exists on contacts_to_tags (contactId, tagId) -- [ ] Deal stage enum constraint is enforced - -### Exit Criteria - -All CRM database tables exist with proper relationships and constraints. Can manually insert test data via Wrangler CLI to verify schema. Migration is version-controlled and ready for production deployment. - ---- - -## Phase 3: Contacts Module - -**Type**: UI + Server Actions -**Estimated**: 2.5 hours -**Files**: See File Map (8 files total) - -### Purpose - -Implement complete contacts CRUD functionality with search, filtering, and tag management. Follows the existing `todos` module pattern. - -### File Map - -- `src/modules/contacts/actions/create-contact.action.ts` (new ~40 lines) - - **Purpose**: Server action to create new contact - - **Key exports**: createContact(data) - - **Dependencies**: contact.schema.ts, getDb(), requireAuth() - - **Returns**: Created contact or error - -- `src/modules/contacts/actions/get-contacts.action.ts` (new ~60 lines) - - **Purpose**: Server action to fetch contacts with search/filter - - **Key exports**: getContacts(searchQuery?, tagId?) - - **Dependencies**: contact.schema.ts, getDb(), requireAuth() - - **Returns**: Array of contacts with tags - -- `src/modules/contacts/actions/update-contact.action.ts` (new ~50 lines) - - **Purpose**: Server action to update contact - - **Key exports**: updateContact(id, data) - - **Dependencies**: contact.schema.ts, getDb(), requireAuth() - - **Returns**: Updated contact or error - -- `src/modules/contacts/actions/delete-contact.action.ts` (new ~35 lines) - - **Purpose**: Server action to delete contact - - **Key exports**: deleteContact(id) - - **Dependencies**: contact.schema.ts, getDb(), requireAuth() - - **Returns**: Success boolean - -- `src/modules/contacts/actions/tag-management.actions.ts` (new ~80 lines) - - **Purpose**: Server actions for tag CRUD and assignment - - **Key exports**: createTag(), getTags(), assignTagToContact(), removeTagFromContact() - - **Dependencies**: tag.schema.ts, getDb(), requireAuth() - - **Returns**: Tag operations results - -- `src/modules/contacts/components/contact-form.tsx` (new ~120 lines) - - **Purpose**: Reusable form for create/edit contact - - **Key exports**: ContactForm component - - **Dependencies**: React Hook Form, Zod, shadcn/ui (Input, Textarea, Button, Form), actions - - **Used by**: New contact page, Edit contact page - -- `src/modules/contacts/components/contact-card.tsx` (new ~80 lines) - - **Purpose**: Display single contact with actions - - **Key exports**: ContactCard component - - **Dependencies**: shadcn/ui (Card, Badge), actions (delete, tags) - - **Used by**: Contact list page - -- `src/app/dashboard/contacts/page.tsx` (new ~90 lines) - - **Purpose**: Contact list page with search and filter - - **Route**: /dashboard/contacts - - **Dependencies**: getContacts action, ContactCard, shadcn/ui (Input for search) - - **Features**: Search by name/email/company, filter by tag - -### Data Flow - -```mermaid -sequenceDiagram - participant U as User - participant C as ContactForm Component - participant A as createContact Action - participant D as D1 Database - participant R as React (revalidate) - - U->>C: Fill form + submit - C->>C: Validate with Zod - C->>A: createContact(validatedData) - A->>A: requireAuth() - get userId - A->>D: INSERT INTO contacts - D->>A: New contact record - A->>R: revalidatePath('/dashboard/contacts') - R->>C: Trigger re-render - A->>C: Return success - C->>U: Show success toast + redirect -``` - -### Critical Dependencies - -**Internal**: -- Contact schemas from Phase 2 -- Auth utilities (`src/modules/auth/utils/server.ts` - requireAuth, getCurrentUser) -- DB connection (`src/db/index.ts` - getDb) - -**External**: -- `react-hook-form` - Form state management -- `zod` - Validation schemas -- `@hookform/resolvers/zod` - RHF + Zod integration -- shadcn/ui components: Card, Input, Textarea, Button, Form, Badge, Select - -**Configuration**: -- None - uses existing D1 binding from Phase 1 - -### Gotchas & Known Issues - -**Ownership Verification Critical**: -- UPDATE/DELETE must check `contact.userId === user.id` -- Without check, users can modify others' contacts (security vulnerability) -- Pattern: Fetch contact first, verify ownership, then mutate - -**Search Implementation**: -- D1 (SQLite) doesn't have full-text search -- Use `LIKE` queries for MVP: `WHERE firstName LIKE '%query%' OR lastName LIKE '%query%' OR email LIKE '%query%'` -- Case-insensitive: Use `COLLATE NOCASE` in SQL -- Performance: Acceptable for <1000 contacts per user - -**Many-to-Many Tag Queries**: -- Fetching contacts with tags requires LEFT JOIN on junction table -- Drizzle syntax is verbose - see examples in Drizzle docs -- Consider using `.with()` or raw SQL for complex queries - -**Tag Color Validation**: -- Store as hex string (#FF5733) -- Validate format with Zod regex: `z.string().regex(/^#[0-9A-Fa-f]{6}$/)` -- Provide color picker in UI (use shadcn/ui Popover + color grid) - -**Form Validation**: -- Email is optional but must be valid format if provided: `z.string().email().optional().or(z.literal(''))` -- Phone is optional and freeform (no format enforcement for MVP) -- At least one of firstName or lastName required - -### Tasks - -- [ ] Create actions directory and implement all 5 action files -- [ ] Create validation schemas for contact create/update (Zod) -- [ ] Implement getContacts with search and filter logic (LIKE queries + LEFT JOIN for tags) -- [ ] Implement tag CRUD actions (create, list, assign to contact, remove from contact) -- [ ] Create ContactForm component with React Hook Form + Zod validation -- [ ] Create ContactCard component with delete button and tag badges -- [ ] Create /dashboard/contacts page with search input and tag filter dropdown -- [ ] Create /dashboard/contacts/new page with ContactForm -- [ ] Create /dashboard/contacts/[id]/edit page with ContactForm (pre-filled) -- [ ] Add navigation link in dashboard layout -- [ ] Test all CRUD operations manually - -### Verification Criteria - -- [ ] Can create new contact with valid data (redirects to contacts list) -- [ ] Form validation catches invalid email format -- [ ] Can search contacts by firstName, lastName, email, company (case-insensitive) -- [ ] Can create new tags with name and color -- [ ] Can assign multiple tags to a contact -- [ ] Can remove tag from contact -- [ ] Can edit contact (form pre-fills with existing data) -- [ ] Can delete contact (shows confirmation, removes from list) -- [ ] Cannot edit/delete another user's contacts (403 error) -- [ ] Contact list updates immediately after create/update/delete (revalidation works) -- [ ] UI is responsive on mobile and desktop - -### Exit Criteria - -Complete contacts management system with CRUD, search, and tagging. Users can create, view, edit, delete, and organize contacts. All operations respect user ownership. Forms validate inputs properly. UI follows shadcn/ui design patterns. - ---- - -## Phase 4: Deals Module - -**Type**: UI + Server Actions -**Estimated**: 2 hours -**Files**: See File Map (7 files total) - -### Purpose - -Implement deals/pipeline management with CRUD operations, contact linking, and simple Kanban-style board view. - -### File Map - -- `src/modules/deals/actions/create-deal.action.ts` (new ~45 lines) - - **Purpose**: Server action to create deal - - **Key exports**: createDeal(data) - - **Dependencies**: deal.schema.ts, getDb(), requireAuth() - - **Returns**: Created deal with contact info - -- `src/modules/deals/actions/get-deals.action.ts` (new ~70 lines) - - **Purpose**: Server action to fetch deals with filters - - **Key exports**: getDeals(stage?, contactId?) - - **Dependencies**: deal.schema.ts, contact.schema.ts, getDb(), requireAuth() - - **Returns**: Array of deals with JOIN to contacts table - -- `src/modules/deals/actions/update-deal.action.ts` (new ~55 lines) - - **Purpose**: Server action to update deal (including stage changes) - - **Key exports**: updateDeal(id, data) - - **Dependencies**: deal.schema.ts, getDb(), requireAuth() - - **Returns**: Updated deal - -- `src/modules/deals/actions/delete-deal.action.ts` (new ~35 lines) - - **Purpose**: Server action to delete deal - - **Key exports**: deleteDeal(id) - - **Dependencies**: deal.schema.ts, getDb(), requireAuth() - - **Returns**: Success boolean - -- `src/modules/deals/components/deal-form.tsx` (new ~110 lines) - - **Purpose**: Form for create/edit deal - - **Key exports**: DealForm component - - **Dependencies**: React Hook Form, Zod, shadcn/ui (Input, Select, Textarea), getContacts action - - **Features**: Contact dropdown selector, currency input, date picker for expectedCloseDate - -- `src/modules/deals/components/deal-card.tsx` (new ~70 lines) - - **Purpose**: Display deal in board column - - **Key exports**: DealCard component - - **Dependencies**: shadcn/ui (Card, Badge), currency formatter - - **Used by**: Pipeline board - -- `src/app/dashboard/deals/page.tsx` (new ~100 lines) - - **Purpose**: Pipeline Kanban board view - - **Route**: /dashboard/deals - - **Dependencies**: getDeals action, DealCard - - **UI**: CSS Grid with 6 columns (one per stage) - -### Data Flow - -```mermaid -flowchart LR - A[Pipeline Board] --> B{Stage Columns} - B --> C[Prospecting] - B --> D[Qualification] - B --> E[Proposal] - B --> F[Negotiation] - B --> G[Closed Won] - B --> H[Closed Lost] - - C --> I[DealCard] - D --> I - E --> I - F --> I - G --> I - H --> I - - I --> J[Edit Click] - I --> K[Stage Dropdown] - - J --> L[DealForm] - K --> M[updateDeal Action] - M --> N[Revalidate Board] -``` - -### Critical Dependencies - -**Internal**: -- Deal schemas from Phase 2 -- Contact schemas (for foreign key and dropdown) -- Auth utilities (requireAuth, getCurrentUser) -- DB connection (getDb) - -**External**: -- `react-hook-form` + `zod` - Form validation -- shadcn/ui components: Card, Input, Select, Textarea, Button, Form, Badge -- Date picker: Consider shadcn/ui date picker or simple HTML date input for MVP - -**Configuration**: -- None - uses existing D1 binding - -### Gotchas & Known Issues - -**Contact Dropdown Performance**: -- Load all user's contacts for dropdown selector -- If user has >100 contacts, consider search/filter in dropdown (use shadcn/ui Combobox instead of Select) -- For MVP, simple Select is fine - -**Stage Enum Constraint**: -- Fixed stages defined in schema: `["Prospecting", "Qualification", "Proposal", "Negotiation", "Closed Won", "Closed Lost"]` -- Enforce in Zod validation AND database constraint -- Changing stage via dropdown updates deal immediately (no drag-drop for MVP) - -**Currency Formatting**: -- Store value as REAL in database (e.g., 5000.00) -- Store currency as TEXT (e.g., "USD", "AUD") -- Display formatted: `new Intl.NumberFormat('en-AU', { style: 'currency', currency: 'AUD' }).format(value)` -- For MVP, default to AUD (hardcode or make it user preference in Phase 2) - -**expectedCloseDate Handling**: -- Store as INTEGER unix timestamp -- Use HTML date input (returns YYYY-MM-DD string) -- Convert to timestamp: `new Date(dateString).getTime()` -- Display formatted: `new Date(timestamp).toLocaleDateString('en-AU')` - -**Board Layout Without Drag-Drop**: -- Use CSS Grid: `grid-template-columns: repeat(6, 1fr)` -- Each column filters deals by stage -- To change stage: Click deal → Edit form → Change stage dropdown → Save -- Phase 2 feature: Add drag-drop with `@dnd-kit/core` - -**Soft Delete for Deals**: -- Not implemented in MVP -- Hard delete is fine for learning project -- Phase 2: Add `deletedAt` column for soft delete - -### Tasks - -- [ ] Create actions directory and implement all 4 action files -- [ ] Create Zod schemas for deal create/update (validate currency, stage enum, dates) -- [ ] Implement getDeals with JOIN to contacts table (include contact name in results) -- [ ] Create DealForm component with contact Select dropdown (load from getContacts action) -- [ ] Add currency input field (number input + currency dropdown) -- [ ] Add expectedCloseDate field (HTML date input) -- [ ] Add stage Select dropdown with 6 options -- [ ] Create DealCard component with formatted currency, contact name, stage badge -- [ ] Create /dashboard/deals page with 6-column grid layout -- [ ] Filter deals by stage and render in appropriate column -- [ ] Create /dashboard/deals/new page with DealForm -- [ ] Create /dashboard/deals/[id]/edit page with DealForm (pre-filled) -- [ ] Add navigation link in dashboard layout -- [ ] Test all CRUD operations manually - -### Verification Criteria - -- [ ] Can create new deal with title, contact, value, currency, stage, expectedCloseDate -- [ ] Deal appears in correct stage column on board -- [ ] Can edit deal and change stage (moves to new column after save) -- [ ] Can delete deal (removes from board) -- [ ] Contact dropdown shows user's contacts only -- [ ] Currency displays formatted (e.g., "$5,000.00 AUD") -- [ ] expectedCloseDate displays formatted (e.g., "15/03/2025") -- [ ] Cannot edit/delete another user's deals (403 error) -- [ ] Board layout is responsive (stacks columns on mobile) -- [ ] Validation catches invalid data (negative value, invalid stage, etc.) - -### Exit Criteria - -Complete pipeline management system with CRUD and visual Kanban board. Users can create deals linked to contacts, track progress through stages, and view pipeline at a glance. All operations respect user ownership. - ---- - -## Phase 5: Dashboard Integration - -**Type**: UI -**Estimated**: 1 hour -**Files**: `src/app/dashboard/page.tsx`, `src/components/stat-card.tsx`, `src/modules/dashboard/actions/get-metrics.action.ts` - -### Purpose - -Enhance dashboard home page with CRM metrics and navigation links. Provides quick overview of contacts and deals. - -### File Map - -- `src/modules/dashboard/actions/get-metrics.action.ts` (new ~60 lines) - - **Purpose**: Server action to compute CRM metrics - - **Key exports**: getDashboardMetrics() - - **Dependencies**: contact.schema.ts, deal.schema.ts, getDb(), requireAuth() - - **Returns**: Object with totalContacts, activeDeals, pipelineValue, contactsByTag - -- `src/components/stat-card.tsx` (new ~40 lines) - - **Purpose**: Reusable metric display card - - **Key exports**: StatCard component - - **Dependencies**: shadcn/ui (Card) - - **Props**: title, value, icon, trend (optional) - -- `src/app/dashboard/page.tsx` (modify existing ~40 lines added) - - **Purpose**: Dashboard home page - - **Modifications**: Add CRM metrics grid, navigation cards - - **Dependencies**: getDashboardMetrics action, StatCard - -- `src/app/dashboard/layout.tsx` (modify existing ~10 lines added) - - **Purpose**: Dashboard sidebar navigation - - **Modifications**: Add links to /dashboard/contacts and /dashboard/deals - - **Dependencies**: None - -### Data Flow - -```mermaid -flowchart TB - A[Dashboard Page] --> B[getDashboardMetrics Action] - B --> C{Query D1} - C --> D[COUNT contacts by user] - C --> E[COUNT deals WHERE stage NOT IN closed] - C --> F[SUM deal values] - C --> G[GROUP contacts by tags] - - D --> H[Metrics Object] - E --> H - F --> H - G --> H - - H --> I[Render StatCards] - I --> J[Total Contacts] - I --> K[Active Deals] - I --> L[Pipeline Value] -``` - -### Critical Dependencies - -**Internal**: -- Contact and deal schemas -- Auth utilities (requireAuth) -- DB connection (getDb) - -**External**: -- shadcn/ui Card component -- Lucide icons for StatCard icons - -**Configuration**: -- None - -### Gotchas & Known Issues - -**Metric Computation Performance**: -- Use COUNT(*) for counts (fast) -- Use SUM(value) for pipeline value (fast) -- Avoid fetching all records then counting in JS (slow) -- Add indexes if queries are slow: `CREATE INDEX idx_deals_user_stage ON deals(userId, stage)` - -**Active Deals Definition**: -- Active = stage NOT IN ('Closed Won', 'Closed Lost') -- SQL: `SELECT COUNT(*) FROM deals WHERE userId = ? AND stage NOT IN ('Closed Won', 'Closed Lost')` - -**Pipeline Value Calculation**: -- Sum only active deals (exclude closed) -- Handle multiple currencies: For MVP, assume all AUD and sum directly -- Phase 2: Convert currencies to base currency using exchange rates - -**Dashboard Layout**: -- Use CSS Grid for metrics cards: `grid-template-columns: repeat(auto-fit, minmax(250px, 1fr))` -- Responsive: Cards wrap on mobile - -**Navigation Cards vs Sidebar**: -- For MVP, add simple navigation links in sidebar -- Phase 2: Add quick action cards (e.g., "Add Contact" button with icon) - -### Tasks - -- [ ] Create getDashboardMetrics action with COUNT and SUM queries -- [ ] Compute totalContacts (all contacts for user) -- [ ] Compute activeDeals (deals with stage not Closed Won/Lost) -- [ ] Compute pipelineValue (SUM of active deal values) -- [ ] Create StatCard component with props: title, value, icon -- [ ] Modify /dashboard page to fetch metrics and render 3 StatCards -- [ ] Add CSS Grid layout for metrics cards -- [ ] Modify dashboard layout to add navigation links (Contacts, Deals) -- [ ] Add Lucide icons to sidebar links (Users icon for Contacts, Briefcase icon for Deals) -- [ ] Test metrics update after creating/deleting contacts/deals - -### Verification Criteria - -- [ ] Dashboard shows correct total contacts count -- [ ] Dashboard shows correct active deals count -- [ ] Dashboard shows correct pipeline value (formatted currency) -- [ ] Metrics update immediately after CRUD operations (revalidation works) -- [ ] StatCards are responsive and wrap on mobile -- [ ] Navigation links work (click Contacts → /dashboard/contacts) -- [ ] Sidebar highlights current route -- [ ] Icons render correctly - -### Exit Criteria - -Dashboard provides useful CRM overview with metrics. Users can navigate to contacts and deals from dashboard. Metrics accurately reflect current data and update in real-time. - ---- - -## Phase 6: Testing & Documentation - -**Type**: Testing + Documentation -**Estimated**: 1 hour -**Files**: `src/lib/seed.ts`, `docs/DATABASE_SCHEMA.md` (update), `docs/TESTING.md` (new), `README.md` (update) - -### Purpose - -Create seed data for testing, verify all features work end-to-end, and document the CRM implementation. - -### File Map - -- `src/lib/seed.ts` (new ~100 lines) - - **Purpose**: Seed script to populate D1 with test data - - **Key exports**: seedCRM() function - - **Dependencies**: Contact/deal schemas, getDb() - - **Creates**: 10 contacts, 5 tags, 5 deals across all stages - -- `docs/TESTING.md` (new ~80 lines) - - **Purpose**: Test plan and manual testing checklist - - **Contains**: Feature checklist, edge cases, security tests - -- `docs/DATABASE_SCHEMA.md` (update existing) - - **Modifications**: Add CRM tables documentation from Phase 2 - - **Already created in Phase 2 planning** - just verify it's accurate - -- `README.md` (modify existing ~30 lines added) - - **Modifications**: Add CRM features section, setup instructions - - **Contains**: Feature list, screenshots (optional), usage guide - -### Seed Data - -Create realistic test data: - -**Contacts** (10 total): -- 3 with multiple tags -- 2 with deals -- 5 with just basic info -- Mix of complete and minimal profiles - -**Tags** (5 total): -- "Customer" (green) -- "Lead" (blue) -- "Partner" (purple) -- "Inactive" (gray) -- "VIP" (gold) - -**Deals** (5 total): -- 1 in Prospecting ($5,000) -- 1 in Qualification ($12,000) -- 1 in Proposal ($25,000) -- 1 in Closed Won ($50,000) -- 1 in Closed Lost ($8,000) - -### Testing Checklist - -**Contacts**: -- [ ] Create contact with all fields filled -- [ ] Create contact with only firstName -- [ ] Edit contact and change email -- [ ] Delete contact (verify deals set contactId to null) -- [ ] Search for contact by firstName -- [ ] Search for contact by email -- [ ] Search for contact by company (case-insensitive) -- [ ] Create tag and assign to contact -- [ ] Assign multiple tags to one contact -- [ ] Filter contacts by tag -- [ ] Remove tag from contact -- [ ] Delete tag (verify junction table cleaned up) - -**Deals**: -- [ ] Create deal linked to contact -- [ ] Create deal with no contact (contactId null) -- [ ] Edit deal and change stage (verify moves to correct column) -- [ ] Edit deal and change contact -- [ ] Delete deal -- [ ] View pipeline board (all 6 columns visible) -- [ ] Verify currency formatting displays correctly -- [ ] Verify expectedCloseDate displays formatted - -**Dashboard**: -- [ ] Metrics show correct counts -- [ ] Create contact → metric updates -- [ ] Delete deal → pipeline value updates -- [ ] Click navigation links (Contacts, Deals) - -**Security**: -- [ ] Cannot view another user's contacts (if multi-user test) -- [ ] Cannot edit another user's contacts -- [ ] Cannot delete another user's deals - -**UI/UX**: -- [ ] Forms validate before submit (invalid email caught) -- [ ] Success toasts appear after create/update/delete -- [ ] Responsive layout works on mobile (test at 375px width) -- [ ] No console errors in browser DevTools - -### Tasks - -- [ ] Create seed script in src/lib/seed.ts -- [ ] Add npm script to package.json: `"db:seed": "tsx src/lib/seed.ts"` -- [ ] Run seed script locally: `pnpm run db:seed` -- [ ] Verify seed data appears in dashboard -- [ ] Create TESTING.md with manual test checklist -- [ ] Run through entire test checklist, check off items -- [ ] Fix any bugs found during testing -- [ ] Update README.md with CRM features section -- [ ] Add setup instructions for new developers -- [ ] Verify docs/DATABASE_SCHEMA.md is complete -- [ ] Take screenshots of key pages (optional but helpful) -- [ ] Create git commit for all testing/docs changes - -### Verification Criteria - -- [ ] Seed script runs without errors -- [ ] All 10 contacts, 5 tags, 5 deals created -- [ ] All items in testing checklist pass -- [ ] No console errors during testing -- [ ] README.md accurately describes CRM features -- [ ] TESTING.md documents all test cases -- [ ] Documentation is ready for handoff/deployment - -### Exit Criteria - -All CRM features tested and verified working. Seed data available for demos. Documentation complete and accurate. Project ready for deployment or Phase 2 feature additions. - ---- - -## Notes - -### Testing Strategy - -**Per-phase verification** (inline): Each phase has verification criteria that must pass before moving to next phase. This catches issues early. - -**Final testing phase** (Phase 6): Comprehensive end-to-end testing with seed data. Ensures all features work together and no integration issues. - -### Deployment Strategy - -**Local development first** (all 6 phases): Build and test everything locally before deploying to Cloudflare. - -**Deploy when ready**: After Phase 6 verification passes: -1. Create production D1 database: `npx wrangler d1 create fullstack-crm` -2. Update wrangler.jsonc with production database ID -3. Run production migrations: `pnpm run db:migrate` -4. Deploy: `pnpm run build && npx wrangler deploy` -5. Set up GitHub Actions for future deployments (optional) - -### Context Management - -**Phases are sized for single sessions**: -- Phase 1-2: Quick setup (can do together) -- Phase 3: Largest phase (~2.5 hours), may need context clear mid-phase -- Phase 4-6: Moderate phases (can each fit in one session) - -**If context gets full mid-phase**: -- Use SESSION.md to track current task (see Session Handoff Protocol) -- Create git checkpoint commit -- Resume from SESSION.md after context clear - -### Phase 2 Feature Ideas - -After MVP is complete, consider adding: -- **Activity Timeline**: Log calls, meetings, emails on contacts -- **Avatar Uploads**: R2 storage for contact photos (reuse todos pattern) -- **Custom Deal Stages**: User-defined pipeline stages (requires new table) -- **Drag-and-Drop Kanban**: Use `@dnd-kit/core` for pipeline board -- **Advanced Search**: Full-text search with Vectorize (semantic search) -- **Email Integration**: Cloudflare Email Routing + Resend for sending -- **Export/Import**: CSV export of contacts/deals -- **Analytics Dashboard**: Charts with Recharts (conversion rates, pipeline trends) diff --git a/src/components/ui/color-picker.tsx b/src/components/ui/color-picker.tsx index 6b6b19a..132fcc6 100644 --- a/src/components/ui/color-picker.tsx +++ b/src/components/ui/color-picker.tsx @@ -1,9 +1,11 @@ "use client"; +import { useState } from "react"; import { HexColorPicker } from "react-colorful"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; interface ColorPickerProps { value: string; @@ -11,6 +13,8 @@ interface ColorPickerProps { } export function ColorPicker({ value, onChange }: ColorPickerProps) { + const [isValidHex, setIsValidHex] = useState(true); + return (
@@ -18,7 +22,6 @@ export function ColorPicker({ value, onChange }: ColorPickerProps) { @@ -32,12 +35,18 @@ export function ColorPicker({ value, onChange }: ColorPickerProps) { value={value} onChange={(e) => { const newColor = e.target.value; + const isValid = /^#[0-9A-Fa-f]{6}$/.test(newColor); + setIsValidHex(isValid || newColor.length < 7); // Allow partial input + if (/^#[0-9A-Fa-f]{0,6}$/.test(newColor)) { onChange(newColor); } }} placeholder="#6366f1" - className="flex-1 font-mono" + className={cn( + "flex-1 font-mono", + !isValidHex && "border-destructive focus-visible:ring-destructive" + )} maxLength={7} />
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index 01e468b..f3284fb 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -11,38 +11,48 @@ function Popover({ return } -function PopoverTrigger({ - ...props -}: React.ComponentProps) { - return -} +const PopoverTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ ...props }, ref) => ( + +)) +PopoverTrigger.displayName = "PopoverTrigger" -function PopoverContent({ - className, - align = "center", - sideOffset = 4, - ...props -}: React.ComponentProps) { - return ( - - - - ) -} +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)) +PopoverContent.displayName = "PopoverContent" -function PopoverAnchor({ - ...props -}: React.ComponentProps) { - return -} +const PopoverAnchor = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ ...props }, ref) => ( + +)) +PopoverAnchor.displayName = "PopoverAnchor" export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } From f15511e2adfaab0b4ae64cf0882813edd7cb5e21 Mon Sep 17 00:00:00 2001 From: Jez Date: Sun, 9 Nov 2025 12:38:20 +1100 Subject: [PATCH 33/34] fix: address CodeRabbit feedback on PR #30 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical fixes: - Add server-side file validation (5MB limit, PNG/JPEG only) in uploadToR2() - Fix LogoutButton to accept ButtonProps (variant, className) - Fix CSS variable syntax in sidebar: w-(--var) → w-[var(--var)] - Fix Tailwind important modifier: size-8! → !size-8 Accessibility & best practices: - Fix nested - - - - - - - - - - + + + +
diff --git a/src/app/(demo)/layout-demo/sidebar/page.tsx b/src/app/(demo)/layout-demo/sidebar/page.tsx index 91557c7..24eda11 100644 --- a/src/app/(demo)/layout-demo/sidebar/page.tsx +++ b/src/app/(demo)/layout-demo/sidebar/page.tsx @@ -87,18 +87,18 @@ export default function SidebarLayoutDemo() { Explore all 5 layout variants - - - - - - - - - - - - + + + +
diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index 30638ac..18aa950 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -170,7 +170,7 @@ function Sidebar({
diff --git a/src/modules/layouts/top-nav/top-nav.layout.tsx b/src/modules/layouts/top-nav/top-nav.layout.tsx index 974c35b..b6df092 100644 --- a/src/modules/layouts/top-nav/top-nav.layout.tsx +++ b/src/modules/layouts/top-nav/top-nav.layout.tsx @@ -42,18 +42,18 @@ export default async function TopNavLayout({ children }: TopNavLayoutProps) { TodoApp
- - - - - + - + +
@@ -81,18 +81,18 @@ export default async function TopNavLayout({ children }: TopNavLayoutProps) {
- - - - - + - + +
diff --git a/src/modules/todos/components/todo-form.tsx b/src/modules/todos/components/todo-form.tsx index c6a9a8a..93b010b 100644 --- a/src/modules/todos/components/todo-form.tsx +++ b/src/modules/todos/components/todo-form.tsx @@ -3,7 +3,6 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { Upload, X } from "lucide-react"; import Image from "next/image"; -import { useRouter } from "next/navigation"; import { useState, useTransition } from "react"; import { useForm } from "react-hook-form"; import toast from "react-hot-toast"; @@ -69,12 +68,15 @@ interface TodoFormProps { type FormData = z.infer; +// Image validation constants +const MAX_IMAGE_SIZE = 5 * 1024 * 1024; // 5MB +const ALLOWED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/jpg"]; + export function TodoForm({ user, categories: initialCategories, initialData, }: TodoFormProps) { - const router = useRouter(); const [isPending, startTransition] = useTransition(); const [imageFile, setImageFile] = useState(null); const [imagePreview, setImagePreview] = useState( @@ -111,17 +113,15 @@ export function TodoForm({ const handleImageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { - // Validate file size (5MB = 5 * 1024 * 1024 bytes) - const maxSize = 5 * 1024 * 1024; - if (file.size > maxSize) { + // Validate file size + if (file.size > MAX_IMAGE_SIZE) { toast.error("Image must be less than 5MB"); e.target.value = ""; // Reset input return; } // Validate file type - const validTypes = ["image/png", "image/jpeg", "image/jpg"]; - if (!validTypes.includes(file.type)) { + if (!ALLOWED_IMAGE_TYPES.includes(file.type)) { toast.error("Only PNG and JPG images are allowed"); e.target.value = ""; // Reset input return; diff --git a/src/services/summarizer.service.ts b/src/services/summarizer.service.ts index d28f955..0beb939 100644 --- a/src/services/summarizer.service.ts +++ b/src/services/summarizer.service.ts @@ -75,7 +75,7 @@ export class SummarizerService { private buildSystemPrompt( maxLength: number, - style: string, + style: SummaryStyles, language: string, ): string { const styleInstructions: Record = { From 9f9ff2b07fbdf9666a0771727602724eece4c15f Mon Sep 17 00:00:00 2001 From: Jez Date: Mon, 10 Nov 2025 07:31:17 +1100 Subject: [PATCH 34/34] feat: rebrand to Full Flare Stack v1.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major rebrand from fork to independent Jezweb open source project. ## Branding Changes - Project name: fullstack-next-cloudflare-demo → Full Flare Stack - Version: 0.1.0 → 1.0.0 (production-ready) - Repository: github.com/jezweb/full-flare-stack - License: MIT (Jez Dawes / Jezweb) - Status: Public open source project ## Documentation Added - Complete README.md rewrite with new branding and value proposition - CHANGELOG.md documenting fork history and v1.0.0 improvements - COMPONENT_INVENTORY.md (43 shadcn/ui components documented) - COMPOSED_PATTERNS_ROADMAP.md (pattern build priorities) - docs/development-planning/ (architecture guides) - docs/templates/PATTERN_TEMPLATE.md ## Architecture Improvements - Three-layer component system (primitives → patterns → features) - Component decision framework - 43 shadcn/ui components pre-installed - Pattern extraction methodology (after 3rd use) - Comprehensive development workflow documentation ## Acknowledgments - Original template: @ifindev/fullstack-next-cloudflare - Contributed 11 PRs (#11-21) with fixes and documentation upstream - Thank you to @ifindev for the excellent starting point! BREAKING CHANGE: Project renamed to Full Flare Stack 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 138 +++ CLAUDE.md | 80 +- README.md | 907 +++++------------- docs/COMPONENT_INVENTORY.md | 274 ++++++ docs/COMPOSED_PATTERNS_ROADMAP.md | 895 +++++++++++++++++ docs/development-planning/README.md | 401 ++++++++ .../architecture-overview.md | 275 ++++++ .../architecture-quick-reference.md | 508 ++++++++++ .../component-decision-framework.md | 389 ++++++++ .../module-development-guide.md | 843 ++++++++++++++++ .../pattern-library-plan.md | 745 ++++++++++++++ docs/templates/PATTERN_TEMPLATE.md | 430 +++++++++ package.json | 40 +- pnpm-lock.yaml | 413 ++++++++ src/components/ui/accordion.tsx | 66 ++ src/components/ui/alert.tsx | 66 ++ src/components/ui/breadcrumb.tsx | 109 +++ src/components/ui/calendar.tsx | 216 +++++ src/components/ui/command.tsx | 184 ++++ src/components/ui/hover-card.tsx | 44 + src/components/ui/pagination.tsx | 127 +++ src/components/ui/progress.tsx | 31 + src/components/ui/radio-group.tsx | 45 + src/components/ui/scroll-area.tsx | 58 ++ src/components/ui/slider.tsx | 63 ++ src/components/ui/sonner.tsx | 40 + src/components/ui/switch.tsx | 31 + src/components/ui/table.tsx | 116 +++ src/components/ui/tabs.tsx | 66 ++ src/components/ui/toggle-group.tsx | 83 ++ src/components/ui/toggle.tsx | 47 + 31 files changed, 7042 insertions(+), 688 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 docs/COMPONENT_INVENTORY.md create mode 100644 docs/COMPOSED_PATTERNS_ROADMAP.md create mode 100644 docs/development-planning/README.md create mode 100644 docs/development-planning/architecture-overview.md create mode 100644 docs/development-planning/architecture-quick-reference.md create mode 100644 docs/development-planning/component-decision-framework.md create mode 100644 docs/development-planning/module-development-guide.md create mode 100644 docs/development-planning/pattern-library-plan.md create mode 100644 docs/templates/PATTERN_TEMPLATE.md create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/breadcrumb.tsx create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/hover-card.tsx create mode 100644 src/components/ui/pagination.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/slider.tsx create mode 100644 src/components/ui/sonner.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/table.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/components/ui/toggle-group.tsx create mode 100644 src/components/ui/toggle.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fb36372 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,138 @@ +# Changelog + +All notable changes to Full Flare Stack will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +--- + +## [1.0.0] - 2025-11-10 + +### 🎉 Initial Release - "Full Flare Stack" + +Forked from [ifindev/fullstack-next-cloudflare](https://github.com/ifindev/fullstack-next-cloudflare) and evolved into an independent Jezweb open-source project. + +### Added + +**Component Architecture** +- Three-layer component system (primitives → patterns → features) +- 43 shadcn/ui components pre-installed and documented +- Component decision framework for where to put code +- Pattern extraction methodology (after 3rd use) + +**Documentation** +- `COMPONENT_INVENTORY.md` - All 43 installed components with usage +- `COMPOSED_PATTERNS_ROADMAP.md` - Build priorities for reusable patterns +- `docs/development-planning/` - Complete architecture guides + - `architecture-overview.md` - Three-layer system explained + - `component-decision-framework.md` - Decision trees for component placement + - `module-development-guide.md` - Step-by-step feature building + - `pattern-library-plan.md` - Detailed pattern specifications +- `docs/templates/PATTERN_TEMPLATE.md` - Template for documenting patterns +- `MODULES.md` - Module system guide +- `MODULE_TEMPLATE.md` - How to create modules +- `LAYOUTS.md` - 5 production-ready layouts + +**UX Improvements** (from PRs #11-21 to upstream) +- Auto-detect port in auth client (fixes hardcoded localhost:3000) +- Fixed navigation link: /todos → /dashboard/todos +- Replaced alert() with toast notifications +- Added ARIA labels for accessibility +- File upload validation (size + type checking) +- Success/error toast feedback throughout app +- Fixed R2 URL double https:// prefix issue +- Environment variable for database ID (no more hardcoded IDs) +- NEXT_REDIRECT error handling standardization +- Standardized error response patterns ({ success, data?, error? }) + +**API Documentation** +- Complete API endpoint documentation (872 lines) +- REST endpoints: /api/summarize, /api/auth/* +- Server Actions: 11 actions fully documented +- Data models, error handling, examples + +**Developer Experience** +- Comprehensive CLAUDE.md for AI-assisted development +- SESSION.md for tracking development progress +- Improved error messages and logging +- Better TypeScript types throughout + +### Changed + +- **Project Name**: `fullstack-next-cloudflare` → `full-flare-stack` +- **Branding**: Now a Jezweb open-source project +- **Version**: 0.1.0 → 1.0.0 (production-ready) +- **License**: MIT (clarified copyright: Jez Dawes / Jezweb) +- **Repository**: github.com/jezweb/full-flare-stack +- **README.md**: Completely rewritten with new value proposition +- **package.json**: Updated metadata, keywords, author information + +### Improved + +- Documentation clarity and completeness +- Code organization (three-layer architecture) +- Error handling consistency +- Type safety across the stack +- Developer onboarding experience +- Component reusability + +--- + +## Upstream Contributions + +The following improvements were contributed back to [ifindev/fullstack-next-cloudflare](https://github.com/ifindev/fullstack-next-cloudflare): + +**PRs #11-16: Quick Fixes** +- #11: Auto-detect port in auth client +- #12: Fix navigation link (/todos → /dashboard/todos) +- #13: Fix typos in method names +- #14: Replace alert() with toast in delete-todo.tsx +- #15: Add ARIA labels for accessibility +- #16: Add file upload validation + +**PRs #17-20: Medium-Difficulty Fixes** +- #17: Fix R2 URL double https:// prefix +- #18: Database ID environment variable +- #19: NEXT_REDIRECT error handling +- #20: Standardize error responses + +**PR #21: Documentation** +- #21: Complete API documentation (872 lines) + +**Total**: 15+ fixes/improvements, ~1,500+ lines changed, 872 lines of documentation + +These PRs improved the upstream project for everyone. Full Flare Stack builds on these foundations with additional architecture and documentation. + +--- + +## Fork History + +- **2025-11-08**: Forked from ifindev/fullstack-next-cloudflare +- **2025-11-08**: Contributed 11 PRs (#11-21) with fixes and documentation +- **2025-11-10**: Added three-layer architecture and 43 shadcn components +- **2025-11-10**: Comprehensive architecture documentation +- **2025-11-10**: Rebranded as "Full Flare Stack" - Jezweb open-source project +- **2025-11-10**: Released v1.0.0 + +--- + +## Acknowledgments + +Thank you to [@ifindev](https://github.com/ifindev) for creating the original template that served as the foundation for Full Flare Stack. + +--- + +## Future Releases + +See [ROADMAP.md](./ROADMAP.md) for planned features and improvements. + +**Upcoming:** +- v1.1.0: First composed patterns (DataTable, PageHeader, EmptyState) +- v1.2.0: Form patterns (SearchableSelect, DateRangePicker) +- v1.3.0: Media patterns (FileUpload, ImageGallery) +- v2.0.0: Multi-tenancy, advanced features + +--- + +**Full Flare Stack** - Built with ❤️ by [Jezweb](https://jezweb.com.au) diff --git a/CLAUDE.md b/CLAUDE.md index 8186024..491f093 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8 +1,10 @@ # Claude Code Project Context -**Project:** fullstack-next-cloudflare-demo -**Type:** Modular starter kit / template -**Last Updated:** 2025-11-08 +**Project:** Full Flare Stack +**Type:** Production-ready Next.js + Cloudflare Workers starter kit +**Repository:** https://github.com/jezweb/full-flare-stack +**Last Updated:** 2025-11-10 +**Version:** 1.0.0 --- @@ -40,6 +42,9 @@ This is a **production-ready modular starter kit** that can be forked and custom - ✅ Example CRUD module (todos with categories) - ✅ Dark/light mode theming - ✅ Modular architecture (see MODULES.md) +- ✅ Three-layer component system (primitives → patterns → features) +- ✅ 43 shadcn/ui components installed (Layer 1 foundation complete) +- ✅ 5 production layout variants (authenticated, marketing, minimal, split, dashboard) **Intentionally missing:** - ❌ Automated tests (manual testing only) @@ -49,6 +54,41 @@ This is a **production-ready modular starter kit** that can be forked and custom --- +## Component Architecture + +This project uses a **three-layer component architecture** for building scalable applications: + +### Layer 1: UI Primitives (`/components/ui/`) +- **43 shadcn/ui components installed** (foundation complete as of 2025-11-10) +- Includes: forms, data display, overlays, feedback, layout, navigation +- See: [COMPONENT_INVENTORY.md](./docs/COMPONENT_INVENTORY.md) for complete list + +### Layer 2: Composed Patterns (`/components/composed/`) +- Reusable UI patterns built from Layer 1 primitives +- NO business logic, NO database access +- Build after 3rd use of a pattern +- Examples: DataTable, PageHeader, SearchableSelect, FileUpload +- See: [COMPOSED_PATTERNS_ROADMAP.md](./docs/COMPOSED_PATTERNS_ROADMAP.md) for build priorities + +### Layer 3: Feature Modules (`/modules/[feature]/`) +- Business logic, Server Actions, database access +- Feature-specific components +- Uses Layer 1 + Layer 2 components +- See: [MODULES.md](./MODULES.md) for module system + +**Quick Decision:** +- Has business logic? → `/modules/[feature]/components/` +- Used in 3+ features? → `/components/composed/[category]/` +- shadcn component? → `/components/ui/` + +**Architecture Documentation:** +- [Architecture Overview](./docs/development-planning/architecture-overview.md) - Three-layer system explained +- [Component Decision Framework](./docs/development-planning/component-decision-framework.md) - Where to put components +- [Pattern Library Plan](./docs/development-planning/pattern-library-plan.md) - Detailed pattern specifications +- [Module Development Guide](./docs/development-planning/module-development-guide.md) - Building features + +--- + ## Development Workflow ### Starting Development @@ -586,18 +626,35 @@ pnpm run lint │ │ ├── (auth)/ # Auth pages (login, signup) │ │ ├── api/ # API routes │ │ └── dashboard/ # Protected pages -│ ├── components/ # Shared UI components -│ │ └── ui/ # shadcn/ui components +│ ├── components/ # Three-layer component system +│ │ ├── ui/ # Layer 1: shadcn/ui primitives (43 components) +│ │ ├── composed/ # Layer 2: Reusable patterns (to be built) +│ │ │ ├── data-display/ +│ │ │ ├── layouts/ +│ │ │ ├── forms/ +│ │ │ ├── feedback/ +│ │ │ ├── media/ +│ │ │ └── navigation/ +│ │ └── shared/ # One-off components │ ├── db/ # Database configuration │ │ ├── index.ts # DB connection │ │ └── schema.ts # Central schema exports -│ ├── modules/ # Feature modules (see MODULES.md) +│ ├── modules/ # Layer 3: Feature modules (see MODULES.md) │ │ ├── auth/ # Authentication (required) │ │ ├── dashboard/ # Dashboard layout (required) │ │ └── todos/ # Example CRUD (optional) │ ├── lib/ # Shared utilities │ └── drizzle/ # Database migrations ├── docs/ # Documentation +│ ├── development-planning/ # Architecture documentation +│ │ ├── architecture-overview.md +│ │ ├── component-decision-framework.md +│ │ ├── pattern-library-plan.md +│ │ └── module-development-guide.md +│ ├── templates/ # Documentation templates +│ │ └── PATTERN_TEMPLATE.md +│ ├── COMPONENT_INVENTORY.md # 43 installed shadcn components +│ ├── COMPOSED_PATTERNS_ROADMAP.md # Pattern build priorities │ ├── API_ENDPOINTS.md │ ├── DATABASE_SCHEMA.md │ ├── IMPLEMENTATION_PHASES.md @@ -667,6 +724,15 @@ ls -la docs/ # Available documentation - [MODULE_TEMPLATE.md](./MODULE_TEMPLATE.md) - Create new modules - [SESSION.md](./SESSION.md) - Current session state +**Component Architecture:** +- [COMPONENT_INVENTORY.md](./docs/COMPONENT_INVENTORY.md) - 43 installed shadcn components +- [COMPOSED_PATTERNS_ROADMAP.md](./docs/COMPOSED_PATTERNS_ROADMAP.md) - Pattern build priorities +- [Architecture Overview](./docs/development-planning/architecture-overview.md) - Three-layer system +- [Component Decision Framework](./docs/development-planning/component-decision-framework.md) - Where components go +- [Pattern Library Plan](./docs/development-planning/pattern-library-plan.md) - Detailed pattern specs +- [Module Development Guide](./docs/development-planning/module-development-guide.md) - Building features +- [PATTERN_TEMPLATE.md](./docs/templates/PATTERN_TEMPLATE.md) - Template for documenting patterns + **Technical Documentation:** - [Next.js 15 Docs](https://nextjs.org/docs) - [Cloudflare Workers](https://developers.cloudflare.com/workers/) @@ -683,6 +749,6 @@ ls -la docs/ # Available documentation --- -**Last Updated:** 2025-11-08 +**Last Updated:** 2025-11-10 **Maintainer:** Jez (jeremy@jezweb.net) **Claude Code Version:** This file is optimized for Claude Code CLI diff --git a/README.md b/README.md index 9798774..c9481de 100644 --- a/README.md +++ b/README.md @@ -1,806 +1,357 @@ -![Banner](banner.svg) +# 🔥 Full Flare Stack -# ⚡ Full-Stack Next.js + Cloudflare Template +**Production-ready Next.js + Cloudflare Workers starter with 43 shadcn components, three-layer architecture, and D1/R2/AI integration.** -A **modular starter kit** for building full-stack applications with Next.js 15 and Cloudflare's powerful edge infrastructure. Perfect for MVPs with generous free tiers and seamless scaling to enterprise-level applications. +[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/) +[![Next.js 15](https://img.shields.io/badge/Next.js-15-black)](https://nextjs.org/) +[![Cloudflare Workers](https://img.shields.io/badge/Cloudflare-Workers-orange)](https://workers.cloudflare.com/) +[![shadcn/ui](https://img.shields.io/badge/shadcn%2Fui-43_components-blue)](https://ui.shadcn.com/) -**Inspired by the [Cloudflare SaaS Stack](https://github.com/supermemoryai/cloudflare-saas-stack)** - the same stack powering [Supermemory.ai](https://git.new/memory), which serves 20k+ users on just $5/month. This template modernizes that approach with Cloudflare Workers (vs Pages), includes comprehensive D1 and R2 examples, and provides a complete development workflow. - -You can read detail explanations and code architecture of this template from Devin AI on [Deepwiki](https://deepwiki.com/ifindev/fullstack-next-cloudflare). - -Don't forget to leave a star if you find this helpful ⭐️ +> A [Jezweb](https://jezweb.com.au) Open Source Project +> Maintained by [Jez Dawes](https://github.com/jezweb) • jeremy@jezweb.net --- -## 🧩 Modular Architecture - -This template uses a **feature-based module system** that makes it easy to: - -- ✅ **Start new projects** - Fork this repo and customize modules -- ✅ **Remove unwanted features** - Delete module folders you don't need -- ✅ **Add new features** - Follow the established pattern for consistency -- ✅ **Reuse modules** - Copy modules to other projects - -**See [MODULES.md](./MODULES.md)** for complete documentation on: -- Available modules (auth, todos, dashboard) -- How to remove a module -- How to add a new module -- Best practices and common issues - -**Quick Start for New Projects:** -```bash -# 1. Fork this repository -git clone https://github.com/your-username/fullstack-next-cloudflare.git my-app -cd my-app - -# 2. Remove unwanted modules (optional) -rm -rf src/modules/todos -# See MODULES.md for complete removal steps - -# 3. Install and configure -pnpm install -cp .dev.vars.example .dev.vars -# Edit .dev.vars with your credentials - -# 4. Start building your app! -``` - ---- - -## 🎨 Flexible Layouts - -Choose from **5 production-ready layouts** to match your app type: +## 🚀 What is Full Flare Stack? -| Layout | Best For | Features | -|--------|----------|----------| -| **Sidebar** | Dashboards, CRMs, Admin Panels | Collapsible sidebar, keyboard shortcuts (Cmd+B), mobile drawer | -| **Top Nav** | Simple Apps, Tools | Horizontal navigation, full-width content | -| **Hybrid** | Complex SaaS, Enterprise | Top header + sidebar, most polished | -| **Centered** | Docs, Blogs, Forms | Max-width content, optimal reading | -| **Marketing** | Landing Pages | Public pages with footer, no auth | - -**See [LAYOUTS.md](./LAYOUTS.md)** for complete documentation on: -- How to choose the right layout -- Customizing navigation and branding -- Responsive behavior and keyboard shortcuts -- Examples and best practices - -**Quick Example:** -```tsx -// app/dashboard/layout.tsx -import SidebarLayout from "@/modules/layouts/sidebar/sidebar.layout"; - -export default async function Layout({ children }) { - return {children}; -} -``` +Full Flare Stack is a **modular, production-ready starter kit** for building full-stack applications on Cloudflare's edge infrastructure. It combines Next.js 15 with Cloudflare Workers, D1, R2, and Workers AI to deliver ultra-fast, globally distributed applications that scale effortlessly. -All layouts follow **shadcn/ui standards** with Radix UI components, full responsiveness, and dark/light theme support. +**Built for:** +- 🎯 **MVPs & Side Projects** - Generous free tiers, deploy in minutes +- 💼 **SaaS Applications** - Multi-tenancy ready, enterprise-scale performance +- 🏢 **Business Tools** - CRMs, dashboards, admin panels with 5 production layouts +- 🤖 **AI-Powered Apps** - Edge AI inference with Workers AI --- -## 🌟 Why Cloudflare + Next.js? - -**Cloudflare's Edge Network** provides unparalleled performance and reliability: -- ⚡ **Ultra-low latency** - Deploy to 300+ locations worldwide -- 💰 **Generous free tiers** - Perfect for MVPs and side projects -- 📈 **Effortless scaling** - From zero to millions of users automatically -- 🔒 **Built-in security** - DDoS protection, WAF, and more -- 🌍 **Global by default** - Your app runs close to every user - -Combined with **Next.js 15**, you get modern React features, Server Components, and Server Actions for optimal performance and developer experience. +## ✨ What Makes It Different? -## 🛠️ Tech Stack +### **1. Three-Layer Component Architecture** -### 🎯 **Frontend** -- ⚛️ **Next.js 15** - App Router with React Server Components (RSC) -- 🎨 **TailwindCSS 4** - Utility-first CSS framework -- 📘 **TypeScript** - Full type safety throughout -- 🧩 **Shadcn UI** - Unstyled, accessible components -- 📋 **React Hook Form + Zod** - Type-safe form handling - -### ☁️ **Backend & Infrastructure** -- 🌐 **Cloudflare Workers** - Serverless edge compute platform -- 🗃️ **Cloudflare D1** - Distributed SQLite database at the edge -- 📦 **Cloudflare R2** - S3-compatible object storage -- 🤖 **Cloudflare Workers AI** - Edge AI inference with OpenSource models -- 🔑 **Better Auth** - Modern authentication with Google OAuth -- 🛠️ **Drizzle ORM** - TypeScript-first database toolkit - -### 🚀 **DevOps & Deployment** -- ⚙️ **GitHub Actions** - Automated CI/CD pipeline -- 🔧 **Wrangler** - Cloudflare's CLI tool -- 👁️ **Preview Deployments** - Test changes before production -- 🔄 **Database Migrations** - Version-controlled schema changes -- 💾 **Automated Backups** - Production database safety - -### 📊 **Data Flow Architecture** -- **Fetching**: Server Actions + React Server Components for optimal performance -- **Mutations**: Server Actions with automatic revalidation -- **AI Processing**: Edge AI inference with Cloudflare Workers AI -- **Type Safety**: End-to-end TypeScript from database to UI -- **Caching**: Built-in Next.js caching with Cloudflare edge caching - -## 🏗️ Project Structure - -This template uses a **feature-based/module-sliced architecture** for better maintainability and scalability: +Full Flare Stack pioneered a clean separation of concerns: ``` -src/ -├── app/ # Next.js App Router -│ ├── (auth)/ # Auth-related pages -│ ├── api/ # API routes (for external access) -│ │ └── summarize/ # AI summarization endpoint -│ ├── dashboard/ # Dashboard pages -│ └── globals.css # Global styles -├── components/ # Shared UI components -├── constants/ # App constants -├── db/ # Database configuration -│ ├── index.ts # DB connection -│ └── schema.ts # Database schemas -├── lib/ # Shared utilities -├── modules/ # Feature modules -│ ├── auth/ # Authentication module -│ │ ├── actions/ # Auth server actions -│ │ ├── components/ # Auth components -│ │ ├── hooks/ # Auth hooks -│ │ ├── models/ # Auth models -│ │ ├── schemas/ # Auth schemas -│ │ └── utils/ # Auth utilities -│ ├── dashboard/ # Dashboard module -│ └── todos/ # Todo module -│ ├── actions/ # Todo server actions -│ ├── components/ # Todo components -│ ├── models/ # Todo models -│ └── schemas/ # Todo schemas -├── services/ # Business logic services -│ └── summarizer.service.ts # AI summarization service -└── drizzle/ # Database migrations -``` - -**Key Architecture Benefits:** -- **Feature Isolation** - Each module contains its own actions, components, and logic -- **Server Actions** - Modern data mutations with automatic revalidation -- **React Server Components** - Optimal performance with server-side rendering -- **Type Safety** - End-to-end TypeScript from database to UI -- **Testable** - Clear separation of concerns makes testing easier - -## 🚀 Getting Started - -### 1. Prerequisites +Layer 1: UI Primitives (/components/ui/) +↓ 43 shadcn/ui components (foundation complete) -- **Cloudflare Account** - [Sign up for free](https://dash.cloudflare.com/sign-up) -- **Node.js 20+** and **pnpm** installed -- **Google OAuth App** - For authentication setup +Layer 2: Composed Patterns (/components/composed/) +↓ Reusable patterns (DataTable, forms, etc.) - build as you need -### 2. Create Cloudflare API Token - -Create an API token for Wrangler authentication: - -1. In the Cloudflare dashboard, go to the **Account API tokens** page -2. Select **Create Token** > find **Edit Cloudflare Workers** > select **Use Template** -3. Customize your token name (e.g., "Next.js Cloudflare Template") -4. Scope your token to your account and zones (if using custom domains) -5. **Add additional permissions** for D1 database and AI access: - - Account - D1:Edit - - Account - D1:Read - - Account - Cloudflare Workers AI:Read - -**Final Token Permissions:** -- All permissions from "Edit Cloudflare Workers" template -- Account - D1:Edit (for database operations) -- Account - D1:Read (for database queries) -- Account - Cloudflare Workers AI:Read (for AI inference) - -### 3. Clone and Setup - -```bash -# Clone the repository -git clone https://github.com/ifindev/fullstack-next-cloudflare.git -cd fullstack-next-cloudflare - -# Install dependencies -pnpm install +Layer 3: Feature Modules (/modules/[feature]/) +↓ Business logic, Server Actions, database access ``` -### 4. Environment Configuration +**Why it matters:** +- ✅ Know exactly where every component belongs +- ✅ Extract patterns after 3rd use (not speculatively) +- ✅ Reusable across projects +- ✅ Clear boundaries prevent technical debt -Create your environment file: +**See:** [Component Architecture Docs](./docs/development-planning/) -```bash -# Copy example environment file -cp .dev.vars.example .dev.vars -``` - -Edit `.dev.vars` with your credentials: - -```bash -# Cloudflare Configuration -CLOUDFLARE_ACCOUNT_ID=your-account-id -CLOUDFLARE_D1_DATABASE_ID=your-database-id -CLOUDFLARE_D1_TOKEN=your-api-token - -# Authentication Secrets -BETTER_AUTH_SECRET=your-random-secret-here -GOOGLE_CLIENT_ID=your-google-client-id -GOOGLE_CLIENT_SECRET=your-google-client-secret -NEXT_PUBLIC_AUTH_URL=http://localhost:3000 # SSR fallback for auth URL - -# Storage -CLOUDFLARE_R2_URL=your-r2-bucket-url -``` - -### 5. Authentication Setup +--- -**Better Auth Secret:** -```bash -# Generate a random secret -openssl rand -base64 32 -# Add to BETTER_AUTH_SECRET in .dev.vars -``` +### **2. 43 shadcn/ui Components Pre-Installed** -**Google OAuth Setup:** -Follow the [Better Auth Google documentation](https://www.better-auth.com/docs/authentication/google) to: -1. Create a Google OAuth 2.0 application -2. Get your Client ID and Client Secret -3. Add authorized redirect URIs +Every primitive you need, ready to use: -### 6. Storage Setup & R2 URL Configuration +| Category | Components | Count | +|----------|------------|-------| +| **Forms** | button, input, select, checkbox, radio-group, slider, switch, calendar, etc. | 13 | +| **Data Display** | table, card, badge, avatar, pagination, separator | 6 | +| **Overlays** | dialog, sheet, popover, tooltip, dropdown-menu, etc. | 8 | +| **Feedback** | alert, toast (sonner), progress, skeleton, scroll-area | 5 | +| **Layout & Nav** | tabs, accordion, breadcrumb, sidebar, command (⌘K) | 7 | -**Understanding R2 URLs:** -Cloudflare R2 has different URL behaviors for development vs production: +**See:** [Component Inventory](./docs/COMPONENT_INVENTORY.md) • [Build Roadmap](./docs/COMPOSED_PATTERNS_ROADMAP.md) -- **Development**: R2 provides a public URL that works immediately -- **Production**: R2 public URLs are not meant for production use - you should set up a custom domain +--- -**Step 1: Create R2 Bucket** -```bash -# Create R2 bucket for development -wrangler r2 bucket create your-app-bucket-dev +### **3. 5 Production-Ready Layouts** -# For production, create a separate bucket -wrangler r2 bucket create your-app-bucket-prod -``` +Choose the layout that matches your app: -**Step 2: Get R2 URL for Development** +| Layout | Best For | Features | +|--------|----------|----------| +| **Sidebar** | Dashboards, Admin Panels | Collapsible sidebar, keyboard shortcuts | +| **Top Nav** | Simple Tools | Horizontal nav, full-width content | +| **Hybrid** | Complex SaaS | Top header + sidebar, most polished | +| **Centered** | Docs, Blogs | Max-width content, optimal reading | +| **Marketing** | Landing Pages | Public pages with footer | -**Enable Public Development URL:** -1. Go to Cloudflare Dashboard -2. Click "R2 Object Storage" in the sidebar -3. Select your bucket from the list -4. Go to the "Settings" tab -5. Find "Public Development URL" section -6. Click "Enable Public URL" -7. Copy the displayed URL (it will be in format: `https://pub-xxxxx.r2.dev`) +**See:** [Layouts Documentation](./LAYOUTS.md) -**Example URL Format:** -``` -https://pub-a1b2c3d4e5f6g7h8i9j0.r2.dev -``` +--- -**Important Notes:** -- The URL is automatically generated by Cloudflare -- No account ID needed - it's all in the provided URL -- This URL is for development only (not production) +### **4. Modular Feature System** -**Step 3: Add R2 URL to Environment Variables** ```bash -# Add to your .dev.vars file (use the URL you copied from dashboard) -CLOUDFLARE_R2_URL=https://pub-a1b2c3d4e5f6g7h8i9j0.r2.dev -``` +src/modules/ +├── auth/ # Required - Google OAuth, session management +├── dashboard/ # Required - Layout wrapper +└── todos/ # Optional - Example CRUD module -**Note:** Replace the example URL with the actual URL you copied from your R2 bucket settings. +# Remove unwanted modules: +rm -rf src/modules/todos -**For Production - Custom Domain Setup (Required):** +# Add new module: +mkdir -p src/modules/products/{actions,components,schemas} +``` -⚠️ **Important**: The default R2 public URL should NOT be used in production as it's not optimized for performance and may have limitations. +**See:** [Module System Guide](./MODULES.md) • [Module Template](./MODULE_TEMPLATE.md) -**Setup Custom Domain for R2:** -```bash -# 1. Go to Cloudflare Dashboard → R2 Storage → Your Bucket → Custom Domains -# 2. Click "Connect Domain" and enter your desired domain (e.g., files.yourdomain.com) -# 3. Update your DNS records as instructed by Cloudflare -# 4. Wait for SSL certificate to be issued (usually a few minutes) +--- -# Your production R2 URL will be: -# https://files.yourdomain.com +### **5. Comprehensive Documentation** -# Add this to your production secrets: -echo "https://files.yourdomain.com" | wrangler secret put CLOUDFLARE_R2_URL -``` +Full Flare Stack includes **production-tested documentation** that saves hours: -**R2 URL Summary:** -- **Development**: Use URL from R2 bucket "Public Development URL" setting -- **Production**: Must use custom domain for better performance and reliability +**Architecture Guides:** +- [Architecture Overview](./docs/development-planning/architecture-overview.md) - Three-layer system +- [Component Decision Framework](./docs/development-planning/component-decision-framework.md) - Where components go +- [Module Development Guide](./docs/development-planning/module-development-guide.md) - Building features +- [Pattern Library Plan](./docs/development-planning/pattern-library-plan.md) - Reusable patterns -**How R2 URLs Work:** -- **Base URL**: Get from R2 bucket settings (format: `https://pub-xxxxx.r2.dev`) -- **File URLs**: `https://pub-xxxxx.r2.dev/{folder}/{file-name}.{extension}` -- **Environment Variable**: Only the base URL goes into `CLOUDFLARE_R2_URL` -- **Code**: The full file paths are constructed programmatically using the base URL +**Reference Docs:** +- [Component Inventory](./docs/COMPONENT_INVENTORY.md) - All 43 installed components +- [Composed Patterns Roadmap](./docs/COMPOSED_PATTERNS_ROADMAP.md) - Build order & priorities +- [API Endpoints](./docs/API_ENDPOINTS.md) - Complete API documentation +- [Database Schema](./docs/DATABASE_SCHEMA.md) - D1 schema reference -## 🛠️ Manual Setup (Detailed) +**Templates:** +- [Pattern Template](./docs/templates/PATTERN_TEMPLATE.md) - Document new patterns -If you prefer to set everything up manually or want to understand each step in detail, follow this comprehensive guide. +--- -### Step 1: Create Cloudflare Resources +## 🛠️ Tech Stack -**Create D1 Database:** -```bash -# Create a new SQLite database at the edge -wrangler d1 create your-app-name - -# Output will show: -# database_name = "your-app-name" -# database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -# -# Copy the database_id - you'll need it for: -# - wrangler.jsonc (d1_databases.database_id) -# - .dev.vars (CLOUDFLARE_D1_DATABASE_ID) -``` +### Frontend +- **Next.js 15.4.6** - App Router, React Server Components, Server Actions +- **React 19.1.0** - Latest React with concurrent features +- **TailwindCSS v4** - Utility-first CSS with native CSS variables +- **shadcn/ui** - 43 pre-installed Radix UI components +- **React Hook Form + Zod** - Type-safe form validation + +### Backend & Infrastructure +- **Cloudflare Workers** - Serverless edge compute (via @opennextjs/cloudflare) +- **Cloudflare D1** - SQLite database at the edge +- **Cloudflare R2** - S3-compatible object storage (no egress fees) +- **Cloudflare Workers AI** - Edge AI inference with open-source models +- **better-auth 1.3.9** - Modern authentication with Google OAuth +- **Drizzle ORM 0.44.5** - TypeScript-first database toolkit + +### DevOps +- **Wrangler 4.46.0** - Cloudflare CLI +- **pnpm** - Fast, efficient package manager +- **Biome** - Fast code formatter +- **GitHub Actions** - CI/CD (ready to configure) -**Create R2 Bucket:** -```bash -# Create object storage bucket -wrangler r2 bucket create your-app-bucket +--- -# List buckets to confirm -wrangler r2 bucket list -``` +## 🚀 Quick Start -### Step 2: Configure Wrangler - -Update `wrangler.jsonc` with your resource IDs: - -```jsonc -{ - "name": "your-app-name", - "d1_databases": [ - { - "binding": "DB", - "database_name": "your-app-name", - "database_id": "your-database-id-from-step-1", - "migrations_dir": "./src/drizzle" - } - ], - "r2_buckets": [ - { - "bucket_name": "your-app-bucket", - "binding": "FILES" - } - ], - "ai": { - "binding": "AI" - } -} -``` +### Prerequisites +- Node.js 18+ (20+ recommended) +- pnpm (`npm install -g pnpm`) +- Cloudflare account (free tier works) +- Google OAuth credentials (for auth) -### Step 3: Set Up Authentication +### 1. Clone & Install -**Generate Better Auth Secret:** ```bash -# On macOS/Linux -openssl rand -base64 32 - -# On Windows (PowerShell) -[System.Convert]::ToBase64String([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32)) - -# Or use online generator: https://generate-secret.vercel.app/32 +git clone https://github.com/jezweb/full-flare-stack.git my-app +cd my-app +pnpm install ``` -**Configure Google OAuth:** -1. Go to [Google Cloud Console](https://console.cloud.google.com/) -2. Create a new project or select existing one -3. Enable Google+ API -4. Create OAuth 2.0 credentials -5. Add authorized redirect URIs: - - `http://localhost:3000/api/auth/callback/google` (development) - - `https://your-app.your-subdomain.workers.dev/api/auth/callback/google` (production) - -### Step 4: Environment Configuration +### 2. Environment Setup -**Create Local Environment File:** ```bash -# .dev.vars for local development -CLOUDFLARE_ACCOUNT_ID=your-account-id -CLOUDFLARE_D1_DATABASE_ID=your-database-id -CLOUDFLARE_D1_TOKEN=your-api-token -BETTER_AUTH_SECRET=your-generated-secret -GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=your-google-client-secret -NEXT_PUBLIC_AUTH_URL=http://localhost:3000 # SSR fallback for auth URL -# Get this from R2 bucket settings: R2 Object Storage → Your Bucket → Settings → Public Development URL -CLOUDFLARE_R2_URL=https://pub-a1b2c3d4e5f6g7h8i9j0.r2.dev -``` +# Copy environment template +cp .dev.vars.example .dev.vars -**Set Production Secrets:** -```bash -# Add each secret to Cloudflare Workers -echo "your-secret-here" | wrangler secret put BETTER_AUTH_SECRET -echo "your-client-id" | wrangler secret put GOOGLE_CLIENT_ID -echo "your-client-secret" | wrangler secret put GOOGLE_CLIENT_SECRET -echo "your-r2-url" | wrangler secret put CLOUDFLARE_R2_URL +# Edit .dev.vars with your credentials: +# - CLOUDFLARE_ACCOUNT_ID (from Cloudflare dashboard) +# - BETTER_AUTH_SECRET (generate with: openssl rand -base64 32) +# - GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET (from Google Cloud Console) +# - CLOUDFLARE_R2_URL (your R2 bucket public URL) ``` -### Step 5: Database Setup +**See:** [Environment Setup Guide](./docs/ENVIRONMENT_SETUP.md) (if available) -**Generate TypeScript Types:** -```bash -# Generate Cloudflare bindings for TypeScript -pnpm run cf-typegen -``` +### 3. Database Setup -**Initialize Database:** ```bash -# Generate initial migration from schema -pnpm run db:generate - -# Apply migrations to local database +# Create D1 database (local development) pnpm run db:migrate:local -# Verify database structure -pnpm run db:inspect:local +# Or use Wrangler to create remote database +pnpm wrangler d1 create full-flare-stack +# Update wrangler.jsonc with database ID ``` -**Optional: Seed Sample Data** -```bash -# Create and run a seed script -wrangler d1 execute your-app-name --local --command=" -INSERT INTO todos (id, title, description, completed, created_at, updated_at) VALUES -('1', 'Welcome to your app', 'This is a sample todo item', false, datetime('now'), datetime('now')), -('2', 'Set up authentication', 'Configure Google OAuth', true, datetime('now'), datetime('now')); -" -``` +### 4. Start Development -### Step 6: Test Your Setup +**Two-terminal setup (recommended):** -**Start Development Servers:** ```bash -# Terminal 1: Start Wrangler (provides D1 access) +# Terminal 1: Wrangler (provides D1 database access) pnpm run wrangler:dev -# Terminal 2: Start Next.js (provides HMR) +# Terminal 2: Next.js (provides hot module reload) pnpm run dev - -# Alternative: Single command (no HMR) -pnpm run dev:cf ``` -**Verify Everything Works:** -1. Open `http://localhost:3000` -2. Test authentication flow -3. Create a todo item -4. Check database: `pnpm run db:studio:local` +**Access:** http://localhost:3000 -### Step 7: Set Up GitHub Actions (Optional) - -**Add Repository Secrets:** -Go to your GitHub repository → Settings → Secrets and add: - -- `CLOUDFLARE_API_TOKEN` - Your API token from Step 2 -- `CLOUDFLARE_ACCOUNT_ID` - Your account ID -- `CLOUDFLARE_D1_DATABASE_ID` - Your D1 database ID (from `wrangler d1 create` output) -- `BETTER_AUTH_SECRET` - Your auth secret -- `GOOGLE_CLIENT_ID` - Your Google client ID -- `GOOGLE_CLIENT_SECRET` - Your Google client secret -- `CLOUDFLARE_R2_URL` - Your R2 bucket URL - -**Deploy Production Database:** +**Alternative (single terminal, no HMR):** ```bash -# Apply migrations to production -pnpm run db:migrate:prod - -# Verify production database -pnpm run db:inspect:prod +pnpm run dev:cf ``` -## 🔧 Advanced Manual Configuration - -### Custom Domain Setup - -**Add Custom Domain:** -1. Go to Cloudflare dashboard → Workers & Pages -2. Select your worker → Settings → Triggers -3. Click "Add Custom Domain" -4. Enter your domain (must be in your Cloudflare account) - -**Update OAuth Redirect URLs:** -Add your custom domain to Google OAuth settings: -- `https://yourdomain.com/api/auth/callback/google` - -### Database Optimization +### 5. Build & Deploy -**Add Indexes for Performance:** -```sql --- Create indexes for better query performance -CREATE INDEX IF NOT EXISTS idx_todos_user_id ON todos(user_id); -CREATE INDEX IF NOT EXISTS idx_todos_created_at ON todos(created_at); -CREATE INDEX IF NOT EXISTS idx_todos_completed ON todos(completed); -``` - -**Monitor Database Performance:** ```bash -# View database insights -wrangler d1 insights your-app-name --since 1h - -# Export data for analysis -wrangler d1 export your-app-name --output backup.sql -``` +# Build for Cloudflare Workers +pnpm run build:cf -### R2 Storage Configuration +# Deploy to preview environment +pnpm run deploy:preview -**Configure CORS for Direct Uploads:** -```bash -# Create CORS policy file -echo '[ - { - "AllowedOrigins": ["https://yourdomain.com", "http://localhost:3000"], - "AllowedMethods": ["GET", "PUT", "POST", "DELETE"], - "AllowedHeaders": ["*"], - "ExposeHeaders": [], - "MaxAgeSeconds": 3000 - } -]' > cors.json - -# Apply CORS policy -wrangler r2 bucket cors put your-app-bucket --file cors.json +# Deploy to production +pnpm run deploy ``` -## 🏃‍♂️ Development Workflow +--- -### Initial Setup -```bash -# 1. Generate Cloudflare types (run after any wrangler.jsonc changes) -pnpm run cf-typegen +## 📖 Documentation Quick Links -# 2. Apply database migrations -pnpm run db:migrate:local +**Getting Started:** +- [Quick Start](#-quick-start) (this file) +- [Environment Setup](./docs/ENVIRONMENT_SETUP.md) (create if needed) +- [Deployment Guide](./docs/DEPLOYMENT.md) (create if needed) -# 3. Build the application for Cloudflare -pnpm run build:cf -``` +**Architecture:** +- [Three-Layer Component System](./docs/development-planning/architecture-overview.md) +- [Module System Guide](./MODULES.md) +- [Component Decision Framework](./docs/development-planning/component-decision-framework.md) -### Daily Development -```bash -# Terminal 1: Start Wrangler for D1 database access -pnpm run wrangler:dev +**Development:** +- [Building Features](./docs/development-planning/module-development-guide.md) +- [Extracting Patterns](./docs/COMPOSED_PATTERNS_ROADMAP.md) +- [Database Migrations](./docs/DATABASE_SCHEMA.md) +- [API Reference](./docs/API_ENDPOINTS.md) -# Terminal 2: Start Next.js development server with HMR -pnpm run dev -``` +**Reference:** +- [Component Inventory](./docs/COMPONENT_INVENTORY.md) +- [Pattern Library Plan](./docs/development-planning/pattern-library-plan.md) +- [Changelog](./CHANGELOG.md) +- [Roadmap](./ROADMAP.md) + +--- -**Development URLs:** -- 🌐 **Next.js with HMR**: `http://localhost:3000` (recommended) -- ⚙️ **Wrangler Dev Server**: `http://localhost:8787` +## 🎯 Example Use Cases -### Alternative Development Options +### SaaS Dashboard ```bash -# Single command - Cloudflare runtime (no HMR) -pnpm run dev:cf +# 1. Keep auth + dashboard modules +# 2. Remove todos module +rm -rf src/modules/todos -# Test with remote Cloudflare resources -pnpm run dev:remote -``` +# 3. Add your feature modules +mkdir -p src/modules/{customers,subscriptions,billing} -## 📜 Available Scripts - -### **Core Development** -| Script | Description | -|--------|-------------| -| `pnpm dev` | Start Next.js with HMR | -| `pnpm run build:cf` | Build for Cloudflare Workers | -| `pnpm run wrangler:dev` | Start Wrangler for local D1 access | -| `pnpm run dev:cf` | Combined build + Cloudflare dev server | - -### **Database Operations** -| Script | Description | -|--------|-------------| -| `pnpm run db:generate` | Generate new migration | -| `pnpm run db:generate:named "migration_name"` | Generate named migration | -| `pnpm run db:migrate:local` | Apply migrations to local D1 | -| `pnpm run db:migrate:preview` | Apply migrations to preview | -| `pnpm run db:migrate:prod` | Apply migrations to production | -| `pnpm run db:studio:local` | Open Drizzle Studio for local DB | -| `pnpm run db:inspect:local` | List local database tables | -| `pnpm run db:reset:local` | Reset local database | - -### **Deployment & Production** -| Script | Description | -|--------|-------------| -| `pnpm run deploy` | Deploy to production | -| `pnpm run deploy:preview` | Deploy to preview environment | -| `pnpm run cf-typegen` | Generate Cloudflare TypeScript types | -| `pnpm run cf:secret` | Add secrets to Cloudflare Workers | - -### **Development Order** - -**First-time setup:** -1. `pnpm run cf-typegen` - Generate types -2. `pnpm run db:migrate:local` - Setup database -3. `pnpm run build:cf` - Build application - -**Daily development:** -1. `pnpm run wrangler:dev` - Start D1 access (Terminal 1) -2. `pnpm run dev` - Start Next.js with HMR (Terminal 2) - -**After schema changes:** -1. `pnpm run db:generate` - Generate migration -2. `pnpm run db:migrate:local` - Apply to local DB - -**After wrangler.jsonc changes:** -1. `pnpm run cf-typegen` - Regenerate types - -## 🤖 AI Development & Testing - -### Testing the AI API - -**⚠️ Authentication Required**: Login to your app first, then test the API. - -**Browser Console (Easiest):** -1. Login at `http://localhost:3000` -2. Open DevTools Console (F12) -3. Run: -```javascript -fetch('/api/summarize', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - credentials: 'include', - body: JSON.stringify({ - text: "Your text to summarize here...", - config: { maxLength: 100, style: "concise" } - }) -}).then(r => r.json()).then(console.log); +# 4. Use Sidebar or Hybrid layout +# 5. Build with DataTable, forms, and charts patterns ``` -**cURL (with session cookies):** -1. Login in browser first -2. DevTools → Application → Cookies → Copy `better-auth.session_token` -3. Use cookie in cURL: +### Marketing Site + App ```bash -curl -X POST http://localhost:3000/api/summarize \ - -H "Content-Type: application/json" \ - -H "Cookie: better-auth.session_token=your-token-here" \ - -d '{"text": "Your text here...", "config": {"maxLength": 100}}' -``` - -**Postman:** -1. Login in browser, copy session cookie from DevTools -2. Add header: `Cookie: better-auth.session_token=your-token-here` - -**Unauthenticated Request Response:** -```json -{ - "success": false, - "error": "Authentication required", - "data": null -} +# 1. Use Marketing layout for landing pages +# 2. Use Sidebar layout for dashboard +# 3. Add CMS module for content management +# 4. Deploy to Cloudflare Workers (global CDN) ``` - -### AI Service Architecture - -The AI integration follows a clean service-based architecture: - -1. **API Route** (`/api/summarize`) - Handles HTTP requests, authentication, and validation -2. **Authentication Layer** - Validates user session before processing requests -3. **SummarizerService** - Encapsulates AI business logic -4. **Error Handling** - Comprehensive error responses with proper status codes -5. **Type Safety** - Full TypeScript support with Zod validation - -### AI Model Options - -Cloudflare Workers AI supports various models: -- **@cf/meta/llama-3.2-1b-instruct** - Text generation (current) -- **@cf/meta/llama-3.2-3b-instruct** - More capable text generation -- **@cf/meta/m2m100-1.2b** - Translation -- **@cf/baai/bge-base-en-v1.5** - Text embeddings -- **@cf/microsoft/resnet-50** - Image classification - -## 🔧 Advanced Configuration - -### Database Schema Changes +### AI-Powered Tool ```bash -# 1. Modify schema files in src/db/schemas/ -# 2. Generate migration -pnpm run db:generate:named "add_user_table" -# 3. Apply to local database -pnpm run db:migrate:local -# 4. Test your changes -# 5. Commit and deploy (migrations run automatically) +# 1. Use Centered layout for focused UX +# 2. Add AI module with Workers AI +# 3. Use Cloudflare R2 for file uploads +# 4. Stream responses with Server Actions ``` -### Adding New Cloudflare Resources -```bash -# 1. Update wrangler.jsonc with new resources -# 2. Regenerate types -pnpm run cf-typegen -# 3. Update your code to use new bindings -``` +--- -### Production Secrets Management -```bash -# Add secrets to production environment -pnpm run cf:secret BETTER_AUTH_SECRET -pnpm run cf:secret GOOGLE_CLIENT_ID -pnpm run cf:secret GOOGLE_CLIENT_SECRET -``` +## 🤝 Contributing -## 📊 Performance & Monitoring +Full Flare Stack is open source and welcomes contributions! -**Built-in Observability:** -- ✅ Cloudflare Analytics (enabled by default) -- ✅ Real User Monitoring (RUM) -- ✅ Error tracking and logging -- ✅ Performance metrics +**Ways to contribute:** +- 🐛 Report bugs via [GitHub Issues](https://github.com/jezweb/full-flare-stack/issues) +- 💡 Suggest features via [GitHub Discussions](https://github.com/jezweb/full-flare-stack/discussions) +- 📝 Improve documentation +- 🎨 Submit composed patterns +- 🧩 Share example modules -**Database Monitoring:** -```bash -# Monitor database performance -wrangler d1 insights next-cf-app +**See:** [Contributing Guide](./CONTRIBUTING.md) for detailed instructions. -# View database metrics in Cloudflare Dashboard -# Navigate to Workers & Pages → D1 → next-cf-app → Metrics -``` +--- -## 🚀 Deployment +## 🙏 Acknowledgments -### Automatic Deployment (Recommended) +Full Flare Stack is a fork of [fullstack-next-cloudflare](https://github.com/ifindev/fullstack-next-cloudflare) by [@ifindev](https://github.com/ifindev). -Push to `main` branch triggers automatic deployment via GitHub Actions: +**What we added:** +- ✅ Three-layer component architecture +- ✅ 43 shadcn/ui components (complete foundation) +- ✅ 5 production-ready layout variants +- ✅ Comprehensive architecture documentation +- ✅ Component decision framework +- ✅ Pattern build roadmap +- ✅ 15+ UX/DX improvements -```bash -git add . -git commit -m "feat: add new feature" -git push origin main -``` +Thank you to **@ifindev** for the excellent starting point! -**Deployment Pipeline:** -1. ✅ Install dependencies -2. ✅ Build application -3. ✅ Run database migrations -4. ✅ Deploy to Cloudflare Workers +**Also inspired by:** +- [Cloudflare SaaS Stack](https://github.com/supermemoryai/cloudflare-saas-stack) - Proven stack powering 20k+ users on $5/month +- [Supermemory.ai](https://git.new/memory) - Real-world Cloudflare Workers production app -### Manual Deployment +--- -```bash -# Deploy to production -pnpm run deploy +## 📄 License -# Deploy to preview environment -pnpm run deploy:preview -``` +MIT License - see [LICENSE](./LICENSE) for details. -## ✍️ Todos +Copyright (c) 2025 Jez Dawes / Jezweb -### 🤖 AI Features -- [ ] Add text translation service with `@cf/meta/m2m100-1.2b` -- [ ] Implement text embeddings for semantic search with `@cf/baai/bge-base-en-v1.5` -- [ ] Add image classification API with `@cf/microsoft/resnet-50` -- [ ] Create chat/conversation API with conversation memory -- [ ] Add content moderation with AI classification -- [ ] Implement sentiment analysis for user feedback +--- -### 💳 Payments & Communication -- [ ] Implement email sending with [Resend](https://resend.com/) & [Cloudflare Email Routing](https://www.cloudflare.com/developer-platform/products/email-routing/) -- [ ] Implement international payment gateway with [Polar.sh](https://polar.sh/) -- [ ] Implement Indonesian payment gateway either with [Xendit](https://www.xendit.co/en-id/), [Midtrans](https://midtrans.com/en), or [Duitku](https://www.duitku.com/) +## 💬 Community & Support -### 📊 Analytics & Performance -- [ ] Add Cloudflare Analytics integration -- [ ] Implement custom metrics tracking -- [ ] Add performance monitoring dashboard -- [ ] Create AI usage analytics and cost tracking +**GitHub:** +- [Issues](https://github.com/jezweb/full-flare-stack/issues) - Bug reports & feature requests +- [Discussions](https://github.com/jezweb/full-flare-stack/discussions) - Community chat +**Jezweb:** +- Website: [jezweb.com.au](https://jezweb.com.au) +- Email: jeremy@jezweb.net +- Phone: +61 411 056 876 +**Official Cloudflare Docs:** +- [Workers](https://developers.cloudflare.com/workers/) +- [D1 Database](https://developers.cloudflare.com/d1/) +- [R2 Storage](https://developers.cloudflare.com/r2/) +- [Workers AI](https://developers.cloudflare.com/workers-ai/) -## 🤝 Contributing +--- -Contributions are welcome! Please feel free to submit issues and pull requests. +## ⭐ Star History -## 📝 License +If you find Full Flare Stack useful, please consider giving it a star on GitHub! -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. +[![Star History Chart](https://api.star-history.com/svg?repos=jezweb/full-flare-stack&type=Date)](https://star-history.com/#jezweb/full-flare-stack&Date) --- -© 2025 Muhammad Arifin. All rights reserved. +**Built with ❤️ by [Jezweb](https://jezweb.com.au) • Powered by ⚡ Cloudflare** diff --git a/docs/COMPONENT_INVENTORY.md b/docs/COMPONENT_INVENTORY.md new file mode 100644 index 0000000..91325fc --- /dev/null +++ b/docs/COMPONENT_INVENTORY.md @@ -0,0 +1,274 @@ +# shadcn/ui Component Inventory + +Complete inventory of installed shadcn/ui components (Layer 1: UI Primitives) and their role in the three-layer architecture. + +**Last Updated:** 2025-11-10 +**Total Components:** 43 + +--- + +## 📊 Component Categories + +### Forms & Inputs (13 components) + +| Component | File | Purpose | Used In Patterns | +|-----------|------|---------|------------------| +| **button** | `button.tsx` | Interactive buttons | All patterns, FormActions, PageHeader | +| **input** | `input.tsx` | Text input | FormField, SearchableSelect, DataTable filters | +| **textarea** | `textarea.tsx` | Multi-line text | FormField, rich text patterns | +| **select** | `select.tsx` | Dropdown selection | FormField, filters, status selectors | +| **checkbox** | `checkbox.tsx` | Boolean selection | DataTable row selection, FormField | +| **label** | `label.tsx` | Form labels | FormField, all forms | +| **form** | `form.tsx` | Form context (React Hook Form) | All form patterns | +| **radio-group** | `radio-group.tsx` | Single selection from visible options | FormField, filter controls, plan selectors | +| **slider** | `slider.tsx` | Range input | Price filters, settings, volume controls | +| **switch** | `switch.tsx` | Toggle on/off | Settings, feature toggles, dark mode | +| **calendar** | `calendar.tsx` | Date selection | DateRangePicker, DatePicker patterns | +| **toggle** | `toggle.tsx` | Toggle button state | Text formatting, active filters | +| **toggle-group** | `toggle-group.tsx` | Exclusive/multiple toggle selection | ViewSwitcher, toolbar controls | + +--- + +### Data Display (6 components) + +| Component | File | Purpose | Used In Patterns | +|-----------|------|---------|------------------| +| **card** | `card.tsx` | Container with sections | CardView, dashboard widgets, product cards | +| **table** | `table.tsx` | Tabular data display | DataTable, CollectionContainer table view | +| **pagination** | `pagination.tsx` | Page navigation | DataTable, ListView, any paginated content | +| **badge** | `badge.tsx` | Status indicators | DataTable columns, status displays, counts | +| **avatar** | `avatar.tsx` | User images | User menus, lists, comment threads | +| **separator** | `separator.tsx` | Visual dividers | Layouts, menus, sections | + +--- + +### Overlays & Popups (8 components) + +| Component | File | Purpose | Used In Patterns | +|-----------|------|---------|------------------| +| **dialog** | `dialog.tsx` | Modal dialogs | Forms, detail views, confirmations | +| **alert-dialog** | `alert-dialog.tsx` | Confirmation dialogs | ConfirmDialog pattern, delete confirmations | +| **sheet** | `sheet.tsx` | Side panels | Mobile navigation, filters, detail views | +| **popover** | `popover.tsx` | Floating content | Date pickers, SearchableSelect (combobox), menus | +| **tooltip** | `tooltip.tsx` | Help text on hover | Icon buttons, abbreviations, help hints | +| **hover-card** | `hover-card.tsx` | Rich preview on hover | User cards, link previews, data previews | +| **dropdown-menu** | `dropdown-menu.tsx` | Action menus | Table row actions, user menus, filters | +| **navigation-menu** | `navigation-menu.tsx` | Complex navigation | Marketing site nav, mega menus | + +--- + +### Feedback & Status (5 components) + +| Component | File | Purpose | Used In Patterns | +|-----------|------|---------|------------------| +| **alert** | `alert.tsx` | Static notifications | EmptyState variants, error states, warnings | +| **sonner** | `sonner.tsx` | Toast notifications | Success/error feedback after actions | +| **progress** | `progress.tsx` | Loading progress | File uploads, multi-step forms, async operations | +| **skeleton** | `skeleton.tsx` | Loading placeholders | LoadingState pattern, suspense fallbacks | +| **scroll-area** | `scroll-area.tsx` | Custom scrollbars | Sidebar navigation, modals with long content | + +--- + +### Layout & Navigation (7 components) + +| Component | File | Purpose | Used In Patterns | +|-----------|------|---------|------------------| +| **tabs** | `tabs.tsx` | Tab navigation | PageHeader tabs, settings sections, view organization | +| **accordion** | `accordion.tsx` | Collapsible sections | Settings, FAQs, mobile nav, MultiStepForm sections | +| **breadcrumb** | `breadcrumb.tsx` | Hierarchical navigation | PageHeader, deep navigation paths | +| **sidebar** | `sidebar.tsx` | Sidebar layouts | DashboardLayout, app navigation | +| **command** | `command.tsx` | Command palette (⌘K) | Global search, quick actions, SearchableSelect | +| **color-picker** | `color-picker.tsx` | Color selection | Theme customization, category colors | +| ~~**combobox**~~ | *N/A* | **Not a component** - Pattern built from `command` + `popover` | SearchableSelect pattern | + +--- + +## 🎯 Installation History + +### November 10, 2025 - Foundation Installation + +**Session 1: Core components** (via layouts module) +- button, card, form, input, label, select, separator, sheet +- dialog, badge, dropdown-menu, navigation-menu, avatar +- checkbox, textarea, tooltip, popover, skeleton +- sidebar, color-picker + +**Session 2: Data display & feedback** (manual installation) +- table, pagination, calendar, sonner, alert, progress +- breadcrumb, tabs, scroll-area, switch + +**Session 3: Advanced forms & UI** (manual installation) +- accordion, radio-group, slider, hover-card +- command, toggle, toggle-group + +--- + +## 📦 Components by Composed Pattern + +Shows which Layer 2 (composed patterns) each Layer 1 (primitive) enables. + +### Priority 1: Data Display Patterns + +**DataTable** +- Uses: `table`, `pagination`, `checkbox`, `badge`, `dropdown-menu`, `button`, `input` +- Status: Ready to build + +**CardView** +- Uses: `card`, `badge`, `button`, `dropdown-menu`, `avatar` +- Status: Ready to build + +**ListView** +- Uses: `separator`, `avatar`, `badge`, `button` +- Status: Ready to build + +**ViewSwitcher** +- Uses: `toggle-group` +- Status: Ready to build + +**CollectionContainer** +- Uses: All data display patterns + `input`, `select`, `button` +- Status: Ready to build + +--- + +### Priority 2: Layout Patterns + +**PageHeader** +- Uses: `breadcrumb`, `tabs`, `button`, `separator` +- Status: Ready to build + +**DashboardLayout** +- Uses: `sidebar`, `navigation-menu`, `dropdown-menu`, `avatar`, `scroll-area` +- Status: Ready to build + +**SidebarNav** +- Uses: `sidebar`, `scroll-area`, `badge`, `separator` +- Status: Ready to build + +--- + +### Priority 3: Form Patterns + +**FormField** +- Uses: `form`, `label`, `input`, `textarea`, `select`, all form primitives +- Status: Ready to build + +**SearchableSelect** (Combobox) +- Uses: `command`, `popover`, `button` +- Status: Ready to build (requires custom composition - see shadcn docs) +- Reference: https://ui.shadcn.com/docs/components/combobox + +**DateRangePicker** +- Uses: `calendar`, `popover`, `button` +- Status: Ready to build + +**MultiStepForm** +- Uses: `tabs`, `accordion`, `progress`, `button`, `form` +- Status: Ready to build + +**FormActions** +- Uses: `button`, `separator` +- Status: Ready to build + +--- + +### Priority 4: Feedback Patterns + +**EmptyState** +- Uses: `alert`, `button` +- Status: Ready to build + +**LoadingState** +- Uses: `skeleton`, `progress` +- Status: Ready to build + +**ConfirmDialog** +- Uses: `alert-dialog`, `button` +- Status: Ready to build + +**Toast System** +- Uses: `sonner` +- Status: Ready to build (just wrap sonner with consistent API) + +--- + +### Priority 5: Media/Upload Patterns + +**FileUpload** +- Uses: `button`, `progress`, `badge`, `card` +- Status: Ready to build (needs R2 integration) + +**ImageGallery** +- Uses: `dialog`, `button`, `card` +- Status: Ready to build + +**AvatarUpload** +- Uses: `avatar`, `button`, `dialog` +- Status: Ready to build + +--- + +## 🚫 Components NOT Installed (Intentionally Skipped) + +| Component | Reason for Skipping | +|-----------|---------------------| +| **drawer** | Already have `sheet` which serves the same purpose | +| **context-menu** | Niche use case (right-click menus), have `dropdown-menu` | +| **menubar** | Desktop app pattern, rare in web apps | +| **carousel** | Marketing site component, not core to business apps | +| **resizable** | Code editor pattern, very niche | +| **input-otp** | Only needed for 2FA, add when implementing auth features | +| **collapsible** | Use `accordion` instead for consistent patterns | +| **combobox** | Not a registry component - build from `command` + `popover` | + +--- + +## 🔄 Upgrade Strategy + +When shadcn/ui releases new components or updates: + +1. **Check changelog**: https://ui.shadcn.com/docs/changelog +2. **Review breaking changes** in Tailwind v4 compatibility +3. **Update components**: `pnpm dlx shadcn@latest update` +4. **Test composed patterns** that depend on updated primitives +5. **Update this inventory** with new components + +--- + +## 📚 Resources + +**Official Documentation:** +- Component docs: https://ui.shadcn.com/docs/components +- Installation guide: https://ui.shadcn.com/docs/installation/vite +- Tailwind v4 setup: https://ui.shadcn.com/docs/tailwind-v4 + +**Related Project Docs:** +- [Architecture Overview](./development-planning/architecture-overview.md) - Three-layer system +- [Component Decision Framework](./development-planning/component-decision-framework.md) - Where to put components +- [Pattern Library Plan](./development-planning/pattern-library-plan.md) - Detailed pattern specifications +- [Composed Patterns Roadmap](./COMPOSED_PATTERNS_ROADMAP.md) - Build order and dependencies + +--- + +## ✅ Component Health Check + +Run this checklist after major updates: + +- [ ] All components render without errors +- [ ] Dark/light mode works for all components +- [ ] TypeScript types are up to date +- [ ] Accessibility features intact (keyboard nav, ARIA labels) +- [ ] Mobile responsive behavior maintained +- [ ] No breaking changes in composed patterns + +--- + +**Next Steps:** +1. Start building Priority 1 composed patterns (DataTable, ViewSwitcher) +2. Document patterns as you build them +3. Extract patterns after 3rd use in features +4. Keep this inventory updated with new components + +--- + +*This inventory represents the foundation (Layer 1) for the three-layer architecture. All business applications will use these primitives to build composed patterns (Layer 2) and feature modules (Layer 3).* diff --git a/docs/COMPOSED_PATTERNS_ROADMAP.md b/docs/COMPOSED_PATTERNS_ROADMAP.md new file mode 100644 index 0000000..55bd6b4 --- /dev/null +++ b/docs/COMPOSED_PATTERNS_ROADMAP.md @@ -0,0 +1,895 @@ +# Composed Patterns Development Roadmap + +Build order and dependencies for Layer 2 (composed patterns) based on the three-layer architecture. + +**Last Updated:** 2025-11-10 +**Status:** Foundation complete, ready to build patterns + +--- + +## 🎯 Build Strategy + +**Golden Rule:** Build patterns as you need them, not speculatively. + +### When to Build a Pattern + +1. **After 3rd use** - You've used similar code in 3 different features +2. **Clear reuse case** - Other features will obviously need this +3. **No business logic** - Pattern can be made generic with props +4. **Dependencies ready** - All required shadcn primitives are installed + +### How to Build + +1. Create pattern in `/components/composed/[category]/[Pattern].tsx` +2. Use only Layer 1 primitives (shadcn/ui components) +3. Accept data via props, emit events via callbacks +4. NO Server Actions, NO database access +5. Document with PATTERN_TEMPLATE.md +6. Use in at least 3 features before considering it "proven" + +--- + +## 📋 Priority 1: Data Display Patterns + +**Why First:** Every feature displays data. These provide maximum ROI. + +**Build Order:** Build when you create your first feature that needs data display (products, users, orders, etc.) + +--- + +### 1.1 DataTable ⚡ HIGHEST PRIORITY + +**Location:** `/components/composed/data-display/DataTable.tsx` + +**Dependencies (shadcn):** +- ✅ table +- ✅ pagination +- ✅ checkbox (for row selection) +- ✅ badge (for status columns) +- ✅ dropdown-menu (for row actions) +- ✅ button +- ✅ input (for search/filters) + +**Features to Implement:** +- [ ] Basic table with columns +- [ ] Sortable columns (client-side) +- [ ] Pagination controls +- [ ] Row selection (single/multiple) +- [ ] Column visibility toggle +- [ ] Search/filter input +- [ ] Row actions dropdown +- [ ] Empty state +- [ ] Loading state +- [ ] Mobile responsive (card view on mobile) +- [ ] Export to CSV (optional) + +**Tech Stack:** +- TanStack Table v8 +- Zod for column definitions (optional) +- Server Actions for data fetching (passed as props) + +**Reference:** +- shadcn Data Table: https://ui.shadcn.com/docs/components/data-table +- TanStack Table: https://tanstack.com/table/latest + +**Usage Example:** +```typescript +// Feature component (Layer 3) +export async function ProductList() { + const products = await getProducts(); + + return ( + router.push(`/products/${product.id}`)} + /> + ); +} +``` + +**Build Trigger:** Create this when building your first CRUD feature module. + +--- + +### 1.2 ViewSwitcher ⚡ HIGH PRIORITY + +**Location:** `/components/composed/data-display/ViewSwitcher.tsx` + +**Dependencies (shadcn):** +- ✅ toggle-group + +**Features to Implement:** +- [ ] Toggle between views (table/card/list/kanban/calendar) +- [ ] Persist preference to localStorage +- [ ] Icon indicators for each view +- [ ] Active state styling +- [ ] Responsive (hide labels on mobile) + +**Usage Example:** +```typescript + +``` + +**Build Trigger:** Build immediately after DataTable (you'll want alternative views). + +--- + +### 1.3 CardView + +**Location:** `/components/composed/data-display/CardView.tsx` + +**Dependencies (shadcn):** +- ✅ card +- ✅ badge +- ✅ button +- ✅ dropdown-menu +- ✅ avatar + +**Features to Implement:** +- [ ] Responsive grid layout +- [ ] Card actions menu +- [ ] Image/icon support +- [ ] Status badges +- [ ] Hover effects +- [ ] Empty state +- [ ] Loading skeleton + +**Usage Example:** +```typescript + ( + + )} + actions={[ + { label: 'Edit', onClick: handleEdit }, + { label: 'Delete', onClick: handleDelete } + ]} +/> +``` + +**Build Trigger:** After ViewSwitcher, when you want grid view option. + +--- + +### 1.4 ListView + +**Location:** `/components/composed/data-display/ListView.tsx` + +**Dependencies (shadcn):** +- ✅ separator +- ✅ avatar +- ✅ badge +- ✅ button + +**Features to Implement:** +- [ ] Compact list items +- [ ] Avatar/icon support +- [ ] Secondary text +- [ ] Right-side actions +- [ ] Grouped lists with headers +- [ ] Sticky section headers +- [ ] Empty state + +**Usage Example:** +```typescript + user.role} + renderItem={(user) => ( + + )} +/> +``` + +**Build Trigger:** After CardView, for mobile-friendly alternative. + +--- + +### 1.5 CollectionContainer + +**Location:** `/components/composed/data-display/CollectionContainer.tsx` + +**Dependencies (shadcn):** +- ✅ All above patterns +- ✅ input (search) +- ✅ select (filters) +- ✅ button (actions) + +**Features to Implement:** +- [ ] Page header with title/actions +- [ ] Search bar +- [ ] Filter controls +- [ ] Sort dropdown +- [ ] View switcher integration +- [ ] Content area (renders active view) +- [ ] Loading/error/empty states + +**Usage Example:** +```typescript + }, + { type: 'card', render: (data) => } + ]} + filters={[ + { field: 'category', label: 'Category', options: categories }, + { field: 'status', label: 'Status', options: ['active', 'draft'] } + ]} + actions={[{ label: 'Add Product', onClick: openDialog, icon: Plus }]} + onSearch={handleSearch} +/> +``` + +**Build Trigger:** After all data display patterns, when you want turnkey collection pages. + +--- + +## 📋 Priority 2: Layout Patterns + +**Why Second:** Consistent layouts speed up every feature. + +**Build Order:** Build after first 2-3 features reveal common layout needs. + +--- + +### 2.1 PageHeader ⚡ HIGH PRIORITY + +**Location:** `/components/composed/layouts/PageHeader.tsx` + +**Dependencies (shadcn):** +- ✅ breadcrumb +- ✅ tabs +- ✅ button +- ✅ separator + +**Features to Implement:** +- [ ] Page title + description +- [ ] Breadcrumbs +- [ ] Action buttons (right-aligned) +- [ ] Back button (optional) +- [ ] Tab navigation (optional) +- [ ] Responsive (stack on mobile) + +**Usage Example:** +```typescript +Export, + + ]} + tabs={[ + { label: 'All', value: 'all', href: '/products' }, + { label: 'Active', value: 'active', href: '/products?status=active' } + ]} +/> +``` + +**Build Trigger:** Immediately, you'll use this on every page. + +--- + +### 2.2 DashboardLayout + +**Location:** `/components/composed/layouts/DashboardLayout.tsx` + +**Dependencies (shadcn):** +- ✅ sidebar +- ✅ navigation-menu +- ✅ dropdown-menu +- ✅ avatar +- ✅ scroll-area + +**Features to Implement:** +- [ ] Collapsible sidebar +- [ ] Top navigation bar +- [ ] Mobile drawer +- [ ] User menu +- [ ] Breadcrumbs integration +- [ ] Content area +- [ ] Footer (optional) + +**Build Trigger:** Already exists (via layouts module). May need refinement. + +--- + +### 2.3 SidebarNav + +**Location:** `/components/composed/layouts/SidebarNav.tsx` + +**Dependencies (shadcn):** +- ✅ sidebar +- ✅ scroll-area +- ✅ badge +- ✅ separator + +**Features to Implement:** +- [ ] Nested navigation items +- [ ] Active state highlighting +- [ ] Icon support +- [ ] Badge/notification counts +- [ ] Collapsible groups +- [ ] Mobile-friendly + +**Build Trigger:** Already exists (via layouts module). May need refinement. + +--- + +## 📋 Priority 3: Form Patterns + +**Why Third:** Forms are everywhere, good patterns save hours per feature. + +**Build Order:** Build as you encounter complex form needs. + +--- + +### 3.1 SearchableSelect (Combobox) ⚡ HIGH PRIORITY + +**Location:** `/components/composed/forms/SearchableSelect.tsx` + +**Dependencies (shadcn):** +- ✅ command +- ✅ popover +- ✅ button + +**Features to Implement:** +- [ ] Search/filter options +- [ ] Keyboard navigation +- [ ] Multi-select mode (optional) +- [ ] Create new option (optional) +- [ ] Async loading support +- [ ] Custom option rendering +- [ ] Clear selection + +**Reference:** +- shadcn Combobox: https://ui.shadcn.com/docs/components/combobox + +**Usage Example:** +```typescript + ( +
+ + {user.name} +
+ )} +/> +``` + +**Build Trigger:** When you need to select from 10+ options (categories, users, tags). + +--- + +### 3.2 DateRangePicker + +**Location:** `/components/composed/forms/DateRangePicker.tsx` + +**Dependencies (shadcn):** +- ✅ calendar +- ✅ popover +- ✅ button + +**Features to Implement:** +- [ ] Date range selection +- [ ] Preset ranges (Today, Last 7 days, Last 30 days, etc.) +- [ ] Clear selection +- [ ] Timezone aware +- [ ] Min/max date constraints +- [ ] Format customization + +**Usage Example:** +```typescript + +``` + +**Build Trigger:** When building reports/analytics features. + +--- + +### 3.3 FormField Enhancement + +**Location:** `/components/composed/forms/FormField.tsx` + +**Dependencies (shadcn):** +- ✅ form +- ✅ label +- ✅ All input primitives + +**Features to Implement:** +- [ ] Unified field wrapper +- [ ] Required indicator +- [ ] Character counter +- [ ] Help text +- [ ] Error message display +- [ ] Field-level loading state + +**Usage Example:** +```typescript + + + +``` + +**Build Trigger:** After building 2-3 forms with repetitive field markup. + +--- + +### 3.4 MultiStepForm + +**Location:** `/components/composed/forms/MultiStepForm.tsx` + +**Dependencies (shadcn):** +- ✅ tabs +- ✅ accordion +- ✅ progress +- ✅ button +- ✅ form + +**Features to Implement:** +- [ ] Step indicator with progress +- [ ] Next/Previous navigation +- [ ] Per-step validation +- [ ] Data persistence between steps +- [ ] Review step +- [ ] Save draft functionality + +**Usage Example:** +```typescript + +``` + +**Build Trigger:** When building onboarding or complex creation flows. + +--- + +### 3.5 FormActions + +**Location:** `/components/composed/forms/FormActions.tsx` + +**Dependencies (shadcn):** +- ✅ button +- ✅ separator + +**Features to Implement:** +- [ ] Save/Cancel buttons +- [ ] Loading states +- [ ] Disabled states +- [ ] Custom actions +- [ ] Sticky footer (optional) +- [ ] Responsive layout + +**Usage Example:** +```typescript + +``` + +**Build Trigger:** After building 2-3 forms with similar button layouts. + +--- + +## 📋 Priority 4: Feedback Patterns + +**Why Fourth:** Good feedback improves UX and perceived performance. + +**Build Order:** Build as you encounter the need for consistent feedback. + +--- + +### 4.1 EmptyState ⚡ HIGH PRIORITY + +**Location:** `/components/composed/feedback/EmptyState.tsx` + +**Dependencies (shadcn):** +- ✅ alert +- ✅ button + +**Features to Implement:** +- [ ] Icon/illustration +- [ ] Title and description +- [ ] Call-to-action button +- [ ] Variants (no-data, no-results, error, no-permission) +- [ ] Custom rendering + +**Usage Example:** +```typescript + +``` + +**Build Trigger:** Immediately, you'll use this everywhere. + +--- + +### 4.2 LoadingState + +**Location:** `/components/composed/feedback/LoadingState.tsx` + +**Dependencies (shadcn):** +- ✅ skeleton +- ✅ progress + +**Features to Implement:** +- [ ] Skeleton loaders (table, card, list) +- [ ] Spinner variants +- [ ] Progress bar +- [ ] Loading messages +- [ ] Full-page overlay option + +**Usage Example:** +```typescript + +``` + +**Build Trigger:** Immediately, for Suspense fallbacks. + +--- + +### 4.3 ConfirmDialog + +**Location:** `/components/composed/feedback/ConfirmDialog.tsx` + +**Dependencies (shadcn):** +- ✅ alert-dialog +- ✅ button + +**Features to Implement:** +- [ ] Variants (danger, warning, info) +- [ ] Custom title/description +- [ ] Async action support +- [ ] Loading state during action +- [ ] Keyboard shortcuts (Enter/Esc) + +**Usage Example:** +```typescript + { + await deleteProduct(id); + toast.success('Product deleted'); + }} +/> +``` + +**Build Trigger:** When implementing delete actions. + +--- + +### 4.4 Toast System + +**Location:** Already provided by `sonner` + +**Usage:** +```typescript +import { toast } from 'sonner'; + +toast.success('Product created successfully'); +toast.error('Failed to create product'); +toast.info('Feature coming soon'); +toast.warning('Unsaved changes'); +``` + +**Build Trigger:** Already available, just document usage patterns. + +--- + +## 📋 Priority 5: Media/Upload Patterns + +**Why Fifth:** File handling is common but requires careful implementation. + +**Build Order:** Build when implementing file upload features. + +--- + +### 5.1 FileUpload + +**Location:** `/components/composed/media/FileUpload.tsx` + +**Dependencies (shadcn):** +- ✅ button +- ✅ progress +- ✅ badge +- ✅ card + +**External Dependencies:** +- react-dropzone (optional, for better DX) +- R2 integration (via Server Action) + +**Features to Implement:** +- [ ] Drag and drop zone +- [ ] Click to upload +- [ ] Multiple file support +- [ ] File type validation +- [ ] Size validation +- [ ] Upload progress +- [ ] Preview thumbnails +- [ ] Remove files +- [ ] Error states + +**Usage Example:** +```typescript + { + const result = await uploadToR2(files); + return result; + }} + existingFiles={product.images} + onRemove={handleRemoveImage} +/> +``` + +**Build Trigger:** When building features with image/file uploads. + +--- + +### 5.2 ImageGallery + +**Location:** `/components/composed/media/ImageGallery.tsx` + +**Dependencies (shadcn):** +- ✅ dialog +- ✅ button +- ✅ card + +**Features to Implement:** +- [ ] Grid layout +- [ ] Lightbox/modal view +- [ ] Image zoom +- [ ] Navigation (prev/next) +- [ ] Thumbnails +- [ ] Delete action +- [ ] Download action +- [ ] Responsive grid + +**Build Trigger:** When displaying multiple images per item. + +--- + +### 5.3 AvatarUpload + +**Location:** `/components/composed/media/AvatarUpload.tsx` + +**Dependencies (shadcn):** +- ✅ avatar +- ✅ button +- ✅ dialog + +**External Dependencies:** +- react-image-crop (for cropping) + +**Features to Implement:** +- [ ] Circle preview +- [ ] Crop/resize UI +- [ ] Replace/remove +- [ ] Fallback initials +- [ ] Loading state +- [ ] Upload to R2 + +**Build Trigger:** When implementing user profiles or team members. + +--- + +## 📋 Priority 6: Navigation Patterns + +**Build Order:** As needed for complex navigation. + +--- + +### 6.1 Breadcrumbs (Wrapper) + +**Location:** `/components/composed/navigation/Breadcrumbs.tsx` + +**Dependencies (shadcn):** +- ✅ breadcrumb + +**Features to Implement:** +- [ ] Auto-generate from route +- [ ] Custom labels +- [ ] Overflow handling +- [ ] Mobile responsive + +**Build Trigger:** Low priority (shadcn breadcrumb is already good). + +--- + +### 6.2 Pagination (Enhanced) + +**Location:** `/components/composed/navigation/Pagination.tsx` + +**Dependencies (shadcn):** +- ✅ pagination +- ✅ select + +**Features to Implement:** +- [ ] Items per page selector +- [ ] Jump to page input +- [ ] Total count display +- [ ] First/last buttons +- [ ] Responsive (compact on mobile) + +**Build Trigger:** When DataTable pagination needs enhancement. + +--- + +## 🎯 Build Phases + +### Phase 1: Foundation (Week 1-2) + +**Build when creating first CRUD feature:** +1. ✅ PageHeader +2. ✅ EmptyState +3. ✅ LoadingState +4. ✅ DataTable +5. ✅ ViewSwitcher + +**Outcome:** Can build complete feature pages with data display. + +--- + +### Phase 2: Forms & Actions (Week 3-4) + +**Build when creating second feature with forms:** +1. ✅ FormField (enhanced) +2. ✅ FormActions +3. ✅ ConfirmDialog +4. ✅ SearchableSelect (Combobox) + +**Outcome:** Can build complex forms with rich interactions. + +--- + +### Phase 3: Alternative Views (Week 5-6) + +**Build when users request different data views:** +1. ✅ CardView +2. ✅ ListView +3. ✅ CollectionContainer +4. ✅ DateRangePicker (if needed) + +**Outcome:** Flexible data display options. + +--- + +### Phase 4: Advanced Features (Week 7+) + +**Build as specific needs arise:** +1. ✅ MultiStepForm (if building onboarding) +2. ✅ FileUpload (if uploading files) +3. ✅ ImageGallery (if displaying images) +4. ✅ Command Palette (for power users) + +**Outcome:** Polish and advanced UX features. + +--- + +## 📊 Progress Tracking + +| Pattern | Priority | Dependencies Ready | Status | Built Date | +|---------|----------|-------------------|--------|------------| +| DataTable | P1 | ✅ | ⏸️ Not started | - | +| ViewSwitcher | P1 | ✅ | ⏸️ Not started | - | +| CardView | P1 | ✅ | ⏸️ Not started | - | +| ListView | P1 | ✅ | ⏸️ Not started | - | +| CollectionContainer | P1 | ✅ | ⏸️ Not started | - | +| PageHeader | P2 | ✅ | ⏸️ Not started | - | +| DashboardLayout | P2 | ✅ | ✅ Exists | 2025-11-08 | +| SidebarNav | P2 | ✅ | ✅ Exists | 2025-11-08 | +| SearchableSelect | P3 | ✅ | ⏸️ Not started | - | +| DateRangePicker | P3 | ✅ | ⏸️ Not started | - | +| FormField | P3 | ✅ | ⏸️ Not started | - | +| MultiStepForm | P3 | ✅ | ⏸️ Not started | - | +| FormActions | P3 | ✅ | ⏸️ Not started | - | +| EmptyState | P4 | ✅ | ⏸️ Not started | - | +| LoadingState | P4 | ✅ | ⏸️ Not started | - | +| ConfirmDialog | P4 | ✅ | ⏸️ Not started | - | +| FileUpload | P5 | ✅ | ⏸️ Not started | - | +| ImageGallery | P5 | ✅ | ⏸️ Not started | - | +| AvatarUpload | P5 | ✅ | ⏸️ Not started | - | + +--- + +## 🔄 Pattern Review Schedule + +After building each pattern: + +1. **Document it** - Use PATTERN_TEMPLATE.md +2. **Test in 3 features** - Ensure it's truly reusable +3. **Refine API** - Improve props based on usage +4. **Add to examples** - Create usage examples +5. **Mark complete** - Update progress table + +--- + +## 📚 Resources + +**Official References:** +- shadcn/ui Components: https://ui.shadcn.com/docs/components +- shadcn/ui Pro Blocks: https://ui.shadcn.com/blocks +- TanStack Table: https://tanstack.com/table/latest +- React Hook Form: https://react-hook-form.com/ + +**Inspiration:** +- Tremor: https://tremor.so/ (Data viz) +- Magic UI: https://magicui.design/ (Animated components) +- Aceternity UI: https://ui.aceternity.com/ (Creative components) + +**Related Docs:** +- [Component Inventory](./COMPONENT_INVENTORY.md) - All installed primitives +- [Pattern Library Plan](./development-planning/pattern-library-plan.md) - Detailed specs +- [Component Decision Framework](./development-planning/component-decision-framework.md) - Where to put components +- [Module Development Guide](./development-planning/module-development-guide.md) - Building features + +--- + +**Next Action:** Start building your first feature module (e.g., products, invoices, tasks). Extract DataTable pattern when you need it in the 3rd feature. diff --git a/docs/development-planning/README.md b/docs/development-planning/README.md new file mode 100644 index 0000000..77eb0be --- /dev/null +++ b/docs/development-planning/README.md @@ -0,0 +1,401 @@ +# Full-Stack Next.js + Cloudflare Architecture Documentation + +Complete reference documentation for building client applications with the three-layer architecture pattern. + +--- + +## 📚 Documentation Set + +This documentation package contains comprehensive guides for developing with the Next.js + Cloudflare stack using a three-layer component architecture. + +### Core Documents + +1. **[Architecture Overview](./architecture-overview.md)** - Start here + - Explains the three-layer architecture + - Directory structure + - Benefits and principles + - Evolution strategy + +2. **[Component Decision Framework](./component-decision-framework.md)** - For daily decisions + - Decision tree for component placement + - Detailed criteria for each layer + - Common scenarios with solutions + - Red flags and anti-patterns + +3. **[Pattern Library Plan](./pattern-library-plan.md)** - For building reusable patterns + - Prioritized list of patterns to build + - Detailed specifications for each pattern + - Implementation strategy + - Resources and inspiration + +4. **[Module Development Guide](./module-development-guide.md)** - For building features + - Step-by-step module creation + - Best practices + - Code examples + - Common patterns + +5. **[Architecture Quick Reference](./architecture-quick-reference.md)** - For quick lookups + - Fast decision trees + - Common tasks and commands + - Code snippets + - Troubleshooting + +--- + +## 🎯 How to Use This Documentation + +### For First-Time Setup + +1. Read **Architecture Overview** to understand the system +2. Skim **Component Decision Framework** to learn the rules +3. Reference **Quick Reference** while working + +### When Building a New Feature + +1. Use **Module Development Guide** for step-by-step instructions +2. Check **Component Decision Framework** when unsure about placement +3. Keep **Quick Reference** open for commands and snippets + +### When Building Reusable Patterns + +1. Consult **Pattern Library Plan** for pattern ideas +2. Follow **Component Decision Framework** for proper abstraction +3. Reference existing patterns in **Pattern Library Plan** + +### When Stuck or Confused + +1. Check **Quick Reference** decision trees +2. Review **Component Decision Framework** scenarios +3. Look at examples in **Module Development Guide** + +--- + +## 🏗️ Architecture Summary + +### The Three Layers + +``` +┌─────────────────────────────────────────────────┐ +│ Layer 3: Feature Modules │ +│ /modules/[feature]/ │ +│ • Business logic │ +│ • Server Actions │ +│ • Database access │ +│ • Feature-specific components │ +└─────────────────────────────────────────────────┘ + ↓ uses +┌─────────────────────────────────────────────────┐ +│ Layer 2: Composed Patterns │ +│ /components/composed/ │ +│ • Reusable UI patterns │ +│ • No business logic │ +│ • Used across 3+ features │ +│ • Prop-based configuration │ +└─────────────────────────────────────────────────┘ + ↓ uses +┌─────────────────────────────────────────────────┐ +│ Layer 1: UI Primitives │ +│ /components/ui/ │ +│ • shadcn/ui components │ +│ • Single-responsibility │ +│ • Maximum flexibility │ +│ • No opinions │ +└─────────────────────────────────────────────────┘ +``` + +### Quick Decision Guide + +**"Where should this component go?"** + +``` +Has business logic? + → YES: /modules/[feature]/components/ + → NO: Continue... + +Will be used in 3+ features? + → YES: /components/composed/[category]/ + → NO: Continue... + +Is it a shadcn component? + → YES: /components/ui/ + → NO: /components/shared/ or inline +``` + +--- + +## 🚀 Getting Started Checklist + +### Phase 1: Foundation Setup +- [ ] Read Architecture Overview +- [ ] Understand the three layers +- [ ] Review existing repo structure +- [ ] Create `/components/composed/` directory structure + +### Phase 2: Build First Feature +- [ ] Follow Module Development Guide +- [ ] Create database schema +- [ ] Build CRUD operations +- [ ] Create UI components +- [ ] Test thoroughly + +### Phase 3: Extract Patterns +- [ ] Identify repeated UI patterns (after 3rd use) +- [ ] Extract to `/components/composed/` +- [ ] Document the pattern +- [ ] Use in multiple features + +### Phase 4: Refine & Scale +- [ ] Build remaining priority patterns +- [ ] Document learnings +- [ ] Share with team +- [ ] Iterate based on usage + +--- + +## 📖 Document Quick Links + +### By Use Case + +**Building a new feature?** +→ [Module Development Guide](./module-development-guide.md) + +**Not sure where a component goes?** +→ [Component Decision Framework](./component-decision-framework.md) + +**Need to extract a reusable pattern?** +→ [Pattern Library Plan](./pattern-library-plan.md) + +**Looking for a quick answer?** +→ [Architecture Quick Reference](./architecture-quick-reference.md) + +**Want to understand the big picture?** +→ [Architecture Overview](./architecture-overview.md) + +--- + +## 💡 Key Principles + +1. **Build patterns as you need them** - Don't over-architect upfront +2. **Extract after 3rd use** - Prove the pattern before abstracting +3. **Server Components by default** - Only use 'use client' when needed +4. **Clear layer boundaries** - No business logic in patterns +5. **Consistent patterns** - Reuse rather than rebuild + +--- + +## 🛠️ Development Workflow + +### Daily Development + +```bash +# Terminal 1: Wrangler (for D1 access) +pnpm run wrangler:dev + +# Terminal 2: Next.js (with HMR) +pnpm run dev + +# Browser: http://localhost:3000 +``` + +### When You Need to... + +**Create a new feature module:** +```bash +mkdir -p src/modules/[feature]/{actions,components,hooks,models,schemas} +# Then follow Module Development Guide +``` + +**Add a database table:** +```bash +# Edit src/db/schema.ts +pnpm run db:generate:named "add_[table]" +pnpm run db:migrate:local +``` + +**Extract a reusable pattern:** +```bash +# 1. Identify the pattern (used 3+ times) +# 2. Create in /components/composed/[category]/ +# 3. Make it generic (props-based) +# 4. Document usage +# 5. Replace usage in features +``` + +**Deploy to production:** +```bash +pnpm run build:cf +pnpm run db:migrate:prod +pnpm run deploy +``` + +--- + +## 📝 Common Patterns Reference + +### Server Action Structure +```typescript +'use server'; +export async function myAction(input: Input): Promise> { + try { + const session = await auth(); // 1. Authenticate + if (!session?.user) return { success: false, error: 'Unauthorized' }; + + const validated = schema.parse(input); // 2. Validate + const result = await db.insert(...); // 3. Database operation + revalidatePath('/path'); // 4. Revalidate + + return { success: true, data: result }; // 5. Return + } catch (error) { + return { success: false, error: 'Message' }; + } +} +``` + +### Component with Server Action +```typescript +// Server Component (default) +export async function MyList() { + const result = await getItems(); + if (!result.success) return ; + return ; +} + +// Client Component (when needed) +'use client'; +export function MyForm() { + const form = useForm({ + resolver: zodResolver(schema), + }); + + const onSubmit = async (data) => { + const result = await createItem(data); + if (result.success) toast.success('Created!'); + }; + + return
...
; +} +``` + +--- + +## 🎓 Learning Path + +### Week 1-2: Foundation +- [ ] Understand the three-layer architecture +- [ ] Build your first feature module +- [ ] Create basic CRUD operations +- [ ] Add a simple page + +### Week 3-4: Patterns +- [ ] Identify repeated UI patterns +- [ ] Extract first reusable pattern (probably DataTable) +- [ ] Build PageHeader and EmptyState patterns +- [ ] Document your patterns + +### Week 5-6: Advanced +- [ ] Build form patterns (multi-step, fields) +- [ ] Add file upload with R2 +- [ ] Create view switcher (table/card/list) +- [ ] Optimize for mobile + +### Week 7-8: Polish +- [ ] Complete pattern library (priority patterns) +- [ ] Add loading/error states everywhere +- [ ] Improve accessibility +- [ ] Document everything + +--- + +## 🆘 Troubleshooting + +### "I don't know where to put this component" +→ Use the decision tree in [Component Decision Framework](./component-decision-framework.md) + +### "This pattern has business logic" +→ It's not a pattern, it's a feature component. Keep it in the module. + +### "I'm repeating code across features" +→ After 3rd use, extract to `/components/composed/`. See [Pattern Library Plan](./pattern-library-plan.md) + +### "My Server Action isn't updating the UI" +→ Did you call `revalidatePath()`? See [Module Development Guide](./module-development-guide.md) + +### "I need more examples" +→ Check the [Module Development Guide](./module-development-guide.md) for detailed examples + +--- + +## 🔄 Keeping Documentation Updated + +As your project evolves: + +1. **Document new patterns** in Pattern Library Plan +2. **Add examples** to Module Development Guide +3. **Update decision framework** if rules change +4. **Share learnings** with your team +5. **Refine based on experience** + +This documentation is a living guide that should evolve with your understanding and needs. + +--- + +## 📊 Success Metrics + +You'll know the architecture is working when: + +- ✅ You can build new features faster +- ✅ Components are easy to find +- ✅ Code duplication decreases +- ✅ New developers onboard quickly +- ✅ Bugs are isolated to modules +- ✅ Refactoring is straightforward +- ✅ Tests are easy to write + +--- + +## 🤝 Contributing Back + +If you discover: +- Better patterns +- Clearer explanations +- Useful examples +- Common gotchas + +Consider contributing back to the original template or sharing with the community. + +--- + +## 📞 Support + +When you need help: + +1. Check [Architecture Quick Reference](./architecture-quick-reference.md) first +2. Review relevant detailed guide +3. Look at existing code examples +4. Experiment in a feature branch +5. Ask your team or community + +--- + +## 🎯 Remember + +> "Build patterns as you need them, not speculatively. +> Extract after the 3rd use, not before. +> Keep layers separated, keep code clean." + +--- + +## 📅 Next Steps + +1. **Today:** Read Architecture Overview +2. **This Week:** Build your first module following Module Development Guide +3. **This Month:** Extract your first 3-5 reusable patterns +4. **This Quarter:** Have a comprehensive pattern library + +Good luck building amazing applications! 🚀 + +--- + +*Last Updated: 2025* +*For: Full-Stack Next.js + Cloudflare Template* +*Repository: github.com/jezweb/fullstack-next-cloudflare* diff --git a/docs/development-planning/architecture-overview.md b/docs/development-planning/architecture-overview.md new file mode 100644 index 0000000..a6fea6c --- /dev/null +++ b/docs/development-planning/architecture-overview.md @@ -0,0 +1,275 @@ +# Full-Stack Next.js + Cloudflare Architecture + +## Overview + +This architecture uses a three-layer component structure designed for building reusable, maintainable client applications on Cloudflare's edge infrastructure. + +## The Three-Layer Architecture + +### Layer 1: UI Primitives (`/components/ui/`) + +**Purpose:** Unstyled, unopinionated building blocks + +**What Lives Here:** +- shadcn/ui components (Button, Input, Dialog, Select, etc.) +- Single-responsibility components +- No business logic +- Maximum flexibility + +**Examples:** +- `button.tsx` +- `input.tsx` +- `dialog.tsx` +- `card.tsx` +- `select.tsx` + +**Rules:** +- Keep shadcn components as-is +- Don't add business logic +- These are imported by higher layers + +--- + +### Layer 2: Composed Patterns (`/components/composed/`) + +**Purpose:** Reusable UI patterns that solve common problems + +**What Lives Here:** +- Combinations of Layer 1 primitives +- Opinionated patterns without business logic +- Reusable across multiple features +- Configured via props + +**Organization:** +``` +/components/composed/ +├── data-display/ # Tables, cards, lists, view switchers +├── layouts/ # Page shells, headers, sidebars +├── forms/ # Multi-step forms, field patterns, actions +├── feedback/ # Empty states, loading, errors +├── media/ # File uploads, image galleries +└── navigation/ # Breadcrumbs, tabs, pagination +``` + +**Examples:** +- `DataTable` - sortable, filterable table with actions +- `PageHeader` - title + actions + breadcrumbs +- `EmptyState` - icon + message + action +- `FileUpload` - drag-drop with progress +- `MultiStepForm` - wizard-style form flow + +**Rules:** +- No direct database access +- No Server Actions +- Accept data via props +- Emit events via callbacks +- Must be reusable across 3+ features + +--- + +### Layer 3: Feature Modules (`/modules/`) + +**Purpose:** Complete features with business logic + +**What Lives Here:** +- Domain-specific features +- Business logic and data handling +- Server Actions +- Database schemas +- Feature-specific components + +**Module Structure:** +``` +/modules/[feature]/ +├── actions/ # Server Actions +├── components/ # Feature-specific UI +├── hooks/ # Feature hooks +├── models/ # Type definitions +├── schemas/ # Zod validation schemas +└── utils/ # Feature utilities +``` + +**Examples:** +- `auth` - Authentication flows +- `todos` - Todo CRUD operations +- `users` - User management +- `products` - Product catalog + +**Rules:** +- Can use both Layer 1 and Layer 2 +- Contains business logic +- Uses Server Actions for mutations +- Includes validation schemas + +--- + +## Data Flow + +``` +User Interaction + ↓ +Feature Module Component (Layer 3) + ↓ +Server Action (business logic) + ↓ +Database/API + ↑ +Feature Module Component + ↓ +Composed Pattern Component (Layer 2) + ↓ +UI Primitives (Layer 1) + ↓ +User sees result +``` + +--- + +## When to Create Each Layer + +### Create a UI Primitive when: +- ✅ It's a single, unopinionated building block +- ✅ shadcn/ui provides it +- ✅ It needs maximum flexibility +- ❌ Don't create your own primitives unless necessary + +### Create a Composed Pattern when: +- ✅ You're combining 2+ primitives repeatedly +- ✅ The pattern appears in 3+ different features +- ✅ It solves a common UI problem +- ✅ It has no business logic +- ❌ Don't create patterns speculatively + +### Create a Feature Module when: +- ✅ It represents a business domain +- ✅ It needs database access +- ✅ It has specific business rules +- ✅ It includes CRUD operations + +--- + +## Directory Structure + +``` +src/ +├── app/ # Next.js App Router +│ ├── (auth)/ # Auth pages +│ ├── api/ # API routes +│ ├── dashboard/ # Dashboard pages +│ └── globals.css +│ +├── components/ +│ ├── ui/ # Layer 1: shadcn primitives +│ │ ├── button.tsx +│ │ ├── input.tsx +│ │ └── ... +│ │ +│ ├── composed/ # Layer 2: Reusable patterns +│ │ ├── data-display/ +│ │ ├── layouts/ +│ │ ├── forms/ +│ │ ├── feedback/ +│ │ ├── media/ +│ │ └── navigation/ +│ │ +│ └── shared/ # One-off components +│ └── Logo.tsx +│ +├── modules/ # Layer 3: Features +│ ├── auth/ +│ ├── todos/ +│ ├── users/ +│ └── ... +│ +├── db/ # Database +│ ├── index.ts +│ └── schema.ts +│ +├── lib/ # Shared utilities +└── services/ # Business services +``` + +--- + +## Benefits of This Architecture + +1. **Clear Separation of Concerns** + - UI primitives are dumb and flexible + - Patterns are smart but generic + - Modules contain business logic + +2. **Reusability** + - Build once, use everywhere + - Easy to find components + - Reduces duplication + +3. **Maintainability** + - Changes to primitives affect everything (rare) + - Changes to patterns affect multiple features (carefully) + - Changes to modules are isolated (common) + +4. **Testability** + - Each layer can be tested independently + - Patterns can be tested without business logic + - Modules can be tested with mock patterns + +5. **Developer Experience** + - Clear mental model + - Easy to navigate + - Know where to put new code + +--- + +## Evolution Strategy + +### Phase 1: Foundation (Current) +- shadcn/ui components installed +- Basic feature modules (auth, todos) +- Initial layout components + +### Phase 2: Pattern Extraction (Next) +- Identify repeated UI patterns +- Extract to `/components/composed/` +- Document each pattern + +### Phase 3: Pattern Library (Future) +- Comprehensive pattern library +- Documentation site +- Storybook or similar + +### Phase 4: Optimization (Ongoing) +- Performance monitoring +- Code splitting +- Pattern refinement + +--- + +## Key Principles + +1. **Don't Over-Engineer Upfront** + - Build patterns as needs emerge + - Start in modules, extract to composed later + - Prove the pattern before abstracting + +2. **Maintain Clear Boundaries** + - No business logic in composed patterns + - No database access in components + - Server Actions only in feature modules + +3. **Prioritize Reusability** + - If used 3+ times, consider extracting + - If used once, keep it in the module + - Document the "why" for each pattern + +4. **Keep It Simple** + - Prefer composition over complexity + - Clear over clever + - Explicit over magic + +--- + +## Reference Documentation + +- [Component Decision Framework](./component-decision-framework.md) +- [Pattern Library Plan](./pattern-library-plan.md) +- [Module Development Guide](./module-development-guide.md) diff --git a/docs/development-planning/architecture-quick-reference.md b/docs/development-planning/architecture-quick-reference.md new file mode 100644 index 0000000..098bf95 --- /dev/null +++ b/docs/development-planning/architecture-quick-reference.md @@ -0,0 +1,508 @@ +# Architecture Quick Reference + +Fast lookup guide for component placement, patterns, and common tasks. + +--- + +## 🎯 Where Does This Go? + +### Component Placement Decision Tree + +``` +❓ What am I building? + +└─ 🎨 UI Building Block (Button, Input, Card) + → /components/ui/ + +└─ 🔄 Reusable UI Pattern (no business logic, used 3+ times) + → /components/composed/[category]/ + +└─ 🏢 Feature with Business Logic (CRUD, auth, data) + → /modules/[feature]/ + +└─ 🔧 One-off Component + → /components/shared/ or inline +``` + +--- + +## 📂 Directory Structure + +``` +src/ +├── app/ # Next.js routes +├── components/ +│ ├── ui/ # shadcn primitives +│ ├── composed/ # YOUR reusable patterns +│ │ ├── data-display/ +│ │ ├── layouts/ +│ │ ├── forms/ +│ │ ├── feedback/ +│ │ └── media/ +│ └── shared/ # One-offs +├── modules/ # Features with business logic +│ └── [feature]/ +│ ├── actions/ +│ ├── components/ +│ ├── hooks/ +│ ├── models/ +│ └── schemas/ +├── db/ # Database +├── lib/ # Shared utilities +└── services/ # Business services +``` + +--- + +## 🚦 Component Rules + +| Layer | Location | Business Logic? | Database? | Reused? | +|-------|----------|----------------|-----------|---------| +| **Primitives** | `/components/ui/` | ❌ | ❌ | ✅ Everywhere | +| **Patterns** | `/components/composed/` | ❌ | ❌ | ✅ 3+ features | +| **Features** | `/modules/[feature]/` | ✅ | ✅ | ❌ Feature-specific | +| **Shared** | `/components/shared/` | ❌ | ❌ | ❌ 1-2 uses | + +--- + +## 🛠️ Common Tasks + +### Create a New Feature Module + +```bash +# 1. Create directory structure +mkdir -p src/modules/[feature]/{actions,components,hooks,models,schemas} + +# 2. Add database schema in src/db/schema.ts +# 3. Generate migration +pnpm run db:generate:named "add_[feature]_table" + +# 4. Apply migration +pnpm run db:migrate:local + +# 5. Create files: +# - models/[entity].ts (TypeScript types) +# - schemas/[entity].schema.ts (Zod validation) +# - actions/create-[entity].ts (Server Action) +# - components/[Entity]List.tsx (UI) +``` + +### Create a Reusable Pattern + +```bash +# 1. Identify the pattern (used 3+ times?) +# 2. Choose category: +# - data-display (tables, cards, lists) +# - layouts (headers, sidebars) +# - forms (multi-step, fields) +# - feedback (empty, loading, errors) +# - media (uploads, galleries) + +# 3. Create component +touch src/components/composed/[category]/[Pattern].tsx + +# 4. Make it generic (props-based, no business logic) +# 5. Document usage +# 6. Use in features +``` + +### Add Database Table + +```typescript +// In src/db/schema.ts +export const myTable = sqliteTable('my_table', { + id: text('id').primaryKey().$defaultFn(() => createId()), + name: text('name').notNull(), + createdAt: integer('created_at', { mode: 'timestamp' }) + .notNull() + .$defaultFn(() => new Date()), +}); + +export type MyEntity = typeof myTable.$inferSelect; +export type NewMyEntity = typeof myTable.$inferInsert; +``` + +```bash +# Generate and apply +pnpm run db:generate:named "add_my_table" +pnpm run db:migrate:local +``` + +### Create Server Action + +```typescript +'use server'; + +import { db } from '@/db'; +import { auth } from '@/lib/auth'; +import { mySchema } from '../schemas/my.schema'; +import { revalidatePath } from 'next/cache'; + +export async function myAction(input: MyInput) { + try { + // 1. Auth + const session = await auth(); + if (!session?.user) { + return { success: false, error: 'Unauthorized' }; + } + + // 2. Validate + const validated = mySchema.parse(input); + + // 3. Database operation + const result = await db.insert(myTable).values(validated).returning(); + + // 4. Revalidate + revalidatePath('/path'); + + // 5. Return + return { success: true, data: result }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed' + }; + } +} +``` + +--- + +## 🎨 Pattern Priority List + +Build in this order as you need them: + +1. **DataTable** - sortable, filterable tables +2. **PageHeader** - title + actions + breadcrumbs +3. **EmptyState** - no data messaging +4. **LoadingState** - skeleton loaders +5. **FormField** - label + input + error +6. **FileUpload** - drag-drop with R2 +7. **CardView** - grid display +8. **ListView** - mobile-friendly lists +9. **ViewSwitcher** - toggle between views +10. **ConfirmDialog** - delete confirmations + +--- + +## 🔍 Decision Flowcharts + +### "Should I Extract This to a Pattern?" + +``` +Does it have business logic? +├─ YES → Keep in module +└─ NO → Continue + ↓ + Used in 3+ features? + ├─ YES → Extract to /components/composed/ + ├─ USED TWICE → Wait for 3rd use + └─ USED ONCE → Keep in module or /components/shared/ +``` + +### "Is This Client or Server Component?" + +``` +Does it need: +- useState/useEffect/event handlers +- Browser APIs +- Interactive form controls +├─ YES → 'use client' +└─ NO → Server Component (default) +``` + +### "Where Should This Type Live?" + +``` +Database schema type? +├─ YES → Export from /db/schema.ts +└─ NO → Continue + ↓ + Feature-specific? + ├─ YES → /modules/[feature]/models/ + └─ NO → Continue + ↓ + Pattern-related? + └─ YES → In the pattern file or /lib/types.ts +``` + +--- + +## 📝 Code Snippets + +### Server Action Response Type + +```typescript +type ActionResponse = + | { success: true; data: T; error?: never } + | { success: false; data?: never; error: string }; +``` + +### Zod Schema Pattern + +```typescript +import { z } from 'zod'; + +export const mySchema = z.object({ + name: z.string().min(1).max(100), + email: z.string().email(), + age: z.number().min(0).optional(), +}); + +export type MyInput = z.infer; +``` + +### React Hook Form Integration + +```typescript +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; + +const form = useForm({ + resolver: zodResolver(mySchema), + defaultValues: { name: '', email: '' }, +}); +``` + +### Server Component with Suspense + +```typescript +export async function MyList() { + const result = await getItems(); + + if (!result.success) { + return ; + } + + return ; +} + +// Wrap with Suspense +export function MyListWithSuspense() { + return ( + }> + + + ); +} +``` + +### Optimistic Updates + +```typescript +'use client'; + +import { useOptimistic } from 'react'; + +export function MyComponent({ items }) { + const [optimistic, addOptimistic] = useOptimistic( + items, + (state, newItem) => [...state, newItem] + ); + + const handleAdd = async (data) => { + addOptimistic(data); // Instant UI update + await createItem(data); // Actual mutation + }; + + return ; +} +``` + +--- + +## 🚨 Common Mistakes + +### ❌ Pattern with Business Logic + +```typescript +// ❌ BAD - pattern directly accessing database +export function UserTable() { + const users = await db.select().from(users); // ❌ NO! + return ; +} +``` + +```typescript +// ✅ GOOD - pattern receives data via props +export function DataTable({ data, columns }) { + return ...
; +} + +// Feature component handles data +export async function UserTable() { + const result = await getUsers(); // ✅ Server Action + return ; +} +``` + +### ❌ Module Importing from Another Module + +```typescript +// ❌ BAD - circular dependencies +import { getUserById } from '@/modules/users/actions'; + +export async function getOrder(id: string) { + const order = await db.select()...; + const user = await getUserById(order.userId); // ❌ Cross-module + return { order, user }; +} +``` + +```typescript +// ✅ GOOD - use database joins or services +export async function getOrder(id: string) { + const result = await db + .select() + .from(orders) + .leftJoin(users, eq(orders.userId, users.id)) // ✅ Join at DB level + .where(eq(orders.id, id)); + + return result; +} +``` + +### ❌ Forgetting to Revalidate + +```typescript +// ❌ BAD - no revalidation after mutation +export async function createItem(data) { + await db.insert(items).values(data); + return { success: true }; // ❌ Page won't update! +} +``` + +```typescript +// ✅ GOOD - revalidate affected paths +import { revalidatePath } from 'next/cache'; + +export async function createItem(data) { + await db.insert(items).values(data); + revalidatePath('/items'); // ✅ Refresh the page + return { success: true }; +} +``` + +--- + +## 📚 Key Files Reference + +| Purpose | File Location | +|---------|--------------| +| Database schemas | `/db/schema.ts` | +| Database connection | `/db/index.ts` | +| Auth configuration | `/lib/auth.ts` | +| Global styles | `/app/globals.css` | +| Cloudflare config | `wrangler.jsonc` | +| Environment vars (local) | `.dev.vars` | +| Dependencies | `package.json` | + +--- + +## 🔧 Useful Commands + +```bash +# Development +pnpm run dev # Next.js with HMR +pnpm run wrangler:dev # Wrangler (D1 access) +pnpm run dev:cf # Combined (no HMR) + +# Database +pnpm run db:generate # Generate migration +pnpm run db:generate:named "name" # Named migration +pnpm run db:migrate:local # Apply to local +pnpm run db:migrate:prod # Apply to production +pnpm run db:studio:local # Open Drizzle Studio +pnpm run db:inspect:local # Inspect schema + +# Deployment +pnpm run build:cf # Build for Cloudflare +pnpm run deploy # Deploy to production +pnpm run cf-typegen # Generate CF types + +# Cloudflare +pnpm run cf:secret # Add secret +wrangler r2 bucket create name # Create R2 bucket +wrangler d1 create name # Create D1 database +``` + +--- + +## 🎓 Learning Resources + +**Official Docs:** +- [Next.js App Router](https://nextjs.org/docs/app) +- [Cloudflare Workers](https://developers.cloudflare.com/workers/) +- [Drizzle ORM](https://orm.drizzle.team/) +- [shadcn/ui](https://ui.shadcn.com/) +- [React Hook Form](https://react-hook-form.com/) +- [Zod](https://zod.dev/) + +**Component Inspiration:** +- [shadcn/ui Pro Blocks](https://ui.shadcn.com/blocks) +- [Tremor](https://tremor.so/) +- [Magic UI](https://magicui.design/) +- [Radix UI](https://www.radix-ui.com/) + +--- + +## 💡 Pro Tips + +1. **Build patterns as you need them** - Don't build speculatively +2. **Start in modules, extract after 3rd use** - Prove the pattern first +3. **Server Components by default** - Add 'use client' only when needed +4. **Consistent response shapes** - `{ success, data, error }` +5. **Validate everything** - Use Zod for all inputs +6. **Revalidate after mutations** - Keep UI in sync +7. **Loading/error/empty states** - Always handle these three +8. **TypeScript everywhere** - No `any` types +9. **Document as you go** - Future you will thank you +10. **Test in development** - Don't wait for production + +--- + +## 🆘 When Stuck + +**Ask yourself:** +1. Does this have business logic? → Feature module +2. Is this used 3+ times? → Pattern +3. Is shadcn/ui enough? → Use it +4. Is this truly one-off? → Shared or inline + +**Still unsure?** +- Check: [Component Decision Framework](./component-decision-framework.md) +- Review: [Architecture Overview](./architecture-overview.md) +- Reference: [Module Development Guide](./module-development-guide.md) +- Look at: Existing modules for examples + +--- + +## 📋 Checklist: New Feature + +- [ ] Database schema created +- [ ] Migration generated and applied +- [ ] Types/models defined +- [ ] Zod schemas created +- [ ] Server Actions implemented (CRUD) +- [ ] Components created +- [ ] Page route added +- [ ] Loading states added +- [ ] Empty states added +- [ ] Error handling complete +- [ ] Tested in development +- [ ] Patterns extracted (if reusable) + +--- + +## 🎯 Remember + +**Three Layers:** +1. **UI Primitives** - Building blocks (shadcn) +2. **Composed Patterns** - Reusable patterns (your library) +3. **Feature Modules** - Business logic (your app) + +**Golden Rule:** +> If you've written it 3 times, extract it to a pattern. + +**Architecture Goal:** +> Clear boundaries, maximum reusability, minimum duplication. diff --git a/docs/development-planning/component-decision-framework.md b/docs/development-planning/component-decision-framework.md new file mode 100644 index 0000000..1bf42d0 --- /dev/null +++ b/docs/development-planning/component-decision-framework.md @@ -0,0 +1,389 @@ +# Component Decision Framework + +Quick reference guide for deciding where components should live in the architecture. + +--- + +## The Decision Tree + +``` +Start here: I need to create a component + ↓ + Does shadcn/ui already provide this? + ├─ YES → Use /components/ui/[component].tsx + └─ NO → Continue + ↓ + Does it have business logic or database access? + ├─ YES → /modules/[feature]/components/ + └─ NO → Continue + ↓ + Will it be used in 3+ different features? + ├─ YES → /components/composed/[category]/ + ├─ MAYBE → Start in module, extract later + └─ NO → Continue + ↓ + Is it truly one-off? + ├─ YES → /components/shared/ or inline + └─ NO → Reconsider if it's actually a pattern +``` + +--- + +## Quick Reference Table + +| Component Type | Location | Has Business Logic? | Reused Across Features? | Examples | +|---------------|----------|---------------------|------------------------|----------| +| UI Primitive | `/components/ui/` | ❌ No | ✅ Yes | Button, Input, Dialog | +| Composed Pattern | `/components/composed/` | ❌ No | ✅ Yes (3+) | DataTable, PageHeader, FileUpload | +| Feature Component | `/modules/[feature]/components/` | ✅ Yes | ❌ No | TodoList, UserProfile, ProductCard | +| Shared Component | `/components/shared/` | ❌ No | ❌ No | Logo, specific landing sections | + +--- + +## Detailed Decision Criteria + +### ✅ Put in `/components/ui/` when: + +**Characteristics:** +- Single, unopinionated building block +- Part of shadcn/ui library +- No business logic +- Maximum flexibility needed +- Used everywhere + +**Examples:** +- ✅ Button +- ✅ Input +- ✅ Dialog +- ✅ Card +- ✅ Select + +**Anti-examples (DON'T put here):** +- ❌ LoginForm (has auth logic) +- ❌ UserCard (feature-specific) +- ❌ DataTable (opinionated pattern) + +--- + +### ✅ Put in `/components/composed/` when: + +**Characteristics:** +- Combines 2+ UI primitives +- Solves a common UI pattern +- NO business logic or database access +- Reusable across 3+ features +- Configured via props +- Emits events via callbacks + +**Questions to ask:** +1. Does this solve a pattern I see repeatedly? +2. Can I use it in different features without modification? +3. Is it independent of business logic? +4. Would another developer expect to find this as a reusable pattern? + +**Examples:** +- ✅ DataTable with sorting/filtering +- ✅ PageHeader with breadcrumbs +- ✅ FileUpload with drag-drop +- ✅ EmptyState with icon and CTA +- ✅ MultiStepForm wizard +- ✅ ConfirmDialog with form + +**Anti-examples (DON'T put here):** +- ❌ TodoList (fetches todos from database) +- ❌ UserProfile (specific to user domain) +- ❌ ProductCheckout (has payment logic) +- ❌ AnalyticsDashboard (specific dashboard) + +**The "3+ Rule":** +If you're not sure, start in the feature module. After you use it in 3 different features, extract it to composed patterns. + +--- + +### ✅ Put in `/modules/[feature]/components/` when: + +**Characteristics:** +- Domain-specific component +- Uses Server Actions +- Accesses database or external APIs +- Contains business logic +- Validates with feature-specific schemas +- Tied to a specific domain model + +**Questions to ask:** +1. Does this component fetch or mutate data? +2. Is it specific to one business domain? +3. Does it use Server Actions from this module? +4. Would it make sense outside this feature? + +**Examples:** +- ✅ TodoList (fetches/displays todos) +- ✅ UserProfile (user-specific data) +- ✅ ProductCard (product-specific) +- ✅ OrderSummary (order-specific) +- ✅ CommentThread (comment-specific) + +**Structure within module:** +```typescript +/modules/todos/ +├── components/ +│ ├── TodoList.tsx // Main list component +│ ├── TodoItem.tsx // Individual item +│ ├── TodoForm.tsx // Create/edit form +│ └── TodoFilters.tsx // Filter controls +``` + +--- + +### ✅ Put in `/components/shared/` when: + +**Characteristics:** +- Used once or twice +- Doesn't fit a broader pattern +- Simple, one-off need +- Not worth extracting to a pattern yet + +**Examples:** +- ✅ Logo +- ✅ Specific landing page hero +- ✅ One-off marketing section +- ✅ Custom 404 page component + +**Warning:** Don't let this become a dumping ground. If you find yourself with many "shared" components, they're probably patterns waiting to be extracted. + +--- + +## Common Scenarios + +### Scenario 1: "I need a table to display users" + +**Analysis:** +- Displays data? → Feature component initially +- Will other features need tables? → Yes, extract pattern + +**Decision:** +1. Start: `/modules/users/components/UserTable.tsx` +2. After using in 3 features: Extract to `/components/composed/data-display/DataTable.tsx` +3. Keep: `/modules/users/components/UserTable.tsx` as a wrapper with user-specific logic + +**Result:** +```typescript +// Feature-specific wrapper +// /modules/users/components/UserTable.tsx +export function UserTable() { + const users = await getUsers(); // Server Action + + return ( + + ); +} + +// Generic pattern +// /components/composed/data-display/DataTable.tsx +export function DataTable({ data, columns, onRowClick }) { + // Generic table logic with sorting, filtering, pagination +} +``` + +--- + +### Scenario 2: "I need a form to create todos" + +**Analysis:** +- Has business logic (validation, submission)? → Yes +- Specific to todos? → Yes + +**Decision:** +Put in `/modules/todos/components/TodoForm.tsx` + +**Note:** If you later need a "ProductForm" and "UserForm" with similar patterns, extract the form PATTERN to `/components/composed/forms/FormContainer.tsx` + +--- + +### Scenario 3: "I need to show 'no results' message" + +**Analysis:** +- Will this be used everywhere? → Yes +- Does it have business logic? → No +- Is it a reusable pattern? → Yes + +**Decision:** +Put in `/components/composed/feedback/EmptyState.tsx` + +**Implementation:** +```typescript +// /components/composed/feedback/EmptyState.tsx +export function EmptyState({ + icon: Icon, + title, + description, + action, + actionLabel +}) { + return ( +
+ +

{title}

+

{description}

+ {action && } +
+ ); +} + +// Usage in feature +// /modules/todos/components/TodoList.tsx +{todos.length === 0 && ( + +)} +``` + +--- + +### Scenario 4: "I need a page layout with sidebar and header" + +**Analysis:** +- Used across multiple pages? → Yes +- No business logic? → Correct +- Clear reusable pattern? → Yes + +**Decision:** +Put in `/components/composed/layouts/DashboardLayout.tsx` + +--- + +### Scenario 5: "I need to upload files for R2" + +**Analysis:** +- Will multiple features need uploads? → Yes +- Has R2-specific logic? → Business logic in Server Action, UI in pattern + +**Decision:** +1. Pattern: `/components/composed/media/FileUpload.tsx` (UI only) +2. Logic: `/modules/[feature]/actions/upload.ts` (R2 Server Action) + +**Split:** +```typescript +// Pattern - handles UI only +// /components/composed/media/FileUpload.tsx +export function FileUpload({ onUpload, accept, maxSize }) { + // Drag-drop UI, validation, progress + // Calls the provided onUpload callback +} + +// Feature - handles business logic +// /modules/documents/actions/upload.ts +export async function uploadDocument(file: File) { + // R2 upload logic + // Database record creation + // Permission checks +} + +// Usage in feature +// /modules/documents/components/DocumentUploader.tsx +export function DocumentUploader() { + return ( + + ); +} +``` + +--- + +## Red Flags + +### 🚩 You're probably doing it wrong if: + +1. **Composed pattern has Server Actions** + - Move Server Actions to feature module + - Pass callbacks via props + +2. **Composed pattern imports from `/modules/`** + - Reverse the dependency + - Modules should import patterns, not vice versa + +3. **UI primitive has opinions about layout** + - Extract to composed pattern + - Keep primitives flexible + +4. **Feature component has no feature-specific logic** + - Move to composed patterns + - Make it generic + +5. **Everything goes in `/components/shared/`** + - Identify patterns + - Extract to composed + - Keep shared minimal + +6. **Creating patterns speculatively** + - Wait until you use it 3 times + - Don't abstract prematurely + - Prove the need first + +--- + +## Testing Your Decision + +Ask yourself: + +1. **Can I describe this component in one sentence?** + - If not, it might need to be split + +2. **Does this component do ONE thing?** + - Single Responsibility Principle applies + +3. **Could another project use this exact component?** + - If yes → Composed pattern + - If no → Feature component + +4. **Does it need to know about my database schema?** + - If yes → Feature component + - If no → Composed pattern or UI primitive + +5. **Am I repeating this pattern?** + - If yes (3+) → Extract to composed + - If no → Keep in feature module + +--- + +## Migration Path + +**When you realize a component is in the wrong place:** + +1. **Don't panic** - this is normal during development +2. **Document why you're moving it** - update this guide if needed +3. **Check dependencies** - what imports this component? +4. **Update imports** - use your IDE's refactor tools +5. **Test thoroughly** - especially if moving from module to composed + +**Common migrations:** +- Feature component → Composed pattern (after 3rd use) +- Shared component → Composed pattern (when pattern emerges) +- Inline component → Shared component → Composed pattern (natural evolution) + +--- + +## Summary Checklist + +Before creating a component, ask: + +- [ ] Does shadcn/ui provide this? → Use it +- [ ] Does it have business logic? → Feature module +- [ ] Will it be used 3+ times? → Composed pattern (or extract later) +- [ ] Is it truly one-off? → Shared or inline +- [ ] Can I clearly categorize it? → You're ready to build + +**When in doubt:** Start in the feature module, extract to composed patterns after the 3rd use. diff --git a/docs/development-planning/module-development-guide.md b/docs/development-planning/module-development-guide.md new file mode 100644 index 0000000..ebe6e42 --- /dev/null +++ b/docs/development-planning/module-development-guide.md @@ -0,0 +1,843 @@ +# Module Development Guide + +Complete guide for building feature modules in the Next.js + Cloudflare architecture. + +--- + +## What is a Feature Module? + +A feature module is a self-contained domain feature that includes: +- **Business logic** (Server Actions) +- **UI components** (feature-specific) +- **Data models** (TypeScript types) +- **Validation schemas** (Zod) +- **Database interactions** (via Drizzle) +- **Custom hooks** (feature-specific) + +--- + +## Module Structure + +``` +/modules/[feature-name]/ +├── actions/ # Server Actions (mutations & queries) +│ ├── create-[entity].ts +│ ├── update-[entity].ts +│ ├── delete-[entity].ts +│ └── get-[entities].ts +│ +├── components/ # Feature-specific UI components +│ ├── [Entity]List.tsx +│ ├── [Entity]Form.tsx +│ ├── [Entity]Card.tsx +│ └── [Entity]Details.tsx +│ +├── hooks/ # Feature-specific React hooks +│ ├── use-[entity].ts +│ └── use-[entity]-form.ts +│ +├── models/ # TypeScript type definitions +│ └── [entity].ts +│ +├── schemas/ # Zod validation schemas +│ └── [entity].schema.ts +│ +└── utils/ # Feature-specific utilities + └── [entity]-helpers.ts +``` + +--- + +## Step-by-Step Module Creation + +### Step 1: Define the Database Schema + +**File:** `/db/schema.ts` (or feature-specific schema file) + +```typescript +import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; +import { createId } from '@paralleldrive/cuid2'; + +export const products = sqliteTable('products', { + id: text('id') + .primaryKey() + .$defaultFn(() => createId()), + name: text('name').notNull(), + description: text('description'), + price: integer('price').notNull(), // Store as cents + status: text('status', { enum: ['draft', 'active', 'archived'] }) + .notNull() + .default('draft'), + userId: text('user_id').notNull(), + imageUrl: text('image_url'), + createdAt: integer('created_at', { mode: 'timestamp' }) + .notNull() + .$defaultFn(() => new Date()), + updatedAt: integer('updated_at', { mode: 'timestamp' }) + .notNull() + .$defaultFn(() => new Date()), +}); + +export type Product = typeof products.$inferSelect; +export type NewProduct = typeof products.$inferInsert; +``` + +**Then generate and apply migration:** +```bash +pnpm run db:generate:named "add_products_table" +pnpm run db:migrate:local +``` + +--- + +### Step 2: Create Type Models + +**File:** `/modules/products/models/product.ts` + +```typescript +import { Product, NewProduct } from '@/db/schema'; + +// Re-export database types +export type { Product, NewProduct }; + +// Add any additional types for the feature +export interface ProductWithUser extends Product { + user: { + id: string; + name: string; + email: string; + }; +} + +export interface ProductListItem { + id: string; + name: string; + price: number; + status: Product['status']; + imageUrl: string | null; + createdAt: Date; +} + +export type ProductStatus = Product['status']; + +export const PRODUCT_STATUSES: { value: ProductStatus; label: string }[] = [ + { value: 'draft', label: 'Draft' }, + { value: 'active', label: 'Active' }, + { value: 'archived', label: 'Archived' }, +]; +``` + +--- + +### Step 3: Create Validation Schemas + +**File:** `/modules/products/schemas/product.schema.ts` + +```typescript +import { z } from 'zod'; + +export const createProductSchema = z.object({ + name: z.string().min(1, 'Name is required').max(100), + description: z.string().max(500).optional(), + price: z.number().min(0, 'Price must be positive'), + status: z.enum(['draft', 'active', 'archived']).default('draft'), + imageUrl: z.string().url().optional().nullable(), +}); + +export const updateProductSchema = createProductSchema.partial().extend({ + id: z.string().cuid2(), +}); + +export const deleteProductSchema = z.object({ + id: z.string().cuid2(), +}); + +export const getProductSchema = z.object({ + id: z.string().cuid2(), +}); + +export const listProductsSchema = z.object({ + status: z.enum(['draft', 'active', 'archived']).optional(), + search: z.string().optional(), + limit: z.number().min(1).max(100).default(20), + offset: z.number().min(0).default(0), +}); + +// Infer types from schemas +export type CreateProductInput = z.infer; +export type UpdateProductInput = z.infer; +export type DeleteProductInput = z.infer; +export type GetProductInput = z.infer; +export type ListProductsInput = z.infer; +``` + +--- + +### Step 4: Create Server Actions + +**File:** `/modules/products/actions/create-product.ts` + +```typescript +'use server'; + +import { eq } from 'drizzle-orm'; +import { revalidatePath } from 'next/cache'; +import { db } from '@/db'; +import { products } from '@/db/schema'; +import { auth } from '@/lib/auth'; +import { + createProductSchema, + type CreateProductInput +} from '../schemas/product.schema'; + +export async function createProduct(input: CreateProductInput) { + try { + // 1. Authenticate user + const session = await auth(); + if (!session?.user) { + return { + success: false, + error: 'Unauthorized', + }; + } + + // 2. Validate input + const validated = createProductSchema.parse(input); + + // 3. Perform database operation + const [product] = await db + .insert(products) + .values({ + ...validated, + userId: session.user.id, + }) + .returning(); + + // 4. Revalidate relevant paths + revalidatePath('/dashboard/products'); + + // 5. Return success response + return { + success: true, + data: product, + }; + } catch (error) { + console.error('Failed to create product:', error); + + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to create product', + }; + } +} +``` + +**File:** `/modules/products/actions/get-products.ts` + +```typescript +'use server'; + +import { eq, like, and, desc } from 'drizzle-orm'; +import { db } from '@/db'; +import { products } from '@/db/schema'; +import { auth } from '@/lib/auth'; +import { + listProductsSchema, + type ListProductsInput +} from '../schemas/product.schema'; + +export async function getProducts(input: ListProductsInput = {}) { + try { + // 1. Authenticate user + const session = await auth(); + if (!session?.user) { + return { + success: false, + error: 'Unauthorized', + data: null, + }; + } + + // 2. Validate input + const validated = listProductsSchema.parse(input); + + // 3. Build query conditions + const conditions = [eq(products.userId, session.user.id)]; + + if (validated.status) { + conditions.push(eq(products.status, validated.status)); + } + + if (validated.search) { + conditions.push(like(products.name, `%${validated.search}%`)); + } + + // 4. Fetch data + const data = await db + .select() + .from(products) + .where(and(...conditions)) + .orderBy(desc(products.createdAt)) + .limit(validated.limit) + .offset(validated.offset); + + // 5. Return success response + return { + success: true, + data, + error: null, + }; + } catch (error) { + console.error('Failed to fetch products:', error); + + return { + success: false, + data: null, + error: error instanceof Error ? error.message : 'Failed to fetch products', + }; + } +} +``` + +**File:** `/modules/products/actions/index.ts` + +```typescript +// Barrel export for all actions +export { createProduct } from './create-product'; +export { getProducts } from './get-products'; +export { getProduct } from './get-product'; +export { updateProduct } from './update-product'; +export { deleteProduct } from './delete-product'; +``` + +--- + +### Step 5: Create Custom Hooks (Optional) + +**File:** `/modules/products/hooks/use-products.ts` + +```typescript +import { useOptimistic } from 'react'; +import { Product } from '../models/product'; + +export function useProducts(initialProducts: Product[]) { + const [optimisticProducts, setOptimisticProducts] = useOptimistic( + initialProducts, + (state, newProduct: Product) => [...state, newProduct] + ); + + return { + products: optimisticProducts, + addOptimisticProduct: setOptimisticProducts, + }; +} +``` + +**File:** `/modules/products/hooks/use-product-form.ts` + +```typescript +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { toast } from 'sonner'; +import { createProduct } from '../actions'; +import { createProductSchema, type CreateProductInput } from '../schemas/product.schema'; + +export function useProductForm() { + const [isSubmitting, setIsSubmitting] = useState(false); + + const form = useForm({ + resolver: zodResolver(createProductSchema), + defaultValues: { + name: '', + description: '', + price: 0, + status: 'draft', + imageUrl: null, + }, + }); + + const onSubmit = async (data: CreateProductInput) => { + setIsSubmitting(true); + + try { + const result = await createProduct(data); + + if (result.success) { + toast.success('Product created successfully'); + form.reset(); + return result.data; + } else { + toast.error(result.error); + } + } catch (error) { + toast.error('An unexpected error occurred'); + } finally { + setIsSubmitting(false); + } + }; + + return { + form, + isSubmitting, + onSubmit: form.handleSubmit(onSubmit), + }; +} +``` + +--- + +### Step 6: Create UI Components + +**File:** `/modules/products/components/ProductList.tsx` + +```typescript +import { Suspense } from 'react'; +import { getProducts } from '../actions'; +import { DataTable } from '@/components/composed/data-display/DataTable'; +import { productColumns } from './product-columns'; +import { ProductListSkeleton } from './ProductListSkeleton'; + +export async function ProductList() { + const result = await getProducts(); + + if (!result.success) { + return
Error: {result.error}
; + } + + return ( + + ); +} + +export function ProductListWithSuspense() { + return ( + }> + + + ); +} +``` + +**File:** `/modules/products/components/ProductForm.tsx` + +```typescript +'use client'; + +import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Button } from '@/components/ui/button'; +import { useProductForm } from '../hooks/use-product-form'; +import { PRODUCT_STATUSES } from '../models/product'; + +interface ProductFormProps { + onSuccess?: () => void; +} + +export function ProductForm({ onSuccess }: ProductFormProps) { + const { form, isSubmitting, onSubmit } = useProductForm(); + + const handleSubmit = async (data: any) => { + const result = await onSubmit(data); + if (result && onSuccess) { + onSuccess(); + } + }; + + return ( +
+ + ( + + Product Name + + + + + + )} + /> + + ( + + Description + +