@blazeo.com/appointment-client 1.0.4 → 1.0.5

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.
@@ -0,0 +1,18 @@
1
+ export declare function fetchCalendarDetails(calendarId: string, options?: {
2
+ includeParticipantsInfo?: boolean;
3
+ }): Promise<{
4
+ calendar: (Record<string, unknown> & {
5
+ openingHours: unknown[];
6
+ }) | null;
7
+ /** Live MST calendar node (same as `CalendarModel.get`) — use for instance calls. */
8
+ cal: unknown;
9
+ openingHours: unknown[];
10
+ participants: unknown[];
11
+ participantsInfo: unknown;
12
+ openingHoursApiResponse: unknown;
13
+ meta: {
14
+ ok: boolean;
15
+ reason?: string;
16
+ };
17
+ }>;
18
+
@@ -0,0 +1,51 @@
1
+ import { CalendarModel } from "@blazeo.com/calendar-client";
2
+ import { getSnapshot } from "mobx-state-tree";
3
+ import { normalizeParticipantOpeningHoursResponse } from "./fetchCalendarWithOpeningHours.js";
4
+
5
+ /**
6
+ * Loads calendar + opening hours + participants and returns a single bundle.
7
+ *
8
+ * Network calls (when calendar exists):
9
+ * - GET /Calendar/Get
10
+ * - GET /Calendar/Participant/OpeningHours/Get
11
+ * - GET /Calendar/Participant/All
12
+ */
13
+ export async function fetchCalendarDetails(calendarId, options = {}) {
14
+ const { includeParticipantsInfo = false } = options;
15
+
16
+ const cal = await CalendarModel.get(calendarId);
17
+ if (cal == null) {
18
+ return {
19
+ calendar: null,
20
+ cal: null,
21
+ openingHours: [],
22
+ participants: [],
23
+ participantsInfo: null,
24
+ openingHoursApiResponse: null,
25
+ meta: { ok: false, reason: "calendar_not_found" }
26
+ };
27
+ }
28
+
29
+ const [openingHoursRes, participants, participantsInfo] = await Promise.all([
30
+ cal.getParticipantOpeningHours({}),
31
+ cal.getParticipants(),
32
+ includeParticipantsInfo ? cal.getParticipantsInfo() : Promise.resolve(null)
33
+ ]);
34
+
35
+ const { list: openingHoursList } = normalizeParticipantOpeningHoursResponse(openingHoursRes);
36
+ const openingHours = Array.isArray(openingHoursList) ? openingHoursList : [];
37
+
38
+ const snap = getSnapshot(cal);
39
+ const calendar = { ...snap, openingHours };
40
+
41
+ return {
42
+ calendar,
43
+ cal,
44
+ openingHours,
45
+ participants: Array.isArray(participants) ? participants : [],
46
+ participantsInfo,
47
+ openingHoursApiResponse: openingHoursRes,
48
+ meta: { ok: true }
49
+ };
50
+ }
51
+
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export { mapCalendarBOToSnapshot } from "./calendar/mapCalendarToBlazeoSnapshot.
18
18
  export type { CalendarInput as CalendarBOInput, MemberBOInput, OpeningHourBOInput, } from "./types/calendar.js";
19
19
  export type { ApexAppointmentInput } from "./types/appointment.js";
20
20
  export { fetchCalendarWithOpeningHours, normalizeParticipantOpeningHoursResponse, pickOpeningHoursArrayFromCalendarPayload, unwrapCalendarGetData, } from "./calendar/fetchCalendarWithOpeningHours.js";
21
+ export { fetchCalendarDetails } from "./calendar/fetchCalendarDetails.js";
21
22
  export { CalendarModel, EventModel, ParticipantModel, OpeningHourModel, } from "@blazeo.com/calendar-client";
22
23
  export { configure, getConfig } from "@blazeo.com/calendar-client";
23
24
  export { getExampleCalendarRoot, getExampleCalendarRootSnapshot, getExampleEvents, getExampleParticipants, getExampleSlots, } from "./exampleData.js";
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ export * from "./facades/index.js";
17
17
  export * from "./models/index.js";
18
18
  export { mapCalendarBOToSnapshot } from "./calendar/mapCalendarToBlazeoSnapshot.js";
19
19
  export { fetchCalendarWithOpeningHours, normalizeParticipantOpeningHoursResponse, pickOpeningHoursArrayFromCalendarPayload, unwrapCalendarGetData, } from "./calendar/fetchCalendarWithOpeningHours.js";
20
+ export { fetchCalendarDetails } from "./calendar/fetchCalendarDetails.js";
20
21
  export { CalendarModel, EventModel, ParticipantModel, OpeningHourModel, } from "@blazeo.com/calendar-client";
21
22
  export { configure, getConfig } from "@blazeo.com/calendar-client";
22
23
  export { getExampleCalendarRoot, getExampleCalendarRootSnapshot, getExampleEvents, getExampleParticipants, getExampleSlots, } from "./exampleData.js";
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@blazeo.com/appointment-client",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
- },
6
+ },
7
7
  "author": "Blazeo",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",
@@ -15,9 +15,8 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@blazeo.com/calendar-client": "^1.0.5",
18
+ "@blazeo.com/calendar-client": "^1.0.17",
19
19
  "mobx": "^6.13.7",
20
20
  "mobx-state-tree": "^7.0.2"
21
21
  }
22
22
  }
23
-
@@ -17,13 +17,61 @@
17
17
  },
18
18
  "..": {
19
19
  "name": "@blazeo.com/appointment-client",
20
- "version": "1.0.3",
20
+ "version": "1.0.4",
21
21
  "dependencies": {
22
- "@blazeo.com/calendar-client": "^1.0.5",
22
+ "@blazeo.com/calendar-client": "^1.0.17",
23
23
  "mobx": "^6.13.7",
24
24
  "mobx-state-tree": "^7.0.2"
25
25
  }
26
26
  },
27
+ "../node_modules/@blazeo.com/calendar-client": {
28
+ "version": "1.0.17",
29
+ "license": "UNLICENSED",
30
+ "dependencies": {
31
+ "mobx": "^6.10.0",
32
+ "mobx-state-tree": "^5.4.0"
33
+ },
34
+ "engines": {
35
+ "node": ">=18"
36
+ }
37
+ },
38
+ "../node_modules/@blazeo.com/calendar-client/node_modules/mobx-state-tree": {
39
+ "version": "5.4.2",
40
+ "license": "MIT",
41
+ "peerDependencies": {
42
+ "mobx": "^6.3.0"
43
+ }
44
+ },
45
+ "../node_modules/mobx": {
46
+ "version": "6.15.0",
47
+ "license": "MIT",
48
+ "funding": {
49
+ "type": "opencollective",
50
+ "url": "https://opencollective.com/mobx"
51
+ }
52
+ },
53
+ "../node_modules/mobx-state-tree": {
54
+ "version": "7.2.0",
55
+ "license": "MIT",
56
+ "dependencies": {
57
+ "ts-essentials": "^9.4.1"
58
+ },
59
+ "peerDependencies": {
60
+ "mobx": "^6.3.0"
61
+ }
62
+ },
63
+ "../node_modules/ts-essentials": {
64
+ "version": "9.4.2",
65
+ "license": "MIT",
66
+ "peerDependencies": {
67
+ "typescript": ">=4.1.0"
68
+ },
69
+ "peerDependenciesMeta": {
70
+ "typescript": {
71
+ "optional": true
72
+ }
73
+ }
74
+ },
27
75
  "node_modules/@babel/code-frame": {
28
76
  "version": "7.29.0",
29
77
  "dev": true,
@@ -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,6 +2,7 @@ import { useMemo, useState } from "react";
2
2
  import {
3
3
  CalendarModel,
4
4
  deleteCalendarAsync,
5
+ fetchCalendarDetails,
5
6
  fetchCalendarWithOpeningHours,
6
7
  updateCalendarAsync,
7
8
  } from "appointment-client";
@@ -217,49 +218,29 @@ export function FetchCalendarTab() {
217
218
 
218
219
  setBusy(true);
219
220
  try {
220
- const bundle = await fetchCalendarWithOpeningHours(id);
221
+ const details = await fetchCalendarDetails(id, { includeParticipantsInfo: true });
221
222
 
222
- if (bundle.cal == null) {
223
+ if (details.cal == null) {
223
224
  const raw = await CalendarModel.getRaw(id);
224
- setNote(
225
- "CalendarModel.get returned null. Showing CalendarModel.getRaw only (opening hours merge skipped)."
226
- );
225
+ setNote("CalendarModel.get returned null. Showing CalendarModel.getRaw only.");
227
226
  setOutput(toDisplayJson(raw));
228
227
  return;
229
228
  }
230
229
 
231
- const snap = getSnapshot(bundle.cal);
230
+ const snap = getSnapshot(details.cal);
232
231
  setLastFetchUpdatePayload(JSON.stringify(calendarSnapshotToUpdatePayload(snap), null, 2));
233
232
 
234
- const [participants, participantsInfo] = await Promise.all([
235
- bundle.cal.getParticipants(),
236
- bundle.cal.getParticipantsInfo(),
237
- ]);
238
-
239
233
  const payload = {
240
- calendar: bundle.calendar,
241
- openingHours: bundle.openingHours,
242
- ...(bundle.participantOpeningHoursResponse != null
243
- ? { participantOpeningHoursApiResponse: bundle.participantOpeningHoursResponse }
244
- : {}),
245
- participants,
246
- participantsInfo,
247
- __openingHoursMeta: {
248
- fromCalendarGet: bundle.fromCalendarGet,
249
- fromParticipantApi: bundle.fromParticipantApi,
250
- embeddedCount: bundle.embeddedFromGet?.length ?? 0,
251
- resolvedCount: bundle.openingHours?.length ?? 0,
252
- },
234
+ calendar: details.calendar,
235
+ openingHours: details.openingHours,
236
+ openingHoursApiResponse: details.openingHoursApiResponse ?? null,
237
+ participants: details.participants,
238
+ participantsInfo: details.participantsInfo ?? null,
239
+ meta: details.meta,
253
240
  };
254
241
 
255
242
  setOutput(toDisplayJson(payload));
256
- setNote(
257
- bundle.fromCalendarGet
258
- ? "Opening hours: embedded on GET /Calendar/Get (`appointment-client` fetchCalendarWithOpeningHours)."
259
- : bundle.fromParticipantApi
260
- ? "Opening hours: from GET /Calendar/Participant/OpeningHours/Get (calendar GET had none)."
261
- : "Opening hours: none returned."
262
- );
243
+ setNote("Loaded calendar + opening hours + participants (3 calls) → single response object.");
263
244
  } catch (err) {
264
245
  setError(explainFetchFailure(err, effective.baseUrl));
265
246
  } finally {
@@ -293,20 +274,18 @@ export function FetchCalendarTab() {
293
274
  const id = c.calendarId ?? String(c.id ?? "");
294
275
  if (!id) return { calendar: getSnapshot(c), openingHours: [], meta: { error: "no id" } };
295
276
  try {
296
- const b = await fetchCalendarWithOpeningHours(id);
277
+ const b = await fetchCalendarDetails(id);
297
278
  return {
298
279
  calendar: b.calendar ?? getSnapshot(c),
299
280
  openingHours: b.openingHours,
300
- __openingHoursMeta: {
301
- fromCalendarGet: b.fromCalendarGet,
302
- fromParticipantApi: b.fromParticipantApi,
303
- },
281
+ participants: b.participants,
282
+ meta: b.meta,
304
283
  };
305
284
  } catch (err) {
306
285
  return {
307
286
  calendar: getSnapshot(c),
308
287
  openingHours: [],
309
- __openingHoursMeta: {
288
+ meta: {
310
289
  error: err instanceof Error ? err.message : String(err),
311
290
  },
312
291
  };
@@ -314,7 +293,7 @@ export function FetchCalendarTab() {
314
293
  })
315
294
  );
316
295
  setNote(
317
- `Loaded ${list.length} calendar(s); opening hours merged per calendar (GET embed → participant API fallback).`
296
+ `Loaded ${list.length} calendar(s); opening hours + participants loaded per calendar (single details bundle).`
318
297
  );
319
298
  setOutput(toDisplayJson(enriched));
320
299
  }