@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.
Files changed (60) hide show
  1. package/blazeo.com-appointment-client-1.0.7.tgz +0 -0
  2. package/dist/calendar/buildUnifiedCalendarView.d.ts +8 -0
  3. package/dist/calendar/buildUnifiedCalendarView.js +20 -5
  4. package/dist/calendar/fetchCalendarDetails.d.ts +8 -40
  5. package/dist/calendar/fetchCalendarDetails.js +116 -47
  6. package/dist/calendar/fetchCalendarWithOpeningHours.d.ts +1 -10
  7. package/dist/calendar/fetchCalendarWithOpeningHours.js +34 -15
  8. package/dist/calendar/getAllParticipantOpeningHours.d.ts +4 -1
  9. package/dist/calendar/getAllParticipantOpeningHours.js +6 -1
  10. package/dist/calendar/getOpeningHours.d.ts +4 -1
  11. package/dist/calendar/getOpeningHours.js +2 -2
  12. package/dist/calendar/getParticipantOpeningHours.js +9 -4
  13. package/dist/calendar/getParticipants.d.ts +4 -1
  14. package/dist/calendar/getParticipants.js +6 -1
  15. package/dist/calendar/mapToDesiredResponse.d.ts +70 -0
  16. package/dist/calendar/mapToDesiredResponse.js +99 -0
  17. package/dist/config/applyBlazeoDefaults.js +3 -2
  18. package/dist/config/blazeoClientDefaults.js +2 -2
  19. package/dist/config/ensureBlazeoHttpReady.d.ts +17 -0
  20. package/dist/config/ensureBlazeoHttpReady.js +31 -0
  21. package/dist/config/initializeAppointmentClient.d.ts +4 -0
  22. package/dist/config/initializeAppointmentClient.js +9 -3
  23. package/dist/config/syncBlazeoConnection.d.ts +6 -0
  24. package/dist/config/syncBlazeoConnection.js +18 -0
  25. package/dist/index.d.ts +4 -1
  26. package/dist/index.js +3 -1
  27. package/package.json +1 -1
  28. package/sample/.env.example +5 -0
  29. package/sample/package-lock.json +1 -1
  30. package/sample/src/AllParticipantOpeningHoursTab.jsx +13 -4
  31. package/sample/src/App2.jsx +22 -2
  32. package/sample/src/AvailabilityTab.jsx +8 -3
  33. package/sample/src/BlazeoConnectionSettings.jsx +16 -15
  34. package/sample/src/CreateCalendarTab.jsx +23 -6
  35. package/sample/src/EventTab.jsx +31 -8
  36. package/sample/src/FetchCalendarTab.jsx +70 -44
  37. package/sample/src/OpeningHoursTab.jsx +13 -4
  38. package/sample/src/ParticipantInfoTab.jsx +8 -3
  39. package/sample/src/ParticipantOpeningHoursTab.jsx +17 -7
  40. package/sample/src/ParticipantTab.jsx +13 -4
  41. package/sample/src/blazeoBootstrap.js +30 -0
  42. package/sample/src/blazeoDemoError.js +14 -0
  43. package/sample/src/blazeoPushConnection.js +23 -0
  44. package/sample/src/main.jsx +3 -3
  45. package/sample/vite.config.js +19 -5
  46. package/src/calendar/buildUnifiedCalendarView.ts +28 -5
  47. package/src/calendar/fetchCalendarDetails.ts +316 -226
  48. package/src/calendar/fetchCalendarWithOpeningHours.ts +130 -99
  49. package/src/calendar/getAllParticipantOpeningHours.ts +9 -1
  50. package/src/calendar/getOpeningHours.ts +2 -2
  51. package/src/calendar/getParticipantOpeningHours.ts +14 -5
  52. package/src/calendar/getParticipants.ts +9 -1
  53. package/src/calendar/mapToDesiredResponse.ts +104 -0
  54. package/src/config/applyBlazeoDefaults.ts +3 -2
  55. package/src/config/blazeoClientDefaults.ts +2 -2
  56. package/src/config/ensureBlazeoHttpReady.ts +41 -0
  57. package/src/config/initializeAppointmentClient.ts +9 -3
  58. package/src/config/syncBlazeoConnection.ts +19 -0
  59. package/src/index.ts +7 -1
  60. 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 { configureBlazeoFromEffective, useBlazeoConnection } from "./BlazeoConnectionSettings.jsx";
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.cal == null) {
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.cal);
232
- setLastFetchUpdatePayload(JSON.stringify(calendarSnapshotToUpdatePayload(snap), null, 2));
233
-
234
- const payload = {
235
- calendarView: details.calendarView,
236
- calendar: details.calendar,
237
- openingHours: details.openingHours,
238
- allParticipantOpeningHours: details.allParticipantOpeningHours,
239
- openingHoursApiResponse: details.participantOpeningHoursResponse ?? null,
240
- participants: (details.participants ?? []).map((p) => (isStateTreeNode(p) ? getSnapshot(p) : p)),
241
- participantsInfo: Array.isArray(details.participantsInfo)
242
- ? details.participantsInfo.map((p) => (isStateTreeNode(p) ? getSnapshot(p) : p))
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
- debugger;
24
- const result = await getParticipantOpeningHours(id);
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(result.meta.error || `Failed to fetch: ${result.meta.reason}`);
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
+ }
@@ -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
- /** Apply file defaults (`blazeoClientDefaults.ts`) before React mounts. */
8
- applyBlazeoClientConfig();
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) {
@@ -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
- preserveSymlinks: true,
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
- "@blazeo.com/calendar-client": path.resolve(
33
- __dirname,
34
- "../node_modules/@blazeo.com/calendar-client/dist/index.mjs"
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
- return members.map((m) => {
310
- const hours = openingHours.filter((oh) => {
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 = hours.map(({ member, ...rest }) => rest as any);
317
- return {
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
  }