diff --git a/backend/api/v1/schedule/views.py b/backend/api/v1/schedule/views.py
index 001352d7..c6b7f6db 100644
--- a/backend/api/v1/schedule/views.py
+++ b/backend/api/v1/schedule/views.py
@@ -321,8 +321,39 @@ class ScheduleEventViewSet(viewsets.ModelViewSet):
return EventSerializer
def get_queryset(self):
- """Фильтрация событий."""
+ """Фильтрация событий.
+
+ Пользователь видит события из:
+ 1. Календарей, где он участник (CalendarRelation)
+ 2. Событий, где он участник (EventRelation)
+ """
+ from django.contrib.auth import get_user_model
+ User = get_user_model()
+
qs = super().get_queryset()
+ user = self.request.user
+
+ # Админы видят все события
+ if not (user.is_staff or user.is_superuser):
+ ct = ContentType.objects.get_for_model(User)
+
+ # ID календарей где пользователь участник
+ accessible_calendar_ids = CalendarRelation.objects.filter(
+ content_type=ct,
+ object_id=user.id
+ ).values_list('calendar_id', flat=True)
+
+ # ID событий где пользователь участник
+ participant_event_ids = EventRelation.objects.filter(
+ content_type=ct,
+ object_id=user.id
+ ).values_list('event_id', flat=True)
+
+ # Объединяем: события из доступных календарей ИЛИ где пользователь участник
+ qs = qs.filter(
+ Q(calendar_id__in=accessible_calendar_ids) |
+ Q(id__in=participant_event_ids)
+ )
# Фильтр по календарю
calendar_id = self.request.query_params.get('calendar')
@@ -409,6 +440,51 @@ class ScheduleEventViewSet(viewsets.ModelViewSet):
})
return Response(occurrences)
+
+ @action(detail=False, methods=['get'], url_path='my-events')
+ def my_events(self, request):
+ """Получить события текущего пользователя (где он участник).
+
+ Query params:
+ - start: начало диапазона (ISO 8601, необязательно)
+ - end: конец диапазона (ISO 8601, необязательно)
+
+ Возвращает только события где пользователь является участником (EventRelation).
+ """
+ from django.contrib.auth import get_user_model
+ User = get_user_model()
+
+ user = request.user
+ ct = ContentType.objects.get_for_model(User)
+
+ # ID событий где пользователь участник
+ participant_event_ids = EventRelation.objects.filter(
+ content_type=ct,
+ object_id=user.id
+ ).values_list('event_id', flat=True)
+
+ # Фильтруем события
+ qs = self.queryset.filter(id__in=participant_event_ids)
+
+ # Фильтр по диапазону дат (если указан)
+ start_str = request.query_params.get('start')
+ end_str = request.query_params.get('end')
+
+ if start_str and end_str:
+ start_dt = parse_datetime(start_str)
+ end_dt = parse_datetime(end_str)
+
+ if start_dt and end_dt:
+ qs = qs.filter(
+ start__lte=end_dt,
+ end__gte=start_dt
+ )
+
+ qs = qs.order_by('start')
+
+ # Используем EventListSerializer для детальной информации
+ serializer = EventListSerializer(qs, many=True)
+ return Response(serializer.data)
class ScheduleRuleViewSet(viewsets.ModelViewSet):
diff --git a/frontend/src/components/EventModal.tsx b/frontend/src/components/EventModal.tsx
index 9b08c74e..60b1fe54 100644
--- a/frontend/src/components/EventModal.tsx
+++ b/frontend/src/components/EventModal.tsx
@@ -334,6 +334,12 @@ export function EventModal({
+ {editingEvent.isRecurring && (
+
+ 💡 Начало и конец определяют время первого события и его продолжительность. Эти параметры будут применены ко всем повторениям.
+
+ )}
+