feat(ui): rich dashboard cards with countries and recent postcards
This commit is contained in:
@@ -102,17 +102,20 @@ def root(user: User = Depends(get_current_user_web)):
|
|||||||
@router.get("/dashboard", response_class=HTMLResponse)
|
@router.get("/dashboard", response_class=HTMLResponse)
|
||||||
def dashboard(request: Request, user: User = Depends(get_current_user_web), db: Session = Depends(get_db)):
|
def dashboard(request: Request, user: User = Depends(get_current_user_web), db: Session = Depends(get_db)):
|
||||||
profiles = db.query(Profile).filter(Profile.user_id == user.id).all()
|
profiles = db.query(Profile).filter(Profile.user_id == user.id).all()
|
||||||
# stats per profile
|
|
||||||
stats = {}
|
stats = {}
|
||||||
|
extras = {}
|
||||||
for p in profiles:
|
for p in profiles:
|
||||||
cards = db.query(Postcard).filter(Postcard.profile_id == p.id).all()
|
cards = db.query(Postcard).filter(Postcard.profile_id == p.id).order_by(Postcard.created_at.desc()).all()
|
||||||
stats[p.id] = {
|
stats[p.id] = {
|
||||||
"total": len(cards),
|
"total": len(cards),
|
||||||
"sent": sum(1 for c in cards if c.status == "sent"),
|
"sent": sum(1 for c in cards if c.status == "sent"),
|
||||||
"delivered": sum(1 for c in cards if c.status == "delivered"),
|
"delivered": sum(1 for c in cards if c.status == "delivered"),
|
||||||
"received": sum(1 for c in cards if c.status == "received"),
|
"received": sum(1 for c in cards if c.status == "received"),
|
||||||
}
|
}
|
||||||
return templates.TemplateResponse("dashboard.html", {"request": request, "user": user, "profiles": profiles, "stats": stats})
|
countries = sorted({c.country for c in cards if c.country})
|
||||||
|
recent = cards[:3]
|
||||||
|
extras[p.id] = {"countries": countries, "recent": recent}
|
||||||
|
return templates.TemplateResponse("dashboard.html", {"request": request, "user": user, "profiles": profiles, "stats": stats, "extras": extras})
|
||||||
|
|
||||||
|
|
||||||
# ---------- Profiles ----------
|
# ---------- Profiles ----------
|
||||||
|
|||||||
@@ -104,6 +104,11 @@ code { background: var(--bg); padding: .15rem .4rem; border-radius: 4px; font-si
|
|||||||
.dc-sent { color: var(--sent); }
|
.dc-sent { color: var(--sent); }
|
||||||
.dc-delivered { color: var(--delivered); }
|
.dc-delivered { color: var(--delivered); }
|
||||||
.dc-received { color: var(--received); }
|
.dc-received { color: var(--received); }
|
||||||
|
.dc-tags { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .75rem; }
|
||||||
|
.dc-tag { display: inline-block; padding: .15rem .5rem; background: var(--bg); border: 1px solid var(--border); border-radius: 999px; font-size: .7rem; color: var(--text-muted); font-weight: 500; }
|
||||||
|
.dc-recent { display: flex; align-items: center; gap: .4rem; margin-top: .6rem; font-size: .75rem; color: var(--text-muted); }
|
||||||
|
.dc-recent-label { flex-shrink: 0; }
|
||||||
|
.dc-recent-item { padding: .1rem .4rem; background: var(--bg); border-radius: 4px; font-family: monospace; font-size: .7rem; }
|
||||||
|
|
||||||
/* Postcard grid */
|
/* Postcard grid */
|
||||||
.postcard-card { display: flex; gap: 1rem; background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px; padding: .75rem; transition: box-shadow .15s; }
|
.postcard-card { display: flex; gap: 1rem; background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px; padding: .75rem; transition: box-shadow .15s; }
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
{% if profiles %}
|
{% if profiles %}
|
||||||
<div class="card-grid">
|
<div class="card-grid">
|
||||||
{% for p in profiles %}
|
{% for p in profiles %}
|
||||||
|
{% set ex = extras[p.id] %}
|
||||||
<a href="/profiles/{{ p.id }}/postcards" class="card dashboard-card">
|
<a href="/profiles/{{ p.id }}/postcards" class="card dashboard-card">
|
||||||
<div class="dc-header">
|
<div class="dc-header">
|
||||||
<span class="dc-icon">📮</span>
|
<span class="dc-icon">📮</span>
|
||||||
@@ -30,6 +31,21 @@
|
|||||||
<span class="dc-label">收到</span>
|
<span class="dc-label">收到</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if ex.countries %}
|
||||||
|
<div class="dc-tags">
|
||||||
|
{% for c in ex.countries %}
|
||||||
|
<span class="dc-tag">{{ c }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if ex.recent %}
|
||||||
|
<div class="dc-recent">
|
||||||
|
<span class="dc-recent-label">最近</span>
|
||||||
|
{% for c in ex.recent %}
|
||||||
|
<span class="dc-recent-item">{{ c.card_number }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user