The committed .ts/.tsx files were written through a shell heredoc that stripped every quote character, so types.ts, App.tsx, api.ts, TaskList.tsx, TaskRow.tsx, TaskCard.tsx and AppShell.tsx were not valid TypeScript. Nothing surfaced this because index.js imported ./App, which resolved to the Create React App template App.js ahead of App.tsx, and typescript was never added as a dependency, so the broken files were never compiled. The preview was showing the CRA starter page. Rewritten and transferred as a binary archive rather than through a shell, with tsc and the test suite as the check that the files actually parse. Priority 1 (blocking) - TaskForm.tsx implemented: title, description, assignee, status, due date and priority, validated with react-hook-form and actionable messages. - App.tsx is now a real container wired to the service layer, replacing the hardcoded mock markup. Filtering by My Tasks / All Tasks / Blocked is driven by the signed-in user instead of a hardcoded id. - Edit, delete and inline status changes work from both the table and the cards. Priority 2 - Accessibility: skip link, ARIA tablist with arrow keys, table roles, dialog with focus trap and restore, aria-invalid and aria-describedby on fields, polite live region, 44px targets. Contrast corrected to meet AA: buttons use #2563eb and accent text #1d4ed8, since the mockup #3b82f6 reaches only 3.7:1 behind white text. - Error handling: ErrorBoundary, load failure with retry, per-filter empty states, blocked guidance, and save failures that keep the dialog and the user's input on screen. - 17 integration tests replace the CRA placeholder test. Removed the dead CRA template files (App.js, index.js, logo.svg, reportWebVitals.js, App_temp.css) and added tsconfig.json plus typescript. Verified in devbox 306: tsc --noEmit clean, 17/17 tests pass, CI build compiles with no lint warnings. Screenshots of all six states are in frontend/preview/.
103 lines
4.4 KiB
Markdown
103 lines
4.4 KiB
Markdown
# TaskFlow frontend
|
|
|
|
React 19 + TypeScript single-page app built with Create React App, implementing
|
|
the approved mockups in `architect/01-mockups/` and the contract in
|
|
`architect/03-feasibility/frontend-handoff.md`.
|
|
|
|
## Commands
|
|
|
|
Run these from `frontend/`:
|
|
|
|
| Command | What it does |
|
|
| --- | --- |
|
|
| `npm install` | Install dependencies |
|
|
| `npm start` | Dev server with hot reload (`HOST=0.0.0.0 PORT=5173` to expose it) |
|
|
| `npm run build` | Production build into `build/` |
|
|
| `npm test` | Jest + React Testing Library, watch mode |
|
|
| `npm run test:ci` | Single non-interactive test run |
|
|
| `npm run typecheck` | `tsc --noEmit` |
|
|
|
|
`CI=true npm run build` treats lint warnings as errors, which is how the build is
|
|
verified before hand-off.
|
|
|
|
## Layout
|
|
|
|
```
|
|
src/
|
|
├── App.tsx container: data loading, filtering, save/delete
|
|
├── App.css reset, design tokens, shared primitives
|
|
├── types.ts Task/User types and the labels used in the UI
|
|
├── services/api.ts REST client (currently an in-memory stand-in)
|
|
└── components/
|
|
├── common/
|
|
│ ├── AppShell.tsx header, team sidebar, main region
|
|
│ ├── TaskFields.tsx avatar, priority, status control, due date
|
|
│ └── ErrorBoundary.tsx render-crash recovery screen
|
|
├── TaskList/
|
|
│ ├── TaskList.tsx filter tabs, empty states, blocked guidance
|
|
│ ├── TaskRow.tsx desktop table row
|
|
│ └── TaskCard.tsx mobile card
|
|
└── TaskForm/TaskForm.tsx create/edit dialog with validation
|
|
```
|
|
|
|
## Backend contract
|
|
|
|
`src/services/api.ts` is the only file that talks to the server. It is an
|
|
in-memory stand-in whose method names and payloads match the documented
|
|
endpoints, so the Backend phase can replace the method bodies with `fetch` or
|
|
`axios` calls without touching a component:
|
|
|
|
| Method | Endpoint |
|
|
| --- | --- |
|
|
| `getCurrentUser()` | `GET /api/auth/me` |
|
|
| `getUsers()` | `GET /api/users` |
|
|
| `getTasks()` | `GET /api/tasks` |
|
|
| `createTask(draft)` | `POST /api/tasks` |
|
|
| `updateTask(id, changes)` | `PUT /api/tasks/:id` |
|
|
| `deleteTask(id)` | `DELETE /api/tasks/:id` |
|
|
|
|
Draft validation lives in `validateDraft` inside that module as well as in the
|
|
form, because the server is the real trust boundary and the same rules have to
|
|
hold there.
|
|
|
|
## States covered
|
|
|
|
Screenshots of each are in `preview/`.
|
|
|
|
- **Loading** — spinner with `role="status"` while the first fetch runs.
|
|
- **Load failure** — `role="alert"` with the reason and a Try again button.
|
|
- **Empty** — distinct copy for no tasks at all, nothing assigned to you, and
|
|
nothing blocked, so an active filter is never mistaken for an empty account.
|
|
- **Blocked** — explanatory notice above the list, per the blocked-state mockup.
|
|
- **Validation** — per-field messages tied to inputs with `aria-describedby`.
|
|
- **Save failure** — the dialog stays open with the user's input intact.
|
|
|
|
## Accessibility
|
|
|
|
Targeting WCAG 2.1 AA.
|
|
|
|
- Skip link to the task list; single visible focus style via `:focus-visible`.
|
|
- Filter tabs are an ARIA tablist with arrow-key navigation and counts in the
|
|
accessible name.
|
|
- The task table exposes `table`/`rowgroup`/`row`/`columnheader`/`cell` roles.
|
|
- The dialog is `role="dialog" aria-modal="true"`, focuses the title field on
|
|
open, traps Tab, closes on Escape, and returns focus to whatever opened it.
|
|
- Errors use `role="alert"` and `aria-invalid`; confirmations go to a polite
|
|
live region.
|
|
- Interactive targets are at least 44px.
|
|
- Contrast: the mockup accent `#3b82f6` only reaches 3.7:1 behind white text, so
|
|
filled buttons use `#2563eb` (5.2:1) and accent text `#1d4ed8` (6.4:1). Amber
|
|
status text was darkened to `#92400e` for the same reason.
|
|
|
|
Still to do by hand, since they cannot be automated here: screen reader passes
|
|
(NVDA/VoiceOver), 200% browser zoom, and cross-browser checks on Firefox, Safari
|
|
and Edge. Note that `@axe-core/react` is listed as a dependency but is not wired
|
|
up: version 4 relies on `ReactDOM.findDOMNode`, which React 19 removed.
|
|
|
|
## Responsive behaviour
|
|
|
|
One breakpoint at 768px. The desktop table and the mobile cards are both
|
|
rendered and CSS picks one, so the hidden view is also removed from the
|
|
accessibility tree and no task is announced twice. Below 768px the team sidebar
|
|
is hidden and Create Task becomes a floating action button.
|