Full-stack web application for structured weekly work reports, manager review workflows, and team performance dashboards.
Team members create and submit weekly reports that capture tasks, achievements, blockers, and plans for the next week. Managers review submitted reports, request corrections, approve reports, and manage team catalog data (users, projects, task types). The system supports report versioning, review history, and status history through the full lifecycle:
DRAFT → SUBMITTED → NEEDS_CORRECTION → SUBMITTED → APPROVED
| Role | Capabilities |
|---|---|
| Team Member | Own reports (create, draft, edit, submit, resubmit), dashboard, report history, version snapshots |
| Manager | Team reports and review actions, manager dashboard, user management, projects, task types |
New accounts registered through the UI are created as Team Member only.
| Layer | Stack |
|---|---|
| Frontend | React, TypeScript, Vite, Tailwind CSS, React Router, React Hook Form, Zod, Recharts |
| Backend | Node.js, Express, TypeScript, Zod validation, JWT in HTTP-only cookies |
| Database | PostgreSQL via Prisma ORM |
| Backend tests | Jest, Supertest |
| Frontend tests | Vitest, jsdom, Testing Library (focused session/API tests) |
| Browser QA | Playwright (scripts in scripts/) |
Request flow (frontend):
Pages and components → domain services under frontend/src/services/ → apiRequest() in frontend/src/services/api/client.ts (credentials included) → REST API at /api/v1/* → Express controllers → services → Prisma → PostgreSQL.
The backend enforces authentication, authorization, validation, and report workflow rules. The frontend provides UX and client-side validation only.
- Access token: JWT stored in an HTTP-only cookie (
accessToken). There is no refresh token; the backend validates the cookie on each protected request. - Bootstrap: On load,
AuthProvidercallsGET /api/v1/auth/meto restore the session. - API calls: All domain services use
apiRequest()(frontend/src/services/api/client.ts) withcredentials: "include". - 401 handling: When any
apiRequest()receives HTTP 401, it callsdispatchUnauthorizedSession()(unauthorized-session.ts). The listener registered inAuthProviderclears the user and sets status to unauthenticated. Route guards (ProtectedRoute) then redirect to/loginwithstate.fromset to the attempted path. Login success can return the user to that path. - Backend inactive users:
requireAuthrejects inactive or missing users with 401 and clears the cookie. Login for inactive accounts returns 401 (same message shape as invalid credentials).
Route guards are UX only; authorization is enforced on the backend.
weekly-report-generator/
├── frontend/ # React SPA
│ └── src/
│ ├── pages/ # Route-level pages (auth, member, manager)
│ ├── components/ # Reusable UI and feature components
│ ├── services/ # API client and domain API modules
│ ├── routes/ # Router, paths, route guards
│ ├── contexts/ # Auth session context
│ ├── hooks/ # Shared hooks (e.g. report catalog)
│ ├── schemas/ # Zod form schemas
│ ├── types/ # TypeScript types
│ ├── test/ # Vitest setup (jest-dom matchers)
│ └── utils/ # Helpers (dates, workflow, API errors)
├── frontend/vitest.config.mjs # Vitest config (separate from Vite build)
├── backend/
│ ├── src/ # Express app, routes, controllers, services, middleware
│ ├── prisma/
│ │ ├── schema.prisma # Database schema
│ │ ├── migrations/ # SQL migrations
│ │ └── seed.ts # Development seed data
│ └── tests/ # Jest integration tests
├── scripts/ # Playwright QA scripts (qa-6.1 … qa-6.4)
├── package.json # npm workspaces (frontend + backend)
└── README.md
- Node.js 20+ (project tested with current LTS-style versions)
- npm 10+ (workspaces)
- PostgreSQL running locally (or reachable via
DATABASE_URL) - Playwright Chromium (only for QA scripts):
npx playwright install chromiumfrom thefrontendworkspace
Copy example files and adjust for your machine. Do not commit real secrets.
Create from backend/.env.example:
| Variable | Purpose |
|---|---|
PORT |
API port (default 3000) |
NODE_ENV |
e.g. development or production |
FRONTEND_URL |
Browser origin for CORS (default http://localhost:5173) |
DATABASE_URL |
PostgreSQL connection string for Prisma |
JWT_SECRET |
Secret for signing access tokens (use a strong value outside local dev) |
JWT_EXPIRES_IN |
Token lifetime (example: 1h) |
Create from frontend/.env.example:
| Variable | Purpose |
|---|---|
VITE_API_BASE_URL |
Backend origin, no trailing slash (default http://localhost:3000) |
From the repository root:
npm installEnsure PostgreSQL is running and DATABASE_URL in backend/.env points to your database.
From the repository root (or backend/ workspace):
npm run db:generate --workspace=backend
npm run db:migrate --workspace=backendOther useful backend database commands:
npm run db:validate --workspace=backend
npm run db:seed --workspace=backendIntegration tests use a separate test database configured in the backend test setup (backend/tests/setup/).
Seed the development database:
npm run db:seed --workspace=backendThe seed defines a shared development password constant in backend/prisma/seed.ts (SEED_PASSWORD). All seeded users use that password. The same value is used by the Playwright QA scripts.
| Role | |
|---|---|
sarah.chen@example.com |
Manager |
alex.jordan@example.com |
Team Member |
morgan.lee@example.com |
Team Member |
riley.patel@example.com |
Team Member |
casey.nguyen@example.com |
Team Member |
Development password: Password123! (defined in seed and QA scripts; change only for non-local environments.)
The seed also creates sample projects, task types, and reports in various workflow states for demos and QA.
Start the backend and frontend in separate terminals from the repository root:
npm run dev:backend
npm run dev:frontendDefault URLs:
- Frontend: http://localhost:5173
- Backend API: http://localhost:3000
- Health check: http://localhost:3000/health
Paths match frontend/src/routes/paths.ts and frontend/src/routes/router.tsx.
| Path | Description |
|---|---|
/login |
Sign in |
/register |
Register as team member |
| Path | Description |
|---|---|
/member/dashboard |
Member dashboard |
/member/reports |
My reports list |
/member/reports/new |
Create report |
/member/reports/:reportId |
Report detail / edit / submit |
/member/reports/history |
Report history |
/member/reports/:reportId/versions |
Version list |
/member/reports/:reportId/versions/:versionNumber |
Version snapshot |
| Path | Description |
|---|---|
/manager/dashboard |
Manager dashboard and charts |
/manager/reports |
Team reports |
/manager/reports/:reportId |
Review, approve, request correction, comments, history |
/manager/review |
Review hub (links to team reports) |
/manager/users |
User list |
/manager/users/:userId |
User detail / activate / deactivate |
/manager/projects |
Project list |
/manager/projects/new |
Create project |
/manager/projects/:projectId |
Project detail / edit / deactivate |
/manager/task-types |
Task type list |
/manager/task-types/new |
Create task type |
/manager/task-types/:taskTypeId |
Task type detail / edit / deactivate |
After sign-in, / redirects to the role-appropriate dashboard.
Unknown URLs show a 404 page when matched by the public or authenticated catch-all routes (see NotFoundPage).
Base path: /api/v1. JSON bodies; success responses use { data: ... } or { data: ..., pagination: ... }. Errors use { error: { code, message } }.
| Area | Methods | Notes |
|---|---|---|
| Auth | POST /auth/register, POST /auth/login, POST /auth/logout, GET /auth/me |
Login sets cookie; /me requires auth |
| Reports | GET/POST /reports, GET/PATCH /reports/:id, POST /reports/:id/submit |
Create: team members only; workflow via explicit actions |
| Versions | GET /reports/:id/versions, GET /reports/:id/versions/:versionNumber |
Snapshots on submit/resubmit |
| Reviews | POST .../request-correction, POST .../approve, POST .../comments, GET .../reviews, GET .../status-history |
Manager actions; owners can read reviews/history |
| Dashboard | GET /dashboard/summary, task-trends, status-by-member, workload-by-project, time-by-task-type, recent-activity |
Manager only; optional weekStartDate (Monday UTC) |
| Users | GET /users, GET/PATCH /users/:id |
Manager |
| Projects | GET/POST /projects, GET/PATCH/DELETE /projects/:id |
DELETE deactivates; authenticated read |
| Task types | GET/POST /task-types, GET/PATCH/DELETE /task-types/:id |
Same pattern as projects |
Reporting weeks use Monday–Sunday UTC dates. One report per user per weekStartDate.
OpenAPI/Swagger is not included in this repository; use this table and backend route modules under backend/src/routes/ for reference.
From the repository root:
npm run test --workspace=frontend # Vitest (session / apiRequest / route guards)
npm run lint --workspace=frontend
npm run build --workspace=frontend
npm run build --workspace=backend
CI=1 npm run test --workspace=backend # Jest + SupertestSetting CI=1 avoids Jest Watchman issues in some environments.
Frontend (Vitest): Tests live next to source as *.test.ts / *.test.tsx. Config: frontend/vitest.config.mjs. Setup: frontend/src/test/setup.ts. TypeScript for tests: frontend/tsconfig.vitest.json. Coverage is intentionally narrow (401 session pipeline and related guards), not full UI coverage.
Backend (Jest): Integration tests under backend/tests/ (auth, reports workflow, catalog access, dashboard, report-week utils). Create backend/.env.test from backend/.env.test.example (or set TEST_DATABASE_URL to a PostgreSQL database whose name contains _test). Jest runs prisma migrate deploy against that database in global setup.
- Migrate/seed dev DB if needed.
npm run test --workspace=frontendandCI=1 npm run test --workspace=backend- Lint and build both workspaces
- With dev servers running (
dev:frontend,dev:backend), run Playwright scripts (below)
Production preview (frontend):
npm run preview --workspace=frontendRequires frontend on port 5173 and backend on port 3000, seeded database, and Chromium installed (npx playwright install chromium from frontend/).
| Script | Focus |
|---|---|
scripts/qa-6.1-auth.mjs |
Login, logout, RBAC route protection, state.from, forbidden banner |
scripts/qa-6.2-reports.mjs |
Member report create, draft, submit, detail, history |
scripts/qa-6.3-manager.mjs |
Manager review, approve, correction, team reports |
scripts/qa-6.4-integration.mjs |
Cross-role integration, lifecycle, dashboard, catalog, errors, responsive checks |
Run from the frontend directory (Playwright is a devDependency of frontend):
cd frontend
node ../scripts/qa-6.1-auth.mjs
node ../scripts/qa-6.2-reports.mjs
node ../scripts/qa-6.3-manager.mjs
node ../scripts/qa-6.4-integration.mjsOptional environment overrides used by some scripts:
QA_FRONTEND_URL(defaulthttp://localhost:5173)QA_API_URL(defaulthttp://localhost:3000)
Re-running QA may skip steps when seed data no longer matches preconditions (e.g. a report already approved). Re-seed if you need a clean demo state.
- Member dashboard metrics are computed from at most the first 100 reports returned by the list API; a footnote appears when the member has more reports than loaded.
- Session expiry UX: protected API
401responses clear client auth state and redirect to sign-in via route guards; individual requests may still surface a brief inline error before redirect. - Frontend lint: oxlint may report React Fast Refresh and
set-state-in-effectwarnings; builds still succeed. - Frontend bundle size: production build may warn about a large JS chunk (Recharts and app code); no code-splitting milestone was applied.
- Deactivated users: login is blocked for inactive accounts; an existing session cookie is rejected on the next protected API request (
401, cookie cleared server-side). - Development database: QA and manual testing can mutate report states; use
db:seedto restore predictable demo data.
npm run buildOr per workspace:
npm run build:frontend
npm run build:backendBackend start after build:
npm run start --workspace=backendEnsure production environment variables and a migrated database are configured before deploying.