Skip to content

Scheduled maintenance: the calendar as its own page under a Calendar section, external .ics calendars drawn on it, and Calendar Settings (#5084) - #5119

Merged
simlarsen merged 22 commits into
masterfrom
claude/maintenance-calendar-section
Oct 20, 2026
Merged

simlarsen merged 22 commits into
masterfrom
claude/maintenance-calendar-section

Conversation

@simlarsen

@simlarsen simlarsen commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

The problem

The maintainer asked (task doc, block 27): "Can you please have calendar view as separate page in side menu with its own side menu section called 'Calendar'. We can then also implement some of these ideas: #5084, where we can overlay external feeds (You can create a new page in Settings called Calendar Settings so all of these options are advanced and can be hidden)".

Until now the scheduled maintenance calendar was the Calendar half of a List | Calendar switch on All Events (#4942), and a project could publish .ics feeds but not read anyone else's (#5084: public holidays, a change freeze kept in Exchange, another project's maintenance).

What changed

The calendar is a page of its own, under a Calendar section

  • Scheduled Maintenance's side menu has a new Calendar section, right after Overview and open like it, holding Maintenance Calendar (/dashboard/:projectId/scheduled-maintenance-events/calendar). It is the calendar Scheduled maintenance: a calendar view, the team's calendar link, and a status page's maintenance calendar (iCal) #4942 built - month, week, day, filters, the + to schedule on a day, Subscribe to calendar - with its own title, breadcrumb and side-menu highlight.
  • All Events is the list again: no switch, and Subscribe to calendar moves to the calendar (where the dialog already lived under the grid). The calendar's filter chips live in its address; saved views stay the list's.
  • The Dashboard's page search finds Maintenance Calendar and Calendar Settings (also by "subscribe to calendar", "external calendars", "public holidays", "change freeze").
  • Old links: the switch never put the view in the address, so /scheduled-maintenance-events still opens All Events. A reader whose browser had left the old switch on Calendar is taken to Maintenance Calendar once (replace, so Back is not trapped); the stored choice is then forgotten and All Events is the list from then on.

External calendars (#5084), set up on Calendar Settings

  • Calendar Settings is a new page in Scheduled Maintenance's Settings section (folded by default - "advanced and can be hidden"), before Numbering, which stays every product's last Settings entry. I put it in the product's own Settings rather than Project Settings: everything about maintenance lives in its side menu (the docs say so), and that is the Settings section the maintainer's words point at.
  • Adding one asks for a Name and its Calendar Link (https://, http:// or webcal://). Color (one the project's other calendars do not use) and Refresh (every hour; 15/30 min, 6 h, a day) are folded under More fields. It is read at once and its row says Working with the events kept, Waiting for the first read, Off, or Not working with the reason and what to do. Row ⋯: Read now, Change link, Turn off / Turn on; Edit changes its name, colour and refresh; Delete. Each action reloads the list, so the row says what happened.
  • On the calendar, their events are drawn beside the project's, lighter (a wash with stripes and a dashed edge in the calendar's colour), named "(Calendar name)", and open nothing. Under the grid, External calendars has a toggle per enabled calendar (hidden ones are never asked for; the reader's choice is kept per project in their browser), a mark on one that could not be read lately (its last good events stay), and Calendar settings. The calendar's ⋯ also has Add External Calendar, which opens Calendar Settings with its Add dialog open. A project with none sees nothing new. A period holding more than 500 overlay events says so.
  • Blackout periods (Scheduling checks and approvals for scheduled maintenance (blackout periods, dependencies, approvals) #4884): not on master (the issue is open), so there is no "use as a source of blackout periods" option yet.

How the calendars are read (server)

  • Two new models: ScheduledMaintenanceExternalCalendar (CRUD at /scheduled-maintenance-external-calendar, in the API reference and so Terraform) and ScheduledMaintenanceExternalCalendarEvent (read-only to everyone; written by the Workers alone). Migration 1805120000000-AddScheduledMaintenanceExternalCalendars, generated against a fully migrated database, no drift after it.
  • The link can be a secret, so it is encrypted and write-only (read: []): never returned by any API or page; only its host is shown. Change link replaces it and forgets the old link's events.
  • A Workers job (ScheduledMaintenance:RefreshExternalCalendars, every minute) reads each enabled calendar whose next read is due, one at a time, batch after batch for at most 45 seconds per run; a calendar is claimed with a compare-and-set first, so two Workers never read one calendar. Read now (POST .../:id/read-now, for whoever may update it) is refused within 30 s of the last read or while it is off.
  • Fetch: through OneUptime's egress guard (DataSourceEgressGuard, like the data sources and threat-intel feeds): the host is resolved and checked before a socket opens and the socket is pinned to the checked addresses; redirects are re-checked (at most 3); 5 MB cap (decompressed), 20 s for the whole read. Never loopback or a metadata address; OneUptime Cloud also refuses private networks.
  • Parse: ical.js (Mozilla's parser) in a worker thread with a heap cap, a recurrence step budget and a 10 s limit after which it is terminated (an RRULE written never to match makes ical.js loop forever - measured). Before it, a one-pass linear pre-scan drops every property never read. RRULE, RDATE, EXDATE, RECURRENCE-ID overrides and cancellations, VTIMEZONE (as Exchange writes it), IANA and old Mozilla TZIDs, floating times (X-WR-TIMEZONE) and all-day dates are handled. Kept: 90 days back to a year ahead, up to 2,000 events (nearest today), the same window the team's link serves. An unchanged calendar is not worked out or written again (content digest).
  • Overlay events are never part of the project's own calendar links (the team's link and a status page's), so two projects showing each other's link see each event once.
  • Up to 20 external calendars per project. Same permissions as scheduled maintenance events (create/edit/delete events -> manage calendars; read events -> see them).

Small things around it

  • The filter bar's end slot (filterBarEnd in useResourceOwners) only ever held the old switch, so it goes; comments that still named the switch are updated (ViewSwitch, RememberedView, CalendarToolbar, the LLM Usage page).
  • OnCallCalendarFeedWiring found the rotated-out link's two sentences by "old link" + "calendar"; Change link's dialog names both, so it now looks for "empty calendar", as its own comment says.
  • Dashboard Offline UI group 6's comment says what the suite's 22 new specs add.

What changes for existing customers

  • The calendar moves from the All Events switch to Scheduled Maintenance -> Calendar -> Maintenance Calendar; a browser left on the calendar is taken there once. All Events' ⋯ no longer has Subscribe to calendar (it is under the calendar). Existing calendar links keep working.
  • Nothing else changes until someone adds an external calendar. The Workers job does nothing for projects without one.

Left out

Tests (what each proves)

Common:

  • ExternalCalendarExpansion.test.ts - the parser on Exchange, Google and OneUptime calendars: weekly and yearly RRULEs, an ended rule, RDATE, EXDATE, RECURRENCE-ID moves and cancellations, VTIMEZONE across DST, IANA/Mozilla/floating times, all-day dates, private events' titles, the window, the 2,000 cap, step budgets, malformed input, and a rule that never matches stopped at the time limit off the main thread.
  • ExternalCalendarText.test.ts - the linear pre-scan, with 400,000-character worst cases under 2 s.
  • ExternalCalendarFetch.test.ts - against a real local server: size cap (plain and gzip), deadline, a server that drips bytes, redirects re-checked by the guard, SSRF refusal, statuses mapped to reasons that never name the link.
  • ExternalCalendarRefresher.test.ts - the claim, what a read writes, a failed read keeping the last good events, the unchanged calendar, a link replaced mid-read, Read now's refusals, and the job: batches until a short one, each calendar tried once a run, the 45 s budget.
  • ScheduledMaintenanceExternalCalendarService/EventService/API/Models tests - write-only encrypted link, normalisation, colour picking, 20-per-project limit, resets on Change link, permissions, Read now routing.
  • ScheduledMaintenanceExternalCalendarPostgres.test.ts (opt-in, wired into Postgres Schema Drift) - bulk replace, the claim race and the cascade on the migrated tables.
  • AddScheduledMaintenanceExternalCalendarsMigration.test.ts - two tables and nothing else, indexes, FKs, down().
  • ExternalCalendarApiSpec.test.ts - from the real OpenAPI generator (what Terraform is built from): the six routes, the link taken on create and update but never read back, nothing the Workers write taken, the events left out of the reference.
  • OwnCalendarLinksLeaveOverlaysOut.test.ts - the team's and status pages' feeds never read external events.
  • Dashboard: side menu (Calendar section open, Calendar Settings in folded Settings), page search covering both pages, overlays (hidden per project, all-day dates in every zone, timed events in the reader's), the calendar drawing and toggling them, the status words, the Calendar Settings page's contract (form fold, write-only link, every row action reloads the list and a refused one does not), every new sentence in every language.

App: ScheduledMaintenanceCalendarWiring (routes, page, the switch gone, the one-time redirect), ScheduledMaintenanceCalendarDarkMode, MaintenanceCalendarDocsFacts / ScheduledMaintenanceDocsFacts (docs held to the product: menus, fields, limits, permissions, egress guard).

Offline browser suite (test-maintenance-calendar-ui, 64 tests, 22 new): the Calendar section and page, All Events alone, the one-time move, overlays drawn/toggled/hidden/failing/truncated, Calendar Settings (status, add with folded fields, refused link, Change link, Turn off/on, refused Read now, a viewer's locked Add External Calendar), at 1280 and 390, light and dark, German and Japanese. It adds about 45 s locally (about 2 min 10 s with its bundle) to Dashboard Offline UI group 6, which lost Custom Roles (about 3 min) to group 7 today.

Run locally

  • Offline browser suite (test-maintenance-calendar-ui): 64/64 green, about 2 min 10 s with its bundle.
  • The whole Common and App jest suites after merging master. Every failure left is environmental or master's own, none in a file this branch touches: the Valkey/Redis suites (no Valkey here), the suites that need CI's config.env (SsoInCommunityEdition "Invalid Plan", the WebAuthn suites' "Invalid URL" without HOST, McpAuthorizePage's /api path), and two from master's own recent merges - TestSendRoutesAskTheRule (ExternalMcpServerAPI /test is not listed) and DashboardStorageArrayListComponents (the Incident template's extra MTTA query).
  • After the last merge of master: this change's Common and App suites, the suites that read the files it changes (search index, saved views, calendar feed wording, docs facts and translations), npx jest Guard and npx jest Locale in Common, npx jest Locale and the root-level tests in App, DashboardLocalesGuard, i18n:check, i18n:validate, docs:check-anchors, repo-root Tests/Ops, and this change's suites once more with ee/ hidden.
  • Postgres: every migration on a throwaway Postgres, CheckSchemaDrift.sh (no drift), then the opt-in external calendar suite against it.
  • npm run compile in Common, App, the Dashboard and E2E, compile-tests in Common, the tsc --listFiles check (nothing from another package), prettier and eslint on every changed file, and the ReDoS checker (no pattern that is not linear).
  • Not run: a full-stack e2e stack (see Left out).

UI review

Looked at, on the real pages in the offline fixture (the product's theme, Tailwind and Inter) at 1280, 1024, 768 and 390 px, light and dark, in English, German, Japanese, French and Russian: the Maintenance Calendar (month, week, day; a busy month; calendars failing, hidden, never read; a viewer), the external calendars' toggles, the calendar's ⋯ menu (owner and viewer), Calendar Settings (rows, empty, waiting, failing, off; Read now refused; Turn off), the Add dialog with More fields open, Edit, Change link, the row menu, All Events, and the docs page's External calendars section (en, de, ja at 1280 and 390).

Changed:

  1. The project's own events come first. In a busy month a calendar from outside the project (a team's shifts, a holiday calendar, a change freeze across the week) took a cell's rows and folded the project's own maintenance into "+N more": react-big-calendar fills a cell by length and start time, and the "own events first" order the calendar handed it was never used. The shared Calendar now lays a page's own events out first and the overlays in the rows they leave (getCalendarLocalizer: a sort that puts overlays after, and a daySpan that never counts an overlay among the multi-day blocks laid out first). Pages without overlays (the on-call schedules) keep the calendar's one localizer.
  2. A keyboard no longer stops on an overlay block in the week and day views. react-big-calendar draws those blocks as buttons in the tab order; an overlay's opened nothing and had its focus ring removed, so a Tab press went nowhere visible. CalendarEventWrapper takes overlays out of the tab order; a screen reader still reads them where they are.
  3. The calendar card's header is All Events'. At tablet widths the card's default header broke "Maintenance Calendar" into a column a word wide beside "Create Scheduled Maintenance Event", and on a laptop wrapped the description over two lines. It now uses the toolbar header the list uses: title and buttons on one line, the description under both. On a phone, Create and the ⋯ are one group as on All Events (the button wraps its words beside the ⋯, instead of taking the line and leaving the ⋯ a line of its own).
  4. Add External Calendar, in the calendar's ⋯, opens the Add dialog on Calendar Settings (?open=add-external-calendar, read once, for whoever may add one, then taken out of the address). It used to open the list, where the same button had to be found again. The docs' add step says so, in all 17 languages.
  5. The toggles. Each is named by its calendar and says shown or hidden with aria-pressed; its name used to flip between "Hide X on the calendar" and "Show X on the calendar" while also pressed and unpressed ("Hide Public holidays, pressed"). What a click does is now the pointer's hint. A calendar not read lately has its mark inside its own chip (it sat between two chips, belonging to neither), and the reason is the toggle's description. A hidden chip's text is gray-500 (gray-400 was 2.5:1).
  6. Overlay blocks are flat: no lift shadow, no hover change (the stylesheet said hovering changes nothing, but the shared hover shadow applied), and no second edge inside the dashed one in dark mode.
  7. Copy. Calendar Settings' description is one sentence: "Show the events of other calendars, such as public holidays or a change freeze, on the Maintenance Calendar." It ran to two lines, three in the empty state. The breadcrumb names the page Maintenance Calendar, as its side menu entry and card do. Two keys nothing reads any more are gone from all 17 locales: the old switch's "Show the events as", and the toggles' short warning.

Left alone (shared, not this change): "Name is required." shows once More fields is opened before the name is typed (BasicForm marks a field touched on blur); the side menu's 224px column truncates "Maintenance Calendar" between 768 and 1023 px; a required dropdown (Refresh) keeps its clear button.

Tests added or changed in the review:

  • Common UI/Components/Calendar/CalendarOverlayEvents.test.tsx (new): own events sort before overlays, an overlay never counts as multi-day, the localizer copy keeps the calendar's settings; month cells laid out own-first (same day, a long own event against a long holiday that began a day earlier, a change freeze across the week); week and day blocks of overlays out of the tab order, month blocks untouched.
  • ScheduledMaintenanceCalendarExternalCalendars.test.tsx: toggles named by their calendar with aria-pressed and hints, the mark inside its toggle with the reason as its description, own events before overlays, the toolbar header.
  • ScheduledMaintenanceCalendarSettingsPage.test.tsx: the Add dialog opened by the address (not otherwise, never for a viewer), the ask removed and the rest of the address kept; the new description.
  • ExternalCalendarAddLink.test.ts (new): the ask and what does not count as one.
  • MaintenanceCalendarInLocale.test.tsx: the toggles' names, hints and description in German, Russian, Japanese and Persian.
  • App ScheduledMaintenanceCalendarWiring (the trail's name, the ⋯ item's wiring to the dialog) and MaintenanceCalendarDocsFacts (the reworded add step and the behaviour behind it).
  • Offline suite (test-maintenance-calendar-ui, 69 tests, 5 new, about 2 min 20 s with its bundle, about 10 s more): the header at 1280, 1024 and 768; a busy month keeps the project's events visible, a long one included; a keyboard in the week skips overlays; Add External Calendar opens the dialog, once; on a phone Create and the ⋯ share a line as on All Events. The toggle tests follow the new names.
  • Mutation-checked: each change reverted on its own fails its tests (14 mutations, 14 caught).

Run after merging master (344bfa3, the Code redesign; its locale conflict resolved with i18n:extract): the changed suites and every suite reading the changed files (the Calendar suites, the on-call schedule calendar suites, breadcrumbs, the readers of ScheduledMaintenanceTable, App's dark mode suites), npx jest Guard (Common 154 suites, App 41), npx jest Locale in Common and App, the general docs suites with the scheduled maintenance and calendar docs suites and DocsCriteriaConditions, App's root tests, RelationSelectColumnsWiring, Tests/Ops/E2ESuitesRunInCi, the changed suites with ee/ hidden, i18n:check, i18n:extract (nothing new), i18n:validate, docs:check-anchors, npm run compile in Common, App, the Dashboard and E2E, compile-tests, the tsc --listFiles check, prettier and eslint on every changed file, the ReDoS checker (no new pattern), and the whole offline suite (69/69). Not run: a full-stack e2e stack.

🤖 Generated with Claude Code

valeriamustafaeva and others added 17 commits October 20, 2026 17:35
…links, worked out off the main thread

Calendars from outside the project (issue #5084) - public holidays, a change
freeze, another project's calendar link - are read by the Workers through the
egress guard, cut down to the properties read, and expanded by ical.js
(RRULE, RDATE, EXDATE, RECURRENCE-ID, VTIMEZONE) in a worker thread that is
terminated past its time limit: an impossible RRULE never returns from
ical.js. Their events replace the old ones in one transaction. The link is
encrypted and write-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…red and kept out of the project's own links

The parser on Exchange, Google and OneUptime calendars (RRULE, EXDATE,
RECURRENCE-ID, VTIMEZONE across DST, all-day, floating and IANA times,
limits, an impossible rule stopped off the main thread), the linear pre-scan
with 400,000-character worst cases, the fetch against a real server (size,
deadline, gzip, redirects re-checked by the egress guard), the services
through the member write pipeline, the refresher's claim and reads, Read now,
both tables, and the team's link holding its own events alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and their SQL against Postgres

Generated against a fully migrated database (no drift after it). The opt-in
Postgres suite runs the bulk replace, the read claim and the cascade on a
clone of the migrated tables, wired into the Postgres Schema Drift workflow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…endar section, external calendars drawn on it, and Calendar Settings

Scheduled Maintenance gets a Calendar section in its side menu, right after
Overview, with the Maintenance Calendar: the calendar that used to be the
Calendar half of a List | Calendar switch on All Events, which is a plain
list again (a reader who had left it on Calendar is taken to the new page
once). Calendar Settings, in the folded Settings section, holds the
calendars from outside the project: name and link, colour and refresh under
More fields, status, Read now, Change link, Turn on/off. Their events are
drawn on the calendar as striped, dashed blocks in their colour, each
calendar a toggle under the grid that this reader's browser remembers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lendars, Calendar Settings and the side menu

The side menu's Calendar section and Calendar Settings, the everyday-section
guard, the overlay logic (hidden calendars per project, all-day dates in
every zone, timed events in the reader's), the calendar drawing and toggling
external calendars for real, the status words, and the Calendar Settings
page's contract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lendars and Calendar Settings in all 17 languages

64 new strings, worded in each of the 16 other languages, and four words the
new pages show that most languages lacked (Busy, Not yet, the kept-events
count). The two link descriptions are translation keys so the extractor
reads them despite the link scheme in them, and three sentences are plainer
before they are translated (redirects, the name field, color).

MaintenanceCalendarInLocale holds every sentence to its page and to every
language, and renders the toggles in German, Russian, Japanese and Persian.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…endars, in all 17 languages

Maintenance Calendar: the calendar page under the side menu's Calendar
section, a note for the readers whose All Events was left on the old
switch, and a section on external calendars - adding one on Calendar
Settings, what it keeps, the actions, the roles, the addresses OneUptime
will not read, and why one is not working. The overview names the new
Calendar section (open, like Overview) and Calendar Settings, and the
settings page points at it. Every language line for line, its labels as
its Dashboard draws them, anchors localized.

Calendar Settings now sits right before Numbering, which stays every
product's last Settings entry (NumberingPagesWiring). The overlay limit
reads the shared EXTERNAL_CALENDAR_VIEW_LIMIT.

The docs facts hold the pages to the product: the side menu sections, the
one-time redirect's key, the form's fields and fold, the limits, the
permission lists equal to scheduled maintenance's, the egress guard, and
the team and status page calendars never reading external calendars.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…endar Settings' rows

A row action's own completion only clears its spinner (RowActions), so a
calendar turned off went on saying Working until the page was reloaded. Every
action that changes a row now reloads the table itself, with a count so two
reloads in one millisecond stay two; a refused action reloads nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ars and Calendar Settings in a real browser

The offline suite drives the Maintenance Calendar under the side menu's
Calendar section, All Events as the list alone, the one-time move of a reader
the old switch left on Calendar, the external calendars drawn beside the
events (lighter, dashed, opening nothing) with a toggle each, and Calendar
Settings: status, adding one (Color and Refresh folded), a refused link,
Change link, Turn off / Turn on, a refused Read now, a viewer's locked Add
External Calendar - at 1280 and 390, light and dark, German and Japanese.

The List | Calendar switch's slot at the end of the filter bar (filterBarEnd)
had no other user and goes with it, as do the comments that still named the
switch. The schema-drift workflow's heartbeat comment is back above its step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…calendar-section

The locale files merged both sides through npm run i18n:extract. The external
calendars' migration moves to 1805120000000, past master's newest
(1805000000000-AddExternalMcpServers, which took the same timestamp).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a backlog, batch after batch for 45 seconds

One batch of 20 a minute read at most 1,200 calendars an hour across every
project, so a backlog larger than that only grew. A run now goes on reading
due calendars, oldest first, until none is left or 45 seconds are up; the
read under way when the time is up is finished. Reads stay one at a time, so
a run's memory never grows, each calendar is tried once a run (a claim
another reader won is not retried), and each claim's lease runs from when it
is made.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in compile-tests

compile-tests (tsconfig.tests.rest.json) refused four lines: a TypeORM index
read through a propertyName it does not have (a property's @Index() is kept as
columns: [its name]), a calendar built with nextReadAt: undefined under
exactOptionalPropertyTypes, and two spies annotated with @types/jest's
SpiedFunction in a suite whose jest comes from @jest/globals (jest-mock's
SpyInstance now). The migration test reads a class name's 13-digit timestamp
without a pattern that could take polynomial time.

Dashboard Offline UI group 6's comment says what Maintenance Calendar's 22
new specs add to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…endar Settings

Suites that read the files this branch changes, run after merging master:

- PageSearchIndexCoversMenus / CoversRoutes: the side menu's new Calendar
  section (Maintenance Calendar) and Calendar Settings are in the page search
  index, with the words people type for them (subscribe to calendar, ical
  feed; external calendars, public holidays, change freeze).
- CustomFieldSavedViewTables: the calendar page passed a saveFilterProps
  table id it never saved views under (saved views are the list's
  ModelTable's); it passes none, and its filter chips still live in the page's
  address.
- OnCallCalendarFeedWiring: the rotated-out link's two sentences are found by
  what tells them apart, as the test's comment says ("empty calendar"),
  not by naming an old link and a calendar - Change link's dialog for an
  external calendar names both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… Prettier and ESLint write them

Formatting only, plus two lint findings in tests: an object cast typed as
Record<string, unknown>, and the docs facts' side-menu section pattern named
and written with counted spaces (no-regex-spaces).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…calendar-section

The migration index keeps master's AddInvestigationRuleWait (1805030000000)
and its indexes (1805040000000) before the external calendars'
(1805120000000), still the newest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…calendar-section

The locale files merged both sides through npm run i18n:extract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and Terraform describe them

Built from the real OpenAPI generator: the calendar's six routes, a link taken
on create and on update (Change link) but never in what is read back, nothing
the Workers write taken on create or update, how the last read went read
back without the content digest, and the events left out of the reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@simlarsen

simlarsen commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

valeriamustafaeva and others added 5 commits October 20, 2026 21:50
…lendars', its header as All Events', Add External Calendar opening its dialog

UI review of the Maintenance Calendar and Calendar Settings (#5084):

- A busy calendar from outside the project folded the page's own
  maintenance into "+N more": react-big-calendar fills a month cell's rows
  by length and start time. The shared Calendar now lays the page's own
  events out first and the overlays in the rows they leave
  (getCalendarLocalizer), and a keyboard no longer stops on an overlay's
  block in the week and day views (it opens nothing).
- The calendar card uses the toolbar header All Events uses: the title was
  broken into a column a word wide at tablet widths, and the description
  wrapped on a laptop.
- Add External Calendar in the calendar's menu opens Calendar Settings with
  its Add dialog open (?open=add-external-calendar, taken back out of the
  address), instead of a list where the same button had to be found again.
- Each external calendar's toggle is named by its calendar and says whether
  it is shown with aria-pressed; a calendar not read lately is marked inside
  its own toggle, the reason its description.
- Calendar Settings' description is one short sentence; the breadcrumb
  names the page Maintenance Calendar, as its menu entry and card do.
- Docs: the add step names the menu path first, in all 17 languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a line on a phone, as on All Events

The calendar card's header drew Create Scheduled Maintenance Event across
the whole width of a phone and pushed the menu onto a line of its own; the
list holds the two as one group, the button wrapping its words beside the
menu. The calendar now does the same.

The offline suite also checks that a busy external calendar never folds the
project's own events (a long one included) into +N more, and the overlay
tests cover a change freeze across the week.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nalPropertyTypes, and the new tests as Prettier writes them

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…calendar-section-ui-review

# Conflicts:
#	packages/App/FeatureSet/Dashboard/src/Locales/da.json
#	packages/App/FeatureSet/Dashboard/src/Locales/de.json
#	packages/App/FeatureSet/Dashboard/src/Locales/en.json
#	packages/App/FeatureSet/Dashboard/src/Locales/es.json
#	packages/App/FeatureSet/Dashboard/src/Locales/fa.json
#	packages/App/FeatureSet/Dashboard/src/Locales/fr.json
#	packages/App/FeatureSet/Dashboard/src/Locales/hi.json
#	packages/App/FeatureSet/Dashboard/src/Locales/it.json
#	packages/App/FeatureSet/Dashboard/src/Locales/ja.json
#	packages/App/FeatureSet/Dashboard/src/Locales/ko.json
#	packages/App/FeatureSet/Dashboard/src/Locales/nl.json
#	packages/App/FeatureSet/Dashboard/src/Locales/no.json
#	packages/App/FeatureSet/Dashboard/src/Locales/pt.json
#	packages/App/FeatureSet/Dashboard/src/Locales/ru.json
#	packages/App/FeatureSet/Dashboard/src/Locales/sv.json
#	packages/App/FeatureSet/Dashboard/src/Locales/zh-CN.json
#	packages/App/FeatureSet/Dashboard/src/Locales/zh-TW.json
@simlarsen
simlarsen merged commit d6e4933 into master Oct 20, 2026
1 of 64 checks passed
@simlarsen
simlarsen deleted the claude/maintenance-calendar-section branch October 20, 2026 23:05
simlarsen pushed a commit that referenced this pull request Oct 21, 2026
…he TypeScript project

js-lint has failed on master since #5119 (the maintenance calendar
section): ESLint's typed config could not parse
ExternalCalendarExpansionWorker.cjs ("was not found by the project
service"). The file is plain CommonJS on purpose - a worker thread starts
a fresh Node, which loads JavaScript, not the TypeScript the server runs
as - so, like the Probe's CommonJS fixtures and FakeTcpdump.cjs, it is
listed in the ignores with the reason, rather than pulled into a
TypeScript project. ESLint passes on the ExternalCalendar directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant