feat: tablero de gestion con graficas de estado y carga por aplicacion
This commit is contained in:
1 parent
646a3bf628
commit
3949224d94
5 files changed
+459
-56
No files matched your search
+8
-56
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useSyncExternalStore } from "react";
|
||||
import Link from "next/link";
|
||||
import { useModules, refreshModules } from "@/lib/store";
|
||||
import {
|
||||
displayName,
|
||||
envOf,
|
||||
@@ -16,61 +16,10 @@ import {
|
||||
type RefApp,
|
||||
} from "@/lib/sam";
|
||||
|
||||
// Store externo: evita setState en efectos (regla react-hooks)
|
||||
// y auto-refresca cada 15s. Los clics llaman a load() directo.
|
||||
let cache: ApiResponse | null = null;
|
||||
let loadError: string | null = null;
|
||||
let started = false;
|
||||
let snap: { data: ApiResponse | null; error: string | null } = {
|
||||
data: null,
|
||||
error: null,
|
||||
};
|
||||
const listeners = new Set<() => void>();
|
||||
|
||||
function notify() {
|
||||
listeners.forEach((l) => l());
|
||||
}
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
const res = await fetch("/api/modules", { cache: "no-store" });
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
cache = await res.json();
|
||||
loadError = null;
|
||||
} catch (e) {
|
||||
loadError = e instanceof Error ? e.message : "Error al cargar módulos";
|
||||
}
|
||||
// Referencia estable: solo cambia si cambian los datos (evita loops)
|
||||
if (snap.data !== cache || snap.error !== loadError) {
|
||||
snap = { data: cache, error: loadError };
|
||||
}
|
||||
notify();
|
||||
}
|
||||
|
||||
function subscribe(cb: () => void) {
|
||||
if (!started) {
|
||||
started = true;
|
||||
load();
|
||||
setInterval(load, 15000);
|
||||
}
|
||||
listeners.add(cb);
|
||||
return () => {
|
||||
listeners.delete(cb);
|
||||
};
|
||||
}
|
||||
|
||||
function snapshot(): { data: ApiResponse | null; error: string | null } {
|
||||
return snap;
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const { data, error } = useSyncExternalStore(
|
||||
subscribe,
|
||||
snapshot,
|
||||
() => ({ data: null, error: null })
|
||||
);
|
||||
const { data, error } = useModules();
|
||||
const loading = data === null && error === null;
|
||||
const fetchModules = load;
|
||||
const fetchModules = refreshModules;
|
||||
|
||||
const modules = data?.modules ?? [];
|
||||
const backend = data?.backend;
|
||||
@@ -190,8 +139,11 @@ export default function Home() {
|
||||
<div className="col-12 text-center">
|
||||
<h4 className="fw-semibold opacity-75">Aplicaciones</h4>
|
||||
</div>
|
||||
<div className="col-12 text-center mt-4">
|
||||
<Link href="/upload" className="btn btn-light btn-sm px-4">
|
||||
<div className="col-12 text-center mt-4 d-flex flex-column gap-2 align-items-center">
|
||||
<Link href="/tablero" className="btn btn-light btn-sm px-4">
|
||||
<i className="bx bx-pie-chart-alt-2 me-1" /> Tablero
|
||||
</Link>
|
||||
<Link href="/upload" className="btn btn-outline-light btn-sm px-4">
|
||||
<i className="bx bx-plus me-1" /> Nuevo Módulo
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user