You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#196 — PRD: Angular web client for SubVora (parity minus reminders)
What to build
The settings screen's security card: change password from inside the app. This endpoint requires a bearer token — a defect fixed server-side in #195 — which the interceptor already supplies, so the work here is the form, the mapped errors, and being explicit about what happens to other sessions.
Implementation Steps
API method — add changePassword(request) to core/api/auth-api.service.ts, matching ChangePasswordRequest on the server (current password plus new password).
Form — a security card in features/settings/settings.component.ts: current password, new password (required, min 8), confirm (client-only). Submit disabled while pending; all three cleared on success.
Store method — changePassword() in settings.store.ts: success shows a confirmation; a 400 maps onto the matching control (wrong current password, new password too short); a 401 is left to the interceptor.
Session honesty — state in the UI what the server actually does to existing refresh tokens after a password change, and if they are revoked, route to /login with an explanation rather than leaving the app in a half-signed-in state. [HITL] — confirm the intended behaviour against AuthService.ChangePasswordAsync while implementing, and make the UI match it.
Spec — settings.store.spec.ts additions: success clears the fields and confirms; a 400 attaches the message to the correct control; a confirm mismatch blocks submission with no network call.
Agent Routing
agent_routing:
complexity_hint: easyrequired_capability: balancedparallel_safe: truecost_preference: lowspeed_preference: balancedownership_scope:
- src/SubVora.Web/src/app/features/settings/**
- src/SubVora.Web/src/app/core/api/auth-api.service.tsverification:
- cd src/SubVora.Web && npx ng test --no-watch
Technical Context Snapshot
Current stack in scope
UI: Angular (20+) standalone components with signals and built-in control flow, Angular Material (Material 3) as the only component library, SCSS. Static SPA — no SSR, no service worker.
State: signal-backed injectable stores, one per domain area, mirroring the MAUI ViewModel split in src/SubVora.Mobile/ViewModels one-to-one. No NgRx.
API access: hand-written models plus one service per API controller under src/SubVora.Web/src/app/core/api. Enums travel as JSON strings (JsonStringEnumConverter in Program.cs), so TypeScript string-literal unions are exact.
Backend consumed unchanged: ASP.NET Core net10.0, /api/v1/, JWT bearer in the Authorization header, tokens in JSON bodies (no cookies).
Tests: Angular CLI unit-test builder (Vitest runner; Karma is deprecated) with HttpTestingController. Stores, interceptors, mappers and utils only — no component-DOM or browser automation.
Dependencies in scope
Reuse: @angular/*, @angular/material, rxjs, and the utilities already added under src/SubVora.Web/src/app/core. No chart library, no date library, no HTTP wrapper library.
New dependency additions allowed for this slice: no. If a dependency looks unavoidable, stop and raise it on the issue rather than adding it.
Architecture alignment
Preserve the repo's load-bearing rules (CLAUDE.md): burn-rate maths is server-side and counts cycles, never days; currency conversion is a read-time projection and stored amounts are never overwritten; nothing advances next_billing_date on a timer; provider matching stays one SQL query; the mobile SQLite cache stays a read-only mirror.
There is deliberately no shared DTO project. Web models mirror the API's JSON contract by convention — a contract change means editing both sides.
create-git-issue provides routing hints only and assigns no concrete agent or model.
run-with-it remains the final runtime routing authority.
Parent
#196 — PRD: Angular web client for SubVora (parity minus reminders)
What to build
The settings screen's security card: change password from inside the app. This endpoint requires a bearer token — a defect fixed server-side in #195 — which the interceptor already supplies, so the work here is the form, the mapped errors, and being explicit about what happens to other sessions.
Implementation Steps
changePassword(request)tocore/api/auth-api.service.ts, matchingChangePasswordRequeston the server (current password plus new password).features/settings/settings.component.ts: current password, new password (required, min 8), confirm (client-only). Submit disabled while pending; all three cleared on success.changePassword()insettings.store.ts: success shows a confirmation; a 400 maps onto the matching control (wrong current password, new password too short); a 401 is left to the interceptor./loginwith an explanation rather than leaving the app in a half-signed-in state.[HITL]— confirm the intended behaviour againstAuthService.ChangePasswordAsyncwhile implementing, and make the UI match it.settings.store.spec.tsadditions: success clears the fields and confirms; a 400 attaches the message to the correct control; a confirm mismatch blocks submission with no network call.Agent Routing
Technical Context Snapshot
Current stack in scope
src/SubVora.Mobile/ViewModelsone-to-one. No NgRx.src/SubVora.Web/src/app/core/api. Enums travel as JSON strings (JsonStringEnumConverterinProgram.cs), so TypeScript string-literal unions are exact.net10.0,/api/v1/, JWT bearer in theAuthorizationheader, tokens in JSON bodies (no cookies).HttpTestingController. Stores, interceptors, mappers and utils only — no component-DOM or browser automation.Dependencies in scope
@angular/*,@angular/material,rxjs, and the utilities already added undersrc/SubVora.Web/src/app/core. No chart library, no date library, no HTTP wrapper library.Architecture alignment
next_billing_dateon a timer; provider matching stays one SQL query; the mobile SQLite cache stays a read-only mirror.create-git-issueprovides routing hints only and assigns no concrete agent or model.run-with-itremains the final runtime routing authority.Integration touchpoints
Acceptance criteria
Blocked by