- teams is now hard-locked to the active team (a hidden field, not a picker) -- there's no "which team" question on a screen already scoped to one. groups/club_wide are dropped for the same reason (both widen the audience past a single team). invited_members/excluded_members stay, re-scoped to sensible pools (add someone off the roster; exclude someone on it) instead of "every club member", with a note that a genuinely multi-team event still needs the desktop. - Opponent and competition are now rendered (game-only) -- opponent via a standalone picker with its own "+ New" popup, competition via EventForm's existing club-agnostic Competition list. - "+ New location"/"+ New opponent" popups create a Location/Opponent scoped to the club without leaving the screen: the modal's own htmx request creates the row and hands back the picker pre-selected via an out-of-band swap, so the rest of the in-progress form is never touched. - A "This repeats" toggle switches the same screen to build an EventSeries instead of a single Event (frequency/interval/weekdays/duration/until), reusing EventSeriesForm and generate_occurrences the same way management.views.EventSeriesCreateView does -- including that view's own behaviour of not sending a per-occurrence notification (the bulk send_deadline_reminders sweep covers it instead, same as a rolling- horizon extension). - Confirmed (and covered with a test) that a single event created this way still fires notify_new_event, notifying whoever's freshly invited. Along the way, found and fixed a real, previously-undetected rendering bug this surfaced: swapping a multi-choice field's widget to CheckboxSelectMultiple *after* setting its queryset/choices silently drops what the queryset/choices setter had already pushed onto the old widget, rendering an empty checkbox list. Hit this for invited_members/excluded_members and weekdays here, and found the same pre-existing bug in CoachCreateNewsView's own team checkboxes (the New Post screen's team picker has been rendering empty) -- fixed all of them (widget swapped in before queryset/choices, not after), with rendering (not just queryset) regression tests for each. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
44 lines
3.1 KiB
Python
44 lines
3.1 KiB
Python
from django.urls import path
|
|
|
|
from . import coach_views, views
|
|
|
|
app_name = "mobile"
|
|
|
|
urlpatterns = [
|
|
# PWA plumbing.
|
|
path("manifest.webmanifest", views.ManifestView.as_view(), name="manifest"),
|
|
path("sw.js", views.ServiceWorkerView.as_view(), name="service_worker"),
|
|
path("icon/<int:size>.png", views.AppIconView.as_view(), name="icon"),
|
|
path("push/subscribe/", views.PushSubscribeView.as_view(), name="push_subscribe"),
|
|
path("calendar/<str:token>.ics", views.CalendarFeedView.as_view(), name="calendar_feed"),
|
|
# Member mode (M1-M7).
|
|
path("", views.HomeView.as_view(), name="home"),
|
|
path("calendar/", views.CalendarView.as_view(), name="calendar"),
|
|
path("referee-signups/<uuid:signup_id>/respond/", views.RefereeSignupRespondView.as_view(), name="referee_signup_respond"),
|
|
path("events/<uuid:pk>/", views.EventDetailView.as_view(), name="event_detail"),
|
|
path("news/", views.NewsListView.as_view(), name="news_list"),
|
|
path("news/<slug:slug>/", views.NewsDetailView.as_view(), name="news_detail"),
|
|
path("me/", views.MeView.as_view(), name="me"),
|
|
path("me/payments/", views.PaymentsView.as_view(), name="payments"),
|
|
path("me/calendar-sync/", views.CalendarFeedSettingsView.as_view(), name="calendar_feed_settings"),
|
|
path("me/<uuid:member_id>/edit/", views.EditProfileView.as_view(), name="edit_profile"),
|
|
path("notifications/", views.NotificationsView.as_view(), name="notifications"),
|
|
# Coach mode (C1-C6).
|
|
path("coach/", coach_views.CoachTodayView.as_view(), name="coach_today"),
|
|
path("coach/squad/", coach_views.CoachSquadView.as_view(), name="coach_squad"),
|
|
path("coach/squad/<uuid:membership_pk>/", coach_views.CoachRosterMemberView.as_view(), name="coach_roster_member"),
|
|
path("coach/squad/<uuid:membership_pk>/remove/", coach_views.CoachRosterRemoveView.as_view(), name="coach_roster_remove"),
|
|
path("coach/schedule/", coach_views.CoachScheduleView.as_view(), name="coach_schedule"),
|
|
path("coach/attendance/<uuid:event_id>/", coach_views.CoachAttendanceView.as_view(), name="coach_attendance"),
|
|
path("coach/attendance/<uuid:event_id>/remind-silent/", coach_views.CoachAttendanceRemindSilentView.as_view(), name="coach_attendance_remind_silent"),
|
|
path("coach/events/new/", coach_views.CoachCreateEventView.as_view(), name="coach_create_event"),
|
|
path("coach/locations/new/", coach_views.CoachLocationCreateView.as_view(), name="coach_location_create"),
|
|
path("coach/opponents/new/", coach_views.CoachOpponentCreateView.as_view(), name="coach_opponent_create"),
|
|
path("coach/news/new/", coach_views.CoachCreateNewsView.as_view(), name="coach_create_news"),
|
|
path("coach/roster/add/", coach_views.CoachAddPlayerView.as_view(), name="coach_add_player"),
|
|
path("coach/staff/add/", coach_views.CoachAddStaffView.as_view(), name="coach_add_staff"),
|
|
path("coach/staff/<uuid:assignment_pk>/remove/", coach_views.CoachStaffRemoveView.as_view(), name="coach_staff_remove"),
|
|
path("coach/lineup/<uuid:event_id>/", coach_views.CoachLineupView.as_view(), name="coach_lineup"),
|
|
path("coach/lineup/<uuid:event_id>/publish/", coach_views.CoachLineupPublishView.as_view(), name="coach_lineup_publish"),
|
|
]
|