feat(dashboard): split country stats into sent/received bar charts side by side
This commit is contained in:
@@ -397,12 +397,18 @@ def dashboard(request: Request, user: User = Depends(get_current_user_web), db:
|
|||||||
else:
|
else:
|
||||||
sent_pct = delivered_pct = received_pct = 0
|
sent_pct = delivered_pct = received_pct = 0
|
||||||
|
|
||||||
# country stats
|
# country stats split by sent/received
|
||||||
country_counts: dict[str, int] = {}
|
sent_country_counts: dict[str, int] = {}
|
||||||
|
received_country_counts: dict[str, int] = {}
|
||||||
for c in all_cards:
|
for c in all_cards:
|
||||||
if c.country:
|
if not c.country:
|
||||||
country_counts[c.country] = country_counts.get(c.country, 0) + 1
|
continue
|
||||||
country_stats = sorted(country_counts.items(), key=lambda x: -x[1])
|
if c.status in ("sent", "delivered"):
|
||||||
|
sent_country_counts[c.country] = sent_country_counts.get(c.country, 0) + 1
|
||||||
|
elif c.status == "received":
|
||||||
|
received_country_counts[c.country] = received_country_counts.get(c.country, 0) + 1
|
||||||
|
sent_country_stats = sorted(sent_country_counts.items(), key=lambda x: -x[1])
|
||||||
|
received_country_stats = sorted(received_country_counts.items(), key=lambda x: -x[1])
|
||||||
|
|
||||||
from datetime import datetime, timezone as _tz
|
from datetime import datetime, timezone as _tz
|
||||||
|
|
||||||
@@ -411,7 +417,7 @@ def dashboard(request: Request, user: User = Depends(get_current_user_web), db:
|
|||||||
"total": total, "sent": sent, "delivered": delivered, "received": received,
|
"total": total, "sent": sent, "delivered": delivered, "received": received,
|
||||||
"countries": countries, "recent": recent,
|
"countries": countries, "recent": recent,
|
||||||
"sent_pct": sent_pct, "delivered_pct": delivered_pct, "received_pct": received_pct,
|
"sent_pct": sent_pct, "delivered_pct": delivered_pct, "received_pct": received_pct,
|
||||||
"country_stats": country_stats,
|
"sent_country_stats": sent_country_stats, "received_country_stats": received_country_stats,
|
||||||
"now": datetime.now(_tz.utc),
|
"now": datetime.now(_tz.utc),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -59,33 +59,43 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if country_stats %}
|
{% if sent_country_stats or received_country_stats %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
|
||||||
<div style="background:var(--card-bg);border:1px solid var(--border);border-radius:10px;padding:1.25rem;margin-bottom:1.5rem">
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1.5rem">
|
||||||
<h2 style="margin-bottom:.75rem">🌍 国家/地区分布</h2>
|
{% if sent_country_stats %}
|
||||||
<canvas id="country-chart" height="100"></canvas>
|
<div style="background:var(--card-bg);border:1px solid var(--border);border-radius:10px;padding:1.25rem">
|
||||||
|
<h2 style="margin-bottom:.75rem">📤 寄出</h2>
|
||||||
|
<canvas id="chart-sent"></canvas>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if received_country_stats %}
|
||||||
|
<div style="background:var(--card-bg);border:1px solid var(--border);border-radius:10px;padding:1.25rem">
|
||||||
|
<h2 style="margin-bottom:.75rem">📬 收到</h2>
|
||||||
|
<canvas id="chart-received"></canvas>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
new Chart(document.getElementById('country-chart'), {
|
{% if sent_country_stats %}
|
||||||
|
new Chart(document.getElementById('chart-sent'), {
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {
|
data: {
|
||||||
labels: [{% for code, _ in country_stats %}'{{ code|flag }} {{ code }}',{% endfor %}],
|
labels: [{% for code, _ in sent_country_stats %}'{{ code|flag }} {{ code }}',{% endfor %}],
|
||||||
datasets: [{
|
datasets: [{ data: [{% for _, count in sent_country_stats %}{{ count }},{% endfor %}], backgroundColor: '#3b82f6', borderRadius: 4, maxBarThickness: 40 }]
|
||||||
data: [{% for _, count in country_stats %}{{ count }},{% endfor %}],
|
|
||||||
backgroundColor: '#3b82f6',
|
|
||||||
borderRadius: 4,
|
|
||||||
maxBarThickness: 40
|
|
||||||
}]
|
|
||||||
},
|
},
|
||||||
options: {
|
options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { stepSize: 1 } }, x: { grid: { display: false } } } }
|
||||||
responsive: true,
|
|
||||||
plugins: { legend: { display: false } },
|
|
||||||
scales: {
|
|
||||||
y: { beginAtZero: true, ticks: { stepSize: 1 } },
|
|
||||||
x: { grid: { display: false } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
{% if received_country_stats %}
|
||||||
|
new Chart(document.getElementById('chart-received'), {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: [{% for code, _ in received_country_stats %}'{{ code|flag }} {{ code }}',{% endfor %}],
|
||||||
|
datasets: [{ data: [{% for _, count in received_country_stats %}{{ count }},{% endfor %}], backgroundColor: '#8b5cf6', borderRadius: 4, maxBarThickness: 40 }]
|
||||||
|
},
|
||||||
|
options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { stepSize: 1 } }, x: { grid: { display: false } } } }
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user