@remit/web-client 0.0.232 → 0.0.234

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,6 +18,9 @@ import { CalendarWorkspace } from "./CalendarWorkspace";
18
18
  * state and get the same surface the app renders. What the stories are for is
19
19
  * what the markup cannot answer — how a week reads once a day is full, and
20
20
  * whether a zoom that is not drawn yet reads as unfinished rather than empty.
21
+ *
22
+ * The strip is a slot rather than something this pane builds, so it is storied
23
+ * under App/Calendar/Agenda and stood in for here.
21
24
  */
22
25
  const TIME_ZONE = "Europe/Amsterdam";
23
26
  const DATE = "2026-06-10";
@@ -123,6 +126,13 @@ function Workspace({
123
126
  date={DATE}
124
127
  events={events}
125
128
  colorByCalendarId={colorByCalendarId}
129
+ // The strip loads its own days and is storied on its own; here it
130
+ // stands in for whatever the route mounts at that zoom.
131
+ agenda={
132
+ <div className="p-4 text-sm text-fg-muted">
133
+ The strip, which App/Calendar/Agenda has to itself.
134
+ </div>
135
+ }
126
136
  isLoading={isLoading}
127
137
  error={error}
128
138
  onRetry={() => undefined}
@@ -185,7 +195,8 @@ export const YearNotBuiltYet: Story = { args: { initialView: "year" } };
185
195
 
186
196
  export const MonthNotBuiltYet: Story = { args: { initialView: "month" } };
187
197
 
188
- export const AgendaNotBuiltYet: Story = { args: { initialView: "agenda" } };
198
+ /** The zoom that mounts the strip instead of the grid. */
199
+ export const Agenda: Story = { args: { initialView: "agenda" } };
189
200
 
190
201
  /**
191
202
  * The calendar beside the nav it is reached from. The entry sits under the
@@ -10,11 +10,14 @@ import {
10
10
  segmentClassName,
11
11
  } from "@remit/ui";
12
12
  import { Loader2 } from "lucide-react";
13
- import { useId, useState } from "react";
13
+ import { type ReactNode, useId, useState } from "react";
14
14
  import { CalendarViewPlaceholder } from "@/components/calendar/CalendarViewPlaceholder";
15
15
  import { NavMenuButton } from "@/components/mail/NavMenuButton";
16
16
  import { ErrorState } from "@/components/ui/ErrorState";
17
- import { calendarViewMountsGrid } from "@/lib/calendar-route";
17
+ import {
18
+ calendarViewMountsAgenda,
19
+ calendarViewMountsGrid,
20
+ } from "@/lib/calendar-route";
18
21
 
19
22
  /**
20
23
  * The calendar's own pane: the toolbar that moves it and whatever the current
@@ -38,6 +41,12 @@ export interface CalendarWorkspaceProps {
38
41
  date: string;
39
42
  events: CalendarEventData[];
40
43
  colorByCalendarId: Record<string, CalendarColorId>;
44
+ /**
45
+ * The strip, at the one zoom that draws it. A slot rather than something
46
+ * built here because it loads its own days over a rolling range, and this
47
+ * pane holds no events and knows no router.
48
+ */
49
+ agenda: ReactNode;
41
50
  /** The first read of this window is still out; the grid has nothing yet. */
42
51
  isLoading?: boolean;
43
52
  /**
@@ -89,6 +98,7 @@ export function CalendarWorkspace({
89
98
  date,
90
99
  events,
91
100
  colorByCalendarId,
101
+ agenda,
92
102
  isLoading = false,
93
103
  error,
94
104
  onRetry,
@@ -150,43 +160,51 @@ export function CalendarWorkspace({
150
160
  </CalendarDateNav>
151
161
  </div>
152
162
 
153
- <div className="min-h-0 flex-1">
154
- {error !== undefined && error !== null ? (
155
- <div className="flex h-full items-center justify-center">
156
- <ErrorState
157
- title="Couldn't load this week"
158
- error={error}
159
- onRetry={onRetry}
163
+ {/* The strip rolls its own range, so it answers for its own days: the
164
+ week this pane read is not what it is drawing, and gating it on that
165
+ read would blank a strip that has everything it needs. It also
166
+ scrolls itself, so it gets a column to fill rather than a box. */}
167
+ {calendarViewMountsAgenda(view) ? (
168
+ <div className="flex min-h-0 flex-1 flex-col">{agenda}</div>
169
+ ) : (
170
+ <div className="min-h-0 flex-1">
171
+ {error !== undefined && error !== null ? (
172
+ <div className="flex h-full items-center justify-center">
173
+ <ErrorState
174
+ title="Couldn't load this week"
175
+ error={error}
176
+ onRetry={onRetry}
177
+ />
178
+ </div>
179
+ ) : isLoading ? (
180
+ <div
181
+ role="status"
182
+ aria-label="Loading the calendar"
183
+ className="flex h-full items-center justify-center bg-surface"
184
+ >
185
+ <Loader2 className="size-6 animate-spin text-fg-subtle" />
186
+ </div>
187
+ ) : calendarViewMountsGrid(view) ? (
188
+ <CalendarGrid
189
+ view={view}
190
+ date={date}
191
+ events={events}
192
+ colorByCalendarId={colorByCalendarId}
193
+ density={density}
194
+ selectedEventId={selectedEventId}
195
+ timeZone={timeZone}
196
+ now={now}
197
+ onSelectEvent={onSelectEvent}
198
+ onPickSlot={onPickSlot}
199
+ onRangeChange={(measuredTitle) =>
200
+ setMeasured({ key: addressKey, title: measuredTitle })
201
+ }
160
202
  />
161
- </div>
162
- ) : isLoading ? (
163
- <div
164
- role="status"
165
- aria-label="Loading the calendar"
166
- className="flex h-full items-center justify-center bg-surface"
167
- >
168
- <Loader2 className="size-6 animate-spin text-fg-subtle" />
169
- </div>
170
- ) : calendarViewMountsGrid(view) ? (
171
- <CalendarGrid
172
- view={view}
173
- date={date}
174
- events={events}
175
- colorByCalendarId={colorByCalendarId}
176
- density={density}
177
- selectedEventId={selectedEventId}
178
- timeZone={timeZone}
179
- now={now}
180
- onSelectEvent={onSelectEvent}
181
- onPickSlot={onPickSlot}
182
- onRangeChange={(measuredTitle) =>
183
- setMeasured({ key: addressKey, title: measuredTitle })
184
- }
185
- />
186
- ) : (
187
- <CalendarViewPlaceholder view={view} />
188
- )}
189
- </div>
203
+ ) : (
204
+ <CalendarViewPlaceholder view={view} />
205
+ )}
206
+ </div>
207
+ )}
190
208
  </div>
191
209
  );
192
210
  }
@@ -0,0 +1,493 @@
1
+ /**
2
+ * The strip and the address, bound together.
3
+ *
4
+ * Four claims, and every one of them is about the URL rather than about pixels:
5
+ * the strip opens on the day the path names, the reader reaching an end asks
6
+ * for a week more rather than replacing the days on screen, a strip nobody
7
+ * scrolled asks for nothing at all, and the day the reader scrolls to is
8
+ * written by `replace` — scrolling is not somewhere they went, so Back belongs
9
+ * to the screen they arrived from and not to every row they passed. The last
10
+ * one is the reason this suite drives the real router instead of a callback: a
11
+ * `push` here would bury the way out under a fortnight of history entries.
12
+ */
13
+ import assert from "node:assert/strict";
14
+ import { afterEach, describe, it } from "node:test";
15
+ import {
16
+ calendarEventOperationsListCalendarEventsQueryKey,
17
+ calendarFreeBusyOperationsListCalendarFreeBusyQueryKey,
18
+ calendarOperationsListCalendarsQueryKey,
19
+ } from "@remit/api-http-client/@tanstack/react-query.gen.ts";
20
+ import {
21
+ type AnyRouter,
22
+ createMemoryHistory,
23
+ createRootRoute,
24
+ createRoute,
25
+ createRouter,
26
+ Outlet,
27
+ RouterProvider,
28
+ } from "@tanstack/react-router";
29
+ import { act, createElement, type ReactNode } from "react";
30
+ import {
31
+ calendarWindow,
32
+ datesInRange,
33
+ extendRangeEnd,
34
+ extendRangeStart,
35
+ rangeAround,
36
+ weekWindowsOver,
37
+ } from "@/hooks/calendar";
38
+ import { useCalendarData } from "@/hooks/useCalendarData";
39
+ import { useCalendarAddress, useCalendarNavigation } from "@/routing";
40
+ import { createDomHarness, type DomHarness } from "@/test-support/dom";
41
+ import {
42
+ calendarSearchSchema,
43
+ calendarViewMountsAgenda,
44
+ } from "../../lib/calendar-route";
45
+ import { stringifySearch } from "../../lib/search-params";
46
+ import { AgendaView } from "./AgendaView";
47
+
48
+ const DATE = "2026-06-10";
49
+ const WORK = "cal_work";
50
+ const HOME = "cal_home";
51
+
52
+ const TO_WEEK = "to week";
53
+
54
+ // The router reads `self` at construction; the shared jsdom globals stop at
55
+ // `window`.
56
+ (globalThis as { self?: typeof globalThis }).self ??= globalThis;
57
+
58
+ let harness: DomHarness | undefined;
59
+
60
+ afterEach(() => {
61
+ harness?.close();
62
+ harness = undefined;
63
+ });
64
+
65
+ /** The zoom switch's own binding, beside the strip the route mounts. */
66
+ function CalendarLayout() {
67
+ const { goToView } = useCalendarNavigation();
68
+ return createElement(
69
+ "div",
70
+ null,
71
+ createElement(
72
+ "button",
73
+ {
74
+ type: "button",
75
+ "aria-label": TO_WEEK,
76
+ onClick: () => goToView("week"),
77
+ },
78
+ "Week",
79
+ ),
80
+ createElement(AgendaView, {
81
+ density: "comfortable",
82
+ onPickSlot: () => undefined,
83
+ }),
84
+ );
85
+ }
86
+
87
+ const testRouter = (
88
+ path: string,
89
+ component: () => ReactNode = CalendarLayout,
90
+ ): AnyRouter => {
91
+ const rootRoute = createRootRoute({ component: Outlet });
92
+ const viewRoute = createRoute({
93
+ getParentRoute: () => rootRoute,
94
+ path: "/calendar/$view/$date",
95
+ validateSearch: calendarSearchSchema,
96
+ component,
97
+ });
98
+ // The strip reads whether an event is open, which is a child of this route.
99
+ const eventRoute = createRoute({
100
+ getParentRoute: () => viewRoute,
101
+ path: "$calendarObjectId",
102
+ component: Outlet,
103
+ });
104
+ const instanceRoute = createRoute({
105
+ getParentRoute: () => eventRoute,
106
+ path: "$recurrenceId",
107
+ component: Outlet,
108
+ });
109
+ const composerRoute = createRoute({
110
+ getParentRoute: () => viewRoute,
111
+ path: "new",
112
+ component: Outlet,
113
+ });
114
+ const routeTree = rootRoute.addChildren([
115
+ viewRoute.addChildren([
116
+ eventRoute.addChildren([instanceRoute]),
117
+ composerRoute,
118
+ ]),
119
+ ]);
120
+ return createRouter({
121
+ routeTree,
122
+ // A list of ticked calendars is repeated params, which is how the app
123
+ // spells one and what a hand-written link says.
124
+ stringifySearch,
125
+ history: createMemoryHistory({ initialEntries: [path] }),
126
+ }) as unknown as AnyRouter;
127
+ };
128
+
129
+ /** How many times a case reaches an end, and so how far the strip can go. */
130
+ const REACHES = 8;
131
+
132
+ /** Every week the strip can reach, given that many pulls at either end. */
133
+ const reachableWeeks = () => {
134
+ let range = rangeAround(DATE);
135
+ for (let reach = 0; reach < REACHES; reach += 1) {
136
+ range = extendRangeEnd(extendRangeStart(range));
137
+ }
138
+ return weekWindowsOver(datesInRange(range));
139
+ };
140
+
141
+ /**
142
+ * Every week the strip can ask for, answered from the cache. A week nobody
143
+ * seeded would fire a real request, and what these cases are about is which
144
+ * weeks get asked for, not what comes back.
145
+ */
146
+ const seed = (dom: DomHarness) => {
147
+ dom.queryClient.setQueryData(calendarOperationsListCalendarsQueryKey(), {
148
+ items: [],
149
+ });
150
+ for (const window of reachableWeeks()) {
151
+ dom.queryClient.setQueryData(
152
+ calendarEventOperationsListCalendarEventsQueryKey({ query: window }),
153
+ { items: [] },
154
+ );
155
+ dom.queryClient.setQueryData(
156
+ calendarFreeBusyOperationsListCalendarFreeBusyQueryKey({
157
+ query: window,
158
+ }),
159
+ { items: [] },
160
+ );
161
+ }
162
+ };
163
+
164
+ interface Mounted {
165
+ dom: DomHarness;
166
+ router: AnyRouter;
167
+ pushes: number;
168
+ replaces: number;
169
+ }
170
+
171
+ const mount = async (path = `/calendar/agenda/${DATE}`): Promise<Mounted> => {
172
+ const dom = createDomHarness();
173
+ harness = dom;
174
+ seed(dom);
175
+
176
+ const router = testRouter(path);
177
+ const counted = { pushes: 0, replaces: 0 };
178
+ const { push, replace } = router.history;
179
+ router.history.push = (...args: Parameters<typeof push>) => {
180
+ counted.pushes += 1;
181
+ return push(...args);
182
+ };
183
+ router.history.replace = (...args: Parameters<typeof replace>) => {
184
+ counted.replaces += 1;
185
+ return replace(...args);
186
+ };
187
+
188
+ await router.load();
189
+ dom.renderApp(createElement(RouterProvider, { router }));
190
+ await dom.flush();
191
+
192
+ return {
193
+ dom,
194
+ router,
195
+ get pushes() {
196
+ return counted.pushes;
197
+ },
198
+ get replaces() {
199
+ return counted.replaces;
200
+ },
201
+ };
202
+ };
203
+
204
+ /**
205
+ * The one element the strip scrolls. jsdom has no layout, so it is given the
206
+ * one part of it these cases turn on: an offset that moves and stays where it
207
+ * was put. Everything else about the strip's geometry is zero, which is the
208
+ * sparse diary this suite is about — every end within reach of every other.
209
+ */
210
+ const stubbed = new WeakSet<Element>();
211
+
212
+ const scroller = (dom: DomHarness): HTMLElement => {
213
+ const found = dom.query<HTMLElement>(".overflow-y-auto");
214
+ if (!found) throw new Error("the strip rendered no scroller");
215
+ if (stubbed.has(found)) return found;
216
+ let top = 0;
217
+ Object.defineProperty(found, "scrollTop", {
218
+ configurable: true,
219
+ get: () => top,
220
+ set: (next: number) => {
221
+ top = Math.max(0, next);
222
+ },
223
+ });
224
+ stubbed.add(found);
225
+ return found;
226
+ };
227
+
228
+ /** A reader scrolling: the gesture, where it took the strip, then the event. */
229
+ const scrollBy = (dom: DomHarness, delta: number) => {
230
+ const element = scroller(dom);
231
+ dom.dispatch(element, new Event("wheel", { bubbles: true }));
232
+ element.scrollTop += delta;
233
+ dom.dispatch(element, new Event("scroll", { bubbles: false }));
234
+ };
235
+
236
+ const scrollDown = (dom: DomHarness) => scrollBy(dom, 200);
237
+ const scrollUp = (dom: DomHarness) => scrollBy(dom, -400);
238
+
239
+ /**
240
+ * The same movement with nobody behind it: a font swapping under a month of
241
+ * rows, a pane resized, the offset taken back after days were prepended.
242
+ */
243
+ const settle = (dom: DomHarness) => {
244
+ const element = scroller(dom);
245
+ element.scrollTop += 120;
246
+ dom.dispatch(element, new Event("scroll", { bubbles: false }));
247
+ };
248
+
249
+ describe("the day the address names", () => {
250
+ it("is the day the strip opens on", async () => {
251
+ const { dom } = await mount();
252
+ assert.match(dom.text(), /Wednesday/, "the header names another day");
253
+ });
254
+
255
+ it("asks for the weeks around it, one request each", async () => {
256
+ const { dom } = await mount();
257
+ const asked = eventWindows(dom);
258
+ const expected = weekWindowsOver(datesInRange(rangeAround(DATE)));
259
+ assert.deepEqual(
260
+ asked.map((window) => window.from).sort(),
261
+ expected.map((window) => window.from).sort(),
262
+ );
263
+ for (const window of asked) {
264
+ const days =
265
+ (Date.parse(window.to) - Date.parse(window.from)) / 86_400_000;
266
+ assert.equal(days, 7, "a read covered more than one week");
267
+ }
268
+ });
269
+ });
270
+
271
+ describe("reaching an end of the strip", () => {
272
+ it("asks for more days without giving up the ones on screen", async () => {
273
+ const { dom } = await mount();
274
+ const before = dom.text();
275
+ assert.doesNotMatch(before, /11 Jul/);
276
+
277
+ scrollDown(dom);
278
+ await dom.flush();
279
+
280
+ const after = dom.text();
281
+ assert.match(after, /11 Jul/, "the days past the window did not arrive");
282
+ assert.match(after, /9 Jun/, "the days it opened with were thrown away");
283
+ });
284
+
285
+ it("grows the end the reader went to, a week at a time, and only that one", async () => {
286
+ const { dom } = await mount();
287
+ scrollDown(dom);
288
+ await dom.flush();
289
+ assert.match(dom.text(), /11 Jul/);
290
+ assert.doesNotMatch(dom.text(), /24 May/, "the end nobody went to grew");
291
+
292
+ scrollUp(dom);
293
+ await dom.flush();
294
+ assert.match(dom.text(), /24 May/, "the days behind never arrived");
295
+ });
296
+
297
+ it("adds week keys rather than widening the one it holds", async () => {
298
+ const { dom } = await mount();
299
+ const opening = eventWindows(dom).map((window) => window.from);
300
+
301
+ scrollDown(dom);
302
+ await dom.flush();
303
+ scrollUp(dom);
304
+ await dom.flush();
305
+
306
+ const after = eventWindows(dom).map((window) => window.from);
307
+ for (const from of opening) {
308
+ assert.ok(after.includes(from), `${from} was given up to fetch more`);
309
+ }
310
+ assert.ok(after.length > opening.length, "no week was added");
311
+ assert.ok(
312
+ after.some((from) => from < opening[0]),
313
+ "nothing reached further back than the week it opened with",
314
+ );
315
+ });
316
+
317
+ it("never grows a single read past what the server will answer", async () => {
318
+ const { dom } = await mount();
319
+ for (let reach = 0; reach < 6; reach += 1) {
320
+ scrollDown(dom);
321
+ await dom.flush();
322
+ }
323
+ for (const window of eventWindows(dom)) {
324
+ const days =
325
+ (Date.parse(window.to) - Date.parse(window.from)) / 86_400_000;
326
+ assert.equal(days, 7, `a read covered ${days} days`);
327
+ }
328
+ });
329
+ });
330
+
331
+ /**
332
+ * The bug this suite exists to keep out. A diary with two events in it draws
333
+ * shorter than the distance the strip fetches at, so both ends are reached from
334
+ * the first layout pass, and every extension raised another scroll event to
335
+ * reach them again. Left to run it walked a five-week window out to 2032 and
336
+ * wrote every day of the way into the path.
337
+ */
338
+ describe("a strip nobody has scrolled", () => {
339
+ it("asks for the weeks it opened with and no others", async () => {
340
+ const { dom } = await mount();
341
+ const opening = eventWindows(dom)
342
+ .map((window) => window.from)
343
+ .sort();
344
+
345
+ for (let pass = 0; pass < 5; pass += 1) {
346
+ settle(dom);
347
+ await dom.flush();
348
+ }
349
+
350
+ assert.deepEqual(
351
+ eventWindows(dom)
352
+ .map((window) => window.from)
353
+ .sort(),
354
+ opening,
355
+ );
356
+ });
357
+
358
+ it("leaves the address on the day it was opened with", async () => {
359
+ const { dom, router } = await mount();
360
+ for (let pass = 0; pass < 5; pass += 1) {
361
+ settle(dom);
362
+ await dom.flush();
363
+ }
364
+ await dom.wait(600);
365
+ await dom.flush();
366
+
367
+ assert.equal(router.state.location.pathname, `/calendar/agenda/${DATE}`);
368
+ });
369
+ });
370
+
371
+ describe("the day the reader scrolled to", () => {
372
+ it("is written to the path, so a reload comes back to it", async () => {
373
+ const { dom, router } = await mount();
374
+ scrollDown(dom);
375
+ await dom.wait(600);
376
+ await dom.flush();
377
+
378
+ assert.match(router.state.location.pathname, /^\/calendar\/agenda\//);
379
+ assert.notEqual(
380
+ router.state.location.pathname,
381
+ `/calendar/agenda/${DATE}`,
382
+ "scrolling left the address on the day it opened",
383
+ );
384
+ });
385
+
386
+ it("replaces rather than pushes, so Back is still the way out", async () => {
387
+ const mounted = await mount();
388
+ scrollDown(mounted.dom);
389
+ await mounted.dom.wait(600);
390
+ await mounted.dom.flush();
391
+
392
+ assert.ok(mounted.replaces > 0, "the address was never written");
393
+ assert.equal(mounted.pushes, 0, "scrolling stacked up history entries");
394
+ });
395
+
396
+ it("stays there while the days it asked for arrive", async () => {
397
+ const { dom, router } = await mount();
398
+ scrollDown(dom);
399
+ await dom.wait(600);
400
+ await dom.flush();
401
+ const landed = router.state.location.pathname;
402
+
403
+ // The prepend takes the scroll offset back, which is another scroll event.
404
+ // It is not the reader moving, so it writes nothing.
405
+ settle(dom);
406
+ await dom.wait(600);
407
+ await dom.flush();
408
+
409
+ assert.equal(router.state.location.pathname, landed);
410
+ });
411
+ });
412
+
413
+ describe("dropping into the grid and back out", () => {
414
+ it("keeps the day and the calendars the reader was looking at", async () => {
415
+ const { dom, router } = await mount();
416
+ await act(async () => {
417
+ await router.navigate({
418
+ to: "/calendar/$view/$date",
419
+ params: { view: "agenda", date: DATE },
420
+ search: { calendarId: [HOME, WORK] },
421
+ });
422
+ });
423
+ await dom.flush();
424
+
425
+ dom.click(dom.byLabel(TO_WEEK));
426
+ await dom.flush();
427
+
428
+ assert.equal(router.state.location.pathname, `/calendar/week/${DATE}`);
429
+ assert.deepEqual(router.state.location.search.calendarId, [HOME, WORK]);
430
+ });
431
+ });
432
+
433
+ /**
434
+ * The layout's own read, bound exactly as the route binds it. Mounted without
435
+ * the strip, so any window in the cache came from here.
436
+ */
437
+ function LayoutOnly() {
438
+ const { view, date, calendarIds } = useCalendarAddress();
439
+ useCalendarData({
440
+ view,
441
+ date,
442
+ calendarIds,
443
+ enabled: !calendarViewMountsAgenda(view),
444
+ });
445
+ return createElement("div", { "data-testid": "layout-only" });
446
+ }
447
+
448
+ describe("the layout's own week, at a zoom that does not draw it", () => {
449
+ const mountLayout = async (view: string): Promise<DomHarness> => {
450
+ const dom = createDomHarness();
451
+ harness = dom;
452
+ seed(dom);
453
+ const router = testRouter(`/calendar/${view}/${DATE}`, LayoutOnly);
454
+ await router.load();
455
+ dom.renderApp(createElement(RouterProvider, { router }));
456
+ await dom.flush();
457
+ return dom;
458
+ };
459
+
460
+ it("reads the week the grid draws", async () => {
461
+ const dom = await mountLayout("week");
462
+ assert.deepEqual(
463
+ eventWindows(dom).map((window) => window.from),
464
+ [calendarWindow("week", DATE).from],
465
+ );
466
+ });
467
+
468
+ /*
469
+ * The strip fetches its own weeks, so this read renders nothing — and the
470
+ * address rewrites on every scroll, so leaving it on would ask again, with
471
+ * its two neighbours prefetched, all the way down the strip.
472
+ */
473
+ it("asks for nothing at all at the agenda zoom", async () => {
474
+ const dom = await mountLayout("agenda");
475
+ assert.deepEqual(eventWindows(dom), []);
476
+ });
477
+ });
478
+
479
+ /** The windows the strip currently has `/calendar-events` open on. */
480
+ const eventWindows = (dom: DomHarness): { from: string; to: string }[] =>
481
+ dom.queryClient
482
+ .getQueryCache()
483
+ .getAll()
484
+ .filter((query) => query.isActive())
485
+ .map(
486
+ (query) =>
487
+ query.queryKey[0] as {
488
+ _id?: string;
489
+ query?: { from: string; to: string };
490
+ },
491
+ )
492
+ .filter((key) => key?._id === "calendarEventOperationsListCalendarEvents")
493
+ .flatMap((key) => (key.query ? [key.query] : []));