Skip to content

Commit a422e72

Browse files
committed
initial commit
0 parents  commit a422e72

File tree

286 files changed

+44494
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

286 files changed

+44494
-0
lines changed

.dockerignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
node_modules
2+
.nuxt
3+
.output
4+
.cache
5+
.data
6+
dist
7+
8+
*.db
9+
*.db-wal
10+
*.db-shm
11+
12+
.env
13+
.env.*
14+
15+
*.log
16+
data/
17+
18+
.DS_Store
19+
.fleet
20+
.idea
21+
.vscode
22+
23+
.claude
24+
.playwright
25+
.playwright-mcp
26+
.git

.github/workflows/ci.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: ci
2+
3+
on: push
4+
5+
jobs:
6+
ci:
7+
runs-on: ${{ matrix.os }}
8+
9+
strategy:
10+
matrix:
11+
os: [ubuntu-latest]
12+
node: [22]
13+
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v6
17+
18+
- name: Install pnpm
19+
uses: pnpm/action-setup@v4
20+
21+
- name: Install node
22+
uses: actions/setup-node@v6
23+
with:
24+
node-version: ${{ matrix.node }}
25+
cache: pnpm
26+
27+
- name: Install dependencies
28+
run: pnpm install
29+
30+
- name: Lint
31+
run: pnpm run lint
32+
33+
- name: Typecheck
34+
run: pnpm run typecheck

.gitignore

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Build outputs
2+
.output
3+
.nuxt
4+
.nitro
5+
.cache
6+
.data
7+
dist
8+
9+
# Dependencies
10+
node_modules
11+
12+
# Environment
13+
.env
14+
.env.*
15+
16+
# Database
17+
*.db
18+
*.db-wal
19+
*.db-shm
20+
21+
# Logs
22+
*.log
23+
24+
# Uploads
25+
data/
26+
27+
# IDE / Editor
28+
.DS_Store
29+
.fleet
30+
.idea
31+
.vscode
32+
.editorconfig
33+
34+
# Claude Code
35+
.claude
36+
37+
# Playwright
38+
.playwright
39+
.playwright-mcp
40+
41+
# Session Stuff
42+
TODO/
43+
prompts/

CLAUDE.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Completo - All the Toppings. None of the Mess.
2+
3+
> **About this file:** CLAUDE.md is for agent guidance — architectural decisions, rules, conventions, and gotchas that can't be inferred from reading code. Don't bloat it with code-level details (file listings, prop docs, full API specs) that agents can discover by reading the source. Focus on the "why", not the "what".
4+
5+
Kanban board app. Nuxt 4 + Nuxt UI 4 + Tailwind 4 + Drizzle ORM + SQLite. Plus Jakarta Sans + JetBrains Mono. Lucide icons (`i-lucide-*`). pnpm.
6+
7+
```bash
8+
pnpm install && npx drizzle-kit push && pnpm db:seed && pnpm dev # http://localhost:3000
9+
```
10+
11+
Demo: `demo@example.com` / `demo1234` | Admin: `admin@example.com` / `admin1234`
12+
13+
## Architecture
14+
15+
### The Core Model
16+
17+
**Statuses and cards belong to projects, not views.** Boards and lists are *views* — they don't own data. Cards have a `projectId` + `statusId`; boards see cards through `boardColumns` (junction table). Removing a column from a board just unlinks it — cards survive. Deleting a status cascades everywhere.
18+
19+
**Don't confuse the two kinds of "column":**
20+
21+
| Concept | What it is | DB table |
22+
|---------|-----------|----------|
23+
| **Board column** | How a status appears on a board (position) | `boardColumns` |
24+
| **Field column** | Which card field shows in a list table | `listColumns` |
25+
26+
### Key Design Decisions
27+
28+
- **Done status & retention:** `doneStatusId` + `doneRetentionDays` on projects. Views **filter out** (not delete) old done cards. Card counts exclude done status. `null` retention = keep forever.
29+
- **Primary keys:** UUIDs everywhere. **Exception:** cards use INTEGER AUTOINCREMENT (for `TK-42` style IDs). Always parse card IDs with `Number()`.
30+
- **Positions:** Integer `position` field. New items = `max(existing) + 1` (not `.length`).
31+
- **Password sentinels:** `'!oauth'` = OAuth-only user, `'!invited'` = admin-created pending setup. Both are unhashable.
32+
- **Synthetic admin role:** API returns `role: 'admin'` for non-member admins viewing projects — don't display it as a real project role.
33+
34+
### Auth & Permissions
35+
36+
- `isAdmin=1` bypasses membership checks via synthetic `{ role: 'owner' }`. My Tasks is NOT admin-elevated.
37+
- **404 not 403** for non-member access (don't leak resource existence).
38+
- **IDOR prevention:** Every card/tag/board endpoint validates resources belong to the correct project.
39+
- **No email in search results** — user search returns name only.
40+
- Domain allowlist restricts self-registration only — invitations and admin-created users bypass it.
41+
- Login requires verified email. `isEmailEnabled()` checks `SMTP_HOST`.
42+
43+
### Email Templates (Gotchas)
44+
45+
- Table-based layout only (no flexbox/grid)
46+
- Solid hex colors only (no `rgba()` — breaks in 21% of clients)
47+
- No `border-radius` on buttons (use VML `v:roundrect` for Outlook)
48+
49+
## Conventions
50+
51+
### Do
52+
53+
- **Fetch:** Pages use `useFetch()`, composables use `$fetch()`. Refresh after mutations.
54+
- **Composables:** `useKanban()`/`useListView()` accept slug-or-ID + optional `{ projectSlug }` to prevent cross-project slug collisions.
55+
- **Transactions:** `db.transaction()` for multi-step DB operations.
56+
- **SSR:** `ssr: true` with `routeRules`. Auth routes `ssr: false`. vuedraggable via `defineAsyncComponent` + `<ClientOnly>`.
57+
- **DB access:** `db` + `schema` auto-imported. `server/utils/` is auto-imported — don't use `~/server/utils/...` (Nuxt 4: `~` = `app/`).
58+
- **Database schema:** `server/database/schema.ts` — all tables, columns, relations. Migrations in `server/database/migrations/`.
59+
- **OpenAPI spec** (`server/api/openapi.get.ts`) must stay in sync with endpoints. Only covers headless API usage — frontend-internal endpoints (slug/key validation, UI column config, notifications, OAuth redirects, registration flows) are intentionally omitted.
60+
- **Write tests** for new features. Run `pnpm test` after changes.
61+
62+
### Don't
63+
64+
- **Don't use `theme()` in scoped CSS** — Tailwind v4 uses `var(--color-*)`.
65+
- **Don't use CSS `ring-*` for tag pills** — they use `box-shadow` inset borders.
66+
- **Don't use native `<input type="date">`** — use `UPopover` + `UCalendar` + `CalendarDate`.
67+
- **Don't use `document.createElement('input')` for file pickers** — use a persistent hidden `<input>` ref.
68+
- **Don't use `@keydown` on forms for Cmd+Enter** — portals break it. Use global `document` listener with `capture: true`.
69+
- **Don't close the browser** during Playwright MCP sessions. Screenshots go in `.playwright/`, clean up after.
70+
71+
### Styling
72+
73+
- **Aesthetic:** "Trello meets Linear" — indigo-violet primary, zinc neutrals.
74+
- **Priority icons:** `alert-circle`=urgent, `chevron-up`=high, `grip-horizontal`=medium, `chevron-down`=low. Colors: red/orange/indigo/slate. Helpers in `app/utils/constants.ts`.
75+
- **Due date colors:** red=overdue, orange=due-soon (today/tomorrow), slate=future.
76+
- **Ticket IDs:** `{projectKey}-{cardId}`, sits above card title (not in footer).
77+
- **Destructive actions:** Wrapped in `<UTooltip>`. Views/projects use type-name-to-confirm; cards use simple confirm.
78+
- **ESLint:** No comma dangles, 1tbs brace style.
79+
80+
## Testing
81+
82+
Two vitest projects: `unit` (fast) + `integration` (sequential, 30s timeout). Test DB on `:43210`.
83+
84+
**Gotchas:**
85+
- `fetch(url('/path'))` for raw responses (ofetch throws on non-2xx)
86+
- `randomKey()` in fixtures to avoid 409 conflicts
87+
- `process.env.NODE_ENV` inlined at build — use custom env vars for runtime gating
88+
- Kill stale test server: `lsof -ti:43210 | xargs kill -9`
89+
90+
## Environment & Commands
91+
92+
`NUXT_SESSION_PASSWORD` is the only required env var (min 32 chars). See `.env.example` for the rest. Key vars: `DATABASE_URL` (default `sqlite.db`), `AI_PROVIDER` (`anthropic`/`openai`/`openrouter`, empty=disabled), `SMTP_HOST` (empty=email disabled), `UPLOAD_DIR` (default `data/uploads`), `NUXT_OAUTH_*_CLIENT_ID/SECRET` (empty=provider disabled).
93+
94+
```bash
95+
pnpm dev / build / test / lint / typecheck
96+
pnpm db:migrate / db:seed / db:cleanup
97+
pnpm user:create <email> <password> [name] [admin]
98+
pnpm user:set-role <email> <admin|user>
99+
pnpm user:verify-email <email>
100+
npx drizzle-kit push # Dev only — diffs schema against DB
101+
npx drizzle-kit generate # Generate migration SQL from schema changes
102+
```
103+
104+
### Schema Changes & Migrations
105+
106+
`drizzle-kit push` = dev (no migration files). `pnpm db:migrate` = production (applies SQL from `server/database/migrations/`).
107+
108+
**After every schema change:** edit `schema.ts``npx drizzle-kit generate` → commit the migration. If you skip generating, production deploys will fail.
109+
110+
**`scripts/package.json`** is a deploy manifest for CLI tools. When changing imports in scripts, update it to keep deps in sync.
111+
112+
## Documentation
113+
114+
- Nuxt: https://nuxt.com/llms.txt
115+
- Nuxt UI: https://ui.nuxt.com/llms.txt
116+
- Nuxt Auth Utils: https://raw.githubusercontent.com/atinux/nuxt-auth-utils/refs/heads/main/README.md

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Completo
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Completo
2+
3+
**All the toppings. None of the mess.**
4+
5+
---
6+
7+
Completo is a self-hosted project board for teams who just want to get things done. Named after the legendary Chilean hot dog, piled high with everything: avocado, tomato, mayo, sauerkraut. Your project starts the same way. Tasks stacked, ideas overflowing, endless opinions - too much of everything. Completo's boards help you make sense of the chaos. No setup marathons, no learning curve — just open a board and go. Get your stuff done. Drag. Drop. Completo.
8+
9+
### Free. Open Source. One command.
10+
11+
Completo costs nothing. Zero. Nil. `undefined`. It's MIT licensed — fork it, break it, fix it, ship it. No hosted plans. No premium tier. No "let's schedule a call to discuss pricing."
12+
13+
Install it with one command:
14+
15+
```bash
16+
git clone https://github.com/ScaleCommerce-dev/completo.git
17+
cd completo
18+
npm install
19+
```
20+
21+
That's it. No Docker compose files. No Kubernetes manifests. No 14-step setup guide written by someone who clearly hates you. SQLite is baked in — there's no database to provision, no connection string to fumble. Start it. Open your browser. Drag. Drop. Completo.
22+
23+
### Why it exists
24+
25+
Because every ticket system starts as "we just need something simple" and ends as a mass of gantt charts, resource leveling matrices, and a 200-page admin guide that nobody reads, maintained by nobody, understood by nobody.
26+
27+
You didn't want that. You wanted a board with columns and cards. So that's what we built. And then we stopped.
28+
29+
### What it does
30+
31+
- **Boards** — Create as many as you need. Configure the columns yourself. Done.
32+
- **Cards** — Title. Description. Assignee. Priority. Drag it. That's the feature list.
33+
- **Projects** — Separate your work. Invite your team. Keep things tidy.
34+
- **My Tasks** — One checklist. Everything assigned to you. Across all projects. Check it off. Go home.
35+
- **SSO** — Sign in with your existing identity provider. No new password to forget and then reset and then forget again.
36+
37+
### What it doesn't do
38+
39+
Gantt charts. Time tracking. Burndown charts. Sprint velocity. Story points. Epics. Dependencies. Custom fields. Webhooks. Integrations. Blockchain-based task verification.
40+
41+
You're welcome.
42+
43+
### The philosophy
44+
45+
Your board should be empty at the end of the week. That's it. That's the philosophy. Every feature in Completo exists to help you get there faster. Everything that doesn't was never added in the first place.
46+
47+
All the toppings. None of the mess.
48+
49+

app/app.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default defineAppConfig({
2+
ui: {
3+
colors: {
4+
primary: 'blue',
5+
neutral: 'slate'
6+
}
7+
}
8+
})

app/app.vue

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script setup>
2+
useHead({
3+
meta: [
4+
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
5+
],
6+
link: [
7+
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
8+
{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' },
9+
{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' },
10+
{ rel: 'icon', sizes: '48x48', href: '/favicon.ico' },
11+
{ rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }
12+
],
13+
htmlAttrs: {
14+
lang: 'en'
15+
}
16+
})
17+
18+
useSeoMeta({
19+
title: 'Completo — All the Toppings. None of the Mess.',
20+
description: 'Drag. Drop. Completo.'
21+
})
22+
</script>
23+
24+
<template>
25+
<UApp>
26+
<NuxtLayout>
27+
<NuxtPage />
28+
</NuxtLayout>
29+
</UApp>
30+
</template>

0 commit comments

Comments
 (0)