# HANDOVER.md — laravel_secopio (nota de traspaso entre modelos)

> **Si acabas de cambiar de modelo, LEE ESTE ARCHIVO PRIMERO.** Resume el estado del trabajo visual
> de la sesión (2026-10-01) y, sobre todo, **las reglas del proyecto**, para no repetirlas ni
> romper nada. El detalle histórico completo está en `AGENTS.md`; esto es solo lo
> necesario para continuar sin contexto. **La sección 11 tiene lo último (2026-10-08).**

---

## 1. Qué es este proyecto en 5 líneas

Migración de SECOPIO (alertas de licitaciones SECOP II) a **Laravel 10 + Breeze**, multi-tenant:
`empresa` → `usuarios` → `perfiles_busqueda`. Laragon/Windows/Apache/MySQL. URL:
`http://laravel_secopio.test`. Todo el texto de UI y el código están en **español**.

## 2. Cómo correr cosas (imprescindible)

```
php NO está en PATH. Usar siempre:
$php = "C:\laragon\bin\php\php-8.1.10-Win32-vs16-x64\php.exe"

& $php artisan route:list --except-vendor
& $php artisan secopio:pipeline
```

- PowerShell rompe backslashes/comillas en `-e`/`--execute`: para BD usar un script PHP temporal y
  borrarlo después.
- El navegador cachea el CSS con `?v=`. Si un cambio "no se ve": subir el `?v=` o Ctrl+F5.

## 3. Las 4 reglas del proyecto (no negociables)

1. **MEDIR antes de corregir.** Todo cambio visual se mide en Chrome headless con
   `getBoundingClientRect()`, nunca a ojo. Hay dos desalineos que quedaron "perfectos" en pantalla
   y eran −15px / −2px en la medición.
2. **No inventar márgenes para disimular un desalineo.** Se corrige la regla que lo causa. Nada de
   `width: 94%`, `max-width` ni márgenes negativos como parche.
3. **No editar CSS con regex `(?s)` / `.*?`.** Ya se comió reglas dos veces. Editar bloques
   completos con la herramienta de edición.
4. **Un valor mágico solo si se puede derivar y documentar** (p. ej. el `11px` de UNSPSC = 12 − 1 del
   borde; el `15px` = 14 de padding + 1 de borde). Si no se puede derivar, se deja el valor real y se
   le cuenta al usuario.

## 4. Cómo medir (receta; los archivos temporales ya se borraron)

Crear en `%TEMP%\secopio_desc\` un HTML que replique el bloque real de la vista + un `<pre id="out">`,
y enlazando **el CSS real por ruta absoluta**
(`file:///C:/laragon/www/laravel_secopio/public/css/estilos.css`).
Si la fila la crea el JS, inyectarla a mano con `innerHTML` (es lo que se hizo con
`#listaDescripciones .elemento-agregado`). Medir dentro de un `try/catch` y volcar los números a un
`<pre>`; luego:

```powershell
$chrome = "C:\Program Files\Google\Chrome\Application\chrome.exe"
& $chrome --headless=new --disable-gpu --no-sandbox --allow-file-access-from-files `
  --window-size=1400,1200 --virtual-time-budget=6000 --dump-dom "file:///C:/.../archivo.html" 2>$null `
  | Out-File "$tmp\dom.html" -Encoding utf8
# luego extraer con regex (?s)<pre id="out">(.*?)</pre>  y reemplazar &gt;/&lt;/&amp;
```

**Medir siempre en varios anchos**: 1400 / 1000 / 760 (escritorio) y 600 / 420 (móvil), y comprobar
`scrollWidth == clientWidth` para descartar desbordamiento horizontal. Los temporales se borran al
terminar.

## 5. Qué se hizo en esta sesión (2026-10-01) — 3 correcciones, todas solo CSS

Archivo único modificado: **`public/css/estilos.css`**. Nada de HTML, JS, IDs, clases ni colores.

### 5.1 Geometría de "Descripción del proceso"
- `#listaDescripciones .elemento-agregado { padding: 10px 14px }` → **`padding: 10px 0 10px 14px`**
  (el padding derecho sobrante era la causa de que "Eliminar" quedara 15px detrás del "Agregar").
- `.grupo-descripcion input { height: 44px }` (estaba 42px contra los 44px del botón).
- `@media (max-width: 700px)`: `.grupo-descripcion input { flex: none }` — arregla un bug
  **preexistente**: con `flex-direction: column`, el `flex: 1` del input se vuelve `flex-basis: 0` y
  lo aplastaba a **18px de alto**. Verificado revirtiendo los cambios con un `<style>` de override.
- **Pendiente de decisión del usuario:** queda 1px entre el borde derecho de "Agregar" y el de
  "Eliminar" (es el `border-right: 1px` de la fila, que ocupa 1px *dentro* de su caja). Para llegar a
  0.00 haría falta quitar ese borde (cambio de diseño) o `margin-right: -1px` (prohibido). **Está
  explicado al usuario; no lo cambies sin que lo pida.**

### 5.2 "Agregar" y "Eliminar" como botones hermanos
El bloque de `.btn-eliminar` se partió en **forma compartida + color** (una sola fuente de verdad,
para que no vuelvan a separarse):
- Bloque de forma: `.seccion-unspsc .btn-eliminar, #listaDescripciones .btn-eliminar,
  #btnAgregarDescripcion { … }` — **sin `background`**.
- Color aparte: los `.btn-eliminar` con `var(--rojo)`, `#btnAgregarDescripcion` con el degradado azul.
- `#btnAgregarDescripcion:hover { background: #0d315b }` es **obligatorio**: el azul base es un ID
  (1,0,0) y le gana a `.grupo-descripcion button:hover` (0,2,1); sin esa regla se pierde el hover.
- Se seleccionado por **ID** porque `btnAgregarDescripcion` no tiene clase y no se podía tocar el HTML.
- Diferencias reales que había (medidas con `getComputedStyle`): `padding 0 22px` vs `0 12px`,
  `font-size 13.3333px` (el default de Chrome para controles de formulario) vs `14px`,
  `font-weight 600` vs `700`, `line-height normal` vs `14px`.
- Resultado: idénticos salvo **1.56px de ancho** (solo la letra de diferencia entre "Agregar" y
  "Eliminar"). **NO se puso `min-width`** a propósito (número mágico).

### 5.3 UNSPSC (tanda anterior, ya aprobada)
`#listaSegmentos/Familias/Clases/Productos { padding: 0 14px }`, `.elemento-agregado` sin
`width: 94%` y con `padding: 7px 11px 7px 12px`, bloque flex en `.seccion-unspsc > div:not([id])`.
Medido 0.00 en los cuatro niveles a 1400/1000/760 sin overflow. **No se vuelve a tocar sin
medir.**

## 6. Estado actual y verificaciones

- Cache-buster: **`estilos.css?v=20261001b` en las 11 vistas**. Si haces otro cambio visual, súbelo
  (20261001c, d…) en todas, o el navegador serving la versión vieja y "no se ve nada".
- `estilos.css`: 311/311 llaves balanceadas, sin BOM, sin U+FFFD (0). Las palabras con tilde van sin
  tilde en comentarios y texto de UI (`Descripcion`, `Clasificacion`) — es convención del proyecto.
- MD5 del CSS servido por Apache = MD5 en disco (verificado).
- `storage/logs/laravel.log` = 0 bytes. Rutas clave devuelven lo esperado (`/` 200,
  `/configurar-filtros` 302 a login sin sesión).
- UNSPSC quedó **idéntico** tras el cambio de botones (medido antes/después: Agregar 81.13px,
  Eliminar 79.25px).

## 7. Lo que estaba a punto de empezar (pendiente)

**El usuario quiere rediseñar los paneles del admin** y tiene una idea que dice ser muy buena:

| Imagen (en `public/assets/`) | Pantalla a rediseñar |
|---|---|
| `imagen httplaravel_secopio.testadmindashboard.png` (1536×1024) | `/admin/dashboard` |
| `imagen httplaravel_secopio.testadminclientes1.png` (1672×941) | `/admin/clientes/1` |

**Aviso importante:** el modelo anterior (`big-pickle`) **no admite imágenes**, así que nunca pudo ver
esos dos PNG. Si el modelo actual **tampoco** puede leerlos, **no lo sigas fingiendo**: dilo y pide
descripción en texto o un boceto. Con eso se trabaja igual de bien (así se hicieron las 3 correcciones
de arriba). Lo que sí se puede extraer de un PNG **sin "verlo"**, porque GD está activo, es
información técnica pura: dimensiones, paleta de colores y posición aproximada de bloques leyendo
píxeles. No sirve para entender la intención del diseño.

## 8. Mapa de archivos (lo que verás tocar)

- `public/css/estilos.css` — **todo el diseño visual** (~2276 líneas). Ojo: hay dos capas, la original
  arriba y un **bloque tardío de ajustes** más abajo que sobrescribe con selectores de ID. Al medir,
  lee el valor computado, no lo que "parece" en la regla que estés mirando.
- `resources/views/publico/configurar-filtros.blade.php` — filtros, cascada UNSPSC (líneas ~409-453
  la sección "Descripción del proceso").
- `resources/views/admin/{dashboard,clientes/index,clientes/show}.blade.php` — los paneles a rediseñar.
- `resources/views/layouts/secopio.blade.php` — layout base con `@stack('estilos')`. **No carga CSS
  por sí mismo**: cada vista hace su `@push('estilos')`. Si creas una vista y olvidas el push, sale
  sin diseñar.
- `public/js/secopio/*.js` (5 archivos, `?v=20260929c`) — filtros, cascada, Tom Select.
- `app/Console/Commands/SecopioPipeline.php` — motor de alertas (`secopio:pipeline`, cada 30 min).
- **No tocar**: `public/.htaccess` (conserva la redirección final a `index.php`), `vendor/tom-select/`.

## 9. Otros trabajos cerrados (no reabrir sin aviso)

Registro multitenant y agrupamiento por nombre de empresa, límites de `max_usuarios`, middleware
`rol.admin` y páginas 403/404/419/500 propias, botón "Volver" según rol, lote de envío con copia
congelada (`envio_proceso`), correos con el diseño de SECOPIO y logo por URL pública,
`/configurar-filtros` accesible con token de registro de un solo uso. Todo documentado en `AGENTS.md`.

## 10. Estado actual del rediseño administrativo (2026-10-02)

- Se rediseñaron visualmente `/admin/dashboard` y `/admin/clientes/1` tomando como referencia las imágenes ubicadas en `public/assets/`.
- El dashboard tiene sidebar con logo SECOPIO y únicamente la opción `Clientes`; se eliminaron `Resumen` y `Configuración` porque no son opciones funcionales del flujo actual.
- `/admin/clientes/1` conserva el logo SECOPIO en la cabecera y usa un menú contextual propio: `Información`, `Usuarios`, `Clasificación UNSPSC`, `Descripción` y `Filtros adicionales`.
- Cada opción del detalle muestra solo su apartado; lo demás permanece oculto.
- En `/admin/dashboard` funcionan localmente el buscador y los filtros `Todos`, `Activos`, `Pendientes` y `Bloqueados`.
- Se eliminó `Volver al registro`; `Cerrar sesión` queda centrado.
- Los iconos provisionales fueron reemplazados por SVG inline.
- `Nuevo cliente` conserva apariencia visual, pero aún no tiene funcionalidad adicional conectada.
- Últimos cache-busters: dashboard `estilos.css?v=20261001ac`; detalle `estilos.css?v=20261001x`.
- Los cambios relevantes se validaron con `php artisan view:cache` usando PHP 8.1.10 de Laragon.
- Pendiente: revisión visual final contra las capturas y decidir si buscador/filtros deben migrar a consulta del backend cuando existan más registros.

## 11. Último trabajo funcional (2026-10-08) — filtros compartidos + login

Todo el detalle está en `AGENTS.md`, sección **"Filtros compartidos por empresa (2026-10-08)"**.
Resumen para no rehacer nada:

- **Los filtros son de la EMPRESA**: los edita solo el `Administrador Cliente` (de su empresa) o el
  super admin (`administrador`/`super-admin`). Cada usuario conserva su fila en `perfiles_busqueda`
  como **copia espejo** (se sincronizan al guardar con `perfiles_busqueda::sincronizarPerfilesDeEmpresa`).
  Referencia: `empresa::perfilReferencia()`. Permiso: `FiltrosController::puedeEditarFiltros()`.
- **Los `Cliente` ya NO inician sesión**: `LoginRequest` valida la clave y, si no tiene rol de
  `RolAdmin::ROLES`, cierra la sesión y muestra el aviso en `/login` (no redirige a ningún lado).
  Las puertas de `FiltrosController` siguen bloqueando a un `Cliente` con sesión (302 a `/` y 403).
- **Bug corregido (destino tras guardar)**: el `Administrador Cliente` guardaba en
  `/configurar-filtros?editar=1` **sin `?volver=`** y caía en la portada `/` en vez de
  `/admin/clientes/{id}`. Causa: el flag de destino usaba `esAdministradorGlobal()` (no incluye
  `Administrador Cliente`). Se añadió **`esAdminPanel()`** (roles de `RolAdmin::ROLES` +
  `Administrador`) usado **solo** para destinos (`volver` de la vista y `redirect` del POST).
  `esAdministradorGlobal()` se mantiene para los permisos. Verificado por HTTP (9 checks).
- **Se quitó el botón "Editar filtros"** de `admin/clientes/show.blade.php` (pedido del dueño). La
  edición sigue existiendo entrando a `/configurar-filtros` (Admin Cliente → su `?editar=`) o a
  `/configurar-filtros?empresa=ID` (super admin). La ruta `admin.filtros.edit` sigue viva sin enlaces.
- **Prueba manual**: se entregó una guía al dueño en el chat (secciones A–G + apéndice 419).
