From 027496baf048fd89da3632058aa734f0218e6c97 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 22 Feb 2026 18:07:15 -0600 Subject: [PATCH] Signed-off-by: Max --- README.md | 22 +++++++++++++++++----- SUPABASE_SETUP.md | 9 +++++---- scripts/README.md | 31 ++++++++++++++++--------------- src/app/tasks/[taskId]/page.tsx | 23 ++++++++++++++++++++--- 4 files changed, 58 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index afffde8..8266856 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,13 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi - Standardized sprint date parsing across Kanban, Backlog, Sprint Board, Archive, and task detail sprint pickers. - `POST /api/sprints` and `PATCH /api/sprints` now normalize `startDate`/`endDate` to `YYYY-MM-DD` before writing to Supabase. +### Feb 23, 2026 updates + +- Removed non-live fallback data paths from the task store; board data now comes from Supabase-backed APIs only. +- Removed legacy schema expectations from API code and docs (`legacy_id`, `meta` dependency, non-existent task columns). +- Task detail now includes explicit Project + Sprint selection; selecting a sprint auto-aligns `projectId`. +- API validation now accepts UUID-shaped IDs used by existing data and returns clearer error payloads for failed writes. + ### Sprint date semantics (important) - Sprint dates are treated as local calendar-day boundaries: @@ -53,8 +60,8 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi - Tasks use labels (`tags: string[]`) and can have multiple labels. - Tasks support attachments (`attachments: TaskAttachment[]`). -- Tasks now track `createdById`, `createdByName`, `createdByAvatarUrl`, `updatedById`, `updatedByName`, and `updatedByAvatarUrl`. -- Tasks now track assignment via `assigneeId`, `assigneeName`, `assigneeEmail`, and `assigneeAvatarUrl`. +- Tasks persist identity references via `createdById`, `updatedById`, and `assigneeId`. +- Display fields (`createdByName`, `updatedByName`, `assigneeName`, avatar/email) are resolved from `users` data in API/UI, not stored as separate task table columns. - There is no active `backlog` status in workflow logic. - A task is considered in Backlog when `sprintId` is empty. - Current status values: @@ -118,7 +125,7 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi ### Labels -- Project selection UI for tasks was removed in favor of labels. +- Labels are independent of project/sprint selection. - You can add/remove labels inline in: - New Task modal - Task Detail page @@ -134,6 +141,7 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi - `/tasks/{taskId}` - Task detail is no longer popup-only behavior. - Each task now has a shareable deep link. +- Task detail includes explicit Project and Sprint dropdowns. ### Attachments @@ -200,8 +208,12 @@ During drag, the active target column shows expanded status drop zones for clari ## Persistence - Client state is managed with Zustand. -- Persistence is done via `/api/tasks`. -- API reads/writes Supabase tables. +- Persistence is handled through Supabase-backed API routes: + - `/api/tasks` + - `/api/projects` + - `/api/sprints` +- No local task/project/sprint fallback dataset is used. +- API reads/writes Supabase tables only. ## Run locally diff --git a/SUPABASE_SETUP.md b/SUPABASE_SETUP.md index 2d737a4..02ea0c3 100644 --- a/SUPABASE_SETUP.md +++ b/SUPABASE_SETUP.md @@ -149,7 +149,7 @@ Or push to git and let Vercel auto-deploy. ### After (Supabase): - PostgreSQL database hosted by Supabase -- JWT-based session tokens +- Supabase Auth credentials + app-managed `gantt_session` cookie sessions - Works on multiple servers/Vercel edge functions - Real-time subscriptions possible (future enhancement) @@ -164,6 +164,7 @@ Or push to git and let Vercel auto-deploy. - Users can only see/modify their own data - Admin operations use service role key -3. **Password hashing remains the same** - - We use scrypt hashing (same as SQLite version) - - Passwords are never stored in plain text +3. **Password handling** + - Passwords are managed by Supabase Auth. + - Do not add `public.users.password_hash`; keep password data out of public tables. + - App login state uses secure, HTTP-only session cookies. diff --git a/scripts/README.md b/scripts/README.md index 0059f54..40cc966 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -41,17 +41,17 @@ A unified CLI that covers all API operations. # Create task ./scripts/gantt.sh task create [status] [priority] [project-id] -./scripts/gantt.sh task create "Fix bug" open high 1 +./scripts/gantt.sh task create "Fix bug" open high <project-uuid> # Create from natural language ./scripts/gantt.sh task natural "Fix login bug by Friday, assign to Matt, high priority" # Update any field ./scripts/gantt.sh task update <task-id> <field> <value> -./scripts/gantt.sh task update abc-123 status done -./scripts/gantt.sh task update abc-123 priority urgent -./scripts/gantt.sh task update abc-123 title "New title" -./scripts/gantt.sh task update abc-123 assigneeId <user-id> +./scripts/gantt.sh task update <task-uuid> status done +./scripts/gantt.sh task update <task-uuid> priority urgent +./scripts/gantt.sh task update <task-uuid> title "New title" +./scripts/gantt.sh task update <task-uuid> assigneeId <user-uuid> # Delete task ./scripts/gantt.sh task delete <task-id> @@ -61,7 +61,7 @@ A unified CLI that covers all API operations. # Attach file ./scripts/gantt.sh task attach <task-id> <file-path> -./scripts/gantt.sh task attach abc-123 ./research.pdf +./scripts/gantt.sh task attach <task-uuid> ./research.pdf ``` ### Project Commands @@ -78,9 +78,9 @@ A unified CLI that covers all API operations. # Update project field ./scripts/gantt.sh project update <project-id> <field> <value> -./scripts/gantt.sh project update abc-123 name "New Name" -./scripts/gantt.sh project update abc-123 description "New desc" -./scripts/gantt.sh project update abc-123 color "#ff0000" +./scripts/gantt.sh project update <project-uuid> name "New Name" +./scripts/gantt.sh project update <project-uuid> description "New desc" +./scripts/gantt.sh project update <project-uuid> color "#ff0000" # Delete project ./scripts/gantt.sh project delete <project-id> @@ -100,9 +100,9 @@ A unified CLI that covers all API operations. # Update sprint field ./scripts/gantt.sh sprint update <sprint-id> <field> <value> -./scripts/gantt.sh sprint update abc-123 name "New Sprint Name" -./scripts/gantt.sh sprint update abc-123 status active -./scripts/gantt.sh sprint update abc-123 startDate "2026-02-25" +./scripts/gantt.sh sprint update <sprint-uuid> name "New Sprint Name" +./scripts/gantt.sh sprint update <sprint-uuid> status active +./scripts/gantt.sh sprint update <sprint-uuid> startDate "2026-02-25" # Delete sprint ./scripts/gantt.sh sprint delete <sprint-id> @@ -242,13 +242,13 @@ export API_URL=http://localhost:3001/api ```bash # Add completion comment -./scripts/gantt.sh task comment abc-123 "Completed. See attached notes." +./scripts/gantt.sh task comment <task-uuid> "Completed. See attached notes." # Attach notes -./scripts/gantt.sh task attach abc-123 ./completion-notes.md +./scripts/gantt.sh task attach <task-uuid> ./completion-notes.md # Mark done -./scripts/gantt.sh task update abc-123 status done +./scripts/gantt.sh task update <task-uuid> status done ``` ### Find Tasks @@ -294,6 +294,7 @@ Task IDs are UUIDs like `33ebc71e-7d40-456c-8f98-bb3578d2bb2b`. Find them: - In the URL when viewing a task - From `task list` output - From `task create` output +- `projectId`, `sprintId`, and `assigneeId` fields are UUID values as well. ## Tips diff --git a/src/app/tasks/[taskId]/page.tsx b/src/app/tasks/[taskId]/page.tsx index bf2a7c5..bd7f625 100644 --- a/src/app/tasks/[taskId]/page.tsx +++ b/src/app/tasks/[taskId]/page.tsx @@ -356,10 +356,9 @@ export default function TaskDetailPage() { const sortedSprints = useMemo( () => sprints - .filter((sprint) => sprint.projectId === editedTask?.projectId) .slice() .sort((a, b) => parseSprintStart(a.startDate).getTime() - parseSprintStart(b.startDate).getTime()), - [sprints, editedTask?.projectId] + [sprints] ) const handleAttachmentUpload = async (event: ChangeEvent<HTMLInputElement>) => { @@ -474,6 +473,24 @@ export default function TaskDetailPage() { }) } + const setEditedTaskSprint = (sprintId: string) => { + if (!editedTask) return + if (!sprintId) { + setEditedTask({ + ...editedTask, + sprintId: undefined, + }) + return + } + + const sprint = sprints.find((entry) => entry.id === sprintId) + setEditedTask({ + ...editedTask, + sprintId, + projectId: sprint?.projectId || editedTask.projectId, + }) + } + const handleSave = async () => { if (!editedTask) return if (!editedTask.projectId) { @@ -819,7 +836,7 @@ export default function TaskDetailPage() { <Label className="text-slate-400">Sprint</Label> <select value={editedTask.sprintId || ""} - onChange={(event) => setEditedTask({ ...editedTask, sprintId: event.target.value || undefined })} + onChange={(event) => setEditedTaskSprint(event.target.value)} className="w-full mt-2 px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-blue-500" > <option value="">No Sprint</option>