Private
Public Access
1
0
Files
Kanban/PLANS.md
T
Catherine Renelle da0cd816aa Add implementation plans for phases 2-9
Detailed task breakdowns, file paths, acceptance criteria,
and recommended execution order for all remaining phases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:34:31 -05:00

513 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Pounce — Implementation Plans (Phases 29)
> Reference doc for implementing remaining features. Each phase is self-contained
> with file paths, code strategies, and acceptance criteria.
---
## Phase 2: Row-Level Security & Multi-Tenancy Hardening
### Goal
Enforce tenant isolation at the database layer so even application bugs can't leak data across tenants.
### Tasks
1. **Apply RLS policies to the live database**
- File: `scripts/setup-rls.sql` (already written)
- Run via `docker exec` on the db container, or add to `docker-entrypoint.sh` after `prisma db push`
- Every tenant-scoped table gets `ENABLE ROW LEVEL SECURITY` + a policy checking `current_setting('app.current_tenant_id')`
2. **Wire up the Prisma RLS client extension**
- File: `src/lib/server/rls.ts` (already written but unused)
- `forTenant(tenantId)` wraps queries in a transaction that does `SET LOCAL app.current_tenant_id = '<id>'`
- `bypassRLS()` for site admin cross-tenant operations
- Update all API routes and server load functions to use `forTenant(locals.tenant.id)` instead of raw `prisma`
3. **Create a shared helper for tenant-scoped DB access**
- File: `src/lib/server/db.ts` — add `export function tenantDb(locals: App.Locals)` that returns `forTenant(locals.tenant!.id)`
- All routes call `tenantDb(locals)` instead of importing `prisma` directly
4. **Test multi-tenant isolation**
- Access via two different hostnames (two tenants auto-created)
- Create boards on each — verify they don't appear on the other
- Verify API endpoints can't fetch cross-tenant data by ID
### Acceptance Criteria
- All API routes use tenant-scoped Prisma client
- Direct SQL query with wrong tenant_id returns 0 rows
- Two separate hostnames show fully isolated data
---
## Phase 3: Roles & Permissions Enforcement
### Goal
Enforce GlobalRole (SITE_ADMIN, USER), TenantRole (ADMIN, MEMBER), and BoardMemberRole (OWNER, EDITOR, VIEWER) throughout the app.
### Tasks
1. **Create route guards**
- File: `src/lib/server/guards.ts`
- `requireAuth(locals)` — throws 401 if not logged in
- `requireTenantAdmin(locals)` — throws 403 if not tenant ADMIN
- `requireSiteAdmin(locals)` — throws 403 if not SITE_ADMIN
- `requireBoardAccess(locals, boardId, minRole?)` — checks membership
2. **Apply guards to all API routes**
- Board CRUD: owners can delete, editors can modify, viewers read-only
- Column/Card CRUD: inherit from board membership
- Already partially done in routes — audit each for consistency
3. **Public board anonymous access**
- `GET /api/boards/[boardId]` — if `visibility === 'PUBLIC'`, allow without auth
- Board view page server load — same check (already done, verify)
- Board listing — anonymous users see only PUBLIC boards (already done, verify)
4. **Board member management UI**
- File: `src/routes/boards/[boardId]/members/+page.svelte`
- API: `POST/DELETE /api/boards/[boardId]/members`
- Add members by email (must be registered on same tenant)
- Change member role (OWNER/EDITOR/VIEWER)
- Remove members
- Only OWNER can manage members
5. **Tenant admin panel (basic)**
- File: `src/routes/admin/+page.svelte` (guarded by tenantRole === 'ADMIN')
- List all users in tenant, change their tenantRole
- List all boards in tenant
### Acceptance Criteria
- VIEWER cannot create/edit/delete cards or columns
- Non-members cannot access PRIVATE boards
- Anonymous can view PUBLIC boards but not edit
- Board owner can add/remove/change member roles
- Tenant admin can see all boards and manage users
---
## Phase 4: Rich Card Features
### Goal
Cards get labels, categories, markdown descriptions, due dates, assignees, checklists, comments, and file attachments.
### Tasks
1. **Tags/Labels system**
- API: `CRUD /api/tags` (tenant-scoped)
- API: `POST/DELETE /api/boards/[boardId]/columns/[columnId]/cards/[cardId]/labels`
- UI: Color picker for tag creation, label pills on cards
- Card modal: add/remove labels section
- File: `src/lib/components/TagPicker.svelte`
2. **Categories (board grouping)**
- API: `CRUD /api/categories` (tenant-scoped)
- Board listing page: group boards by category, filter dropdown
- Board create/edit: optional category selector
3. **Markdown description editor**
- Card modal: replace plain textarea with markdown editor
- Use `marked` (already installed) for rendering
- Toggle between edit and preview modes
- File: `src/lib/components/MarkdownEditor.svelte`
4. **Due dates with urgency indicators**
- Card modal: date picker input for due date
- Card thumbnail: colored badge (green = future, yellow = soon, red = overdue)
- API: PATCH card with `dueDate` field (already supported)
- Use `date-fns` (already installed) for relative time
5. **Assignees**
- Card modal: dropdown to assign board members
- API: `POST/DELETE /api/boards/[boardId]/columns/[columnId]/cards/[cardId]/assignees`
- Show avatar circles on card thumbnails (already rendering if present)
6. **Checklists**
- Card modal: add checklists with items, toggle completion
- API: `CRUD /api/boards/[boardId]/columns/[columnId]/cards/[cardId]/checklists`
- API: `CRUD .../checklists/[checklistId]/items`
- Progress bar on card thumbnail showing X/Y items done
- File: `src/lib/components/Checklist.svelte`
7. **Comments**
- Card modal: comment list + add comment form (markdown)
- API: `CRUD /api/boards/[boardId]/columns/[columnId]/cards/[cardId]/comments`
- Show author name, avatar, timestamp, edit/delete own comments
- File: `src/lib/components/CommentList.svelte`
8. **File attachments**
- Card modal: drag-drop or file picker to upload
- API: `POST /api/boards/[boardId]/columns/[columnId]/cards/[cardId]/attachments` (multipart)
- API: `GET /api/attachments/[id]` to download
- Store in `/app/uploads` Docker volume (already mounted)
- Show file list with name, size, download link
- File: `src/lib/components/Attachments.svelte`
### Acceptance Criteria
- All features visible in the card modal
- Labels appear as colored pills on board view cards
- Due date badge with color coding
- Checklist progress bar on card thumbnails
- Comments with markdown rendering
- File upload/download working via Docker volume
---
## Phase 5: Real-Time Updates (Socket.IO)
### Goal
Multiple users viewing the same board see live updates: card moves, new cards, column changes, comments, presence.
### Tasks
1. **Socket.IO server integration**
- File: `server.js` (production) — already has basic setup
- File: `src/socket/index.ts` — auth middleware, room management (already written)
- Add Vite dev middleware for Socket.IO (simple HTTP server attach in dev)
2. **Client-side socket connection**
- File: `src/lib/stores/socket.ts` (already written)
- Connect on app load (layout), disconnect on unload
- Join board room when entering board view, leave when exiting
3. **Broadcast events from API routes**
- After every write operation (create/update/delete), emit to the board room
- Events: `card:created`, `card:updated`, `card:moved`, `card:deleted`, `column:created`, `column:updated`, `column:moved`, `column:deleted`, `comment:created`, etc.
- File: `src/lib/server/realtime.ts` — helper `broadcast(boardId, event, data)`
- Import and call from each API route's POST/PATCH/DELETE handler
- In dev, use a global event emitter; in prod, use Socket.IO directly
4. **Client-side event handlers on board view**
- File: `src/routes/boards/[boardId]/+page.svelte`
- Listen for socket events and update local `columns` state
- Handle: new cards appearing, cards moving, columns reordering, deletions
- Ignore events triggered by the current user (deduplicate via socket ID)
5. **Optimistic UI with rollback**
- Card moves: update local state immediately, send API request
- If API fails: revert to previous state, show toast error
- Other users see the final state from the socket event
6. **Presence indicators**
- Track which users are viewing a board (via socket room)
- Show avatar dots in board header: "Also viewing: Alice, Bob"
- Events: `user-joined`, `user-left` (already emitted in socket/index.ts)
### Acceptance Criteria
- Open two browser tabs on the same board
- Move a card in tab A → appears in tab B within 1 second
- Add a column in tab B → appears in tab A
- Presence shows both tabs' users
- If API fails, optimistic update rolls back
---
## Phase 6: Activity Log & Notifications
### Goal
Track all significant actions, provide activity feeds, and notify users of relevant events.
### Tasks
1. **Activity logging service**
- File: `src/lib/server/activity.ts`
- `logActivity(boardId, userId, action, metadata?, cardId?)` — writes to `activities` table
- Actions: `board.created`, `card.created`, `card.moved`, `card.updated`, `card.deleted`, `column.created`, `column.deleted`, `comment.added`, `member.added`, `member.removed`
- Call from every API route after successful writes
2. **Activity feed API**
- `GET /api/boards/[boardId]/activity` — paginated, returns recent activity for a board
- `GET /api/boards/[boardId]/columns/[columnId]/cards/[cardId]/activity` — card-specific
- Include user name, action description, timestamp, link
3. **Board activity sidebar**
- File: `src/lib/components/ActivityFeed.svelte`
- Toggleable sidebar on board view showing recent actions
- "Alice moved 'Fix login bug' from To Do to In Progress — 5 min ago"
4. **Card activity in modal**
- Show activity timeline in card modal below comments
- Interleave comments and activity chronologically
5. **Notification generation**
- File: `src/lib/server/notifications.ts`
- Generate notifications for: assigned to card, mentioned in comment (@username), due date approaching (daily cron or on-access check)
- Write to `notifications` table
6. **Notification UI**
- Bell icon in navbar with unread count badge
- Dropdown showing recent notifications
- Click to navigate to relevant card/board
- Mark as read (individual + mark all)
- API: `GET /api/notifications`, `PATCH /api/notifications/[id]` (mark read), `POST /api/notifications/read-all`
- File: `src/lib/components/NotificationBell.svelte`
7. **Real-time notification push**
- When a notification is created, emit via Socket.IO to the target user
- User's socket joins `user:<userId>` room on connect
- Bell icon updates live without page refresh
### Acceptance Criteria
- Board sidebar shows chronological activity
- Card modal shows activity + comments interleaved
- Bell icon shows unread notification count
- Assigning a user to a card generates a notification
- Notifications appear in real-time via socket
---
## Phase 7: Search, Filters & Templates
### Goal
Find cards quickly with filters and search. Create boards from templates.
### Tasks
1. **Board-level card filters**
- Filter bar above columns: by label, assignee, due date range
- Client-side filtering (hide non-matching cards, don't remove from state)
- File: `src/lib/components/BoardFilters.svelte`
- Filter state stored in URL search params for shareability
2. **Global search with PostgreSQL full-text search**
- Add `tsvector` column to `cards` table (or use generated column)
- Create GIN index on the tsvector
- Migration: `ALTER TABLE cards ADD COLUMN search_vector tsvector GENERATED ALWAYS AS (to_tsvector('english', coalesce(title,'') || ' ' || coalesce(description,''))) STORED;`
- `CREATE INDEX cards_search_idx ON cards USING gin(search_vector);`
- API: `GET /api/search?q=<query>` — returns matching cards with board/column info
- UI: Search bar in navbar, results dropdown with card titles + board names
- File: `src/lib/components/SearchBar.svelte`
3. **Board templates**
- System templates: "Basic Kanban" (To Do, In Progress, Done), "Scrum Sprint" (Backlog, Sprint, In Review, Done), "Bug Tracker" (New, Confirmed, In Progress, Resolved, Closed)
- Tenant templates: save any board as a template
- API: `GET /api/templates`, `POST /api/templates` (save board as template), `POST /api/boards?templateId=<id>` (create from template)
- Template data stored as JSON in `board_templates.template` column
- Board creation UI: "Start from template" option
- Seed system templates in `prisma/seed.ts`
4. **Archive/unarchive UI**
- Board listing: toggle to show archived boards
- Board header: archive/unarchive button (OWNER only)
- Card: archive button in modal (already `archived` field in schema)
- Archived cards hidden from board view (already filtered in queries)
- Board view: toggle to show archived cards (grayed out)
### Acceptance Criteria
- Filter cards by label → only matching cards visible
- Search "login" → finds cards with "login" in title or description
- Create board from "Basic Kanban" template → 3 columns pre-created
- Archive a card → disappears from board, visible in archive view
---
## Phase 8: Polish & UX
### Goal
Make the app feel production-quality with dark mode, keyboard shortcuts, responsive design, loading states, and accessibility.
### Tasks
1. **Dark mode**
- Tailwind v4 dark mode via `class` strategy
- Toggle button in navbar (sun/moon icon)
- Persist preference in localStorage + cookie (for SSR)
- File: `src/lib/stores/theme.ts`
- Update `app.css` with dark variants for custom colors
- `<html>` element gets `class="dark"` from layout
2. **Keyboard shortcuts**
- `n` — new card (in focused column)
- `Escape` — close modal/cancel edit
- `b` — back to boards
- `/` — focus search bar
- `?` — show keyboard shortcuts help modal
- File: `src/lib/components/KeyboardShortcuts.svelte`
- Register in layout, context-aware (don't trigger in input fields)
3. **Responsive / mobile design**
- Board view: horizontal scroll on mobile (already works with overflow-x)
- Column width: responsive (narrower on small screens)
- Card modal: full-screen on mobile
- Navbar: hamburger menu on mobile
- Board listing: single column grid on mobile
4. **Loading skeletons**
- Board listing: card-shaped skeleton placeholders
- Board view: column skeleton placeholders
- Card modal: skeleton for async-loaded details
- File: `src/lib/components/Skeleton.svelte`
5. **Toast notifications**
- Success/error toasts for user actions (card created, error saving, etc.)
- Auto-dismiss after 35 seconds
- Stack in bottom-right corner
- File: `src/lib/components/Toast.svelte`, `src/lib/stores/toasts.ts`
6. **Error handling**
- Global error page: `src/routes/+error.svelte`
- API error responses: consistent JSON `{ error: string }` format
- Client-side fetch wrapper with automatic error toast
7. **Accessibility**
- ARIA labels on all icon-only buttons (partially done)
- Keyboard navigation for drag-and-drop (arrow keys to move cards)
- Focus management in modals (trap focus, restore on close)
- Screen reader announcements for drag-and-drop results
### Acceptance Criteria
- Dark mode toggles and persists across sessions
- All pages usable on a 375px mobile screen
- Keyboard shortcuts work (test: press `/` → search focuses)
- Loading skeletons appear on slow connections
- Toasts appear for create/delete/error actions
---
## Phase 9: Admin & DevOps
### Goal
Admin dashboards for site-wide and per-tenant management. Production readiness.
### Tasks
1. **Site Admin dashboard**
- Route: `src/routes/admin/site/+page.svelte` (guarded: SITE_ADMIN only)
- Cross-tenant statistics: total tenants, users, boards, cards
- Tenant list with user counts, board counts, created date
- Create/edit/delete tenants
- Impersonate user (login as any user for debugging)
- Uses `bypassRLS()` Prisma client
2. **Tenant Admin dashboard**
- Route: `src/routes/admin/+page.svelte` (guarded: tenantRole ADMIN)
- Tenant settings: name, allowed domains
- User management: list, change roles, deactivate
- Tag management: create/edit/delete tenant-scoped tags
- Category management: create/edit/delete categories
- Board overview: all boards with member counts
3. **Health check endpoint**
- Route: `src/routes/api/health/+server.ts`
- Returns `{ status: 'ok', db: true/false, uptime: seconds }`
- Check DB connectivity with a simple query
- Used by Docker HEALTHCHECK and monitoring
4. **Structured logging**
- Replace `console.log` with a logger (pino or simple structured wrapper)
- Log format: JSON with timestamp, level, message, metadata
- Request logging middleware in hooks.server.ts (method, path, status, duration)
5. **Rate limiting**
- In-memory rate limiter for auth endpoints (login, register)
- File: `src/lib/server/ratelimit.ts`
- 10 attempts per minute per IP for login
- 5 registrations per hour per IP
- Return 429 Too Many Requests
6. **Docker health check**
- Add `HEALTHCHECK` to Dockerfile pointing at `/api/health`
- Update `docker-compose.yml` with healthcheck for app service
7. **Backup documentation**
- File: `docs/backup.md`
- PostgreSQL: `pg_dump` command for the kanban database
- Uploads volume: `docker cp` or volume backup
- Restore procedure
### Acceptance Criteria
- Site admin can see all tenants and their stats
- Tenant admin can manage users and tags
- `/api/health` returns 200 with DB status
- Login rate-limited to 10/min per IP
- Docker health check passes
---
## File Structure After All Phases
```
src/
├── hooks.server.ts
├── app.css
├── app.d.ts
├── app.html
├── socket/
│ └── index.ts
├── lib/
│ ├── server/
│ │ ├── db.ts
│ │ ├── auth.ts
│ │ ├── tenant.ts
│ │ ├── rls.ts
│ │ ├── permissions.ts
│ │ ├── guards.ts (Phase 3)
│ │ ├── validation.ts
│ │ ├── activity.ts (Phase 6)
│ │ ├── notifications.ts (Phase 6)
│ │ ├── realtime.ts (Phase 5)
│ │ └── ratelimit.ts (Phase 9)
│ ├── stores/
│ │ ├── socket.ts
│ │ ├── theme.ts (Phase 8)
│ │ └── toasts.ts (Phase 8)
│ ├── utils/
│ │ └── fractional-index.ts
│ └── components/
│ ├── CardModal.svelte
│ ├── TagPicker.svelte (Phase 4)
│ ├── MarkdownEditor.svelte (Phase 4)
│ ├── Checklist.svelte (Phase 4)
│ ├── CommentList.svelte (Phase 4)
│ ├── Attachments.svelte (Phase 4)
│ ├── ActivityFeed.svelte (Phase 6)
│ ├── NotificationBell.svelte (Phase 6)
│ ├── BoardFilters.svelte (Phase 7)
│ ├── SearchBar.svelte (Phase 7)
│ ├── Skeleton.svelte (Phase 8)
│ ├── Toast.svelte (Phase 8)
│ └── KeyboardShortcuts.svelte (Phase 8)
└── routes/
├── +layout.svelte
├── +layout.server.ts
├── +page.svelte
├── +error.svelte (Phase 8)
├── auth/
│ ├── login/+page.svelte
│ └── register/+page.svelte
├── boards/
│ ├── +page.svelte
│ ├── +page.server.ts
│ └── [boardId]/
│ ├── +page.svelte
│ ├── +page.server.ts
│ └── members/ (Phase 3)
│ └── +page.svelte
├── admin/ (Phase 3/9)
│ ├── +page.svelte
│ └── site/
│ └── +page.svelte
└── api/
├── health/+server.ts (Phase 9)
├── search/+server.ts (Phase 7)
├── tags/+server.ts (Phase 4)
├── categories/+server.ts (Phase 4)
├── templates/+server.ts (Phase 7)
├── notifications/+server.ts (Phase 6)
├── auth/...
└── boards/...
```
---
## Implementation Order Recommendation
Phases can be partially parallelized:
- **Phase 2** first (RLS) — foundational security
- **Phase 3** next (Roles) — needed before multi-user features
- **Phase 4** (Rich Cards) — the biggest feature payload, can start during Phase 3
- **Phase 5** (Real-Time) — depends on Phase 4 for full value
- **Phase 6** (Activity/Notifications) — depends on Phase 5
- **Phase 7** (Search/Filters/Templates) — independent, can be done anytime after Phase 4
- **Phase 8** (Polish) — do alongside or after feature phases
- **Phase 9** (Admin/DevOps) — can start admin panels during Phase 3, finish last