PhoenixKitCalendar.Web.CalendarLive (PhoenixKitCalendar v0.1.0)

Copy Markdown View Source

The calendar admin page.

Interaction model (quorum-designed, 2026-07-09)

What you see is a SET of calendar layers. "Me" is the default set {me}; viewing one person is a set of size 1; "Everyone" is a select-all-permitted shortcut, not a mode. Holders of calendar.view_others get a toolbar "Calendars · N" button opening a Google-Calendar-style checklist panel: search, Me/Everyone shortcuts, and a checkbox list where every person carries a deterministic palette color — events on the grid are tinted with their owner's color instead of name prefixes. Clicking a NAME solos that person; the checkbox toggles membership. People without calendar access stay selectable (their history must remain reviewable) and are badged; people with no events in the visible range get an "empty" badge.

State

The selection lives in the URL (?people=uuid1,uuid2 or ?people=all; absent = own calendar), so views are shareable and the back button works. Every mount/patch SANITIZES the list — unknown ids are dropped, and viewers without calendar.view_others are forced to {me} regardless of the URL (authorization is enforced on the query in PhoenixKitCalendar.Events, not just in the template).

Authorization

Page access needs the base calendar key (admin on_mount chain). Everything finer goes through the Events context, which re-checks the caller's scope against each event's persisted owner. The modal authorizes PER EVENT at open time (can_edit_event?), so with calendar.edit_others you edit anyone's event inline from any view; without it you get read-only details. Creating needs exactly one selected calendar you may edit.

Time semantics

Timed events are stored in UTC and shown/entered in the viewer's timezone (core's offset-hours model: user_timezone column → site "time_zone" setting → UTC). When the target calendar's owner sits in a different offset, the modal says so and offers a checkbox to switch the entry frame to THEIR timezone — toggling re-renders the same instant, never reinterprets the digits. All-day events use real dates (no timezone); the form's end date is INCLUSIVE ("last day") and shifted to the exclusive storage form at this boundary.