feat: categorias de integracion (pagina-web/sam/profesores/otras-apps) + badge prod/dev
This commit is contained in:
1 parent
3787152ca0
commit
66ed027043
3 files changed
+59
-19
No files matched your search
+9
-4
@@ -4,6 +4,7 @@ import { useSyncExternalStore } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import {
|
import {
|
||||||
displayName,
|
displayName,
|
||||||
|
envOf,
|
||||||
getModuleMeta,
|
getModuleMeta,
|
||||||
getStatusInfo,
|
getStatusInfo,
|
||||||
moduleUrl,
|
moduleUrl,
|
||||||
@@ -74,7 +75,7 @@ export default function Home() {
|
|||||||
|
|
||||||
const bySection: Record<string, ModuleState[]> = {};
|
const bySection: Record<string, ModuleState[]> = {};
|
||||||
modules.forEach((m) => {
|
modules.forEach((m) => {
|
||||||
const s = sectionOf(m.name);
|
const s = sectionOf(m.name, m.category);
|
||||||
if (!bySection[s]) bySection[s] = [];
|
if (!bySection[s]) bySection[s] = [];
|
||||||
bySection[s].push(m);
|
bySection[s].push(m);
|
||||||
});
|
});
|
||||||
@@ -82,6 +83,7 @@ export default function Home() {
|
|||||||
const renderModuleCard = (m: ModuleState) => {
|
const renderModuleCard = (m: ModuleState) => {
|
||||||
const meta = getModuleMeta(m.name);
|
const meta = getModuleMeta(m.name);
|
||||||
const status = getStatusInfo(m);
|
const status = getStatusInfo(m);
|
||||||
|
const env = envOf(m.branch);
|
||||||
const url = moduleUrl(m);
|
const url = moduleUrl(m);
|
||||||
const name = displayName(m.name);
|
const name = displayName(m.name);
|
||||||
return (
|
return (
|
||||||
@@ -107,6 +109,9 @@ export default function Home() {
|
|||||||
<small className="text-muted" style={{ fontSize: "0.75rem" }}>
|
<small className="text-muted" style={{ fontSize: "0.75rem" }}>
|
||||||
{status.label} · :{m.port} · {m.branch}
|
{status.label} · :{m.port} · {m.branch}
|
||||||
</small>
|
</small>
|
||||||
|
<span className={`badge ${env.cls} badge-respaldo`}>
|
||||||
|
{env.label}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="text-end pe-3" style={{ width: 40 }}>
|
<td className="text-end pe-3" style={{ width: 40 }}>
|
||||||
@@ -202,8 +207,8 @@ export default function Home() {
|
|||||||
|
|
||||||
{SECTIONS.map((section) => {
|
{SECTIONS.map((section) => {
|
||||||
const list = bySection[section.id] ?? [];
|
const list = bySection[section.id] ?? [];
|
||||||
const isSystem = section.id === "system";
|
const isReview = section.id === "otras-apps";
|
||||||
if (!isSystem && list.length === 0) return null;
|
if (!isReview && list.length === 0) return null;
|
||||||
return (
|
return (
|
||||||
<div className="col-12" key={section.id}>
|
<div className="col-12" key={section.id}>
|
||||||
<div className="row row-cards">
|
<div className="row row-cards">
|
||||||
@@ -224,7 +229,7 @@ export default function Home() {
|
|||||||
|
|
||||||
{list.map(renderModuleCard)}
|
{list.map(renderModuleCard)}
|
||||||
|
|
||||||
{isSystem && backend && (
|
{isReview && backend && (
|
||||||
<>
|
<>
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<div className="app-card p-2">
|
<div className="app-card p-2">
|
||||||
|
|||||||
+30
-2
@@ -10,11 +10,14 @@ export default function UploadPage() {
|
|||||||
branch: "main",
|
branch: "main",
|
||||||
port: "",
|
port: "",
|
||||||
subdomain: "",
|
subdomain: "",
|
||||||
|
category: "otras-apps",
|
||||||
});
|
});
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [result, setResult] = useState<{ ok: boolean; message: string } | null>(null);
|
const [result, setResult] = useState<{ ok: boolean; message: string } | null>(null);
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (
|
||||||
|
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
||||||
|
) => {
|
||||||
setForm({ ...form, [e.target.name]: e.target.value });
|
setForm({ ...form, [e.target.name]: e.target.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,6 +30,7 @@ export default function UploadPage() {
|
|||||||
name: form.name.trim().toLowerCase(),
|
name: form.name.trim().toLowerCase(),
|
||||||
repoUrl: form.repoUrl.trim(),
|
repoUrl: form.repoUrl.trim(),
|
||||||
branch: form.branch.trim() || "main",
|
branch: form.branch.trim() || "main",
|
||||||
|
category: form.category,
|
||||||
};
|
};
|
||||||
if (form.port) payload.port = parseInt(form.port, 10);
|
if (form.port) payload.port = parseInt(form.port, 10);
|
||||||
if (form.subdomain) payload.subdomain = form.subdomain.trim();
|
if (form.subdomain) payload.subdomain = form.subdomain.trim();
|
||||||
@@ -43,7 +47,7 @@ export default function UploadPage() {
|
|||||||
ok: true,
|
ok: true,
|
||||||
message: `Módulo "${payload.name}" registrado. Deploy: ${data.webhook ? "webhook creado" : "sin webhook"}${data.deployLog ? " · " + data.deployLog.slice(0, 80) : ""}`,
|
message: `Módulo "${payload.name}" registrado. Deploy: ${data.webhook ? "webhook creado" : "sin webhook"}${data.deployLog ? " · " + data.deployLog.slice(0, 80) : ""}`,
|
||||||
});
|
});
|
||||||
setForm({ name: "", repoUrl: "", branch: "main", port: "", subdomain: "" });
|
setForm({ name: "", repoUrl: "", branch: "main", port: "", subdomain: "", category: "otras-apps" });
|
||||||
} else {
|
} else {
|
||||||
setResult({ ok: false, message: data.error || `Error HTTP ${res.status}` });
|
setResult({ ok: false, message: data.error || `Error HTTP ${res.status}` });
|
||||||
}
|
}
|
||||||
@@ -188,6 +192,27 @@ export default function UploadPage() {
|
|||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<div className="col-md-6">
|
||||||
|
<label className="form-label fw-semibold">
|
||||||
|
¿A qué corresponde? <span className="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="category"
|
||||||
|
value={form.category}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
className="form-select"
|
||||||
|
>
|
||||||
|
<option value="pagina-web">Página Web</option>
|
||||||
|
<option value="sam">SAM</option>
|
||||||
|
<option value="profesores">Profesores</option>
|
||||||
|
<option value="otras-apps">Otras Aplicaciones</option>
|
||||||
|
</select>
|
||||||
|
<div className="form-text">
|
||||||
|
Define en qué sección del índice SAM aparece el módulo.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<label className="form-label fw-semibold">Rama</label>
|
<label className="form-label fw-semibold">Rama</label>
|
||||||
<input
|
<input
|
||||||
@@ -197,6 +222,9 @@ export default function UploadPage() {
|
|||||||
placeholder="main"
|
placeholder="main"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
/>
|
/>
|
||||||
|
<div className="form-text">
|
||||||
|
main = producción, otra rama = desarrollo.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 mt-2">
|
<div className="col-12 mt-2">
|
||||||
|
|||||||
+20
-13
@@ -19,6 +19,7 @@ export interface ModuleState {
|
|||||||
subdomain: string;
|
subdomain: string;
|
||||||
port: number;
|
port: number;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
category?: string;
|
||||||
lastDeploy?: LastDeploy;
|
lastDeploy?: LastDeploy;
|
||||||
container?: string | null;
|
container?: string | null;
|
||||||
}
|
}
|
||||||
@@ -184,20 +185,26 @@ export interface Section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SECTIONS: Section[] = [
|
export const SECTIONS: Section[] = [
|
||||||
{ id: "apps", title: "Selección de Aplicación", icon: "bx-grid-alt" },
|
{ id: "pagina-web", title: "Página Web", icon: "bx-globe" },
|
||||||
{ id: "system", title: "Backend & Sistema", icon: "bx-server" },
|
{ id: "sam", title: "SAM", icon: "bx-layer" },
|
||||||
|
{ id: "profesores", title: "Profesores", icon: "bx-pencil" },
|
||||||
|
{ id: "otras-apps", title: "Otras Aplicaciones", icon: "bx-cube" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const MODULE_SECTION: Record<string, string> = {
|
// Categoría que pregunta la integración (default del deployer: otras-apps).
|
||||||
// Ejemplos:
|
// "Otras Aplicaciones" concentra la revisión prod/dev: módulos
|
||||||
// contabilidad: "system",
|
// integrados + backend + gestión.
|
||||||
// pagos: "system",
|
export function sectionOf(name: string, category?: string): string {
|
||||||
};
|
const c = (category || "").trim().toLowerCase();
|
||||||
|
if (c && SECTIONS.some((s) => s.id === c)) return c;
|
||||||
|
void name;
|
||||||
|
return "otras-apps";
|
||||||
|
}
|
||||||
|
|
||||||
export function sectionOf(name: string): string {
|
// Rama main/master = producción, resto = desarrollo.
|
||||||
const key = name.toLowerCase();
|
export function envOf(branch: string) {
|
||||||
if (MODULE_SECTION[key]) return MODULE_SECTION[key];
|
const b = (branch || "").trim().toLowerCase();
|
||||||
const known = SECTIONS.some((s) => s.id === key);
|
return b === "main" || b === "master" || b === "prod" || b === "production"
|
||||||
if (known) return key;
|
? { label: "Prod", cls: "bg-success" }
|
||||||
return "apps";
|
: { label: "Dev", cls: "bg-warning text-dark" };
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user