Agregar resp membresias

This commit is contained in:
Pablinux
2025-06-02 23:17:28 -05:00
parent ac9e63e7d9
commit 1b4809116b
20 changed files with 6582 additions and 35 deletions

View File

@@ -1,7 +1,8 @@
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const mysql = require('mysql');
//const mysql = require('mysql');
const mysql2 = require('mysql2'); // Importa mysql2
const cloud_file = require('express-fileupload');
const myConecction = require('express-myconnection');
const cors_origins = require('cors');
@@ -23,6 +24,7 @@ const generalesRutas = require('./rutas/rt_Generales');
const cloud_rutas = require('./rutas/rt_cloud');
const app_restaurant = require('./rutas/rt_apps');
const app_arduino = require('./rutas/rt_arduino');
const proyectos = require('./rutas/rt_proyectos');
//configuraciones
app.set('port',process.env.PORT||puerto);
@@ -46,7 +48,7 @@ app.use(session({
//middlewares
app.use(express.static(__dirname+'/public'));//para usar la carpeta public *js*css*img
app.use(morgan('dev'));
app.use(myConecction(mysql,{//conexion bd
app.use(myConecction(mysql2,{ // <-- Usa 'mysql2' aquí
host:global.config.db.host,
user: global.config.db.user,
password:global.config.db.pswd,
@@ -73,6 +75,7 @@ app.use('/', generalesRutas);
app.use('/', cloud_rutas);
app.use('/', app_restaurant);
app.use('/', app_arduino);
app.use('/', proyectos);
//prueba de json directa
app.get('/pruebaJson',function(req,res){

View File

@@ -40,4 +40,10 @@ controlador.upload = (req, res) => {
res.render('video_upload')
};
/** aqui iniciamo la vista de ejs **/
controlador.config = (req, res) => {
//res.render('speedtest');
res.render('configuracion');
};
module.exports = controlador;

View File

@@ -135,6 +135,100 @@ controlador.eliminarCliente = (req, res) => {
});
}
/**** Listar miembros *****/
controlador.listarMiembros = (req, res) => {
req.getConnection((err, conn) => {
if (err) {
return res.status(500).json({ error: 'Error de conexión' });
}
conn.query('SELECT * FROM miembros ORDER BY nombre ASC', (err, miembros) => {
if (err) {
return res.status(500).json({ error: 'Error al consultar miembros' });
}
res.json(miembros);
});
});
};
/**** Obtener miembro para edición (ahora vía /api/miembros/:id) *****/
controlador.editarMiembro = (req, res) => {
const { id } = req.params; // *** MODIFICADO: id ahora viene de req.params ***
req.getConnection((err, conn) => {
if (err) {
return res.status(500).json({ error: 'Error de conexión' });
}
conn.query('SELECT * FROM miembros WHERE id = ?', [id], (err, miembro) => {
if (err) {
return res.status(500).json({ error: 'Error al consultar miembro' });
}
if (miembro.length === 0) {
return res.status(404).json({ error: 'Miembro no encontrado' });
}
res.json(miembro[0]);
});
});
};
/**** Guardar/Crear nuevo miembro (vía POST /api/miembros) *****/
controlador.guardarMiembro = (req, res) => {
const miembro = req.body; // Obtener los datos del miembro desde el cuerpo de la solicitud
req.getConnection((err, conn) => {
if (err) {
return res.status(500).json({ error: 'Error de conexión' });
}
conn.query('INSERT INTO miembros SET ?', miembro, (err, result) => {
if (err) {
return res.status(500).json({ error: 'Error al guardar miembro' });
}
res.status(201).json({ id: result.insertId, ...miembro, message: 'Miembro creado exitosamente' }); // 201 Created
});
});
};
/**** Actualizar miembro existente (vía PUT /api/miembros/:id) *****/
controlador.actualizarMiembro = (req, res) => {
const { id } = req.params; // *** MODIFICADO: id ahora viene de req.params ***
const miembroActualizado = req.body; // Obtener los datos actualizados del miembro desde el cuerpo de la solicitud
req.getConnection((err, conn) => {
if (err) {
return res.status(500).json({ error: 'Error de conexión' });
}
conn.query('UPDATE miembros SET ? WHERE id = ?', [miembroActualizado, id], (err, result) => {
if (err) {
return res.status(500).json({ error: 'Error al actualizar miembro' });
}
if (result.affectedRows === 0) {
return res.status(404).json({ error: 'Miembro no encontrado' });
}
res.json({ message: 'Miembro actualizado exitosamente' });
});
});
};
/**** Eliminar miembro (vía DELETE /api/miembros/:id) *****/
controlador.eliminarMiembro = (req, res) => {
const { id } = req.params; // *** MODIFICADO: id ahora viene de req.params ***
req.getConnection((err, conn) => {
if (err) {
return res.status(500).json({ error: 'Error de conexión' });
}
conn.query('DELETE FROM miembros WHERE id = ?', [id], (err, result) => {
if (err) {
return res.status(500).json({ error: 'Error al eliminar miembro' });
}
if (result.affectedRows === 0) {
return res.status(404).json({ error: 'Miembro no encontrado' });
}
res.json({ message: 'Miembro eliminado exitosamente' });
});
});
};
/**
* controlador para obtener las ciudades de la base de datos
* @param {*} req

File diff suppressed because it is too large Load Diff

View File

@@ -10,4 +10,7 @@ rutas.get('/users', controlador_init.user);//devuelve usuaios con el el ROL mese
rutas.get('/speedtest', controlador_init.speedtest);//testing velocimetro server
rutas.get('/videos', controlador_init.videos);//videos sigma server
rutas.get('/video_upload', controlador_init.upload);//videos sigma server
/*** CONFIGURACION DE UN PAGINA QUE CONFIGURE EL ARCHIVO config.js ***/
rutas.get('/configuracion', controlador_init.config);//llama a un view de configuracion del archivo config.js
module.exports = rutas;

View File

@@ -18,9 +18,22 @@ rutas.post('/actCliente/:client_id', controladorClientes.modificaCliente);//actu
//eliminar clientes
rutas.get('/eliminarCliente/:client_id', controladorClientes.eliminarCliente);
//almacena cliente
rutas.post('/addCliente', controladorClientes.guardaCliente);//almacena en bd el nuevo cliente
rutas.post('/addCliente', controladorClientes.guardaCliente);//almacena en bd el nuevo cliente: https://app.factura-e.net/addCliente
rutas.get('/addClienteForm', controladorClientes.verFormNclientes);//muesta form para crear cliente
// --- Rutas RESTful para Miembros --- https://app.factura-e.net/api/miembros
// Listar todos los miembros (GET /api/miembros)
rutas.get('/api/miembros', controladorClientes.listarMiembros);
// Obtener un miembro específico por ID (GET /api/miembros/:id)
// Usado para "editar" (obtener datos para un formulario de edición)
rutas.get('/api/miembros/:id', controladorClientes.editarMiembro);
// Crear un nuevo miembro (POST /api/miembros)
rutas.post('/api/miembros', controladorClientes.guardarMiembro);
// Actualizar un miembro existente por ID (PUT /api/miembros/:id)
rutas.put('/api/miembros/:id', controladorClientes.actualizarMiembro);
// Eliminar un miembro por ID (DELETE /api/miembros/:id)
rutas.delete('/api/miembros/:id', controladorClientes.eliminarMiembro);
// Ruta para obtener ciudades
rutas.get('/api/ciudades', controladorClientes.obtenerCiudades);

947
src/rutas/rt_proyectos.js Normal file
View File

@@ -0,0 +1,947 @@
// src/rutas/rt_proyectos.js
// Importar el módulo express para crear el router
const express = require('express');
const router = express.Router();
// Importar el controlador de proyectos
// Asumo que el controlador estará en 'src/controladores/controlador_proyecto.js'
const controlador_proyecto = require('../controladores/controlador_proyecto');
// --- Rutas para las VISTAS EJS (Renderizan archivos EJS) ---
/**
* @swagger
* /panel_proyectos:
* get:
* summary: Muestra el dashboard principal de gestión de proyectos.
* tags: [Proyectos]
* responses:
* 200:
* description: Dashboard de proyectos renderizado exitosamente.
* content:
* text/html:
* schema:
* type: string
* example: |
* <!DOCTYPE html>... (HTML del dashboard de proyectos)
* 500:
* description: Error del servidor al cargar los proyectos.
*/
router.get('/panel_proyectos', controlador_proyecto.panel_proyectos);
/**
* @swagger
* /proyecto_memoria_tecnica:
* get:
* summary: Muestra el formulario para crear o editar una Memoria Técnica.
* tags: [Proyectos]
* parameters:
* - in: query
* name: proyectoId
* schema:
* type: integer
* description: ID del proyecto para pre-seleccionar en el formulario.
* - in: query
* name: memoriaId
* schema:
* type: integer
* description: ID de la Memoria Técnica a editar.
* responses:
* 200:
* description: Formulario de Memoria Técnica renderizado exitosamente.
* content:
* text/html:
* schema:
* type: string
* example: |
* <!DOCTYPE html>... (HTML del formulario de memoria técnica)
* 500:
* description: Error del servidor al cargar el formulario.
*/
router.get('/proyecto_memoria_tecnica', controlador_proyecto.memoriaTecnica);
/**
* @swagger
* /lista_memorias_tecnicas:
* get:
* summary: Muestra una tabla con todas las memorias técnicas.
* tags: [Proyectos]
* responses:
* 200:
* description: Lista de memorias técnicas renderizada exitosamente.
* content:
* text/html:
* schema:
* type: string
* example: |
* <!DOCTYPE html>... (HTML de la lista de memorias)
* 500:
* description: Error del servidor al cargar la lista.
*/
router.get('/lista_memorias_tecnicas', controlador_proyecto.listarMemoriasTecnicas);
// --- Rutas de la API RESTful (Para operaciones CRUD de datos) ---
// --- Rutas para la Memoria Técnica Principal ---
/**
* @swagger
* /api/proyectos/memorias-tecnicas:
* post:
* summary: Crea una nueva Memoria Técnica.
* tags: [Memoria Técnica API]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - id_proyecto
* - id_empresa_autora
* - objetivo_memoria_especifico
* properties:
* id_proyecto:
* type: integer
* description: ID del proyecto asociado.
* id_empresa_autora:
* type: integer
* description: ID de la empresa autora de la memoria.
* objetivo_memoria_especifico:
* type: string
* description: Objetivo específico de esta memoria técnica.
* descripcion_introduccion:
* type: string
* description: Descripción introductoria de la memoria.
* funcionamiento_operacion:
* type: string
* description: Detalles de funcionamiento y operación.
* conectividad_red:
* type: string
* description: Información sobre conectividad de red.
* acceso_remoto_aplicaciones:
* type: string
* description: Detalles de acceso remoto y aplicaciones.
* seguridad_fisica_logica:
* type: string
* responses:
* 201:
* description: Memoria Técnica creada exitosamente.
* content:
* application/json:
* schema:
* type: object
* properties:
* message:
* type: string
* example: Memoria técnica creada exitosamente
* id:
* type: integer
* example: 1
* 400:
* description: Datos de entrada inválidos.
* 500:
* description: Error del servidor.
*/
router.post('/api/proyectos/memorias-tecnicas', controlador_proyecto.createMemoriaTecnica);
/**
* @swagger
* /api/proyectos/memorias-tecnicas/{id}:
* get:
* summary: Obtiene una Memoria Técnica por su ID.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica a obtener.
* responses:
* 200:
* description: Detalles de la Memoria Técnica.
* content:
* application/json:
* schema:
* type: object
* properties:
* id: { type: integer, example: 1 }
* id_proyecto: { type: integer, example: 101 }
* objetivo_memoria_especifico: { type: string, example: "Documentar instalación de sistema CCTV" }
* descripcion_introduccion: { type: string, example: "Esta memoria detalla la configuración inicial del sistema." }
* 404:
* description: Memoria Técnica no encontrada.
* 500:
* description: Error del servidor.
*/
router.get('/api/proyectos/memorias-tecnicas/:id', controlador_proyecto.getMemoriaTecnicaById);
/**
* @swagger
* /api/proyectos/memorias-tecnicas/{id}:
* put:
* summary: Actualiza una Memoria Técnica existente.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica a actualizar.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* id_proyecto:
* type: integer
* id_empresa_autora:
* type: integer
* objetivo_memoria_especifico:
* type: string
* descripcion_introduccion:
* type: string
* funcionamiento_operacion:
* type: string
* conectividad_red:
* type: string
* acceso_remoto_aplicaciones:
* type: string
* seguridad_fisica_logica:
* type: string
* responses:
* 200:
* description: Memoria Técnica actualizada exitosamente.
* content:
* application/json:
* schema:
* type: object
* properties:
* message:
* type: string
* example: Memoria técnica actualizada exitosamente
* 400:
* description: Datos de entrada inválidos.
* 404:
* description: Memoria Técnica no encontrada.
* 500:
* description: Error del servidor.
*/
router.put('/api/proyectos/memorias-tecnicas/:id', controlador_proyecto.updateMemoriaTecnica);
/**
* @swagger
* /api/proyectos/memorias-tecnicas/{id}:
* delete:
* summary: Elimina una Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica a eliminar.
* responses:
* 204:
* description: Memoria Técnica eliminada exitosamente.
* 404:
* description: Memoria Técnica no encontrada.
* 500:
* description: Error del servidor.
*/
router.delete('/api/proyectos/memorias-tecnicas/:id', controlador_proyecto.deleteMemoriaTecnica);
// --- Rutas para Componentes de la Memoria Técnica ---
/**
* @swagger
* /api/proyectos/memorias-tecnicas/{memoriaId}/componentes:
* get:
* summary: Obtiene todos los componentes asociados a una Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: memoriaId
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica.
* responses:
* 200:
* description: Lista de componentes.
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id: { type: integer, example: 1 }
* id_memoria: { type: integer, example: 10 }
* nombre_componente: { type: string, example: "Cámara IP Hikvision" }
* tipo: { type: string, example: "Cámara" }
* modelo_marca: { type: string, example: "DS-2CD2143G0-I" }
* numero_serie: { type: string, example: "SN123456789" }
* ubicacion: { type: string, example: "Entrada principal" }
* descripcion_breve: { type: string, example: "Cámara domo de 4MP con IR" }
* 500:
* description: Error del servidor.
* post:
* summary: Agrega un nuevo componente a una Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: memoriaId
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - nombre_componente
* properties:
* nombre_componente:
* type: string
* tipo:
* type: string
* modelo_marca:
* type: string
* numero_serie:
* type: string
* ubicacion:
* type: string
* descripcion_breve:
* type: string
* responses:
* 201:
* description: Componente añadido exitosamente.
* 400:
* description: Datos de entrada inválidos.
* 500:
* description: Error del servidor.
*/
router.get('/api/proyectos/memorias-tecnicas/:memoriaId/componentes', controlador_proyecto.getComponentesByMemoriaId);
router.post('/api/proyectos/memorias-tecnicas/:memoriaId/componentes', controlador_proyecto.addComponente);
/**
* @swagger
* /api/proyectos/componentes/{id}:
* put:
* summary: Actualiza un componente de la Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID del componente a actualizar.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* nombre_componente:
* type: string
* tipo:
* type: string
* modelo_marca:
* type: string
* numero_serie:
* type: string
* ubicacion:
* type: string
* descripcion_breve:
* type: string
* responses:
* 200:
* description: Componente actualizado exitosamente.
* 404:
* description: Componente no encontrado.
* 500:
* description: Error del servidor.
* delete:
* summary: Elimina un componente de la Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID del componente a eliminar.
* responses:
* 204:
* description: Componente eliminado exitosamente.
* 404:
* description: Componente no encontrado.
* 500:
* description: Error del servidor.
*/
router.put('/api/proyectos/componentes/:id', controlador_proyecto.updateComponente);
router.delete('/api/proyectos/componentes/:id', controlador_proyecto.deleteComponente);
// --- Rutas para Plan de Mantenimiento ---
/**
* @swagger
* /api/proyectos/memorias-tecnicas/{memoriaId}/mantenimiento:
* get:
* summary: Obtiene todos los planes de mantenimiento asociados a una Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: memoriaId
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica.
* responses:
* 200:
* description: Lista de planes de mantenimiento.
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id: { type: integer, example: 1 }
* id_memoria: { type: integer, example: 10 }
* tipo_mantenimiento: { type: string, example: "Preventivo" }
* frecuencia: { type: string, example: "Mensual" }
* tareas_procedimientos: { type: string, example: "Limpieza de filtros y revisión de conexiones." }
* responsable: { type: string, example: "Equipo de Mantenimiento A" }
* 500:
* description: Error del servidor.
* post:
* summary: Agrega un nuevo plan de mantenimiento a una Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: memoriaId
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - tareas_procedimientos
* properties:
* tipo_mantenimiento:
* type: string
* frecuencia:
* type: string
* tareas_procedimientos:
* type: string
* responsable:
* type: string
* responses:
* 201:
* description: Plan de mantenimiento añadido exitosamente.
* 400:
* description: Datos de entrada inválidos.
* 500:
* description: Error del servidor.
*/
router.get('/api/proyectos/memorias-tecnicas/:memoriaId/mantenimiento', controlador_proyecto.getMantenimientoByMemoriaId);
router.post('/api/proyectos/memorias-tecnicas/:memoriaId/mantenimiento', controlador_proyecto.addMantenimiento);
/**
* @swagger
* /api/proyectos/mantenimiento/{id}:
* put:
* summary: Actualiza un plan de mantenimiento.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID del plan de mantenimiento a actualizar.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* tipo_mantenimiento:
* type: string
* frecuencia:
* type: string
* tareas_procedimientos:
* type: string
* responsable:
* type: string
* responses:
* 200:
* description: Plan de mantenimiento actualizado exitosamente.
* 404:
* description: Plan de mantenimiento no encontrado.
* 500:
* description: Error del servidor.
* delete:
* summary: Elimina un plan de mantenimiento.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID del plan de mantenimiento a eliminar.
* responses:
* 200:
* description: Plan de mantenimiento eliminado exitosamente.
* 404:
* description: Plan de mantenimiento no encontrado.
* 500:
* description: Error del servidor.
*/
router.put('/api/proyectos/mantenimiento/:id', controlador_proyecto.updateMantenimiento);
router.delete('/api/proyectos/mantenimiento/:id', controlador_proyecto.deleteMantenimiento);
// --- Rutas para Credenciales de Dispositivos ---
/**
* @swagger
* /api/proyectos/memorias-tecnicas/{memoriaId}/credenciales:
* get:
* summary: Obtiene todas las credenciales asociadas a una Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: memoriaId
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica.
* responses:
* 200:
* description: Lista de credenciales.
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id: { type: integer, example: 1 }
* id_memoria: { type: integer, example: 10 }
* tipo_credencial: { type: string, example: "Usuario" }
* valor_credencial: { type: string, example: "admin" }
* descripcion: { type: string, example: "Credencial de acceso al NVR" }
* 500:
* description: Error del servidor.
* post:
* summary: Agrega una nueva credencial a una Memoria Técnica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: memoriaId
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - tipo_credencial
* - valor_credencial
* properties:
* tipo_credencial:
* type: string
* valor_credencial:
* type: string
* descripcion:
* type: string
* responses:
* 201:
* description: Credencial agregada exitosamente.
* 400:
* description: Datos de entrada inválidos.
* 500:
* description: Error del servidor.
*/
router.get('/api/proyectos/memorias-tecnicas/:memoriaId/credenciales', controlador_proyecto.getCredencialesByMemoriaId);
router.post('/api/proyectos/memorias-tecnicas/:memoriaId/credenciales', controlador_proyecto.addCredencial);
/**
* @swagger
* /api/proyectos/credenciales/{id}:
* put:
* summary: Actualiza una credencial de dispositivo.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la credencial a actualizar.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* tipo_credencial:
* type: string
* valor_credencial:
* type: string
* descripcion:
* type: string
* responses:
* 200:
* description: Credencial actualizada exitosamente.
* 404:
* description: Credencial no encontrada.
* 500:
* description: Error del servidor.
* delete:
* summary: Elimina una credencial de dispositivo.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la credencial a eliminar.
* responses:
* 200:
* description: Credencial eliminada exitosamente.
* 404:
* description: Credencial no encontrada.
* 500:
* description: Error del servidor.
*/
router.put('/api/proyectos/credenciales/:id', controlador_proyecto.updateCredencial);
router.delete('/api/proyectos/credenciales/:id', controlador_proyecto.deleteCredencial);
// --- Rutas para Documentos Genéricos (relacionados con Memoria Técnica) ---
/**
* @swagger
* /api/documentos:
* post:
* summary: Sube un nuevo documento.
* tags: [Documentos y Archivos API]
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* required:
* - documento
* - id_entidad_asociada
* - tipo_entidad_asociada
* properties:
* documento:
* type: string
* format: binary
* memoriaId:
* type: integer
* description: ID de la Memoria Técnica a la que se asocia el documento (opcional).
* descripcion:
* type: string
* description: Descripción del documento.
* responses:
* 201:
* description: Documento subido exitosamente.
* 400:
* description: Datos de entrada inválidos o archivo no proporcionado.
* 500:
* description: Error del servidor.
*/
router.post('/api/documentos', controlador_proyecto.uploadDocumento);
/**
* @swagger
* /api/documentos/{id}:
* get:
* summary: Obtiene un documento por su ID.
* tags: [Documentos y Archivos API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID del documento a obtener.
* responses:
* 200:
* description: Documento encontrado (puede devolver el archivo o metadatos).
* 404:
* description: Documento no encontrado.
* 500:
* description: Error del servidor.
* delete:
* summary: Elimina un documento por su ID.
* tags: [Documentos y Archivos API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID del documento a eliminar.
* responses:
* 200:
* description: Documento eliminado exitosamente.
* 404:
* description: Documento no encontrado.
* 500:
* description: Error del servidor.
*/
router.get('/api/documentos/:id', controlador_proyecto.getDocumentoById);
router.delete('/api/documentos/:id', controlador_proyecto.deleteDocumento);
/**
* @swagger
* /api/proyectos/memorias-tecnicas/{memoriaId}/documentos:
* get:
* summary: Obtiene todos los documentos asociados a una Memoria Técnica.
* tags: [Documentos y Archivos API]
* parameters:
* - in: path
* name: memoriaId
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica.
* responses:
* 200:
* description: Lista de documentos.
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id: { type: integer, example: 1 }
* nombre_documento: { type: string, example: "Manual_CCTV.pdf" }
* ruta_archivo: { type: string, example: "uploads/documentos/manual_cctv.pdf" }
* tipo_entidad_asociada: { type: string, example: "MEMORIA_TECNICA" }
* 500:
* description: Error del servidor.
*/
router.get('/api/proyectos/memorias-tecnicas/:memoriaId/documentos', controlador_proyecto.getDocumentosByMemoriaId);
router.get('/api/documentos/:id/download', controlador_proyecto.downloadDocumento); // Nueva ruta para descargar
// --- Rutas para Imágenes Genéricas (relacionadas con Memoria Técnica) ---
/**
* @swagger
* /api/imagenes:
* post:
* summary: Sube una nueva imagen.
* tags: [Documentos y Archivos API]
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* required:
* - imagen
* - id_entidad_asociada
* - tipo_entidad_asociada
* properties:
* imagen:
* type: string
* format: binary
* memoriaId:
* type: integer
* description: ID de la Memoria Técnica a la que se asocia la imagen (opcional).
* descripcion:
* type: string
* description: Descripción de la imagen.
* responses:
* 201:
* description: Imagen subida exitosamente.
* 400:
* description: Datos de entrada inválidos o archivo no proporcionado.
* 500:
* description: Error del servidor.
*/
router.post('/api/imagenes', controlador_proyecto.uploadImagen);
/**
* @swagger
* /api/imagenes/{id}:
* get:
* summary: Obtiene una imagen por su ID.
* tags: [Documentos y Archivos API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la imagen a obtener.
* responses:
* 200:
* description: Imagen encontrada (puede devolver el archivo o metadatos).
* 404:
* description: Imagen no encontrada.
* 500:
* description: Error del servidor.
* delete:
* summary: Elimina una imagen por su ID.
* tags: [Documentos y Archivos API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la imagen a eliminar.
* responses:
* 200:
* description: Imagen eliminada exitosamente.
* 404:
* description: Imagen no encontrada.
* 500:
* description: Error del servidor.
*/
router.get('/api/imagenes/:id', controlador_proyecto.getImagenById);
router.delete('/api/imagenes/:id', controlador_proyecto.deleteImagen);
router.get('/api/imagenes/:id/view', controlador_proyecto.viewImagen); // Nueva ruta para visualizar
/**
* @swagger
* /api/entidades/{tipoEntidad}/{entidadId}/documentos:
* get:
* summary: Obtiene todos los documentos asociados a una entidad específica.
* tags: [Documentos y Archivos API]
* parameters:
* - in: path
* name: tipoEntidad
* required: true
* schema:
* type: string
* description: Tipo de la entidad (ej. 'PROYECTO', 'CLIENTE', 'MEMORIA_TECNICA').
* - in: path
* name: entidadId
* required: true
* schema:
* type: integer
* description: ID de la entidad.
* responses:
* 200:
* description: Lista de documentos.
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id: { type: integer, example: 1 }
* nombre_documento: { type: string, example: "Contrato_Cliente.pdf" }
* tipo_entidad_asociada: { type: string, example: "CLIENTE" }
* id_entidad_asociada: { type: integer, example: 5 }
* 500:
* description: Error del servidor.
*/
router.get('/api/entidades/:tipoEntidad/:entidadId/documentos', controlador_proyecto.getDocumentosByEntidad);
/**
* @swagger
* /api/entidades/{tipoEntidad}/{entidadId}/imagenes:
* get:
* summary: Obtiene todas las imágenes asociadas a una entidad específica.
* tags: [Documentos y Archivos API]
* parameters:
* - in: path
* name: tipoEntidad
* required: true
* schema:
* type: string
* description: Tipo de la entidad (ej. 'PROYECTO', 'CLIENTE', 'MEMORIA_TECNICA').
* - in: path
* name: entidadId
* required: true
* schema:
* type: integer
* description: ID de la entidad.
* responses:
* 200:
* description: Lista de imágenes.
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id: { type: integer, example: 1 }
* nombre_imagen: { type: string, example: "Fachada_Edificio.jpg" }
* tipo_entidad_asociada: { type: string, example: "PROYECTO" }
* id_entidad_asociada: { type: integer, example: 3 }
* 500:
* description: Error del servidor.
*/
router.get('/api/entidades/:tipoEntidad/:entidadId/imagenes', controlador_proyecto.getImagenesByEntidad);
// Nueva ruta para generar PDF
/**
* @swagger
* /api/proyectos/memorias-tecnicas/{id}/generar-pdf:
* get:
* summary: Genera un PDF de una memoria técnica específica.
* tags: [Memoria Técnica API]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la Memoria Técnica para generar el PDF.
* responses:
* 200:
* description: PDF generado y enviado exitosamente.
* content:
* application/pdf:
* schema:
* type: string
* format: binary
* 404:
* description: Memoria Técnica no encontrada.
* 500:
* description: Error del servidor al generar el PDF.
*/
router.get('/api/proyectos/memorias-tecnicas/:id/generar-pdf', controlador_proyecto.generarPdfMemoriaTecnica);
// Exportar el router
module.exports = router;

View File

@@ -41,7 +41,8 @@ const swaggerOptions = {
'./rutas/rt_Generales.js',
'./rutas/rt_cloud.js',
'./rutas/rt_apps.js',
'./rutas/rt_arduino.js'
'./rutas/rt_arduino.js',
'./rutas/rt_proyectos.js',
]
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

View File

@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lista de Memorias Técnicas - SIGMA</title>
<link rel="stylesheet" href="/css/estilos_generales.css">
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f4f4f4;
color: #333;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
max-width: 1200px;
margin: auto;
}
h1 {
color: #007bff;
margin-bottom: 20px;
}
.action-buttons {
margin-bottom: 20px;
text-align: right;
}
.action-buttons button {
padding: 10px 15px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
.action-buttons button:hover {
background-color: #218838;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
color: #555;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
.table-actions button {
padding: 5px 10px;
margin-right: 5px;
border: none;
border-radius: 3px;
cursor: pointer;
}
.table-actions .btn-cargar {
background-color: #007bff;
color: white;
}
.table-actions .btn-cargar:hover {
background-color: #0056b3;
}
.table-actions .btn-eliminar {
background-color: #dc3545;
color: white;
}
.table-actions .btn-eliminar:hover {
background-color: #c82333;
}
</style>
</head>
<body>
<div class="container">
<h1>Lista de Memorias Técnicas</h1>
<div class="action-buttons">
<button onclick="location.href='/proyecto_memoria_tecnica'">Crear Nueva Memoria Técnica</button>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>Objetivo de la Memoria</th>
<th>Proyecto</th>
<th>Cliente</th>
<th>Empresa Autora</th>
<th>Fecha Elaboración</th>
<th>Última Modificación</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<% if (memorias && memorias.length > 0) { %>
<% memorias.forEach(function(memoria) { %>
<tr>
<td><%= memoria.id %></td>
<td><%= memoria.objetivo_memoria_especifico %></td>
<td><%= memoria.nombre_proyecto || 'N/A' %></td>
<td><%= memoria.nombre_cliente || 'N/A' %></td>
<td><%= memoria.nombre_empresa_autora || 'N/A' %></td>
<td><%= new Date(memoria.fecha_elaboracion).toLocaleDateString() %></td>
<td><%= new Date(memoria.ultima_modificacion).toLocaleDateString() %></td>
<td class="table-actions">
<button class="btn-cargar" onclick="cargarMemoria(<%= memoria.id %>)">Cargar</button>
<button class="btn-eliminar" onclick="eliminarMemoria(<%= memoria.id %>)">Eliminar</button>
</td>
</tr>
<% }); %>
<% } else { %>
<tr>
<td colspan="8">No hay memorias técnicas disponibles.</td>
</tr>
<% } %>
</tbody>
</table>
</div>
<script>
function cargarMemoria(memoriaId) {
// Redirige al formulario de memoria técnica, pasando el ID como parámetro
window.location.href = `/proyecto_memoria_tecnica?memoriaId=${memoriaId}`;
}
async function eliminarMemoria(memoriaId) {
if (confirm(`¿Está seguro de eliminar la memoria técnica con ID ${memoriaId}? Esto eliminará todos los datos asociados (componentes, mantenimiento, etc.).`)) {
try {
const response = await fetch(`/api/proyectos/memorias-tecnicas/${memoriaId}`, {
method: 'DELETE'
});
if (response.status === 204) { // 204 No Content es para eliminaciones exitosas
alert('Memoria técnica eliminada exitosamente.');
location.reload(); // Recargar la página para actualizar la tabla
} else {
const errorData = await response.json();
alert('Error al eliminar memoria técnica: ' + (errorData.error || response.statusText));
}
} catch (error) {
console.error('Error de red al eliminar memoria:', error);
alert('Error de conexión al eliminar la memoria técnica.');
}
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,130 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Panel de Proyectos - SIGMA</title>
<link rel="stylesheet" href="/css/estilos_generales.css">
<style>
/* Estilos básicos en línea para una visualización rápida.
Idealmente, estos estilos deberían ir en un archivo CSS externo en /public/css/ */
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f4f4f4;
color: #333;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
max-width: 1000px;
margin: auto;
}
h1 {
color: #007bff;
margin-bottom: 20px;
}
.action-buttons {
margin-bottom: 20px;
text-align: right;
}
.action-buttons button {
padding: 10px 15px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
.action-buttons button:hover {
background-color: #218838;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
color: #555;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
.table-actions button {
padding: 5px 10px;
margin-right: 5px;
border: none;
border-radius: 3px;
cursor: pointer;
}
.table-actions .btn-ver {
background-color: #007bff;
color: white;
}
.table-actions .btn-ver:hover {
background-color: #0056b3;
}
.table-actions .btn-memoria {
background-color: #ffc107;
color: #333;
}
.table-actions .btn-memoria:hover {
background-color: #e0a800;
}
</style>
</head>
<body>
<div class="container">
<h1>Panel de Gestión de Proyectos</h1>
<div class="action-buttons">
<button onclick="location.href='/proyecto_memoria_tecnica'">Crear Nueva Memoria Técnica</button>
</div>
<h2>Lista de Proyectos</h2>
<table>
<thead>
<tr>
<th>ID Proyecto</th>
<th>Nombre del Proyecto</th>
<th>Cliente</th>
<th>Objetivo</th>
<th>Estado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<% if (proyectos && proyectos.length > 0) { %>
<% proyectos.forEach(function(proyecto) { %>
<tr>
<td><%= proyecto.id %></td>
<td><%= proyecto.nombre_proyecto %></td>
<td><%= proyecto.nombre_cliente || 'N/A' %></td>
<td><%= proyecto.objetivo %></td>
<td><%= proyecto.estado %></td>
<td class="table-actions">
<button class="btn-ver" onclick="location.href='/proyectos/<%= proyecto.id %>'">Ver Detalles</button>
<button class="btn-memoria" onclick="location.href='/proyecto_memoria_tecnica?proyectoId=<%= proyecto.id %>'">Memoria Técnica</button>
</td>
</tr>
<% }); %>
<% } else { %>
<tr>
<td colspan="6">No hay proyectos disponibles.</td>
</tr>
<% } %>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -0,0 +1,284 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Memoria Técnica - <%= memoria.id %></title>
<style>
/* Estilos CSS para el PDF */
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
font-size: 10pt;
line-height: 1.5;
color: #333;
}
.header, .footer {
width: 100%;
text-align: center;
position: fixed;
font-size: 8pt;
color: #555;
}
.header {
top: 0;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
}
.footer {
bottom: 0;
border-top: 1px solid #eee;
padding-top: 5px;
}
.content {
margin: 20mm; /* Márgenes definidos en page.pdf */
}
h1 {
text-align: center;
color: #007bff;
font-size: 18pt;
margin-bottom: 20px;
}
h2 {
color: #0056b3;
font-size: 14pt;
border-bottom: 1px solid #007bff;
padding-bottom: 5px;
margin-top: 30px;
}
h3 {
color: #333;
font-size: 12pt;
margin-top: 20px;
}
.section-block {
margin-bottom: 20px;
padding: 10px;
border: 1px solid #eee;
border-radius: 5px;
background-color: #f9f9f9;
}
.data-item {
margin-bottom: 8px;
}
.data-item strong {
color: #007bff;
display: inline-block;
width: 150px; /* Ajusta según necesidad */
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
font-size: 9pt;
}
th, td {
border: 1px solid #ddd;
padding: 6px;
text-align: left;
}
th {
background-color: #e9ecef;
color: #495057;
}
.text-area-content {
white-space: pre-wrap; /* Mantiene saltos de línea y espacios */
background-color: #fff;
border: 1px solid #ddd;
padding: 10px;
border-radius: 4px;
margin-top: 5px;
}
.image-gallery {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
}
.image-gallery img {
max-width: 100%; /* Asegura que la imagen no se desborde */
height: auto;
border: 1px solid #ddd;
padding: 5px;
background-color: #fff;
}
.page-break {
page-break-before: always;
}
</style>
</head>
<body>
<div class="header">
Memoria Técnica SIGMA - ID: <%= memoria.id %>
</div>
<div class="footer">
Página <span class="pageNumber"></span> de <span class="totalPages"></span>
</div>
<div class="content">
<h1>MEMORIA TÉCNICA DEL PROYECTO</h1>
<div class="section-block">
<h2>Datos Generales de la Memoria</h2>
<div class="data-item"><strong>ID Memoria:</strong> <%= memoria.id %></div>
<div class="data-item"><strong>Proyecto:</strong> <%= proyecto ? proyecto.nombre_proyecto : 'N/A' %></div>
<div class="data-item"><strong>Cliente:</strong> <%= proyecto ? proyecto.nombre_cliente : 'N/A' %></div>
<div class="data-item"><strong>Empresa Autora:</strong> <%= empresaAutora ? (empresaAutora.RazonSocial || empresaAutora.Nombre) : 'N/A' %></div>
<div class="data-item"><strong>Fecha Elaboración:</strong> <%= new Date(memoria.fecha_elaboracion).toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' }) %></div>
<div class="data-item"><strong>Última Modificación:</strong> <%= new Date(memoria.ultima_modificacion).toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' }) %></div>
<div class="data-item"><strong>Objetivo de la Memoria:</strong></div>
<div class="text-area-content"><%= memoria.objetivo_memoria_especifico || 'No especificado.' %></div>
<div class="data-item"><strong>Descripción/Introducción:</strong></div>
<div class="text-area-content"><%= memoria.descripcion_introduccion || 'No especificado.' %></div>
</div>
<% if (memoria.funcionamiento_operacion || memoria.conectividad_red || memoria.seguridad_fisica_logica || credenciales.length > 0) { %>
<div class="page-break"></div>
<div class="section-block">
<h2>Información de Conectividad y Seguridad</h2>
<% if (memoria.funcionamiento_operacion) { %>
<h3>Funcionamiento y Operación:</h3>
<div class="text-area-content"><%= memoria.funcionamiento_operacion %></div>
<% } %>
<% if (memoria.conectividad_red) { %>
<h3>Conectividad de Red:</h3>
<div class="text-area-content"><%= memoria.conectividad_red %></div>
<% } %>
<% if (memoria.acceso_remoto_aplicaciones) { %>
<h3>Acceso Remoto y Aplicaciones:</h3>
<div class="text-area-content"><%= memoria.acceso_remoto_aplicaciones %></div>
<% } %>
<% if (memoria.seguridad_fisica_logica) { %>
<h3>Seguridad Física y Lógica:</h3>
<div class="text-area-content"><%= memoria.seguridad_fisica_logica %></div>
<% } %>
<% if (credenciales.length > 0) { %>
<h3>Credenciales de Dispositivos:</h3>
<table>
<thead>
<tr>
<th>Tipo</th>
<th>Valor</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<% credenciales.forEach(function(cred) { %>
<tr>
<td><%= cred.tipo_credencial %></td>
<td><%= cred.valor_credencial %></td>
<td><%= cred.descripcion || 'N/A' %></td>
</tr>
<% }); %>
</tbody>
</table>
<% } %>
</div>
<% } %>
<% if (componentes.length > 0) { %>
<div class="page-break"></div>
<div class="section-block">
<h2>Componentes del Proyecto</h2>
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Tipo</th>
<th>Modelo/Marca</th>
<th>Nº Serie</th>
<th>Ubicación</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<% componentes.forEach(function(comp) { %>
<tr>
<td><%= comp.nombre_componente %></td>
<td><%= comp.tipo || 'N/A' %></td>
<td><%= comp.modelo_marca || 'N/A' %></td>
<td><%= comp.numero_serie || 'N/A' %></td>
<td><%= comp.ubicacion || 'N/A' %></td>
<td><%= comp.descripcion_breve || 'N/A' %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>
<% } %>
<% if (mantenimiento.length > 0) { %>
<div class="page-break"></div>
<div class="section-block">
<h2>Plan de Mantenimiento</h2>
<table>
<thead>
<tr>
<th>Tipo Mantenimiento</th>
<th>Frecuencia</th>
<th>Tareas/Procedimientos</th>
<th>Responsable</th>
</tr>
</thead>
<tbody>
<% mantenimiento.forEach(function(mtto) { %>
<tr>
<td><%= mtto.tipo_mantenimiento || 'N/A' %></td>
<td><%= mtto.frecuencia || 'N/A' %></td>
<td><%= mtto.tareas_procedimientos %></td>
<td><%= mtto.responsable || 'N/A' %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>
<% } %>
<% if (documentos.length > 0 || imagenes.length > 0) { %>
<div class="page-break"></div>
<div class="section-block">
<h2>Documentos y Archivos Adjuntos</h2>
<% if (documentos.length > 0) { %>
<h3>Documentos:</h3>
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Descripción</th>
<th>Tipo</th>
<th>Tamaño</th>
</tr>
</thead>
<tbody>
<% documentos.forEach(function(doc) { %>
<tr>
<td><%= doc.nombre_documento %></td>
<td><%= doc.descripcion || 'N/A' %></td>
<td><%= doc.tipo_contenido || 'N/A' %></td>
<td><%= (doc.tamano_bytes / 1024).toFixed(2) %> KB</td>
</tr>
<% }); %>
</tbody>
</table>
<% } %>
<% if (imagenes.length > 0) { %>
<h3 style="margin-top: 20px;">Imágenes / Planos:</h3>
<div class="image-gallery">
<% imagenes.forEach(function(img) { %>
<div>
<img src="<%= getFileUrl(img.ruta_imagen) %>" alt="<%= img.nombre_imagen %>" style="max-width: 300px; height: auto;">
<p style="text-align: center; font-size: 8pt;"><%= img.nombre_imagen %></p>
<p style="text-align: center; font-size: 7pt; color: #666;"><%= img.descripcion || '' %></p>
</div>
<% }); %>
</div>
<% } %>
</div>
<% } %>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff