Versión: 1.0 Fecha: Marzo 2026 Autor: Iván Darío Madrid Daza Programa: Análisis y Desarrollo de Software — SENA
La estructura real y completa del repositorio vive en CONTEXT.md.
Aquí solo se conserva un mapa mínimo para entender los ejemplos de código:
Artify/
├── frontend/
└── backend/
├── config/
├── controllers/
├── middlewares/
├── routes/
├── utils/
└── server.js
admin.css, admin.js, editor.js.frontend/assets/images/..env nunca se suben al repositorio.Se usa camelCase para todas las variables en JavaScript.
// Ejemplo recomendado
let currentImage = null;
let zoomLevel = 100;
let cropMode = false;
let operationsHistory = [];
// Incorrecto
let CurrentImage = null;
let zoom_level = 100;
let CROPMODE = false;
Las constantes que no cambian durante la ejecución se escriben en mayúsculas con guiones bajos.
// Ejemplo recomendado - tomado de editor.js
const RESOLUCION_MINIMA_ANCHO = 1366;
const RESOLUCION_MINIMA_ALTO = 768;
const LOCAL_STORAGE_KEY = 'artify_no_mostrar_modal_resolucion';
// Incorrecto
const resolucionMinimaAncho = 1366;
const resolucionMinimaAlto = 768;
// Ejemplo recomendado - tomado de editor.js
let fileInput, btnSubir, btnDescargar, dropZone;
let btnRecortar, btnRedimensionar, btnRotar;
let btnDeshacer, btnRehacer;
let btnZoomIn, btnZoomOut, zoomLevelDisplay;
// Incorrecto
let fi, bs, bd, dz;
let b1, b2, b3;
// Ejemplo recomendado - tomado del backend modular
const { correo, password } = req.body;
const passwordValida = bcrypt.compareSync(password, usuario.usr_contrasena);
const queryAcceso = `UPDATE USUARIO SET ...`;
// Incorrecto
const { Correo, Password } = req.body;
const password_valida = bcrypt.compareSync(...);
// Ejemplo recomendado
function sincronizarImagenYCanvas(callback) { ... }
function mostrarError(inputId, mensaje) { ... }
function renderizarTabla(usuarios) { ... }
// Incorrecto
function sincronizarImagenYCanvas(CallBack) { ... }
function mostrarError(InputId, Mensaje) { ... }
Todas las funciones usan verbos en español que describen claramente su acción.
// Ejemplo recomendado - tomado de editor.js y admin.js
function sincronizarImagenYCanvas(callback) { ... }
function guardarEstadoEnHistorial() { ... }
function actualizarEstado(mensaje, tipo) { ... }
function registrarOperacion(tipo, descripcion) { ... }
function renderizarTabla(usuarios) { ... }
function actualizarEstadisticas(usuarios) { ... }
function cargarUsuarios() { ... }
function limpiarErrores() { ... }
function cerrarModal() { ... }
function formatearFecha(fechaStr) { ... }
// Incorrecto
function sync(cb) { ... }
function saveState() { ... }
function update(msg, t) { ... }
// Ejemplo recomendado - tomado de admin.js
async function cargarUsuarios() {
try {
const res = await fetch(`${API}/api/admin/usuarios`);
const data = await res.json();
if (data.mensaje === 'ok') {
renderizarTabla(data.usuarios);
}
} catch (err) {
console.error('Error al cargar usuarios:', err);
mostrarNotificacion('error', 'Error al cargar los usuarios');
}
}
// Incorrecto — mezclar promesas con async/await
async function cargarUsuarios() {
fetch(`${API}/api/admin/usuarios`)
.then(res => res.json())
.then(data => { ... });
}
// Ejemplo recomendado - tomado de admin.js
document.getElementById('btnAdminLogin').addEventListener('click', async () => {
const correo = document.getElementById('adminEmail').value.trim();
const password = document.getElementById('adminPassword').value;
// ...
});
document.getElementById('searchInput').addEventListener('input', (e) => {
const termino = e.target.value.toLowerCase();
// ...
});
Las funciones que se llaman desde HTML usan window para exponerlas globalmente.
// Ejemplo recomendado - tomado de admin.js
window.abrirEditar = function(id) {
const usuario = todosLosUsuarios.find(u => u.usr_id_usuario === id);
// ...
};
window.abrirEliminar = function(id, nombre) {
usuarioIdEliminar = id;
// ...
};
// Ejemplo recomendado - tomado del backend modular
const db = mysql2.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
// Ejemplo recomendado - tomado de editor.js
let cropArea = {
x: 0,
y: 0,
width: 0,
height: 0
};
// Ejemplo recomendado - tomado del backend modular
res.json({
mensaje: 'Login exitoso',
usuario: {
id: usuario.usr_id_usuario,
nombres: usuario.usr_nombres,
apellidos: usuario.usr_apellidos,
correo: usuario.usr_correo,
rol: usuario.usr_rol,
},
});
// Ejemplo recomendado - tomado del backend modular
const configDefecto = JSON.stringify({
notificaciones: true,
formatoDefecto: 'png',
autoguardado: false,
});
<!-- Ejemplo recomendado - tomado de admin.html -->
<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Panel Admin — Artify</title>
<link rel="icon" href="../assets/images/modx-icon.svg" type="image/svg" />
<link rel="stylesheet" href="../assets/css/admin.css" />
</head>
<body>
<!-- Contenido -->
<script src="../assets/js/admin.js"></script>
</body>
</html>
<!-- Ejemplo recomendado - tomado de admin.html -->
<div class="admin-login-overlay" id="loginOverlay">
<div class="admin-login-card">
<div class="modal-overlay" id="modalUsuario">
<button class="btn-admin-login" id="btnAdminLogin">
<span class="error-message" id="adminEmail-error">
<!-- Incorrecto -->
<div class="adminLoginOverlay" id="login_overlay">
<div class="AdminLoginCard">
<!-- Ejemplo recomendado - tomado de admin.html -->
<!-- ===== LOGIN DEL ADMINISTRADOR ===== -->
<!-- ===== PANEL PRINCIPAL ===== -->
<!-- HEADER -->
<!-- SIDEBAR -->
<!-- CONTENIDO PRINCIPAL -->
<!-- TABLA DE USUARIOS -->
<!-- ===== MODAL AGREGAR / EDITAR USUARIO ===== -->
<!-- ===== MODAL CONFIRMAR ELIMINAR ===== -->
<!-- NOTIFICACIÓN -->
<!-- Ejemplo recomendado - tomado de admin.html -->
<input type="email" id="adminEmail" placeholder="admin@artify.com" autocomplete="email" />
<input type="password" id="adminPassword" autocomplete="current-password" />
<button type="button" class="toggle-password" aria-label="Mostrar contraseña">
/* Ejemplo recomendado - tomado de admin.css */
:root {
--bg: #0a0a0a;
--card: #161616;
--card2: #1e1e1e;
--primary: #4ae3f4;
--primary-dark: #469af3;
--accent: #28ffce;
--text: #f5f5f5;
--text-muted: #acabab;
--font-head: 'Paytone One', sans-serif;
--font-body: 'Inconsolata', monospace;
--radius: 10px;
--shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
--sidebar-width: 170px;
}
/* Ejemplo recomendado - tomado de admin.css */
.admin-login-overlay { ... }
.admin-login-card { ... }
.header-title { ... }
.btn-admin-login { ... }
.content-toolbar { ... }
.estado-badge { ... }
.estado-activo { ... }
.nav-section-title { ... }
/* Incorrecto */
.adminLoginOverlay { ... }
.AdminLoginCard { ... }
.headerTitle { ... }
/* Ejemplo recomendado - tomado de admin.css */
/* ========== VARIABLES ========== */
/* ========== RESET ========== */
/* ========== LOGIN OVERLAY ========== */
/* ========== FORM GROUPS ========== */
/* ========== BOTÓN LOGIN ========== */
/* ========== HEADER ========== */
/* ========== LAYOUT ========== */
/* ========== SIDEBAR ========== */
/* ========== CONTENIDO ========== */
/* ========== TABLA ========== */
/* ========== MODALES ========== */
/* ========== NOTIFICACIÓN ========== */
/* Ejemplo recomendado - tomado de admin.css */
@media (min-width: 1400px) {
:root { --sidebar-width: 160px; }
.admin-content { max-width: 100%; }
}
@media (min-width: 2560px) {
body { font-size: 16px; }
:root { --sidebar-width: 180px; }
}
// Ejemplo recomendado - tomado de editor.js y admin.js
// ========== VARIABLES GLOBALES ==========
// ========== ELEMENTOS DEL DOM ==========
// ========== CONSTANTES ==========
// ========== CONFIGURACIÓN ==========
// ========== UTILIDADES ==========
// ========== LÓGICA PRINCIPAL ==========
// ========== EVENTOS ==========
// Ejemplo recomendado
const API = 'http://localhost:3000'; // Valor que no cambia → const
let todosLosUsuarios = []; // Valor que cambia → let
let usuarioIdEliminar = null;
let modoEdicion = false;
// Incorrecto
var API = 'http://localhost:3000'; // Nunca usar var
var todosLosUsuarios = [];
// Ejemplo recomendado - tomado de admin.js
try {
const res = await fetch(`${API}/api/admin/usuarios`);
const data = await res.json();
renderizarTabla(data.usuarios);
} catch (err) {
console.error('Error al cargar usuarios:', err);
mostrarNotificacion('error', 'Error al cargar los usuarios');
}
// Ejemplo recomendado - tomado del backend modular y admin.js
console.log('Conectado a MySQL correctamente');
console.log('Login exitoso para:', usuario.usr_nombres);
console.log('Rol:', usuario.usr_rol);
console.error('Error al conectar a MySQL:', err.message);
console.warn('No se pudo actualizar último acceso:', err.message);
console.log('Datos recibidos desde el formulario:');
console.log('Redirigiendo al editor...');
// Incorrecto
console.log('connected');
console.log('ok');
console.log(err);
// Ejemplo recomendado - tomado del backend modular
// ========== DEPENDENCIAS ==========
// ========== CONFIGURACIÓN ==========
// ========== CONEXIÓN A MYSQL ==========
// ========== ENDPOINT DE LOGIN ==========
// ========== ENDPOINT DE REGISTRO ==========
// ========== ENDPOINTS PANEL DE ADMINISTRACIÓN ==========
// ========== LIMPIEZA AUTOMÁTICA DE SESIONES INACTIVAS ==========
// Ejemplo recomendado - tomado del backend modular
const db = mysql2.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
// Incorrecto — nunca hardcodear credenciales
const db = mysql2.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contrasena',
database: 'artify_db',
});
// Ejemplo recomendado - tomado del backend modular
return res.status(401).json({ mensaje: 'Usuario no encontrado' });
return res.status(401).json({ mensaje: 'Contraseña incorrecta' });
return res.status(500).json({ mensaje: 'Error en el servidor' });
return res.status(400).json({ mensaje: 'El correo o cédula ya está registrado' });
res.json({ mensaje: 'Login exitoso', usuario: { ... } });
res.json({ mensaje: 'Usuario agregado correctamente' });
res.json({ mensaje: 'Usuario editado correctamente' });
res.json({ mensaje: 'Usuario eliminado correctamente' });
// Ejemplo recomendado - tomado del backend modular
db.query(query, [params], (err, results) => {
if (err) {
console.error('Error en la consulta:', err.message);
return res.status(500).json({ mensaje: 'Error en el servidor' });
}
// Lógica de éxito
res.json({ mensaje: 'ok', datos: results });
});
-- Ejemplo recomendado
SELECT * FROM USUARIO;
SELECT * FROM SESION_EDICION;
SELECT * FROM OPERACION;
SELECT * FROM CONFIGURACION;
SELECT * FROM IMAGEN;
-- Incorrecto
SELECT * FROM usuario;
SELECT * FROM sesion_edicion;
Cada columna tiene un prefijo de dos o tres letras que identifica a qué tabla pertenece.
-- Ejemplo recomendado
-- Tabla USUARIO → prefijo usr_
usr_id_usuario
usr_nombres
usr_apellidos
usr_cedula
usr_correo
usr_contrasena
usr_fecha_registro
usr_ultimo_acceso
usr_sesion_activa
usr_estado_usuario
usr_rol
-- Tabla SESION_EDICION → prefijo ses_
ses_id_sesion
ses_usr_id_usuario
ses_fecha_inicio
ses_fecha_fin
ses_estado_sesion
-- Tabla OPERACION → prefijo opr_
opr_id_operacion
opr_usr_id_usuario
opr_ses_id_sesion
opr_tipo_operacion
opr_fecha_hora
// Ejemplo recomendado - tomado del backend modular
const query = `
SELECT usr_id_usuario, usr_nombres, usr_apellidos,
usr_cedula, usr_correo, usr_estado_usuario, usr_rol
FROM USUARIO
ORDER BY usr_fecha_registro DESC
`;
const queryUpdate = `
UPDATE USUARIO
SET usr_ultimo_acceso = NOW(),
usr_sesion_activa = 1
WHERE usr_id_usuario = ?
`;
// Incorrecto
const query = 'SELECT usr_id_usuario, usr_nombres, usr_apellidos, usr_cedula, usr_correo, usr_estado_usuario, usr_rol FROM USUARIO ORDER BY usr_fecha_registro DESC';
-- Ejemplo recomendado - definido en artify_db
CREATE VIEW v_usuarios_activos AS
SELECT u.usr_id_usuario,
CONCAT(u.usr_nombres, ' ', u.usr_apellidos) AS nombre_completo,
u.usr_correo,
...
FROM USUARIO u
WHERE u.usr_estado_usuario = 'activo';
// Ejemplo recomendado - usado en todo el proyecto
// ========== VARIABLES GLOBALES ==========
// ========== ENDPOINTS PANEL DE ADMINISTRACIÓN ==========
// ========== LIMPIEZA AUTOMÁTICA DE SESIONES INACTIVAS ==========
/* Ejemplo recomendado - usado en admin.css */
/* ========== VARIABLES ========== */
/* ========== HEADER ========== */
/* ========== TABLA ========== */
<!-- Ejemplo recomendado - usado en admin.html -->
<!-- ===== LOGIN DEL ADMINISTRADOR ===== -->
<!-- ===== PANEL PRINCIPAL ===== -->
// Ejemplo recomendado - tomado del backend modular
// Buscar usuario en la base de datos
const query = 'SELECT * FROM USUARIO WHERE usr_correo = ?';
// Validar contraseña con bcrypt
const passwordValida = bcrypt.compareSync(password, usuario.usr_contrasena);
// Crear configuración por defecto
const configDefecto = JSON.stringify({ ... });
// Eliminar registros relacionados primero
const queries = [
'DELETE FROM IMAGEN WHERE img_usr_id_usuario = ?',
'DELETE FROM OPERACION WHERE opr_usr_id_usuario = ?',
...
];
// CAMBIO: Artify en blanco y más grande
// Sidebar más angosto
// Soporte para ultrawide y pantallas divididas
# Ejemplo recomendado - commits reales del proyecto Artify
git commit -m "feat(registro): crear configuración por defecto automáticamente al registrar usuario"
git commit -m "feat(sesion): agregar control de inactividad y limpieza automática de sesiones"
git commit -m "feat(filtros): registrar operaciones de filtros en MySQL"
git commit -m "feat(admin): agregar panel de administración con CRUD completo sobre tabla USUARIO"
git commit -m "feat(roles): redirigir al panel de admin o editor según rol del usuario en login"
git commit -m "fix(admin): eliminar registros relacionados antes de eliminar usuario"
git commit -m "fix(db): adaptar endpoints para OPERACION, IMAGEN y SESION_EDICION"
git commit -m "fix(sesion): corregir campos en cron job de limpieza de sesiones"
git commit -m "style(admin): mejorar estilos del panel para pantallas ultrawide"
| Tipo | Uso |
|---|---|
feat |
Nueva funcionalidad |
fix |
Corrección de errores |
style |
Cambios de estilos CSS o formato |
refactor |
Reestructuración de código sin cambiar funcionalidad |
docs |
Cambios en documentación |
chore |
Tareas de mantenimiento |
Documento generado para el proyecto Artify — SENA 2026