@blazeo.com/appointment-client 1.0.4 → 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.
- package/blazeo.com-appointment-client-1.0.6.tgz +0 -0
- package/dist/calendar/blazeoCalendarRelationMethods.d.ts +4 -36
- package/dist/calendar/blazeoCalendarRelationMethods.js +0 -1
- package/dist/calendar/buildUnifiedCalendarView.d.ts +31 -0
- package/dist/calendar/buildUnifiedCalendarView.js +265 -0
- package/dist/calendar/calendarCreation.d.ts +27 -0
- package/dist/calendar/calendarCreation.js +167 -0
- package/dist/calendar/calendarCreationFacade.d.ts +4 -13
- package/dist/calendar/calendarCreationFacade.js +3 -5
- package/dist/calendar/createCalendar.d.ts +67 -37
- package/dist/calendar/createCalendar.js +1 -3
- package/dist/calendar/fetchCalendarDetails.d.ts +73 -0
- package/dist/calendar/fetchCalendarDetails.js +192 -0
- package/dist/calendar/fetchCalendarWithOpeningHours.d.ts +34 -21
- package/dist/calendar/fetchCalendarWithOpeningHours.js +95 -75
- package/dist/calendar/getAllParticipantOpeningHours.d.ts +19 -0
- package/dist/calendar/getAllParticipantOpeningHours.js +17 -0
- package/dist/calendar/getOpeningHours.d.ts +5 -0
- package/dist/calendar/getOpeningHours.js +9 -0
- package/dist/calendar/getParticipantOpeningHours.d.ts +37 -0
- package/dist/calendar/getParticipantOpeningHours.js +43 -0
- package/dist/calendar/getParticipants.d.ts +4 -0
- package/dist/calendar/getParticipants.js +8 -0
- package/dist/calendar/mapCalendarBoToBlazeoSnapshot.d.ts +9 -9
- package/dist/calendar/mapCalendarBoToBlazeoSnapshot.js +43 -43
- package/dist/calendar/mapCalendarToBlazeoSnapshot.d.ts +22 -3
- package/dist/calendar/mapCalendarToBlazeoSnapshot.js +0 -1
- package/dist/config/applyBlazeoClientConfig.d.ts +2 -2
- package/dist/config/applyBlazeoClientConfig.js +13 -13
- package/dist/config/applyBlazeoDefaults.d.ts +0 -1
- package/dist/config/applyBlazeoDefaults.js +0 -1
- package/dist/config/blazeo.config.d.ts +10 -10
- package/dist/config/blazeo.config.js +10 -10
- package/dist/config/blazeoClientDefaults.d.ts +1 -2
- package/dist/config/blazeoClientDefaults.js +0 -1
- package/dist/config/initializeAppointmentClient.d.ts +4 -28
- package/dist/config/initializeAppointmentClient.js +5 -24
- package/dist/events/appointmentEventFacade.d.ts +55 -32
- package/dist/events/appointmentEventFacade.js +5 -10
- package/dist/events/mapAppointmentToEventSnapshot.d.ts +1 -4
- package/dist/events/mapAppointmentToEventSnapshot.js +0 -1
- package/dist/exampleData.d.ts +114 -10
- package/dist/exampleData.js +4 -5
- package/dist/facade/calendarCreationFacade.d.ts +39 -39
- package/dist/facade/calendarCreationFacade.js +95 -95
- package/dist/facade/mapCalendarBOToSnapshot.d.ts +9 -9
- package/dist/facade/mapCalendarBOToSnapshot.js +43 -43
- package/dist/facades/index.d.ts +11 -11
- package/dist/facades/index.js +11 -11
- package/dist/index.d.ts +23 -81
- package/dist/index.js +21 -32
- package/dist/models/CalendarRootModel.d.ts +36 -11
- package/dist/models/CalendarRootModel.js +22 -5
- package/dist/models/CalendarSlotModel.d.ts +8 -8
- package/dist/models/CalendarSlotModel.js +7 -7
- package/dist/models/EventModel.d.ts +10 -10
- package/dist/models/EventModel.js +9 -9
- package/dist/models/ParticipantModel.d.ts +8 -8
- package/dist/models/ParticipantModel.js +7 -7
- package/dist/models/index.d.ts +4 -4
- package/dist/models/index.js +4 -4
- package/dist/types/appointment.d.ts +27 -27
- package/dist/types/appointment.js +5 -5
- package/dist/types/calendar.d.ts +51 -51
- package/dist/types/calendar.js +5 -5
- package/dist/types/calendarBo.d.ts +61 -61
- package/dist/types/calendarBo.js +5 -5
- package/package.json +9 -4
- package/sample/build_error.txt +0 -0
- package/sample/demo.js +70 -0
- package/sample/package-lock.json +53 -2
- package/sample/package.json +3 -1
- package/sample/scripts/getInfoByCalendar.mjs +36 -0
- package/sample/scripts/getParticipantOpeningHours.mjs +48 -0
- package/sample/src/AllParticipantOpeningHoursTab.jsx +73 -0
- package/sample/src/App2.jsx +39 -2
- package/sample/src/BlazeoConnectionSettings.jsx +1 -1
- package/sample/src/EventTab.jsx +128 -0
- package/sample/src/FetchCalendarTab.jsx +72 -43
- package/sample/src/OpeningHoursTab.jsx +78 -0
- package/sample/src/ParticipantInfoTab.jsx +72 -0
- package/sample/src/ParticipantOpeningHoursTab.jsx +88 -0
- package/sample/src/ParticipantTab.jsx +2 -2
- package/src/calendar/blazeoCalendarRelationMethods.ts +19 -0
- package/src/calendar/buildUnifiedCalendarView.ts +322 -0
- package/src/calendar/calendarCreation.ts +179 -0
- package/src/calendar/createCalendar.ts +243 -0
- package/src/calendar/fetchCalendarDetails.ts +226 -0
- package/src/calendar/fetchCalendarWithOpeningHours.ts +99 -0
- package/src/calendar/getAllParticipantOpeningHours.ts +22 -0
- package/src/calendar/getOpeningHours.ts +10 -0
- package/src/calendar/getParticipantOpeningHours.ts +46 -0
- package/src/calendar/getParticipants.ts +9 -0
- package/src/calendar/mapCalendarToBlazeoSnapshot.ts +46 -0
- package/src/config/applyBlazeoDefaults.ts +13 -0
- package/src/config/blazeoClientDefaults.ts +11 -0
- package/src/config/initializeAppointmentClient.ts +18 -0
- package/src/events/appointmentEventFacade.ts +148 -0
- package/src/events/mapAppointmentToEventSnapshot.ts +65 -0
- package/src/exampleData.ts +79 -0
- package/src/index.ts +45 -0
- package/src/models/CalendarRootModel.ts +60 -0
- package/tsconfig.json +16 -0
- package/blazeo.com-appointment-client-1.0.4.tgz +0 -0
package/sample/src/EventTab.jsx
CHANGED
|
@@ -2,6 +2,7 @@ import { useMemo, useState } from "react";
|
|
|
2
2
|
import {
|
|
3
3
|
cancelAppointmentEventAsync,
|
|
4
4
|
createAppointmentEventAsync,
|
|
5
|
+
EventModel,
|
|
5
6
|
rescheduleAppointmentEventAsync,
|
|
6
7
|
} from "appointment-client";
|
|
7
8
|
import { getSnapshot, isStateTreeNode } from "mobx-state-tree";
|
|
@@ -65,6 +66,14 @@ function resultToJson(result) {
|
|
|
65
66
|
return JSON.stringify(result, null, 2);
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
function safeJsonParse(text, fallback) {
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(text);
|
|
72
|
+
} catch {
|
|
73
|
+
return fallback;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
68
77
|
export function EventTab() {
|
|
69
78
|
const { effective } = useBlazeoConnection();
|
|
70
79
|
const [offsetMinutes, setOffsetMinutes] = useState(-new Date().getTimezoneOffset());
|
|
@@ -74,6 +83,35 @@ export function EventTab() {
|
|
|
74
83
|
const [rescheduleJson, setRescheduleJson] = useState(() =>
|
|
75
84
|
JSON.stringify(getExampleReschedulePayload(), null, 2)
|
|
76
85
|
);
|
|
86
|
+
const [searchCompanyKey, setSearchCompanyKey] = useState("");
|
|
87
|
+
const [searchFrom, setSearchFrom] = useState(() => new Date().toISOString().slice(0, 10));
|
|
88
|
+
const [searchTo, setSearchTo] = useState(() => {
|
|
89
|
+
const d = new Date();
|
|
90
|
+
d.setDate(d.getDate() + 7);
|
|
91
|
+
return d.toISOString().slice(0, 10);
|
|
92
|
+
});
|
|
93
|
+
const [searchFiltersJson, setSearchFiltersJson] = useState(() =>
|
|
94
|
+
JSON.stringify(
|
|
95
|
+
{
|
|
96
|
+
calendarId: "",
|
|
97
|
+
participantId: "",
|
|
98
|
+
leadId: "",
|
|
99
|
+
visitorName: "",
|
|
100
|
+
visitorEmail: "",
|
|
101
|
+
visitorPhone: "",
|
|
102
|
+
title: "",
|
|
103
|
+
search: "",
|
|
104
|
+
attendeeStatus: "",
|
|
105
|
+
eventSource: "",
|
|
106
|
+
sort: "",
|
|
107
|
+
sortOrder: "desc",
|
|
108
|
+
page: 1,
|
|
109
|
+
page_size: 25,
|
|
110
|
+
},
|
|
111
|
+
null,
|
|
112
|
+
2
|
|
113
|
+
)
|
|
114
|
+
);
|
|
77
115
|
const [cancelEventId, setCancelEventId] = useState("");
|
|
78
116
|
const [busy, setBusy] = useState(false);
|
|
79
117
|
const [error, setError] = useState("");
|
|
@@ -153,6 +191,42 @@ export function EventTab() {
|
|
|
153
191
|
}
|
|
154
192
|
}
|
|
155
193
|
|
|
194
|
+
async function handleSearchByDateRange(e) {
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
setError("");
|
|
197
|
+
setOutput("");
|
|
198
|
+
const companyKey = searchCompanyKey.trim();
|
|
199
|
+
if (!companyKey) return setError("Enter company key.");
|
|
200
|
+
if (!searchFrom) return setError("Pick start date.");
|
|
201
|
+
if (!searchTo) return setError("Pick end date.");
|
|
202
|
+
if (!ensureBase()) return;
|
|
203
|
+
configureBlazeoFromEffective(effective);
|
|
204
|
+
|
|
205
|
+
const optsFromJson = safeJsonParse(searchFiltersJson, {});
|
|
206
|
+
const startDateFrom = new Date(`${searchFrom}T00:00:00.000Z`).toISOString();
|
|
207
|
+
const startDateTo = new Date(`${searchTo}T23:59:59.999Z`).toISOString();
|
|
208
|
+
|
|
209
|
+
setBusy(true);
|
|
210
|
+
try {
|
|
211
|
+
const res = await EventModel.getByDateRangeWithFilters(
|
|
212
|
+
companyKey,
|
|
213
|
+
startDateFrom,
|
|
214
|
+
startDateTo,
|
|
215
|
+
optsFromJson
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const events = (res?.events ?? []).map((e) =>
|
|
219
|
+
isStateTreeNode(e) ? getSnapshot(e) : (e?.toJSON?.() ?? e)
|
|
220
|
+
);
|
|
221
|
+
const totalCount = res?.totalCount ?? events.length;
|
|
222
|
+
setOutput(JSON.stringify({ totalCount, events }, null, 2));
|
|
223
|
+
} catch (err) {
|
|
224
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
225
|
+
} finally {
|
|
226
|
+
setBusy(false);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
156
230
|
return (
|
|
157
231
|
<>
|
|
158
232
|
<div className="card">
|
|
@@ -171,6 +245,60 @@ export function EventTab() {
|
|
|
171
245
|
</label>
|
|
172
246
|
</div>
|
|
173
247
|
|
|
248
|
+
<div className="card">
|
|
249
|
+
<h2>Search events (date range + filters)</h2>
|
|
250
|
+
<p className="muted small">
|
|
251
|
+
Calls <code>EventModel.getByDateRangeWithFilters</code> →
|
|
252
|
+
<code> GET /event/search/daterange/get</code> (company scope). Offset header comes from the{" "}
|
|
253
|
+
<code>offset</code> field above.
|
|
254
|
+
</p>
|
|
255
|
+
<form onSubmit={handleSearchByDateRange} className="form">
|
|
256
|
+
<label className="form__label">
|
|
257
|
+
<span>Company key</span>
|
|
258
|
+
<input
|
|
259
|
+
className="form__input"
|
|
260
|
+
value={searchCompanyKey}
|
|
261
|
+
onChange={(e) => setSearchCompanyKey(e.target.value)}
|
|
262
|
+
placeholder="company_key"
|
|
263
|
+
autoComplete="off"
|
|
264
|
+
/>
|
|
265
|
+
</label>
|
|
266
|
+
<div className="connection-card__row">
|
|
267
|
+
<label className="form__label">
|
|
268
|
+
<span>Start date (from)</span>
|
|
269
|
+
<input
|
|
270
|
+
type="date"
|
|
271
|
+
className="form__input"
|
|
272
|
+
value={searchFrom}
|
|
273
|
+
onChange={(e) => setSearchFrom(e.target.value)}
|
|
274
|
+
/>
|
|
275
|
+
</label>
|
|
276
|
+
<label className="form__label">
|
|
277
|
+
<span>Start date (to)</span>
|
|
278
|
+
<input
|
|
279
|
+
type="date"
|
|
280
|
+
className="form__input"
|
|
281
|
+
value={searchTo}
|
|
282
|
+
onChange={(e) => setSearchTo(e.target.value)}
|
|
283
|
+
/>
|
|
284
|
+
</label>
|
|
285
|
+
</div>
|
|
286
|
+
<label className="form__label">
|
|
287
|
+
<span>Filters (JSON)</span>
|
|
288
|
+
<textarea
|
|
289
|
+
className="form__textarea"
|
|
290
|
+
value={searchFiltersJson}
|
|
291
|
+
onChange={(e) => setSearchFiltersJson(e.target.value)}
|
|
292
|
+
spellCheck={false}
|
|
293
|
+
rows={10}
|
|
294
|
+
/>
|
|
295
|
+
</label>
|
|
296
|
+
<button type="submit" className="btn btn--secondary" disabled={busy}>
|
|
297
|
+
{busy ? "Loading…" : "Search"}
|
|
298
|
+
</button>
|
|
299
|
+
</form>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
174
302
|
<div className="card">
|
|
175
303
|
<h2>Create event</h2>
|
|
176
304
|
<form onSubmit={handleCreate} className="form">
|
|
@@ -2,7 +2,7 @@ import { useMemo, useState } from "react";
|
|
|
2
2
|
import {
|
|
3
3
|
CalendarModel,
|
|
4
4
|
deleteCalendarAsync,
|
|
5
|
-
|
|
5
|
+
fetchCalendarDetails,
|
|
6
6
|
updateCalendarAsync,
|
|
7
7
|
} from "appointment-client";
|
|
8
8
|
import { getSnapshot, isStateTreeNode } from "mobx-state-tree";
|
|
@@ -52,8 +52,10 @@ function explainFetchFailure(err, configuredBaseUrl) {
|
|
|
52
52
|
return `${msg}\n\n${proxyHint}`;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/** Opening hours list from
|
|
55
|
+
/** Opening hours list from `calendarView`, embedded `calendar.openingHours`, or legacy `openingHours`. */
|
|
56
56
|
function pickOpeningHoursListFromBundle(parsed) {
|
|
57
|
+
const fromView = parsed?.calendarView?.openingHours;
|
|
58
|
+
if (Array.isArray(fromView) && fromView.length > 0) return fromView;
|
|
57
59
|
const fromCal = parsed?.calendar?.openingHours;
|
|
58
60
|
if (Array.isArray(fromCal) && fromCal.length > 0) return fromCal;
|
|
59
61
|
const oh = parsed?.openingHours;
|
|
@@ -80,7 +82,7 @@ function OpeningHoursSummary({ outputJson }) {
|
|
|
80
82
|
return (
|
|
81
83
|
<p className="muted small" style={{ marginBottom: "0.75rem" }}>
|
|
82
84
|
No opening-hours rows parsed for the table. Check{" "}
|
|
83
|
-
<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.
|
|
84
86
|
</p>
|
|
85
87
|
);
|
|
86
88
|
}
|
|
@@ -217,48 +219,48 @@ export function FetchCalendarTab() {
|
|
|
217
219
|
|
|
218
220
|
setBusy(true);
|
|
219
221
|
try {
|
|
220
|
-
const
|
|
222
|
+
const details = await fetchCalendarDetails(id);
|
|
221
223
|
|
|
222
|
-
if (
|
|
224
|
+
if (details.cal == null) {
|
|
223
225
|
const raw = await CalendarModel.getRaw(id);
|
|
224
|
-
setNote(
|
|
225
|
-
"CalendarModel.get returned null. Showing CalendarModel.getRaw only (opening hours merge skipped)."
|
|
226
|
-
);
|
|
226
|
+
setNote("CalendarModel.get returned null. Showing CalendarModel.getRaw only.");
|
|
227
227
|
setOutput(toDisplayJson(raw));
|
|
228
228
|
return;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
const snap = getSnapshot(
|
|
231
|
+
const snap = getSnapshot(details.cal);
|
|
232
232
|
setLastFetchUpdatePayload(JSON.stringify(calendarSnapshotToUpdatePayload(snap), null, 2));
|
|
233
233
|
|
|
234
|
-
const [participants, participantsInfo] = await Promise.all([
|
|
235
|
-
bundle.cal.getParticipants(),
|
|
236
|
-
bundle.cal.getParticipantsInfo(),
|
|
237
|
-
]);
|
|
238
|
-
|
|
239
234
|
const payload = {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
participants,
|
|
246
|
-
participantsInfo
|
|
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,
|
|
247
244
|
__openingHoursMeta: {
|
|
248
|
-
fromCalendarGet:
|
|
249
|
-
fromParticipantApi:
|
|
250
|
-
|
|
251
|
-
|
|
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,
|
|
252
250
|
},
|
|
251
|
+
meta: details.meta,
|
|
253
252
|
};
|
|
254
253
|
|
|
255
254
|
setOutput(toDisplayJson(payload));
|
|
256
255
|
setNote(
|
|
257
|
-
|
|
258
|
-
? "
|
|
259
|
-
:
|
|
260
|
-
|
|
261
|
-
|
|
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.")
|
|
262
264
|
);
|
|
263
265
|
} catch (err) {
|
|
264
266
|
setError(explainFetchFailure(err, effective.baseUrl));
|
|
@@ -283,38 +285,46 @@ export function FetchCalendarTab() {
|
|
|
283
285
|
|
|
284
286
|
setBusy(true);
|
|
285
287
|
try {
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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));
|
|
290
295
|
} else {
|
|
291
296
|
const enriched = await Promise.all(
|
|
292
297
|
list.map(async (c) => {
|
|
293
298
|
const id = c.calendarId ?? String(c.id ?? "");
|
|
294
299
|
if (!id) return { calendar: getSnapshot(c), openingHours: [], meta: { error: "no id" } };
|
|
295
300
|
try {
|
|
296
|
-
const b = await
|
|
301
|
+
const b = await fetchCalendarDetails(id);
|
|
297
302
|
return {
|
|
303
|
+
calendarView: b.calendarView,
|
|
298
304
|
calendar: b.calendar ?? getSnapshot(c),
|
|
299
305
|
openingHours: b.openingHours,
|
|
306
|
+
participants: (b.participants ?? []).map((p) => (isStateTreeNode(p) ? getSnapshot(p) : p)),
|
|
300
307
|
__openingHoursMeta: {
|
|
301
308
|
fromCalendarGet: b.fromCalendarGet,
|
|
302
309
|
fromParticipantApi: b.fromParticipantApi,
|
|
310
|
+
calendarViewUsedAllParticipantOpeningHours: b.meta?.calendarViewUsedAllParticipantOpeningHours,
|
|
303
311
|
},
|
|
312
|
+
meta: b.meta,
|
|
304
313
|
};
|
|
305
314
|
} catch (err) {
|
|
306
315
|
return {
|
|
307
316
|
calendar: getSnapshot(c),
|
|
308
317
|
openingHours: [],
|
|
309
|
-
|
|
318
|
+
meta: {
|
|
310
319
|
error: err instanceof Error ? err.message : String(err),
|
|
311
320
|
},
|
|
312
321
|
};
|
|
313
322
|
}
|
|
314
323
|
})
|
|
315
324
|
);
|
|
325
|
+
const total = byCompany?.totalCount ?? list.length;
|
|
316
326
|
setNote(
|
|
317
|
-
`Loaded ${list.length} calendar(s); opening hours
|
|
327
|
+
`Loaded ${list.length} calendar(s) (totalCount=${total}); opening hours (embed → participant API) + participants per calendar.`
|
|
318
328
|
);
|
|
319
329
|
setOutput(toDisplayJson(enriched));
|
|
320
330
|
}
|
|
@@ -431,13 +441,32 @@ export function FetchCalendarTab() {
|
|
|
431
441
|
return (
|
|
432
442
|
<>
|
|
433
443
|
<div className="card">
|
|
434
|
-
<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({ baseUrl, consumer })</code> — same unified shape as{" "}
|
|
461
|
+
<code>calendarView</code> below. This tab runs <code>fetchCalendarDetails</code> so extra arrays stay visible.
|
|
462
|
+
</p>
|
|
435
463
|
<p className="muted small">
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
<code>
|
|
440
|
-
<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 ("All") 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.
|
|
441
470
|
</p>
|
|
442
471
|
<p className="muted small">
|
|
443
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({})</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,
|
|
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
|
|
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
|
+
}
|