415 lines
21 KiB
HTML
415 lines
21 KiB
HTML
<!doctype html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>UML Plataforma Semillero</title>
|
|
<style>
|
|
:root {
|
|
--bg: #f5f7fb;
|
|
--card: #ffffff;
|
|
--ink: #1a2433;
|
|
--muted: #526074;
|
|
--line: #c7d2e4;
|
|
--accent: #0b6aa2;
|
|
--accent-soft: #e8f4fb;
|
|
--ok: #0c7a43;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
body {
|
|
margin: 0;
|
|
font-family: "Segoe UI", Tahoma, sans-serif;
|
|
background: linear-gradient(180deg, #eff4fb 0%, #f7fafc 55%, #ffffff 100%);
|
|
color: var(--ink);
|
|
}
|
|
|
|
.wrap {
|
|
width: min(1200px, 94vw);
|
|
margin: 24px auto 48px;
|
|
display: grid;
|
|
gap: 16px;
|
|
}
|
|
|
|
.card {
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
border-radius: 14px;
|
|
padding: 16px;
|
|
box-shadow: 0 6px 18px rgba(20, 45, 80, 0.06);
|
|
}
|
|
|
|
h1, h2, h3 { margin: 0 0 10px; }
|
|
h1 { font-size: 1.6rem; }
|
|
h2 { font-size: 1.2rem; color: var(--accent); }
|
|
p { margin: 0; color: var(--muted); }
|
|
|
|
.meta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.chip {
|
|
font-size: 0.82rem;
|
|
padding: 5px 10px;
|
|
border-radius: 99px;
|
|
background: var(--accent-soft);
|
|
border: 1px solid #cde6f5;
|
|
color: #085785;
|
|
}
|
|
|
|
.diagram {
|
|
overflow-x: auto;
|
|
border: 1px dashed var(--line);
|
|
border-radius: 10px;
|
|
padding: 10px;
|
|
background: #fbfdff;
|
|
}
|
|
|
|
svg {
|
|
min-width: 930px;
|
|
width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
|
|
.legend {
|
|
margin-top: 10px;
|
|
font-size: 0.9rem;
|
|
color: var(--muted);
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 0.93rem;
|
|
}
|
|
|
|
th, td {
|
|
border: 1px solid var(--line);
|
|
padding: 9px;
|
|
text-align: left;
|
|
vertical-align: top;
|
|
}
|
|
|
|
th {
|
|
background: #f0f6ff;
|
|
color: #173d63;
|
|
}
|
|
|
|
code {
|
|
font-family: Consolas, "Courier New", monospace;
|
|
font-size: 0.86rem;
|
|
color: #173d63;
|
|
}
|
|
|
|
.ok {
|
|
color: var(--ok);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.small {
|
|
font-size: 0.84rem;
|
|
color: var(--muted);
|
|
margin-top: 8px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main class="wrap">
|
|
<section class="card">
|
|
<h1>Diagrama UML - Plataforma INNOVATECH (Semillero)</h1>
|
|
<p>Documento tecnico de arquitectura funcional: casos de uso, clases e interacciones entre Usuario, Frontend Angular, Backend Express y Base de Datos PostgreSQL.</p>
|
|
<div class="meta">
|
|
<span class="chip">Frontend: Angular 21</span>
|
|
<span class="chip">Backend: Express + Node.js</span>
|
|
<span class="chip">DB: PostgreSQL</span>
|
|
<span class="chip">Fecha: 2026-02-15</span>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>1) UML de Casos de Uso</h2>
|
|
<div class="diagram">
|
|
<svg viewBox="0 0 1120 480" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Casos de uso">
|
|
<defs>
|
|
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#50739b"/>
|
|
</marker>
|
|
</defs>
|
|
|
|
<rect x="270" y="40" width="790" height="395" rx="12" fill="#f8fcff" stroke="#a9c7df"/>
|
|
<text x="290" y="65" font-size="16" fill="#0b6aa2" font-weight="700">Sistema Plataforma Registros Sanitarios</text>
|
|
|
|
<circle cx="90" cy="120" r="26" fill="#ffffff" stroke="#829ab3"/>
|
|
<line x1="90" y1="147" x2="90" y2="205" stroke="#829ab3"/>
|
|
<line x1="62" y1="168" x2="118" y2="168" stroke="#829ab3"/>
|
|
<line x1="90" y1="205" x2="66" y2="240" stroke="#829ab3"/>
|
|
<line x1="90" y1="205" x2="114" y2="240" stroke="#829ab3"/>
|
|
<text x="40" y="270" font-size="14" fill="#2f455d">Usuario</text>
|
|
|
|
<circle cx="90" cy="320" r="26" fill="#ffffff" stroke="#829ab3"/>
|
|
<line x1="90" y1="347" x2="90" y2="405" stroke="#829ab3"/>
|
|
<line x1="62" y1="368" x2="118" y2="368" stroke="#829ab3"/>
|
|
<line x1="90" y1="405" x2="66" y2="440" stroke="#829ab3"/>
|
|
<line x1="90" y1="405" x2="114" y2="440" stroke="#829ab3"/>
|
|
<text x="20" y="460" font-size="14" fill="#2f455d">Administrador</text>
|
|
|
|
<ellipse cx="470" cy="120" rx="140" ry="34" fill="#fff" stroke="#7aa3c6"/>
|
|
<text x="392" y="125" font-size="14" fill="#173d63">Consultar registros</text>
|
|
|
|
<ellipse cx="470" cy="205" rx="140" ry="34" fill="#fff" stroke="#7aa3c6"/>
|
|
<text x="392" y="210" font-size="14" fill="#173d63">Consultar productos</text>
|
|
|
|
<ellipse cx="470" cy="290" rx="140" ry="34" fill="#fff" stroke="#7aa3c6"/>
|
|
<text x="382" y="295" font-size="14" fill="#173d63">Buscar / Filtrar / Paginar</text>
|
|
|
|
<ellipse cx="760" cy="150" rx="160" ry="34" fill="#fff" stroke="#7aa3c6"/>
|
|
<text x="675" y="155" font-size="14" fill="#173d63">Ver detalle de estado</text>
|
|
|
|
<ellipse cx="760" cy="265" rx="160" ry="34" fill="#fff" stroke="#7aa3c6"/>
|
|
<text x="655" y="270" font-size="14" fill="#173d63">Importar CSV de registros</text>
|
|
|
|
<ellipse cx="760" cy="350" rx="160" ry="34" fill="#fff" stroke="#7aa3c6"/>
|
|
<text x="675" y="355" font-size="14" fill="#173d63">Actualizar datos (refresh)</text>
|
|
|
|
<line x1="116" y1="170" x2="330" y2="120" stroke="#50739b" stroke-width="1.4" marker-end="url(#arrow)"/>
|
|
<line x1="116" y1="182" x2="330" y2="205" stroke="#50739b" stroke-width="1.4" marker-end="url(#arrow)"/>
|
|
<line x1="116" y1="194" x2="330" y2="290" stroke="#50739b" stroke-width="1.4" marker-end="url(#arrow)"/>
|
|
|
|
<line x1="118" y1="365" x2="600" y2="265" stroke="#50739b" stroke-width="1.4" marker-end="url(#arrow)"/>
|
|
<line x1="118" y1="378" x2="600" y2="350" stroke="#50739b" stroke-width="1.4" marker-end="url(#arrow)"/>
|
|
|
|
<line x1="610" y1="120" x2="640" y2="140" stroke="#809ab3" stroke-dasharray="6 5"/>
|
|
<text x="615" y="122" font-size="12" fill="#526074">include</text>
|
|
|
|
<line x1="610" y1="205" x2="640" y2="165" stroke="#809ab3" stroke-dasharray="6 5"/>
|
|
<text x="615" y="194" font-size="12" fill="#526074">include</text>
|
|
|
|
<line x1="610" y1="290" x2="640" y2="262" stroke="#809ab3" stroke-dasharray="6 5"/>
|
|
<text x="615" y="280" font-size="12" fill="#526074">extend</text>
|
|
</svg>
|
|
</div>
|
|
<p class="legend">Actor principal: <strong>Usuario</strong>. Actor con permisos ampliados: <strong>Administrador</strong> (carga CSV y actualizacion intensiva).</p>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>2) UML de Clases (Aplicacion + Datos)</h2>
|
|
<div class="diagram">
|
|
<svg viewBox="0 0 1160 640" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Diagrama de clases">
|
|
<defs>
|
|
<marker id="arrow2" markerWidth="11" markerHeight="11" refX="9" refY="3.5" orient="auto">
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#355f85"/>
|
|
</marker>
|
|
</defs>
|
|
|
|
<rect x="30" y="30" width="300" height="220" rx="10" fill="#fff" stroke="#8fb5d4"/>
|
|
<rect x="30" y="30" width="300" height="34" fill="#edf6fd" stroke="#8fb5d4"/>
|
|
<text x="40" y="52" font-weight="700" fill="#173d63">Frontend::App (semillero/src/app/app.ts)</text>
|
|
<text x="40" y="84" font-size="13" fill="#2f455d">- http: HttpClient</text>
|
|
<text x="40" y="105" font-size="13" fill="#2f455d">- view, search, limit, offset (signals)</text>
|
|
<text x="40" y="126" font-size="13" fill="#2f455d">- registros[], productos[]</text>
|
|
<line x1="30" y1="140" x2="330" y2="140" stroke="#c7d9ea"/>
|
|
<text x="40" y="154" font-size="13" fill="#2f455d">+ load()</text>
|
|
<text x="40" y="175" font-size="13" fill="#2f455d">+ uploadCsv()</text>
|
|
<text x="40" y="196" font-size="13" fill="#2f455d">+ buildParams()</text>
|
|
<text x="40" y="217" font-size="13" fill="#2f455d">+ setView(), nextPage(), prevPage()</text>
|
|
|
|
<rect x="400" y="30" width="320" height="220" rx="10" fill="#fff" stroke="#8fb5d4"/>
|
|
<rect x="400" y="30" width="320" height="34" fill="#edf6fd" stroke="#8fb5d4"/>
|
|
<text x="410" y="52" font-weight="700" fill="#173d63">Backend::Express API (backend/server.js)</text>
|
|
<text x="410" y="84" font-size="13" fill="#2f455d">- app: Express</text>
|
|
<text x="410" y="105" font-size="13" fill="#2f455d">- pool: pg.Pool</text>
|
|
<text x="410" y="126" font-size="13" fill="#2f455d">- upload: multer</text>
|
|
<line x1="400" y1="140" x2="720" y2="140" stroke="#c7d9ea"/>
|
|
<text x="410" y="154" font-size="13" fill="#2f455d">+ GET /api/health</text>
|
|
<text x="410" y="175" font-size="13" fill="#2f455d">+ GET /api/productos</text>
|
|
<text x="410" y="196" font-size="13" fill="#2f455d">+ GET /api/registros</text>
|
|
<text x="410" y="217" font-size="13" fill="#2f455d">+ POST /api/registros/import</text>
|
|
|
|
<rect x="790" y="30" width="330" height="220" rx="10" fill="#fff" stroke="#8fb5d4"/>
|
|
<rect x="790" y="30" width="330" height="34" fill="#edf6fd" stroke="#8fb5d4"/>
|
|
<text x="800" y="52" font-weight="700" fill="#173d63">Servicio Importacion CSV (funciones en server.js)</text>
|
|
<text x="800" y="84" font-size="13" fill="#2f455d">+ cleanValue(), normalizeDate()</text>
|
|
<text x="800" y="105" font-size="13" fill="#2f455d">+ dedupeRows(), buildInsertQuery()</text>
|
|
<text x="800" y="126" font-size="13" fill="#2f455d">+ getActorId() cacheado</text>
|
|
<line x1="790" y1="140" x2="1120" y2="140" stroke="#c7d9ea"/>
|
|
<text x="800" y="154" font-size="13" fill="#2f455d">Responsabilidad:</text>
|
|
<text x="800" y="175" font-size="13" fill="#2f455d">- parsear CSV y mapear columnas</text>
|
|
<text x="800" y="196" font-size="13" fill="#2f455d">- upsert a productos/registros/actores</text>
|
|
|
|
<rect x="30" y="315" width="340" height="285" rx="10" fill="#fff" stroke="#97bfdc"/>
|
|
<rect x="30" y="315" width="340" height="34" fill="#edf6fd" stroke="#97bfdc"/>
|
|
<text x="40" y="337" font-weight="700" fill="#173d63">DB::productos</text>
|
|
<text x="40" y="368" font-size="13" fill="#2f455d">+ id_producto: text (PK)</text>
|
|
<text x="40" y="389" font-size="13" fill="#2f455d">+ nombre_comercial: text</text>
|
|
<text x="40" y="410" font-size="13" fill="#2f455d">+ categoria_general: text</text>
|
|
<text x="40" y="431" font-size="13" fill="#2f455d">+ grupo, marca, presentacion_comercial</text>
|
|
<text x="40" y="452" font-size="13" fill="#2f455d">+ vida_util, nivel_riesgo, usos</text>
|
|
|
|
<rect x="410" y="315" width="340" height="285" rx="10" fill="#fff" stroke="#97bfdc"/>
|
|
<rect x="410" y="315" width="340" height="34" fill="#edf6fd" stroke="#97bfdc"/>
|
|
<text x="420" y="337" font-weight="700" fill="#173d63">DB::registros_sanitarios</text>
|
|
<text x="420" y="368" font-size="13" fill="#2f455d">+ id_registro: text (PK)</text>
|
|
<text x="420" y="389" font-size="13" fill="#2f455d">+ id_producto: text (FK productos)</text>
|
|
<text x="420" y="410" font-size="13" fill="#2f455d">+ expediente, estado_registro</text>
|
|
<text x="420" y="431" font-size="13" fill="#2f455d">+ fecha_vencimiento, modalidad</text>
|
|
<text x="420" y="452" font-size="13" fill="#2f455d">+ observaciones</text>
|
|
<text x="420" y="473" font-size="13" fill="#2f455d">+ id_titular: bigint (FK actores)</text>
|
|
<text x="420" y="494" font-size="13" fill="#2f455d">+ id_rol: bigint (FK actores)</text>
|
|
|
|
<rect x="790" y="315" width="330" height="285" rx="10" fill="#fff" stroke="#97bfdc"/>
|
|
<rect x="790" y="315" width="330" height="34" fill="#edf6fd" stroke="#97bfdc"/>
|
|
<text x="800" y="337" font-weight="700" fill="#173d63">DB::actores</text>
|
|
<text x="800" y="368" font-size="13" fill="#2f455d">+ id_actor: bigserial (PK)</text>
|
|
<text x="800" y="389" font-size="13" fill="#2f455d">+ tipo: text (Titular|Rol)</text>
|
|
<text x="800" y="410" font-size="13" fill="#2f455d">+ nombre, ciudad, pais</text>
|
|
<text x="800" y="431" font-size="13" fill="#2f455d">+ rol_actividad</text>
|
|
<text x="800" y="452" font-size="13" fill="#2f455d">UNIQUE(tipo,nombre,ciudad,pais,rol_actividad)</text>
|
|
|
|
<line x1="330" y1="130" x2="400" y2="130" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow2)"/>
|
|
<text x="334" y="120" font-size="12" fill="#355f85">HTTP /api/*</text>
|
|
|
|
<line x1="720" y1="160" x2="790" y2="160" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow2)"/>
|
|
<text x="730" y="150" font-size="12" fill="#355f85">usa</text>
|
|
|
|
<line x1="560" y1="250" x2="560" y2="315" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow2)"/>
|
|
<text x="568" y="286" font-size="12" fill="#355f85">SQL</text>
|
|
|
|
<line x1="955" y1="250" x2="955" y2="315" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow2)"/>
|
|
<text x="963" y="286" font-size="12" fill="#355f85">upsert actores</text>
|
|
|
|
<line x1="370" y1="450" x2="410" y2="450" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow2)"/>
|
|
<text x="364" y="440" font-size="12" fill="#355f85">1..* FK</text>
|
|
|
|
<line x1="750" y1="470" x2="790" y2="470" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow2)"/>
|
|
<text x="660" y="462" font-size="12" fill="#355f85">id_titular / id_rol</text>
|
|
</svg>
|
|
</div>
|
|
<p class="legend">Las clases de dominio visibles en frontend son <code>Producto</code> y <code>Registro</code>. El backend concentra logica de consulta, paginacion y proceso ETL (CSV -> tablas relacionales).</p>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>3) UML de Interacciones (Secuencia)</h2>
|
|
<div class="diagram">
|
|
<svg viewBox="0 0 1160 560" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Secuencia consulta e importacion">
|
|
<defs>
|
|
<marker id="arrow3" markerWidth="11" markerHeight="11" refX="9" refY="3.5" orient="auto">
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#355f85"/>
|
|
</marker>
|
|
<marker id="arrow3resp" markerWidth="11" markerHeight="11" refX="9" refY="3.5" orient="auto">
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#5f7f9f"/>
|
|
</marker>
|
|
</defs>
|
|
|
|
<text x="20" y="30" font-weight="700" fill="#0b6aa2">A) Flujo de consulta (registros/productos)</text>
|
|
|
|
<text x="60" y="58" font-size="13" fill="#173d63">Usuario</text>
|
|
<text x="290" y="58" font-size="13" fill="#173d63">Frontend App</text>
|
|
<text x="560" y="58" font-size="13" fill="#173d63">Express API</text>
|
|
<text x="850" y="58" font-size="13" fill="#173d63">PostgreSQL</text>
|
|
|
|
<line x1="90" y1="70" x2="90" y2="255" stroke="#a1b2c5" stroke-dasharray="5 5"/>
|
|
<line x1="330" y1="70" x2="330" y2="255" stroke="#a1b2c5" stroke-dasharray="5 5"/>
|
|
<line x1="610" y1="70" x2="610" y2="255" stroke="#a1b2c5" stroke-dasharray="5 5"/>
|
|
<line x1="900" y1="70" x2="900" y2="255" stroke="#a1b2c5" stroke-dasharray="5 5"/>
|
|
|
|
<line x1="100" y1="95" x2="320" y2="95" stroke="#355f85" marker-end="url(#arrow3)"/>
|
|
<text x="120" y="88" font-size="12" fill="#355f85">1) Buscar / cambiar vista / paginar</text>
|
|
|
|
<line x1="340" y1="128" x2="600" y2="128" stroke="#355f85" marker-end="url(#arrow3)"/>
|
|
<text x="360" y="121" font-size="12" fill="#355f85">2) GET /api/registros|productos?q,limit,offset</text>
|
|
|
|
<line x1="620" y1="160" x2="890" y2="160" stroke="#355f85" marker-end="url(#arrow3)"/>
|
|
<text x="640" y="153" font-size="12" fill="#355f85">3) SELECT + JOIN + ORDER + LIMIT/OFFSET</text>
|
|
|
|
<line x1="890" y1="192" x2="620" y2="192" stroke="#355f85" marker-end="url(#arrow3)"/>
|
|
<text x="700" y="186" font-size="12" fill="#355f85">4) rows[]</text>
|
|
|
|
<line x1="600" y1="224" x2="340" y2="224" stroke="#355f85" marker-end="url(#arrow3)"/>
|
|
<text x="410" y="218" font-size="12" fill="#355f85">5) JSON {items, limit, offset}</text>
|
|
|
|
<line x1="320" y1="248" x2="100" y2="248" stroke="#355f85" marker-end="url(#arrow3)"/>
|
|
<text x="145" y="241" font-size="12" fill="#355f85">6) Render tabla + estado en UI</text>
|
|
|
|
<text x="20" y="315" font-weight="700" fill="#0b6aa2">B) Flujo de importacion CSV</text>
|
|
|
|
<line x1="90" y1="335" x2="90" y2="540" stroke="#a1b2c5" stroke-dasharray="5 5"/>
|
|
<line x1="330" y1="335" x2="330" y2="540" stroke="#a1b2c5" stroke-dasharray="5 5"/>
|
|
<line x1="610" y1="335" x2="610" y2="540" stroke="#a1b2c5" stroke-dasharray="5 5"/>
|
|
<line x1="900" y1="335" x2="900" y2="540" stroke="#a1b2c5" stroke-dasharray="5 5"/>
|
|
|
|
<line x1="100" y1="365" x2="320" y2="365" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow3)"/>
|
|
<text x="120" y="358" font-size="12" fill="#355f85">1) Selecciona archivo CSV y enviar</text>
|
|
|
|
<line x1="340" y1="398" x2="600" y2="398" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow3)"/>
|
|
<text x="355" y="391" font-size="12" fill="#355f85">2) POST /api/registros/import (multipart/form-data)</text>
|
|
|
|
<line x1="620" y1="430" x2="890" y2="430" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow3)"/>
|
|
<text x="640" y="423" font-size="12" fill="#355f85">3) upsert actores, productos y registros (batch)</text>
|
|
|
|
<line x1="890" y1="462" x2="620" y2="462" stroke="#5f7f9f" stroke-width="1.6" stroke-dasharray="7 5" marker-end="url(#arrow3resp)"/>
|
|
<text x="705" y="456" font-size="12" fill="#355f85">4) conteos procesados (respuesta)</text>
|
|
|
|
<line x1="600" y1="494" x2="340" y2="494" stroke="#5f7f9f" stroke-width="1.6" stroke-dasharray="7 5" marker-end="url(#arrow3resp)"/>
|
|
<text x="382" y="488" font-size="12" fill="#355f85">5) JSON {processed, inserted, skipped}</text>
|
|
|
|
<line x1="320" y1="525" x2="100" y2="525" stroke="#355f85" stroke-width="1.6" marker-end="url(#arrow3)"/>
|
|
<text x="123" y="518" font-size="12" fill="#355f85">6) Mensaje de exito + recarga /api/registros</text>
|
|
</svg>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>4) Puentes entre Frontend, Backend, Archivos y APIs</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Flujo</th>
|
|
<th>Frontend (archivo/metodo)</th>
|
|
<th>API</th>
|
|
<th>Backend (archivo/logica)</th>
|
|
<th>Datos involucrados</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Consulta de productos</td>
|
|
<td><code>semillero/src/app/app.ts</code><br/>metodo <code>load()</code> con <code>endpoint='productos'</code></td>
|
|
<td><code>GET /api/productos?q=&limit=&offset=</code></td>
|
|
<td><code>backend/server.js</code><br/>ruta <code>app.get('/api/productos')</code>, SQL filtrado + paginacion</td>
|
|
<td>Tabla <code>productos</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Consulta de registros</td>
|
|
<td><code>semillero/src/app/app.ts</code><br/>metodo <code>load()</code> con <code>endpoint='registros'</code></td>
|
|
<td><code>GET /api/registros?q=&limit=&offset=</code></td>
|
|
<td><code>backend/server.js</code><br/>ruta <code>app.get('/api/registros')</code>, LEFT JOIN con actores/productos</td>
|
|
<td>Tablas <code>registros_sanitarios</code>, <code>productos</code>, <code>actores</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Importacion CSV</td>
|
|
<td><code>semillero/src/app/app.ts</code><br/>metodo <code>uploadCsv()</code> con <code>FormData(file)</code></td>
|
|
<td><code>POST /api/registros/import</code></td>
|
|
<td><code>backend/server.js</code><br/>multer + csv-parse + upserts por lotes</td>
|
|
<td>Inserta/actualiza <code>actores</code>, <code>productos</code>, <code>registros_sanitarios</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Proxy de desarrollo</td>
|
|
<td><code>semillero/proxy.conf.json</code><br/>redirige <code>/api</code> a backend local</td>
|
|
<td><code>/api -> http://localhost:3000</code></td>
|
|
<td>Permite que Angular (<code>4200</code>) consuma Express (<code>3000</code>) sin CORS local</td>
|
|
<td>Canal de integracion FE/BE</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p class="small">Modelo de datos formal definido en <code>backend/comando.sql</code>. Endpoints y proceso ETL definidos en <code>backend/server.js</code>. La UI que consume estas APIs esta en <code>semillero/src/app/app.ts</code> y <code>semillero/src/app/app.html</code>.</p>
|
|
<!-- <p class="small ok">Salida lista para presentacion tecnica de semillero.</p> -->
|
|
</section>
|
|
</main>
|
|
</body>
|
|
</html>
|