A fast, keyboard-first project management tool — think Linear × Jira.
Built with Next.js 16, TypeScript, Tailwind v4, shadcn/ui, and Supabase (Auth · Postgres + Row-Level Security · Realtime).
- 🔐 Auth & multi-tenancy — email/password auth; workspaces with role-based access (owner / admin / member / viewer) enforced by Postgres Row-Level Security, not application code.
- 🗂️ Projects & issues — issues carry status, priority, assignee, labels,
due date, story points, subtasks, blocking dependencies, and time
tracking. Issue keys (
MA-42) are numbered atomically by a database trigger. - 🧲 Kanban board — drag and drop between columns (mouse and keyboard), with live updates for everyone via Supabase Realtime.
- 📋 List view — sortable columns, filters (status / priority / assignee /
label), text search, and
J/Krow navigation. - ⌨️ Keyboard-first — a
⌘Kcommand palette that searches issues and jumps anywhere, plus shortcuts for every common action. - 🧠 Smart defaults — suggests labels from the issue text and an assignee from team history and current workload.
- 📊 Analytics — status, priority, workload, and a created-vs-completed trend, with a colour palette validated for contrast and colour-blindness.
- 🌗 Light & dark mode throughout.
| Dashboard | Kanban board |
|---|---|
![]() |
![]() |
| List view | Issue detail |
|---|---|
![]() |
![]() |
| Analytics | Command palette |
|---|---|
![]() |
![]() |
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router; src/proxy.ts replaces middleware) |
| Language | TypeScript (strict) |
| UI | Tailwind CSS v4 + shadcn/ui (Radix) + lucide-react |
| Server state | React Query |
| Client state | Zustand |
| Drag & drop | dnd-kit |
| Charts | Recharts |
| Backend | Supabase — Auth, Postgres (RLS), Realtime |
| Hosting | Vercel |
There is no custom backend server. The browser talks to Supabase directly, and every access rule lives in SQL policies.
npm installCopy .env.example to .env.local and add your Supabase anon (publishable)
key from Project Settings → API:
NEXT_PUBLIC_SUPABASE_URL=https://<project-ref>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon / publishable key>Until these are set, every route redirects to /setup with instructions.
Apply the migrations in supabase/migrations/ in order — via the Supabase
dashboard SQL editor, the Supabase MCP, or the CLI:
| File | What it creates |
|---|---|
00001_init.sql |
tables, indexes, triggers (issue numbering, activity log, updated_at, profile-on-signup) |
00002_rls.sql |
row-level-security policies + create_workspace / accept_workspace_invite RPCs |
00003_realtime.sql |
realtime publication for issues & comments |
00004_security_hardening.sql |
pinned search_path, extension schema, RPC grants |
npm run dev # http://localhost:3000Sign up, create a workspace, add a project, and start filing issues.
Tip: Supabase enables email confirmation by default. For local demos, turn it off under Authentication → Sign In / Providers → Email.
| Command | Purpose |
|---|---|
npm run dev |
dev server (Turbopack) |
npm run build |
production build |
npm run lint |
ESLint |
npx tsc --noEmit |
typecheck |
| Keys | Action |
|---|---|
⌘K |
Command palette (search issues, navigate, run actions) |
C |
New issue |
G then D / P / A / M |
Go to Dashboard / Projects / Analytics / Members |
G then B / L |
Board / List (inside a project) |
J / K |
Move between rows in the list view |
| Space + arrows | Move a card between board columns |
? |
Show all shortcuts |
- RLS is the authorization layer. Workspace membership gates every row;
security definerhelper functions (is_workspace_member,workspace_role, …) avoid recursive policy lookups. Changing who-can-see-what means editingsupabase/migrations/00002_rls.sql, not TypeScript. - Sessions are refreshed and routes guarded in
src/proxy.ts(Next 16's renamed middleware). - Data layer. React Query hooks in
src/hooks/wrap every Supabase read and write. Board and inline edits are optimistic with rollback; Supabasepostgres_changessubscriptions invalidate the cache so every open client converges in real time. - Issue numbers (
MA-1,MA-2, …) come from aBEFORE INSERTtrigger that bumpsprojects.next_issue_numberatomically — no client-side race.
src/
├── app/
│ ├── (auth)/ login · register (+ brand panel)
│ ├── (app)/[workspaceSlug]/ dashboard · projects · analytics · members · settings
│ ├── onboarding/ setup/ auth/callback/
│ └── proxy.ts session refresh + route guards
├── components/
│ ├── board/ issues/ project/ analytics/ feature UIs
│ └── ui/ shadcn primitives
├── hooks/ React Query data hooks (+ realtime)
├── lib/ supabase clients · types · smart defaults
└── stores/ Zustand UI state
supabase/migrations/ schema · RLS · realtime · hardening
- Import the repo in Vercel (the app is at the repo root — no root-directory setting needed).
- Add
NEXT_PUBLIC_SUPABASE_URLandNEXT_PUBLIC_SUPABASE_ANON_KEY. - In Supabase Authentication → URL Configuration, add your Vercel URL so email links and OAuth redirects resolve.






