Ajoute colonnes Δ sem. / Fin cible, supprime solde global, indicateurs minima
- Nouvelles colonnes : Δ sem. (solde cumulé semaine) et Fin cible (heure de sortie aprem optimale pour 0h suppl. en fin de semaine) - Δ sem. n'est affiché que quand la journée est complète et la chaîne de jours ininterrompue (pas de saut de jour non saisi) - Fin cible disparaît quand la journée est validée (passée/complète) - Delta = "—" pour les jours non travaillés (futur ou congé jour) - Indicateur warn (fond rouge) sur matin/aprem si horaires < minimum (matin 09:00→12:00, aprem 14:00→17:00) ; mise à jour live via JS - Suppression du solde global (3 cartes au lieu de 4) - Tableau sans largeurs fixes + container 1100 px pour éviter scroll horizontal sur écrans normaux ; touch-scroll conservé sur mobile - Base reste 39h/semaine (7h48/jour) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
104
app/main.py
104
app/main.py
@ -43,7 +43,7 @@ def _weeks_by_year(all_weeks: list) -> dict:
|
||||
|
||||
|
||||
def _build_soldes_ctx(year: int, week: int, conges: list[dict], h_jour: int) -> dict:
|
||||
"""Compute week result + global solde for the soldes partial."""
|
||||
"""Compute week result for the soldes partial."""
|
||||
dates = week_dates(year, week)
|
||||
raw = load_week_pointages(year, week)
|
||||
p_by_date = {p["date"]: p for p in raw}
|
||||
@ -53,21 +53,7 @@ def _build_soldes_ctx(year: int, week: int, conges: list[dict], h_jour: int) ->
|
||||
for d in dates
|
||||
]
|
||||
result = compute_week(pointages, conges, h_jour)
|
||||
|
||||
all_weeks = list_all_weeks()
|
||||
total_t = total_d = 0
|
||||
for yw in all_weeks:
|
||||
for p in load_week_pointages(*yw):
|
||||
for f, g in [("matin_sortie", "matin_entree"), ("aprem_sortie", "aprem_entree")]:
|
||||
if p.get(f) and p.get(g):
|
||||
total_t += max(0, hhmm_to_minutes(p[f]) - hhmm_to_minutes(p[g]))
|
||||
total_d += heures_dues(p["date"], conges, h_jour)
|
||||
|
||||
return {
|
||||
"result": result,
|
||||
"solde_global": minutes_to_hhmm(total_t - total_d),
|
||||
"solde_global_min": total_t - total_d,
|
||||
}
|
||||
return {"result": result}
|
||||
|
||||
|
||||
def _build_jour_ctx(date: str, conges: list[dict], h_jour: int) -> dict:
|
||||
@ -95,8 +81,8 @@ def _build_jour_ctx(date: str, conges: list[dict], h_jour: int) -> dict:
|
||||
def _oob_calc(date: str, jour: dict) -> str:
|
||||
"""Build OOB <span> fragments for the two calc cells (safe non-table elements)."""
|
||||
trav_inner = f'<span class="calc-num">{"—" if not jour["travaille_min"] else jour["travaille"]}</span>'
|
||||
delta_cls = "delta-pos" if jour["delta_min"] > 0 else ("delta-neg" if jour["delta_min"] < 0 else "delta-zero")
|
||||
if jour["travaille_min"] or jour["du_min"]:
|
||||
if jour["travaille_min"]:
|
||||
delta_cls = "delta-pos" if jour["delta_min"] > 0 else ("delta-neg" if jour["delta_min"] < 0 else "delta-zero")
|
||||
sign = "+" if jour["delta_min"] > 0 else ""
|
||||
delta_inner = f'<span class="{delta_cls}">{sign}{jour["delta"]}</span>'
|
||||
else:
|
||||
@ -107,20 +93,47 @@ def _oob_calc(date: str, jour: dict) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _htmx_conge_and_soldes(request: Request, date: str, d, conges: list[dict], h_jour: int) -> HTMLResponse:
|
||||
"""Return OOB <div>/<span> fragments for congé state + calc + soldes. No table elements."""
|
||||
iso = d.isocalendar()
|
||||
jour = _build_jour_ctx(date, conges, h_jour)
|
||||
def _oob_week_extras(result: dict) -> str:
|
||||
"""OOB <span> for all 5 days' cumulative delta and sortie cible."""
|
||||
html = ""
|
||||
for jour in result["jours"]:
|
||||
date = jour["date"]
|
||||
# Cumulative delta — only when chain of complete days is unbroken
|
||||
cm = jour["delta_cumul_min"]
|
||||
if jour.get("cumul_visible"):
|
||||
if cm == 0:
|
||||
cumul_inner = '<span class="delta-zero">0h00</span>'
|
||||
else:
|
||||
cls = "delta-pos" if cm > 0 else "delta-neg"
|
||||
sign = "+" if cm > 0 else "-"
|
||||
cumul_inner = f'<span class="{cls}">{sign}{jour["delta_cumul"]}</span>'
|
||||
else:
|
||||
cumul_inner = '<span class="delta-zero">—</span>'
|
||||
html += f'<span id="calc-{date}-cumul" hx-swap-oob="true">{cumul_inner}</span>'
|
||||
# Sortie cible — only for incomplete days
|
||||
cible = jour.get("sortie_cible") if not jour.get("is_complete") else None
|
||||
cible_inner = f'<span class="cible-num">{cible}</span>' if cible else '<span class="delta-zero">—</span>'
|
||||
html += f'<span id="calc-{date}-cible" hx-swap-oob="true">{cible_inner}</span>'
|
||||
return html
|
||||
|
||||
|
||||
def _oob_time_cells(date: str, jour: dict) -> str:
|
||||
"""OOB <div> for matin/aprem cells with congé state and warn class."""
|
||||
cg = jour["conge"]
|
||||
|
||||
cg_matin = "matin" in cg or "jour" in cg
|
||||
cg_aprem = "aprem" in cg or "jour" in cg
|
||||
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 ""
|
||||
|
||||
matin_cg = "matin" in cg or "jour" in cg
|
||||
aprem_cg = "aprem" in cg or "jour" in cg
|
||||
matin_warn = not matin_cg and jour["du_min"] > 0 and (
|
||||
(jour.get("matin_entree") and jour["matin_entree"] > "09:00") or
|
||||
(jour.get("matin_sortie") and jour["matin_sortie"] < "12:00")
|
||||
)
|
||||
aprem_warn = not aprem_cg and jour["du_min"] > 0 and (
|
||||
(jour.get("aprem_entree") and jour["aprem_entree"] > "14:00") or
|
||||
(jour.get("aprem_sortie") and jour["aprem_sortie"] < "17:00")
|
||||
)
|
||||
matin_cls = "td-fill cg" if matin_cg else ("td-fill warn" if matin_warn else "td-fill")
|
||||
aprem_cls = "td-fill cg" if aprem_cg else ("td-fill warn" if aprem_warn else "td-fill")
|
||||
matin_html = (
|
||||
f'<div id="cg-matin-{date}" hx-swap-oob="true" class="td-fill{" cg" if cg_matin else ""}">'
|
||||
f'<div id="cg-matin-{date}" hx-swap-oob="true" class="{matin_cls}">'
|
||||
f'<div class="time-pair">'
|
||||
f'<input class="time-input" type="time" name="matin_entree" data-date="{date}" value="{jour["matin_entree"] or ""}">'
|
||||
f'<span class="time-sep">→</span>'
|
||||
@ -128,13 +141,25 @@ def _htmx_conge_and_soldes(request: Request, date: str, d, conges: list[dict], h
|
||||
f'</div></div>'
|
||||
)
|
||||
aprem_html = (
|
||||
f'<div id="cg-aprem-{date}" hx-swap-oob="true" class="td-fill{" cg" if cg_aprem else ""}">'
|
||||
f'<div id="cg-aprem-{date}" hx-swap-oob="true" class="{aprem_cls}">'
|
||||
f'<div class="time-pair">'
|
||||
f'<input class="time-input" type="time" name="aprem_entree" data-date="{date}" value="{jour["aprem_entree"] or ""}">'
|
||||
f'<span class="time-sep">→</span>'
|
||||
f'<input class="time-input" type="time" name="aprem_sortie" data-date="{date}" value="{jour["aprem_sortie"] or ""}">'
|
||||
f'</div></div>'
|
||||
)
|
||||
return matin_html + aprem_html
|
||||
|
||||
|
||||
def _htmx_conge_and_soldes(request: Request, date: str, d, conges: list[dict], h_jour: int) -> HTMLResponse:
|
||||
"""Return OOB <div>/<span> fragments for congé state + calc + soldes. No table elements."""
|
||||
iso = d.isocalendar()
|
||||
jour = _build_jour_ctx(date, conges, h_jour)
|
||||
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 ""
|
||||
btns_html = (
|
||||
f'<div id="cg-btn-{date}" hx-swap-oob="true">'
|
||||
f'<div class="conge-btns">'
|
||||
@ -146,16 +171,27 @@ def _htmx_conge_and_soldes(request: Request, date: str, d, conges: list[dict], h
|
||||
|
||||
soldes_ctx = _build_soldes_ctx(iso.year, iso.week, conges, h_jour)
|
||||
soldes_html = templates.get_template("_soldes.html").render({"request": request, **soldes_ctx})
|
||||
return HTMLResponse(matin_html + aprem_html + btns_html + _oob_calc(date, jour) + soldes_html)
|
||||
return HTMLResponse(
|
||||
_oob_time_cells(date, jour)
|
||||
+ btns_html
|
||||
+ _oob_calc(date, jour)
|
||||
+ _oob_week_extras(soldes_ctx["result"])
|
||||
+ soldes_html
|
||||
)
|
||||
|
||||
|
||||
def _htmx_calc_and_soldes(request: Request, date: str, d, conges: list[dict], h_jour: int) -> HTMLResponse:
|
||||
"""Return OOB <span> for calc cells + OOB <div> for soldes. No table elements."""
|
||||
"""Return OOB <span>/<div> for calc cells, time cells, week extras, and soldes."""
|
||||
iso = d.isocalendar()
|
||||
jour = _build_jour_ctx(date, conges, h_jour)
|
||||
soldes_ctx = _build_soldes_ctx(iso.year, iso.week, conges, h_jour)
|
||||
soldes_html = templates.get_template("_soldes.html").render({"request": request, **soldes_ctx})
|
||||
return HTMLResponse(_oob_calc(date, jour) + soldes_html)
|
||||
return HTMLResponse(
|
||||
_oob_calc(date, jour)
|
||||
+ _oob_time_cells(date, jour)
|
||||
+ _oob_week_extras(soldes_ctx["result"])
|
||||
+ soldes_html
|
||||
)
|
||||
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
|
||||
Reference in New Issue
Block a user