feat: Allow members to see Rada Izby events without joining
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

- Members can VIEW Rada Izby events (title, date, location)
- Only Rada members can RSVP and see attendee list
- Add can_user_see_attendees() method to NordaEvent
- Update event template to conditionally show RSVP and attendees
- Add info banner for non-Rada members viewing restricted events

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-02-03 12:46:06 +01:00
parent 61345fa260
commit 672a8a38cb
2 changed files with 73 additions and 14 deletions

View File

@ -1739,7 +1739,11 @@ class NordaEvent(Base):
return self.event_date < date.today()
def can_user_view(self, user) -> bool:
"""Check if a user can view this event based on access_level."""
"""Check if a user can view this event (title, date, location).
All members can SEE all events including Rada Izby events.
They just can't register or see attendee list for restricted events.
"""
if not user or not user.is_authenticated:
return False
@ -1754,13 +1758,56 @@ class NordaEvent(Base):
elif access == 'members_only':
return user.is_norda_member or user.has_role(SystemRole.MEMBER)
elif access == 'rada_only':
return user.is_rada_member
# Wszyscy członkowie WIDZĄ wydarzenia Rady Izby (tytuł, data, miejsce)
# ale nie mogą dołączyć ani zobaczyć uczestników
return user.is_norda_member or user.has_role(SystemRole.MEMBER) or user.is_rada_member
else:
return False
def can_user_attend(self, user) -> bool:
"""Check if a user can register for this event."""
return self.can_user_view(user)
"""Check if a user can register for this event.
For Rada Izby events, only designated board members can register.
"""
if not user or not user.is_authenticated:
return False
# Admins can attend everything
if user.is_admin or user.has_role(SystemRole.OFFICE_MANAGER):
return True
access = self.access_level or 'members_only'
if access == 'public':
return True
elif access == 'members_only':
return user.is_norda_member or user.has_role(SystemRole.MEMBER)
elif access == 'rada_only':
# Tylko członkowie Rady Izby mogą się zapisać
return user.is_rada_member
else:
return False
def can_user_see_attendees(self, user) -> bool:
"""Check if a user can see the attendee list.
For Rada Izby events, only board members can see who's attending.
"""
if not user or not user.is_authenticated:
return False
# Admins can see attendees
if user.is_admin or user.has_role(SystemRole.OFFICE_MANAGER):
return True
access = self.access_level or 'members_only'
if access == 'rada_only':
# Tylko członkowie Rady Izby widzą listę uczestników
return user.is_rada_member
else:
# Dla innych wydarzeń - wszyscy uprawnieni widzą uczestników
return self.can_user_view(user)
class EventAttendee(Base):

View File

@ -254,23 +254,35 @@
{% endif %}
{% if not event.is_past %}
<div class="rsvp-section">
<div>
<strong>Chcesz wziąć udział?</strong>
<p class="text-muted" style="margin: 0;">{{ event.attendee_count }} osób już się zapisało{% if event.max_attendees %} (limit: {{ event.max_attendees }}){% endif %}</p>
{% if event.can_user_attend(current_user) %}
<div class="rsvp-section">
<div>
<strong>Chcesz wziąć udział?</strong>
<p class="text-muted" style="margin: 0;">{{ event.attendee_count }} osób już się zapisało{% if event.max_attendees %} (limit: {{ event.max_attendees }}){% endif %}</p>
</div>
<button id="rsvp-btn" class="btn {% if user_attending %}btn-secondary attending{% else %}btn-primary{% endif %}" onclick="toggleRSVP()">
{% if user_attending %}Wypisz się{% else %}Wezmę udział{% endif %}
</button>
</div>
<button id="rsvp-btn" class="btn {% if user_attending %}btn-secondary attending{% else %}btn-primary{% endif %}" onclick="toggleRSVP()">
{% if user_attending %}Wypisz się{% else %}Wezmę udział{% endif %}
</button>
</div>
{% elif event.access_level == 'rada_only' %}
<div class="rsvp-section" style="background: #fef3c7; border: 1px solid #fde68a;">
<svg width="24" height="24" fill="none" stroke="#92400e" stroke-width="2" viewBox="0 0 24 24" style="flex-shrink: 0;">
<path d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
</svg>
<div>
<strong style="color: #92400e;">Wydarzenie tylko dla Rady Izby</strong>
<p class="text-muted" style="margin: 0; color: #a16207;">Zapisy są dostępne wyłącznie dla członków Rady Izby NORDA.</p>
</div>
</div>
{% endif %}
{% else %}
<div class="rsvp-section" style="background: var(--border);">
<p class="text-muted" style="margin: 0;">To wydarzenie już się odbyło. {{ event.attendee_count }} osób brało udział.</p>
<p class="text-muted" style="margin: 0;">To wydarzenie już się odbyło.{% if event.can_user_see_attendees(current_user) %} {{ event.attendee_count }} osób brało udział.{% endif %}</p>
</div>
{% endif %}
</div>
{% if event.attendees %}
{% if event.attendees and event.can_user_see_attendees(current_user) %}
<div class="attendees-section">
<h2>Uczestnicy ({{ event.attendee_count }})</h2>
<div class="attendees-list">