Team Kanban board (Trello-style) with assignments, a ReactFlow board canvas, PWA install and Android push notifications. Backend: Supabase.
Working today: teams (create, switch, roles, add/remove members) and boards (create, private boards, backgrounds, lists, archive/restore) a drag-and-drop card canvas built on ReactFlow, and full card detail (assignees, labels, dates, checklists, comments with @mentions, attachments), and My Tasks with a team workload view, and live updates with an in-app notification inbox — on top of a fully locked-down Postgres schema. The PWA and Android push are next.
context.md is the current state of the project — read it first. docs/IMPLEMENTATION_PLAN.md has the architecture and the phases.
Requirements: Node 22+.
npm install
cp .env.example .env.local # then fill in the values below
npm run dev # http://localhost:5173.env.local needs, from Supabase Dashboard → Project Settings → API:
VITE_SUPABASE_URL: the project URLVITE_SUPABASE_ANON_KEY: the anon / public key (never theservice_rolekey)VITE_DEV_USER_PASSWORD: development only — the password of the seeded test users. Vite strips it from production builds.
Settings → Backend in the app shows whether the connection works.
The Supabase CLI is installed as a dev dependency and needs no Docker for the commands we use.
npx supabase login
npx supabase link --project-ref <your-dev-project-ref>
npm run db:push # apply supabase/migrations to the linked project
npm run db:types # regenerate src/types/database.types.ts from the live schemaThen seed the test users: Supabase Dashboard → SQL Editor, paste supabase/seed.sql, Run.
It is idempotent and creates four confirmed users who share VITE_DEV_USER_PASSWORD:
| User | In the seeded data |
|---|---|
| ada@kanban.test | owner of "Product" |
| grace@kanban.test | admin of "Product" |
| linus@kanban.test | member of "Product" |
| mallory@kanban.test | owner of "Outsiders" — shares no team, used to prove isolation |
Never run the seed against production.
There are no login screens until Phase 11. In development, a user switcher in the header signs in as one of the seeded users with real Supabase Auth, so Row Level Security applies exactly as it will in production. It is compiled out of production builds.
| Script | What it does |
|---|---|
npm run dev |
Vite dev server |
npm run build |
Typecheck + production build |
npm run check |
Typecheck, lint, format check and unit tests |
npm test / npm run test:watch |
Vitest unit and component tests |
npm run test:rls |
Row Level Security suite against the dev project |
npm run e2e |
Playwright tests (desktop + Pixel 7 viewports) |
npm run lint |
oxlint (React, a11y, TypeScript rules) |
npm run format / npm run format:check |
Prettier (with Tailwind class sorting) |
First Playwright run: npx playwright install chromium.
src/
app/ shell, routes, error/404 pages
components/ui/ shared UI primitives (Button, Input, Dialog, ...)
features/ one folder per product area
lib/ env, Supabase client, api/* (typed data access), theme, utilities
types/ database.types.ts (generated — npm run db:types, never hand-edited)
styles/ Tailwind entry + design tokens (light/dark)
supabase/
migrations/ hand-written SQL: schema, RLS, triggers, RPCs
seed.sql development test users, teams and a sample board
tests/e2e/ Playwright
tests/rls/ Row Level Security suite (network, dev project)
- Components never call Supabase directly — always through
src/lib/api/*, which returns typed data and UI-safe errors. - Each feature keeps its queries and mutations in one
use*.tshook file; that file also decides what to invalidate. - Anything that writes more than one row is a Postgres function (
create_team,create_board,add_team_member, …), not several round trips. - Boards, lists and cards are ordered by fractional-index strings, so moving one item rewrites one
row. Sorting uses
lib/ordering.ts, which matches Postgres'collate "C"byte order. - Permissions are enforced by Row Level Security, never by hiding buttons. The UI hides what the
server would refuse, and
npm run test:rlsproves the server refuses it.