Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions docs/ARQUITECTURA.md

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions docs/modulos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Índice de módulos — Parking EDM

> Documentación técnica por módulo. Ver primero [../ARQUITECTURA.md](../ARQUITECTURA.md) para la visión general (stack, capas, flujo de auth, deuda técnica priorizada).

## Núcleo de la aplicación

| Módulo | Descripción |
|---|---|
| [Arranque de la app](./app-raiz.md) | `App.tsx`, `main.tsx` — composición raíz, pestañas, sesión y rol |
| [Autenticación](./auth.md) | `src/auth` — MSAL/Azure AD (implementación activa + implementación paralela sin usar) |
| [Capa Graph](./graph.md) | `src/graph` — `GraphRest` (cliente HTTP) y `GraphServicesContext` (DI de servicios) |
| [Servicios](./services.md) | `src/Services` — un repositorio por lista de SharePoint |
| [Ports](./ports.md) | `src/Ports` — adaptadores hacia forma de UI (solo 2 de ~11 servicios) |
| [Hooks](./hooks.md) | `src/Hooks` — estado de UI (loading/error/paginación) sobre los servicios |
| [Models](./models.md) | `src/Models` — tipos de dominio, modelos crudos de SharePoint y modelos de vista |
| [Utils](./utils.md) | `src/utils` — helpers puros (fechas, Excel, correo, color de estado) |

## Componentes — Administración

| Módulo | Descripción |
|---|---|
| [AdminCells](./components-admincells.md) | Pestaña "Celdas" — dashboard de capacidad, reserva rápida, detalle/asignación de celda (módulo más grande del proyecto) |
| [Admin-Settings](./components-admin-settings.md) | Pestaña "Administración" — días visibles, horarios, Términos y Condiciones |
| [Colaboradores-Permanentes](./components-colaboradores-permanentes.md) | Sub-tab "Colaboradores Fijos" |
| [AgregarColaborador](./components-agregar-colaborador.md) | Modal de alta de colaborador fijo |
| [DetallesColaborador](./components-detalles-colaborador.md) | Modal de solo lectura (hoy inalcanzable en producción) |
| [PermisosApp](./components-permisos-app.md) | Sub-tab "Usuarios APP" — membresía del grupo de acceso a la app |
| [AddGraphUsers](./components-add-graph-users.md) | Modal para otorgar acceso a la app |
| [RegistroVehicular](./components-registro-vehicular.md) | Sub-tab "Registro vehicular" |
| [AgregarRegistroVehicular](./components-agregar-registro-vehicular.md) | Modal de alta de registro vehicular |
| [Modals](./components-modals.md) | Modal de confirmación genérico (con Términos y Condiciones opcionales) |
| [ToggleSwitch](./components-toggle-switch.md) | Switch accesible genérico (sin uso actualmente) |

## Componentes — Reservas, Pico y Placa, Reportes y UI compartida

| Módulo | Descripción |
|---|---|
| [Mis-Reservas](./components-mis-reservas.md) | Tabla de reservas propias (o de todos, en modo admin) |
| [Reservar](./components-reservar.md) | Pantalla de creación de reserva — **no montada actualmente** |
| [PicoPlaca](./components-picoplaca.md) | Pestaña "Pico y placa" — reglas de restricción vehicular (hoy no aplicadas en la reserva) |
| [ReporteRegistros](./components-reporte-registros.md) | Reporte de registro vehicular |
| [Reportes](./components-reportes.md) | Pestaña "Reportes" (contenedor) — aforo de celdas + reporte de registro |
| [Notices](./components-notices.md) | Aviso y *feature flag* de reservas deshabilitadas |
| [Toast](./components-toast.md) | Sistema de notificaciones (toasts) de toda la app |

---
Para el resumen priorizado de deuda técnica (con severidad crítica/alta/media/baja) y recomendaciones, ver [../ARQUITECTURA.md §5](../ARQUITECTURA.md#5-deuda-técnica).
31 changes: 31 additions & 0 deletions docs/modulos/app-raiz.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Módulo: Arranque de la app (`src/App.tsx`, `src/main.tsx`)

> Ver también: [Arquitectura general](../ARQUITECTURA.md) · [Índice de módulos](./README.md)

## `src/main.tsx`
Punto de entrada. Monta React (`StrictMode`) envolviendo `<App/>` en `<AuthProvider>` (`src/auth/AuthProvider.tsx`). No hay router: toda la navegación de la app es estado interno de `App.tsx`.

## `src/App.tsx`
Componente raíz de la aplicación. Renderiza tres estados posibles según `useAuth()`:
- `!ready` → pantalla "Conectando…".
- `!account` → pantalla de login con botón "Iniciar sesión" (`signIn()` → `msal.loginPopup`).
- con sesión → monta `<GraphServicesProvider><AppInner/></GraphServicesProvider>`.

### `AppInner` (función interna, no exportada)
- Carga el perfil (`UserService.getMeBasic()`) y el rol/permiso (`SharedServices.getRole(mail)`) en dos `useEffect` secuenciales (primero perfil, luego rol dependiente del perfil).
- `NAVS_ADMIN` (línea 24) define las pestañas visibles para administradores: Reservas, Celdas, Administración, Pico y placa, Colaboradores, Reportes — solo visibles si `userRole === 'admin'`.
- Usuarios no-admin solo ven `ReservationsDisabledNotice` + `MisReservas` (modo de solo lectura de sus propias reservas).
- `useRoleHelpers()` (función local) implementa `changeUser(email)`: alterna el campo `Rol` en la lista `usuariosparking` entre `admin`/`Usuario` — es el mecanismo detrás del botón "Cambiar rol" en la cabecera (`HeaderBar`), visible solo si `Permitidos=true` para ese usuario.
- Envuelve todo el contenido autenticado en `<ToastProvider>` para exponer `useToast()` a los descendientes.

### `HeaderBar` (función local, reutilizable)
Cabecera común a todas las pantallas (logueado o no): muestra avatar/nombre/correo/rol, botón "Cambiar rol" (si aplica) y la acción primaria (Iniciar/Cerrar sesión).

## Patrones de programación
- Composición por render condicional de pestañas (no hay `react-router`); el estado de sesión/rol vive en este único componente y se pasa hacia abajo por props, no por un contexto adicional.
- `useMemo` para instanciar servicios derivados de `useGraphServices()` (`UserService`, `SharedServices`) una sola vez por cambio de `graph`/`usuariosParking`.
- Flags booleanos independientes por cada etapa asíncrona (`userLoading`, `permLoading`, `changingRole`) en vez de una máquina de estados única.

## Notas de calidad
- La normalización de la respuesta de Graph (`normRows`, dentro de `useRoleHelpers`) contempla varias formas (`res?.data`, `res?.value`, array plano) — indicio de que el contrato de retorno de los Services no es 100% uniforme entre sí (ver [Servicios](./services.md)).
- El cambio de rol (`changeUser`) reescribe directamente el campo `Rol` vía Graph con los mismos permisos delegados que el resto de la app — no hay una capa de autorización adicional del lado servidor; ver deuda técnica de autorización en [ARQUITECTURA.md §3.2](../ARQUITECTURA.md#32-autorización-roles).
42 changes: 42 additions & 0 deletions docs/modulos/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Módulo: Autenticación (`src/auth`)

> Ver también: [Arquitectura general](../ARQUITECTURA.md) · [Índice de módulos](./README.md)

Este módulo integra la app con Azure AD (Entra ID) vía MSAL (`@azure/msal-browser`). Contiene **dos implementaciones paralelas**; solo una está activa.

## `src/auth/msal.ts` — implementación activa (bajo nivel)
Instancia única de `PublicClientApplication` con `clientId`/`authority` **hardcodeados** directamente en el código (no usa variables de entorno). `cache.cacheLocation = 'localStorage'`.

`SCOPES` solicitados: `openid, profile, email, User.Read, Sites.ReadWrite.All, Directory.Read.All, Mail.Send` (con `Group.ReadWrite.All` dejado comentado).

Funciones exportadas:
- `initMSAL()`: inicializa MSAL una sola vez (guard `initialized`).
- `ensureActiveAccount()`: garantiza que siempre haya una "active account" seleccionada en MSAL.
- `ensureLogin(): Promise<AccountInfo>`: login interactivo vía **popup** (`msal.loginPopup`).
- `getAccessToken(): Promise<string>`: intenta `acquireTokenSilent`; si requiere interacción (`InteractionRequiredAuthError`), hace *fallback* a `acquireTokenPopup`.
- `logout()`: `msal.logoutPopup`.

## `src/auth/AuthProvider.tsx` — implementación activa (contexto React)
Contexto React que envuelve `msal.ts` y es el que efectivamente consume el resto de la app vía `useAuth()`:
```ts
type AuthCtx = { ready, account, getToken, signIn, signOut };
```
No fuerza login automático al montar — solo inicializa MSAL (`initMSAL()`) y deja `ready = true`; el login ocurre cuando el usuario pulsa "Iniciar sesión" en `App.tsx`.

## `src/auth/authContext.tsx` — **implementación paralela sin usar (código muerto)**
Segundo archivo con una implementación de auth completa e independiente:
- Instancia propia de `PublicClientApplication`.
- Lee configuración desde variables de entorno `import.meta.env.VITE_AAD_TENANT_ID` / `VITE_AAD_CLIENT_ID` / `VITE_AAD_REDIRECT_URI` (que sí existen en el `.env` versionado del repo, pero no las usa nadie más).
- Usa flujo de **redirect** (`loginRedirect`/`acquireTokenRedirect`) en vez de popup.
- Define su propio `useAuth()`/`AuthProvider` con la **misma firma pública** (`ready, account, getToken`) que la versión activa, más `logout` en vez de `signIn`/`signOut`.

Ningún componente de la app importa este archivo — se verificó por búsqueda en todo `src/`. Es la implementación "descartada" de un rediseño de auth que no se completó ni se limpió.

## Patrones de programación
- Context + hook de consumo (`useAuth()`) con guard de error si se usa fuera del provider — patrón estándar repetido en otros contextos de la app (`GraphServicesContext`, `ToastProvider`).
- `useMemo`/`useCallback` para mantener estable la identidad de `value` del contexto entre renders.

## Notas de calidad (deuda técnica)
- Tener dos implementaciones de auth completas y con la misma interfaz pública es una fuente de confusión para cualquier persona nueva en el proyecto: hay que verificar cuál se usa realmente (`AuthProvider.tsx`, importado desde `main.tsx`) antes de tocar código de sesión.
- El archivo `.env` versionado en git alimenta únicamente a la implementación muerta (`authContext.tsx`) — ver [ARQUITECTURA.md §5.1, punto 1](../ARQUITECTURA.md#51-crítica--riesgos-de-seguridad-y-bugs-funcionales-activos).
- Recomendación: eliminar `authContext.tsx` si no hay planes de migrar a flujo *redirect*, o documentar por qué se mantiene.
18 changes: 18 additions & 0 deletions docs/modulos/components-add-graph-users.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Módulo: `Components/AddGraphUsers` — Otorgar acceso a la app

> Ver también: [Arquitectura general](../ARQUITECTURA.md) · [Índice de módulos](./README.md) · [Usuarios de la app](./components-permisos-app.md)

Modal invocado desde [`usuariosApp.tsx`](./components-permisos-app.md) para elegir un usuario del directorio y otorgarle acceso a la app (agregarlo al grupo de Microsoft 365 correspondiente).

## `ModalAgregarPermiso.tsx` (196 líneas)
`Props = {isOpen, onClose, onSave?, slotsLoading?, workers: any[], workersLoading?}` — solo devuelve al padre `{userId, name, mail}`; la escritura real en el grupo la hace el componente padre.

- Clon estructural casi idéntico a [`ModalAgregarColaborador`](./components-agregar-colaborador.md) (mismo patrón de combobox, efectos, botones de pie).
- Nombre/Correo son **de solo lectura** (`readOnly`) — solo se llenan eligiendo del directorio, a diferencia del modal de colaboradores donde siguen editables tras el prellenado.
- Valida además que se haya elegido explícitamente un `selectedUserId` (no basta con nombre/correo no vacíos) antes de habilitar "Guardar".
- **No** llama `nameProve()` — a diferencia de `ModalAgregarColaborador` y del selector de reserva de `slotDetailsModal.tsx`, esta pantalla no filtra cuentas genéricas/de rol al otorgar acceso.

## Notas de calidad (deuda técnica)
- `workers: any[]` (línea 11) — el único punto del proyecto donde el directorio de trabajadores no se tipa como `Worker[]`.
- `slotsLoading?: boolean` es una prop irrelevante para este dominio (otorgar acceso no tiene nada que ver con celdas de parqueo) y tampoco se usa en el cuerpo — residuo de copiar el modal de colaboradores como plantilla.
- Ausencia de `nameProve()` aquí, presente en otros dos puntos de selección de trabajador, es una inconsistencia real de la regla de negocio "no permitir cuentas genéricas": un admin podría otorgar acceso de app a una cuenta como `"facturacion"` aunque la misma elección sería rechazada en el alta de colaboradores.
25 changes: 25 additions & 0 deletions docs/modulos/components-admin-settings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Módulo: `Components/Admin-Settings` — Configuración global

> Ver también: [Arquitectura general](../ARQUITECTURA.md) · [Índice de módulos](./README.md) · [Servicios](./services.md) · [Ports](./ports.md)

Pestaña "Administración" (`selected === 'admin'` en `App.tsx`).

## `AdminSettings.tsx` (307 líneas)
`Props = {settingsSvc: SettingsService, settingsItemId?: string (default '1')}`.

Formulario de configuración global: número de días visibles para reservar (`VisibleDays`), Términos y Condiciones (textarea HTML libre), y horarios AM/PM (`InicioManana/FinalManana/InicioTarde/FinalTarde`, expuestos en el `FormState` con el nombre interno de SharePoint `InicioHorarioMa_x00f1_ana`/`FinalMa_x00f1_ana` — artefacto de codificación de "Mañana" que SharePoint genera cuando no puede usar tildes en el nombre interno de columna).

- Convierte valores de hora heterogéneos (numérico o texto tipo `"7:00 a. m."`) mediante un parser propio `fromHH` (líneas 29-59): quita espacios NBSP, normaliza variantes de `a.m./p.m.`, y cae a interpretación de 24 horas si no reconoce el formato.
- Renderiza inputs `<input type="time" step={3600}>` para las 4 horas, convirtiendo ida y vuelta con `toHH`/`fromHH` en cada cambio.
- Validación cliente: `horariosInvalid` exige `InicioAM < FinalAM` y `InicioTarde < FinalTarde`; deshabilita "Guardar" si no se cumple, con mensaje de advertencia inline.
- `save()` mapea el formulario de vuelta al campo real `TerminosyCondiciones` y llama `settingsSvc.update(...)`.

## Patrones de programación
- Helpers puros de parseo/formateo (`clamp`, `clampHour`, `toHH`, `fromHH`) definidos a nivel de módulo (no dentro de handlers JSX) — testeables de forma aislada, aunque por su naturaleza (parsing de convenciones de SharePoint) encajarían mejor en `Models/time.ts` o un util dedicado.
- Componente pequeño, de una sola responsabilidad — uno de los más contenidos del proyecto.

## Notas de calidad (deuda técnica)
- `row: any` al leer el ítem cargado desde el servicio (línea 85) — sin tipo real pese a que `SettingsService` sí está tipado.
- El artefacto de SharePoint `Ma_x00f1_ana` se filtra directamente hasta el tipo `FormState` y a los `id` de JSX, en vez de quedar aislado detrás de una capa de mapeo — ver también [Models — `Settings.ts`](./models.md).
- El campo `TyC` (Términos y Condiciones) se guarda **sin sanitizar**, y se renderiza después vía `dangerouslySetInnerHTML` en [`Components/Modals/modals.tsx`](./components-modals.md) — riesgo de XSS almacenado si una cuenta admin se ve comprometida. Ver [ARQUITECTURA.md §5.1, punto 2](../ARQUITECTURA.md#51-crítica--riesgos-de-seguridad-y-bugs-funcionales-activos).
- Sin capa de i18n (textos y tooltips hardcodeados en español), consistente con el resto de la app.
Loading
Loading