Refonte de l'interface : sauvegarde automatique, menu d'état unique et panneau aujourd'hui
- Chaque heure validée enregistre le jour (plus de bouton ✓), avec un témoin
« enregistré » et conservation du focus entre les champs
- Les boutons MA/AM/J/F/AS deviennent un bouton d'état unique à pastilles
ouvrant un menu déroulant (congés, férié, astreinte)
- Panneau Aujourd'hui : sortie cible en grand et progression travaillé/dû
de la semaine, rafraîchis à chaque saisie
- Polish général : coins arrondis, ombres douces, hiérarchie des soldes,
états de focus visibles
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
69
app/main.py
69
app/main.py
@ -314,6 +314,21 @@ def _build_soldes_ctx(year, week, conges, h_jour, user_id):
|
||||
]
|
||||
result = compute_week(pointages, conges, h_jour, load_plages(user_id), load_astreintes(user_id), load_facteurs_astreinte(), load_feries(user_id))
|
||||
result["heures_jour"] = minutes_to_hhmm(h_jour)
|
||||
# Panneau « aujourd'hui » : uniquement si le jour courant appartient à la
|
||||
# semaine affichée (re-rendu à chaque enregistrement via _soldes.html).
|
||||
today_str = datetime.today().date().isoformat()
|
||||
jour_today = next((j for j in result["jours"] if j["date"] == today_str), None)
|
||||
if jour_today is not None:
|
||||
trav_total = sum(j["travaille_min"] for j in result["jours"])
|
||||
du_total = trav_total - result["solde_min"]
|
||||
pct = min(100, round(trav_total * 100 / du_total)) if du_total > 0 else None
|
||||
result["today"] = {
|
||||
"nom": JOURS_FR[datetime.strptime(today_str, "%Y-%m-%d").weekday()],
|
||||
"jour": jour_today,
|
||||
"pct": pct,
|
||||
"trav_total": minutes_to_hhmm(trav_total),
|
||||
"du_total": minutes_to_hhmm(du_total),
|
||||
}
|
||||
return {"result": result}
|
||||
|
||||
|
||||
@ -445,22 +460,48 @@ def _oob_time_cells(date, jour, plages):
|
||||
|
||||
|
||||
def _btns_html(date, jour):
|
||||
"""Boutons congé + astreinte + férié d'une ligne (cible du swap HTMX)."""
|
||||
"""Bouton d'état d'une ligne (cible du swap HTMX) : pastilles résumant
|
||||
congés / férié / astreinte, menu déroulant des cinq bascules. Doit rester
|
||||
synchronisé avec le rendu Jinja de _ligne.html."""
|
||||
cg = jour["conge"]
|
||||
btn_ma_on = " on" if "matin" in cg else ""
|
||||
btn_am_on = " on" if "aprem" in cg else ""
|
||||
btn_j_on = " jour-on" if "jour" in cg else ""
|
||||
btn_as_on = " as-on" if jour.get("astreinte") else ""
|
||||
btn_f_on = " ferie-on" if jour.get("ferie") else ""
|
||||
chips = []
|
||||
if "jour" in cg:
|
||||
chips.append('<span class="cg-chip st-jour">J</span>')
|
||||
if "matin" in cg:
|
||||
chips.append('<span class="cg-chip st-ma">MA</span>')
|
||||
if "aprem" in cg:
|
||||
chips.append('<span class="cg-chip st-am">AM</span>')
|
||||
if jour.get("ferie"):
|
||||
chips.append('<span class="cg-chip st-ferie">F</span>')
|
||||
if jour.get("astreinte"):
|
||||
chips.append('<span class="cg-chip st-as">AS</span>')
|
||||
state = "".join(chips) or '<span class="cg-none">—</span>'
|
||||
d = _e(date)
|
||||
|
||||
def opt(url, chip_cls, chip_txt, label, on, title=""):
|
||||
t = f' title="{title}"' if title else ""
|
||||
return (f'<button hx-post="{url}" hx-swap="none" class="cg-opt{" on" if on else ""}"{t}>'
|
||||
f'<span class="cg-opt-chip {chip_cls}">{chip_txt}</span> {label}</button>')
|
||||
|
||||
return (
|
||||
f'<div id="cg-btn-{_e(date)}" hx-swap-oob="true">'
|
||||
f'<div class="conge-btns">'
|
||||
f'<button hx-post="/conge/{_e(date)}/matin" hx-swap="none" class="btn-cg{btn_ma_on}" title="Congé matin">MA</button>'
|
||||
f'<button hx-post="/conge/{_e(date)}/aprem" hx-swap="none" class="btn-cg{btn_am_on}" title="Congé après-midi">AM</button>'
|
||||
f'<button hx-post="/conge/{_e(date)}/jour" hx-swap="none" class="btn-cg btn-jour{btn_j_on}" title="Journée entière">J</button>'
|
||||
f'<button hx-post="/ferie/{_e(date)}/toggle" hx-swap="none" class="btn-cg btn-ferie{btn_f_on}" title="Jour férié (dû 0, astreinte comptée 0h00 → 0h00)">F</button>'
|
||||
f'<button hx-post="/astreinte/{_e(date)}/toggle" hx-swap="none" class="btn-cg btn-as{btn_as_on}" title="Jour d'astreinte">AS</button>'
|
||||
f'</div></div>'
|
||||
f'<div id="cg-btn-{d}" class="cg-cell" hx-swap-oob="true">'
|
||||
f'<details class="cg-menu">'
|
||||
f'<summary class="cg-trigger" title="Congés, jour férié, astreinte">'
|
||||
f'<span class="cg-state">{state}</span>'
|
||||
f'<span class="cg-caret">▾</span>'
|
||||
f'</summary>'
|
||||
f'<div class="cg-pop">'
|
||||
+ opt(f"/conge/{d}/matin", "ma", "MA", "Congé matin", "matin" in cg)
|
||||
+ opt(f"/conge/{d}/aprem", "am", "AM", "Congé après-midi", "aprem" in cg)
|
||||
+ opt(f"/conge/{d}/jour", "jour", "J", "Journée entière", "jour" in cg)
|
||||
+ '<div class="cg-opt-sep"></div>'
|
||||
+ opt(f"/ferie/{d}/toggle", "ferie", "F", "Jour férié", jour.get("ferie"),
|
||||
"Jour férié : dû 0, astreinte comptée 0h00 → 0h00")
|
||||
+ opt(f"/astreinte/{d}/toggle", "as", "AS", "Jour d'astreinte", jour.get("astreinte"),
|
||||
"Jour d'astreinte (18h → 18h, ou 0h → 0h sam/dim/férié)")
|
||||
+ '</div>'
|
||||
f'</details>'
|
||||
f'</div>'
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user