Add a news review workflow and a staff notification area

News gains a PENDING_REVIEW status between draft and published. A
non-editor author (can_add_news but not can_publish_news -- a
coach_manager, not an ADMIN/EDITOR) gets a "Send for review" button
instead of Publish; an editor/admin always sees Publish directly, no
review step. Submitting notifies every ADMIN/EDITOR in-app only (see
notify_members' new send_email=False) -- a review queue that emailed
on every submission would get noisy fast.

The notification area itself: a topbar bell (badge + dropdown, same
<details>/<summary> convention as the sidebar's user-menu, generalised
to a shared .dismissable-details close handler) visible on every page,
plus a fuller "Notifications" card on the dashboard, both fed by a new
notification_bell context processor. "Mark all read" clears the
signed-in staff member's own unread notifications for this club.

This is the reusable notification system's first consumer beyond news
publishing itself -- validates that notify_members()/Notification
generalise the way they were meant to.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
2026-08-21 09:52:59 +02:00
parent 20a44a915f
commit 0ecdeac354
16 changed files with 421 additions and 26 deletions

View File

@@ -6,6 +6,7 @@ app_name = "management"
urlpatterns = [
path("", views.HomeView.as_view(), name="home"),
path("notifications/mark-all-read/", views.NotificationMarkAllReadView.as_view(), name="notification_mark_all_read"),
# People
path("members/", views.MemberListView.as_view(), name="member_list"),
path("memberships/", views.MembershipListView.as_view(), name="membership_list"),
@@ -86,6 +87,7 @@ urlpatterns = [
path("news/<uuid:pk>/", views.NewsDetailView.as_view(), name="news_detail"),
path("news/<uuid:pk>/edit/", views.NewsUpdateView.as_view(), name="news_update"),
path("news/<uuid:pk>/delete/", views.NewsDeleteView.as_view(), name="news_delete"),
path("news/<uuid:pk>/submit-for-review/", views.NewsSubmitForReviewView.as_view(), name="news_submit_for_review"),
path("news/<uuid:pk>/publish/", views.NewsPublishView.as_view(), name="news_publish"),
path("news/<uuid:pk>/unpublish/", views.NewsUnpublishView.as_view(), name="news_unpublish"),
path("news/<uuid:pk>/photos/", views.NewsPhotoUploadView.as_view(), name="news_photo_upload"),