Saltar a contenido

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

<div id="windows-container">
    <!-- Las ventanas se cargan dinámicamente aquí -->
</div>

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>&times;</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.