MEJORA: Refactorizacion a HTMX para WebSockets en tiempo real y agregado widget-tweaks

This commit is contained in:
Nachou-u
2026-09-09 08:46:44 -03:00
parent 7684136e46
commit 87467b1092
8 changed files with 107 additions and 197 deletions
+3 -71
View File
@@ -41,7 +41,7 @@
</div>
<div class="data-card">
<div class="data-table-wrapper">
<div class="data-table-wrapper" hx-ext="ws" ws-connect="/ws/expenses/">
<table class="data-table">
<thead>
<tr>
@@ -54,77 +54,9 @@
<th>Acciones</th>
</tr>
</thead>
<tbody>
<tbody id="expenses-tbody">
{% for expense in expenses %}
<tr>
<td style="font-family: var(--font-mono); font-size: 0.75rem; color: var(--efintel-gray);">{{ expense.expense_code|truncatechars:12 }}</td>
<td style="font-size:0.8rem; font-weight: 600;">{{ expense.date|date:"d/m/Y" }}</td>
<td>
<span class="project-tag" {% if expense.project.type == 'GASTO_OPERACIONAL_OVERHEAD' %}style="background:#e2e8f0; color:#475569;"{% endif %}>
{{ expense.project.code }}
</span>
</td>
<td style="font-weight:600; color:var(--efintel-dark); font-size: 0.85rem;">
{{ expense.description|truncatechars:40 }}<br>
<span style="font-size:0.65rem; color:var(--efintel-gray);">{{ expense.responsible_person.username|default:expense.user.username }}</span>
{% if expense.documents.all %}
<div style="margin-top: 0.25rem; display: flex; flex-direction: column; gap: 0.2rem;">
{% for doc in expense.documents.all %}
<div style="display:inline-flex; align-items:center; gap:0.3rem;">
<a href="{{ doc.file.url }}" target="_blank" style="font-size:0.65rem; color:var(--efintel-blue); text-decoration:none;" title="{{ doc.title|default:doc.file.name }}">
<i data-lucide="paperclip" style="width:10px;"></i> {{ doc.title|default:doc.file.name|truncatechars:20 }}
</a>
<button type="button" onclick="previewDocument('{{ doc.file.url }}', '{{ doc.title|escapejs }}', {% if doc.is_pdf %}true{% else %}false{% endif %})" style="background:none; border:none; color:#0284c7; cursor:pointer; padding:0; font-size:0.65rem;" title="Vista Previa Modal">
<i data-lucide="eye" style="width:12px;"></i> Ver
</button>
</div>
{% endfor %}
</div>
{% endif %}
</td>
<td class="amount-cell">
<div>${{ expense.amount_clp|floatformat:"0"|intcomma }} CLP</div>
<div style="font-size:0.68rem; color:var(--efintel-gray); font-family:var(--font-mono);">${{ expense.amount_usd|floatformat:"2"|intcomma }} USD</div>
</td>
<td>
{% if expense.approval_status == 'EN_REVISION_DOCUMENTAL' %}
<span class="badge badge-amber"><i data-lucide="clock" style="width:12px;"></i> En Revisión</span>
{% elif expense.approval_status == 'SOLICITADO_FINANZAS' %}
<span class="badge badge-blue"><i data-lucide="send" style="width:12px;"></i> Solicitado Finanzas</span>
{% elif expense.approval_status == 'LIBERADO_PAGADO' %}
<span class="badge badge-emerald"><i data-lucide="check-check" style="width:12px;"></i> Pagado</span>
{% else %}
<span class="badge badge-red"><i data-lucide="x-circle" style="width:12px;"></i> Rechazado</span>
{% endif %}
</td>
<td>
<div style="display: flex; gap: 0.3rem;">
{% if expense.approval_status == 'EN_REVISION_DOCUMENTAL' %}
<a href="{% url 'approve_expense' expense.id 'solicitar' %}" class="btn-primary" style="padding:0.3rem; font-size:0.65rem; background: var(--efintel-blue);" title="Elevar a Finanzas"><i data-lucide="arrow-right"></i></a>
{% endif %}
{% if expense.approval_status == 'SOLICITADO_FINANZAS' and 'Administrador Finanzas' in request.user.groups.all.0.name or request.user.is_superuser %}
<a href="{% url 'approve_expense' expense.id 'pagar' %}" class="btn-primary" style="padding:0.3rem; font-size:0.65rem; background: var(--efintel-success);" title="Liberar Pago"><i data-lucide="check"></i></a>
<a href="{% url 'approve_expense' expense.id 'rechazar' %}" class="btn-danger" style="padding:0.3rem; font-size:0.65rem;" title="Rechazar"><i data-lucide="x"></i></a>
{% endif %}
{% if expense.approval_status != 'SOLICITADO_FINANZAS' and expense.approval_status != 'LIBERADO_PAGADO' %}
{% if request.user.groups.all.0.name == 'Administrador' or request.user.is_superuser or expense.user == request.user %}
<a href="/gastos/{{ expense.id }}/editar/" class="btn-secondary" style="padding: 0.3rem; background: var(--efintel-light); color: var(--efintel-petroleum);" title="Editar"><i data-lucide="pencil" style="width:14px;"></i></a>
{% endif %}
{% if request.user.groups.all.0.name == 'Administrador' or request.user.is_superuser %}
<form method="post" action="/gastos/{{ expense.id }}/eliminar/" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn-danger" style="padding: 0.3rem;" onclick="return confirm('¿Seguro que desea eliminar esta erogación? Esta acción quedará en el Log de Auditoría.');" title="Eliminar"><i data-lucide="trash-2" style="width:14px;"></i></button>
</form>
{% endif %}
{% else %}
<span style="font-size:0.75rem; color:var(--efintel-gray); padding: 0.3rem;" title="Comprobante Bloqueado"><i data-lucide="lock" style="width:14px;"></i></span>
{% endif %}
</div>
</td>
</tr>
{% include 'expenses/partials/expense_row.html' with expense=expense %}
{% empty %}
<tr>
<td colspan="7" style="text-align: center; padding: 2rem; color: var(--efintel-gray);">No hay gastos registrados para este filtro.</td>
@@ -0,0 +1,67 @@
{% load humanize %}
<tr id="expense-{{ expense.id }}" class="fade-in">
<td style="font-family: var(--font-mono); font-size: 0.75rem; color: var(--efintel-gray);">{{ expense.expense_code|truncatechars:12 }}</td>
<td style="font-size:0.8rem; font-weight: 600;">{{ expense.date|date:"d/m/Y" }}</td>
<td>
<span class="project-tag" {% if expense.project.type == 'GASTO_OPERACIONAL_OVERHEAD' %}style="background:#e2e8f0; color:#475569;"{% endif %}>
{{ expense.project.code }}
</span>
</td>
<td style="font-weight:600; color:var(--efintel-dark); font-size: 0.85rem;">
{{ expense.description|truncatechars:40 }}<br>
<span style="font-size:0.65rem; color:var(--efintel-gray);">{{ expense.responsible_person.username|default:expense.user.username }}</span>
{% if expense.documents.all %}
<div style="margin-top: 0.25rem; display: flex; flex-direction: column; gap: 0.2rem;">
{% for doc in expense.documents.all %}
<div style="display:inline-flex; align-items:center; gap:0.3rem;">
<a href="{{ doc.file.url }}" target="_blank" style="font-size:0.65rem; color:var(--efintel-blue); text-decoration:none;" title="{{ doc.title|default:doc.file.name }}">
<i data-lucide="paperclip" style="width:10px;"></i> {{ doc.title|default:doc.file.name|truncatechars:20 }}
</a>
</div>
{% endfor %}
</div>
{% endif %}
</td>
<td class="amount-cell">
<div>${{ expense.amount_clp|floatformat:"0"|intcomma }} CLP</div>
<div style="font-size:0.68rem; color:var(--efintel-gray); font-family:var(--font-mono);">${{ expense.amount_usd|floatformat:"2"|intcomma }} USD</div>
</td>
<td>
{% if expense.approval_status == 'EN_REVISION_DOCUMENTAL' %}
<span class="badge badge-amber"><i data-lucide="clock" style="width:12px;"></i> En Revisión</span>
{% elif expense.approval_status == 'SOLICITADO_FINANZAS' %}
<span class="badge badge-blue"><i data-lucide="send" style="width:12px;"></i> Solicitado Finanzas</span>
{% elif expense.approval_status == 'LIBERADO_PAGADO' %}
<span class="badge badge-emerald"><i data-lucide="check-check" style="width:12px;"></i> Pagado</span>
{% else %}
<span class="badge badge-red"><i data-lucide="x-circle" style="width:12px;"></i> Rechazado</span>
{% endif %}
</td>
<td>
<div style="display: flex; gap: 0.3rem;">
{% if expense.approval_status == 'EN_REVISION_DOCUMENTAL' %}
<a href="{% url 'approve_expense' expense.id 'solicitar' %}" class="btn-primary" style="padding:0.3rem; font-size:0.65rem; background: var(--efintel-blue);" title="Elevar a Finanzas"><i data-lucide="arrow-right"></i></a>
{% endif %}
{% if expense.approval_status == 'SOLICITADO_FINANZAS' and 'Administrador Finanzas' in request.user.groups.all.0.name or request.user.is_superuser %}
<a href="{% url 'approve_expense' expense.id 'pagar' %}" class="btn-primary" style="padding:0.3rem; font-size:0.65rem; background: var(--efintel-success);" title="Liberar Pago"><i data-lucide="check"></i></a>
<a href="{% url 'approve_expense' expense.id 'rechazar' %}" class="btn-danger" style="padding:0.3rem; font-size:0.65rem;" title="Rechazar"><i data-lucide="x"></i></a>
{% endif %}
{% if expense.approval_status != 'SOLICITADO_FINANZAS' and expense.approval_status != 'LIBERADO_PAGADO' %}
{% if request.user.groups.all.0.name == 'Administrador' or request.user.is_superuser or expense.user == request.user %}
<a href="/gastos/{{ expense.id }}/editar/" class="btn-secondary" style="padding: 0.3rem; background: var(--efintel-light); color: var(--efintel-petroleum);" title="Editar"><i data-lucide="pencil" style="width:14px;"></i></a>
{% endif %}
{% if request.user.groups.all.0.name == 'Administrador' or request.user.is_superuser %}
<form method="post" action="/gastos/{{ expense.id }}/eliminar/" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn-danger" style="padding: 0.3rem;" onclick="return confirm('¿Seguro que desea eliminar esta erogación? Esta acción quedará en el Log de Auditoría.');" title="Eliminar"><i data-lucide="trash-2" style="width:14px;"></i></button>
</form>
{% endif %}
{% else %}
<span style="font-size:0.75rem; color:var(--efintel-gray); padding: 0.3rem;" title="Comprobante Bloqueado"><i data-lucide="lock" style="width:14px;"></i></span>
{% endif %}
</div>
</td>
</tr>