diff --git a/README.md b/README.md index 96f9df9..b4ae478 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,14 @@ Si le rythme normal suffit déjà à rentrer dans les clous, aucune préconisati Les plages qui ne respectent pas les minimums sont signalées en rouge dès la saisie. +**Enregistrement automatique** : chaque heure validée (sortie du champ, Entrée ou +sélecteur) enregistre le jour ; un témoin « ✓ enregistré » s'affiche un instant +dans la colonne Jour. Plus de bouton à cliquer. + +Quand la semaine affichée contient le jour courant, un **panneau Aujourd'hui** +s'invite en tête de page : heure de sortie cible en grand et barre de progression +travaillé/dû de la semaine, actualisés à chaque saisie. + Le bandeau au-dessus du tableau ajoute, quand il reste du retard à rattraper, une ligne **Reste à faire** : le total encore dû, le nombre de demi-journées restantes, et la part supplémentaire moyenne à ajouter sur chacune. @@ -173,8 +181,9 @@ docker compose run --rm pointeuse pytest -v ## Congés & jours fériés -Quatre boutons par ligne : **MA** (congé matin), **AM** (congé après-midi), **J** -(journée entière) et **F** (jour férié). Les trois premiers réduisent les heures +Chaque ligne porte un bouton d'état unique (colonne **État**) qui résume la +situation par des pastilles colorées et ouvre un menu : **MA** (congé matin), **AM** +(congé après-midi), **J** (journée entière) et **F** (jour férié). Les trois premiers réduisent les heures dues et recalculent les préconisations **Fin cible** pour les demi-journées restantes. **F** marque un jour férié (fête locale non calculée, pont...) : heures dues nulles, aucune présence obligatoire, ligne grisée avec badge « férié » — sans @@ -185,7 +194,7 @@ d'astreinte (×2 en journée par défaut) et pour la fenêtre d'astreinte ci-des ## Astreinte & repos de 11h -Le bouton **AS** marque un jour d'astreinte : la période couverte va de **18:00 +Le bouton **AS** du menu d'état marque un jour d'astreinte : la période couverte va de **18:00 ce jour-là à 18:00 le lendemain** (deux astreintes consécutives se recollent sans trou) — sauf **samedi, dimanche et jour férié**, qui comptent de **0h00 à 0h00** (journée entière ; un jour AS déclaré sam/dim/férié garde sa borne du lendemain diff --git a/app/main.py b/app/main.py index 33ed161..974c921 100644 --- a/app/main.py +++ b/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('J') + if "matin" in cg: + chips.append('MA') + if "aprem" in cg: + chips.append('AM') + if jour.get("ferie"): + chips.append('F') + if jour.get("astreinte"): + chips.append('AS') + state = "".join(chips) or '—' + d = _e(date) + + def opt(url, chip_cls, chip_txt, label, on, title=""): + t = f' title="{title}"' if title else "" + return (f'') + return ( - f'
' - f'
' - f'' - f'' - f'' - f'' - f'' - f'
' + f'
' + f'
' + f'' + f'{state}' + f'▾' + f'' + f'
' + + 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) + + '
' + + 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é)") + + '
' + f'
' + f'
' ) diff --git a/app/templates/_ligne.html b/app/templates/_ligne.html index f6c5fcd..e99bccc 100644 --- a/app/templates/_ligne.html +++ b/app/templates/_ligne.html @@ -18,6 +18,11 @@ {{ nom }} {{ j.date[8:] }}/{{ j.date[5:7] }} {% if j.ferie %}férié{% endif %} + + @@ -84,38 +89,46 @@ - - - - -
-
- - - - - -
+
+
+ + + {%- if 'jour' in j.conge %}J{% endif -%} + {%- if 'matin' in j.conge %}MA{% endif -%} + {%- if 'aprem' in j.conge %}AM{% endif -%} + {%- if j.ferie %}F{% endif -%} + {%- if j.astreinte %}AS{% endif -%} + {%- if not (j.conge or j.ferie or j.astreinte) %}—{% endif -%} + + ▾ + +
+ + + +
+ + +
+
@@ -123,7 +136,7 @@ {% if j.astreinte_qualifiee %} {% set nb_as = (j.astreinte_events or []) | length %} - +