Persiste proposal, specs delta (auth, api-client, access-requests, platform-shell, testing), design con 8 ADRs, tasks (27 items en 10 fases), verify-report (28 tests, 0 críticos, 0 warnings tras el cierre) y archive-report final. Cierra el ciclo SDD del cleanup de deuda técnica del console.
3.5 KiB
Access Requests Specification
Delta for: Access Requests — console-tech-debt-cleanup
ADDED Requirements
Requirement: Failed Query Surfaces Visible Error State
When requestsQuery.isError is true, AccessRequestsPage MUST render an explicit error state. The error state MUST be visually distinguishable from an empty list. The error state MUST include a retry action that re-executes the query.
Scenario: API 500 on initial load
- GIVEN the operator navigates to the Access Requests page
- WHEN
listAccessRequestsrejects (e.g. HTTP 500) - THEN the table area is replaced by an error state element (not an empty table)
- AND the error state contains a retry button
- AND clicking the retry button calls
requestsQuery.refetch()
Scenario: API failure mid-session
- GIVEN the page has loaded successfully and shows data
- WHEN the operator triggers a manual refresh and
listAccessRequestsrejects - THEN the error state renders (replacing the previous data view)
- AND the operator can retry without reloading the full page
Requirement: isActionable Is the Single Source of Truth for Actionable Status
The RequestDetail component MUST use the isActionable(request) helper to determine whether approve, reject, and "mark as review" actions are enabled. Inline status-string arrays (['pending','in_review'].includes(...)) MUST NOT appear in AccessRequestsPage or RequestDetail. The isActionable helper is already defined in the file; this requirement mandates it be used exclusively.
Scenario: Pending request — all action buttons enabled
- GIVEN a request with
status = 'pending' - WHEN
RequestDetailrenders - THEN the "Revisar" button is enabled
- AND the "Aprobar" button is enabled
- AND the "Rechazar" button is enabled
- AND these states are derived from
isActionable(request)
Scenario: In-review request — approve and reject enabled, review disabled
- GIVEN a request with
status = 'in_review' - WHEN
RequestDetailrenders - THEN "Aprobar" and "Rechazar" are enabled
- AND "Revisar" is disabled (status is not
'pending') - AND these states are derived from
isActionable(request)
Scenario: Approved or rejected request — all action buttons disabled
- GIVEN a request with
status = 'approved'orstatus = 'rejected' - WHEN
RequestDetailrenders - THEN "Revisar", "Aprobar", and "Rechazar" are all disabled
- AND
isActionable(request)returns false
Requirement: Mutation Success Shows Toast and Invalidates Cache
Each mutation (approve, reject, mark-as-review) MUST show a toast.success message on success and MUST invalidate the ['platform-access-requests'] query key. On error, each mutation MUST show a toast.error with the error message.
Scenario: Approve mutation success
- GIVEN a request with status
pendingorin_review - WHEN the operator clicks "Aprobar" and
approveAccessRequestresolves - THEN
toast.success('Solicitud aprobada')fires - AND
queryClient.invalidateQueries({ queryKey: ['platform-access-requests'] })is called
Scenario: Reject mutation success
- GIVEN the rejection modal is open with a reason of at least 3 characters
- WHEN the operator clicks "Rechazar" and
rejectAccessRequestresolves - THEN
toast.success('Solicitud rechazada')fires - AND the modal closes
- AND the query cache is invalidated
Scenario: Mutation error
- GIVEN any mutation (
approve,reject, orreview) is triggered - WHEN the API call rejects with an
Error - THEN
toast.error(err.message)fires - AND the query cache is NOT invalidated