From 14e0a3aca688e3376a117224390a82ef35057da2 Mon Sep 17 00:00:00 2001 From: Roberto Musso Date: Thu, 19 Feb 2026 16:56:48 +0100 Subject: [PATCH] chore: mark US-005 complete in prd.json and update progress log Co-Authored-By: Claude Sonnet 4.6 --- prd.json | 4 ++-- progress.txt | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/prd.json b/prd.json index 979064a..51065f0 100644 --- a/prd.json +++ b/prd.json @@ -86,8 +86,8 @@ "Typecheck passes" ], "priority": 5, - "passes": false, - "notes": "" + "passes": true, + "notes": "Completed: clients.list (ordered by name), clients.create (UUID + createdAt), clients.update (partial), clients.delete (guard returns error payload if children exist), clients.deleteWithCascade (BFS recursive — nulls orphaned tasks.projectId, deletes projects, then clients). All queries use .all()/.run() for drizzle better-sqlite3 sync driver." }, { "id": "US-006", diff --git a/progress.txt b/progress.txt index 47152be..58e4f00 100644 --- a/progress.txt +++ b/progress.txt @@ -3,6 +3,8 @@ - electron-trpc uses `exposeElectronTRPC()` in preload and `createIPCHandler({ router, windows })` in main; renderer uses `ipcLink()` from `electron-trpc/renderer` - appRouter lives at `src/main/router/index.ts`; renderer client at `src/renderer/lib/trpc.ts` - `@/*` path alias maps to `src/renderer/*` (configured in tsconfig.json paths) +- Drizzle ORM with better-sqlite3 (sync driver): SELECT queries MUST end with `.all()` to execute; INSERT/UPDATE/DELETE MUST end with `.run()` +- `inArray(column, values)` works with nullable columns when values is `string[]` (TypeScript covariance allows string[] → (string | null)[]) - All DB tables use `CREATE TABLE IF NOT EXISTS` for non-destructive migrations - All IDs are UUIDs generated via `crypto.randomUUID()` - TypeScript strict mode + noUncheckedIndexedAccess enabled; always account for possible undefined on array access @@ -48,3 +50,20 @@ - ESLint `import/no-unresolved` requires `eslint-import-resolver-typescript` with `alwaysTryTypes: true` to resolve TypeScript path aliases - The `writingMode: 'vertical-rl'` + `transform: 'rotate(180deg)'` CSS pattern creates bottom-to-top text for vertical affordance labels --- + +## 2026-02-19 - US-005 +- What was implemented: + - Full clients tRPC router replacing stubs in `src/main/router/index.ts` + - Added imports: `eq`, `asc`, `inArray` from `drizzle-orm`; `getDb` from `../db`; `clients`, `projects`, `tasks` from `../db/schema` + - `clients.list`: `db.select().from(clients).orderBy(asc(clients.name)).all()` + - `clients.create`: inserts with `crypto.randomUUID()` + `Date.now()` via `.run()` + - `clients.update`: partial update — only sets fields that are defined in input, skips if no-op + - `clients.delete`: checks for child clients and child projects; returns `{ error: string }` payload if any exist; otherwise deletes and returns `{ success: true }` + - `clients.deleteWithCascade`: BFS loop collects all descendant client IDs, finds their projects, nulls `projectId` on orphaned tasks, deletes projects, then deletes all clients +- Files changed: `src/main/router/index.ts`, `prd.json`, `progress.txt` +- **Learnings for future iterations:** + - Drizzle ORM with better-sqlite3 sync driver: SELECT must call `.all()` to get an array; INSERT/UPDATE/DELETE must call `.run()` to execute — NOT calling these causes TypeScript errors (query builder ≠ result) + - `inArray(nullableColumn, string[])` is TypeScript-safe because `string[]` is assignable to `(string | null)[]` via covariance + - Guard against empty arrays before using `inArray` — while `allClientIds` is never empty (starts with input.id), `projectIds` could be empty; guarded with `if (projectIds.length > 0)` block + - `@typescript-eslint/no-non-null-assertion` is configured as a warning (not error) in this project — `queue.shift()!` is fine after a `length > 0` check +---