geocrop-platform./apps/nextgen/.harness/plans/stubs-sprint-1b/A.md

6.3 KiB

Track A — Identity & Access

Owner: Person A Branch: fix/stubs-identity-access (worktree at .worktrees/stubs-identity-access/) Files in scope: client/src/pages/Login.tsx, client/src/pages/Profile.tsx, client/src/pages/Teachers.tsx, client/src/pages/admin/Users.tsx No backend changes unless explicitly noted in the task.

Conventions (from AGENTS.md)

  • Work in a worktree branched from dev. Do not edit the main checkout.
  • Use the shared axios instance at client/src/store/api.ts.
  • Route gating stays in App.tsx via <ProtectedRoute allowedRoles> — do not gate inside pages.
  • PWA service worker is only built by npm run build (not npm run dev).
  • .env is gitignored. Never commit secrets.
  • Do not commit or push without an explicit "go" from the reviewer.

Tasks

A1. Login — Password recovery button

  • File: client/src/pages/Login.tsx:208-213
  • Current: "Recovery" link calls alert('Password recovery is not yet available. Please contact your administrator.').
  • Decision (pick one, ask backend if needed):
    • (a) Backend exists — wire to POST /auth/forgot-password. Show success/error message in-place.
    • (b) Backend does not exist — demote the control to a non-button text label, or render it disabled with title="Contact your administrator". Do not pretend the action does something.
  • Acceptance: Clicking Recovery either triggers a real request or visibly does nothing. No alert().

A2. Profile — Avatar upload

  • File: client/src/pages/Profile.tsx:147-167 (TODO comment at :156)
  • Current: File picker opens, reads file to a data URL, logs console.warn('Avatar selected but upload endpoint not yet available. Local preview only.'). The selected image is never displayed or persisted.
  • Desired: After picking a file, the avatar in the header updates and the change persists across reloads.
  • Decision (check backend first):
    • (a) POST /users/:id/avatar exists — upload the file (or data URL) to that route and use the returned URL.
    • (b) No upload endpoint — read file to data URL and PUT /users/:id with avatar_url. If PUT does not accept that field, fall back to localStorage keyed on user.id (clearly document in code that this is the interim store).
  • Acceptance: Picking a JPG changes the avatar in the header and survives a full page reload.

A3. Profile — Request Leave form

  • File: client/src/pages/Profile.tsx:358-382
  • Current: "Request Leave" button fires three sequential window.prompt(...) calls (start date, end date, leave type), then POST /hr/leave-requests. Replace with a single modal.
  • Desired: Modal with three fields: start_date (date input), end_date (date input, must be ≥ start), leave_type (select: Annual / Sick / Maternity / Other). Validate end ≥ start before submit. On 2xx, show an in-modal success and call loadStaffData(). On error, show inline error.
  • Acceptance: No window.prompt. End-before-start triggers an inline validation error, not a request to the server.

A4. Profile — Leave record info button

  • File: client/src/pages/Profile.tsx:422-426
  • Current: Per-row info icon calls alert(\Leave record {lr.uid}:\nType: {lr.leave_type}\nDates: {lr.start_date} → {lr.end_date}\nStatus: ${lr.status}`)`.
  • Desired: Replace with either (a) a popover anchored to the row, or (b) a small modal. Show the same four fields, formatted (new Date(...).toLocaleDateString()).
  • Acceptance: Clicking the info icon opens a popover/modal. No alert().

A5. Teachers — Department filter button

  • File: client/src/pages/Teachers.tsx:222-241
  • Current: "Departmental Sync" button opens a window.prompt listing department IDs and asks the teacher to type one in. Awful UX.
  • Desired: Replace with a <select> of departments (place it next to the existing search box). Add departmentFilter: number | 'all' state, filter filteredTeachers by t.department_id === departmentFilter when set, and include "All" option.
  • Acceptance: No window.prompt. Selecting a department narrows the table live.

A6. Teachers — Reset password button

  • File: client/src/pages/Teachers.tsx:331-352
  • Current: Per-row Key icon calls window.prompt('Reset password for …') and sends it via PUT /users/:id/reset-password. Success and failure both alert().
  • Desired: Small "Reset password" modal containing one password input (min length 6) with a generated "auto-fill temporary password" helper button. Drop both alert()s — use inline modal status (loading / success / error).
  • Acceptance: No window.prompt. No alert() for success or error.

A7. Admin Users — Reset credentials button

  • File: client/src/pages/admin/Users.tsx:123-137
  • Current: Same window.prompt pattern as A6, with three alert() calls (:127, :133, :135).
  • Desired: Share a component with A6 — extract client/src/components/ResetPasswordModal.tsx, parameterised by user and the endpoint URL (or just by userId). Use it from both A6 and A7.
  • Acceptance: Same modal in both pages. No window.prompt. No alert().

Cross-cutting checks

  • grep -rn "alert(" client/src/pages/Login.tsx client/src/pages/Profile.tsx client/src/pages/Teachers.tsx client/src/pages/admin/Users.tsx returns no hits left in Track A scope.
  • grep -rn "window.prompt" client/src/pages/Login.tsx client/src/pages/Profile.tsx client/src/pages/Teachers.tsx client/src/pages/admin/Users.tsx returns no hits left.
  • npm run build is green in your worktree.

Manual test

  1. Log in as admin@school.com / admin123.
  2. Profile tab → upload an avatar (A2), request leave (A3), click a leave row's info icon (A4).
  3. Log out, on the Login page click Recovery (A1).
  4. Log back in. Teachers page → use the new department dropdown (A5) and reset a teacher's password (A6).
  5. Admin → Users → reset a user's credentials (A7, same modal as A6).
  6. No browser alert, confirm, or prompt dialog should appear anywhere.

Out of scope here (other tracks)

  • Fees.tsx, MyCourses.tsx, AdminDashboard.tsx, ExamEditor.tsx, Settings.tsx alert/soundness work — see Track B and C.
  • General "swap all alert() to toast" pass across remaining controllers (HRManagement, Inventory, Library, etc.) — Track C's C3 lays the toast foundation; the sweep is a follow-up.