@blazeo.com/appointment-client 1.0.7 → 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.
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -40,9 +40,7 @@ function coerceMemberId(v) {
40
40
  * Canonical member id used in both `members[].id` and `openingHours[].member`.
41
41
  */
42
42
  function resolveParticipantMemberId(calPart) {
43
- const n = pick(calPart, "id", "Id");
44
- if (n != null && typeof n === "number" && !Number.isNaN(n))
45
- return n;
43
+ // Prefer the participantId GUID when available — it is the stable cross-endpoint identifier.
46
44
  const sid = pick(calPart, "participantId", "ParticipantId", "participant_id");
47
45
  if (sid != null && String(sid).trim() !== "") {
48
46
  const t = String(sid).trim();
@@ -50,6 +48,14 @@ function resolveParticipantMemberId(calPart) {
50
48
  return Number(t);
51
49
  return t;
52
50
  }
51
+ // Fall back to id — accepts both numeric and string (e.g. a GUID stored as id).
52
+ const n = pick(calPart, "id", "Id");
53
+ if (n != null) {
54
+ if (typeof n === "number" && !Number.isNaN(n))
55
+ return n;
56
+ if (typeof n === "string" && n.trim() !== "")
57
+ return n.trim();
58
+ }
53
59
  return "";
54
60
  }
55
61
  function dayOrderIndex(d) {
@@ -153,7 +153,8 @@ export async function fetchCalendarDetails(calendarId, options = {}) {
153
153
  const infoList = unwrapModelList(participantsInfoRaw);
154
154
  // Merge participantList and infoList to ensure we have all members
155
155
  const mergedParticipantsMap = new Map();
156
- const getAnyId = (obj) => obj.id ?? obj.Id ?? obj.participantId ?? obj.ParticipantId ?? obj.participant_id;
156
+ // Prefer the participantId GUID; fall back to numeric id.
157
+ const getAnyId = (obj) => obj.participantId ?? obj.ParticipantId ?? obj.participant_id ?? obj.id ?? obj.Id;
157
158
  // 1. Add from standard list
158
159
  participantList.forEach((p) => {
159
160
  const id = getAnyId(p);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blazeo.com/appointment-client",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -6,6 +6,7 @@
6
6
  "": {
7
7
  "name": "appointment-client-sample",
8
8
  "dependencies": {
9
+ "@blazeo.com/appointment-client": "file:../blazeo.com-appointment-client-1.0.7.tgz",
9
10
  "appointment-client": "file:..",
10
11
  "react": "^19.0.0",
11
12
  "react-dom": "^19.0.0"
@@ -17,7 +18,7 @@
17
18
  },
18
19
  "..": {
19
20
  "name": "@blazeo.com/appointment-client",
20
- "version": "1.0.6",
21
+ "version": "1.0.7",
21
22
  "dependencies": {
22
23
  "@blazeo.com/calendar-client": "^1.0.18",
23
24
  "mobx": "^6.13.7",
@@ -319,6 +320,38 @@
319
320
  "node": ">=6.9.0"
320
321
  }
321
322
  },
323
+ "node_modules/@blazeo.com/appointment-client": {
324
+ "version": "1.0.7",
325
+ "resolved": "file:../blazeo.com-appointment-client-1.0.7.tgz",
326
+ "integrity": "sha512-b2E5TIdxvNJLH3oEvCQngDhzQ08iwXNolPcok3emAlGipO0f8iGDij5hnVUntt68c2Lr3IPz3cvr/BcUY6MofQ==",
327
+ "dependencies": {
328
+ "@blazeo.com/calendar-client": "^1.0.18",
329
+ "mobx": "^6.13.7",
330
+ "mobx-state-tree": "^7.0.2"
331
+ }
332
+ },
333
+ "node_modules/@blazeo.com/calendar-client": {
334
+ "version": "1.0.18",
335
+ "resolved": "https://registry.npmjs.org/@blazeo.com/calendar-client/-/calendar-client-1.0.18.tgz",
336
+ "integrity": "sha512-wHqOZKUAH4JDgsdwaktEG68H5YfArOW3LEouCXznIza4YqNrlkfdJEyqiwNf6KjY2nELi+s2tFM22/1MKaWf5Q==",
337
+ "license": "UNLICENSED",
338
+ "dependencies": {
339
+ "mobx": "^6.10.0",
340
+ "mobx-state-tree": "^5.4.0"
341
+ },
342
+ "engines": {
343
+ "node": ">=18"
344
+ }
345
+ },
346
+ "node_modules/@blazeo.com/calendar-client/node_modules/mobx-state-tree": {
347
+ "version": "5.4.2",
348
+ "resolved": "https://registry.npmjs.org/mobx-state-tree/-/mobx-state-tree-5.4.2.tgz",
349
+ "integrity": "sha512-SGXAh2KCBQbWVcxeQbZEr5pchTgcfNZmGVRL2a2Me+pSMH98bZWXD6EOuuijbTGbc0hOoOsbab3JdwJyr+fW7Q==",
350
+ "license": "MIT",
351
+ "peerDependencies": {
352
+ "mobx": "^6.3.0"
353
+ }
354
+ },
322
355
  "node_modules/@esbuild/aix-ppc64": {
323
356
  "version": "0.25.12",
324
357
  "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
@@ -1425,6 +1458,28 @@
1425
1458
  "yallist": "^3.0.2"
1426
1459
  }
1427
1460
  },
1461
+ "node_modules/mobx": {
1462
+ "version": "6.15.3",
1463
+ "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.15.3.tgz",
1464
+ "integrity": "sha512-6+ZSYDs5zgH5CdGfEU2q2Lsa5PztVryL1ys7kAImTU25n2A9LAMj/yneVsQpd03MfwMLDQF+7kakJR9Z+cQxSw==",
1465
+ "license": "MIT",
1466
+ "funding": {
1467
+ "type": "opencollective",
1468
+ "url": "https://opencollective.com/mobx"
1469
+ }
1470
+ },
1471
+ "node_modules/mobx-state-tree": {
1472
+ "version": "7.2.0",
1473
+ "resolved": "https://registry.npmjs.org/mobx-state-tree/-/mobx-state-tree-7.2.0.tgz",
1474
+ "integrity": "sha512-o46IsMI/lHITYCmkbQgNOCDT8M9Hd2WAgOlHt42hCg+XUZP21obF5ui5Vv9epIsQV8Wpp/GWNHu1YFhTx6iq0g==",
1475
+ "license": "MIT",
1476
+ "dependencies": {
1477
+ "ts-essentials": "^9.4.1"
1478
+ },
1479
+ "peerDependencies": {
1480
+ "mobx": "^6.3.0"
1481
+ }
1482
+ },
1428
1483
  "node_modules/ms": {
1429
1484
  "version": "2.1.3",
1430
1485
  "dev": true,
@@ -1598,6 +1653,20 @@
1598
1653
  "url": "https://github.com/sponsors/SuperchupuDev"
1599
1654
  }
1600
1655
  },
1656
+ "node_modules/ts-essentials": {
1657
+ "version": "9.4.2",
1658
+ "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.4.2.tgz",
1659
+ "integrity": "sha512-mB/cDhOvD7pg3YCLk2rOtejHjjdSi9in/IBYE13S+8WA5FBSraYf4V/ws55uvs0IvQ/l0wBOlXy5yBNZ9Bl8ZQ==",
1660
+ "license": "MIT",
1661
+ "peerDependencies": {
1662
+ "typescript": ">=4.1.0"
1663
+ },
1664
+ "peerDependenciesMeta": {
1665
+ "typescript": {
1666
+ "optional": true
1667
+ }
1668
+ }
1669
+ },
1601
1670
  "node_modules/update-browserslist-db": {
1602
1671
  "version": "1.2.3",
1603
1672
  "dev": true,
@@ -10,6 +10,7 @@
10
10
  "participants:info": "node ./scripts/getInfoByCalendar.mjs"
11
11
  },
12
12
  "dependencies": {
13
+ "@blazeo.com/appointment-client": "file:../blazeo.com-appointment-client-1.0.7.tgz",
13
14
  "appointment-client": "file:..",
14
15
  "react": "^19.0.0",
15
16
  "react-dom": "^19.0.0"
@@ -304,23 +304,14 @@ export function FetchCalendarTab() {
304
304
  const id = c.calendarId ?? String(c.id ?? "");
305
305
  if (!id) return { calendar: getSnapshot(c), openingHours: [], meta: { error: "no id" } };
306
306
  try {
307
+ // fetchCalendarDetails now returns the flat unified view directly
307
308
  const b = await fetchCalendarDetails(id, {
308
309
  ...connectionOpts,
309
310
  baseUrl: effective.baseUrl,
310
311
  ...(effective.consumer ? { consumer: effective.consumer } : {}),
311
312
  });
312
- return {
313
- calendarView: b.calendarView,
314
- calendar: b.calendar ?? getSnapshot(c),
315
- openingHours: b.openingHours,
316
- participants: (b.participants ?? []).map((p) => (isStateTreeNode(p) ? getSnapshot(p) : p)),
317
- __openingHoursMeta: {
318
- fromCalendarGet: b.fromCalendarGet,
319
- fromParticipantApi: b.fromParticipantApi,
320
- calendarViewUsedAllParticipantOpeningHours: b.meta?.calendarViewUsedAllParticipantOpeningHours,
321
- },
322
- meta: b.meta,
323
- };
313
+ // b IS the unified calendarView: members/openingHours/participants at top level
314
+ return b ?? { calendar: getSnapshot(c), openingHours: [], meta: { error: "null response" } };
324
315
  } catch (err) {
325
316
  return {
326
317
  calendar: getSnapshot(c),
@@ -469,29 +460,29 @@ export function FetchCalendarTab() {
469
460
  <div className="card">
470
461
  <h2>Fetch calendar · calendarView</h2>
471
462
  <p className="muted small">
472
- Runs <code>fetchCalendarDetails(calendarId)</code>. JSON field <code>calendarView</code> is first: one object with
473
- calendar snapshot fields + <code>members</code> + <code>openingHours</code>, and a **new** <code>participants</code> array
474
- where each participant has their own <code>openingHours</code> nested inside.
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.)
475
467
  </p>
476
468
  <p className="muted small">
477
469
  Uses <code>fetchCalendarDetails</code>: legacy <code>openingHours</code> prefers embed on{" "}
478
470
  <code>CalendarModel.getRaw</code>, else <code>getParticipantOpeningHours</code>.{" "}
479
- <code>calendarView.openingHours</code> prefers <code>getAllParticipantOpeningHours</code> (
471
+ Unified <code>openingHours</code> prefers <code>getAllParticipantOpeningHours</code> (
480
472
  <code>GET /Calendar/Participant/OpeningHours/All/Get</code>) when the API returns rows. Members combine{" "}
481
473
  <code>CalendarModel.getParticipants</code> + <code>CalendarModel.getParticipantsInfo</code> (each member may
482
474
  include <code>participantInfo</code>).
483
475
  </p>
484
476
  <p className="muted small">
485
- <strong>Single object in code:</strong> <code>fetchCalendarBundle(calendarId)</code> after{" "}
486
- <code>initializeAppointmentClient(&#123; baseUrl, consumer &#125;)</code> same unified shape as{" "}
487
- <code>calendarView</code> below. This tab runs <code>fetchCalendarDetails</code> so extra arrays stay visible.
477
+ <strong>In code:</strong> <code>fetchCalendarBundle(calendarId)</code> (after{" "}
478
+ <code>initializeAppointmentClient(&#123; baseUrl, consumer &#125;)</code>) is an alias for the same unified fetch;
479
+ use either alongside explicit <code>baseUrl</code> / <code>consumer</code> options when needed.
488
480
  </p>
489
481
  <p className="muted small">
490
482
  <strong>DevTools Network:</strong> Each fetch fires <code>/Calendar/Get</code> <strong>twice</strong> (
491
483
  <code>CalendarModel.get</code> + <code>getRaw</code>). Other calls use different URLs — filter by{" "}
492
- <code>Participant</code>, <code>OpeningHours</code>, or <code>GetInfo</code>. Those power{" "}
493
- <code>calendarView</code>. If you only see <code>Calendar/Get</code> yet the UI JSON has members/hours,
494
- widen the Network filter (&quot;All&quot;) 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 (&quot;All&quot;) or disable search; if the unified object is empty/missing fields,
495
486
  check the <strong>Console</strong> for errors on the participant/opening-hours requests.
496
487
  </p>
497
488
  <p className="muted small">
@@ -567,11 +558,18 @@ export function FetchCalendarTab() {
567
558
  <div className="card">
568
559
  <h2>Fetch calendars by company</h2>
569
560
  <p className="muted small">
570
- Calls <code>CalendarModel.getByCompany</code> → <code>GET /Calendar/All</code>. If the UI shows{" "}
571
- <strong>Failed to fetch</strong> while Base URL points at Azure/production, that is usually{" "}
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{" "}
572
570
  <strong>CORS</strong>: enable proxy via <code>VITE_DEV_PROXY_TARGET</code> in{" "}
573
- <code>sample/.env.development</code> and Base URL <code>http://localhost:5173/blazeo-api</code>{" "}
574
- (restart dev server).
571
+ <code>sample/.env.development</code> and Base URL <code>http://localhost:5173/blazeo-api</code> (restart dev
572
+ server).
575
573
  </p>
576
574
  <form onSubmit={handleFetchByCompany} className="form">
577
575
  <label className="form__label">
@@ -37,14 +37,19 @@ function coerceMemberId(v: unknown): number | string | null {
37
37
  * Canonical member id used in both `members[].id` and `openingHours[].member`.
38
38
  */
39
39
  function resolveParticipantMemberId(calPart: Record<string, any>): number | string {
40
- const n = pick<number | null>(calPart, "id", "Id");
41
- if (n != null && typeof n === "number" && !Number.isNaN(n)) return n;
40
+ // Prefer the participantId GUID when available — it is the stable cross-endpoint identifier.
42
41
  const sid = pick<string>(calPart, "participantId", "ParticipantId", "participant_id");
43
42
  if (sid != null && String(sid).trim() !== "") {
44
43
  const t = String(sid).trim();
45
44
  if (/^\d+$/.test(t)) return Number(t);
46
45
  return t;
47
46
  }
47
+ // Fall back to id — accepts both numeric and string (e.g. a GUID stored as id).
48
+ const n = pick<number | string | null>(calPart, "id", "Id");
49
+ if (n != null) {
50
+ if (typeof n === "number" && !Number.isNaN(n)) return n;
51
+ if (typeof n === "string" && n.trim() !== "") return n.trim();
52
+ }
48
53
  return "";
49
54
  }
50
55
 
@@ -194,7 +194,9 @@ export async function fetchCalendarDetails(
194
194
  // Merge participantList and infoList to ensure we have all members
195
195
  const mergedParticipantsMap = new Map<string, any>();
196
196
 
197
- const getAnyId = (obj: any) => obj.id ?? obj.Id ?? obj.participantId ?? obj.ParticipantId ?? obj.participant_id;
197
+ // Prefer the participantId GUID; fall back to numeric id.
198
+ const getAnyId = (obj: any) =>
199
+ obj.participantId ?? obj.ParticipantId ?? obj.participant_id ?? obj.id ?? obj.Id;
198
200
 
199
201
  // 1. Add from standard list
200
202
  participantList.forEach((p: any) => {