# Gantt Board

Task and sprint board built with Next.js + Zustand and Supabase-backed API persistence.

## Current Product Behavior

### Feb 20, 2026 updates

- Added task attachments with Supabase persistence.
- Added URL-based task detail pages (`/tasks/{taskId}`) so tasks can be opened/shared by link.
- Replaced flat notes/comments with threaded comments.
- Added unlimited nested replies (reply to comment, reply to reply, no depth limit).
- Updated UI wording from "notes" to "comments".
- Improved attachment opening/rendering by coercing MIME types (including `.md` as text) and using blob URLs for reliable in-browser viewing.
- Added lightweight collaborator identity tracking for task/comment authorship.

### Feb 21, 2026 updates

- Stabilized auth and settings flows with Supabase Auth credentials.
- Login/password validation now uses Supabase Auth credentials.
- Reset password flow no longer depends on `public.users.password_hash`.
- Settings API response handling was hardened to avoid client script errors.
- Avatar preset key collision warning in settings was fixed.

### Feb 22, 2026 updates

- Fixed sprint date timezone drift (for example `2026-02-22` rendering as `2/21` in US time zones).
- 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; sprint selection no longer rewrites task `projectId`.
- API validation now accepts UUID-shaped IDs used by existing data and returns clearer error payloads for failed writes.

### Feb 24, 2026 updates

- Standardized CLI tools to API passthrough mode only (no direct DB logic in scripts).
- Consolidated shared CLI transport into `scripts/lib/api_client.sh`.
- Added refactor safety tests:
  - sprint selection unit tests
  - mocked CLI API-contract tests across task/project/sprint/auth/debug command paths

### Feb 25, 2026 updates

- Added a dedicated `/sprints` page (previously missing, caused route 404).
- Added sprint management UI on `/sprints` to view all sprints and perform create/edit/delete actions.
- Added a dedicated `/tasks` list page with URL-based filtering and scope controls.
- Removed sprint-to-project coupling across app/API/CLI. Tasks remain project-scoped; sprints are now global.
- Added DB migration script to drop `sprints.project_id`: `supabase/remove_sprint_project_id.sql`.

### Sprint date semantics (important)

- Sprint dates are treated as local calendar-day boundaries:
  - `startDate` means local `12:00:00 AM` of that date.
  - `endDate` means local `11:59:59.999 PM` of that date.
- Supabase stores sprint dates as SQL `DATE` (`YYYY-MM-DD`) in `sprints.start_date` and `sprints.end_date`.
- API contract for `/api/sprints`:
  - Accepts date strings with a `YYYY-MM-DD` prefix (`YYYY-MM-DD` or ISO timestamp).
  - Normalizes and persists only the date part (`YYYY-MM-DD`).
  - `PATCH` returns `400` for invalid `startDate`/`endDate` format.
  - Example request payloads:
  - `POST /api/sprints`
    - `{ "name": "Sprint 1", "startDate": "2026-02-16", "endDate": "2026-02-22", "status": "active" }`
  - `PATCH /api/sprints`
    - `{ "id": "<sprint-id>", "startDate": "2026-02-16T00:00:00.000Z", "endDate": "2026-02-22T23:59:59.999Z" }`
    - persisted as `start_date = '2026-02-16'`, `end_date = '2026-02-22'`
- Do not use `new Date("YYYY-MM-DD")` for sprint display logic. Use shared helpers:
  - `parseSprintStart(...)`
  - `parseSprintEnd(...)`
  - `toLocalDateInputValue(...)`

### Data model and status rules

- Tasks use labels (`tags: string[]`) and can have multiple labels.
- Tasks support attachments (`attachments: TaskAttachment[]`).
- 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:
  - `open`
  - `todo`
  - `blocked`
  - `in-progress`
  - `review`
  - `validate`
  - `archived`
  - `canceled`
  - `done`
- New tasks default to `status: open`.

### Authentication and sessions

- Added account-based authentication with:
  - `POST /api/auth/register`
  - `POST /api/auth/login`
  - `POST /api/auth/logout`
  - `GET /api/auth/session`
- Added a dedicated login/register screen at `/login`.
- Added account settings at `/settings` for authenticated users.
- Main board (`/`) and task detail pages (`/tasks/{taskId}`) require an authenticated session.
- Main board and task detail headers include quick access to Settings and Logout.
- API routes now enforce auth on task read/write/delete (`/api/tasks` returns `401` when unauthenticated).
- Added `PATCH /api/auth/account` to update profile and password for the current user.
- Session cookie: `gantt_session` (HTTP-only, same-site lax, secure in production).
- Optional machine-to-machine auth for API routes:
  - Set `GANTT_MACHINE_TOKEN` in server env.
  - Send either `Authorization: Bearer <token>` or `x-gantt-machine-token: <token>`.
  - Optional identity fields: `GANTT_MACHINE_USER_ID`, `GANTT_MACHINE_USER_NAME`, `GANTT_MACHINE_USER_EMAIL`.
- Added `Remember me` in auth forms:
  - Checked: persistent 30-day cookie/session.
  - Unchecked: browser-session cookie (clears on browser close), with server-side short-session expiry.
- Forgot-password currently uses a dev reset-link flow unless an external email provider is integrated.
- Task/comment authorship now uses authenticated user identity.
- Added `GET /api/auth/users` so task forms can load assignable users.
- User records now include optional `avatarUrl` profile photos.

### Task assignment

- You can assign or unassign tasks in:
  - New Task modal
  - Task detail popup on board
  - URL task detail page (`/tasks/{taskId}`)
- New tasks default to the current signed-in user as assignee.
- Kanban and Backlog cards show a visible assignee avatar/initial pill.
- When someone else updates status (for example closes a task), updater fields still track who made that change.

### Profile photos and account settings

- Account Settings (`/settings`) has separate save actions:
  - `Save Profile` for name/email/photo
  - `Update Password` for password-only changes
- Profile photos can be uploaded/removed in settings.
- Users without uploaded photos get a generated default avatar (unique by user seed).
- Settings also provides multiple preset avatar choices.
- Profile photos appear in:
  - Board/task header identity chips
  - Assignee pills on Kanban and Backlog
  - Threaded comment author avatars
  - Creator/updater identity rows on task detail views
- Avatar updates persist in Supabase profile tables and propagate through session/user APIs.

### Labels

- Labels are independent of project/sprint selection.
- You can add/remove labels inline in:
  - New Task modal
  - Task Detail page
- Label entry supports:
  - Enter/comma to add
  - Existing-label suggestions
  - Quick-add chips
  - Case-insensitive de-duplication

### Task detail pages

- Clicking a task from Kanban or Backlog opens a dedicated route:
  - `/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.

### Task list route

- Added a dedicated task list route at `/tasks`.
- Supported query params:
  - `scope=active-sprint|all` (default: `all`)
  - `status=<comma-separated status list>` (for example `in-progress,review`)
  - `priority=<comma-separated priority list>` (for example `urgent,high`)
- Unknown status/priority tokens are ignored.
- Filtering is client-side against the synced task dataset.
- Results are sorted by `updatedAt` descending.

### Attachments

- Task detail page supports adding multiple attachments per task.
- Attachments are stored with each task in Supabase and survive refresh/restart.
- Attachment UI supports:
  - Upload multiple files
  - Open/view file
  - Markdown (`.md`) preview rendering in browser tab
  - Text/code preview for common file types including iOS project/code files (`.swift`, `.plist`, `.pbxproj`, `.storyboard`, `.xib`, `.xcconfig`, `.entitlements`)
  - Syntax-highlighted source rendering (highlight.js) with copy-source action
  - CSV table preview and sandboxed HTML preview with source view
  - Download file
  - Remove attachment

### Threaded comments

- Comments are now threaded, not flat.
- You can:
  - Add top-level comments
  - Reply to any comment
  - Reply to replies recursively (unlimited depth)
  - Delete any comment or reply from the thread
- Thread data is persisted in Supabase through the existing `/api/tasks` sync flow.

Current persisted comment shape:

```json
{
  "id": "string",
  "text": "string",
  "createdAt": "2026-02-23T19:00:00.000Z",
  "commentAuthorId": "user-uuid-or-assistant",
  "replies": []
}
```

- `commentAuthorId` is required.
- Legacy `author` objects are not supported by the UI parser.

### Backlog drag and drop

Backlog view supports moving tasks between:
- Current sprint
- Other sprint sections
- Backlog

Status behavior on drop:
- Drop into any sprint section: `status -> open`
- Drop into backlog section: `status -> open`
- `sprintId` is set/cleared based on destination

Changes persist through store sync to Supabase.

### Kanban drag and drop

Kanban supports drag by visible left handle on each task card.

Columns:
- `To Do` contains statuses: `open`, `todo`
- `In Progress` contains statuses: `blocked`, `in-progress`, `review`, `validate`
- `Done` contains statuses: `archived`, `canceled`, `done`

Drop behavior:
- Drop on column body: applies that column's default status
  - `To Do` -> `open`
  - `In Progress` -> `in-progress`
  - `Done` -> `done`
- Drop on status chip target: applies exact status
- Drop on a task: adopts that task's exact status

During drag, the active target column shows expanded status drop zones for clarity.

### Layout

- Left sidebar cards (Current Sprint and Labels quick view) were removed.
- Main board now uses full width for Kanban/Backlog views.

## Persistence

- Client state is managed with Zustand.
- 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

```bash
npm install
npm run dev
```

Open `http://localhost:3000`.
Task URLs follow `http://localhost:3000/tasks/{taskId}`.

## Scripts

```bash
npm run dev
npm run build
npm run start
npm run lint
```

## CLI Tools for AI Access

Located in `scripts/` directory. These allow programmatic access without browser automation.

```bash
# Full wrapper
./scripts/gantt.sh task list open
./scripts/gantt.sh task create "Fix login issue" todo high p1
./scripts/gantt.sh sprint close <sprint-id>
./scripts/gantt.sh auth session

# Focused helpers
./scripts/task.sh list --json
./scripts/project.sh list --json
./scripts/sprint.sh list --active --json
./scripts/attach-file.sh <task-id> <file-path>
./scripts/view-attachment.sh <task-id> [index]
```

**Architecture:** CLI is API passthrough only. Business logic lives in API/server code.

**Docs:**
- `scripts/README.md` (command details and env vars)
- `docs/API_CLI_PASSTHROUGH_PATTERN.md` (refactor/architecture standard)

**Refactor test suite:**

```bash
npm run test:refactor
```

## Notes

- You may see a Next.js warning about multiple lockfiles and inferred workspace root. This is non-blocking for local dev.
