Arquitectura Frontend¶
El Sistema A3 utiliza una arquitectura frontend híbrida que combina Django Templates con comportamiento SPA (Single Page Application) usando JavaScript vanilla y jQuery.
Arquitectura General¶
graph TB
subgraph "Cliente"
Browser[Navegador]
end
subgraph "Sistema de Ventanas"
WM[Window Manager<br/>js.js]
Windows[Stack de Ventanas]
end
subgraph "Componentes"
Templates[Django Templates]
Static[CSS / Bootstrap]
JS[JavaScript Modules]
end
subgraph "Backend"
Views[Django Views]
API[REST API]
end
Browser --> WM
WM --> Windows
Windows --> Templates
Windows --> JS
Templates --> Static
WM --> Views
JS --> API
style WM fill:#ff6b6b
Window Manager (Sistema de Ventanas)¶
Concepto¶
El corazón del frontend es el Window Manager ubicado en static/js/js.js. Este sistema simula una aplicación de escritorio con ventanas que se abren unas sobre otras.
Funcionamiento¶
1. Estructura HTML Base
2. Abrir una Ventana
// Función principal para abrir ventanas
function abrirVentana(url, titulo, ancho, alto) {
// 1. Crear contenedor de ventana
var ventana = crearVentanaHTML(titulo, ancho, alto);
// 2. Hacer AJAX request para obtener contenido
$.ajax({
url: url,
success: function(html) {
ventana.find('.window-content').html(html);
}
});
// 3. Agregar al stack de ventanas
$('#windows-container').append(ventana);
// 4. Configurar eventos (cerrar, minimizar, etc.)
configurarEventos(ventana);
}
3. Stack de Ventanas
Las ventanas se apilan usando z-index:
var windowZIndex = 1000;
function traerAlFrente(ventana) {
windowZIndex++;
ventana.css('z-index', windowZIndex);
}
Características¶
- Múltiples ventanas abiertas simultáneamente
- Drag and drop para mover ventanas
- Redimensionamiento de ventanas
- Minimizar/Maximizar/Cerrar
- Navegación por historial dentro de ventanas
Django Templates¶
Template Hierarchy¶
templates/
├── base.html # Template base con estilos y scripts
├── menu.html # Menú principal
├── index.html # Dashboard home
│
├── inventario/
│ ├── inventario.html # Vista principal
│ ├── casa.html # Detalle de casa
│ └── form_casa.html # Formulario
│
├── apartados/
│ ├── apartados.html
│ └── apartado_detalle.html
│
└── ... (otros módulos)
Template Base¶
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Sistema A3{% endblock %}</title>
<!-- Bootstrap CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom CSS -->
<link href="{% static 'css/estilos.css' %}" rel="stylesheet">
{% block extra_css %}{% endblock %}
</head>
<body>
{% include 'menu.html' %}
<div id="windows-container"></div>
{% block content %}{% endblock %}
<!-- jQuery -->
<script src="{% static 'js/jquery.min.js' %}"></script>
<!-- Bootstrap JS -->
<script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
<!-- Window Manager -->
<script src="{% static 'js/js.js' %}"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
JavaScript Modular¶
Estructura¶
static/js/
├── js.js # Window manager y core
├── inventario.js # Lógica de inventario
├── apartados.js # Lógica de apartados
├── clientes.js # Lógica de clientes
├── tickets.js # Lógica de tickets
└── utils.js # Utilidades comunes
Patrón Módulo¶
// static/js/inventario.js
var InventarioModule = (function() {
// Variables privadas
var casaSeleccionada = null;
// Funciones privadas
function validarFormulario(form) {
// Validación
}
// API pública
return {
listarCasas: function() {
$.ajax({
url: '/api/inventario/casas/',
success: function(data) {
renderizarLista(data);
}
});
},
crearCasa: function(data) {
$.ajax({
url: '/api/inventario/casas/',
method: 'POST',
data: JSON.stringify(data),
contentType: 'application/json',
success: function(response) {
mostrarMensaje('Casa creada exitosamente');
}
});
}
};
})();
AJAX y Comunicación con Backend¶
Patrón Request-Response¶
function hacerRequest(url, method, data, callback) {
$.ajax({
url: url,
method: method,
data: data,
dataType: 'json',
headers: {
'X-CSRFToken': getCookie('csrftoken')
},
success: function(response) {
callback(null, response);
},
error: function(xhr, status, error) {
callback(error, null);
}
});
}
CSRF Protection¶
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
// Configurar jQuery para incluir CSRF token
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!(/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type))) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
Bootstrap y Estilos¶
Grid System¶
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<!-- Sidebar -->
</div>
<div class="col-md-9">
<!-- Contenido principal -->
</div>
</div>
</div>
Componentes Usados¶
- Navbar: Menú principal
- Cards: Contenedores de información
- Modals: Diálogos y confirmaciones
- Forms: Formularios con validación
- Tables: Listados de datos
- Buttons: Acciones
- Alerts: Mensajes y notificaciones
Chart.js para Visualizaciones¶
// Ejemplo: Gráfica de ventas
var ctx = document.getElementById('graficaVentas').getContext('2d');
var chart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Ene', 'Feb', 'Mar', 'Abr', 'May'],
datasets: [{
label: 'Ventas',
data: [12, 19, 3, 5, 2],
backgroundColor: 'rgba(54, 162, 235, 0.5)'
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
PDF.js para Visualizar PDFs¶
// Cargar y mostrar PDF
pdfjsLib.getDocument(url).promise.then(function(pdf) {
pdf.getPage(1).then(function(page) {
var canvas = document.getElementById('pdf-canvas');
var context = canvas.getContext('2d');
var viewport = page.getViewport({ scale: 1.5 });
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({
canvasContext: context,
viewport: viewport
});
});
});
Notificaciones y Mensajes¶
function mostrarNotificacion(tipo, mensaje) {
var html = `
<div class="alert alert-${tipo} alert-dismissible fade show">
${mensaje}
<button type="button" class="close" data-dismiss="alert">
<span>×</span>
</button>
</div>
`;
$('#notificaciones-container').html(html);
// Auto-ocultar después de 5 segundos
setTimeout(function() {
$('.alert').fadeOut();
}, 5000);
}
Validación de Formularios¶
function validarFormulario(form) {
var valido = true;
// Validar campos requeridos
form.find('[required]').each(function() {
if ($(this).val() === '') {
$(this).addClass('is-invalid');
valido = false;
} else {
$(this).removeClass('is-invalid');
}
});
// Validar email
var email = form.find('input[type="email"]');
if (email.length && !validarEmail(email.val())) {
email.addClass('is-invalid');
valido = false;
}
return valido;
}
function validarEmail(email) {
var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
Progressive Web App (PWA)¶
Service Worker¶
// static/js/service-worker.js
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('v1').then(function(cache) {
return cache.addAll([
'/',
'/static/css/bootstrap.min.css',
'/static/js/jquery.min.js',
'/static/js/js.js'
]);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});
Web Push Notifications¶
// Solicitar permiso para notificaciones
Notification.requestPermission().then(function(permission) {
if (permission === 'granted') {
subscribeUserToPush();
}
});
function subscribeUserToPush() {
navigator.serviceWorker.ready.then(function(registration) {
registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY)
}).then(function(subscription) {
// Enviar subscription al backend
fetch('/api/push/subscribe/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify(subscription)
});
});
});
}
Best Practices Implementadas¶
✅ Separación de responsabilidades: HTML (estructura), CSS (presentación), JS (comportamiento) ✅ Progressive Enhancement: Funciona sin JS (para algunas páginas) ✅ Responsive Design: Bootstrap grid system ✅ CSRF Protection: Tokens en todas las peticiones POST/PUT/DELETE ✅ Error Handling: Try-catch y callbacks de error ✅ Loading States: Spinners y feedback visual ✅ Validación client-side: Antes de enviar al servidor ✅ Caché inteligente: Service worker para assets estáticos
Optimizaciones¶
- Lazy loading de imágenes
- Minificación de JS y CSS en producción
- Compresión gzip/brotli (WhiteNoise)
- CDN para Bootstrap y jQuery (opcional)
- Debouncing en búsquedas y filtros
Próximas Mejoras¶
- Migrar a framework moderno (React/Vue)
- TypeScript para type safety
- Bundler moderno (Webpack/Vite)
- Testing frontend (Jest)
- Storybook para componentes
Para más detalles sobre el backend que alimenta este frontend, consulta Arquitectura Backend.