feat: sidebar IT con acciones reales (agregar/actualizar/eliminar + proxy deployer)
This commit is contained in:
1 parent
e20e9d6657
commit
23df440b79
5 files changed
+398
-17
No files matched your search
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user