diff --git a/backend/app/routers/events.py b/backend/app/routers/events.py
index e743f47..5a9525e 100644
--- a/backend/app/routers/events.py
+++ b/backend/app/routers/events.py
@@ -35,6 +35,7 @@ def _event_to_dict(
display_calendar_name: str | None = None,
display_calendar_color: str | None = None,
can_modify: bool = False,
+ has_active_invitees: bool = False,
) -> dict:
"""Serialize a CalendarEvent ORM object to a response dict including calendar info."""
# For invited events: use display calendar if set, otherwise fallback to "Invited"/gray
@@ -74,6 +75,7 @@ def _event_to_dict(
"invitation_id": invitation_id,
"display_calendar_id": display_calendar_id,
"can_modify": can_modify,
+ "has_active_invitees": has_active_invitees,
}
return d
@@ -243,6 +245,21 @@ async def get_events(
all_event_ids = [e.id for e in events]
override_map = await get_invitation_overrides_for_user(db, current_user.id, all_event_ids)
+ # Batch-fetch event IDs that have accepted/tentative invitees (for owner's shared icon)
+ active_invitee_set: set[int] = set()
+ if all_event_ids:
+ active_inv_result = await db.execute(
+ select(EventInvitation.event_id).where(
+ EventInvitation.event_id.in_(all_event_ids),
+ EventInvitation.status.in_(["accepted", "tentative"]),
+ ).distinct()
+ )
+ active_invitee_set = {r[0] for r in active_inv_result.all()}
+ # Also mark parent events: if a parent has active invitees, all its children should show the icon
+ parent_ids = {e.parent_event_id for e in events if e.parent_event_id and e.parent_event_id in active_invitee_set}
+ if parent_ids:
+ active_invitee_set.update(e.id for e in events if e.parent_event_id in active_invitee_set)
+
response: List[dict] = []
for e in events:
# Determine if this event is from an invitation
@@ -272,6 +289,7 @@ async def get_events(
display_calendar_name=disp_cal_name,
display_calendar_color=disp_cal_color,
can_modify=inv_can_modify,
+ has_active_invitees=(parent_id in active_invitee_set or e.id in active_invitee_set),
))
# Fetch the user's Birthdays system calendar; only generate virtual events if visible
diff --git a/frontend/src/components/calendar/CalendarPage.tsx b/frontend/src/components/calendar/CalendarPage.tsx
index 3cec58e..dc58d13 100644
--- a/frontend/src/components/calendar/CalendarPage.tsx
+++ b/frontend/src/components/calendar/CalendarPage.tsx
@@ -389,6 +389,7 @@ export default function CalendarPage() {
calendarColor: event.calendar_color || 'hsl(var(--accent-color))',
is_invited: event.is_invited,
can_modify: event.can_modify,
+ has_active_invitees: event.has_active_invitees,
},
}));
@@ -531,6 +532,7 @@ export default function CalendarPage() {
const isAllDay = arg.event.allDay;
const isRecurring = arg.event.extendedProps.is_recurring || arg.event.extendedProps.parent_event_id;
const isInvited = arg.event.extendedProps.is_invited;
+ const hasActiveInvitees = arg.event.extendedProps.has_active_invitees;
const calColor = arg.event.extendedProps.calendarColor as string;
// Sync --event-color on the parent FC element so CSS rules (background, hover)
@@ -544,7 +546,7 @@ export default function CalendarPage() {
const icons = (
<>
- {isInvited && }
+ {(isInvited || hasActiveInvitees) && }
{isRecurring && }
>
);
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index 357a297..0bbc8d1 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -117,6 +117,7 @@ export interface CalendarEvent {
invitation_id?: number | null;
display_calendar_id?: number | null;
can_modify?: boolean;
+ has_active_invitees?: boolean;
created_at: string;
updated_at: string;
}