Files
henryor af5535dfcc docs(openspec): archiva el change console-tech-debt-cleanup
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.
2026-05-07 21:14:56 -05:00

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 listAccessRequests rejects (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 listAccessRequests rejects
  • 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 RequestDetail renders
  • 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 RequestDetail renders
  • 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' or status = 'rejected'
  • WHEN RequestDetail renders
  • 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 pending or in_review
  • WHEN the operator clicks "Aprobar" and approveAccessRequest resolves
  • 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 rejectAccessRequest resolves
  • THEN toast.success('Solicitud rechazada') fires
  • AND the modal closes
  • AND the query cache is invalidated

Scenario: Mutation error

  • GIVEN any mutation (approve, reject, or review) is triggered
  • WHEN the API call rejects with an Error
  • THEN toast.error(err.message) fires
  • AND the query cache is NOT invalidated