Files
2026-09-07 18:44:32 -03:00

480 lines
27 KiB
HTML

{% extends 'base.html' %}
{% load humanize %}
{% block content %}
<div class="header-section" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem;">
<h1>Panel de Gestión Administrativa</h1>
</div>
<style>
.sidebar-btn {
display: flex;
align-items: center;
gap: 0.75rem;
width: 100%;
text-align: left;
padding: 0.8rem 1rem;
background: transparent;
border: none;
border-radius: 6px;
color: var(--efintel-dark);
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.sidebar-btn:hover {
background: rgba(0, 0, 0, 0.05);
color: var(--efintel-blue);
}
.sidebar-btn.active {
background: rgba(5, 59, 80, 0.08); /* efintel-petroleum with opacity */
color: var(--efintel-petroleum);
font-weight: 600;
border-left: 4px solid var(--efintel-petroleum);
}
.sidebar-btn i {
width: 18px;
height: 18px;
}
.tab-content {
display: none;
animation: fadeIn 0.3s ease;
}
.tab-content.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
<div class="gestion-layout" style="display: grid; grid-template-columns: 260px 1fr; gap: 2rem; align-items: start;">
<!-- Sidebar Navigation -->
<div class="data-card" style="padding: 1rem; position: sticky; top: 2rem; background: white; border-radius: 8px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05);">
<nav class="sidebar-nav" style="display: flex; flex-direction: column; gap: 0.25rem;">
<button class="sidebar-btn active" onclick="switchTab(event, 'tab-proyectos')">
<i data-lucide="folder-kanban"></i> Proyectos y Centros
</button>
<button class="sidebar-btn" onclick="switchTab(event, 'tab-categorias')">
<i data-lucide="tags"></i> Categorías Contables
</button>
<button class="sidebar-btn" onclick="switchTab(event, 'tab-proveedores')">
<i data-lucide="building-2"></i> Proveedores
</button>
<button class="sidebar-btn" onclick="switchTab(event, 'tab-responsables')">
<i data-lucide="users"></i> Responsables
</button>
<hr style="border: none; border-top: 1px solid var(--efintel-light); margin: 0.75rem 0;" />
<button class="sidebar-btn" onclick="switchTab(event, 'tab-config')">
<i data-lucide="settings"></i> Configuración General
</button>
<button class="sidebar-btn" onclick="switchTab(event, 'tab-bitacora')">
<i data-lucide="scroll-text"></i> Bitácora de Auditoría
</button>
</nav>
</div>
<!-- Main Content Area -->
<div class="gestion-content">
<!-- Pestaña Proyectos -->
<div id="tab-proyectos" class="tab-content active">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
<h2>Proyectos y Centros de Costo</h2>
<a href="{% url 'project_create' %}" class="btn btn-primary">Nuevo Proyecto / Centro</a>
</div>
<div class="data-card">
<div class="data-table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Código</th>
<th>Nombre</th>
<th>Portafolio / Jerarquía</th>
<th>Categoría / Especialidad</th>
<th>Estado</th>
<th>Especialistas Habilitados</th>
<th>Encargado (Responsable)</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
{% for project in projects %}
<tr>
<td style="font-weight: 600; color: var(--efintel-blue);">{{ project.code }}</td>
<td style="font-weight: 600; color: var(--accent-secondary);">
{% if project.parent %}<span style="color:var(--efintel-gray); margin-right:5px;">↳</span>{% endif %}
{{ project.name }}
</td>
<td>
{% if project.parent %}
<span style="font-size: 0.85rem; color: var(--efintel-blue); font-weight: 500;">{{ project.parent.code }} - {{ project.parent.name }}</span>
{% else %}
<span class="badge badge-emerald" style="font-size: 0.75rem;">Portafolio Principal</span>
{% endif %}
</td>
<td>
{% if project.category %}
<span style="font-size: 0.8rem; font-weight: 600; color: var(--efintel-petroleum);">{{ project.category.code }} - {{ project.category.name }}</span>
{% else %}
<span style="color: var(--efintel-gray); font-size: 0.75rem; font-style: italic;">Sin asignar</span>
{% endif %}
</td>
<td>
{% if project.status == 'ACTIVO' %}
<span class="badge badge-emerald"><i class="status-dot green"></i> Activo</span>
{% elif project.status == 'PAUSADA' %}
<span class="badge badge-amber"><i class="status-dot orange"></i> En Pausa</span>
{% else %}
<span class="badge badge-red"><i class="status-dot red"></i> Terminado</span>
{% endif %}
</td>
<td>
{% if project.category %}
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem; max-width: 250px;">
{% for spec in project.category.specialists.all %}
<span class="badge badge-blue" style="font-size: 0.7rem; font-weight: 600; padding: 0.15rem 0.35rem; border-radius: 4px;">{{ spec.user.username }}</span>
{% empty %}
<span style="color: var(--efintel-gray); font-size: 0.75rem; font-style: italic;">Sin especialistas</span>
{% endfor %}
</div>
{% else %}
<span style="color: var(--efintel-gray); font-size: 0.75rem; font-style: italic;">Asigna una categoría para ver especialistas</span>
{% endif %}
</td>
<td>
{% if project.manager %}
<span style="font-weight: 500;">{{ project.manager.username }}</span>
{% else %}
<em style="color: var(--text-secondary);">No asignado</em>
{% endif %}
</td>
<td>
<div style="display: flex; gap: 0.25rem;">
<a href="{% url 'project_edit' project.id %}" class="btn-icon" title="Editar" style="color: var(--efintel-blue); padding: 0.4rem; border-radius: 4px; transition: background 0.2s;">
<i data-lucide="edit-3" style="width: 16px;"></i>
</a>
<form action="{% url 'project_delete' project.id %}" method="POST" style="display: inline;" onsubmit="return confirm('¿Estás seguro de que deseas eliminar este proyecto? Se eliminarán todos los gastos asociados.');">
{% csrf_token %}
<button type="submit" class="btn-icon" title="Eliminar" style="color: var(--efintel-danger); padding: 0.4rem; border-radius: 4px; transition: background 0.2s; background: transparent; border: none; cursor: pointer;">
<i data-lucide="trash-2" style="width: 16px;"></i>
</button>
</form>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" style="text-align: center;">No hay proyectos registrados.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Pestaña Categorías -->
<div id="tab-categorias" class="tab-content">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
<div>
<h2 style="color: var(--efintel-petroleum); margin-bottom: 0.25rem;">Categorías Contables</h2>
<p style="color: var(--efintel-gray); font-size: 0.9rem;">Estructura de imputación financiera.</p>
</div>
<a href="{% url 'category_create' %}" class="btn-primary" style="text-decoration: none;">
<i data-lucide="plus-circle" style="width: 16px;"></i> Nueva Categoría
</a>
</div>
<div class="data-card" style="padding: 0;">
<div class="data-table-wrapper" style="margin: 0; padding: 1.5rem;">
<table class="data-table">
<thead>
<tr>
<th>Código</th>
<th>Nombre</th>
<th>Partida / Jerarquía</th>
<th>Descripción</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
{% for cat in categories %}
<tr>
<td style="font-weight: 600; color: var(--accent-primary);">{{ cat.code }}</td>
<td style="font-weight: 600; color: var(--text-primary);">
{% if cat.parent %}<span style="color:var(--efintel-gray); margin-right:5px;">↳</span>{% endif %}
{{ cat.name }}
</td>
<td>
{% if cat.parent %}
<span style="font-size: 0.85rem; color: var(--efintel-blue); font-weight: 500;">{{ cat.parent.code }} - {{ cat.parent.name }}</span>
{% else %}
<span class="badge badge-amber" style="font-size: 0.75rem;">Partida Principal</span>
{% endif %}
</td>
<td>{{ cat.description|default:"Sin descripción" }}</td>
<td>
<div style="display: flex; gap: 0.25rem;">
<a href="{% url 'category_edit' cat.id %}" class="btn-icon" title="Editar" style="color: var(--efintel-blue); padding: 0.4rem; border-radius: 4px; transition: background 0.2s;">
<i data-lucide="edit-3" style="width: 16px;"></i>
</a>
<form action="{% url 'category_delete' cat.id %}" method="POST" style="display: inline;" onsubmit="return confirm('¿Estás seguro de que deseas eliminar esta categoría? Se podrían perder asociaciones con los gastos.');">
{% csrf_token %}
<button type="submit" class="btn-icon" title="Eliminar" style="color: var(--efintel-danger); padding: 0.4rem; border-radius: 4px; transition: background 0.2s; background: transparent; border: none; cursor: pointer;">
<i data-lucide="trash-2" style="width: 16px;"></i>
</button>
</form>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" style="text-align: center;">No hay categorías registradas.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Pestaña Proveedores -->
<div id="tab-proveedores" class="tab-content">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
<div>
<h2 style="color: var(--efintel-petroleum); margin-bottom: 0.25rem;">Directorio de Proveedores</h2>
<p style="color: var(--efintel-gray); font-size: 0.9rem;">Registro centralizado de proveedores habilitados.</p>
</div>
<a href="{% url 'vendor_create' %}" class="btn-primary" style="text-decoration: none;">
<i data-lucide="plus-circle" style="width: 16px;"></i> Nuevo Proveedor
</a>
</div>
<div class="data-card" style="padding: 0;">
<div class="data-table-wrapper" style="margin: 0; padding: 1.5rem;">
<table class="data-table">
<thead>
<tr>
<th>RUT</th>
<th>Razón Social</th>
<th>Giro Comercial</th>
<th>Datos Bancarios</th>
<th>Plazo Pago</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
{% for vendor in vendors %}
<tr>
<td style="font-weight: 600; color: var(--accent-primary);">{{ vendor.rut }}</td>
<td style="font-weight: 600; color: var(--text-primary);">{{ vendor.name }}</td>
<td>{{ vendor.business_line|default:"-" }}</td>
<td>
{% if vendor.bank %}
<div style="font-size: 0.85rem;">{{ vendor.bank }}</div>
<div style="font-size: 0.75rem; color: var(--text-secondary);">{{ vendor.account_type }} - {{ vendor.account_number }}</div>
{% else %}
<em style="color: var(--text-secondary);">No registrados</em>
{% endif %}
</td>
<td>{{ vendor.payment_terms }} días</td>
<td>
<div style="display: flex; gap: 0.25rem;">
<a href="{% url 'vendor_edit' vendor.id %}" class="btn-icon" title="Editar" style="color: var(--efintel-blue); padding: 0.4rem; border-radius: 4px; transition: background 0.2s;">
<i data-lucide="edit-3" style="width: 16px;"></i>
</a>
<form action="{% url 'vendor_delete' vendor.id %}" method="POST" style="display: inline;" onsubmit="return confirm('¿Estás seguro de que deseas eliminar este proveedor? Sus gastos quedarán huérfanos.');">
{% csrf_token %}
<button type="submit" class="btn-icon" title="Eliminar" style="color: var(--efintel-danger); padding: 0.4rem; border-radius: 4px; transition: background 0.2s; background: transparent; border: none; cursor: pointer;">
<i data-lucide="trash-2" style="width: 16px;"></i>
</button>
</form>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" style="text-align: center;">No hay proveedores registrados.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Pestaña Responsables -->
<div id="tab-responsables" class="tab-content">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
<div>
<h2 style="color: var(--efintel-petroleum); margin-bottom: 0.25rem;">Responsables y Colaboradores</h2>
<p style="color: var(--efintel-gray); font-size: 0.9rem;">Gestión de acceso y roles del personal.</p>
</div>
<a href="{% url 'responsable_create' %}" class="btn-primary" style="text-decoration: none;">
<i data-lucide="plus-circle" style="width: 16px;"></i> Nuevo Responsable
</a>
</div>
<div class="data-card" style="padding: 0;">
<div class="data-table-wrapper" style="margin: 0; padding: 1.5rem;">
<table class="data-table">
<thead>
<tr>
<th>Usuario</th>
<th>Correo</th>
<th>Rol / Permiso</th>
<th>Especialidades Habilitadas (CBS)</th>
<th>Descripción / Bio</th>
<th>F. Registro</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
{% for resp in responsables %}
<tr>
<td style="font-weight: 600; color: var(--text-primary);">{{ resp.username }}</td>
<td>{{ resp.email|default:"-" }}</td>
<td>
{% if resp.groups.all %}
{% with role=resp.groups.all.0.name %}
{% if role == 'Administrador' %}
<span class="badge badge-red">Administrador</span>
{% elif role == 'Director de Proyecto' %}
<span class="badge badge-blue">Director</span>
{% else %}
<span class="badge badge-emerald">Gestor</span>
{% endif %}
{% endwith %}
{% else %}
<span class="badge badge-amber">Sin Rol</span>
{% endif %}
</td>
<td>
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem; max-width: 250px;">
{% for spec in resp.profile.specialties.all %}
<span class="badge badge-blue" style="font-size: 0.7rem; padding: 0.15rem 0.35rem;" title="{{ spec.name }}">{{ spec.code }}</span>
{% empty %}
<span style="color: var(--efintel-gray); font-size: 0.75rem; font-style: italic;">Ninguna</span>
{% endfor %}
</div>
</td>
<td>{{ resp.profile.description|default:"Sin descripción personal" }}</td>
<td>{{ resp.profile.created_at|date:"d/m/Y"|default:"-" }}</td>
<td>
<div style="display: flex; gap: 0.25rem;">
<a href="{% url 'responsable_edit' resp.id %}" class="btn-icon" title="Editar" style="color: var(--efintel-blue); padding: 0.4rem; border-radius: 4px; transition: background 0.2s;">
<i data-lucide="edit-3" style="width: 16px;"></i>
</a>
{% if resp != request.user %}
<form action="{% url 'responsable_delete' resp.id %}" method="POST" style="display: inline;" onsubmit="return confirm('¿Estás seguro de que deseas eliminar este responsable?');">
{% csrf_token %}
<button type="submit" class="btn-icon" title="Eliminar" style="color: var(--efintel-danger); padding: 0.4rem; border-radius: 4px; transition: background 0.2s; background: transparent; border: none; cursor: pointer;">
<i data-lucide="trash-2" style="width: 16px;"></i>
</button>
</form>
{% endif %}
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" style="text-align: center;">No hay responsables registrados.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Pestaña Configuración -->
<div id="tab-config" class="tab-content">
<div style="margin-bottom: 1.5rem;">
<h2 style="color: var(--efintel-petroleum); margin-bottom: 0.25rem;">Configuración General del Sistema</h2>
<p style="color: var(--efintel-gray); font-size: 0.9rem;">Parámetros de Impuestos y Tasas globales del ERP.</p>
</div>
<div style="max-width: 600px; display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem;">
<div class="data-card" style="padding: 1.5rem;">
<div style="color: var(--text-secondary); font-size: 0.85rem; margin-bottom: 0.5rem; font-weight:600;">Tasa de IVA (% Dinámico SII)</div>
<div style="font-size: 2.2rem; font-weight: 700; color: var(--efintel-petroleum);">{{ settings.iva_rate|default:"19.00" }}%</div>
</div>
<div class="data-card" style="padding: 1.5rem;">
<div style="color: var(--text-secondary); font-size: 0.85rem; margin-bottom: 0.5rem; font-weight:600;">Tasa de Cambio USD / CLP</div>
<div style="font-size: 2.2rem; font-weight: 700; color: var(--efintel-petroleum);">${{ settings.usd_exchange_rate|floatformat:"0"|default:"950" }}</div>
</div>
</div>
<div style="margin-top: 1.5rem;">
<a href="{% url 'settings_edit' %}" class="btn-primary" style="text-decoration: none;">
<i data-lucide="sliders" style="width:16px;"></i> Modificar Parámetros del Sistema
</a>
</div>
</div>
<!-- Pestaña Bitácora -->
<div id="tab-bitacora" class="tab-content">
<div style="margin-bottom: 1.5rem;">
<h2 style="color: var(--efintel-petroleum); margin-bottom: 0.25rem;">Bitácora de Auditoría y Trazabilidad (Logs)</h2>
<p style="color: var(--text-secondary); font-size: 0.9rem;">Historial detallado de operaciones realizadas por los administradores en el sistema.</p>
</div>
<div class="data-card" style="padding: 1.5rem;">
{% for log in logs %}
<div class="log-item">
<div class="log-info">
<div class="log-action">
{% if "Creado" in log.action or "Creación" in log.action %}
{{ log.action }}
{% elif "Eliminado" in log.action or "Eliminación" in log.action %}
{{ log.action }}
{% else %}
{{ log.action }}
{% endif %}
</div>
<div class="log-desc">{{ log.description }}</div>
</div>
<div class="log-meta">
<div style="font-weight: 500; color: var(--accent-primary);">@{{ log.user.username|default:"Sistema" }}</div>
<div style="font-size: 0.75rem; color: var(--text-secondary); margin-top: 0.25rem;">{{ log.timestamp|date:"d/m/Y H:i:s" }}</div>
</div>
</div>
{% empty %}
<div style="text-align: center; color: var(--text-secondary); padding: 2rem;">No hay registros en la bitácora de auditoría.</div>
{% endfor %}
</div>
</div>
</div> <!-- End Main Content Area -->
</div> <!-- End Gestion Layout -->
{% endblock %}
{% block extra_js %}
<script>
function switchTab(evt, tabId) {
// Ocultar todos los contenidos de pestaña
const contents = document.querySelectorAll('.tab-content');
contents.forEach(content => content.classList.remove('active'));
// Desactivar todos los botones de pestaña
const buttons = document.querySelectorAll('.sidebar-btn');
buttons.forEach(btn => btn.classList.remove('active'));
// Activar pestaña actual
document.getElementById(tabId).classList.add('active');
evt.currentTarget.classList.add('active');
// Guardar la pestaña activa en localStorage
localStorage.setItem('activeTab', tabId);
}
// Restaurar pestaña activa al cargar la página
document.addEventListener("DOMContentLoaded", function() {
const activeTab = localStorage.getItem('activeTab');
if (activeTab && document.getElementById(activeTab)) {
const btn = Array.from(document.querySelectorAll('.sidebar-btn')).find(b => b.getAttribute('onclick').includes(activeTab));
if (btn) {
// Simular clic en el botón
btn.click();
}
}
});
</script>
{% endblock %}