Skip to main content

Screen: Calendar

web/availability.html — nav label "Availability", on-screen title "Calendar". Combines team free/busy with an internal Google Calendar event cache, so SABLE can show both open working time and meeting context, deliberately at different levels of detail for different viewers. Slot, overlap and link rules live in web/availability-model.js so they are tested directly.

Choosing people​

Chips at the top of Times that work for all hold the people being compared; × removes one. + Add person is a type-ahead over internal staff, Project team adds the lead and members of a current project (read from /api/portfolio, so it follows View as), and the rail list toggles the same selection. The default is the signed-in viewer; the selection is kept per viewer in localStorage (sable_state:availability-people:<person id>), swept on sign-out.

Everyone row and timeline​

Above the picked people's rows, an Everyone row is shaded by how many of them are busy: free for all is lightest, all busy darkest, graded in between. Each stretch is focusable and its tooltip names who is busy ("2 of 3 busy: Lindsey, Shalonda"). Calendars that cannot vouch for free time are named as "not checked" rather than counted as free.

Times that work for all​

Shared openings for the meeting length (30/45/60/90 min), soonest first, never in the past. Working hours are 9am–6pm America/New_York and are computed there; people records carry no time zone, so ET is the stated rule. Weekends are skipped unless Include weekends is ticked. Times are shown in the viewer's browser time zone, with the ET time beside them when that differs.

Each time has Create in Google Calendar ↗: a plain link to Google's prefilled event form (calendar.google.com/calendar/render?action=TEMPLATE, UTC dates, picked people in add). SABLE writes nothing; the organiser reviews and saves the event in Google.

Only readable calendars count. Not active (stale), Error and Unknown calendars are excluded and named under the list. So is a readable calendar where we can't see any meetings: zero busy blocks in the whole synced window and zero events in calendar_events for that calendar, which usually means the free/busy read sees nothing rather than that the person is free. Include anyway counts those people as free.

Accepted, unanswered and tentative invitations are busy; declined invitations are free. That matches Google free/busy and was checked against Google on 20–21 Oct 2026.

Calendar intelligence​

Admins, leads, and consultants can see upcoming internal event details, project-linked meetings, and unmatched meetings that may need routing. Admins and leads can correct a meeting's project match from this panel — see Calendar for how those manual overrides persist through future syncs instead of being wiped out.

Busy blocks only​

The scheduling timeline uses opaque busy blocks — it shows when someone is unavailable, never what they're doing. This is the same privacy split documented on the Availability and Calendar API page.

Approval path for writes​

Creating, updating, or cancelling a Google Calendar event through SABLE is proposed through approvals and only executes when the calendar-write switch (CALENDAR_ACT_WRITES_ENABLED) is armed. The Create in Google Calendar link is not that path: it only opens Google's own form.

Where the code lives​

  • web/availability.html, web/availability-model.js
  • web/api/availability.js, _availability.js
  • web/api/calendar.js, _calendar.js
  • Tests: web/tests/availability-model.test.mjs, web/tests/availability.test.mjs