@remit/web-client 0.0.231 → 0.0.233
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.
- package/package.json +1 -1
- package/src/components/calendar/AgendaStrip.stories.tsx +263 -0
- package/src/components/calendar/AgendaStrip.tsx +145 -0
- package/src/components/calendar/AgendaView.tsx +222 -0
- package/src/components/calendar/CalendarComposePane.stories.tsx +101 -11
- package/src/components/calendar/CalendarComposePane.tsx +83 -23
- package/src/components/calendar/CalendarComposeSeed.tsx +35 -0
- package/src/components/calendar/CalendarEventPane.stories.tsx +30 -1
- package/src/components/calendar/CalendarEventPane.tsx +28 -5
- package/src/components/calendar/CalendarViewPlaceholder.tsx +3 -4
- package/src/components/calendar/CalendarWorkspace.stories.tsx +34 -1
- package/src/components/calendar/CalendarWorkspace.tsx +71 -23
- package/src/components/calendar/OpenCalendarEvent.tsx +189 -7
- package/src/components/calendar/WriteCalendarEvent.tsx +94 -0
- package/src/components/calendar/agenda-address.render.test.ts +389 -0
- package/src/components/calendar/agenda-strip.render.test.ts +241 -0
- package/src/components/calendar/calendar-workspace.render.test.ts +13 -6
- package/src/components/calendar/calendar-write-flow.render.test.ts +356 -0
- package/src/hooks/calendar/agenda-window.test.ts +235 -0
- package/src/hooks/calendar/agenda-window.ts +183 -0
- package/src/hooks/calendar/calendar-reads.render.test.ts +265 -0
- package/src/hooks/calendar/calendar-writes.render.test.ts +305 -0
- package/src/hooks/calendar/draft.test.ts +241 -0
- package/src/hooks/calendar/draft.ts +168 -0
- package/src/hooks/calendar/index.ts +88 -0
- package/src/hooks/calendar/instance.ts +136 -0
- package/src/hooks/calendar/recurrence-rule.test.ts +92 -0
- package/src/hooks/calendar/recurrence-rule.ts +133 -0
- package/src/hooks/calendar/resource.test.ts +98 -0
- package/src/hooks/calendar/resource.ts +74 -0
- package/src/hooks/calendar/selection.ts +107 -0
- package/src/hooks/calendar/useCalendarEvent.ts +46 -0
- package/src/hooks/calendar/useCalendarEvents.ts +195 -0
- package/src/hooks/calendar/useCalendarFreeBusy.ts +80 -0
- package/src/hooks/calendar/useCalendarWrites.ts +174 -0
- package/src/hooks/calendar/useCalendars.ts +43 -0
- package/src/hooks/calendar/window.test.ts +148 -0
- package/src/hooks/calendar/window.ts +168 -0
- package/src/hooks/useCalendarData.ts +62 -40
- package/src/lib/calendar-density.ts +13 -1
- package/src/lib/calendar-route.test.ts +20 -1
- package/src/lib/calendar-route.ts +21 -7
- package/src/routes/calendar/$view.$date/new.tsx +2 -2
- package/src/routes/calendar/$view.$date.tsx +51 -15
- package/src/routing/calendar.ts +32 -4
- package/src/test-support/http.ts +8 -0
- package/src/lib/calendar-fixtures.ts +0 -207
|
@@ -13,18 +13,22 @@
|
|
|
13
13
|
* was readable is kept, so `/calendar/fortnight/2026-06-10` lands on that day's
|
|
14
14
|
* week instead of on an error.
|
|
15
15
|
*/
|
|
16
|
+
import type { CalendarSlotPick } from "@remit/ui";
|
|
16
17
|
import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";
|
|
17
18
|
import { useCallback, useState } from "react";
|
|
19
|
+
import { AgendaView } from "@/components/calendar/AgendaView";
|
|
20
|
+
import { CalendarComposeSeedProvider } from "@/components/calendar/CalendarComposeSeed";
|
|
18
21
|
import { CalendarShell } from "@/components/calendar/CalendarShell";
|
|
19
22
|
import { CalendarWorkspace } from "@/components/calendar/CalendarWorkspace";
|
|
23
|
+
import { calendarInstanceId, deviceTimeZone } from "@/hooks/calendar";
|
|
20
24
|
import { useCalendarData } from "@/hooks/useCalendarData";
|
|
21
25
|
import {
|
|
22
26
|
readCalendarDensity,
|
|
23
27
|
writeCalendarDensity,
|
|
24
28
|
} from "@/lib/calendar-density";
|
|
25
|
-
import { fixtureTimeZone } from "@/lib/calendar-fixtures";
|
|
26
29
|
import {
|
|
27
30
|
calendarSearchSchema,
|
|
31
|
+
calendarViewMountsAgenda,
|
|
28
32
|
canonicalCalendarParams,
|
|
29
33
|
isoDate,
|
|
30
34
|
} from "@/lib/calendar-route";
|
|
@@ -53,11 +57,13 @@ export const Route = createFileRoute("/calendar/$view/$date")({
|
|
|
53
57
|
|
|
54
58
|
function CalendarViewLayout() {
|
|
55
59
|
const { view, date, calendarIds } = useCalendarAddress();
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
// The strip fetches the days it holds a week at a time and draws none of
|
|
61
|
+
// this, so at that zoom the layout asks for nothing: the address rewrites on
|
|
62
|
+
// every scroll, and each rewrite would otherwise fetch a week nobody renders
|
|
63
|
+
// along with the two beside it.
|
|
64
|
+
const drawsGrid = !calendarViewMountsAgenda(view);
|
|
65
|
+
const { events, colorByCalendarId, isLoading, error, retry, instanceOf } =
|
|
66
|
+
useCalendarData({ view, date, calendarIds, enabled: drawsGrid });
|
|
61
67
|
const { goToView, goToToday, step, openEvent, openComposer } =
|
|
62
68
|
useCalendarNavigation();
|
|
63
69
|
const openedEvent = useOpenCalendarEvent();
|
|
@@ -70,6 +76,23 @@ function CalendarViewLayout() {
|
|
|
70
76
|
writeCalendarDensity(next);
|
|
71
77
|
setDensity(next);
|
|
72
78
|
}, []);
|
|
79
|
+
const [pick, setPick] = useState<CalendarSlotPick | undefined>(undefined);
|
|
80
|
+
|
|
81
|
+
const selectEvent = useCallback(
|
|
82
|
+
(eventId: string) => {
|
|
83
|
+
const instance = instanceOf(eventId);
|
|
84
|
+
openEvent(instance.calendarObjectId, instance.recurrenceId);
|
|
85
|
+
},
|
|
86
|
+
[instanceOf, openEvent],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const pickSlot = useCallback(
|
|
90
|
+
(slot: CalendarSlotPick) => {
|
|
91
|
+
setPick(slot);
|
|
92
|
+
openComposer();
|
|
93
|
+
},
|
|
94
|
+
[openComposer],
|
|
95
|
+
);
|
|
73
96
|
|
|
74
97
|
const workspace = (
|
|
75
98
|
<CalendarWorkspace
|
|
@@ -77,24 +100,37 @@ function CalendarViewLayout() {
|
|
|
77
100
|
date={date}
|
|
78
101
|
events={events}
|
|
79
102
|
colorByCalendarId={colorByCalendarId}
|
|
103
|
+
agenda={<AgendaView density={density} onPickSlot={pickSlot} />}
|
|
104
|
+
isLoading={isLoading}
|
|
105
|
+
error={error}
|
|
106
|
+
onRetry={retry}
|
|
80
107
|
density={density}
|
|
81
|
-
selectedEventId={
|
|
82
|
-
|
|
108
|
+
selectedEventId={
|
|
109
|
+
openedEvent
|
|
110
|
+
? calendarInstanceId(
|
|
111
|
+
openedEvent.calendarObjectId,
|
|
112
|
+
openedEvent.recurrenceId ?? "",
|
|
113
|
+
)
|
|
114
|
+
: ""
|
|
115
|
+
}
|
|
116
|
+
timeZone={deviceTimeZone()}
|
|
83
117
|
now={new Date().toISOString()}
|
|
84
118
|
onChangeView={goToView}
|
|
85
119
|
onToday={goToToday}
|
|
86
120
|
onStep={step}
|
|
87
121
|
onChangeDensity={changeDensity}
|
|
88
|
-
onSelectEvent={
|
|
89
|
-
onPickSlot={
|
|
122
|
+
onSelectEvent={selectEvent}
|
|
123
|
+
onPickSlot={pickSlot}
|
|
90
124
|
/>
|
|
91
125
|
);
|
|
92
126
|
|
|
93
127
|
return (
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
128
|
+
<CalendarComposeSeedProvider pick={pick}>
|
|
129
|
+
<CalendarShell
|
|
130
|
+
workspace={workspace}
|
|
131
|
+
reading={<Outlet />}
|
|
132
|
+
hasOpenEvent={openedEvent !== undefined || isWriting}
|
|
133
|
+
/>
|
|
134
|
+
</CalendarComposeSeedProvider>
|
|
99
135
|
);
|
|
100
136
|
}
|
package/src/routing/calendar.ts
CHANGED
|
@@ -82,7 +82,19 @@ export interface CalendarNavigation {
|
|
|
82
82
|
step: (direction: -1 | 1) => void;
|
|
83
83
|
/** Home, from every view and every distance away. */
|
|
84
84
|
goToToday: () => void;
|
|
85
|
-
|
|
85
|
+
/**
|
|
86
|
+
* The day the strip has scrolled to, written by `replace`. Scrolling is not
|
|
87
|
+
* somewhere the reader went — Back belongs to the screen they arrived from,
|
|
88
|
+
* not to every row they passed on the way down.
|
|
89
|
+
*/
|
|
90
|
+
setAnchorDate: (date: string) => void;
|
|
91
|
+
/** Out of the list and into the grid for one day. */
|
|
92
|
+
zoomToDay: (date: string) => void;
|
|
93
|
+
/**
|
|
94
|
+
* One occurrence names the series it belongs to and the instance under it;
|
|
95
|
+
* a resource that does not recur names only itself.
|
|
96
|
+
*/
|
|
97
|
+
openEvent: (calendarObjectId: string, recurrenceId?: string) => void;
|
|
86
98
|
closeEvent: () => void;
|
|
87
99
|
openComposer: () => void;
|
|
88
100
|
}
|
|
@@ -118,13 +130,29 @@ export function useCalendarNavigation(): CalendarNavigation {
|
|
|
118
130
|
step: (direction: -1 | 1) =>
|
|
119
131
|
goTo({ view, date: stepCalendarDate(date, view, direction) }),
|
|
120
132
|
goToToday: () => goTo({ view, date: isoDate(new Date()) }),
|
|
121
|
-
|
|
133
|
+
setAnchorDate: (next: string) =>
|
|
122
134
|
navigate({
|
|
123
|
-
to:
|
|
124
|
-
params: { view, date
|
|
135
|
+
to: VIEW_ROUTE,
|
|
136
|
+
params: { view, date: next },
|
|
125
137
|
search: true,
|
|
126
138
|
hash: retainPanels,
|
|
139
|
+
replace: true,
|
|
127
140
|
}),
|
|
141
|
+
zoomToDay: (next: string) => goTo({ view: "day", date: next }),
|
|
142
|
+
openEvent: (calendarObjectId: string, recurrenceId?: string) =>
|
|
143
|
+
recurrenceId === undefined || recurrenceId === ""
|
|
144
|
+
? navigate({
|
|
145
|
+
to: "/calendar/$view/$date/$calendarObjectId",
|
|
146
|
+
params: { view, date, calendarObjectId },
|
|
147
|
+
search: true,
|
|
148
|
+
hash: retainPanels,
|
|
149
|
+
})
|
|
150
|
+
: navigate({
|
|
151
|
+
to: "/calendar/$view/$date/$calendarObjectId/$recurrenceId",
|
|
152
|
+
params: { view, date, calendarObjectId, recurrenceId },
|
|
153
|
+
search: true,
|
|
154
|
+
hash: retainPanels,
|
|
155
|
+
}),
|
|
128
156
|
closeEvent: () => goTo({ view, date }),
|
|
129
157
|
openComposer: () =>
|
|
130
158
|
navigate({
|
package/src/test-support/http.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface HttpCall {
|
|
|
8
8
|
method: string;
|
|
9
9
|
url: string;
|
|
10
10
|
path: string;
|
|
11
|
+
/** Every request header, lower-cased — a conditional write is one of these. */
|
|
12
|
+
headers: Record<string, string>;
|
|
11
13
|
body: Record<string, unknown> | undefined;
|
|
12
14
|
}
|
|
13
15
|
|
|
@@ -36,10 +38,16 @@ export const mockFetch = (responder: Responder = () => ({})): HttpMock => {
|
|
|
36
38
|
const request = input instanceof Request ? input : undefined;
|
|
37
39
|
const url = request ? request.url : String(input);
|
|
38
40
|
const rawBody = request ? await request.clone().text() : undefined;
|
|
41
|
+
const headers: Record<string, string> = {};
|
|
42
|
+
const sent = request ? request.headers : new Headers(init?.headers);
|
|
43
|
+
sent.forEach((value, name) => {
|
|
44
|
+
headers[name.toLowerCase()] = value;
|
|
45
|
+
});
|
|
39
46
|
const call: HttpCall = {
|
|
40
47
|
method: (request?.method ?? init?.method ?? "GET").toUpperCase(),
|
|
41
48
|
url,
|
|
42
49
|
path: new URL(url, "http://localhost").pathname,
|
|
50
|
+
headers,
|
|
43
51
|
body: rawBody ? JSON.parse(rawBody) : undefined,
|
|
44
52
|
};
|
|
45
53
|
calls.push(call);
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A week of invented calendar, so the routes and the grid can be walked before
|
|
3
|
-
* the REST layer exists. Every name here is made up and every event is
|
|
4
|
-
* generated from the day being looked at, which is what keeps the grid full
|
|
5
|
-
* wherever the reader navigates instead of only in June 2026.
|
|
6
|
-
*
|
|
7
|
-
* Stage A.3 deletes this file: `hooks/useCalendarData` swaps its body for the
|
|
8
|
-
* query hooks and nothing that renders a calendar changes.
|
|
9
|
-
*/
|
|
10
|
-
import type {
|
|
11
|
-
CalendarColorId,
|
|
12
|
-
CalendarDescriptor,
|
|
13
|
-
CalendarEventData,
|
|
14
|
-
} from "@remit/ui";
|
|
15
|
-
|
|
16
|
-
export const workCalendarId = "fixture-work";
|
|
17
|
-
export const personalCalendarId = "fixture-personal";
|
|
18
|
-
export const teamCalendarId = "fixture-team";
|
|
19
|
-
|
|
20
|
-
export const fixtureCalendars: CalendarDescriptor[] = [
|
|
21
|
-
{
|
|
22
|
-
id: workCalendarId,
|
|
23
|
-
accountId: "fixture-account-work",
|
|
24
|
-
accountLabel: "Work",
|
|
25
|
-
name: "Work",
|
|
26
|
-
color: "cal-1",
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: teamCalendarId,
|
|
30
|
-
accountId: "fixture-account-work",
|
|
31
|
-
accountLabel: "Work",
|
|
32
|
-
name: "Team",
|
|
33
|
-
color: "cal-6",
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: personalCalendarId,
|
|
37
|
-
accountId: "fixture-account-personal",
|
|
38
|
-
accountLabel: "Personal",
|
|
39
|
-
name: "Personal",
|
|
40
|
-
color: "cal-4",
|
|
41
|
-
},
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
export const fixtureColorByCalendarId: Record<string, CalendarColorId> =
|
|
45
|
-
Object.fromEntries(
|
|
46
|
-
fixtureCalendars.map((calendar) => [calendar.id, calendar.color]),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const pad = (value: number): string => String(value).padStart(2, "0");
|
|
50
|
-
|
|
51
|
-
/** The Monday of the week a day falls in; the grid starts its weeks there. */
|
|
52
|
-
function weekStart(date: string): Date {
|
|
53
|
-
const [year, month, day] = date.split("-").map(Number);
|
|
54
|
-
const instant = new Date(Date.UTC(year, month - 1, day));
|
|
55
|
-
const weekday = (instant.getUTCDay() + 6) % 7;
|
|
56
|
-
instant.setUTCDate(instant.getUTCDate() - weekday);
|
|
57
|
-
return instant;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function dayOffsetFrom(start: Date, days: number): string {
|
|
61
|
-
const instant = new Date(start);
|
|
62
|
-
instant.setUTCDate(instant.getUTCDate() + days);
|
|
63
|
-
return `${instant.getUTCFullYear()}-${pad(instant.getUTCMonth() + 1)}-${pad(
|
|
64
|
-
instant.getUTCDate(),
|
|
65
|
-
)}`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** The zone the device is on, which is the only clock a fixture can honestly use. */
|
|
69
|
-
export const fixtureTimeZone = (): string =>
|
|
70
|
-
Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
71
|
-
|
|
72
|
-
/** An ISO instant carrying the device's own offset for that day. */
|
|
73
|
-
function at(date: string, hour: number, minute: number): string {
|
|
74
|
-
const local = new Date(`${date}T${pad(hour)}:${pad(minute)}:00`);
|
|
75
|
-
const offset = -local.getTimezoneOffset();
|
|
76
|
-
const sign = offset < 0 ? "-" : "+";
|
|
77
|
-
const size = Math.abs(offset);
|
|
78
|
-
return `${date}T${pad(hour)}:${pad(minute)}:00${sign}${pad(
|
|
79
|
-
Math.floor(size / 60),
|
|
80
|
-
)}:${pad(size % 60)}`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const template: CalendarEventData = {
|
|
84
|
-
id: "",
|
|
85
|
-
calendarId: workCalendarId,
|
|
86
|
-
title: "",
|
|
87
|
-
start: "",
|
|
88
|
-
end: "",
|
|
89
|
-
allDay: false,
|
|
90
|
-
location: "",
|
|
91
|
-
notes: "",
|
|
92
|
-
attendees: [],
|
|
93
|
-
myRsvp: "accepted",
|
|
94
|
-
threadId: "",
|
|
95
|
-
threadSubject: "",
|
|
96
|
-
timeZone: "",
|
|
97
|
-
zoneCertainty: "explicit",
|
|
98
|
-
recurrenceRule: "",
|
|
99
|
-
seriesId: "",
|
|
100
|
-
seriesException: false,
|
|
101
|
-
status: "confirmed",
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
interface FixtureSpec {
|
|
105
|
-
dayOffset: number;
|
|
106
|
-
startHour: number;
|
|
107
|
-
startMinute: number;
|
|
108
|
-
endHour: number;
|
|
109
|
-
endMinute: number;
|
|
110
|
-
title: string;
|
|
111
|
-
calendarId: string;
|
|
112
|
-
location: string;
|
|
113
|
-
recurrenceRule: string;
|
|
114
|
-
seriesId: string;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const spec = (
|
|
118
|
-
dayOffset: number,
|
|
119
|
-
title: string,
|
|
120
|
-
startHour: number,
|
|
121
|
-
endHour: number,
|
|
122
|
-
over: Partial<FixtureSpec> = {},
|
|
123
|
-
): FixtureSpec => ({
|
|
124
|
-
dayOffset,
|
|
125
|
-
startHour,
|
|
126
|
-
startMinute: 0,
|
|
127
|
-
endHour,
|
|
128
|
-
endMinute: 0,
|
|
129
|
-
title,
|
|
130
|
-
calendarId: workCalendarId,
|
|
131
|
-
location: "",
|
|
132
|
-
recurrenceRule: "",
|
|
133
|
-
seriesId: "",
|
|
134
|
-
...over,
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* One made-up week: a daily standup, a couple of long blocks, an overlap on
|
|
139
|
-
* Wednesday and something on a Saturday, so every band the grid draws has
|
|
140
|
-
* something in it.
|
|
141
|
-
*/
|
|
142
|
-
const WEEK: FixtureSpec[] = [
|
|
143
|
-
spec(0, "Standup", 9, 9, {
|
|
144
|
-
startMinute: 15,
|
|
145
|
-
endMinute: 30,
|
|
146
|
-
recurrenceRule: "Every weekday, 09:15",
|
|
147
|
-
seriesId: "fixture-standup",
|
|
148
|
-
}),
|
|
149
|
-
spec(0, "Roadmap review", 11, 12, { calendarId: teamCalendarId }),
|
|
150
|
-
spec(1, "Standup", 9, 9, {
|
|
151
|
-
startMinute: 15,
|
|
152
|
-
endMinute: 30,
|
|
153
|
-
recurrenceRule: "Every weekday, 09:15",
|
|
154
|
-
seriesId: "fixture-standup",
|
|
155
|
-
}),
|
|
156
|
-
spec(1, "Supplier call", 14, 15, {
|
|
157
|
-
calendarId: teamCalendarId,
|
|
158
|
-
location: "Meet",
|
|
159
|
-
}),
|
|
160
|
-
spec(2, "Standup", 9, 9, {
|
|
161
|
-
startMinute: 15,
|
|
162
|
-
endMinute: 30,
|
|
163
|
-
recurrenceRule: "Every weekday, 09:15",
|
|
164
|
-
seriesId: "fixture-standup",
|
|
165
|
-
}),
|
|
166
|
-
spec(2, "Design review", 10, 12, { calendarId: teamCalendarId }),
|
|
167
|
-
spec(2, "Dentist", 11, 12, {
|
|
168
|
-
calendarId: personalCalendarId,
|
|
169
|
-
location: "Kerkstraat 4",
|
|
170
|
-
}),
|
|
171
|
-
spec(3, "Standup", 9, 9, {
|
|
172
|
-
startMinute: 15,
|
|
173
|
-
endMinute: 30,
|
|
174
|
-
recurrenceRule: "Every weekday, 09:15",
|
|
175
|
-
seriesId: "fixture-standup",
|
|
176
|
-
}),
|
|
177
|
-
spec(3, "One to one", 15, 16, { location: "Room 2" }),
|
|
178
|
-
spec(4, "Standup", 9, 9, {
|
|
179
|
-
startMinute: 15,
|
|
180
|
-
endMinute: 30,
|
|
181
|
-
recurrenceRule: "Every weekday, 09:15",
|
|
182
|
-
seriesId: "fixture-standup",
|
|
183
|
-
}),
|
|
184
|
-
spec(4, "Retro", 16, 17, { calendarId: teamCalendarId }),
|
|
185
|
-
spec(5, "Climbing", 10, 12, { calendarId: personalCalendarId }),
|
|
186
|
-
];
|
|
187
|
-
|
|
188
|
-
/** The invented week around a day, in the zone the device is running on. */
|
|
189
|
-
export function fixtureEventsAround(date: string): CalendarEventData[] {
|
|
190
|
-
const start = weekStart(date);
|
|
191
|
-
const timeZone = fixtureTimeZone();
|
|
192
|
-
return WEEK.map((entry) => {
|
|
193
|
-
const day = dayOffsetFrom(start, entry.dayOffset);
|
|
194
|
-
return {
|
|
195
|
-
...template,
|
|
196
|
-
id: `fixture-${day}-${entry.title.toLowerCase().replace(/\s+/g, "-")}`,
|
|
197
|
-
calendarId: entry.calendarId,
|
|
198
|
-
title: entry.title,
|
|
199
|
-
start: at(day, entry.startHour, entry.startMinute),
|
|
200
|
-
end: at(day, entry.endHour, entry.endMinute),
|
|
201
|
-
location: entry.location,
|
|
202
|
-
timeZone,
|
|
203
|
-
recurrenceRule: entry.recurrenceRule,
|
|
204
|
-
seriesId: entry.seriesId,
|
|
205
|
-
};
|
|
206
|
-
});
|
|
207
|
-
}
|