@blazeo.com/appointment-client 1.0.6 → 1.0.8
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/Appointment-Client/.gitattributes +2 -0
- package/blazeo.com-appointment-client-1.0.8.tgz +0 -0
- package/dist/calendar/buildUnifiedCalendarView.d.ts +8 -0
- package/dist/calendar/buildUnifiedCalendarView.js +29 -8
- package/dist/calendar/fetchCalendarDetails.d.ts +8 -40
- package/dist/calendar/fetchCalendarDetails.js +117 -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 +70 -1
- package/sample/package.json +1 -0
- 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 +94 -70
- 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 +35 -7
- package/src/calendar/fetchCalendarDetails.ts +318 -226
- package/src/calendar/fetchCalendarWithOpeningHours.ts +130 -99
- package/src/calendar/getAllParticipantOpeningHours.ts +9 -1
- package/src/calendar/getOpeningHours.ts +2 -2
- package/src/calendar/getParticipantOpeningHours.ts +14 -5
- package/src/calendar/getParticipants.ts +9 -1
- package/src/calendar/mapToDesiredResponse.ts +104 -0
- package/src/config/applyBlazeoDefaults.ts +3 -2
- package/src/config/blazeoClientDefaults.ts +2 -2
- package/src/config/ensureBlazeoHttpReady.ts +41 -0
- package/src/config/initializeAppointmentClient.ts +9 -3
- package/src/config/syncBlazeoConnection.ts +19 -0
- package/src/index.ts +7 -1
- package/blazeo.com-appointment-client-1.0.6.tgz +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useMemo, useState } from "react";
|
|
2
|
-
import { createCalendarAsync, createCalendarWithRelationsAsync } from "appointment-client";
|
|
2
|
+
import { createCalendarAsync, createCalendarWithRelationsAsync, ensureBlazeoHttpReady } from "appointment-client";
|
|
3
3
|
import { getSnapshot } from "mobx-state-tree";
|
|
4
4
|
import { configureBlazeoFromEffective, useBlazeoConnection } from "./BlazeoConnectionSettings.jsx";
|
|
5
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
5
6
|
|
|
6
7
|
/** Demo payload aligned with `CalendarBOInput` / server `CalendarBO`. */
|
|
7
8
|
export function getExampleCalendarBOInput() {
|
|
@@ -38,7 +39,7 @@ export function getExampleCalendarBOInput() {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
export function CreateCalendarTab() {
|
|
41
|
-
const { effective } = useBlazeoConnection();
|
|
42
|
+
const { effective, connectionOpts } = useBlazeoConnection();
|
|
42
43
|
const [localOnly, setLocalOnly] = useState(false);
|
|
43
44
|
const [saveRelations, setSaveRelations] = useState(true);
|
|
44
45
|
const [busy, setBusy] = useState(false);
|
|
@@ -70,7 +71,13 @@ export function CreateCalendarTab() {
|
|
|
70
71
|
setError("Set Base URL in the connection card above.");
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
73
|
-
if (!localOnly)
|
|
74
|
+
if (!localOnly) {
|
|
75
|
+
configureBlazeoFromEffective(effective);
|
|
76
|
+
ensureBlazeoHttpReady({
|
|
77
|
+
baseUrl: effective.baseUrl,
|
|
78
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
74
81
|
|
|
75
82
|
const hasRelations = (payload.members?.length ?? 0) > 0 || (payload.openingHours?.length ?? 0) > 0;
|
|
76
83
|
const useRelations = saveRelations && hasRelations && !localOnly;
|
|
@@ -78,10 +85,20 @@ export function CreateCalendarTab() {
|
|
|
78
85
|
setBusy(true);
|
|
79
86
|
try {
|
|
80
87
|
const result = useRelations
|
|
81
|
-
? await createCalendarWithRelationsAsync(payload, {
|
|
82
|
-
|
|
88
|
+
? await createCalendarWithRelationsAsync(payload, {
|
|
89
|
+
localOnly,
|
|
90
|
+
...connectionOpts,
|
|
91
|
+
baseUrl: effective.baseUrl,
|
|
92
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
93
|
+
})
|
|
94
|
+
: await createCalendarAsync(payload, {
|
|
95
|
+
localOnly,
|
|
96
|
+
...connectionOpts,
|
|
97
|
+
baseUrl: effective.baseUrl,
|
|
98
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
99
|
+
});
|
|
83
100
|
setOutput(JSON.stringify(result.ok ? getSnapshot(result.calendar) : result, null, 2));
|
|
84
|
-
if (!result.ok) setError(result.error);
|
|
101
|
+
if (!result.ok) setError(mapBlazeoDemoError(result.error));
|
|
85
102
|
} finally {
|
|
86
103
|
setBusy(false);
|
|
87
104
|
}
|
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
|
+
ensureBlazeoHttpReady,
|
|
5
6
|
EventModel,
|
|
6
7
|
rescheduleAppointmentEventAsync,
|
|
7
8
|
} from "appointment-client";
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
configureBlazeoFromEffective,
|
|
11
12
|
useBlazeoConnection,
|
|
12
13
|
} from "./BlazeoConnectionSettings.jsx";
|
|
14
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
13
15
|
|
|
14
16
|
function getExampleCreatePayload() {
|
|
15
17
|
const start = new Date();
|
|
@@ -75,7 +77,7 @@ function safeJsonParse(text, fallback) {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
export function EventTab() {
|
|
78
|
-
const { effective } = useBlazeoConnection();
|
|
80
|
+
const { effective, connectionOpts } = useBlazeoConnection();
|
|
79
81
|
const [offsetMinutes, setOffsetMinutes] = useState(-new Date().getTimezoneOffset());
|
|
80
82
|
const [createJson, setCreateJson] = useState(() =>
|
|
81
83
|
JSON.stringify(getExampleCreatePayload(), null, 2)
|
|
@@ -118,6 +120,7 @@ export function EventTab() {
|
|
|
118
120
|
const [output, setOutput] = useState("");
|
|
119
121
|
|
|
120
122
|
const opts = useMemo(() => ({ offsetMinutes: Number(offsetMinutes) || 0 }), [offsetMinutes]);
|
|
123
|
+
const eventOpts = useMemo(() => ({ ...opts, ...connectionOpts }), [opts, connectionOpts]);
|
|
121
124
|
|
|
122
125
|
function ensureBase() {
|
|
123
126
|
if (!effective.baseUrl) {
|
|
@@ -133,6 +136,10 @@ export function EventTab() {
|
|
|
133
136
|
setOutput("");
|
|
134
137
|
if (!ensureBase()) return;
|
|
135
138
|
configureBlazeoFromEffective(effective);
|
|
139
|
+
ensureBlazeoHttpReady({
|
|
140
|
+
baseUrl: effective.baseUrl,
|
|
141
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
142
|
+
});
|
|
136
143
|
let payload;
|
|
137
144
|
try {
|
|
138
145
|
payload = JSON.parse(createJson);
|
|
@@ -142,9 +149,9 @@ export function EventTab() {
|
|
|
142
149
|
}
|
|
143
150
|
setBusy(true);
|
|
144
151
|
try {
|
|
145
|
-
const result = await createAppointmentEventAsync(payload,
|
|
152
|
+
const result = await createAppointmentEventAsync(payload, eventOpts);
|
|
146
153
|
setOutput(resultToJson(result));
|
|
147
|
-
if (!result.ok) setError(result.error);
|
|
154
|
+
if (!result.ok) setError(mapBlazeoDemoError(result.error));
|
|
148
155
|
} finally {
|
|
149
156
|
setBusy(false);
|
|
150
157
|
}
|
|
@@ -156,6 +163,10 @@ export function EventTab() {
|
|
|
156
163
|
setOutput("");
|
|
157
164
|
if (!ensureBase()) return;
|
|
158
165
|
configureBlazeoFromEffective(effective);
|
|
166
|
+
ensureBlazeoHttpReady({
|
|
167
|
+
baseUrl: effective.baseUrl,
|
|
168
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
169
|
+
});
|
|
159
170
|
let payload;
|
|
160
171
|
try {
|
|
161
172
|
payload = JSON.parse(rescheduleJson);
|
|
@@ -165,9 +176,9 @@ export function EventTab() {
|
|
|
165
176
|
}
|
|
166
177
|
setBusy(true);
|
|
167
178
|
try {
|
|
168
|
-
const result = await rescheduleAppointmentEventAsync(payload,
|
|
179
|
+
const result = await rescheduleAppointmentEventAsync(payload, eventOpts);
|
|
169
180
|
setOutput(resultToJson(result));
|
|
170
|
-
if (!result.ok) setError(result.error);
|
|
181
|
+
if (!result.ok) setError(mapBlazeoDemoError(result.error));
|
|
171
182
|
} finally {
|
|
172
183
|
setBusy(false);
|
|
173
184
|
}
|
|
@@ -181,11 +192,19 @@ export function EventTab() {
|
|
|
181
192
|
if (!id) return setError("Enter Blazeo event id to cancel.");
|
|
182
193
|
if (!ensureBase()) return;
|
|
183
194
|
configureBlazeoFromEffective(effective);
|
|
195
|
+
ensureBlazeoHttpReady({
|
|
196
|
+
baseUrl: effective.baseUrl,
|
|
197
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
198
|
+
});
|
|
184
199
|
setBusy(true);
|
|
185
200
|
try {
|
|
186
|
-
const result = await cancelAppointmentEventAsync(id, {
|
|
201
|
+
const result = await cancelAppointmentEventAsync(id, {
|
|
202
|
+
...connectionOpts,
|
|
203
|
+
baseUrl: effective.baseUrl,
|
|
204
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
205
|
+
});
|
|
187
206
|
setOutput(JSON.stringify(result, null, 2));
|
|
188
|
-
if (!result.ok) setError(result.error);
|
|
207
|
+
if (!result.ok) setError(mapBlazeoDemoError(result.error));
|
|
189
208
|
} finally {
|
|
190
209
|
setBusy(false);
|
|
191
210
|
}
|
|
@@ -201,6 +220,10 @@ export function EventTab() {
|
|
|
201
220
|
if (!searchTo) return setError("Pick end date.");
|
|
202
221
|
if (!ensureBase()) return;
|
|
203
222
|
configureBlazeoFromEffective(effective);
|
|
223
|
+
ensureBlazeoHttpReady({
|
|
224
|
+
baseUrl: effective.baseUrl,
|
|
225
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
226
|
+
});
|
|
204
227
|
|
|
205
228
|
const optsFromJson = safeJsonParse(searchFiltersJson, {});
|
|
206
229
|
const startDateFrom = new Date(`${searchFrom}T00:00:00.000Z`).toISOString();
|
|
@@ -221,7 +244,7 @@ export function EventTab() {
|
|
|
221
244
|
const totalCount = res?.totalCount ?? events.length;
|
|
222
245
|
setOutput(JSON.stringify({ totalCount, events }, null, 2));
|
|
223
246
|
} catch (err) {
|
|
224
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
247
|
+
setError(mapBlazeoDemoError(err instanceof Error ? err.message : String(err)));
|
|
225
248
|
} finally {
|
|
226
249
|
setBusy(false);
|
|
227
250
|
}
|
|
@@ -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,19 +304,14 @@ 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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
fromParticipantApi: b.fromParticipantApi,
|
|
310
|
-
calendarViewUsedAllParticipantOpeningHours: b.meta?.calendarViewUsedAllParticipantOpeningHours,
|
|
311
|
-
},
|
|
312
|
-
meta: b.meta,
|
|
313
|
-
};
|
|
307
|
+
// fetchCalendarDetails now returns the flat unified view directly
|
|
308
|
+
const b = await fetchCalendarDetails(id, {
|
|
309
|
+
...connectionOpts,
|
|
310
|
+
baseUrl: effective.baseUrl,
|
|
311
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
312
|
+
});
|
|
313
|
+
// b IS the unified calendarView: members/openingHours/participants at top level
|
|
314
|
+
return b ?? { calendar: getSnapshot(c), openingHours: [], meta: { error: "null response" } };
|
|
314
315
|
} catch (err) {
|
|
315
316
|
return {
|
|
316
317
|
calendar: getSnapshot(c),
|
|
@@ -369,6 +370,10 @@ export function FetchCalendarTab() {
|
|
|
369
370
|
setMutateOutput("");
|
|
370
371
|
if (!ensureBaseConfigured()) return;
|
|
371
372
|
configureBlazeoFromEffective(effective);
|
|
373
|
+
ensureBlazeoHttpReady({
|
|
374
|
+
baseUrl: effective.baseUrl,
|
|
375
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
376
|
+
});
|
|
372
377
|
let payload;
|
|
373
378
|
try {
|
|
374
379
|
payload = JSON.parse(updateJson);
|
|
@@ -378,7 +383,11 @@ export function FetchCalendarTab() {
|
|
|
378
383
|
}
|
|
379
384
|
setBusy(true);
|
|
380
385
|
try {
|
|
381
|
-
const result = await updateCalendarAsync(payload, {
|
|
386
|
+
const result = await updateCalendarAsync(payload, {
|
|
387
|
+
...connectionOpts,
|
|
388
|
+
baseUrl: effective.baseUrl,
|
|
389
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
390
|
+
});
|
|
382
391
|
if (result.ok) {
|
|
383
392
|
setMutateNote("updateCalendarAsync → POST /Calendar/Event/Update");
|
|
384
393
|
setMutateOutput(
|
|
@@ -392,7 +401,7 @@ export function FetchCalendarTab() {
|
|
|
392
401
|
)
|
|
393
402
|
);
|
|
394
403
|
} else {
|
|
395
|
-
setError(result.error);
|
|
404
|
+
setError(mapBlazeoDemoError(result.error));
|
|
396
405
|
if (result.apiResponse != null) {
|
|
397
406
|
setMutateOutput(JSON.stringify(result.apiResponse, null, 2));
|
|
398
407
|
}
|
|
@@ -414,6 +423,10 @@ export function FetchCalendarTab() {
|
|
|
414
423
|
}
|
|
415
424
|
if (!ensureBaseConfigured()) return;
|
|
416
425
|
configureBlazeoFromEffective(effective);
|
|
426
|
+
ensureBlazeoHttpReady({
|
|
427
|
+
baseUrl: effective.baseUrl,
|
|
428
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
429
|
+
});
|
|
417
430
|
if (
|
|
418
431
|
!window.confirm(
|
|
419
432
|
`Delete calendar "${id}"?\n\nThis calls GET /Calendar/Remove (cannot be undone on the server).`
|
|
@@ -423,12 +436,16 @@ export function FetchCalendarTab() {
|
|
|
423
436
|
}
|
|
424
437
|
setBusy(true);
|
|
425
438
|
try {
|
|
426
|
-
const result = await deleteCalendarAsync(id, {
|
|
439
|
+
const result = await deleteCalendarAsync(id, {
|
|
440
|
+
...connectionOpts,
|
|
441
|
+
baseUrl: effective.baseUrl,
|
|
442
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
443
|
+
});
|
|
427
444
|
if (result.ok) {
|
|
428
445
|
setMutateNote("deleteCalendarAsync → GET /Calendar/Remove");
|
|
429
446
|
setMutateOutput(JSON.stringify({ calendarId: id, apiResponse: result.apiResponse ?? null }, null, 2));
|
|
430
447
|
} else {
|
|
431
|
-
setError(result.error);
|
|
448
|
+
setError(mapBlazeoDemoError(result.error));
|
|
432
449
|
if (result.apiResponse != null) {
|
|
433
450
|
setMutateOutput(JSON.stringify(result.apiResponse, null, 2));
|
|
434
451
|
}
|
|
@@ -443,29 +460,29 @@ export function FetchCalendarTab() {
|
|
|
443
460
|
<div className="card">
|
|
444
461
|
<h2>Fetch calendar · calendarView</h2>
|
|
445
462
|
<p className="muted small">
|
|
446
|
-
Runs <code>fetchCalendarDetails(calendarId)</code>. JSON
|
|
447
|
-
calendar snapshot fields + <code>members</code> + <code>openingHours</code>,
|
|
448
|
-
where each participant
|
|
463
|
+
Runs <code>fetchCalendarDetails(calendarId)</code>. The JSON shown below <strong>is</strong> the unified calendar view:
|
|
464
|
+
one object with calendar snapshot fields + <code>members</code> + <code>openingHours</code>, plus a{" "}
|
|
465
|
+
<code>participants</code> array where each participant may include nested <code>openingHours</code>. (
|
|
466
|
+
<code>fetchCalendarBundle(calendarId)</code> returns the same shape.)
|
|
449
467
|
</p>
|
|
450
468
|
<p className="muted small">
|
|
451
469
|
Uses <code>fetchCalendarDetails</code>: legacy <code>openingHours</code> prefers embed on{" "}
|
|
452
470
|
<code>CalendarModel.getRaw</code>, else <code>getParticipantOpeningHours</code>.{" "}
|
|
453
|
-
<code>
|
|
471
|
+
Unified <code>openingHours</code> prefers <code>getAllParticipantOpeningHours</code> (
|
|
454
472
|
<code>GET /Calendar/Participant/OpeningHours/All/Get</code>) when the API returns rows. Members combine{" "}
|
|
455
473
|
<code>CalendarModel.getParticipants</code> + <code>CalendarModel.getParticipantsInfo</code> (each member may
|
|
456
474
|
include <code>participantInfo</code>).
|
|
457
475
|
</p>
|
|
458
476
|
<p className="muted small">
|
|
459
|
-
<strong>
|
|
460
|
-
<code>initializeAppointmentClient({ baseUrl, consumer })</code>
|
|
461
|
-
<code>
|
|
477
|
+
<strong>In code:</strong> <code>fetchCalendarBundle(calendarId)</code> (after{" "}
|
|
478
|
+
<code>initializeAppointmentClient({ baseUrl, consumer })</code>) is an alias for the same unified fetch;
|
|
479
|
+
use either alongside explicit <code>baseUrl</code> / <code>consumer</code> options when needed.
|
|
462
480
|
</p>
|
|
463
481
|
<p className="muted small">
|
|
464
482
|
<strong>DevTools Network:</strong> Each fetch fires <code>/Calendar/Get</code> <strong>twice</strong> (
|
|
465
483
|
<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
|
-
|
|
468
|
-
widen the Network filter ("All") or disable search; if <code>calendarView</code> is empty/missing fields,
|
|
484
|
+
<code>Participant</code>, <code>OpeningHours</code>, or <code>GetInfo</code>. Those power the unified view. If you only see <code>Calendar/Get</code> yet the UI JSON has members/hours,
|
|
485
|
+
widen the Network filter ("All") or disable search; if the unified object is empty/missing fields,
|
|
469
486
|
check the <strong>Console</strong> for errors on the participant/opening-hours requests.
|
|
470
487
|
</p>
|
|
471
488
|
<p className="muted small">
|
|
@@ -541,11 +558,18 @@ export function FetchCalendarTab() {
|
|
|
541
558
|
<div className="card">
|
|
542
559
|
<h2>Fetch calendars by company</h2>
|
|
543
560
|
<p className="muted small">
|
|
544
|
-
|
|
545
|
-
|
|
561
|
+
Step 1: <code>CalendarModel.getByCompany</code> → <code>GET /Calendar/All</code> (company key → calendar
|
|
562
|
+
list). Step 2: for each calendar id, this tab runs the same{" "}
|
|
563
|
+
<code>fetchCalendarDetails(calendarId)</code> pipeline as <strong>Fetch calendar · calendarView</strong>, so
|
|
564
|
+
the JSON below is an <strong>array</strong> of unified objects (members, openingHours, participants — same
|
|
565
|
+
shape as a single fetch). If an id is missing from the list row, that entry falls back to the raw snapshot
|
|
566
|
+
only.
|
|
567
|
+
</p>
|
|
568
|
+
<p className="muted small">
|
|
569
|
+
If the UI shows <strong>Failed to fetch</strong> while Base URL points at Azure/production, that is usually{" "}
|
|
546
570
|
<strong>CORS</strong>: enable proxy via <code>VITE_DEV_PROXY_TARGET</code> in{" "}
|
|
547
|
-
<code>sample/.env.development</code> and Base URL <code>http://localhost:5173/blazeo-api</code>
|
|
548
|
-
|
|
571
|
+
<code>sample/.env.development</code> and Base URL <code>http://localhost:5173/blazeo-api</code> (restart dev
|
|
572
|
+
server).
|
|
549
573
|
</p>
|
|
550
574
|
<form onSubmit={handleFetchByCompany} className="form">
|
|
551
575
|
<label className="form__label">
|
|
@@ -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
|
+
}
|