Apariencia
Admin — Navegación, formularios y listas (UI)
Snapshot de la interfaz del panel admin (camaroneras_admin). Se actualiza cuando un módulo cambia la UI (parte del cierre de cada tarea con UI).
Navegación y rutas
| Ruta | Tipo | Guard / acceso |
|---|---|---|
/login | pública (GuestRoute) | si hay sesión → /dashboard; sin link de registro (alta solo por invitación) |
/onboarding | registro | requiere onboardingToken (si no → /login); nombra la organización en la alta delegada |
/forgot-password, /reset-password | públicas (GuestRoute) | recuperación |
/accept-invitation?token= | pública | sin token → /login |
/dashboard | protegida | ProtectedRoute (sesión) |
/piscinas | protegida | ProtectedRoute + piscinas:ver (sin permiso → /dashboard) |
Gestión de usuarios/roles: no vive en este repo. La gestión cross-tenant de usuarios/roles de una organización se hace desde
camaroneras_platform(/organizations/:id/members,/organizations/:id/roles, superadmin) — verplataforma/superadmin-flow.md.
- Layout (AppLayout): sidebar en desktop, topbar + drawer (Sheet) en mobile. El menú es una lista plana de ítems, filtrada según los permisos del usuario (hook
usePermission). Avatar con iniciales + logout.- Sidebar colapsable (solo desktop): toggle que alterna entre menú completo (
w-64) y rail de solo íconos (w-16, con tooltips). El drawer mobile no se colapsa. - Preferencias de UI persistentes (
localStorage, vía hookuseLocalStorageenshared/hooks/): colapso del sidebar (clavesidebar-collapsed). SilocalStorageno está disponible, cae a memoria (la UI no se rompe, solo no persiste entre recargas).
- Sidebar colapsable (solo desktop): toggle que alterna entre menú completo (
- SessionRestore envuelve el router: al recargar, hace silent refresh con la cookie antes de pintar (evita parpadeo a /login).
- Gate de cliente web: en login, si
clientsno incluyeweb→ no entra ("usa la app móvil").
Componentes de formulario (convención)
En src/shared/components/, reutilizables, con label + error + aria:
- FormField — input (text/email/number/date) con label y error.
- PasswordField — password con botón mostrar/ocultar.
- FormSelect — select (Base UI) que recibe
options: {value,label}[]. - FormCombobox — select con búsqueda (Base UI Combobox), mismo contrato que FormSelect.
El
<Select>crudo solo se usa en ediciones inline de tabla (sin label), no en formularios.
Formularios y sus campos
Auth
- Login: email, contraseña (PasswordField). Sin link de registro (alta solo por invitación); link ¿olvidaste contraseña?
- Onboarding: nombre de la camaronera, tu nombre (administrador).
- Forgot password: email. Reset password: código OTP, nueva contraseña, confirmar.
- Accept invitation: contraseña, confirmar (token viene en la URL).
Piscinas
- Nueva/editar piscina (dialog): código (FormField), hectáreas (FormField number), tipo (FormSelect: engorde/precriadero), tope lb/ha (FormField number, opcional), sector (FormCombobox, con búsqueda).
- Sectores (dialog): nombre (FormField) + lista con eliminar.
- Equipos (dialog, por piscina): tipo de equipo (FormSelect), cantidad (FormField), desde (FormField date).
Listas y detalles
Piscinas (tabla)
Columnas: Piscina (código), Sector, Hectáreas, Tipo (badge), Tope lb/ha, Acciones (Equipos · Editar · Eliminar, según permiso).
- Detalle de equipos (dialog): equipos vigentes (tipo + cantidad), formulario de asignación, e historial de asignaciones con fecha.
Protecciones transversales
- Rutas protegidas por sesión (ProtectedRoute) y por permiso (module/action).
- Acciones (botones) ocultas/deshabilitadas según
usePermission. - La UI es espejo de UX; la autorización real es el backend (403 aunque la UI falle).