Dashboard improvements
This commit is contained in:
parent
d37d0f95f1
commit
3154cfac22
@ -65,9 +65,22 @@ a.danger {
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-context {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-context h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard-grid {
|
.dashboard-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 500px));
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
gap: 20px
|
gap: 20px
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +92,7 @@ a.danger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-chart {
|
.card-chart {
|
||||||
max-width: 500px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-chart canvas {
|
.card-chart canvas {
|
||||||
@ -87,6 +100,11 @@ a.danger {
|
|||||||
height: 220px !important;
|
height: 220px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart-box {
|
||||||
|
height: 300px;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.tag{
|
.tag{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
@ -104,7 +122,7 @@ a.danger {
|
|||||||
|
|
||||||
.kpi-grid {
|
.kpi-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-file, minmax(180px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
}
|
}
|
||||||
@ -128,6 +146,18 @@ a.danger {
|
|||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* .comparison strong {
|
||||||
|
color:#b91c1c;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.comparison strong.positive {
|
||||||
|
color: #166534;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comparison strong.negative {
|
||||||
|
color:#b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
@ -206,3 +236,51 @@ tbody tr:hover {
|
|||||||
background: #fef3c7;
|
background: #fef3c7;
|
||||||
color: #92400e;
|
color: #92400e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-filters {
|
||||||
|
margin: 1rem 0 1.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: #f9fafb;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-filters form {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.75rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-filters select,
|
||||||
|
.dashboard-filters button {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-filters .checkbox {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.3rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-presets {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset {
|
||||||
|
padding: 0.35rem 0.6rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #e5e7eb;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #111827;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset:hover {
|
||||||
|
background: #d1d5db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset.active {
|
||||||
|
background: #2563eb;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
@ -3,64 +3,49 @@
|
|||||||
{% block title %}Dashboard{% endblock %}
|
{% block title %}Dashboard{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
||||||
|
|
||||||
<h1>
|
<section class="dashboard-context">
|
||||||
Dashboard
|
<h2>
|
||||||
{% if selected_account_obj %}
|
{% if selected_account_obj %}
|
||||||
— {{ selected_account_obj.name }}
|
{{ selected_account_obj.name }}
|
||||||
|
{% else %}
|
||||||
|
Todas las cuentas
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h1>
|
</h2>
|
||||||
|
|
||||||
|
<p class="muted">
|
||||||
|
{% if selected_month %}
|
||||||
|
{{ selected_month }}/{{ selected_year}}
|
||||||
|
{% else %}
|
||||||
|
Año {{ selected_year }}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- ========================= -->
|
<!-- ========================= -->
|
||||||
<!-- Filters -->
|
<!-- Filters -->
|
||||||
<!-- ========================= -->
|
<!-- ========================= -->
|
||||||
|
|
||||||
<div>
|
<section class="dashboard-filters">
|
||||||
<a href="{% url 'dashboard' %}?period=this_month">Este mes</a> |
|
<div class="dashboard-presets">
|
||||||
<a href="{% url 'dashboard' %}?period=last_month">Mes anterior</a> |
|
<a href="{% url 'dashboard' %}" data-period="this_month" class="preset {% if period == 'this_month' %}active{% endif %}">
|
||||||
<a href="{% url 'dashboard' %}?period=this_year">Este año</a>
|
Este mes
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{% url 'dashboard' %}" data-period="last_month" class="preset {% if period == 'last_month' %}active{% endif %}">
|
||||||
|
Mes anterior
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{% url 'dashboard' %}" data-period="this_year" class="preset {% if period == 'this_year' %}active{% endif %}">
|
||||||
|
Este año
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<form method="get" class="filters">
|
<form method="get">
|
||||||
<label>
|
|
||||||
Año:
|
|
||||||
<select name="year">
|
|
||||||
{% for y in year_list %}
|
|
||||||
<option value="{{ y }}"
|
|
||||||
{% if y == selected_year %}selected{% endif %}
|
|
||||||
>
|
|
||||||
{{y}}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
Mes:
|
|
||||||
<select name="month">
|
|
||||||
<option value="">Todos</option>
|
|
||||||
{% for m in months %}
|
|
||||||
<option value="{{ m }}"
|
|
||||||
{% if m == selected_month %}selected{% endif %}
|
|
||||||
>
|
|
||||||
{{ m }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="compare" value="1"
|
|
||||||
{% if compare_enabled %}checked{% endif %}>
|
|
||||||
Comparar con periodo anterior
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="account">Cuenta:</label>
|
|
||||||
<select name="account" id="account">
|
<select name="account" id="account">
|
||||||
<option value="">Todas</option>
|
<option value="">Todas las cuentas</option>
|
||||||
{% for acc in accounts %}
|
{% for acc in accounts %}
|
||||||
<option value="{{ acc.id }}"
|
<option value="{{ acc.id }}"
|
||||||
{% if selected_account == acc.id %}selected{% endif %}
|
{% if selected_account == acc.id %}selected{% endif %}
|
||||||
@ -70,23 +55,59 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select name="year">
|
||||||
|
{% for y in year_list %}
|
||||||
|
<option value="{{ y }}"
|
||||||
|
{% if y == selected_year %}selected{% endif %}
|
||||||
|
>
|
||||||
|
{{ y }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select name="month">
|
||||||
|
<option value="">Todo el año</option>
|
||||||
|
{% for m in months %}
|
||||||
|
<option value="{{ m }}"
|
||||||
|
{% if m == selected_month %}selected{% endif %}
|
||||||
|
>
|
||||||
|
{{ m }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" name="compare" value="1"
|
||||||
|
{% if compare_enabled %}checked{% endif %}>
|
||||||
|
Comparar
|
||||||
|
</label>
|
||||||
|
|
||||||
<button type="submit">Aplicar</button>
|
<button type="submit">Aplicar</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p>
|
</section>
|
||||||
Mostrando:
|
|
||||||
<strong>{{ selected_year }}</strong>
|
|
||||||
{% if selected_month %}/<strong>{{ selected_month }}</strong>{% endif %}
|
|
||||||
</p>
|
|
||||||
<p>Comparado con el periodo inmediatamente anterior</p>
|
|
||||||
|
|
||||||
<hr>
|
<script>
|
||||||
|
document.querySelectorAll('.dashboard-presets a').forEach(link => {
|
||||||
|
link.addEventListener('click', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const period = link.dataset.period;
|
||||||
|
const isActive = link.classList.contains('active');
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
window.location.href = link.href;
|
||||||
|
} else {
|
||||||
|
window.location.href = `${link.href}?period=${period}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- ========================= -->
|
<!-- ========================= -->
|
||||||
<!-- KPIs -->
|
<!-- KPIs -->
|
||||||
<!-- ========================= -->
|
<!-- ========================= -->
|
||||||
|
|
||||||
|
|
||||||
<section class="kpi-grid">
|
<section class="kpi-grid">
|
||||||
|
|
||||||
<div class="kpi-card">
|
<div class="kpi-card">
|
||||||
@ -111,145 +132,15 @@
|
|||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% if compare_enabled %}
|
<!-- ========================= -->
|
||||||
<section class="kpi compare">
|
<!-- Charts -->
|
||||||
<h3>Comparación con periodo anterior</h3>
|
<!-- ========================= -->
|
||||||
|
<section class="chart-box">
|
||||||
{% if kpi_previous_total == 0 %}
|
<canvas id="mainChart"></canvas>
|
||||||
<p><em>No hay datos en el periodo anterior</em></p>
|
|
||||||
{% else %}
|
|
||||||
<p>
|
|
||||||
Total anterior: <strong>{{ kpi_previous_total|floatformat:2 }}</strong>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Diferencia:
|
|
||||||
{% if kpi_trend == "up" %}
|
|
||||||
<span style="color:red;">▲ {{ kpi_difference|floatformat:2 }}€</span>
|
|
||||||
{% elif kpi_trend == "down" %}
|
|
||||||
<span style="color:green;">▼ {{ kpi_difference_abs|floatformat:2 }}€</span>
|
|
||||||
{% else %}
|
|
||||||
<span>0€</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Variación:
|
|
||||||
{% if kpi_percentage is not None %}
|
|
||||||
{{ kpi_percentage|floatformat:2 }}%
|
|
||||||
{% else %}
|
|
||||||
Variación: N/D
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- ========================= -->
|
|
||||||
<!-- Expenses by category -->
|
|
||||||
<!-- ========================= -->
|
|
||||||
<h2>Gastos por categoría</h2>
|
|
||||||
|
|
||||||
{% if not by_category %}
|
<h3>Evolución anual por cuenta ({{ selected_year }})</h3>
|
||||||
<p>No hay gastos para este periodo.</p>
|
|
||||||
{% else %}
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Categoría</th>
|
|
||||||
<th>Total</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for row in by_category %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ row.category__name }}</td>
|
|
||||||
<td>{{ row.total }}</td>
|
|
||||||
</tr>
|
|
||||||
{% empty %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">Sin datos</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if compare_enabled %}
|
|
||||||
<h3>Comparativa por categoría</h3>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Categoría</th>
|
|
||||||
<th>Actual</th>
|
|
||||||
<th>Anterior</th>
|
|
||||||
<th>Diferencia</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for row in category_comparison %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ row.category }}</td>
|
|
||||||
<td>{{ row.current|floatformat:2 }}€</td>
|
|
||||||
<td>{{ row.previous }}</td>
|
|
||||||
<td>
|
|
||||||
{% if row.difference > 0 %}
|
|
||||||
<span style="color:red;">▲ {{ row.difference|floatformat:2 }}€</span>
|
|
||||||
{% elif row.difference < 0 %}
|
|
||||||
<span style="color:green;">▼ {{ row.difference_abs|floatformat:2 }}€</span>
|
|
||||||
{% else %}
|
|
||||||
=
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<!-- ========================= -->
|
|
||||||
<!-- Expenses by month -->
|
|
||||||
<!-- ========================= -->
|
|
||||||
|
|
||||||
<h2>Gastos por mes</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{% for row in by_month %}
|
|
||||||
<li>Mes {{ row.month }} → {{ row.total }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<!-- ========================= -->
|
|
||||||
<!-- Chart -->
|
|
||||||
<!-- ========================= -->
|
|
||||||
|
|
||||||
<h2>Distribución mensual (año completo)</h2>
|
|
||||||
|
|
||||||
<canvas id="expensesChart"></canvas>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const labels = {{ chart_labels|safe }};
|
|
||||||
const data = {{ chart_data|safe }};
|
|
||||||
|
|
||||||
const ctx = document.getElementById('expensesChart');
|
|
||||||
|
|
||||||
new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: {
|
|
||||||
labels: labels,
|
|
||||||
datasets:[{
|
|
||||||
label: 'Gastos',
|
|
||||||
data: data,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h2>Evolución anual por cuenta ({{ selected_year }})</h2>
|
|
||||||
|
|
||||||
<div class="dashboard-grid">
|
<div class="dashboard-grid">
|
||||||
{% for acc in accounts_charts %}
|
{% for acc in accounts_charts %}
|
||||||
@ -262,6 +153,23 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const labels = {{ chart_labels|safe }};
|
||||||
|
const data = {{ chart_data|safe }};
|
||||||
|
|
||||||
|
const ctx = document.getElementById('mainChart');
|
||||||
|
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets:[{
|
||||||
|
label: 'Gastos',
|
||||||
|
data: data,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<script>
|
<script>
|
||||||
const monthLabels = {{ chart_labels|safe }};
|
const monthLabels = {{ chart_labels|safe }};
|
||||||
|
|
||||||
@ -289,4 +197,38 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% if compare_enabled %}
|
||||||
|
<section class="comparison">
|
||||||
|
<h3>Comparativa</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Diferencia:
|
||||||
|
<strong class="{% if kpi_trend == 'up' %}positive{% endif %}">
|
||||||
|
{% if kpi_trend == "up" %}+{% endif %}
|
||||||
|
{{ kpi_difference_abs|floatformat:2 }} €
|
||||||
|
</strong>
|
||||||
|
{% if kpi_percentage %}
|
||||||
|
({{ kpi_percentage|floatformat:1 }}%)
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Categoría</th>
|
||||||
|
<th>Total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in by_category %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.category__name }}</td>
|
||||||
|
<td>{{ row.total|floatformat:2 }} €</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -530,6 +530,7 @@ def dashboard(request):
|
|||||||
'selected_account_obj': selected_account_obj,
|
'selected_account_obj': selected_account_obj,
|
||||||
'kpi_balance': kpi_balance,
|
'kpi_balance': kpi_balance,
|
||||||
'accounts_charts': accounts_charts,
|
'accounts_charts': accounts_charts,
|
||||||
|
'period':period,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user