Nuevo ámbito A08 para gestión de notas e informes formales: Arquitectura: - Separación contenido (archivos .md) de presentación (web HTML) - Plantillas configurables para diferentes tipos de notas - Vista previa con membrete institucional UTN-FRLR - Impresión/PDF via window.print() con CSS @media print Archivos creados: - docs/ambito/dtic-NOTAS/A08_dtic-NOTAS.md (manifiesto) - docs/ambito/dtic-NOTAS/A08.P001_Sistema-Web-Notas.md (plan) - docs/ambito/dtic-NOTAS/app/index.html (interfaz) - docs/ambito/dtic-NOTAS/app/styles.css (estilos institucionales) - docs/ambito/dtic-NOTAS/app/app.js (lógica + marked.js CDN) - docs/ambito/dtic-NOTAS/plantillas/nota-simple.md - docs/ambito/dtic-NOTAS/contenido/2026/ejemplo-nota.md Migración: - dtic-NOTAs/ movido a docs/ambito/dtic-NOTAS/dtic-NOTAs/ - Histórico 2019-2025 preservado en .Hist/ - Convención de nombres documentada: Nota-<AÑO><NUMERO>_<Asunto>.<ext> Uso: 1. Abrir docs/ambito/dtic-NOTAS/app/index.html en navegador 2. Seleccionar plantilla o cargar archivo .md 3. Completar datos de nota 4. Generar vista previa e imprimir Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
319 lines
8.3 KiB
JavaScript
319 lines
8.3 KiB
JavaScript
/**
|
|
* Generador de Notas - DTIC UTN-FRLR
|
|
* Lógica de la aplicación web
|
|
*/
|
|
|
|
// Estado de la aplicación
|
|
let estado = {
|
|
plantillaCargada: null,
|
|
contenidoCargado: null,
|
|
datosNota: {}
|
|
};
|
|
|
|
// Inicialización
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Establecer fecha actual por defecto
|
|
document.getElementById('fecha').valueAsDate = new Date();
|
|
|
|
// Cargar último número disponible (simulado)
|
|
document.getElementById('numero').placeholder = 'Ej: ' + generarNumeroSugerido();
|
|
});
|
|
|
|
/**
|
|
* Genera un número sugerido basado en la fecha
|
|
* Formato: AAMM (ej: 2604 para abril 2026)
|
|
*/
|
|
function generarNumeroSugerido() {
|
|
const fecha = new Date();
|
|
const año = fecha.getFullYear() % 100;
|
|
const mes = String(fecha.getMonth() + 1).padStart(2, '0');
|
|
return `${año}${mes}`;
|
|
}
|
|
|
|
/**
|
|
* Carga la plantilla seleccionada
|
|
*/
|
|
function cargarPlantilla() {
|
|
const plantilla = document.getElementById('plantilla').value;
|
|
|
|
if (!plantilla) {
|
|
alert('Por favor seleccioná una plantilla');
|
|
return;
|
|
}
|
|
|
|
// Plantillas embebidas
|
|
const plantillas = {
|
|
'nota-simple': `---
|
|
tipo: nota-simple
|
|
numero: ${document.getElementById('numero').value || 'XXXX'}
|
|
fecha: ${document.getElementById('fecha').value || '____'}
|
|
destinatario: [Nombre del Destinatario]
|
|
remitente: DTIC - UTN FRLR
|
|
asunto: [Asunto de la nota]
|
|
---
|
|
|
|
# Nota Simple
|
|
|
|
Estimado/a [Nombre]:
|
|
|
|
Por medio de la presente, me dirijo a usted para [propósito de la nota].
|
|
|
|
[Cuerpo principal de la nota - desarrollar el tema]
|
|
|
|
Sin otro particular, saludo a usted atentamente.
|
|
|
|
`,
|
|
|
|
'informe-tecnico': `---
|
|
tipo: informe-tecnico
|
|
numero: ${document.getElementById('numero').value || 'XXXX'}
|
|
fecha: ${document.getElementById('fecha').value || '____'}
|
|
destinatario: [Nombre del Destinatario]
|
|
remitente: DTIC - UTN FRLR
|
|
asunto: Informe Técnico sobre [tema]
|
|
---
|
|
|
|
# Informe Técnico
|
|
|
|
## 1. Objeto del Informe
|
|
|
|
El presente informe tiene por objeto [describir el propósito].
|
|
|
|
## 2. Antecedentes
|
|
|
|
[Describir contexto y situación previa]
|
|
|
|
## 3. Análisis Técnico
|
|
|
|
[Desarrollar análisis detallado]
|
|
|
|
## 4. Conclusiones
|
|
|
|
[Conclusiones y recomendaciones]
|
|
|
|
## 5. Anexos
|
|
|
|
- Anexo A: [Descripción]
|
|
- Anexo B: [Descripción]
|
|
|
|
`,
|
|
|
|
'memorando': `---
|
|
tipo: memorando
|
|
numero: ${document.getElementById('numero').value || 'XXXX'}
|
|
fecha: ${document.getElementById('fecha').value || '____'}
|
|
destinatario: [Personal de DTIC / Nombre]
|
|
remitente: Dirección DTIC
|
|
asunto: [Asunto del memorando]
|
|
---
|
|
|
|
# Memorando
|
|
|
|
**PARA:** [Destinatario]
|
|
|
|
**DE:** [Remitente]
|
|
|
|
**FECHA:** [Fecha]
|
|
|
|
**ASUNTO:** [Asunto]
|
|
|
|
---
|
|
|
|
[Cuerpo del memorando]
|
|
|
|
Se comunica a todo el personal que [contenido].
|
|
|
|
Es importante destacar que [puntos clave].
|
|
|
|
Quedan a disposición para cualquier consulta.
|
|
|
|
`,
|
|
|
|
'solicitud': `---
|
|
tipo: solicitud
|
|
numero: ${document.getElementById('numero').value || 'XXXX'}
|
|
fecha: ${document.getElementById('fecha').value || '____'}
|
|
destinatario: [Autoridad/Área]
|
|
remitente: DTIC - UTN FRLR
|
|
asunto: Solicitud de [qué se solicita]
|
|
---
|
|
|
|
# Nota de Solicitud
|
|
|
|
Por medio de la presente, me dirijo a usted con el fin de solicitar [describir lo que se solicita].
|
|
|
|
**Fundamentación:**
|
|
|
|
[Explicar razones y necesidad de lo solicitado]
|
|
|
|
**Especificaciones:**
|
|
|
|
- [Ítem 1]
|
|
- [Ítem 2]
|
|
- [Ítem 3]
|
|
|
|
Agradeciendo de antemano su atención, saludo a usted atentamente.
|
|
|
|
`
|
|
};
|
|
|
|
const contenido = plantillas[plantilla];
|
|
document.getElementById('editorTexto').value = contenido;
|
|
estado.plantillaCargada = plantilla;
|
|
|
|
// Extraer metadatos del frontmatter
|
|
extraerMetadatos(contenido);
|
|
|
|
alert('Plantilla "' + plantilla + '" cargada. Editá el contenido y generá la vista previa.');
|
|
}
|
|
|
|
/**
|
|
* Carga contenido desde archivo .md
|
|
*/
|
|
function cargarContenido() {
|
|
const input = document.getElementById('contenido');
|
|
|
|
if (!input.files || !input.files[0]) {
|
|
alert('Por favor seleccioná un archivo .md');
|
|
return;
|
|
}
|
|
|
|
const archivo = input.files[0];
|
|
const lector = new FileReader();
|
|
|
|
lector.onload = (e) => {
|
|
document.getElementById('editorTexto').value = e.target.result;
|
|
estado.contenidoCargado = archivo.name;
|
|
extraerMetadatos(e.target.result);
|
|
alert('Contenido cargado desde: ' + archivo.name);
|
|
};
|
|
|
|
lector.readAsText(archivo);
|
|
}
|
|
|
|
/**
|
|
* Extrae metadatos del frontmatter (YAML entre ---)
|
|
*/
|
|
function extraerMetadatos(contenido) {
|
|
const match = contenido.match(/^---\n([\s\S]*?)\n---/);
|
|
|
|
if (match) {
|
|
const frontmatter = match[1];
|
|
|
|
// Extraer campos individuales
|
|
const campos = {
|
|
numero: frontmatter.match(/numero:\s*(.+)/)?.[1]?.trim(),
|
|
fecha: frontmatter.match(/fecha:\s*(.+)/)?.[1]?.trim(),
|
|
destinatario: frontmatter.match(/destinatario:\s*(.+)/)?.[1]?.trim(),
|
|
asunto: frontmatter.match(/asunto:\s*(.+)/)?.[1]?.trim()
|
|
};
|
|
|
|
// Completar formulario
|
|
if (campos.numero && campos.numero !== 'XXXX') {
|
|
document.getElementById('numero').value = campos.numero;
|
|
}
|
|
if (campos.fecha && campos.fecha !== '____') {
|
|
document.getElementById('fecha').value = campos.fecha;
|
|
}
|
|
if (campos.destinatario && !campos.destinatario.includes('[')) {
|
|
document.getElementById('destinatario').value = campos.destinatario;
|
|
}
|
|
if (campos.asunto && !campos.asunto.includes('[')) {
|
|
document.getElementById('asunto').value = campos.asunto;
|
|
}
|
|
|
|
estado.datosNota = { ...estado.datosNota, ...campos };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Genera la vista previa del documento
|
|
*/
|
|
function generarVistaPrevia() {
|
|
const contenido = document.getElementById('editorTexto').value;
|
|
|
|
// Obtener datos del formulario
|
|
const datos = {
|
|
numero: document.getElementById('numero').value,
|
|
fecha: formatearFecha(document.getElementById('fecha').value),
|
|
destinatario: document.getElementById('destinatario').value,
|
|
asunto: document.getElementById('asunto').value
|
|
};
|
|
|
|
// Separar frontmatter del cuerpo
|
|
const cuerpo = contenido.replace(/^---\n[\s\S]*?\n---\n/, '');
|
|
|
|
// Renderizar markdown a HTML
|
|
const htmlCuerpo = marked.parse(cuerpo);
|
|
|
|
// Construir documento
|
|
const template = document.getElementById('templateNota');
|
|
const clone = template.content.cloneNode(true);
|
|
|
|
// Completar datos
|
|
clone.querySelector('.campo-numero').textContent = datos.numero || '____';
|
|
clone.querySelector('.campo-fecha').textContent = datos.fecha || '____';
|
|
clone.querySelector('.campo-destinatario').textContent = datos.destinatario || '____';
|
|
clone.querySelector('.campo-asunto').textContent = datos.asunto || '____';
|
|
clone.querySelector('.cuerpo').innerHTML = htmlCuerpo;
|
|
|
|
// Actualizar vista previa
|
|
const preview = document.getElementById('vistaPrevia');
|
|
preview.innerHTML = '';
|
|
preview.appendChild(clone);
|
|
|
|
// Scroll a la vista previa
|
|
preview.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
|
|
/**
|
|
* Formatea fecha de YYYY-MM-DD a DD/MM/YYYY
|
|
*/
|
|
function formatearFecha(fechaISO) {
|
|
if (!fechaISO) return '____';
|
|
const [año, mes, dia] = fechaISO.split('-');
|
|
return `${dia}/${mes}/${año}`;
|
|
}
|
|
|
|
/**
|
|
* Imprime o genera PDF del documento
|
|
*/
|
|
function imprimir() {
|
|
const vistaPrevia = document.getElementById('vistaPrevia');
|
|
|
|
if (!vistaPrevia.innerHTML.trim() || vistaPrevia.querySelector('.placeholder')) {
|
|
alert('Por favor generá primero la vista previa');
|
|
return;
|
|
}
|
|
|
|
// Crear ventana de impresión
|
|
const contenido = vistaPrevia.innerHTML;
|
|
|
|
const ventana = window.open('', '_blank');
|
|
ventana.document.write(`
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Nota ${document.getElementById('numero').value || '____'}</title>
|
|
<style>
|
|
${document.querySelector('style').innerHTML}
|
|
body { background: white; }
|
|
.sidebar, .editor { display: none; }
|
|
.container { display: block; padding: 0; }
|
|
.preview { position: absolute; width: 100%; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
${contenido}
|
|
<script>
|
|
window.onload = () => {
|
|
window.print();
|
|
window.onafterprint = () => window.close();
|
|
};
|
|
</script>
|
|
</body>
|
|
</html>
|
|
`);
|
|
ventana.document.close();
|
|
}
|