Signed-off-by: Max <ai-agent@topdoglabs.com>
This commit is contained in:
parent
6cdd584b82
commit
027496baf0
22
README.md
22
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.
|
- 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.
|
- `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 date semantics (important)
|
||||||
|
|
||||||
- Sprint dates are treated as local calendar-day boundaries:
|
- 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 use labels (`tags: string[]`) and can have multiple labels.
|
||||||
- Tasks support attachments (`attachments: TaskAttachment[]`).
|
- Tasks support attachments (`attachments: TaskAttachment[]`).
|
||||||
- Tasks now track `createdById`, `createdByName`, `createdByAvatarUrl`, `updatedById`, `updatedByName`, and `updatedByAvatarUrl`.
|
- Tasks persist identity references via `createdById`, `updatedById`, and `assigneeId`.
|
||||||
- Tasks now track assignment via `assigneeId`, `assigneeName`, `assigneeEmail`, and `assigneeAvatarUrl`.
|
- 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.
|
- There is no active `backlog` status in workflow logic.
|
||||||
- A task is considered in Backlog when `sprintId` is empty.
|
- A task is considered in Backlog when `sprintId` is empty.
|
||||||
- Current status values:
|
- Current status values:
|
||||||
@ -118,7 +125,7 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi
|
|||||||
|
|
||||||
### Labels
|
### 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:
|
- You can add/remove labels inline in:
|
||||||
- New Task modal
|
- New Task modal
|
||||||
- Task Detail page
|
- Task Detail page
|
||||||
@ -134,6 +141,7 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi
|
|||||||
- `/tasks/{taskId}`
|
- `/tasks/{taskId}`
|
||||||
- Task detail is no longer popup-only behavior.
|
- Task detail is no longer popup-only behavior.
|
||||||
- Each task now has a shareable deep link.
|
- Each task now has a shareable deep link.
|
||||||
|
- Task detail includes explicit Project and Sprint dropdowns.
|
||||||
|
|
||||||
### Attachments
|
### Attachments
|
||||||
|
|
||||||
@ -200,8 +208,12 @@ During drag, the active target column shows expanded status drop zones for clari
|
|||||||
## Persistence
|
## Persistence
|
||||||
|
|
||||||
- Client state is managed with Zustand.
|
- Client state is managed with Zustand.
|
||||||
- Persistence is done via `/api/tasks`.
|
- Persistence is handled through Supabase-backed API routes:
|
||||||
- API reads/writes Supabase tables.
|
- `/api/tasks`
|
||||||
|
- `/api/projects`
|
||||||
|
- `/api/sprints`
|
||||||
|
- No local task/project/sprint fallback dataset is used.
|
||||||
|
- API reads/writes Supabase tables only.
|
||||||
|
|
||||||
## Run locally
|
## Run locally
|
||||||
|
|
||||||
|
|||||||
@ -149,7 +149,7 @@ Or push to git and let Vercel auto-deploy.
|
|||||||
|
|
||||||
### After (Supabase):
|
### After (Supabase):
|
||||||
- PostgreSQL database hosted by 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
|
- Works on multiple servers/Vercel edge functions
|
||||||
- Real-time subscriptions possible (future enhancement)
|
- 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
|
- Users can only see/modify their own data
|
||||||
- Admin operations use service role key
|
- Admin operations use service role key
|
||||||
|
|
||||||
3. **Password hashing remains the same**
|
3. **Password handling**
|
||||||
- We use scrypt hashing (same as SQLite version)
|
- Passwords are managed by Supabase Auth.
|
||||||
- Passwords are never stored in plain text
|
- Do not add `public.users.password_hash`; keep password data out of public tables.
|
||||||
|
- App login state uses secure, HTTP-only session cookies.
|
||||||
|
|||||||
@ -41,17 +41,17 @@ A unified CLI that covers all API operations.
|
|||||||
|
|
||||||
# Create task
|
# Create task
|
||||||
./scripts/gantt.sh task create <title> [status] [priority] [project-id]
|
./scripts/gantt.sh task create <title> [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
|
# Create from natural language
|
||||||
./scripts/gantt.sh task natural "Fix login bug by Friday, assign to Matt, high priority"
|
./scripts/gantt.sh task natural "Fix login bug by Friday, assign to Matt, high priority"
|
||||||
|
|
||||||
# Update any field
|
# Update any field
|
||||||
./scripts/gantt.sh task update <task-id> <field> <value>
|
./scripts/gantt.sh task update <task-id> <field> <value>
|
||||||
./scripts/gantt.sh task update abc-123 status done
|
./scripts/gantt.sh task update <task-uuid> status done
|
||||||
./scripts/gantt.sh task update abc-123 priority urgent
|
./scripts/gantt.sh task update <task-uuid> priority urgent
|
||||||
./scripts/gantt.sh task update abc-123 title "New title"
|
./scripts/gantt.sh task update <task-uuid> title "New title"
|
||||||
./scripts/gantt.sh task update abc-123 assigneeId <user-id>
|
./scripts/gantt.sh task update <task-uuid> assigneeId <user-uuid>
|
||||||
|
|
||||||
# Delete task
|
# Delete task
|
||||||
./scripts/gantt.sh task delete <task-id>
|
./scripts/gantt.sh task delete <task-id>
|
||||||
@ -61,7 +61,7 @@ A unified CLI that covers all API operations.
|
|||||||
|
|
||||||
# Attach file
|
# Attach file
|
||||||
./scripts/gantt.sh task attach <task-id> <file-path>
|
./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
|
### Project Commands
|
||||||
@ -78,9 +78,9 @@ A unified CLI that covers all API operations.
|
|||||||
|
|
||||||
# Update project field
|
# Update project field
|
||||||
./scripts/gantt.sh project update <project-id> <field> <value>
|
./scripts/gantt.sh project update <project-id> <field> <value>
|
||||||
./scripts/gantt.sh project update abc-123 name "New Name"
|
./scripts/gantt.sh project update <project-uuid> name "New Name"
|
||||||
./scripts/gantt.sh project update abc-123 description "New desc"
|
./scripts/gantt.sh project update <project-uuid> description "New desc"
|
||||||
./scripts/gantt.sh project update abc-123 color "#ff0000"
|
./scripts/gantt.sh project update <project-uuid> color "#ff0000"
|
||||||
|
|
||||||
# Delete project
|
# Delete project
|
||||||
./scripts/gantt.sh project delete <project-id>
|
./scripts/gantt.sh project delete <project-id>
|
||||||
@ -100,9 +100,9 @@ A unified CLI that covers all API operations.
|
|||||||
|
|
||||||
# Update sprint field
|
# Update sprint field
|
||||||
./scripts/gantt.sh sprint update <sprint-id> <field> <value>
|
./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 <sprint-uuid> name "New Sprint Name"
|
||||||
./scripts/gantt.sh sprint update abc-123 status active
|
./scripts/gantt.sh sprint update <sprint-uuid> status active
|
||||||
./scripts/gantt.sh sprint update abc-123 startDate "2026-02-25"
|
./scripts/gantt.sh sprint update <sprint-uuid> startDate "2026-02-25"
|
||||||
|
|
||||||
# Delete sprint
|
# Delete sprint
|
||||||
./scripts/gantt.sh sprint delete <sprint-id>
|
./scripts/gantt.sh sprint delete <sprint-id>
|
||||||
@ -242,13 +242,13 @@ export API_URL=http://localhost:3001/api
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Add completion comment
|
# 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
|
# Attach notes
|
||||||
./scripts/gantt.sh task attach abc-123 ./completion-notes.md
|
./scripts/gantt.sh task attach <task-uuid> ./completion-notes.md
|
||||||
|
|
||||||
# Mark done
|
# Mark done
|
||||||
./scripts/gantt.sh task update abc-123 status done
|
./scripts/gantt.sh task update <task-uuid> status done
|
||||||
```
|
```
|
||||||
|
|
||||||
### Find Tasks
|
### 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
|
- In the URL when viewing a task
|
||||||
- From `task list` output
|
- From `task list` output
|
||||||
- From `task create` output
|
- From `task create` output
|
||||||
|
- `projectId`, `sprintId`, and `assigneeId` fields are UUID values as well.
|
||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
|
|||||||
@ -356,10 +356,9 @@ export default function TaskDetailPage() {
|
|||||||
const sortedSprints = useMemo(
|
const sortedSprints = useMemo(
|
||||||
() =>
|
() =>
|
||||||
sprints
|
sprints
|
||||||
.filter((sprint) => sprint.projectId === editedTask?.projectId)
|
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => parseSprintStart(a.startDate).getTime() - parseSprintStart(b.startDate).getTime()),
|
.sort((a, b) => parseSprintStart(a.startDate).getTime() - parseSprintStart(b.startDate).getTime()),
|
||||||
[sprints, editedTask?.projectId]
|
[sprints]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleAttachmentUpload = async (event: ChangeEvent<HTMLInputElement>) => {
|
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 () => {
|
const handleSave = async () => {
|
||||||
if (!editedTask) return
|
if (!editedTask) return
|
||||||
if (!editedTask.projectId) {
|
if (!editedTask.projectId) {
|
||||||
@ -819,7 +836,7 @@ export default function TaskDetailPage() {
|
|||||||
<Label className="text-slate-400">Sprint</Label>
|
<Label className="text-slate-400">Sprint</Label>
|
||||||
<select
|
<select
|
||||||
value={editedTask.sprintId || ""}
|
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"
|
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>
|
<option value="">No Sprint</option>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user