94 lines
3.5 KiB
Markdown
94 lines
3.5 KiB
Markdown
# F-129 — Logs viewer: orden DESC + quitar autoscroll
|
|
|
|
## Diagnóstico
|
|
|
|
El operador reporta que el visor de logs (`/admin/logs`) muestra los eventos en orden ASC (más antiguo arriba, más reciente abajo). Con el `autoScroll` activo, el usuario ve el stream llegando por abajo, pero si hace scroll arriba para buscar un evento anterior, **el stream salta automáticamente hacia abajo cada vez que llega un nuevo log**, lo que resulta molesto.
|
|
|
|
Solicitud:
|
|
1. Orden **DESC** (evento más reciente arriba)
|
|
2. Quitar el autoscroll (no hace falta porque el último evento siempre está arriba visible)
|
|
|
|
## Diseño
|
|
|
|
Usar `flex-direction: column-reverse` sobre el contenedor del log para invertir visualmente el orden DOM sin tocar el array. Esto preserva:
|
|
- Estado del array en orden de llegada (cronológico) — fácil de gestionar
|
|
- Cap de 200 líneas: `next.slice(-200)` mantiene las 200 más recientes, que con column-reverse quedan al inicio visible
|
|
- Sin `bottomRef`, sin `handleScroll`, sin `scrollToBottom`, sin `autoScroll` state
|
|
|
|
### Cambios
|
|
|
|
`apps/admin/src/components/ServerLogViewer.tsx`:
|
|
|
|
```diff
|
|
- const [autoScroll, setAutoScroll] = useState(true);
|
|
- const bottomRef = useRef<HTMLDivElement>(null);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
- const isAtBottomRef = useRef(true);
|
|
|
|
- const scrollToBottom = useCallback(() => {
|
|
- if (autoScroll) bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
- }, [autoScroll]);
|
|
-
|
|
- const handleScroll = useCallback(() => {
|
|
- const el = containerRef.current;
|
|
- if (!el) return;
|
|
- const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
- isAtBottomRef.current = distFromBottom < 50;
|
|
- setAutoScroll(isAtBottomRef.current);
|
|
- }, []);
|
|
|
|
- useEffect(() => {
|
|
- if (status === 'live') scrollToBottom();
|
|
- }, [logs, scrollToBottom, status]);
|
|
```
|
|
|
|
Toolbar:
|
|
```diff
|
|
<div className="flex items-center gap-2">
|
|
- <label className="flex items-center gap-1.5 text-xs text-gray-400 cursor-pointer">
|
|
- <input type="checkbox" checked={autoScroll} onChange={...} ... />
|
|
- Auto-scroll
|
|
- </label>
|
|
<button onClick={() => setLogs([])} ... >Limpiar</button>
|
|
</div>
|
|
```
|
|
|
|
Contenedor de logs:
|
|
```diff
|
|
<div
|
|
ref={containerRef}
|
|
- onScroll={handleScroll}
|
|
className="flex-1 overflow-y-auto bg-[#0d1117] font-mono text-xs leading-relaxed"
|
|
+ className="flex-1 overflow-y-auto bg-[#0d1117] font-mono text-xs leading-relaxed flex flex-col-reverse"
|
|
style={{ minHeight: 0 }}
|
|
>
|
|
<table className="w-full table-fixed">
|
|
<tbody>
|
|
{logs.map((entry, i) => ...)}
|
|
</tbody>
|
|
</table>
|
|
- <div ref={bottomRef} />
|
|
</div>
|
|
```
|
|
|
|
### Cap de 200 líneas
|
|
|
|
Se mantiene: `next.slice(-200)` (las 200 más recientes). Con `column-reverse`, las más recientes se ven arriba automáticamente. El usuario puede hacer scroll hacia abajo para ver las más antiguas.
|
|
|
|
### Sin autoscroll: por qué es OK
|
|
|
|
Con `column-reverse` y array en orden cronológico:
|
|
- Posición 0 (DOM top) = log más reciente
|
|
- El usuario SIEMPRE ve el log más reciente arriba sin necesidad de scroll
|
|
- No hay "salto" del stream que moleste al hacer scroll arriba
|
|
|
|
## Sin cambios en backend
|
|
|
|
El SSE `GET /admin/logs/stream` sigue emitiendo en orden cronológico. Solo cambiamos la presentación.
|
|
|
|
## Plan
|
|
|
|
1. Editar `apps/admin/src/components/ServerLogViewer.tsx` — quitar `autoScroll` state, `bottomRef`, `handleScroll`, `scrollToBottom`, useEffect de scroll. Quitar checkbox. Añadir `flex flex-col-reverse` al contenedor.
|
|
2. `cd apps/admin && npx tsc --noEmit`.
|
|
3. `cd apps/admin && npm run build`.
|
|
4. Cerrar gates. |