# Phase 2 — Core CRUD

Build the 4 primary domains: Residences, Tasks, Contractors, and Documents.

## Checklist

- [ ] Residences: list (with summary card), detail, create/edit form, delete
- [ ] Tasks: kanban board (drag-and-drop columns), create/edit form, task actions (complete, cancel, archive, in-progress)
- [ ] Task completion: form with file upload (photos), completion history with image viewer
- [ ] Task templates: autocomplete search + template browser
- [ ] Contractors: list (search, filter by favorite/specialty), detail (quick actions), create/edit form, delete
- [ ] Documents: tabbed view (warranties/documents), detail, create/edit form (type-specific fields), file upload/download, image gallery

---

## 1. Residences

### Screens

| Route | Screen | Description |
|-------|--------|-------------|
| `/app/residences` | Residence List | Grid of residence cards with summary data |
| `/app/residences/new` | Create Residence | Form: name, address, type, photo |
| `/app/residences/[id]` | Residence Detail | Summary dashboard, task/contractor/document counts, users |
| `/app/residences/[id]/edit` | Edit Residence | Same form as create, pre-filled |

### API Endpoints Used

| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/residences/my/` | User's residences with summaries |
| GET | `/api/residences/:id/` | Residence detail |
| POST | `/api/residences/` | Create residence |
| PUT | `/api/residences/:id/` | Update residence |
| DELETE | `/api/residences/:id/` | Delete residence |

### Components

- **ResidenceCard**: Summary card showing name, address, type icon, task counts (overdue, due soon, total)
- **ResidenceForm**: React Hook Form + Zod. Fields: name (required), address, residence type (dropdown from lookups), photo upload
- **ResidenceSummary**: Dashboard showing task breakdown, contractor count, document count

### TanStack Query Hooks

```typescript
useResidences()                    // GET /residences/my/
useResidence(id)                   // GET /residences/:id/
useCreateResidence()               // POST /residences/
useUpdateResidence(id)             // PUT /residences/:id/
useDeleteResidence(id)             // DELETE /residences/:id/
```

### Cache Invalidation

After create/update/delete → invalidate `['residences']` and `['residence', id]` queries.

---

## 2. Tasks

### Screens

| Route | Screen | Description |
|-------|--------|-------------|
| `/app/tasks` | Kanban Board | All tasks across residences, grouped by column |
| `/app/residences/[id]/tasks` | Residence Kanban | Tasks for a specific residence |
| `/app/tasks/new` | Create Task | Full task form |
| `/app/tasks/[id]` | Task Detail | Full task info, completion history, actions |
| `/app/tasks/[id]/edit` | Edit Task | Same form as create, pre-filled |
| `/app/tasks/[id]/complete` | Complete Task | Completion form with photo upload |

### Kanban Board

Columns (from backend `TaskColumnsResponse`):
1. **Overdue** (red indicator)
2. **Due Today** (orange indicator)
3. **Due Soon** (yellow indicator, within 30 days)
4. **Upcoming** (blue indicator)
5. **In Progress** (green indicator)
6. **Completed** (gray indicator)

**Drag-and-drop**: Use `@dnd-kit/core` + `@dnd-kit/sortable` for dragging tasks between columns. Dropping a task triggers the appropriate API action:
- Drop on "In Progress" → `POST /tasks/:id/in-progress/`
- Drop on "Completed" → Opens completion form
- Other moves may just reorder (no API call)

**Board controls**:
- Filter by residence (dropdown)
- Filter by category, priority
- Search by title

### Task Form Fields

| Field | Type | Required | Source |
|-------|------|----------|--------|
| Title | Text / Autocomplete | Yes | Free text or template |
| Residence | Select | Yes | User's residences |
| Category | Select | No | Lookups (categories) |
| Priority | Select | No | Lookups (priorities) |
| Due Date | Date picker | No | Calendar |
| Frequency | Select | No | Lookups (frequencies) — for recurring tasks |
| Estimated Cost | Number | No | Currency input |
| Notes | Textarea | No | Free text |
| Assigned Contractor | Select | No | User's contractors |

### Task Actions

| Action | Endpoint | UI Trigger |
|--------|----------|------------|
| Complete | `POST /tasks/:id/complete/` | Button + completion form |
| Mark In Progress | `POST /tasks/:id/in-progress/` | Button or drag |
| Cancel | `POST /tasks/:id/cancel/` | Menu action |
| Archive | `POST /tasks/:id/archive/` | Menu action |
| Uncancel | `POST /tasks/:id/uncancel/` | Menu action |
| Unarchive | `POST /tasks/:id/unarchive/` | Menu action |

### Task Completion Form

| Field | Type | Required |
|-------|------|----------|
| Completed At | DateTime | Yes (default: now) |
| Actual Cost | Number | No |
| Notes | Textarea | No |
| Rating | Star rating (1-5) | No |
| Photos | File upload (multiple) | No |

File upload uses `FormData` with multipart, same as mobile's `submitFormWithBinaryData`.

### Task Templates

- **Autocomplete search**: As user types in title field, search `/api/task-templates/search/?q=...`
- **Template browser**: Modal with categorized templates, click to prefill form
- Template data populates: title, category, priority, estimated cost, frequency

### API Endpoints Used

| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/tasks/` | All user's tasks (kanban columns) |
| GET | `/api/tasks/by-residence/:id/` | Tasks for one residence |
| POST | `/api/tasks/` | Create task |
| PUT | `/api/tasks/:id/` | Update task |
| DELETE | `/api/tasks/:id/` | Delete task |
| POST | `/api/tasks/:id/complete/` | Complete task |
| POST | `/api/tasks/:id/in-progress/` | Mark in progress |
| POST | `/api/tasks/:id/cancel/` | Cancel task |
| POST | `/api/tasks/:id/archive/` | Archive task |
| POST | `/api/task-completions/` | Create completion (with images) |
| GET | `/api/task-completions/` | List completions |
| GET | `/api/task-templates/search/` | Search templates |

### TanStack Query Hooks

```typescript
useTasks()                         // GET /tasks/
useTasksByResidence(residenceId)   // GET /tasks/by-residence/:id/
useTask(id)                        // GET /tasks/:id/
useCreateTask()                    // POST /tasks/
useUpdateTask(id)                  // PUT /tasks/:id/
useDeleteTask(id)                  // DELETE /tasks/:id/
useCompleteTask(id)                // POST /tasks/:id/complete/
useTaskTemplateSearch(query)       // GET /task-templates/search/?q=...
useCreateCompletion()              // POST /task-completions/
```

---

## 3. Contractors

### Screens

| Route | Screen | Description |
|-------|--------|-------------|
| `/app/contractors` | Contractor List | Filterable list with search |
| `/app/contractors/new` | Create Contractor | Form with contact details |
| `/app/contractors/[id]` | Contractor Detail | Full info, quick actions, linked tasks |
| `/app/contractors/[id]/edit` | Edit Contractor | Same form as create, pre-filled |

### List Features

- **Search**: Filter by name, company
- **Filter**: By specialty (from lookups), by favorite
- **Sort**: By name, recently added
- **Quick actions**: Call, email, favorite toggle

### Contractor Form Fields

| Field | Type | Required | Source |
|-------|------|----------|--------|
| Name | Text | Yes | Free text |
| Company | Text | No | Free text |
| Phone | Phone | No | Tel input |
| Email | Email | No | Email input |
| Specialty | Select | No | Lookups (specialties) |
| Notes | Textarea | No | Free text |
| Is Favorite | Toggle | No | Boolean |
| Residence | Select | Yes | User's residences |

### Contractor Detail

- Contact info with click-to-call (`tel:`) and click-to-email (`mailto:`)
- Favorite toggle
- Linked tasks (tasks assigned to this contractor)
- Edit / delete actions

### API Endpoints Used

| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/contractors/` | User's contractors |
| GET | `/api/contractors/:id/` | Contractor detail |
| POST | `/api/contractors/` | Create contractor |
| PUT | `/api/contractors/:id/` | Update contractor |
| DELETE | `/api/contractors/:id/` | Delete contractor |
| POST | `/api/contractors/:id/favorite/` | Toggle favorite |

### TanStack Query Hooks

```typescript
useContractors()                   // GET /contractors/
useContractor(id)                  // GET /contractors/:id/
useCreateContractor()              // POST /contractors/
useUpdateContractor(id)            // PUT /contractors/:id/
useDeleteContractor(id)            // DELETE /contractors/:id/
useToggleFavorite(id)              // POST /contractors/:id/favorite/
```

---

## 4. Documents

### Screens

| Route | Screen | Description |
|-------|--------|-------------|
| `/app/documents` | Document List | Tabbed view: Warranties / Documents |
| `/app/documents/new` | Create Document | Form with type-specific fields |
| `/app/documents/[id]` | Document Detail | Full info, file viewer, image gallery |
| `/app/documents/[id]/edit` | Edit Document | Same form as create, pre-filled |

### Tabbed View

- **Warranties tab**: Documents with `is_warranty = true`. Shows expiry dates, status (active/expired/expiring soon)
- **Documents tab**: All other documents. Shows type, date added, file preview

### Document Form Fields

| Field | Type | Required | Source |
|-------|------|----------|--------|
| Title | Text | Yes | Free text |
| Residence | Select | Yes | User's residences |
| Type | Select | No | Document type options |
| Notes | Textarea | No | Free text |
| File | File upload | No | File picker |
| Is Warranty | Toggle | No | Boolean |
| Purchase Date | Date | Conditional | If warranty |
| Expiry Date | Date | Conditional | If warranty |
| Purchase Price | Number | Conditional | If warranty |

### Document Detail

- File preview (PDF viewer, image gallery)
- Download button
- Warranty status indicator (if warranty)
- Edit / delete actions

### API Endpoints Used

| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/documents/` | User's documents |
| GET | `/api/documents/:id/` | Document detail |
| POST | `/api/documents/` | Create document |
| PUT | `/api/documents/:id/` | Update document |
| DELETE | `/api/documents/:id/` | Delete document |
| POST | `/api/documents/:id/activate/` | Activate document |
| POST | `/api/documents/:id/deactivate/` | Deactivate document |

### TanStack Query Hooks

```typescript
useDocuments()                     // GET /documents/
useDocument(id)                    // GET /documents/:id/
useCreateDocument()                // POST /documents/
useUpdateDocument(id)              // PUT /documents/:id/
useDeleteDocument(id)              // DELETE /documents/:id/
```

---

## Shared Patterns

### Form Pattern

Every CRUD form follows the same structure:

```typescript
// src/components/forms/TaskForm.tsx
const taskSchema = z.object({
  title: z.string().min(1, 'Title is required'),
  residenceId: z.number().min(1, 'Residence is required'),
  categoryId: z.number().optional(),
  // ...
});

type TaskFormData = z.infer<typeof taskSchema>;

export function TaskForm({ task, onSubmit }: Props) {
  const form = useForm<TaskFormData>({
    resolver: zodResolver(taskSchema),
    defaultValues: task ? mapTaskToFormData(task) : defaults,
  });

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)}>
        {/* Fields */}
      </form>
    </Form>
  );
}
```

### Mutation Pattern

```typescript
export function useCreateTask() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: CreateTaskRequest) => api.tasks.createTask(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['tasks'] });
      queryClient.invalidateQueries({ queryKey: ['residences'] }); // Summary counts change
    },
  });
}
```

### Loading / Error / Empty States

Every list and detail page handles three states:
1. **Loading**: Skeleton loader (shadcn Skeleton component)
2. **Error**: Error banner with retry button
3. **Empty**: Empty state illustration with CTA to create first item

## Deliverables

At the end of Phase 2, you should have:
1. Full CRUD for all 4 domains
2. Kanban board with drag-and-drop
3. Task completion with photo upload
4. Template search and autocomplete
5. All forms validated with Zod
6. Proper cache invalidation after mutations
