Make the whole page scroll, show referee sign-up on the event page too

The app shell now scrolls as one page (body/document) instead of boxing
content inside an inner overflow-auto <main> under a fixed header/tab bar --
header and tab bar are sticky instead, which reads as a single intuitive
scroll (most noticeable on Calendar's long agenda) and, as a side effect,
means htmx's boosted-navigation scroll-to-top actually resets what the user
sees. The inner "This week"/"Today" sticky sub-headers are no longer sticky
themselves, since they'd otherwise collide with the now-sticky app header.

Event detail also shows the same referee Accept/Decline card the Calendar
row offers, for a signed-in account with a pending or accepted sign-up on
that game -- not just on the Calendar. Both the calendar row's and the
event page's Accept/Decline forms are now htmx-boosted (no hx-boost="false")
since neither is Alpine-toggled, so a response no longer forces a full page
reload.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
2026-08-22 18:08:17 +02:00
parent 875250366e
commit e349fbbf03
9 changed files with 133 additions and 26 deletions

View File

@@ -10,9 +10,11 @@
on their own behalf, not something a parent does for a child.
Expects ``row`` ({event, referee_signup}) in scope. Accept/Decline are
two small forms, not a single multi-button one, so hx-boost="false" can
sit on each without a formaction/htmx-boost mismatch (see event_detail.
html's own top-of-file comment for that failure mode).
two small forms, not a single multi-button one -- and, unlike this
app's RSVP forms, boosted (no hx-boost="false"): nothing here is
Alpine-owned/toggled, so there's no htmx/Alpine conflict to dodge, and a
boosted POST avoids the jarring full-page reload a plain form submit
would cause on a page the user is mid-scroll on.
{% endcomment %}
<div class="flex items-center gap-3 bg-white px-4 py-3">
<div class="w-9.5 shrink-0 text-center">
@@ -35,12 +37,12 @@
<span class="pill pill-referee shrink-0">{% trans "Confirmed" %}</span>
{% else %}
<div class="flex shrink-0 gap-1.5">
<form method="post" action="{% url "mobile:referee_signup_respond" row.referee_signup.pk %}" hx-boost="false">
<form method="post" action="{% url "mobile:referee_signup_respond" row.referee_signup.pk %}">
{% csrf_token %}
<input type="hidden" name="response" value="accept">
<button type="submit" class="pill pill-referee">{% trans "I'll ref" %}</button>
</form>
<form method="post" action="{% url "mobile:referee_signup_respond" row.referee_signup.pk %}" hx-boost="false">
<form method="post" action="{% url "mobile:referee_signup_respond" row.referee_signup.pk %}">
{% csrf_token %}
<input type="hidden" name="response" value="decline">
<button type="submit" class="pill pill-neutral">{% trans "Can't" %}</button>

View File

@@ -23,6 +23,14 @@
the OLD shell's <body> class/attributes in place under the NEW shell's
content. See coach/base.html's own comment for the same rule mirrored
the other direction.
The page itself scrolls (body/document), not a boxed-in <main> -- header
and tab bar are sticky (top-0/bottom-0) rather than the earlier fixed-
height-shell-with-an-inner-overflow-auto-region approach, which read as
two separate scrollable areas nested inside each other (most noticeable
on Calendar's long agenda). A side benefit: htmx's boosted navigation
scrolls the window back to top by default, which now actually resets
what the user sees -- with the old inner <main> scroll it didn't.
{% endcomment %}
<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE|default:"en" }}">
@@ -62,8 +70,8 @@
{% block extra_head %}{% endblock extra_head %}
</head>
<body class="flex h-screen flex-col overflow-hidden bg-paper font-sans text-slate" hx-boost="true" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' data-vapid-public-key="{{ vapid_public_key }}" data-csrftoken="{{ csrf_token }}">
<header class="app-header">
<body class="flex min-h-screen flex-col bg-paper font-sans text-slate" hx-boost="true" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' data-vapid-public-key="{{ vapid_public_key }}" data-csrftoken="{{ csrf_token }}">
<header class="app-header sticky top-0 z-20">
<div class="flex min-h-11 items-center gap-2.5">
<a class="flex items-center gap-2.5" href="{% url "mobile:home" %}">
{% if club.logo %}
@@ -95,7 +103,7 @@
{% block header_extra %}{% endblock header_extra %}
</header>
<main class="flex-1 overflow-y-auto">
<main class="flex-1">
<div class="flex flex-col gap-4 px-4 py-4">
{% if messages %}
<div class="flex flex-col gap-2">
@@ -109,7 +117,7 @@
</div>
</main>
<nav class="tab-bar">
<nav class="tab-bar sticky bottom-0 z-20">
<a class="tab-bar-item {% if active_tab == "home" %}tab-bar-item-active{% endif %}" href="{% url "mobile:home" %}">
{% lucide "house" size=21 %}
<span class="tab-bar-label">{% trans "Home" %}</span>

View File

@@ -60,7 +60,7 @@
{% else %}
{% if this_week %}
<div>
<div class="sticky top-0 z-10 bg-paper px-4 py-2 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "This week" %}</div>
<div class="bg-paper px-4 py-2 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "This week" %}</div>
<div class="flex flex-col gap-px bg-line">
{% for row in this_week %}
{% if row.referee_signup %}{% include "mobile/_calendar_referee_row.html" %}{% else %}{% include "mobile/_calendar_row.html" %}{% endif %}
@@ -71,7 +71,7 @@
{% if next_week %}
<div>
<div class="sticky top-0 z-10 bg-paper px-4 py-2 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Next week" %}</div>
<div class="bg-paper px-4 py-2 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Next week" %}</div>
<div class="flex flex-col gap-px bg-line">
{% for row in next_week %}
{% if row.referee_signup %}{% include "mobile/_calendar_referee_row.html" %}{% else %}{% include "mobile/_calendar_row.html" %}{% endif %}
@@ -82,7 +82,7 @@
{% for month in later_months %}
<div>
<div class="sticky top-0 z-10 bg-paper px-4 py-2 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{{ month.month_start|date:"F Y" }}</div>
<div class="bg-paper px-4 py-2 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{{ month.month_start|date:"F Y" }}</div>
<div class="flex flex-col gap-px bg-line">
{% for row in month.rows %}
{% if row.referee_signup %}{% include "mobile/_calendar_referee_row.html" %}{% else %}{% include "mobile/_calendar_row.html" %}{% endif %}

View File

@@ -58,6 +58,34 @@
{% endif %}
</div>
{% if referee_signup %}
<div class="m-card p-4">
<div class="flex items-center justify-between">
<span class="font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Refereeing" %}</span>
{% if referee_signup.status == "accepted" %}<span class="pill pill-referee">{% trans "Confirmed" %}</span>{% endif %}
</div>
{% if referee_signup.status == "accepted" %}
<p class="mt-2 text-sm text-muted">{% trans "You're confirmed to referee this game." %}</p>
{% else %}
<p class="mt-2 text-sm text-ink">{% trans "You're eligible to referee this game -- are you in?" %}</p>
<div class="mt-3 grid grid-cols-2 gap-2">
<form method="post" action="{% url "mobile:referee_signup_respond" referee_signup.pk %}">
{% csrf_token %}
<input type="hidden" name="response" value="accept">
<input type="hidden" name="next" value="event_detail">
<button type="submit" class="btn w-full bg-referee text-white">{% trans "I'll ref" %}</button>
</form>
<form method="post" action="{% url "mobile:referee_signup_respond" referee_signup.pk %}">
{% csrf_token %}
<input type="hidden" name="response" value="decline">
<input type="hidden" name="next" value="event_detail">
<button type="submit" class="btn btn-secondary w-full">{% trans "Can't" %}</button>
</form>
</div>
{% endif %}
</div>
{% endif %}
{% if lineup %}
<div class="m-card p-4">
<div class="flex items-center justify-between">
@@ -198,7 +226,7 @@
</div>
{% endif %}
{% if not your_answers and not squad_summary %}
{% if not your_answers and not squad_summary and not lineup and not referee_signup %}
<div class="m-card p-6 text-center">
<p class="text-sm text-muted">{% trans "No one you manage is invited to this event." %}</p>
</div>

View File

@@ -51,7 +51,7 @@
{% else %}
{% if today %}
<div>
<div class="sticky top-0 z-10 bg-paper py-1 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Today" %}</div>
<div class="bg-paper py-1 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Today" %}</div>
<div class="flex flex-col gap-2">
{% for row in today %}
{% include "mobile/_notification_row.html" %}
@@ -62,7 +62,7 @@
{% if earlier_this_week %}
<div>
<div class="sticky top-0 z-10 bg-paper py-1 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Earlier this week" %}</div>
<div class="bg-paper py-1 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Earlier this week" %}</div>
<div class="flex flex-col gap-2">
{% for row in earlier_this_week %}
{% include "mobile/_notification_row.html" %}
@@ -73,7 +73,7 @@
{% if older %}
<div>
<div class="sticky top-0 z-10 bg-paper py-1 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Older" %}</div>
<div class="bg-paper py-1 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Older" %}</div>
<div class="flex flex-col gap-2">
{% for row in older %}
{% include "mobile/_notification_row.html" %}