# API administrativa: solicitudes de registro

Contrato para la SPA React que consulta y revisa solicitudes. Complementa `authentication-api.md`; este documento es la fuente de verdad del módulo.

## Acceso

Todas las rutas requieren sesión Sanctum. Usar `GET /api/v1/auth/me` al iniciar la SPA y decidir la UI con `user.permissions`, nunca solo con roles.

| UI | Permiso |
|---|---|
| Sidebar y listado | `registration_requests.view_any` |
| Detalle | `registration_requests.view` |
| Aprobar | `registration_requests.approve` |
| Rechazar | `registration_requests.reject` |

Un `dependent` no tiene estos permisos. Antes de cada `POST`, pedir `GET /sanctum/csrf-cookie` usando el cliente con `withCredentials` y `withXSRFToken`.

Todos los identificadores que devuelve o recibe este módulo son UUID públicos, nunca IDs numéricos.

## Listado

`GET /api/v1/admin/registration-requests` requiere `registration_requests.view_any`.

| Parámetro | Reglas |
|---|---|
| `page` | entero, mínimo 1 |
| `per_page` | entero entre 1 y 100; predeterminado 15 |
| `status` | `pending`, `approved`, `rejected`, `cancelled` |
| `search` | nombre, apellido, nombre completo, username, correo, cédula, WhatsApp, código o nombre de farmacia |
| `pharmacy_id` | UUID público de farmacia |
| `date_from`, `date_to` | `YYYY-MM-DD`, aplicado a `requested_at`; `date_to` incluye todo el día |
| `sort` | `requested_at`, `first_name`, `last_name`, `username`, `status`, `approved_at`, `rejected_at` |
| `direction` | `asc` o `desc` |

El orden predeterminado es `requested_at desc`. Reiniciar a `page=1` al cambiar filtros, búsqueda u orden.

```text
GET /api/v1/admin/registration-requests?status=pending&search=FAR001&page=1&per_page=15
```

Respuesta `200`:

```json
{
  "success": true,
  "message": "Solicitudes de registro obtenidas correctamente.",
  "data": [{
    "id": "8d20d56e-83ce-48c7-a713-b26aee541a42",
    "first_name": "Albert",
    "last_name": "Mata",
    "full_name": "Albert Mata",
    "username": "albertmat34",
    "email": "albert@example.com",
    "identification_number": "00112345678",
    "whatsapp": "8095551234",
    "city": "Santo Domingo",
    "nickname": "Beto",
    "status": "pending",
    "requested_at": "2026-07-14T14:30:00.000000Z",
    "approved_at": null,
    "rejected_at": null,
    "pharmacy": { "id": "e9331f00-8974-487c-a530-85416046c503", "code": "FAR001", "name": "Farmacia Ejemplo", "city": "Santo Domingo" },
    "reviewer": null
  }],
  "meta": { "current_page": 1, "per_page": 15, "total": 1, "last_page": 1 }
}
```

## Detalle

`GET /api/v1/admin/registration-requests/{id}` requiere `registration_requests.view`.

Además de los campos del listado, devuelve `phone`, `terms_accepted_at`, `rejection_reason`, `created_user`, `request_ip` y `request_user_agent`. Las fechas son ISO 8601. Mostrar IP y user agent solo en la vista administrativa de detalle; no en tablas, búsqueda, exports ni almacenamiento del navegador.

`created_user` es `null` hasta aprobar. Después contiene el mismo contrato público del usuario autenticado, incluyendo `roles`, `permissions` y `pharmacy`.

## Aprobar

`POST /api/v1/admin/registration-requests/{id}/approve` requiere `registration_requests.approve`.

No enviar payload. No enviar rol, farmacia, contraseña, estado ni fechas. Si sigue `pending`, el backend crea el usuario activo con la farmacia guardada, conserva su hash de contraseña y asigna el rol fijo `dependent`.

Respuesta `200`: mensaje `Solicitud aprobada y usuario creado correctamente.` y el objeto de detalle con `status: "approved"` y `created_user` poblado.

## Rechazar

`POST /api/v1/admin/registration-requests/{id}/reject` requiere `registration_requests.reject`.

```json
{ "rejection_reason": "La información suministrada no pudo ser validada." }
```

El motivo se recorta y debe tener de 3 a 1000 caracteres no vacíos. Respuesta `200`: mensaje `Solicitud rechazada correctamente.` y detalle con `status: "rejected"`, `rejected_at`, `reviewer` y `rejection_reason`.

## Errores y comportamiento

| HTTP/código | Acción de la SPA |
|---|---|
| `401 UNAUTHENTICATED` | Limpiar sesión local y redirigir a login. |
| `403 FORBIDDEN` | Mostrar acceso denegado; no reintentar. |
| `404 RESOURCE_NOT_FOUND` | Informar y volver al listado. |
| `409 REGISTRATION_REQUEST_ALREADY_REVIEWED` | La revisó otro administrador o dejó de estar pendiente; recargar detalle/listado sin reintentar. |
| `422 VALIDATION_ERROR` | Mostrar `errors[campo]`. |
| `422 REGISTRATION_DATA_ALREADY_EXISTS` | Mantener pendiente la solicitud y mostrar los conflictos de cédula, username o correo. |

La concurrencia es normal: no asumir que una solicitud visualizada como `pending` seguirá pendiente al confirmar la acción.
