@blazeo.com/appointment-client 1.0.5 → 1.0.6

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 (103) hide show
  1. package/blazeo.com-appointment-client-1.0.6.tgz +0 -0
  2. package/dist/calendar/blazeoCalendarRelationMethods.d.ts +4 -36
  3. package/dist/calendar/blazeoCalendarRelationMethods.js +0 -1
  4. package/dist/calendar/buildUnifiedCalendarView.d.ts +31 -0
  5. package/dist/calendar/buildUnifiedCalendarView.js +265 -0
  6. package/dist/calendar/calendarCreation.d.ts +27 -0
  7. package/dist/calendar/calendarCreation.js +167 -0
  8. package/dist/calendar/calendarCreationFacade.d.ts +4 -13
  9. package/dist/calendar/calendarCreationFacade.js +3 -5
  10. package/dist/calendar/createCalendar.d.ts +67 -37
  11. package/dist/calendar/createCalendar.js +1 -3
  12. package/dist/calendar/fetchCalendarDetails.d.ts +73 -18
  13. package/dist/calendar/fetchCalendarDetails.js +192 -51
  14. package/dist/calendar/fetchCalendarWithOpeningHours.d.ts +34 -21
  15. package/dist/calendar/fetchCalendarWithOpeningHours.js +95 -75
  16. package/dist/calendar/getAllParticipantOpeningHours.d.ts +19 -0
  17. package/dist/calendar/getAllParticipantOpeningHours.js +17 -0
  18. package/dist/calendar/getOpeningHours.d.ts +5 -0
  19. package/dist/calendar/getOpeningHours.js +9 -0
  20. package/dist/calendar/getParticipantOpeningHours.d.ts +37 -0
  21. package/dist/calendar/getParticipantOpeningHours.js +43 -0
  22. package/dist/calendar/getParticipants.d.ts +4 -0
  23. package/dist/calendar/getParticipants.js +8 -0
  24. package/dist/calendar/mapCalendarBoToBlazeoSnapshot.d.ts +9 -9
  25. package/dist/calendar/mapCalendarBoToBlazeoSnapshot.js +43 -43
  26. package/dist/calendar/mapCalendarToBlazeoSnapshot.d.ts +22 -3
  27. package/dist/calendar/mapCalendarToBlazeoSnapshot.js +0 -1
  28. package/dist/config/applyBlazeoClientConfig.d.ts +2 -2
  29. package/dist/config/applyBlazeoClientConfig.js +13 -13
  30. package/dist/config/applyBlazeoDefaults.d.ts +0 -1
  31. package/dist/config/applyBlazeoDefaults.js +0 -1
  32. package/dist/config/blazeo.config.d.ts +10 -10
  33. package/dist/config/blazeo.config.js +10 -10
  34. package/dist/config/blazeoClientDefaults.d.ts +1 -2
  35. package/dist/config/blazeoClientDefaults.js +0 -1
  36. package/dist/config/initializeAppointmentClient.d.ts +4 -28
  37. package/dist/config/initializeAppointmentClient.js +5 -24
  38. package/dist/events/appointmentEventFacade.d.ts +55 -32
  39. package/dist/events/appointmentEventFacade.js +5 -10
  40. package/dist/events/mapAppointmentToEventSnapshot.d.ts +1 -4
  41. package/dist/events/mapAppointmentToEventSnapshot.js +0 -1
  42. package/dist/exampleData.d.ts +114 -10
  43. package/dist/exampleData.js +4 -5
  44. package/dist/facade/calendarCreationFacade.d.ts +39 -39
  45. package/dist/facade/calendarCreationFacade.js +95 -95
  46. package/dist/facade/mapCalendarBOToSnapshot.d.ts +9 -9
  47. package/dist/facade/mapCalendarBOToSnapshot.js +43 -43
  48. package/dist/facades/index.d.ts +11 -11
  49. package/dist/facades/index.js +11 -11
  50. package/dist/index.d.ts +23 -82
  51. package/dist/index.js +21 -33
  52. package/dist/models/CalendarRootModel.d.ts +36 -11
  53. package/dist/models/CalendarRootModel.js +22 -5
  54. package/dist/models/CalendarSlotModel.d.ts +8 -8
  55. package/dist/models/CalendarSlotModel.js +7 -7
  56. package/dist/models/EventModel.d.ts +10 -10
  57. package/dist/models/EventModel.js +9 -9
  58. package/dist/models/ParticipantModel.d.ts +8 -8
  59. package/dist/models/ParticipantModel.js +7 -7
  60. package/dist/models/index.d.ts +4 -4
  61. package/dist/models/index.js +4 -4
  62. package/dist/types/appointment.d.ts +27 -27
  63. package/dist/types/appointment.js +5 -5
  64. package/dist/types/calendar.d.ts +51 -51
  65. package/dist/types/calendar.js +5 -5
  66. package/dist/types/calendarBo.d.ts +61 -61
  67. package/dist/types/calendarBo.js +5 -5
  68. package/package.json +8 -2
  69. package/sample/build_error.txt +0 -0
  70. package/sample/demo.js +70 -0
  71. package/sample/package-lock.json +5 -2
  72. package/sample/package.json +3 -1
  73. package/sample/scripts/getInfoByCalendar.mjs +36 -0
  74. package/sample/scripts/getParticipantOpeningHours.mjs +48 -0
  75. package/sample/src/AllParticipantOpeningHoursTab.jsx +73 -0
  76. package/sample/src/App2.jsx +39 -2
  77. package/sample/src/BlazeoConnectionSettings.jsx +1 -1
  78. package/sample/src/FetchCalendarTab.jsx +70 -20
  79. package/sample/src/OpeningHoursTab.jsx +78 -0
  80. package/sample/src/ParticipantInfoTab.jsx +72 -0
  81. package/sample/src/ParticipantOpeningHoursTab.jsx +88 -0
  82. package/sample/src/ParticipantTab.jsx +2 -2
  83. package/src/calendar/blazeoCalendarRelationMethods.ts +19 -0
  84. package/src/calendar/buildUnifiedCalendarView.ts +322 -0
  85. package/src/calendar/calendarCreation.ts +179 -0
  86. package/src/calendar/createCalendar.ts +243 -0
  87. package/src/calendar/fetchCalendarDetails.ts +226 -0
  88. package/src/calendar/fetchCalendarWithOpeningHours.ts +99 -0
  89. package/src/calendar/getAllParticipantOpeningHours.ts +22 -0
  90. package/src/calendar/getOpeningHours.ts +10 -0
  91. package/src/calendar/getParticipantOpeningHours.ts +46 -0
  92. package/src/calendar/getParticipants.ts +9 -0
  93. package/src/calendar/mapCalendarToBlazeoSnapshot.ts +46 -0
  94. package/src/config/applyBlazeoDefaults.ts +13 -0
  95. package/src/config/blazeoClientDefaults.ts +11 -0
  96. package/src/config/initializeAppointmentClient.ts +18 -0
  97. package/src/events/appointmentEventFacade.ts +148 -0
  98. package/src/events/mapAppointmentToEventSnapshot.ts +65 -0
  99. package/src/exampleData.ts +79 -0
  100. package/src/index.ts +45 -0
  101. package/src/models/CalendarRootModel.ts +60 -0
  102. package/tsconfig.json +16 -0
  103. package/blazeo.com-appointment-client-1.0.5.tgz +0 -0
@@ -6,16 +6,25 @@ import {
6
6
  import { CalendarTab } from "./CalendarTab.jsx";
7
7
  import { EventTab } from "./EventTab.jsx";
8
8
  import { ParticipantTab } from "./ParticipantTab.jsx";
9
+ import { ParticipantInfoTab } from "./ParticipantInfoTab.jsx";
10
+ import { ParticipantOpeningHoursTab } from "./ParticipantOpeningHoursTab.jsx";
11
+ import { AllParticipantOpeningHoursTab } from "./AllParticipantOpeningHoursTab.jsx";
9
12
  import { AvailabilityTab } from "./AvailabilityTab.jsx";
10
13
  import { CreateCalendarTab } from "./CreateCalendarTab.jsx";
11
14
  import { FetchCalendarTab } from "./FetchCalendarTab.jsx";
15
+ import { OpeningHoursTab } from "./OpeningHoursTab.jsx";
12
16
 
13
17
  const TABS = [
14
18
  { id: "calendar", label: "Calendar" },
15
- { id: "fetch", label: "Fetch calendar" },
19
+ /** `FetchCalendarTab` `fetchCalendarDetails` + `calendarView` (unified object). */
20
+ { id: "fetch", label: "Fetch · calendarView" },
16
21
  { id: "create", label: "Create calendar" },
17
22
  { id: "event", label: "Event" },
18
23
  { id: "participant", label: "Participant" },
24
+ { id: "participant-info", label: "Participant info" },
25
+ { id: "opening-hours", label: "Opening Hours" },
26
+ { id: "participant-opening-hours", label: "Participant opening hours" },
27
+ { id: "all-participant-opening-hours", label: "All participant opening hours" },
19
28
  { id: "availability", label: "Availability / booking" },
20
29
  ];
21
30
 
@@ -80,7 +89,7 @@ function AppShell() {
80
89
  <main className="page">
81
90
  <header className="header">
82
91
  <h1>appointment-client</h1>
83
- <p className="muted">Browser sample — tabbed explorer for the npm package</p>
92
+ <p className="muted">Browser sample — use tab <strong>Fetch · calendarView</strong> for the unified <code>calendarView</code> object.</p>
84
93
  </header>
85
94
 
86
95
  <ConnectionSettingsCard />
@@ -128,6 +137,34 @@ function AppShell() {
128
137
  <ParticipantTab />
129
138
  </section>
130
139
  )}
140
+ {activeId === "participant-info" && (
141
+ <section role="tabpanel" id="panel-participant-info" aria-labelledby="tab-participant-info">
142
+ <ParticipantInfoTab />
143
+ </section>
144
+ )}
145
+ {activeId === "opening-hours" && (
146
+ <section role="tabpanel" id="panel-opening-hours" aria-labelledby="tab-opening-hours">
147
+ <OpeningHoursTab />
148
+ </section>
149
+ )}
150
+ {activeId === "participant-opening-hours" && (
151
+ <section
152
+ role="tabpanel"
153
+ id="panel-participant-opening-hours"
154
+ aria-labelledby="tab-participant-opening-hours"
155
+ >
156
+ <ParticipantOpeningHoursTab />
157
+ </section>
158
+ )}
159
+ {activeId === "all-participant-opening-hours" && (
160
+ <section
161
+ role="tabpanel"
162
+ id="panel-all-participant-opening-hours"
163
+ aria-labelledby="tab-all-participant-opening-hours"
164
+ >
165
+ <AllParticipantOpeningHoursTab />
166
+ </section>
167
+ )}
131
168
  {activeId === "availability" && (
132
169
  <section role="tabpanel" id="panel-availability" aria-labelledby="tab-availability">
133
170
  <AvailabilityTab />
@@ -45,7 +45,7 @@ export function mergeBlazeoUiWithFile(uiBaseUrl, uiConsumer) {
45
45
  }
46
46
 
47
47
  /**
48
- * Re-apply global Blazeo `configure` from the merged connection card state.
48
+ * Re-apply global Blazeo `configure` from the effective connection card state.
49
49
  * Call at the start of any handler that uses `CalendarModel` / `EventModel` HTTP so
50
50
  * `getParticipantOpeningHours` (instance env) and static helpers always see the same
51
51
  * `baseUrl`.
@@ -3,7 +3,6 @@ import {
3
3
  CalendarModel,
4
4
  deleteCalendarAsync,
5
5
  fetchCalendarDetails,
6
- fetchCalendarWithOpeningHours,
7
6
  updateCalendarAsync,
8
7
  } from "appointment-client";
9
8
  import { getSnapshot, isStateTreeNode } from "mobx-state-tree";
@@ -53,8 +52,10 @@ function explainFetchFailure(err, configuredBaseUrl) {
53
52
  return `${msg}\n\n${proxyHint}`;
54
53
  }
55
54
 
56
- /** Opening hours list from participant API wrapper or embedded `calendar.openingHours`. */
55
+ /** Opening hours list from `calendarView`, embedded `calendar.openingHours`, or legacy `openingHours`. */
57
56
  function pickOpeningHoursListFromBundle(parsed) {
57
+ const fromView = parsed?.calendarView?.openingHours;
58
+ if (Array.isArray(fromView) && fromView.length > 0) return fromView;
58
59
  const fromCal = parsed?.calendar?.openingHours;
59
60
  if (Array.isArray(fromCal) && fromCal.length > 0) return fromCal;
60
61
  const oh = parsed?.openingHours;
@@ -81,7 +82,7 @@ function OpeningHoursSummary({ outputJson }) {
81
82
  return (
82
83
  <p className="muted small" style={{ marginBottom: "0.75rem" }}>
83
84
  No opening-hours rows parsed for the table. Check{" "}
84
- <code>calendar.openingHours</code> or <code>openingHours</code> in the JSON below.
85
+ <code>calendar.openingHours</code>, <code>calendarView.openingHours</code>, or <code>openingHours</code> in the JSON below.
85
86
  </p>
86
87
  );
87
88
  }
@@ -218,7 +219,7 @@ export function FetchCalendarTab() {
218
219
 
219
220
  setBusy(true);
220
221
  try {
221
- const details = await fetchCalendarDetails(id, { includeParticipantsInfo: true });
222
+ const details = await fetchCalendarDetails(id);
222
223
 
223
224
  if (details.cal == null) {
224
225
  const raw = await CalendarModel.getRaw(id);
@@ -231,16 +232,36 @@ export function FetchCalendarTab() {
231
232
  setLastFetchUpdatePayload(JSON.stringify(calendarSnapshotToUpdatePayload(snap), null, 2));
232
233
 
233
234
  const payload = {
235
+ calendarView: details.calendarView,
234
236
  calendar: details.calendar,
235
237
  openingHours: details.openingHours,
236
- openingHoursApiResponse: details.openingHoursApiResponse ?? null,
237
- participants: details.participants,
238
- participantsInfo: details.participantsInfo ?? null,
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
+ },
239
251
  meta: details.meta,
240
252
  };
241
253
 
242
254
  setOutput(toDisplayJson(payload));
243
- setNote("Loaded calendar + opening hours + participants (3 calls) → single response object.");
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
+ );
244
265
  } catch (err) {
245
266
  setError(explainFetchFailure(err, effective.baseUrl));
246
267
  } finally {
@@ -264,10 +285,13 @@ export function FetchCalendarTab() {
264
285
 
265
286
  setBusy(true);
266
287
  try {
267
- const list = await CalendarModel.getByCompany(key);
268
- if (list == null || list.length === 0) {
269
- setNote("getByCompany returned null or an empty list.");
270
- setOutput(toDisplayJson(list));
288
+ const byCompany = await CalendarModel.getByCompany(key);
289
+ const list = byCompany?.calendars ?? byCompany;
290
+ if (list == null || !Array.isArray(list) || list.length === 0) {
291
+ setNote(
292
+ "getByCompany returned null or an empty list. (calendar-client ≥1.0.17 returns { calendars, totalCount }.)"
293
+ );
294
+ setOutput(toDisplayJson(byCompany));
271
295
  } else {
272
296
  const enriched = await Promise.all(
273
297
  list.map(async (c) => {
@@ -276,9 +300,15 @@ export function FetchCalendarTab() {
276
300
  try {
277
301
  const b = await fetchCalendarDetails(id);
278
302
  return {
303
+ calendarView: b.calendarView,
279
304
  calendar: b.calendar ?? getSnapshot(c),
280
305
  openingHours: b.openingHours,
281
- participants: b.participants,
306
+ participants: (b.participants ?? []).map((p) => (isStateTreeNode(p) ? getSnapshot(p) : p)),
307
+ __openingHoursMeta: {
308
+ fromCalendarGet: b.fromCalendarGet,
309
+ fromParticipantApi: b.fromParticipantApi,
310
+ calendarViewUsedAllParticipantOpeningHours: b.meta?.calendarViewUsedAllParticipantOpeningHours,
311
+ },
282
312
  meta: b.meta,
283
313
  };
284
314
  } catch (err) {
@@ -292,8 +322,9 @@ export function FetchCalendarTab() {
292
322
  }
293
323
  })
294
324
  );
325
+ const total = byCompany?.totalCount ?? list.length;
295
326
  setNote(
296
- `Loaded ${list.length} calendar(s); opening hours + participants loaded per calendar (single details bundle).`
327
+ `Loaded ${list.length} calendar(s) (totalCount=${total}); opening hours (embed → participant API) + participants per calendar.`
297
328
  );
298
329
  setOutput(toDisplayJson(enriched));
299
330
  }
@@ -410,13 +441,32 @@ export function FetchCalendarTab() {
410
441
  return (
411
442
  <>
412
443
  <div className="card">
413
- <h2>Fetch calendar</h2>
444
+ <h2>Fetch calendar · calendarView</h2>
445
+ <p className="muted small">
446
+ Runs <code>fetchCalendarDetails(calendarId)</code>. JSON field <code>calendarView</code> is first: one object with
447
+ calendar snapshot fields + <code>members</code> + <code>openingHours</code>, and a **new** <code>participants</code> array
448
+ where each participant has their own <code>openingHours</code> nested inside.
449
+ </p>
450
+ <p className="muted small">
451
+ Uses <code>fetchCalendarDetails</code>: legacy <code>openingHours</code> prefers embed on{" "}
452
+ <code>CalendarModel.getRaw</code>, else <code>getParticipantOpeningHours</code>.{" "}
453
+ <code>calendarView.openingHours</code> prefers <code>getAllParticipantOpeningHours</code> (
454
+ <code>GET /Calendar/Participant/OpeningHours/All/Get</code>) when the API returns rows. Members combine{" "}
455
+ <code>CalendarModel.getParticipants</code> + <code>CalendarModel.getParticipantsInfo</code> (each member may
456
+ include <code>participantInfo</code>).
457
+ </p>
458
+ <p className="muted small">
459
+ <strong>Single object in code:</strong> <code>fetchCalendarBundle(calendarId)</code> after{" "}
460
+ <code>initializeAppointmentClient(&#123; baseUrl, consumer &#125;)</code> — same unified shape as{" "}
461
+ <code>calendarView</code> below. This tab runs <code>fetchCalendarDetails</code> so extra arrays stay visible.
462
+ </p>
414
463
  <p className="muted small">
415
- Uses <code>fetchCalendarWithOpeningHours</code> from <code>appointment-client</code>: merges
416
- embedded <code>openingHours</code> from <code>GET /Calendar/Get</code> when present (MST omits
417
- them); otherwise calls <code>calendar.getParticipantOpeningHours()</code> (
418
- <code>GET /Calendar/Participant/OpeningHours/Get</code>), matching{" "}
419
- <code>@blazeo.com/calendar-client</code>.
464
+ <strong>DevTools Network:</strong> Each fetch fires <code>/Calendar/Get</code> <strong>twice</strong> (
465
+ <code>CalendarModel.get</code> + <code>getRaw</code>). Other calls use different URLs — filter by{" "}
466
+ <code>Participant</code>, <code>OpeningHours</code>, or <code>GetInfo</code>. Those power{" "}
467
+ <code>calendarView</code>. If you only see <code>Calendar/Get</code> yet the UI JSON has members/hours,
468
+ widen the Network filter (&quot;All&quot;) or disable search; if <code>calendarView</code> is empty/missing fields,
469
+ check the <strong>Console</strong> for errors on the participant/opening-hours requests.
420
470
  </p>
421
471
  <p className="muted small">
422
472
  Effective: <code>{effective.baseUrl || "(set connection or blazeoClientDefaults.ts)"}</code>
@@ -0,0 +1,78 @@
1
+ import { useState } from "react";
2
+ import { fetchCalendarWithOpeningHours } from "appointment-client";
3
+ import {
4
+ configureBlazeoFromEffective,
5
+ useBlazeoConnection,
6
+ } from "./BlazeoConnectionSettings.jsx";
7
+
8
+ export function OpeningHoursTab() {
9
+ const { effective } = useBlazeoConnection();
10
+ const [calendarId, setCalendarId] = useState("");
11
+ const [busy, setBusy] = useState(false);
12
+ const [error, setError] = useState("");
13
+ const [output, setOutput] = useState("");
14
+
15
+ async function handleFetch(e) {
16
+ e.preventDefault();
17
+ setError("");
18
+ setOutput("");
19
+ const id = calendarId.trim();
20
+ if (!id) {
21
+ setError("Enter a calendar id.");
22
+ return;
23
+ }
24
+ if (!effective.baseUrl) {
25
+ setError("Set Base URL in the connection card above.");
26
+ return;
27
+ }
28
+ configureBlazeoFromEffective(effective);
29
+ setBusy(true);
30
+ try {
31
+ const res = await fetchCalendarWithOpeningHours(id);
32
+ setOutput(JSON.stringify(res, null, 2));
33
+ } catch (err) {
34
+ setError(err instanceof Error ? err.message : String(err));
35
+ } finally {
36
+ setBusy(false);
37
+ }
38
+ }
39
+
40
+ return (
41
+ <>
42
+ <div className="card">
43
+ <h2>Opening Hours (Detailed)</h2>
44
+ <p className="muted small">
45
+ Fetches business hours using <code>fetchCalendarWithOpeningHours</code>.
46
+ </p>
47
+ <form onSubmit={handleFetch} className="form">
48
+ <label className="form__label">
49
+ <span>Calendar id</span>
50
+ <input
51
+ type="text"
52
+ className="form__input"
53
+ value={calendarId}
54
+ onChange={(e) => setCalendarId(e.target.value)}
55
+ />
56
+ </label>
57
+ <button type="submit" className="btn btn--primary" disabled={busy}>
58
+ {busy ? "Loading…" : "Fetch Opening Hours"}
59
+ </button>
60
+ </form>
61
+ </div>
62
+
63
+ {error ? (
64
+ <div className="card card--error" role="alert">
65
+ <h2>Error</h2>
66
+ <pre className="pre-block">{error}</pre>
67
+ </div>
68
+ ) : null}
69
+
70
+ {output ? (
71
+ <div className="card card--success">
72
+ <h2>Result</h2>
73
+ <pre className="pre-block">{output}</pre>
74
+ </div>
75
+ ) : null}
76
+ </>
77
+ );
78
+ }
@@ -0,0 +1,72 @@
1
+ import { useState } from "react";
2
+ import { CalendarParticipantModel } from "appointment-client";
3
+ import { configureBlazeoFromEffective, useBlazeoConnection } from "./BlazeoConnectionSettings.jsx";
4
+
5
+ export function ParticipantInfoTab() {
6
+ const { effective } = useBlazeoConnection();
7
+ const [calendarId, setCalendarId] = useState("");
8
+ const [busy, setBusy] = useState(false);
9
+ const [error, setError] = useState("");
10
+ const [output, setOutput] = useState("");
11
+
12
+ async function handleFetch(e) {
13
+ e.preventDefault();
14
+ setError("");
15
+ setOutput("");
16
+ const id = calendarId.trim();
17
+ if (!id) return setError("Enter a calendar id.");
18
+ if (!effective.baseUrl) return setError("Set Base URL in the connection card above.");
19
+
20
+ configureBlazeoFromEffective(effective);
21
+ setBusy(true);
22
+ try {
23
+ const info = await CalendarParticipantModel.getInfoByCalendar(id);
24
+ setOutput(JSON.stringify({ calendarId: id, count: Array.isArray(info) ? info.length : 0, info }, null, 2));
25
+ } catch (err) {
26
+ setError(err instanceof Error ? err.message : String(err));
27
+ } finally {
28
+ setBusy(false);
29
+ }
30
+ }
31
+
32
+ return (
33
+ <>
34
+ <div className="card">
35
+ <h2>Participant Info by Calendar</h2>
36
+ <p className="muted small">
37
+ Calls <code>CalendarParticipantModel.getInfoByCalendar(calendarId)</code>.
38
+ </p>
39
+ <form onSubmit={handleFetch} className="form">
40
+ <label className="form__label">
41
+ <span>Calendar id</span>
42
+ <input
43
+ type="text"
44
+ className="form__input"
45
+ value={calendarId}
46
+ onChange={(e) => setCalendarId(e.target.value)}
47
+ autoComplete="off"
48
+ />
49
+ </label>
50
+ <button type="submit" className="btn btn--secondary" disabled={busy}>
51
+ {busy ? "Loading…" : "Fetch participant info"}
52
+ </button>
53
+ </form>
54
+ </div>
55
+
56
+ {error ? (
57
+ <div className="card card--error" role="alert">
58
+ <h2>Error</h2>
59
+ <pre className="pre-block">{error}</pre>
60
+ </div>
61
+ ) : null}
62
+
63
+ {output ? (
64
+ <div className="card card--success">
65
+ <h2>Result</h2>
66
+ <pre className="pre-block">{output}</pre>
67
+ </div>
68
+ ) : null}
69
+ </>
70
+ );
71
+ }
72
+
@@ -0,0 +1,88 @@
1
+ import { useState } from "react";
2
+ import { getParticipantOpeningHours } from "appointment-client";
3
+ import { configureBlazeoFromEffective, useBlazeoConnection } from "./BlazeoConnectionSettings.jsx";
4
+
5
+ export function ParticipantOpeningHoursTab() {
6
+ const { effective } = useBlazeoConnection();
7
+ const [calendarId, setCalendarId] = useState("");
8
+ const [busy, setBusy] = useState(false);
9
+ const [error, setError] = useState("");
10
+ const [output, setOutput] = useState("");
11
+
12
+ async function handleFetch(e) {
13
+ e.preventDefault();
14
+ setError("");
15
+ setOutput("");
16
+ const id = calendarId.trim();
17
+ if (!id) return setError("Enter a calendar id.");
18
+ if (!effective.baseUrl) return setError("Set Base URL in the connection card above.");
19
+
20
+ configureBlazeoFromEffective(effective);
21
+ setBusy(true);
22
+ try {
23
+ debugger;
24
+ const result = await getParticipantOpeningHours(id);
25
+
26
+ if (!result.meta.ok) {
27
+ setError(result.meta.error || `Failed to fetch: ${result.meta.reason}`);
28
+ if (result.raw) setOutput(JSON.stringify(result.raw, null, 2));
29
+ return;
30
+ }
31
+
32
+ setOutput(JSON.stringify({
33
+ calendarId: id,
34
+ count: result.openingHours.length,
35
+ openingHours: result.openingHours,
36
+ raw: result.raw,
37
+ meta: result.meta
38
+ }, null, 2));
39
+ } catch (err) {
40
+ setError(err instanceof Error ? err.message : String(err));
41
+ } finally {
42
+ setBusy(false);
43
+ }
44
+ }
45
+
46
+ return (
47
+ <>
48
+ <div className="card">
49
+ <h2>Participant Opening Hours (Direct)</h2>
50
+ <p className="muted small">
51
+ Calls <code>CalendarModel.get(calendarId)</code> then{" "}
52
+ <code>calendar.getParticipantOpeningHours(&#123;&#125;)</code> (API:{" "}
53
+ <code>GET /Calendar/Participant/OpeningHours/Get</code>).
54
+ </p>
55
+ <form onSubmit={handleFetch} className="form">
56
+ <label className="form__label">
57
+ <span>Calendar id</span>
58
+ <input
59
+ type="text"
60
+ className="form__input"
61
+ value={calendarId}
62
+ onChange={(e) => setCalendarId(e.target.value)}
63
+ autoComplete="off"
64
+ />
65
+ </label>
66
+ <button type="submit" className="btn btn--secondary" disabled={busy}>
67
+ {busy ? "Loading…" : "Fetch participant opening hours"}
68
+ </button>
69
+ </form>
70
+ </div>
71
+
72
+ {error ? (
73
+ <div className="card card--error" role="alert">
74
+ <h2>Error</h2>
75
+ <pre className="pre-block">{error}</pre>
76
+ </div>
77
+ ) : null}
78
+
79
+ {output ? (
80
+ <div className="card card--success">
81
+ <h2>Result</h2>
82
+ <pre className="pre-block">{output}</pre>
83
+ </div>
84
+ ) : null}
85
+ </>
86
+ );
87
+ }
88
+
@@ -1,5 +1,5 @@
1
1
  import { useMemo, useState } from "react";
2
- import { getExampleParticipants, ParticipantModel } from "appointment-client";
2
+ import { getExampleParticipants, getParticipants } from "appointment-client";
3
3
  import { getSnapshot, isStateTreeNode } from "mobx-state-tree";
4
4
  import {
5
5
  configureBlazeoFromEffective,
@@ -43,7 +43,7 @@ export function ParticipantTab() {
43
43
  configureBlazeoFromEffective(effective);
44
44
  setBusy(true);
45
45
  try {
46
- const res = await ParticipantModel.getAllByCalendar(id);
46
+ const res = await getParticipants(id);
47
47
  setOutput(toDisplayJson(res));
48
48
  } catch (err) {
49
49
  setError(err instanceof Error ? err.message : String(err));
@@ -0,0 +1,19 @@
1
+ /** `GET /Calendar/Participant/Add` — attach `participantId` to this calendar. */
2
+ export function addParticipantToCalendar(calendar: any, participantId: string) {
3
+ return calendar.addParticipant(participantId);
4
+ }
5
+
6
+ /** `GET /Calendar/Participant/Remove` */
7
+ export function removeParticipantFromCalendar(calendar: any, participantId: string) {
8
+ return calendar.removeParticipant(participantId);
9
+ }
10
+
11
+ /** `POST /Calendar/Participant/Availability/OpeningHour/Save` — one day/slot. */
12
+ export function saveCalendarOpeningHour(calendar: any, payload: any) {
13
+ return calendar.saveOpeningHour(payload);
14
+ }
15
+
16
+ /** `POST /Calendar/Participant/Availability/OpeningHours/Save` — batch body (API-specific shape). */
17
+ export function saveCalendarOpeningHoursBatch(calendar: any, payload: any) {
18
+ return calendar.saveOpeningHours(payload);
19
+ }