@blazeo.com/appointment-client 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blazeo.com-appointment-client-1.0.7.tgz +0 -0
- package/dist/calendar/buildUnifiedCalendarView.d.ts +8 -0
- package/dist/calendar/buildUnifiedCalendarView.js +20 -5
- package/dist/calendar/fetchCalendarDetails.d.ts +8 -40
- package/dist/calendar/fetchCalendarDetails.js +116 -47
- package/dist/calendar/fetchCalendarWithOpeningHours.d.ts +1 -10
- package/dist/calendar/fetchCalendarWithOpeningHours.js +34 -15
- package/dist/calendar/getAllParticipantOpeningHours.d.ts +4 -1
- package/dist/calendar/getAllParticipantOpeningHours.js +6 -1
- package/dist/calendar/getOpeningHours.d.ts +4 -1
- package/dist/calendar/getOpeningHours.js +2 -2
- package/dist/calendar/getParticipantOpeningHours.js +9 -4
- package/dist/calendar/getParticipants.d.ts +4 -1
- package/dist/calendar/getParticipants.js +6 -1
- package/dist/calendar/mapToDesiredResponse.d.ts +70 -0
- package/dist/calendar/mapToDesiredResponse.js +99 -0
- package/dist/config/applyBlazeoDefaults.js +3 -2
- package/dist/config/blazeoClientDefaults.js +2 -2
- package/dist/config/ensureBlazeoHttpReady.d.ts +17 -0
- package/dist/config/ensureBlazeoHttpReady.js +31 -0
- package/dist/config/initializeAppointmentClient.d.ts +4 -0
- package/dist/config/initializeAppointmentClient.js +9 -3
- package/dist/config/syncBlazeoConnection.d.ts +6 -0
- package/dist/config/syncBlazeoConnection.js +18 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/sample/.env.example +5 -0
- package/sample/package-lock.json +1 -1
- package/sample/src/AllParticipantOpeningHoursTab.jsx +13 -4
- package/sample/src/App2.jsx +22 -2
- package/sample/src/AvailabilityTab.jsx +8 -3
- package/sample/src/BlazeoConnectionSettings.jsx +16 -15
- package/sample/src/CreateCalendarTab.jsx +23 -6
- package/sample/src/EventTab.jsx +31 -8
- package/sample/src/FetchCalendarTab.jsx +70 -44
- package/sample/src/OpeningHoursTab.jsx +13 -4
- package/sample/src/ParticipantInfoTab.jsx +8 -3
- package/sample/src/ParticipantOpeningHoursTab.jsx +17 -7
- package/sample/src/ParticipantTab.jsx +13 -4
- package/sample/src/blazeoBootstrap.js +30 -0
- package/sample/src/blazeoDemoError.js +14 -0
- package/sample/src/blazeoPushConnection.js +23 -0
- package/sample/src/main.jsx +3 -3
- package/sample/vite.config.js +19 -5
- package/src/calendar/buildUnifiedCalendarView.ts +28 -5
- package/src/calendar/fetchCalendarDetails.ts +316 -226
- package/src/calendar/fetchCalendarWithOpeningHours.ts +130 -99
- package/src/calendar/getAllParticipantOpeningHours.ts +9 -1
- package/src/calendar/getOpeningHours.ts +2 -2
- package/src/calendar/getParticipantOpeningHours.ts +14 -5
- package/src/calendar/getParticipants.ts +9 -1
- package/src/calendar/mapToDesiredResponse.ts +104 -0
- package/src/config/applyBlazeoDefaults.ts +3 -2
- package/src/config/blazeoClientDefaults.ts +2 -2
- package/src/config/ensureBlazeoHttpReady.ts +41 -0
- package/src/config/initializeAppointmentClient.ts +9 -3
- package/src/config/syncBlazeoConnection.ts +19 -0
- package/src/index.ts +7 -1
- package/blazeo.com-appointment-client-1.0.6.tgz +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps a raw Blazeo API calendar payload (often PascalCase) to the specific camelCase
|
|
3
|
+
* shape requested by the user, including typenames and normalized fields.
|
|
4
|
+
*/
|
|
5
|
+
export function mapToDesiredCalendarResponse(payload, openingHours = [], members = []) {
|
|
6
|
+
if (!payload)
|
|
7
|
+
return null;
|
|
8
|
+
const pick = (obj, ...keys) => {
|
|
9
|
+
for (const k of keys) {
|
|
10
|
+
if (obj[k] !== undefined && obj[k] !== null)
|
|
11
|
+
return obj[k];
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
};
|
|
15
|
+
const n = (v) => (v != null && v !== "" ? Number(v) : null);
|
|
16
|
+
// Map members with typename
|
|
17
|
+
const mappedMembers = members.map(m => ({
|
|
18
|
+
id: m.id ?? pick(m, "id", "Id"),
|
|
19
|
+
name: m.name ?? pick(m, "name", "Name", "alias", "Alias"),
|
|
20
|
+
email: m.email ?? pick(m, "email", "Email") ?? null,
|
|
21
|
+
status: m.status ?? pick(m, "status", "Status") ?? 0,
|
|
22
|
+
__typename: "Member"
|
|
23
|
+
}));
|
|
24
|
+
// Map opening hours with typename and raw fields
|
|
25
|
+
const mappedOpeningHours = openingHours.map(oh => ({
|
|
26
|
+
id: pick(oh, "id", "Id") ?? 0,
|
|
27
|
+
createdOn: pick(oh, "createdOn", "CreatedOn", "created_on") ?? "0001-01-01T00:00:00.000Z",
|
|
28
|
+
modifiedOn: pick(oh, "modifiedOn", "ModifiedOn", "modified_on") ?? "0001-01-01T00:00:00.000Z",
|
|
29
|
+
member: pick(oh, "member", "Member"),
|
|
30
|
+
openingHourId: pick(oh, "openingHourId", "OpeningHourId", "opening_hour_id") ?? "",
|
|
31
|
+
calendarId: pick(oh, "calendarId", "CalendarId", "calendar_id") ?? "",
|
|
32
|
+
participantId: pick(oh, "participantId", "ParticipantId", "participant_id") ?? "",
|
|
33
|
+
days: oh.days ?? [],
|
|
34
|
+
startHour: oh.startHour ?? pick(oh, "startHour", "StartHour") ?? 0,
|
|
35
|
+
startMinute: oh.startMinute ?? pick(oh, "startMinute", "StartMinute") ?? 0,
|
|
36
|
+
endHour: oh.endHour ?? pick(oh, "endHour", "EndHour") ?? 0,
|
|
37
|
+
endMinute: oh.endMinute ?? pick(oh, "endMinute", "EndMinute") ?? 0,
|
|
38
|
+
off: !!(oh.off ?? pick(oh, "off", "Off")),
|
|
39
|
+
__typename: "OpeningHour"
|
|
40
|
+
}));
|
|
41
|
+
// Map theme
|
|
42
|
+
const rawTheme = pick(payload, "theme", "Theme");
|
|
43
|
+
const theme = rawTheme ? {
|
|
44
|
+
id: pick(rawTheme, "id", "Id"),
|
|
45
|
+
color: pick(rawTheme, "color", "Color"),
|
|
46
|
+
logoUrl: pick(rawTheme, "logoUrl", "LogoUrl") ?? null,
|
|
47
|
+
__typename: "Theme"
|
|
48
|
+
} : null;
|
|
49
|
+
// Map reminders
|
|
50
|
+
const rawReminders = pick(payload, "reminderChannelStatuses", "ReminderChannelStatuses") ?? [];
|
|
51
|
+
const reminderChannelStatuses = Array.isArray(rawReminders) ? rawReminders.map((r) => ({
|
|
52
|
+
id: pick(r, "id", "Id"),
|
|
53
|
+
calendarId: pick(r, "calendarId", "CalendarId"),
|
|
54
|
+
channelId: pick(r, "channelId", "ChannelId"),
|
|
55
|
+
status: !!pick(r, "status", "Status"),
|
|
56
|
+
appointmentReminders: (pick(r, "appointmentReminders", "AppointmentReminders") ?? []).map((ar) => ({
|
|
57
|
+
id: pick(ar, "id", "Id"),
|
|
58
|
+
reminderChannelStatusId: pick(ar, "reminderChannelStatusId", "ReminderChannelStatusId"),
|
|
59
|
+
recipientType: pick(ar, "recipientType", "RecipientType"),
|
|
60
|
+
beforeEventTime: pick(ar, "beforeEventTime", "BeforeEventTime"),
|
|
61
|
+
unit: pick(ar, "unit", "Unit"),
|
|
62
|
+
__typename: "AppointmentReminder"
|
|
63
|
+
})),
|
|
64
|
+
__typename: "ReminderChannelStatus"
|
|
65
|
+
})) : [];
|
|
66
|
+
return {
|
|
67
|
+
id: n(pick(payload, "id", "Id")),
|
|
68
|
+
durationUnit: n(pick(payload, "durationUnit", "DurationUnit")),
|
|
69
|
+
minimumBookingNotice: n(pick(payload, "minimumBookingNotice", "MinimumBookingNotice")),
|
|
70
|
+
minimumBookingNoticeUnit: n(pick(payload, "minimumBookingNoticeUnit", "MinimumBookingNoticeUnit")),
|
|
71
|
+
minimumCancelationNotice: n(pick(payload, "minimumCancelationNotice", "MinimumCancelationNotice")),
|
|
72
|
+
minimumCancelationNoticeUnit: n(pick(payload, "minimumCancelationNoticeUnit", "MinimumCancelationNoticeUnit")),
|
|
73
|
+
futureLimit: n(pick(payload, "futureLimit", "FutureLimit")),
|
|
74
|
+
futureLimitUnit: n(pick(payload, "futureLimitUnit", "FutureLimitUnit")),
|
|
75
|
+
bufferTime: n(pick(payload, "bufferTime", "BufferTime")),
|
|
76
|
+
bufferTimeUnit: n(pick(payload, "bufferTimeUnit", "BufferTimeUnit")),
|
|
77
|
+
calendarLink: pick(payload, "calendarLink", "CalendarLink"),
|
|
78
|
+
uuid: pick(payload, "uuid", "Uuid", "calendarId", "CalendarId"),
|
|
79
|
+
location: pick(payload, "location", "Location") ?? "",
|
|
80
|
+
bookingPageTitle: pick(payload, "bookingPageTitle", "BookingPageTitle") ?? null,
|
|
81
|
+
reminderChannelStatuses,
|
|
82
|
+
members: mappedMembers,
|
|
83
|
+
createdOn: pick(payload, "createdOn", "CreatedOn") ?? "0001-01-01T00:00:00.000Z",
|
|
84
|
+
modifiedOn: pick(payload, "modifiedOn", "ModifiedOn") ?? "0001-01-01T00:00:00.000Z",
|
|
85
|
+
name: pick(payload, "name", "Name"),
|
|
86
|
+
timeZoneId: pick(payload, "timeZoneId", "TimeZoneId"),
|
|
87
|
+
description: pick(payload, "description", "Description") ?? "",
|
|
88
|
+
assignmentType: n(pick(payload, "assignmentType", "AssignmentType", "assignmentMethod", "AssignmentMethod")),
|
|
89
|
+
duration: n(pick(payload, "duration", "Duration")),
|
|
90
|
+
bookingLimit: n(pick(payload, "bookingLimit", "BookingLimit")),
|
|
91
|
+
calendarJson: pick(payload, "calendarJson", "CalendarJson") ?? null,
|
|
92
|
+
isThirdPartySaved: !!pick(payload, "isThirdPartySaved", "IsThirdPartySaved"),
|
|
93
|
+
themeId: n(pick(payload, "themeId", "ThemeId")),
|
|
94
|
+
theme,
|
|
95
|
+
openingHours: mappedOpeningHours,
|
|
96
|
+
appointmentUserDefinedFields: pick(payload, "appointmentUserDefinedFields", "AppointmentUserDefinedFields") ?? [],
|
|
97
|
+
__typename: "Calendar"
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { configure } from "@blazeo.com/calendar-client";
|
|
2
1
|
import { blazeoClientConfig } from "./blazeoClientDefaults.js";
|
|
2
|
+
import { syncBlazeoConnection } from "./syncBlazeoConnection.js";
|
|
3
3
|
/** Push {@link blazeoClientConfig} into `@blazeo.com/calendar-client` (global store). Call explicitly if you use file defaults; otherwise use {@link initializeAppointmentClient}. */
|
|
4
4
|
export function applyBlazeoClientConfig() {
|
|
5
|
+
debugger;
|
|
5
6
|
const baseUrl = blazeoClientConfig.baseUrl?.trim().replace(/\/+$/, "");
|
|
6
7
|
if (!baseUrl)
|
|
7
8
|
return;
|
|
8
9
|
const consumer = blazeoClientConfig.consumer?.trim();
|
|
9
|
-
|
|
10
|
+
syncBlazeoConnection({
|
|
10
11
|
baseUrl,
|
|
11
12
|
...(consumer ? { consumer } : {}),
|
|
12
13
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type EnsureBlazeoHttpOptions = {
|
|
2
|
+
baseUrl?: string;
|
|
3
|
+
consumer?: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Ensures global Blazeo `configure({ baseUrl })` runs before any `CalendarModel` / `EventModel` HTTP.
|
|
7
|
+
* Uses the same resolution as {@link resolveBlazeoConnection}: explicit args, existing `getConfig()`,
|
|
8
|
+
* then `blazeoClientDefaults` — so file defaults apply even if the host never called `configure`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ensureBlazeoHttpReady(options?: EnsureBlazeoHttpOptions): {
|
|
11
|
+
ok: true;
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
consumer?: string;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
error: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { configure } from "@blazeo.com/calendar-client";
|
|
2
|
+
import { resolveBlazeoConnection } from "../calendar/createCalendar.js";
|
|
3
|
+
/**
|
|
4
|
+
* Ensures global Blazeo `configure({ baseUrl })` runs before any `CalendarModel` / `EventModel` HTTP.
|
|
5
|
+
* Uses the same resolution as {@link resolveBlazeoConnection}: explicit args, existing `getConfig()`,
|
|
6
|
+
* then `blazeoClientDefaults` — so file defaults apply even if the host never called `configure`.
|
|
7
|
+
*/
|
|
8
|
+
export function ensureBlazeoHttpReady(options = {}) {
|
|
9
|
+
// Hard-prefer explicit args (call-site can bypass any module-resolution mismatch).
|
|
10
|
+
const explicitBase = options.baseUrl?.trim().replace(/\/+$/, "");
|
|
11
|
+
const explicitConsumer = options.consumer?.trim() || undefined;
|
|
12
|
+
if (explicitBase) {
|
|
13
|
+
configure({
|
|
14
|
+
baseUrl: explicitBase,
|
|
15
|
+
...(explicitConsumer ? { consumer: explicitConsumer } : {}),
|
|
16
|
+
});
|
|
17
|
+
return { ok: true, baseUrl: explicitBase, ...(explicitConsumer ? { consumer: explicitConsumer } : {}) };
|
|
18
|
+
}
|
|
19
|
+
const { baseUrl, consumer } = resolveBlazeoConnection(options);
|
|
20
|
+
if (!baseUrl) {
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
error: "Blazeo base URL is not set. Call initializeAppointmentClient({ baseUrl }) or configure({ baseUrl }) at app startup, set blazeoClientConfig.baseUrl, or pass baseUrl when calling fetch APIs.",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
configure({
|
|
27
|
+
baseUrl,
|
|
28
|
+
...(consumer ? { consumer } : {}),
|
|
29
|
+
});
|
|
30
|
+
return { ok: true, baseUrl, ...(consumer ? { consumer } : {}) };
|
|
31
|
+
}
|
|
@@ -3,5 +3,9 @@ export interface AppointmentClientConfig {
|
|
|
3
3
|
consumer?: string;
|
|
4
4
|
fetch?: typeof fetch;
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Applies Blazeo connection (same as {@link syncBlazeoConnection}) and marks the client as configured
|
|
8
|
+
* when a non-empty `baseUrl` was written to `@blazeo.com/calendar-client`.
|
|
9
|
+
*/
|
|
6
10
|
export declare function initializeAppointmentClient(config: AppointmentClientConfig): void;
|
|
7
11
|
export declare function isAppointmentClientConfigured(): boolean;
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getConfig } from "@blazeo.com/calendar-client";
|
|
2
|
+
import { syncBlazeoConnection } from "./syncBlazeoConnection.js";
|
|
2
3
|
let isConfigured = false;
|
|
4
|
+
/**
|
|
5
|
+
* Applies Blazeo connection (same as {@link syncBlazeoConnection}) and marks the client as configured
|
|
6
|
+
* when a non-empty `baseUrl` was written to `@blazeo.com/calendar-client`.
|
|
7
|
+
*/
|
|
3
8
|
export function initializeAppointmentClient(config) {
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
if (syncBlazeoConnection(config)) {
|
|
10
|
+
isConfigured = true;
|
|
11
|
+
}
|
|
6
12
|
}
|
|
7
13
|
export function isAppointmentClientConfigured() {
|
|
8
14
|
return isConfigured || getConfig() !== null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AppointmentClientConfig } from "./initializeAppointmentClient.js";
|
|
2
|
+
/**
|
|
3
|
+
* Writes `baseUrl` / `consumer` into the global `@blazeo.com/calendar-client` `configure()` store.
|
|
4
|
+
* Returns whether anything was applied (skipped when `baseUrl` is empty after trim).
|
|
5
|
+
*/
|
|
6
|
+
export declare function syncBlazeoConnection(config: AppointmentClientConfig): boolean;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { configure } from "@blazeo.com/calendar-client";
|
|
2
|
+
/**
|
|
3
|
+
* Writes `baseUrl` / `consumer` into the global `@blazeo.com/calendar-client` `configure()` store.
|
|
4
|
+
* Returns whether anything was applied (skipped when `baseUrl` is empty after trim).
|
|
5
|
+
*/
|
|
6
|
+
export function syncBlazeoConnection(config) {
|
|
7
|
+
const baseUrl = config.baseUrl?.trim().replace(/\/+$/, "");
|
|
8
|
+
if (!baseUrl)
|
|
9
|
+
return false;
|
|
10
|
+
configure({
|
|
11
|
+
baseUrl,
|
|
12
|
+
...(config.consumer != null && String(config.consumer).trim() !== ""
|
|
13
|
+
? { consumer: String(config.consumer).trim() }
|
|
14
|
+
: {}),
|
|
15
|
+
...(config.fetch ? { fetch: config.fetch } : {}),
|
|
16
|
+
});
|
|
17
|
+
return true;
|
|
18
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export { initializeAppointmentClient, isAppointmentClientConfigured } from "./config/initializeAppointmentClient.js";
|
|
1
|
+
export { initializeAppointmentClient, isAppointmentClientConfigured, } from "./config/initializeAppointmentClient.js";
|
|
2
|
+
export { syncBlazeoConnection } from "./config/syncBlazeoConnection.js";
|
|
3
|
+
export { ensureBlazeoHttpReady } from "./config/ensureBlazeoHttpReady.js";
|
|
4
|
+
export type { EnsureBlazeoHttpOptions } from "./config/ensureBlazeoHttpReady.js";
|
|
2
5
|
export { blazeoClientConfig } from "./config/blazeoClientDefaults.js";
|
|
3
6
|
export { applyBlazeoClientConfig } from "./config/applyBlazeoDefaults.js";
|
|
4
7
|
export { createCalendarRoot, CalendarRootModel, CalendarSlotModel, EventModel, ParticipantModel } from "./models/CalendarRootModel.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { getExampleSlots } from "./exampleData.js";
|
|
2
|
-
export { initializeAppointmentClient, isAppointmentClientConfigured } from "./config/initializeAppointmentClient.js";
|
|
2
|
+
export { initializeAppointmentClient, isAppointmentClientConfigured, } from "./config/initializeAppointmentClient.js";
|
|
3
|
+
export { syncBlazeoConnection } from "./config/syncBlazeoConnection.js";
|
|
4
|
+
export { ensureBlazeoHttpReady } from "./config/ensureBlazeoHttpReady.js";
|
|
3
5
|
export { blazeoClientConfig } from "./config/blazeoClientDefaults.js";
|
|
4
6
|
export { applyBlazeoClientConfig } from "./config/applyBlazeoDefaults.js";
|
|
5
7
|
export { createCalendarRoot, CalendarRootModel, CalendarSlotModel, EventModel, ParticipantModel } from "./models/CalendarRootModel.js";
|
package/package.json
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Copy to `.env` or `.env.local` in this folder and set your API host (no trailing slash).
|
|
2
|
+
# Vite exposes only vars prefixed with VITE_ to the browser.
|
|
3
|
+
VITE_BLAZEO_BASE_URL=https://your-blazeo-api.example.com
|
|
4
|
+
# Optional, if your tenant uses it:
|
|
5
|
+
# VITE_BLAZEO_CONSUMER=
|
package/sample/package-lock.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import { getAllParticipantOpeningHours } from "appointment-client";
|
|
2
|
+
import { ensureBlazeoHttpReady, getAllParticipantOpeningHours } from "appointment-client";
|
|
3
3
|
import { configureBlazeoFromEffective, useBlazeoConnection } from "./BlazeoConnectionSettings.jsx";
|
|
4
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
4
5
|
|
|
5
6
|
export function AllParticipantOpeningHoursTab() {
|
|
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,20 @@ export function AllParticipantOpeningHoursTab() {
|
|
|
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
|
-
const res = await getAllParticipantOpeningHours(id
|
|
28
|
+
const res = await getAllParticipantOpeningHours(id, {
|
|
29
|
+
...connectionOpts,
|
|
30
|
+
baseUrl: effective.baseUrl,
|
|
31
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
32
|
+
});
|
|
24
33
|
setOutput(JSON.stringify(res, null, 2));
|
|
25
34
|
} catch (err) {
|
|
26
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
35
|
+
setError(mapBlazeoDemoError(err instanceof Error ? err.message : String(err)));
|
|
27
36
|
} finally {
|
|
28
37
|
setBusy(false);
|
|
29
38
|
}
|
package/sample/src/App2.jsx
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
BlazeoConnectionProvider,
|
|
4
4
|
useBlazeoConnection,
|
|
5
5
|
} from "./BlazeoConnectionSettings.jsx";
|
|
6
|
+
import { ensureBlazeoHttpReady, getConfig } from "appointment-client";
|
|
6
7
|
import { CalendarTab } from "./CalendarTab.jsx";
|
|
7
8
|
import { EventTab } from "./EventTab.jsx";
|
|
8
9
|
import { ParticipantTab } from "./ParticipantTab.jsx";
|
|
@@ -35,14 +36,23 @@ function ConnectionSettingsCard() {
|
|
|
35
36
|
setBaseUrlInput,
|
|
36
37
|
setConsumerInput,
|
|
37
38
|
effective,
|
|
39
|
+
connectionOpts,
|
|
38
40
|
} = useBlazeoConnection();
|
|
39
41
|
|
|
42
|
+
const ready = ensureBlazeoHttpReady({
|
|
43
|
+
baseUrl: effective.baseUrl,
|
|
44
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
45
|
+
});
|
|
46
|
+
const cfg = getConfig?.() ?? null;
|
|
47
|
+
|
|
40
48
|
return (
|
|
41
49
|
<div className="card connection-card">
|
|
42
50
|
<h2>Blazeo connection</h2>
|
|
43
51
|
<p className="muted small">
|
|
44
52
|
Values are saved in <code>localStorage</code>. Empty fields fall back to{" "}
|
|
45
|
-
<code>appointment-client/src/config/blazeoClientDefaults.ts</code>.
|
|
53
|
+
<code>appointment-client/src/config/blazeoClientDefaults.ts</code>. All tabs use the <strong>effective</strong> URL
|
|
54
|
+
here and run <code>pushBlazeoConnection</code> + <code>ensureBlazeoHttpReady</code> so calendar APIs receive{" "}
|
|
55
|
+
<code>baseUrl</code> from this card.
|
|
46
56
|
</p>
|
|
47
57
|
<p className="muted small">
|
|
48
58
|
Effective:{" "}
|
|
@@ -54,6 +64,12 @@ function ConnectionSettingsCard() {
|
|
|
54
64
|
</>
|
|
55
65
|
) : null}
|
|
56
66
|
</p>
|
|
67
|
+
<p className="muted small">
|
|
68
|
+
Debug: <code>connectionOpts</code> → <code>{JSON.stringify(connectionOpts)}</code> ·{" "}
|
|
69
|
+
<code>ensureBlazeoHttpReady</code> →{" "}
|
|
70
|
+
<code>{ready.ok ? "ok" : "missing_base_url"}</code> · <code>getConfig().baseUrl</code> →{" "}
|
|
71
|
+
<code>{cfg?.baseUrl ?? "(null)"}</code>
|
|
72
|
+
</p>
|
|
57
73
|
<div className="connection-card__row">
|
|
58
74
|
<label className="form__label">
|
|
59
75
|
<span>Base URL</span>
|
|
@@ -89,7 +105,11 @@ function AppShell() {
|
|
|
89
105
|
<main className="page">
|
|
90
106
|
<header className="header">
|
|
91
107
|
<h1>appointment-client</h1>
|
|
92
|
-
<p className="muted">
|
|
108
|
+
<p className="muted">
|
|
109
|
+
Browser sample — set <strong>Base URL</strong> in <strong>Blazeo connection</strong> below first; every tab uses
|
|
110
|
+
those values for <code>configure</code> + API calls. Tab <strong>Fetch · calendarView</strong> shows the unified{" "}
|
|
111
|
+
<code>calendarView</code> object.
|
|
112
|
+
</p>
|
|
93
113
|
</header>
|
|
94
114
|
|
|
95
115
|
<ConnectionSettingsCard />
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo, useState } from "react";
|
|
2
|
-
import { EventModel } from "appointment-client";
|
|
2
|
+
import { ensureBlazeoHttpReady, EventModel } from "appointment-client";
|
|
3
3
|
import { configureBlazeoFromEffective, useBlazeoConnection } from "./BlazeoConnectionSettings.jsx";
|
|
4
|
+
import { mapBlazeoDemoError } from "./blazeoDemoError.js";
|
|
4
5
|
|
|
5
6
|
function parseYmd(ymd) {
|
|
6
7
|
const t = (ymd ?? "").trim();
|
|
@@ -10,7 +11,7 @@ function parseYmd(ymd) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export function AvailabilityTab() {
|
|
13
|
-
const { effective } = useBlazeoConnection();
|
|
14
|
+
const { effective, connectionOpts } = useBlazeoConnection();
|
|
14
15
|
const [calendarId, setCalendarId] = useState("");
|
|
15
16
|
const [date, setDate] = useState(() => new Date().toISOString().slice(0, 10));
|
|
16
17
|
const [offsetMinutes, setOffsetMinutes] = useState(-new Date().getTimezoneOffset());
|
|
@@ -30,13 +31,17 @@ export function AvailabilityTab() {
|
|
|
30
31
|
if (!parts) return setError("Pick a valid date.");
|
|
31
32
|
if (!effective.baseUrl) return setError("Set Base URL above.");
|
|
32
33
|
configureBlazeoFromEffective(effective);
|
|
34
|
+
ensureBlazeoHttpReady({
|
|
35
|
+
baseUrl: effective.baseUrl,
|
|
36
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
37
|
+
});
|
|
33
38
|
|
|
34
39
|
setBusy(true);
|
|
35
40
|
try {
|
|
36
41
|
const list = await EventModel.getAvailability(id, parts.y, parts.m, parts.d, opts);
|
|
37
42
|
setOutput(JSON.stringify(list.map((n) => n.toJSON?.() ?? n), null, 2));
|
|
38
43
|
} catch (err) {
|
|
39
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
44
|
+
setError(mapBlazeoDemoError(err instanceof Error ? err.message : String(err)));
|
|
40
45
|
} finally {
|
|
41
46
|
setBusy(false);
|
|
42
47
|
}
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
useMemo,
|
|
7
7
|
useState,
|
|
8
8
|
} from "react";
|
|
9
|
-
import { blazeoClientConfig
|
|
10
|
-
import {
|
|
9
|
+
import { blazeoClientConfig } from "appointment-client";
|
|
10
|
+
import { pushBlazeoConnection } from "./blazeoPushConnection.js";
|
|
11
11
|
|
|
12
12
|
const STORAGE_BASE = "appointment-client-sample:blazeoBaseUrl";
|
|
13
13
|
const STORAGE_CONSUMER = "appointment-client-sample:blazeoConsumer";
|
|
@@ -46,18 +46,10 @@ export function mergeBlazeoUiWithFile(uiBaseUrl, uiConsumer) {
|
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Re-apply global Blazeo `configure` from the effective connection card state.
|
|
49
|
-
*
|
|
50
|
-
* `getParticipantOpeningHours` (instance env) and static helpers always see the same
|
|
51
|
-
* `baseUrl`.
|
|
49
|
+
* Pushes into both calendar-client entrypoints ({@link pushBlazeoConnection}).
|
|
52
50
|
*/
|
|
53
51
|
export function configureBlazeoFromEffective(effective) {
|
|
54
|
-
|
|
55
|
-
if (!baseUrl) return;
|
|
56
|
-
const consumer = (effective?.consumer ?? "").trim() || undefined;
|
|
57
|
-
// Configure appointment-client (which configures its internal calendar-client instance)
|
|
58
|
-
initializeAppointmentClient({ baseUrl, ...(consumer ? { consumer } : {}) });
|
|
59
|
-
// Also configure the calendar-client instance that the sample (and Vite alias) may resolve.
|
|
60
|
-
configureCalendarClient({ baseUrl, ...(consumer ? { consumer } : {}) });
|
|
52
|
+
pushBlazeoConnection(effective);
|
|
61
53
|
}
|
|
62
54
|
|
|
63
55
|
const BlazeoConnectionContext = createContext(null);
|
|
@@ -71,6 +63,15 @@ export function BlazeoConnectionProvider({ children }) {
|
|
|
71
63
|
[baseUrlInput, consumerInput]
|
|
72
64
|
);
|
|
73
65
|
|
|
66
|
+
/** Pass into `fetchCalendarDetails`, `createCalendarAsync`, etc. (explicit `baseUrl` for `ensureBlazeoHttpReady`). */
|
|
67
|
+
const connectionOpts = useMemo(
|
|
68
|
+
() => ({
|
|
69
|
+
...(effective.baseUrl ? { baseUrl: effective.baseUrl } : {}),
|
|
70
|
+
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
71
|
+
}),
|
|
72
|
+
[effective.baseUrl, effective.consumer]
|
|
73
|
+
);
|
|
74
|
+
|
|
74
75
|
useEffect(() => {
|
|
75
76
|
writeStored(STORAGE_BASE, baseUrlInput.trim());
|
|
76
77
|
}, [baseUrlInput]);
|
|
@@ -87,8 +88,7 @@ export function BlazeoConnectionProvider({ children }) {
|
|
|
87
88
|
useLayoutEffect(() => {
|
|
88
89
|
const { baseUrl, consumer } = mergeBlazeoUiWithFile(baseUrlInput, consumerInput);
|
|
89
90
|
if (!baseUrl) return;
|
|
90
|
-
|
|
91
|
-
configureCalendarClient({ baseUrl, ...(consumer ? { consumer } : {}) });
|
|
91
|
+
pushBlazeoConnection({ baseUrl, consumer });
|
|
92
92
|
}, [baseUrlInput, consumerInput]);
|
|
93
93
|
|
|
94
94
|
const value = useMemo(
|
|
@@ -98,8 +98,9 @@ export function BlazeoConnectionProvider({ children }) {
|
|
|
98
98
|
setBaseUrlInput,
|
|
99
99
|
setConsumerInput,
|
|
100
100
|
effective,
|
|
101
|
+
connectionOpts,
|
|
101
102
|
}),
|
|
102
|
-
[baseUrlInput, consumerInput, effective]
|
|
103
|
+
[baseUrlInput, consumerInput, effective, connectionOpts]
|
|
103
104
|
);
|
|
104
105
|
|
|
105
106
|
return (
|
|
@@ -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
|
}
|