feat: sidebar IT con acciones reales (agregar/actualizar/eliminar + proxy deployer)

This commit is contained in:
Nurfog committed 2026-09-09 09:54:53 -03:00
1 parent e20e9d6657
commit 23df440b79
5 files changed
+398 -17

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
"use client";
import { useState } from "react";
import SamShell from "@/components/SamShell";
import { refreshModules, useModules } from "@/lib/store";
import {
displayName,
envOf,
getModuleMeta,
itMenu,
} from "@/lib/sam";
// Actualizar: redeploy manual + reinstalación de webhook por módulo.
export default function Actualizar() {
const { data, error } = useModules();
const modules = data?.modules ?? [];
const [busy, setBusy] = useState<string | null>(null);
const [msg, setMsg] = useState<{ ok: boolean; text: string } | null>(null);
const call = async (name: string, action: "deploy" | "webhook") => {
setBusy(`${action}:${name}`);
setMsg(null);
try {
const res = await fetch(
action === "deploy"
? `/api/deployer/modules/${name}/deploy`
: `/api/deployer/modules/${name}/webhook`,
{ method: "POST", cache: "no-store" }
);
const j = await res.json();
if (!res.ok) throw new Error(j.error || `HTTP ${res.status}`);
setMsg({
ok: true,
text:
action === "deploy"
? `"${name}" actualizado (sha ${(j.sha || "").slice(0, 7)})`
: `"${name}": webhook ${j.created ? `creado #${j.created}` : j.updated ? `actualizado #${j.updated}` : "ok"}`,
});
refreshModules();
} catch (e) {
setMsg({
ok: false,
text: e instanceof Error ? e.message : "Error",
});
} finally {
setBusy(null);
}
};
return (
<SamShell
brand="IT"
subtitle="Actualizar módulos"
menu={itMenu(modules)}
activeHref="/actualizar"
topUser="SAM"
backHref="/"
>
<div className="alert alert-light section-header mb-3 py-2 px-3">
<i className="bx bx-refresh me-1" /> Actualizar módulos
</div>
{error && <div className="alert alert-danger">{error}</div>}
{msg && (
<div className={`alert ${msg.ok ? "alert-success" : "alert-danger"}`}>
{msg.text}
</div>
)}
<div className="app-card p-3">
<div className="table-responsive">
<table className="table table-hover align-middle mb-0">
<thead>
<tr>
<th>Módulo</th>
<th>Rama</th>
<th>Último deploy</th>
<th className="text-end">Acciones</th>
</tr>
</thead>
<tbody>
{modules.map((m) => {
const env = envOf(m.branch);
return (
<tr key={m.name}>
<td>
<i className={`bx ${getModuleMeta(m.name).icon} me-2`} />
<strong>{displayName(m.name)}</strong>{" "}
<span className={`badge ${env.cls} badge-respaldo`}>
{env.label}
</span>
<div>
<small className="text-muted">
{m.lastDeploy
? `${(m.lastDeploy.sha || "").slice(0, 7)} · ${m.lastDeploy.ok ? "ok" : "falló"}`
: "sin deploys"}
</small>
</div>
</td>
<td>
<code className="small">{m.branch}</code>
</td>
<td>
<small className="text-muted">
{m.lastDeploy?.time
? new Date(m.lastDeploy.time).toLocaleString("es-CL")
: "—"}
</small>
</td>
<td className="text-end">
<div className="d-flex gap-2 justify-content-end">
<button
className="btn btn-sm btn-primary"
disabled={busy !== null}
onClick={() => call(m.name, "deploy")}
>
{busy === `deploy:${m.name}` ? (
<span className="spinner-border spinner-border-sm" />
) : (
<>
<i className="bx bx-refresh me-1" />
Actualizar
</>
)}
</button>
<button
className="btn btn-sm btn-outline-secondary"
disabled={busy !== null}
onClick={() => call(m.name, "webhook")}
title="Reinstalar webhook Gitea"
>
<i className="bx bx-link" />
</button>
</div>
</td>
</tr>
);
})}
{modules.length === 0 && (
<tr>
<td colSpan={4} className="text-center text-muted py-4">
No hay módulos integrados.
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</SamShell>
);
}