@blazeo.com/appointment-client 1.0.6 → 1.0.7
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/blazeo.com-appointment-client-1.0.7.tgz +0 -0
- package/dist/calendar/buildUnifiedCalendarView.d.ts +8 -0
- package/dist/calendar/buildUnifiedCalendarView.js +20 -5
- package/dist/calendar/fetchCalendarDetails.d.ts +8 -40
- package/dist/calendar/fetchCalendarDetails.js +116 -47
- package/dist/calendar/fetchCalendarWithOpeningHours.d.ts +1 -10
- package/dist/calendar/fetchCalendarWithOpeningHours.js +34 -15
- package/dist/calendar/getAllParticipantOpeningHours.d.ts +4 -1
- package/dist/calendar/getAllParticipantOpeningHours.js +6 -1
- package/dist/calendar/getOpeningHours.d.ts +4 -1
- package/dist/calendar/getOpeningHours.js +2 -2
- package/dist/calendar/getParticipantOpeningHours.js +9 -4
- package/dist/calendar/getParticipants.d.ts +4 -1
- package/dist/calendar/getParticipants.js +6 -1
- package/dist/calendar/mapToDesiredResponse.d.ts +70 -0
- package/dist/calendar/mapToDesiredResponse.js +99 -0
- package/dist/config/applyBlazeoDefaults.js +3 -2
- package/dist/config/blazeoClientDefaults.js +2 -2
- package/dist/config/ensureBlazeoHttpReady.d.ts +17 -0
- package/dist/config/ensureBlazeoHttpReady.js +31 -0
- package/dist/config/initializeAppointmentClient.d.ts +4 -0
- package/dist/config/initializeAppointmentClient.js +9 -3
- package/dist/config/syncBlazeoConnection.d.ts +6 -0
- package/dist/config/syncBlazeoConnection.js +18 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/sample/.env.example +5 -0
- package/sample/package-lock.json +1 -1
- package/sample/src/AllParticipantOpeningHoursTab.jsx +13 -4
- package/sample/src/App2.jsx +22 -2
- package/sample/src/AvailabilityTab.jsx +8 -3
- package/sample/src/BlazeoConnectionSettings.jsx +16 -15
- package/sample/src/CreateCalendarTab.jsx +23 -6
- package/sample/src/EventTab.jsx +31 -8
- package/sample/src/FetchCalendarTab.jsx +70 -44
- package/sample/src/OpeningHoursTab.jsx +13 -4
- package/sample/src/ParticipantInfoTab.jsx +8 -3
- package/sample/src/ParticipantOpeningHoursTab.jsx +17 -7
- package/sample/src/ParticipantTab.jsx +13 -4
- package/sample/src/blazeoBootstrap.js +30 -0
- package/sample/src/blazeoDemoError.js +14 -0
- package/sample/src/blazeoPushConnection.js +23 -0
- package/sample/src/main.jsx +3 -3
- package/sample/vite.config.js +19 -5
- package/src/calendar/buildUnifiedCalendarView.ts +28 -5
- package/src/calendar/fetchCalendarDetails.ts +316 -226
- package/src/calendar/fetchCalendarWithOpeningHours.ts +130 -99
- package/src/calendar/getAllParticipantOpeningHours.ts +9 -1
- package/src/calendar/getOpeningHours.ts +2 -2
- package/src/calendar/getParticipantOpeningHours.ts +14 -5
- package/src/calendar/getParticipants.ts +9 -1
- package/src/calendar/mapToDesiredResponse.ts +104 -0
- package/src/config/applyBlazeoDefaults.ts +3 -2
- package/src/config/blazeoClientDefaults.ts +2 -2
- package/src/config/ensureBlazeoHttpReady.ts +41 -0
- package/src/config/initializeAppointmentClient.ts +9 -3
- package/src/config/syncBlazeoConnection.ts +19 -0
- package/src/index.ts +7 -1
- package/blazeo.com-appointment-client-1.0.6.tgz +0 -0
|
@@ -1,226 +1,316 @@
|
|
|
1
|
-
import { CalendarModel } from "@blazeo.com/calendar-client";
|
|
2
|
-
import { getSnapshot } from "mobx-state-tree";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
const
|
|
64
|
-
if (Array.isArray(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*
|
|
76
|
-
* **
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* - `GET /Calendar/Participant/
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
return {
|
|
113
|
-
calendar: null,
|
|
114
|
-
cal: null,
|
|
115
|
-
calendarView: null as UnifiedCalendarView | null,
|
|
116
|
-
openingHours: [] as any[],
|
|
117
|
-
participants: [] as any[],
|
|
118
|
-
participantsInfo: null as any,
|
|
119
|
-
allParticipantOpeningHours: null as any[] | null,
|
|
120
|
-
embeddedFromGet: [] as any[],
|
|
121
|
-
fromCalendarGet: false,
|
|
122
|
-
fromParticipantApi: false,
|
|
123
|
-
participantOpeningHoursResponse: null as any,
|
|
124
|
-
rawGet:
|
|
125
|
-
meta: { ok: false as const, reason: "
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
1
|
+
import { CalendarModel } from "@blazeo.com/calendar-client";
|
|
2
|
+
import { getSnapshot } from "mobx-state-tree";
|
|
3
|
+
import { ensureBlazeoHttpReady } from "../config/ensureBlazeoHttpReady.js";
|
|
4
|
+
import {
|
|
5
|
+
unwrapCalendarGetData,
|
|
6
|
+
pickOpeningHoursArrayFromCalendarPayload,
|
|
7
|
+
normalizeParticipantOpeningHoursResponse,
|
|
8
|
+
} from "./fetchCalendarWithOpeningHours.js";
|
|
9
|
+
import { buildUnifiedCalendarView, type UnifiedCalendarView } from "./buildUnifiedCalendarView.js";
|
|
10
|
+
import { mapToDesiredCalendarResponse } from "./mapToDesiredResponse.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Normalizes the REST envelope from `calendar.getParticipantOpeningHours()`
|
|
14
|
+
* (`GET /Calendar/Participant/OpeningHours/Get`) into a plain row array.
|
|
15
|
+
*/
|
|
16
|
+
export function normalizeOpeningHours(res: any): any[] {
|
|
17
|
+
const { list } = normalizeParticipantOpeningHoursResponse(res);
|
|
18
|
+
return Array.isArray(list) ? list : [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeAllParticipantOpeningHoursResult(raw: any): any[] {
|
|
22
|
+
if (Array.isArray(raw)) return raw;
|
|
23
|
+
const { list } = normalizeParticipantOpeningHoursResponse(raw);
|
|
24
|
+
return Array.isArray(list) ? list : [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Prefer union of `/Participant/All` and `/Participant/Get` so members reconcile when either list is incomplete. */
|
|
28
|
+
function mergeParticipantSnapshots(
|
|
29
|
+
a: any[] | null | undefined,
|
|
30
|
+
b: any[] | null | undefined
|
|
31
|
+
): any[] {
|
|
32
|
+
const byKey = new Map<string, any>();
|
|
33
|
+
const ingest = (p: any) => {
|
|
34
|
+
if (p == null) return;
|
|
35
|
+
const participantId = String(
|
|
36
|
+
p.participantId ?? p.ParticipantId ?? p.participant_id ?? ""
|
|
37
|
+
).trim().toLowerCase();
|
|
38
|
+
const calPartId = String(
|
|
39
|
+
p.calendarParticipantId ?? p.CalendarParticipantId ?? p.calendarparticipant_id ?? ""
|
|
40
|
+
).trim()
|
|
41
|
+
.toLowerCase();
|
|
42
|
+
const key = participantId || calPartId;
|
|
43
|
+
if (!key) return;
|
|
44
|
+
if (!byKey.has(key)) byKey.set(key, p);
|
|
45
|
+
};
|
|
46
|
+
if (Array.isArray(a)) a.forEach(ingest);
|
|
47
|
+
if (Array.isArray(b)) b.forEach(ingest);
|
|
48
|
+
return [...byKey.values()];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Coerce MST / envelope / nested `data` shapes into a plain array for participants and GetInfo lists. */
|
|
52
|
+
function unwrapModelList(raw: any): any[] {
|
|
53
|
+
if (raw == null) return [];
|
|
54
|
+
if (Array.isArray(raw)) return raw;
|
|
55
|
+
if (typeof raw === "string") {
|
|
56
|
+
try {
|
|
57
|
+
return unwrapModelList(JSON.parse(raw));
|
|
58
|
+
} catch {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (typeof raw !== "object") return [];
|
|
63
|
+
const topArr = (raw as any).items ?? (raw as any).Items;
|
|
64
|
+
if (Array.isArray(topArr)) return topArr;
|
|
65
|
+
const d = raw.data ?? raw.Data;
|
|
66
|
+
if (Array.isArray(d)) return d;
|
|
67
|
+
if (d != null && typeof d === "object") {
|
|
68
|
+
const inner = (d as any).data ?? (d as any).Data ?? (d as any).items ?? (d as any).Items;
|
|
69
|
+
if (Array.isArray(inner)) return inner;
|
|
70
|
+
}
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Calendar + legacy opening hours + full detail bundle (`fetchCalendarDetails`), **or**
|
|
76
|
+
* only the **single unified object** via {@link fetchCalendarBundle}.
|
|
77
|
+
*
|
|
78
|
+
* **Flow**
|
|
79
|
+
* 1. **Calendar** — parallel `CalendarModel.get` + `getRaw` (both `GET /Calendar/Get`: model + raw envelope).
|
|
80
|
+
* 2. **Legacy `openingHours`** — embed from raw, else `getParticipantOpeningHours` (narrow endpoint).
|
|
81
|
+
* 3. **`calendarView` (one object)** — in parallel after calendar is known:
|
|
82
|
+
* - `GET /Calendar/Participant/All` and optional `/Participant/Get` merge when both return rows.
|
|
83
|
+
* - `GET /Calendar/Participants/GetInfo`
|
|
84
|
+
* - `GET /Calendar/Participant/OpeningHours/All/Get` when options enable it (`preferAllParticipantOpeningHours`)
|
|
85
|
+
* Then `calendarView` = calendar snapshot fields + **`members`** (with **`participantInfo`**) + **`openingHours`**
|
|
86
|
+
* (`openingHours[].member` → `members[].id`).
|
|
87
|
+
*
|
|
88
|
+
* Server still performs multiple HTTP calls; on the client, **`calendarView`** is returned as **one object**.
|
|
89
|
+
*/
|
|
90
|
+
export async function fetchCalendarDetails(
|
|
91
|
+
calendarId: string,
|
|
92
|
+
options: {
|
|
93
|
+
includeParticipantsInfo?: boolean;
|
|
94
|
+
includeUnifiedCalendarView?: boolean;
|
|
95
|
+
/** Prefer all-participant opening hours for **`calendarView`** when the API returns rows (default `true`). */
|
|
96
|
+
preferAllParticipantOpeningHours?: boolean;
|
|
97
|
+
/** Optional; applied with `resolveBlazeoConnection` so `CalendarModel.get` sees `baseUrl` without prior global `configure`. */
|
|
98
|
+
baseUrl?: string;
|
|
99
|
+
consumer?: string;
|
|
100
|
+
} = {}
|
|
101
|
+
) {
|
|
102
|
+
const {
|
|
103
|
+
includeParticipantsInfo = false,
|
|
104
|
+
includeUnifiedCalendarView = true,
|
|
105
|
+
preferAllParticipantOpeningHours = true,
|
|
106
|
+
baseUrl: optBaseUrl,
|
|
107
|
+
consumer: optConsumer,
|
|
108
|
+
} = options;
|
|
109
|
+
|
|
110
|
+
const conn = ensureBlazeoHttpReady({ baseUrl: optBaseUrl, consumer: optConsumer });
|
|
111
|
+
if (!conn.ok) {
|
|
112
|
+
return {
|
|
113
|
+
calendar: null,
|
|
114
|
+
cal: null,
|
|
115
|
+
calendarView: null as UnifiedCalendarView | null,
|
|
116
|
+
openingHours: [] as any[],
|
|
117
|
+
participants: [] as any[],
|
|
118
|
+
participantsInfo: null as any,
|
|
119
|
+
allParticipantOpeningHours: null as any[] | null,
|
|
120
|
+
embeddedFromGet: [] as any[],
|
|
121
|
+
fromCalendarGet: false,
|
|
122
|
+
fromParticipantApi: false,
|
|
123
|
+
participantOpeningHoursResponse: null as any,
|
|
124
|
+
rawGet: null as any,
|
|
125
|
+
meta: { ok: false as const, reason: "missing_base_url" as const, detail: conn.error },
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const fetchParticipantsInfo = includeParticipantsInfo || includeUnifiedCalendarView;
|
|
130
|
+
const fetchAllHours = includeUnifiedCalendarView && preferAllParticipantOpeningHours;
|
|
131
|
+
|
|
132
|
+
// Calendar: `GET /Calendar/Get` is used to get the raw data first.
|
|
133
|
+
const rawRes = await (CalendarModel as any).getRaw(calendarId);
|
|
134
|
+
const payload = unwrapCalendarGetData(rawRes);
|
|
135
|
+
if (!payload) {
|
|
136
|
+
return {
|
|
137
|
+
calendar: null,
|
|
138
|
+
cal: null,
|
|
139
|
+
calendarView: null as UnifiedCalendarView | null,
|
|
140
|
+
openingHours: [],
|
|
141
|
+
participants: null,
|
|
142
|
+
participantsInfo: null,
|
|
143
|
+
allParticipantOpeningHours: null,
|
|
144
|
+
embeddedFromGet: [],
|
|
145
|
+
fromCalendarGet: false,
|
|
146
|
+
fromParticipantApi: false,
|
|
147
|
+
participantOpeningHoursResponse: null as any,
|
|
148
|
+
rawGet: rawRes,
|
|
149
|
+
meta: { ok: false as const, reason: "calendar_not_found" },
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Build the model instance manually to ensure the environment is correctly set.
|
|
154
|
+
// The static CalendarModel.get in calendar-client has a bug where it wraps env in { env: ... }.
|
|
155
|
+
const cal: any = (CalendarModel as any).create(
|
|
156
|
+
{ ...payload, calendarId },
|
|
157
|
+
{ baseUrl: conn.baseUrl, consumer: conn.consumer }
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const embedded = pickOpeningHoursArrayFromCalendarPayload(payload) ?? [];
|
|
161
|
+
let participantOpeningHoursResponse: any = null;
|
|
162
|
+
let resolved: any[] | null = embedded.length > 0 ? embedded : null;
|
|
163
|
+
|
|
164
|
+
if ((resolved == null || resolved.length === 0) && cal != null) {
|
|
165
|
+
participantOpeningHoursResponse = await cal.getParticipantOpeningHours({ calendarId });
|
|
166
|
+
const { list } = normalizeParticipantOpeningHoursResponse(participantOpeningHoursResponse);
|
|
167
|
+
if (list != null && list.length > 0) resolved = list;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const openingHours = Array.isArray(resolved) ? resolved : [];
|
|
171
|
+
|
|
172
|
+
// 2) Participants + participant info + all participant opening hours (parallel)
|
|
173
|
+
const getCalPart = (CalendarModel as any).getCalendarParticipant;
|
|
174
|
+
const participantsViaGetPromise =
|
|
175
|
+
includeUnifiedCalendarView && typeof getCalPart === "function"
|
|
176
|
+
? getCalPart.call(CalendarModel, calendarId)
|
|
177
|
+
: Promise.resolve(null);
|
|
178
|
+
|
|
179
|
+
const [participantsRaw, participantsViaGet, participantsInfoRaw, allHoursRaw] = await Promise.all([
|
|
180
|
+
cal.getParticipants(),
|
|
181
|
+
participantsViaGetPromise,
|
|
182
|
+
fetchParticipantsInfo ? cal.getParticipantsInfo() : Promise.resolve(null),
|
|
183
|
+
fetchAllHours ? cal.getAllParticipantOpeningHours() : Promise.resolve(null),
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
const participantList = mergeParticipantSnapshots(
|
|
187
|
+
unwrapModelList(participantsRaw),
|
|
188
|
+
unwrapModelList(participantsViaGet)
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const infoList = unwrapModelList(participantsInfoRaw);
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
// Merge participantList and infoList to ensure we have all members
|
|
195
|
+
const mergedParticipantsMap = new Map<string, any>();
|
|
196
|
+
|
|
197
|
+
const getAnyId = (obj: any) => obj.id ?? obj.Id ?? obj.participantId ?? obj.ParticipantId ?? obj.participant_id;
|
|
198
|
+
|
|
199
|
+
// 1. Add from standard list
|
|
200
|
+
participantList.forEach((p: any) => {
|
|
201
|
+
const id = getAnyId(p);
|
|
202
|
+
if (id) {
|
|
203
|
+
mergedParticipantsMap.set(String(id).toLowerCase(), {
|
|
204
|
+
id: id,
|
|
205
|
+
name: p.name ?? p.Name ?? p.alias ?? p.Alias ?? "Member",
|
|
206
|
+
email: p.email ?? p.Email,
|
|
207
|
+
status: p.status ?? p.Status ?? 0,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// 2. Add from info list (fallback/enrich)
|
|
213
|
+
infoList.forEach((i: any) => {
|
|
214
|
+
const id = getAnyId(i);
|
|
215
|
+
if (!id) return;
|
|
216
|
+
const key = String(id).toLowerCase();
|
|
217
|
+
const existing = mergedParticipantsMap.get(key);
|
|
218
|
+
|
|
219
|
+
if (!existing) {
|
|
220
|
+
mergedParticipantsMap.set(key, {
|
|
221
|
+
id: id,
|
|
222
|
+
name: i.alias || i.Alias || i.name || i.Name || "Member",
|
|
223
|
+
email: i.email || i.Email,
|
|
224
|
+
status: i.status ?? i.Status ?? (i.isApproved ? 1 : 0),
|
|
225
|
+
});
|
|
226
|
+
} else {
|
|
227
|
+
// Enrich existing with email/name if missing
|
|
228
|
+
if (!existing.email) existing.email = i.email || i.Email;
|
|
229
|
+
if (!existing.name || existing.name === "Member") {
|
|
230
|
+
existing.name = i.alias || i.Alias || i.name || i.Name || existing.name;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// 3. Synthetic Fallback: If openingHours reference a member we don't have, add them.
|
|
236
|
+
openingHours.forEach((oh: any) => {
|
|
237
|
+
const mid = oh.member ?? oh.Member ?? oh.participantId ?? oh.ParticipantId;
|
|
238
|
+
if (mid) {
|
|
239
|
+
const key = String(mid).toLowerCase();
|
|
240
|
+
if (!mergedParticipantsMap.has(key)) {
|
|
241
|
+
mergedParticipantsMap.set(key, {
|
|
242
|
+
id: mid,
|
|
243
|
+
name: "Member",
|
|
244
|
+
email: null,
|
|
245
|
+
status: 0,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const finalParticipantList = Array.from(mergedParticipantsMap.values());
|
|
252
|
+
const allParticipantOpeningHours = fetchAllHours ? normalizeAllParticipantOpeningHoursResult(allHoursRaw) : null;
|
|
253
|
+
|
|
254
|
+
const openingHoursForUnifiedView =
|
|
255
|
+
includeUnifiedCalendarView &&
|
|
256
|
+
preferAllParticipantOpeningHours &&
|
|
257
|
+
allParticipantOpeningHours != null &&
|
|
258
|
+
allParticipantOpeningHours.length > 0
|
|
259
|
+
? allParticipantOpeningHours
|
|
260
|
+
: openingHours;
|
|
261
|
+
|
|
262
|
+
const calendarViewRaw = includeUnifiedCalendarView
|
|
263
|
+
? buildUnifiedCalendarView(payload as any, openingHoursForUnifiedView, finalParticipantList, infoList)
|
|
264
|
+
: null;
|
|
265
|
+
|
|
266
|
+
const calendarView = calendarViewRaw ? mapToDesiredCalendarResponse(calendarViewRaw, calendarViewRaw.openingHours, calendarViewRaw.members) : null;
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
const unifiedUsedAllEndpoint =
|
|
270
|
+
includeUnifiedCalendarView &&
|
|
271
|
+
preferAllParticipantOpeningHours &&
|
|
272
|
+
allParticipantOpeningHours != null &&
|
|
273
|
+
allParticipantOpeningHours.length > 0;
|
|
274
|
+
|
|
275
|
+
if (!calendarView) return null as any;
|
|
276
|
+
|
|
277
|
+
// Attach metadata as non-enumerable properties so they don't show up in JSON.stringify
|
|
278
|
+
// but are still accessible for debugging if needed.
|
|
279
|
+
Object.defineProperties(calendarView, {
|
|
280
|
+
_cal: { value: cal, enumerable: false },
|
|
281
|
+
_participants: { value: participantList, enumerable: false },
|
|
282
|
+
_openingHours: { value: openingHours, enumerable: false },
|
|
283
|
+
_rawGet: { value: rawRes, enumerable: false },
|
|
284
|
+
_meta: {
|
|
285
|
+
value: {
|
|
286
|
+
ok: true,
|
|
287
|
+
calendarViewUsedAllParticipantOpeningHours: unifiedUsedAllEndpoint,
|
|
288
|
+
calendarViewMemberCount: calendarView.members.length,
|
|
289
|
+
calendarViewOpeningHourCount: calendarView.openingHours.length,
|
|
290
|
+
},
|
|
291
|
+
enumerable: false
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
return calendarView as any;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Single return value only: unified calendar **`calendarView`** —
|
|
300
|
+
* snapshot fields plus **`members`** (with **`participantInfo`**) plus **`openingHours`**
|
|
301
|
+
* (prefers all-participant opening hours when available). Same shape as `fetchCalendarDetails().calendarView`.
|
|
302
|
+
* Returns **`null`** if the calendar cannot be loaded (`CalendarModel.get`).
|
|
303
|
+
*/
|
|
304
|
+
export async function fetchCalendarBundle(
|
|
305
|
+
calendarId: string,
|
|
306
|
+
connection?: { baseUrl?: string; consumer?: string }
|
|
307
|
+
): Promise<UnifiedCalendarView | null> {
|
|
308
|
+
const d = await fetchCalendarDetails(calendarId, {
|
|
309
|
+
includeUnifiedCalendarView: true,
|
|
310
|
+
includeParticipantsInfo: true,
|
|
311
|
+
preferAllParticipantOpeningHours: true,
|
|
312
|
+
...connection,
|
|
313
|
+
});
|
|
314
|
+
if (!d) return null;
|
|
315
|
+
return d;
|
|
316
|
+
}
|