feat: upload dentro de la plantilla SamShell (sin sidenav propio)
This commit is contained in:
1 parent
70df8e95a9
commit
3d9685adeb
1 file changed
+185
-209
+185
-209
@@ -1,9 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Link from "next/link";
|
import SamShell from "@/components/SamShell";
|
||||||
|
import { useModules } from "@/lib/store";
|
||||||
|
import { itMenu } from "@/lib/sam";
|
||||||
|
|
||||||
export default function UploadPage() {
|
export default function UploadPage() {
|
||||||
|
const { data } = useModules();
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
name: "",
|
name: "",
|
||||||
repoUrl: "",
|
repoUrl: "",
|
||||||
@@ -59,222 +62,195 @@ export default function UploadPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<SamShell
|
||||||
<div className="sidenav">
|
brand="IT"
|
||||||
<div className="row text-light login-main-text">
|
subtitle="Agregar módulo"
|
||||||
<div className="col-12 text-center">
|
menu={itMenu(data?.modules ?? [])}
|
||||||
<div
|
activeHref="/upload"
|
||||||
className="mx-auto d-flex align-items-center justify-content-center bg-white rounded-3"
|
topUser="SAM"
|
||||||
style={{ width: 80, height: 80 }}
|
backHref="/"
|
||||||
>
|
>
|
||||||
<i className="bx bx-upload text-primary" style={{ fontSize: 48 }} />
|
<div className="alert alert-light section-header mb-3 py-2 px-3">
|
||||||
</div>
|
<i className="bx bx-cloud-upload me-1" /> Integrar Nuevo Módulo
|
||||||
</div>
|
|
||||||
<div className="col-12 text-center mt-3">
|
|
||||||
<h1 className="fw-bold fs-2">Sistema SAM</h1>
|
|
||||||
</div>
|
|
||||||
<div className="col-12 text-center">
|
|
||||||
<h4 className="fw-semibold opacity-75">Subir Proyecto</h4>
|
|
||||||
</div>
|
|
||||||
<div className="col-12 text-center mt-4">
|
|
||||||
<Link href="/" className="btn btn-outline-light btn-sm px-4">
|
|
||||||
<i className="bx bx-arrow-back me-1" /> Volver
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="main">
|
<div className="app-card p-4">
|
||||||
<div className="row">
|
<p className="text-muted mb-4">
|
||||||
<div className="col-lg-1" />
|
Registra un proyecto <strong>Next.js 15 + Bootstrap</strong> desde un repositorio
|
||||||
<div className="col-lg-10 mt-4">
|
Gitea. Se clonará, desplegará automáticamente y quedará disponible en{" "}
|
||||||
<div className="alert alert-light section-header mb-3 py-2 px-3">
|
<code>https://{"{subdominio}"}.devops.norteamericano.cl</code>. Cada push futuro
|
||||||
<i className="bx bx-cloud-upload me-1" /> Integrar Nuevo Módulo
|
actualiza el módulo vía webhook.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{result && (
|
||||||
|
<div className={`alert ${result.ok ? "alert-success" : "alert-danger"}`}>
|
||||||
|
<i className={`bx ${result.ok ? "bx-check-circle" : "bx-error-circle"} me-2`} />
|
||||||
|
{result.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="row g-3">
|
||||||
|
<div className="col-md-6">
|
||||||
|
<label className="form-label fw-semibold">
|
||||||
|
Nombre del módulo <span className="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="name"
|
||||||
|
value={form.name}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="ej: ventas"
|
||||||
|
pattern="^[a-z][a-z0-9-]+$"
|
||||||
|
required
|
||||||
|
className="form-control"
|
||||||
|
/>
|
||||||
|
<div className="form-text">Minúsculas, guiones, empieza con letra.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="app-card p-4">
|
<div className="col-12">
|
||||||
<p className="text-muted mb-4">
|
<label className="form-label fw-semibold">
|
||||||
Registra un proyecto <strong>Next.js 15 + Bootstrap</strong> desde un repositorio
|
URL del repositorio Gitea <span className="text-danger">*</span>
|
||||||
Gitea. Se clonará, desplegará automáticamente y quedará disponible en{" "}
|
</label>
|
||||||
<code>https://{"{subdominio}"}.devops.norteamericano.cl</code>. Cada push futuro
|
<input
|
||||||
actualiza el módulo vía webhook.
|
name="repoUrl"
|
||||||
</p>
|
value={form.repoUrl}
|
||||||
|
onChange={handleChange}
|
||||||
{result && (
|
placeholder="https://git.norteamericano.cl/user/mi-proyecto.git"
|
||||||
<div className={`alert ${result.ok ? "alert-success" : "alert-danger"}`}>
|
required
|
||||||
<i className={`bx ${result.ok ? "bx-check-circle" : "bx-error-circle"} me-2`} />
|
className="form-control"
|
||||||
{result.message}
|
/>
|
||||||
</div>
|
<div className="form-text">Debe ser accesible desde el deployer (https://git.norteamericano.cl).</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div className="row g-3">
|
|
||||||
<div className="col-md-6">
|
|
||||||
<label className="form-label fw-semibold">
|
|
||||||
Nombre del módulo <span className="text-danger">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
name="name"
|
|
||||||
value={form.name}
|
|
||||||
onChange={handleChange}
|
|
||||||
placeholder="ej: ventas"
|
|
||||||
pattern="^[a-z][a-z0-9-]+$"
|
|
||||||
required
|
|
||||||
className="form-control"
|
|
||||||
/>
|
|
||||||
<div className="form-text">Minúsculas, guiones, empieza con letra.</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-12">
|
|
||||||
<label className="form-label fw-semibold">
|
|
||||||
URL del repositorio Gitea <span className="text-danger">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
name="repoUrl"
|
|
||||||
value={form.repoUrl}
|
|
||||||
onChange={handleChange}
|
|
||||||
placeholder="https://git.norteamericano.cl/user/mi-proyecto.git"
|
|
||||||
required
|
|
||||||
className="form-control"
|
|
||||||
/>
|
|
||||||
<div className="form-text">Debe ser accesible desde el deployer (https://git.norteamericano.cl).</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-12">
|
|
||||||
<div className="alert alert-light border py-2 small">
|
|
||||||
<i className="bx bx-cog me-1" />
|
|
||||||
<strong>Configuración automática:</strong> puerto y subdominio se asignan automáticamente
|
|
||||||
{form.name && (
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
→ <code>https://{form.subdomain || form.name}.devops.norteamericano.cl</code>
|
|
||||||
{form.port ? ` :${form.port}` : " (puerto auto)"}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
. Se crean los registros en Caddy y DNS.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<details className="col-12">
|
|
||||||
<summary className="small text-muted" style={{ cursor: "pointer" }}>
|
|
||||||
Opciones avanzadas (puerto / subdominio manual)
|
|
||||||
</summary>
|
|
||||||
<div className="row g-3 mt-1">
|
|
||||||
<div className="col-md-6">
|
|
||||||
<label className="form-label fw-semibold">
|
|
||||||
Subdominio <small className="text-muted">(opcional)</small>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
name="subdomain"
|
|
||||||
value={form.subdomain}
|
|
||||||
onChange={handleChange}
|
|
||||||
placeholder="ej: ventas (default: nombre)"
|
|
||||||
className="form-control"
|
|
||||||
/>
|
|
||||||
<div className="form-text">https://{form.subdomain || form.name || "subdominio"}.devops.norteamericano.cl</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-md-6">
|
|
||||||
<label className="form-label fw-semibold">
|
|
||||||
Puerto <small className="text-muted">(opcional)</small>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
name="port"
|
|
||||||
type="number"
|
|
||||||
value={form.port}
|
|
||||||
onChange={handleChange}
|
|
||||||
placeholder="auto (3011+)"
|
|
||||||
min={3000}
|
|
||||||
max={4000}
|
|
||||||
className="form-control"
|
|
||||||
/>
|
|
||||||
<div className="form-text">Se asigna automáticamente si se deja vacío.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</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">
|
|
||||||
<label className="form-label fw-semibold">Rama</label>
|
|
||||||
<input
|
|
||||||
name="branch"
|
|
||||||
value={form.branch}
|
|
||||||
onChange={handleChange}
|
|
||||||
placeholder="main"
|
|
||||||
className="form-control"
|
|
||||||
/>
|
|
||||||
<div className="form-text">
|
|
||||||
main = producción, otra rama = desarrollo.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-12 mt-2">
|
|
||||||
<div className="alert alert-info py-2 small mb-0">
|
|
||||||
<i className="bx bx-info-circle me-1" />
|
|
||||||
El repositorio debe ser un proyecto <strong>Next.js 15</strong> (con{" "}
|
|
||||||
<code>package.json</code> en raíz o en <code>frontend/</code>) y usar{" "}
|
|
||||||
<strong>Bootstrap</strong>. Si no trae <code>Dockerfile</code>, se genera uno automáticamente.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-12 mt-3 d-flex gap-2">
|
|
||||||
<button type="submit" disabled={loading} className="btn btn-primary px-4">
|
|
||||||
{loading ? (
|
|
||||||
<>
|
|
||||||
<span className="spinner-border spinner-border-sm me-2" /> Desplegando...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<i className="bx bx-rocket me-1" /> Registrar y Desplegar
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
<Link href="/" className="btn btn-outline-secondary">
|
|
||||||
Cancelar
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="app-card p-3 mt-3">
|
<div className="col-12">
|
||||||
<h6 className="fw-semibold mb-2">
|
<div className="alert alert-light border py-2 small">
|
||||||
<i className="bx bx-book-open me-1" /> Requisitos del repositorio
|
<i className="bx bx-cog me-1" />
|
||||||
</h6>
|
<strong>Configuración automática:</strong> puerto y subdominio se asignan automáticamente
|
||||||
<ul className="small text-muted mb-0">
|
{form.name && (
|
||||||
<li>
|
<span>
|
||||||
Proyecto Next.js 15 con <code>next build</code> / <code>next start</code>
|
{" "}
|
||||||
</li>
|
→ <code>https://{form.subdomain || form.name}.devops.norteamericano.cl</code>
|
||||||
<li>Bootstrap 5 (via CDN o npm) + Boxicons opcional</li>
|
{form.port ? ` :${form.port}` : " (puerto auto)"}
|
||||||
<li>
|
</span>
|
||||||
Variable <code>NEXT_PUBLIC_API_URL=https://api.devops.norteamericano.cl</code>{" "}
|
)}
|
||||||
para consumir el backend
|
. Se crean los registros en Caddy y DNS.
|
||||||
</li>
|
</div>
|
||||||
<li>Branch por defecto: main (configurable)</li>
|
</div>
|
||||||
</ul>
|
|
||||||
|
<details className="col-12">
|
||||||
|
<summary className="small text-muted" style={{ cursor: "pointer" }}>
|
||||||
|
Opciones avanzadas (puerto / subdominio manual)
|
||||||
|
</summary>
|
||||||
|
<div className="row g-3 mt-1">
|
||||||
|
<div className="col-md-6">
|
||||||
|
<label className="form-label fw-semibold">
|
||||||
|
Subdominio <small className="text-muted">(opcional)</small>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="subdomain"
|
||||||
|
value={form.subdomain}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="ej: ventas (default: nombre)"
|
||||||
|
className="form-control"
|
||||||
|
/>
|
||||||
|
<div className="form-text">https://{form.subdomain || form.name || "subdominio"}.devops.norteamericano.cl</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-6">
|
||||||
|
<label className="form-label fw-semibold">
|
||||||
|
Puerto <small className="text-muted">(opcional)</small>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="port"
|
||||||
|
type="number"
|
||||||
|
value={form.port}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="auto (3011+)"
|
||||||
|
min={3000}
|
||||||
|
max={4000}
|
||||||
|
className="form-control"
|
||||||
|
/>
|
||||||
|
<div className="form-text">Se asigna automáticamente si se deja vacío.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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">
|
||||||
|
<label className="form-label fw-semibold">Rama</label>
|
||||||
|
<input
|
||||||
|
name="branch"
|
||||||
|
value={form.branch}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="main"
|
||||||
|
className="form-control"
|
||||||
|
/>
|
||||||
|
<div className="form-text">
|
||||||
|
main = producción, otra rama = desarrollo.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12 mt-2">
|
||||||
|
<div className="alert alert-info py-2 small mb-0">
|
||||||
|
<i className="bx bx-info-circle me-1" />
|
||||||
|
El repositorio debe ser un proyecto <strong>Next.js 15</strong> (con{" "}
|
||||||
|
<code>package.json</code> en raíz o en <code>frontend/</code>) y usar{" "}
|
||||||
|
<strong>Bootstrap</strong>. Si no trae <code>Dockerfile</code>, se genera uno automáticamente.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12 mt-3 d-flex gap-2">
|
||||||
|
<button type="submit" disabled={loading} className="btn btn-primary px-4">
|
||||||
|
{loading ? (
|
||||||
|
<>
|
||||||
|
<span className="spinner-border spinner-border-sm me-2" /> Desplegando...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<i className="bx bx-rocket me-1" /> Registrar y Desplegar
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
|
<div className="app-card p-3 mt-3">
|
||||||
|
<h6 className="fw-semibold mb-2">
|
||||||
|
<i className="bx bx-book-open me-1" /> Requisitos del repositorio
|
||||||
|
</h6>
|
||||||
|
<ul className="small text-muted mb-0">
|
||||||
|
<li>
|
||||||
|
Proyecto Next.js 15 con <code>next build</code> / <code>next start</code>
|
||||||
|
</li>
|
||||||
|
<li>Bootstrap 5 (via CDN o npm) + Boxicons opcional</li>
|
||||||
|
<li>
|
||||||
|
Variable <code>NEXT_PUBLIC_API_URL=https://api.devops.norteamericano.cl</code>{" "}
|
||||||
|
para consumir el backend
|
||||||
|
</li>
|
||||||
|
<li>Branch por defecto: main (configurable)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</SamShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user