feat(A08): Ámbito dtic-NOTAS creado - Sistema web de notas/informes
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>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
2ab51af7f1
commit
85645ce0b8
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
Reference in New Issue
Block a user