5 Commits

Author SHA1 Message Date
925c9caf91 Fix QA and pentest findings for event invitations
C-01: Use func.count() for invitation cap instead of loading all rows
C-02: Remove unused display_calendar_id from EventInvitationResponse
F-01: Add field allowlist for invited editors (blocks is_starred,
      recurrence_rule, calendar_id mutations)
W-02: Memoize existingInviteeIds Set in EventDetailPanel
W-03: Block per-occurrence overrides on declined/pending invitations
S-01: Make can_modify non-optional in EventInvitation TypeScript type

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 01:28:01 +08:00
f35798c757 Add per-invitee can_modify toggle for event edit access
Allows event owners to grant individual invitees edit permission via a
toggle in the invitee list. Invited editors can modify event details
(title, description, time, location) but cannot change calendars, manage
invitees, delete events, or bulk-edit recurring series (scope restricted
to "this" only). The can_modify flag resets on decline to prevent silent
re-grant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 00:59:36 +08:00
a68ec0e23e Add display calendar support: model, router, service, types, visibility filter
Previously unstaged changes required for the display calendar feature:
- EventInvitation model: display_calendar_id column
- Event invitations router: display-calendar PUT endpoint
- Event invitation service: display calendar update logic
- CalendarPage: respect display_calendar_id in visibility filter
- Types: display_calendar_id on CalendarEvent interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 19:03:22 +08:00
df857a5719 Fix QA findings: flush before notify, dedup RSVP, sa_false, validation
- C-02: flush invitations before creating notifications so invitation_id
  is available in notification data; eliminates extra pending fetch
- C-03: skip RSVP notification when status hasn't changed
- C-01: add defensive comments on update/delete endpoints
- W-01: add ge=1, le=2147483647 per-element validation on user_ids
- W-04: deduplicate invited_event_ids query via get_invited_event_ids()
- W-06: replace Python False with sa_false() in or_() clauses
- Frontend: extract resolveInvitationId helper, prefer data.invitation_id

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:01:15 +08:00
8652c9f2ce Implement event invitation feature (invite, RSVP, per-occurrence override, leave)
Full-stack implementation of event invitations allowing users to invite connected
contacts to calendar events. Invitees can respond Going/Tentative/Declined, with
per-occurrence overrides for recurring series. Invited events appear on the invitee's
calendar with a Users icon indicator. LeaveEventDialog replaces delete for invited events.

Backend: Migration 054 (2 tables + notification types), EventInvitation model with
lazy="raise", service layer, dual-router (events + event-invitations), cascade on
disconnect, events/dashboard queries extended with OR for invited events.

Frontend: Types, useEventInvitations hook, InviteeSection (view list + RSVP buttons +
invite search), LeaveEventDialog, event invite toast with 3 response buttons, calendar
eventContent render with Users icon for invited events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 02:47:27 +08:00