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

20 KiB
Raw Blame History

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