Commit Graph

20 Commits

Author SHA1 Message Date
9e581370e4 Add a club event-background photo, shown in black & white on event heroes
New Club.event_background, uploaded from the identity page. Individual
events have no photo of their own (an established, deliberate scope
decision for this build), so this is the one club-wide stand-in for the
photo the design canvas's own hero mockups call for -- shown under the
same dark gradient the hero already used, filtered to grayscale so it
never fights the club's own brand colours. Falls back to the existing
plain dark background when nothing's uploaded.

Applied to both event hero treatments: M2's own event-detail screen (the
"big black thing" this was reported against) and Home's "next up" card.
The image and gradient are separate absolutely-positioned layers behind
a z-10 content wrapper, not a filter on the card itself, so the
grayscale treatment never touches the text/buttons drawn on top of it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 18:11:02 +02:00
5bfb6ce76e Bigger unread badge with a real count, and a Clear all action for notifications
The header bell's unread indicator was an 8px dot -- now a proper count
badge (capped at "9+"), matching the size/legibility of similar badges
elsewhere in the app. Notifications also gains "Clear all" next to
"Mark all read" -- a hard delete of the list (the same clear-all gesture
a phone's own notification centre uses), not another read-state flip.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 17:41:14 +02:00
385e2f3c38 Enforce the RSVP deadline server-side; fix the calendar window shrinking late in the week
EventDetailView.post now rejects a POST once event.deadline has passed
(400), matching the read-only treatment the UI already showed -- a
disabled button is only a hint, not a guarantee against a direct POST.
M2's own "Your answers" card now also shows a read-only status pill
instead of the In/Maybe/Out buttons once closed, the same way Home's
hero card already did.

Also fixed Calendar's window: it was pinned to "through next calendar
week's Sunday", which meant the effective lookahead shrank to as little
as 8-9 days whenever today fell late in the week (e.g. a Friday), so an
event 10 days out could silently not appear even though it's well within
what "the next two weeks" should mean. The window is now always >= 14
days from today regardless of which weekday today is.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 17:39:23 +02:00
81f8f7f7dd Notify members when a new event is planned
New events.tasks.notify_new_event, scheduled from
management.views.EventCreateView.form_valid -- the deliberate "a staff
member planned one new event" action, not every Event row that happens
to get created. A recurring series' rolling-horizon extension
(extend_event_series) and bulk fixture imports are NOT wired to this on
purpose: either would flood everyone with one push per occurrence
instead of the single, deliberate action this is meant to catch.

Notifies whoever is still NO_RESPONSE right after creation -- exactly
"everyone who needs to respond", using the existing notify_members() ->
Notification -> mobile.signals push chain already in place, so this is
in-app (visible in the M7 inbox, mark read/mark-all-read) and a push
notification both, with no new plumbing needed there.

Generalized mobile.views.NotificationsView's News-only "tap to open the
source" handling (previously isinstance(source, News)) into
_notification_source_link, covering Event sources too -- tapping an
"new event" notification now marks it read and opens the event's answer
screen, the same way a news notification already opened the article.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 17:03:26 +02:00
1bf3a9d37a Merge the News page's title into the shared navy header too
Same header_extra treatment Calendar and Me just got -- a "News" title
in the app-header instead of no page heading at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 16:16:16 +02:00
65801a7843 Add a working event-kind filter to Calendar; merge per-screen headers into the shared navy bar
Calendar gains a real "All/Games/Practices" filter (?kind=), a working
version of the design mock's own "Games only" pill -- its List/Month
toggle isn't reproduced, that's a whole second view mode, not a filter.

base.html gains a header_extra block so a screen can add its own title
into the *same* navy app-header instead of stacking a second,
differently-coloured block underneath it. Me's avatar/name/subtitle row
moved there from its own bg-ink block (a visible colour seam that didn't
match the design canvas's own M5 markup, which keeps it all in one navy
area), and Calendar now shows a "Calendar" title the same way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 16:14:32 +02:00
40bee9af48 Show a News empty state on Home instead of hiding the card
Previously the whole "Club news" card just vanished when there was
nothing to show, dropping the "All news" link along with it. Now it
always renders -- either the real teaser or a "No news yet" card, still
linking through to mobile:news_list either way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 16:09:15 +02:00
515480ce51 Add calendar sync: a combined iCal subscription feed per account
New mobile:calendar_feed (/app/calendar/<token>.ics) -- a standard
webcal-style subscription feed calendar apps can poll, so a family's
schedule shows up in Apple/Google/Outlook Calendar alongside everything
else. Token-authenticated rather than session-authenticated (calendar
apps can't do interactive login); CalendarFeedToken is deliberately not
club-scoped, since the same token works under whichever club subdomain
the request is fetched from -- an account active in more than one club
needs only the one link.

Combined across every managed person (their name goes in the event
title when there's more than one), every event they're invited to
regardless of RSVP status, not capped to Calendar's own 2-week window --
a synced calendar app is exactly where someone wants the whole season
visible. A cancelled event stays in the feed as STATUS:CANCELLED rather
than disappearing, so it's removed properly on the subscriber's next
refresh instead of just vanishing.

New mobile:calendar_feed_settings (linked from Me) shows the webcal://
and https:// links plus a "reset my calendar link" action that
immediately invalidates the old one. Uses the icalendar package for
RFC 5545 generation rather than hand-rolling escaping/line-folding.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 15:15:11 +02:00
0e81e729b3 Show up to 3 news items on Home, with a link to a full "All news" page
Home's news card now shows up to 3 recent items (lead item with its photo
placeholder, the next two as compact rows) instead of just the latest
one, plus an "All news" link -- the design canvas's own M1 markup already
had that link, just unbuilt until now. Both also now filter by
visibility (internal or both, never external-only -- that's the public
website's own audience), which the teaser never actually enforced before.

New mobile:news_list page is the full archive: every published,
member-visible item for the club, newest first, not narrowed to any
particular team the way Home's own teaser is. Paginated at 20/page. The
bottom tab bar's News tab now links here instead of a dead #news anchor
on Home.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 15:07:48 +02:00
96fc58e453 Home/Calendar polish: person switcher placement, full-width Calendar, sponsors
- Home: the person switcher (chip row) now lives in the white content area
  instead of the navy header, matching the design canvas's own M1 markup,
  and renders smaller. Home is now the only screen that shows it at all.
- Calendar: dropped the person switcher and the "My schedule"/"All members"
  toggle entirely -- it always shows every event self.managed_people is
  invited to, full stop. Rows now run edge-to-edge (-mx-4) instead of
  living in a rounded, inset card, matching the M3 design canvas's own
  full-bleed layout. (The design mock's List/Month/"Games only" controls
  aren't reproduced -- no real functionality behind them yet.)
- Home's "Needs your answer" card caps at 5 items with a "+N more in
  Calendar" link, so it can't crowd the dues/news cards below it off the
  first screenful.
- Home gains a sponsors strip at the very bottom (horizontally scrolling,
  scrollbar hidden) -- active sponsors only, reshuffled on every request.
  club.services.sponsors.active_sponsors factors the "what counts as
  active" query out of club/api.py's public sponsors endpoint so both it
  and Home share one definition.
- Also removed mobile/views.py's now-fully-dead _PlaceholderScreen and its
  template -- every M1-M7 screen has had a real implementation for a while
  and nothing subclassed it anymore.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 15:00:33 +02:00
119341001a Round the app header crest and show initials without a logo
The header crest was management.css's hexagon clip-path mark, and its
no-logo fallback rendered an empty coloured hexagon with no initials at
all. Both now render as a plain circle, and the fallback shows the
club's initials on its own colour -- same source (Club.logo/Club.initials)
the PWA install icon already uses, just inline in the header instead of
a generated PNG.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 14:27:56 +02:00
397b9cf687 Add an "All" scope to the person switcher, default once there's a family
A lone member (or a parent of exactly one child) still lands straight on
their own record and never sees the switcher, same as before. The moment
there's more than one managed person, an "All" chip appears first and is
selected by default -- Home's cards (hero, needs-your-answer, dues) now
aggregate across everyone in scope instead of just one person, and
Calendar's own "My schedule" default does the same for its agenda.

PersonScopeMixin gains ``scope_everyone`` (bool) and ``people_in_scope``
(the effective list to filter by, regardless of which mode is active) --
kept separate from Calendar's own, unrelated ``?scope=all`` toggle
("every club event" vs. this "every person I manage"). Every RSVP form
now names its target member explicitly (``member_id``) rather than
relying on the old implicit "whoever is currently scoped" fallback, which
stops working the moment "All" -- not one person -- is the default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 14:22:40 +02:00
2cdba6a571 Build M6 Edit personal info for the mobile app
The last of the seven Member-mode screens. A real editable form for the
fields Member actually has (first/last name, date of birth, email,
phone, emergency phone) -- the design mock's national-register-number,
address, allergies/notes and consent-toggle rows have no backing field
and are omitted rather than added as new schema for a screen-building
pass. Two mock rows do have real data and are shown read-only instead:
every guardian via Member.guardians ("Emergency contact"), and any open
onboarding requirement for the person's current-season membership (via
club.services.onboarding.checklist_for) as a banner -- no upload/complete
action, that stays staff-only elsewhere in the platform.

Authorization: the target Member (from the URL) must be one of the
signed-in account's managed_people, checked on both GET and POST -- an
unmanaged id 404s, same as another club's Event/News already does
elsewhere in this app.

This completes M1-M7 (Home, Event detail, Calendar, News article, Me &
my people, Edit personal info, Notifications). Coach mode (C1-C6) is a
separate, later phase per the design doc and is intentionally not
started here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 14:11:56 +02:00
19e1acb93a Build M5 Me & my people for the mobile app
An account-level hub: a header for the signed-in account's own record
(member-since year, a "Team manager {team}" label for someone managing
1-2 teams, otherwise "Staff"), a "People I manage" list linking each
person into M6's edit screen, and a settings card linking into M6 (for
the account's own record) and M7 (Notifications).

The mockup's "Household & contacts"/"Payments & dues" rows and its
"Coach mode" promo have nowhere to lead in this build -- no dedicated
screen for the former, no Coach mode screens at all for the latter (same
precedent base.html's own role switcher already established) -- so
they're omitted rather than built as dead links. "Licence OK" has no
backing field either; each row's meta line uses real current-season
roster data (team + jersey number) instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 14:04:04 +02:00
3559c936f7 Build M4 News article for the mobile app
A photo-hero permalink for one published news item -- same dark-gradient
hero treatment as M2's event detail, with the club's main_photo where one
exists. Visibility mirrors news.tasks.notify_news_published's own
"actually live" gate, so a scheduled-but-not-yet-published item 404s here
the same way. Shows the English translation only when the request's
active language actually is English; native text otherwise.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 13:57:50 +02:00
f41a255b61 Build M7 Notifications for the mobile app
A flat, day-grouped notification list (Today / Earlier this week / Older)
scoped to every person the account manages, not just the switched-to one
-- matching how the header's unread badge already counts. The design
mock's per-type cards (RSVP-needed, invoice-due, medical-form, ...) have
no backing model support yet -- only news publishing creates a member
notification today -- so this stays generic rather than fabricating
categories; a notification whose source resolves to a News item links
through to it. Mark-all-read and mark-one-read actions on the same URL,
plus a first UI trigger for the push-subscribe plumbing built earlier.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 13:52:44 +02:00
13f369e10c Build M2 Event detail ("answer for several") for the mobile app
Full event-detail screen: hero header, Face-off/Meet/Where facts (the
design mock's Kit/dressing-room details have no backing Event field, so
they're simply omitted), a per-managed-person RSVP card so a parent with
several kids on the same event can answer for each independently, and a
club-visible squad-response aggregate (counts only, never who answered
what).

Extends the existing quick-RSVP POST (built for M1's Home hero) to also
accept "maybe" for M2's three-way buttons, and adds an optional
next=event_detail redirect target so answering here doesn't bounce back
to Home -- M1's and M3's existing forms are unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 13:44:53 +02:00
29acd22b7f Build M3 Calendar for the mobile app
A bounded chronological agenda (this week / next week, no month paging --
the mobile screen doesn't need the desktop week/month grid
events.services.calendar was built for) scoped to the person switcher's
current selection, plus an "All members" toggle for the whole club's
schedule. Each row links into the still-placeholder-GET event-detail
screen that M2 builds out next.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 10:52:59 +02:00
18627d2843 Build M1 Home for the mobile app
Hero card for the soonest upcoming event with a quick In/Out RSVP,
a "needs your answer" list, a season-dues card, and a news teaser --
all scoped to the person switcher's current selection. The RSVP quick
action posts to the (still placeholder-GET) event-detail URL that a
later screen (M2) will build out fully.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 10:39:18 +02:00
09df5d25b8 Scaffold the mobile member app: PWA shell, push, and app-shell tokens
New `mobile` Django app mounted at /app/ -- the installed PWA for Member
mode (M1-M7, see design_handoff_rosterchief_platform/README.md). Coach
mode (C1-C6) is a later phase and has no routes yet.

Foundation pieces:
- assets/mobile.css: Tailwind v4 theme reusing management.css's design
  tokens (same per-club --tenant-* theming pattern), plus the ice/coach
  accent and mobile's 14px card radius.
- PushSubscription model + pywebpush-based sender (mobile/services/push.py),
  wired to notifications.Notification via a post_save signal so the
  existing notification system gains a push channel without knowing about
  PWAs itself.
- Per-club manifest.webmanifest + service worker (served at /app/sw.js)
  + a server-rendered fallback home-screen icon (club initials on
  secondary_color) for clubs without an uploaded logo -- confirmed with
  the user as the fallback, never a generic RosterChief mark.
- App shell (base.html): navy header, person switcher (every child a
  signed-in parent manages, plus "Me"), bottom tab bar, safe-area insets.
  Vendored htmx + Alpine for the screens built on top of it.
- Placeholder views/routes for all seven M1-M7 screens so the shell is
  fully wired end-to-end before each screen is built out individually.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
2026-08-21 10:27:13 +02:00