@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
|
@@ -2,11 +2,16 @@ import { useMemo, useState } from "react";
|
|
|
2
2
|
import {
|
|
3
3
|
CalendarModel,
|
|
4
4
|
deleteCalendarAsync,
|
|
5
|
+
ensureBlazeoHttpReady,
|
|
5
6
|
fetchCalendarDetails,
|
|
6
7
|
updateCalendarAsync,
|
|
7
8
|
} from "appointment-client";
|
|
8
9
|
import { getSnapshot, isStateTreeNode } from "mobx-state-tree";
|
|
9
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
configureBlazeoFromEffective,
|
|
12
|
+
useBlazeoConnection,
|
|
13
|
+
} from "./BlazeoConnectionSettings.jsx";
|
|
14
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
10
15
|
import { getExampleCalendarBOInput } from "./CreateCalendarTab.jsx";
|
|
11
16
|
|
|
12
17
|
function pick(row, ...keys) {
|
|
@@ -32,7 +37,7 @@ function explainFetchFailure(err, configuredBaseUrl) {
|
|
|
32
37
|
msg === "Failed to fetch" ||
|
|
33
38
|
msg === "Load failed" ||
|
|
34
39
|
(err instanceof TypeError && (/fetch/i.test(msg) || /network/i.test(msg)));
|
|
35
|
-
if (!isNetwork) return msg;
|
|
40
|
+
if (!isNetwork) return mapBlazeoDemoError(msg);
|
|
36
41
|
|
|
37
42
|
const isRemote =
|
|
38
43
|
configuredBaseUrl &&
|
|
@@ -54,6 +59,7 @@ function explainFetchFailure(err, configuredBaseUrl) {
|
|
|
54
59
|
|
|
55
60
|
/** Opening hours list from `calendarView`, embedded `calendar.openingHours`, or legacy `openingHours`. */
|
|
56
61
|
function pickOpeningHoursListFromBundle(parsed) {
|
|
62
|
+
if (Array.isArray(parsed?.openingHours)) return parsed.openingHours;
|
|
57
63
|
const fromView = parsed?.calendarView?.openingHours;
|
|
58
64
|
if (Array.isArray(fromView) && fromView.length > 0) return fromView;
|
|
59
65
|
const fromCal = parsed?.calendar?.openingHours;
|
|
@@ -177,7 +183,7 @@ function calendarSnapshotToUpdatePayload(snap) {
|
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
export function FetchCalendarTab() {
|
|
180
|
-
const { effective } = useBlazeoConnection();
|
|
186
|
+
const { effective, connectionOpts } = useBlazeoConnection();
|
|
181
187
|
const [calendarId, setCalendarId] = useState("");
|
|
182
188
|
const [companyKey, setCompanyKey] = useState("");
|
|
183
189
|
const [busy, setBusy] = useState(false);
|
|
@@ -216,52 +222,48 @@ export function FetchCalendarTab() {
|
|
|
216
222
|
}
|
|
217
223
|
if (!ensureBaseConfigured()) return;
|
|
218
224
|
configureBlazeoFromEffective(effective);
|
|
225
|
+
ensureBlazeoHttpReady({
|
|
226
|
+
baseUrl: effective.baseUrl,
|
|
227
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
228
|
+
});
|
|
219
229
|
|
|
220
230
|
setBusy(true);
|
|
221
231
|
try {
|
|
222
|
-
const details = await fetchCalendarDetails(id
|
|
232
|
+
const details = await fetchCalendarDetails(id, {
|
|
233
|
+
...connectionOpts,
|
|
234
|
+
baseUrl: effective.baseUrl,
|
|
235
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const meta = details?._meta ?? details?.meta;
|
|
239
|
+
if (meta && !meta.ok && meta.reason === "missing_base_url") {
|
|
240
|
+
setError(mapBlazeoDemoError(meta.detail ?? ""));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
223
243
|
|
|
224
|
-
if (details
|
|
244
|
+
if (!details) {
|
|
245
|
+
ensureBlazeoHttpReady({
|
|
246
|
+
baseUrl: effective.baseUrl,
|
|
247
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
248
|
+
});
|
|
225
249
|
const raw = await CalendarModel.getRaw(id);
|
|
226
250
|
setNote("CalendarModel.get returned null. Showing CalendarModel.getRaw only.");
|
|
227
251
|
setOutput(toDisplayJson(raw));
|
|
228
252
|
return;
|
|
229
253
|
}
|
|
230
254
|
|
|
231
|
-
const snap = getSnapshot(details.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
: details.participantsInfo ?? null,
|
|
244
|
-
__openingHoursMeta: {
|
|
245
|
-
fromCalendarGet: details.fromCalendarGet,
|
|
246
|
-
fromParticipantApi: details.fromParticipantApi,
|
|
247
|
-
calendarViewUsedAllParticipantOpeningHours: details.meta?.calendarViewUsedAllParticipantOpeningHours,
|
|
248
|
-
embeddedCount: details.embeddedFromGet?.length ?? 0,
|
|
249
|
-
resolvedCount: details.openingHours?.length ?? 0,
|
|
250
|
-
},
|
|
251
|
-
meta: details.meta,
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
setOutput(toDisplayJson(payload));
|
|
255
|
-
setNote(
|
|
256
|
-
(details.calendarView
|
|
257
|
-
? "Use `calendarView`: one object (calendar + nested participants with openingHours). "
|
|
258
|
-
: "") +
|
|
259
|
-
(details.fromCalendarGet
|
|
260
|
-
? "Opening hours: embedded on GET /Calendar/Get; participants from GET /Calendar/Participant/All. Single bundle in output."
|
|
261
|
-
: details.fromParticipantApi
|
|
262
|
-
? "Opening hours: GET /Calendar/Participant/OpeningHours/Get; participants from GET /Calendar/Participant/All. Single bundle in output."
|
|
263
|
-
: "Calendar loaded; opening hours empty from both embed + participant API; participants from GET /Calendar/Participant/All.")
|
|
264
|
-
);
|
|
255
|
+
const snap = details._cal ? getSnapshot(details._cal) : null;
|
|
256
|
+
if (snap) {
|
|
257
|
+
setLastFetchUpdatePayload(JSON.stringify(calendarSnapshotToUpdatePayload(snap), null, 2));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// If it's the new flat response, just use it directly for output.
|
|
261
|
+
setOutput(toDisplayJson(details));
|
|
262
|
+
|
|
263
|
+
// We can still try to extract meta for the UI if needed
|
|
264
|
+
if (meta) {
|
|
265
|
+
setNote(`Source: ${meta.calendarViewUsedAllParticipantOpeningHours ? "AllParticipantOpeningHours" : "Embedded/ParticipantApi"}`);
|
|
266
|
+
}
|
|
265
267
|
} catch (err) {
|
|
266
268
|
setError(explainFetchFailure(err, effective.baseUrl));
|
|
267
269
|
} finally {
|
|
@@ -282,6 +284,10 @@ export function FetchCalendarTab() {
|
|
|
282
284
|
}
|
|
283
285
|
if (!ensureBaseConfigured()) return;
|
|
284
286
|
configureBlazeoFromEffective(effective);
|
|
287
|
+
ensureBlazeoHttpReady({
|
|
288
|
+
baseUrl: effective.baseUrl,
|
|
289
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
290
|
+
});
|
|
285
291
|
|
|
286
292
|
setBusy(true);
|
|
287
293
|
try {
|
|
@@ -298,7 +304,11 @@ export function FetchCalendarTab() {
|
|
|
298
304
|
const id = c.calendarId ?? String(c.id ?? "");
|
|
299
305
|
if (!id) return { calendar: getSnapshot(c), openingHours: [], meta: { error: "no id" } };
|
|
300
306
|
try {
|
|
301
|
-
const b = await fetchCalendarDetails(id
|
|
307
|
+
const b = await fetchCalendarDetails(id, {
|
|
308
|
+
...connectionOpts,
|
|
309
|
+
baseUrl: effective.baseUrl,
|
|
310
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
311
|
+
});
|
|
302
312
|
return {
|
|
303
313
|
calendarView: b.calendarView,
|
|
304
314
|
calendar: b.calendar ?? getSnapshot(c),
|
|
@@ -369,6 +379,10 @@ export function FetchCalendarTab() {
|
|
|
369
379
|
setMutateOutput("");
|
|
370
380
|
if (!ensureBaseConfigured()) return;
|
|
371
381
|
configureBlazeoFromEffective(effective);
|
|
382
|
+
ensureBlazeoHttpReady({
|
|
383
|
+
baseUrl: effective.baseUrl,
|
|
384
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
385
|
+
});
|
|
372
386
|
let payload;
|
|
373
387
|
try {
|
|
374
388
|
payload = JSON.parse(updateJson);
|
|
@@ -378,7 +392,11 @@ export function FetchCalendarTab() {
|
|
|
378
392
|
}
|
|
379
393
|
setBusy(true);
|
|
380
394
|
try {
|
|
381
|
-
const result = await updateCalendarAsync(payload, {
|
|
395
|
+
const result = await updateCalendarAsync(payload, {
|
|
396
|
+
...connectionOpts,
|
|
397
|
+
baseUrl: effective.baseUrl,
|
|
398
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
399
|
+
});
|
|
382
400
|
if (result.ok) {
|
|
383
401
|
setMutateNote("updateCalendarAsync → POST /Calendar/Event/Update");
|
|
384
402
|
setMutateOutput(
|
|
@@ -392,7 +410,7 @@ export function FetchCalendarTab() {
|
|
|
392
410
|
)
|
|
393
411
|
);
|
|
394
412
|
} else {
|
|
395
|
-
setError(result.error);
|
|
413
|
+
setError(mapBlazeoDemoError(result.error));
|
|
396
414
|
if (result.apiResponse != null) {
|
|
397
415
|
setMutateOutput(JSON.stringify(result.apiResponse, null, 2));
|
|
398
416
|
}
|
|
@@ -414,6 +432,10 @@ export function FetchCalendarTab() {
|
|
|
414
432
|
}
|
|
415
433
|
if (!ensureBaseConfigured()) return;
|
|
416
434
|
configureBlazeoFromEffective(effective);
|
|
435
|
+
ensureBlazeoHttpReady({
|
|
436
|
+
baseUrl: effective.baseUrl,
|
|
437
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
438
|
+
});
|
|
417
439
|
if (
|
|
418
440
|
!window.confirm(
|
|
419
441
|
`Delete calendar "${id}"?\n\nThis calls GET /Calendar/Remove (cannot be undone on the server).`
|
|
@@ -423,12 +445,16 @@ export function FetchCalendarTab() {
|
|
|
423
445
|
}
|
|
424
446
|
setBusy(true);
|
|
425
447
|
try {
|
|
426
|
-
const result = await deleteCalendarAsync(id, {
|
|
448
|
+
const result = await deleteCalendarAsync(id, {
|
|
449
|
+
...connectionOpts,
|
|
450
|
+
baseUrl: effective.baseUrl,
|
|
451
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
452
|
+
});
|
|
427
453
|
if (result.ok) {
|
|
428
454
|
setMutateNote("deleteCalendarAsync → GET /Calendar/Remove");
|
|
429
455
|
setMutateOutput(JSON.stringify({ calendarId: id, apiResponse: result.apiResponse ?? null }, null, 2));
|
|
430
456
|
} else {
|
|
431
|
-
setError(result.error);
|
|
457
|
+
setError(mapBlazeoDemoError(result.error));
|
|
432
458
|
if (result.apiResponse != null) {
|
|
433
459
|
setMutateOutput(JSON.stringify(result.apiResponse, null, 2));
|
|
434
460
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import { fetchCalendarWithOpeningHours } from "appointment-client";
|
|
2
|
+
import { ensureBlazeoHttpReady, fetchCalendarWithOpeningHours } from "appointment-client";
|
|
3
3
|
import {
|
|
4
4
|
configureBlazeoFromEffective,
|
|
5
5
|
useBlazeoConnection,
|
|
6
6
|
} from "./BlazeoConnectionSettings.jsx";
|
|
7
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
7
8
|
|
|
8
9
|
export function OpeningHoursTab() {
|
|
9
|
-
const { effective } = useBlazeoConnection();
|
|
10
|
+
const { effective, connectionOpts } = useBlazeoConnection();
|
|
10
11
|
const [calendarId, setCalendarId] = useState("");
|
|
11
12
|
const [busy, setBusy] = useState(false);
|
|
12
13
|
const [error, setError] = useState("");
|
|
@@ -26,12 +27,20 @@ export function OpeningHoursTab() {
|
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
configureBlazeoFromEffective(effective);
|
|
30
|
+
ensureBlazeoHttpReady({
|
|
31
|
+
baseUrl: effective.baseUrl,
|
|
32
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
33
|
+
});
|
|
29
34
|
setBusy(true);
|
|
30
35
|
try {
|
|
31
|
-
const res = await fetchCalendarWithOpeningHours(id
|
|
36
|
+
const res = await fetchCalendarWithOpeningHours(id, {
|
|
37
|
+
...connectionOpts,
|
|
38
|
+
baseUrl: effective.baseUrl,
|
|
39
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
40
|
+
});
|
|
32
41
|
setOutput(JSON.stringify(res, null, 2));
|
|
33
42
|
} catch (err) {
|
|
34
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
43
|
+
setError(mapBlazeoDemoError(err instanceof Error ? err.message : String(err)));
|
|
35
44
|
} finally {
|
|
36
45
|
setBusy(false);
|
|
37
46
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import { CalendarParticipantModel } from "appointment-client";
|
|
2
|
+
import { CalendarParticipantModel, ensureBlazeoHttpReady } from "appointment-client";
|
|
3
3
|
import { configureBlazeoFromEffective, useBlazeoConnection } from "./BlazeoConnectionSettings.jsx";
|
|
4
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
4
5
|
|
|
5
6
|
export function ParticipantInfoTab() {
|
|
6
|
-
const { effective } = useBlazeoConnection();
|
|
7
|
+
const { effective, connectionOpts } = useBlazeoConnection();
|
|
7
8
|
const [calendarId, setCalendarId] = useState("");
|
|
8
9
|
const [busy, setBusy] = useState(false);
|
|
9
10
|
const [error, setError] = useState("");
|
|
@@ -18,12 +19,16 @@ export function ParticipantInfoTab() {
|
|
|
18
19
|
if (!effective.baseUrl) return setError("Set Base URL in the connection card above.");
|
|
19
20
|
|
|
20
21
|
configureBlazeoFromEffective(effective);
|
|
22
|
+
ensureBlazeoHttpReady({
|
|
23
|
+
baseUrl: effective.baseUrl,
|
|
24
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
25
|
+
});
|
|
21
26
|
setBusy(true);
|
|
22
27
|
try {
|
|
23
28
|
const info = await CalendarParticipantModel.getInfoByCalendar(id);
|
|
24
29
|
setOutput(JSON.stringify({ calendarId: id, count: Array.isArray(info) ? info.length : 0, info }, null, 2));
|
|
25
30
|
} catch (err) {
|
|
26
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
31
|
+
setError(mapBlazeoDemoError(err instanceof Error ? err.message : String(err)));
|
|
27
32
|
} finally {
|
|
28
33
|
setBusy(false);
|
|
29
34
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import { getParticipantOpeningHours } from "appointment-client";
|
|
2
|
+
import { ensureBlazeoHttpReady, getParticipantOpeningHours } from "appointment-client";
|
|
3
3
|
import { configureBlazeoFromEffective, useBlazeoConnection } from "./BlazeoConnectionSettings.jsx";
|
|
4
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
4
5
|
|
|
5
6
|
export function ParticipantOpeningHoursTab() {
|
|
6
|
-
const { effective } = useBlazeoConnection();
|
|
7
|
+
const { effective, connectionOpts } = useBlazeoConnection();
|
|
7
8
|
const [calendarId, setCalendarId] = useState("");
|
|
8
9
|
const [busy, setBusy] = useState(false);
|
|
9
10
|
const [error, setError] = useState("");
|
|
@@ -18,13 +19,22 @@ export function ParticipantOpeningHoursTab() {
|
|
|
18
19
|
if (!effective.baseUrl) return setError("Set Base URL in the connection card above.");
|
|
19
20
|
|
|
20
21
|
configureBlazeoFromEffective(effective);
|
|
22
|
+
ensureBlazeoHttpReady({
|
|
23
|
+
baseUrl: effective.baseUrl,
|
|
24
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
25
|
+
});
|
|
21
26
|
setBusy(true);
|
|
22
27
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
const result = await getParticipantOpeningHours(id, {
|
|
29
|
+
...connectionOpts,
|
|
30
|
+
baseUrl: effective.baseUrl,
|
|
31
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
32
|
+
});
|
|
33
|
+
|
|
26
34
|
if (!result.meta.ok) {
|
|
27
|
-
setError(
|
|
35
|
+
setError(
|
|
36
|
+
mapBlazeoDemoError(result.meta.error || `Failed to fetch: ${result.meta.reason}`)
|
|
37
|
+
);
|
|
28
38
|
if (result.raw) setOutput(JSON.stringify(result.raw, null, 2));
|
|
29
39
|
return;
|
|
30
40
|
}
|
|
@@ -37,7 +47,7 @@ export function ParticipantOpeningHoursTab() {
|
|
|
37
47
|
meta: result.meta
|
|
38
48
|
}, null, 2));
|
|
39
49
|
} catch (err) {
|
|
40
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
50
|
+
setError(mapBlazeoDemoError(err instanceof Error ? err.message : String(err)));
|
|
41
51
|
} finally {
|
|
42
52
|
setBusy(false);
|
|
43
53
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useMemo, useState } from "react";
|
|
2
|
-
import { getExampleParticipants, getParticipants } from "appointment-client";
|
|
2
|
+
import { ensureBlazeoHttpReady, getExampleParticipants, getParticipants } from "appointment-client";
|
|
3
3
|
import { getSnapshot, isStateTreeNode } from "mobx-state-tree";
|
|
4
4
|
import {
|
|
5
5
|
configureBlazeoFromEffective,
|
|
6
6
|
useBlazeoConnection,
|
|
7
7
|
} from "./BlazeoConnectionSettings.jsx";
|
|
8
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
8
9
|
|
|
9
10
|
function toDisplayJson(value) {
|
|
10
11
|
if (value == null) return JSON.stringify(value, null, 2);
|
|
@@ -20,7 +21,7 @@ function toDisplayJson(value) {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export function ParticipantTab() {
|
|
23
|
-
const { effective } = useBlazeoConnection();
|
|
24
|
+
const { effective, connectionOpts } = useBlazeoConnection();
|
|
24
25
|
const example = useMemo(() => getExampleParticipants(), []);
|
|
25
26
|
const [calendarId, setCalendarId] = useState("");
|
|
26
27
|
const [busy, setBusy] = useState(false);
|
|
@@ -41,12 +42,20 @@ export function ParticipantTab() {
|
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
44
|
configureBlazeoFromEffective(effective);
|
|
45
|
+
ensureBlazeoHttpReady({
|
|
46
|
+
baseUrl: effective.baseUrl,
|
|
47
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
48
|
+
});
|
|
44
49
|
setBusy(true);
|
|
45
50
|
try {
|
|
46
|
-
const res = await getParticipants(id
|
|
51
|
+
const res = await getParticipants(id, {
|
|
52
|
+
...connectionOpts,
|
|
53
|
+
baseUrl: effective.baseUrl,
|
|
54
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
55
|
+
});
|
|
47
56
|
setOutput(toDisplayJson(res));
|
|
48
57
|
} catch (err) {
|
|
49
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
58
|
+
setError(mapBlazeoDemoError(err instanceof Error ? err.message : String(err)));
|
|
50
59
|
} finally {
|
|
51
60
|
setBusy(false);
|
|
52
61
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs once before React mounts. Order:
|
|
3
|
+
* 1. `blazeoClientDefaults.ts` (via applyBlazeoClientConfig) — optional file defaults
|
|
4
|
+
* 2. `VITE_BLAZEO_BASE_URL` / `VITE_BLAZEO_CONSUMER` — overrides when set
|
|
5
|
+
*
|
|
6
|
+
* Uses {@link pushBlazeoConnection} so calendar-client is configured the same way as the UI card.
|
|
7
|
+
*/
|
|
8
|
+
import { applyBlazeoClientConfig } from "appointment-client";
|
|
9
|
+
import { pushBlazeoConnection } from "./blazeoPushConnection.js";
|
|
10
|
+
|
|
11
|
+
function normalizeBase(u) {
|
|
12
|
+
const t = (u ?? "").trim();
|
|
13
|
+
if (!t) return "";
|
|
14
|
+
return t.replace(/\/+$/, "");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function bootstrapBlazeoClient() {
|
|
18
|
+
debugger;
|
|
19
|
+
applyBlazeoClientConfig();
|
|
20
|
+
|
|
21
|
+
const envBase = normalizeBase(import.meta.env.VITE_BLAZEO_BASE_URL ?? "");
|
|
22
|
+
const envConsumer = (import.meta.env.VITE_BLAZEO_CONSUMER ?? "").trim();
|
|
23
|
+
|
|
24
|
+
if (!envBase) return;
|
|
25
|
+
|
|
26
|
+
pushBlazeoConnection({
|
|
27
|
+
baseUrl: envBase,
|
|
28
|
+
...(envConsumer ? { consumer: envConsumer } : {}),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps low-level Blazeo / calendar-client errors to actionable copy for this sample.
|
|
3
|
+
*/
|
|
4
|
+
export function mapBlazeoDemoError(message) {
|
|
5
|
+
const m = String(message ?? "");
|
|
6
|
+
if (
|
|
7
|
+
/Model env requires baseUrl|requires baseUrl\. Call configure|baseUrl is missing|Blazeo base URL is not set/i.test(
|
|
8
|
+
m
|
|
9
|
+
)
|
|
10
|
+
) {
|
|
11
|
+
return "Blazeo Base URL is not set for API calls. Enter **Base URL** (and optional Consumer) in the **Blazeo connection** card at the top of this page, or set `blazeoClientConfig` / `VITE_BLAZEO_BASE_URL`.";
|
|
12
|
+
}
|
|
13
|
+
return m;
|
|
14
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ensureBlazeoHttpReady, initializeAppointmentClient } from "appointment-client";
|
|
2
|
+
import { configure } from "@blazeo.com/calendar-client";
|
|
3
|
+
|
|
4
|
+
function normalizeBase(u) {
|
|
5
|
+
const t = (u ?? "").trim();
|
|
6
|
+
if (!t) return "";
|
|
7
|
+
return t.replace(/\/+$/, "");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Applies Base URL from the connection card. With Vite `resolve.alias` pointing
|
|
12
|
+
* `@blazeo.com/calendar-client` at one file, `configure` here matches `CalendarModel`'s store.
|
|
13
|
+
* `initializeAppointmentClient` + `ensureBlazeoHttpReady` keep `appointment-client` helpers aligned.
|
|
14
|
+
*/
|
|
15
|
+
export function pushBlazeoConnection(effective) {
|
|
16
|
+
const baseUrl = normalizeBase(effective?.baseUrl ?? "");
|
|
17
|
+
if (!baseUrl) return;
|
|
18
|
+
const consumer = (effective?.consumer ?? "").trim() || undefined;
|
|
19
|
+
const cfg = { baseUrl, ...(consumer ? { consumer } : {}) };
|
|
20
|
+
initializeAppointmentClient(cfg);
|
|
21
|
+
configure(cfg);
|
|
22
|
+
ensureBlazeoHttpReady(cfg);
|
|
23
|
+
}
|
package/sample/src/main.jsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { applyBlazeoClientConfig } from "appointment-client";
|
|
2
1
|
import { StrictMode } from "react";
|
|
3
2
|
import { createRoot } from "react-dom/client";
|
|
4
3
|
import { App } from "./App2.jsx";
|
|
4
|
+
import { bootstrapBlazeoClient } from "./blazeoBootstrap.js";
|
|
5
5
|
import "./style.css";
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/** Blazeo: `blazeoClientDefaults.ts` + optional `VITE_BLAZEO_*` (.env) → `configure()` for calendar-client. */
|
|
8
|
+
bootstrapBlazeoClient();
|
|
9
9
|
|
|
10
10
|
const rootEl = document.getElementById("app");
|
|
11
11
|
if (!rootEl) {
|
package/sample/vite.config.js
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import react from "@vitejs/plugin-react";
|
|
2
2
|
import { defineConfig, loadEnv } from "vite";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
4
5
|
import { fileURLToPath } from "url";
|
|
5
6
|
|
|
6
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
8
|
|
|
9
|
+
/** Single physical entry for `@blazeo.com/calendar-client` (hoisted or nested under `file:..` parent). */
|
|
10
|
+
function calendarClientEntry() {
|
|
11
|
+
const candidates = [
|
|
12
|
+
path.resolve(__dirname, "node_modules/@blazeo.com/calendar-client/dist/index.mjs"),
|
|
13
|
+
path.resolve(__dirname, "../node_modules/@blazeo.com/calendar-client/dist/index.mjs"),
|
|
14
|
+
];
|
|
15
|
+
for (const p of candidates) {
|
|
16
|
+
if (fs.existsSync(p)) return p;
|
|
17
|
+
}
|
|
18
|
+
return candidates[0];
|
|
19
|
+
}
|
|
20
|
+
|
|
8
21
|
export default defineConfig(({ mode }) => {
|
|
9
22
|
const env = loadEnv(mode, path.resolve(__dirname, "."), "");
|
|
10
23
|
const proxyTarget = (env.VITE_DEV_PROXY_TARGET ?? "").trim().replace(/\/+$/, "");
|
|
@@ -26,13 +39,14 @@ export default defineConfig(({ mode }) => {
|
|
|
26
39
|
plugins: [react()],
|
|
27
40
|
/** Ensure one instance so `configure()` and models share the same store. */
|
|
28
41
|
resolve: {
|
|
29
|
-
|
|
42
|
+
// `file:..` appointment-client is symlinked; `true` can resolve the same dep twice → two `getConfig()` stores.
|
|
43
|
+
preserveSymlinks: false,
|
|
30
44
|
dedupe: ["@blazeo.com/calendar-client", "mobx", "mobx-state-tree"],
|
|
31
45
|
alias: {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
),
|
|
46
|
+
// Ensure Vite/Rollup can always resolve the local workspace package.
|
|
47
|
+
// (Avoids `Failed to resolve import "appointment-client"` during dev/build.)
|
|
48
|
+
"appointment-client": path.resolve(__dirname, "../dist/index.js"),
|
|
49
|
+
"@blazeo.com/calendar-client": calendarClientEntry(),
|
|
36
50
|
},
|
|
37
51
|
},
|
|
38
52
|
optimizeDeps: {
|
|
@@ -56,6 +56,7 @@ export interface UnifiedCalendarMember {
|
|
|
56
56
|
status: number | null;
|
|
57
57
|
/** Full row from Participants/GetInfo (plain object). */
|
|
58
58
|
participantInfo?: Record<string, unknown> | null;
|
|
59
|
+
__typename?: string;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
export interface UnifiedParticipantWithHours extends UnifiedCalendarMember {
|
|
@@ -63,13 +64,20 @@ export interface UnifiedParticipantWithHours extends UnifiedCalendarMember {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
export interface UnifiedOpeningHourRow {
|
|
67
|
+
id?: number | string;
|
|
68
|
+
createdOn?: string;
|
|
69
|
+
modifiedOn?: string;
|
|
66
70
|
member: number | string;
|
|
71
|
+
openingHourId?: string;
|
|
72
|
+
calendarId?: string;
|
|
73
|
+
participantId?: string;
|
|
67
74
|
days: string[];
|
|
68
75
|
startHour: number;
|
|
69
76
|
startMinute: number;
|
|
70
77
|
endHour: number;
|
|
71
78
|
endMinute: number;
|
|
72
79
|
off: boolean;
|
|
80
|
+
__typename?: string;
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
function dayOrderIndex(d: string): number {
|
|
@@ -253,6 +261,7 @@ export function buildUnifiedCalendarView(
|
|
|
253
261
|
email: email ?? null,
|
|
254
262
|
status: deriveMemberStatus({}, inf),
|
|
255
263
|
participantInfo: participantInfoPlain,
|
|
264
|
+
__typename: "Member",
|
|
256
265
|
});
|
|
257
266
|
}
|
|
258
267
|
}
|
|
@@ -277,13 +286,20 @@ export function buildUnifiedCalendarView(
|
|
|
277
286
|
const off = Boolean(pick(row, "off", "Off"));
|
|
278
287
|
|
|
279
288
|
openingHours.push({
|
|
289
|
+
id: pick(row, "id", "Id") ?? 0,
|
|
290
|
+
createdOn: pick(row, "createdOn", "CreatedOn", "created_on") ?? "0001-01-01T00:00:00.000Z",
|
|
291
|
+
modifiedOn: pick(row, "modifiedOn", "ModifiedOn", "modified_on") ?? "0001-01-01T00:00:00.000Z",
|
|
280
292
|
member: memberId,
|
|
293
|
+
openingHourId: pick(row, "openingHourId", "OpeningHourId", "opening_hour_id") ?? "",
|
|
294
|
+
calendarId: pick(row, "calendarId", "CalendarId", "calendar_id") ?? "",
|
|
295
|
+
participantId: pick(row, "participantId", "ParticipantId", "participant_id") ?? "",
|
|
281
296
|
days,
|
|
282
297
|
startHour,
|
|
283
298
|
startMinute,
|
|
284
299
|
endHour,
|
|
285
300
|
endMinute,
|
|
286
301
|
off,
|
|
302
|
+
__typename: "OpeningHour",
|
|
287
303
|
});
|
|
288
304
|
}
|
|
289
305
|
|
|
@@ -294,6 +310,7 @@ export function buildUnifiedCalendarView(
|
|
|
294
310
|
members,
|
|
295
311
|
openingHours,
|
|
296
312
|
participants: buildNestedParticipants(members, openingHours),
|
|
313
|
+
__typename: "Calendar",
|
|
297
314
|
} as UnifiedCalendarView;
|
|
298
315
|
|
|
299
316
|
return view;
|
|
@@ -306,17 +323,23 @@ function buildNestedParticipants(
|
|
|
306
323
|
members: UnifiedCalendarMember[],
|
|
307
324
|
openingHours: UnifiedOpeningHourRow[]
|
|
308
325
|
): UnifiedParticipantWithHours[] {
|
|
309
|
-
|
|
310
|
-
|
|
326
|
+
const nested: UnifiedParticipantWithHours[] = [];
|
|
327
|
+
members.forEach((m) => {
|
|
328
|
+
const hoursForThisMember = openingHours.filter((oh) => {
|
|
311
329
|
const mid = String(oh.member).trim().toLowerCase();
|
|
312
330
|
const pid = String(m.id).trim().toLowerCase();
|
|
313
331
|
return mid === pid;
|
|
314
332
|
});
|
|
315
333
|
// Remove the 'member' field from the nested opening hours as it's redundant.
|
|
316
|
-
const nestedHours =
|
|
317
|
-
|
|
334
|
+
const nestedHours = hoursForThisMember.map(({ member, ...rest }) => ({
|
|
335
|
+
...rest,
|
|
336
|
+
__typename: "OpeningHour"
|
|
337
|
+
}));
|
|
338
|
+
nested.push({
|
|
318
339
|
...m,
|
|
319
340
|
openingHours: nestedHours,
|
|
320
|
-
|
|
341
|
+
__typename: "Member",
|
|
342
|
+
} as UnifiedParticipantWithHours);
|
|
321
343
|
});
|
|
344
|
+
return nested;
|
|
322
345
|
}
|