@blazeo.com/appointment-client 1.0.8 → 1.0.10

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.
@@ -1,5 +1,5 @@
1
1
  import { getSnapshot } from "mobx-state-tree";
2
- import { addParticipantToCalendar, saveCalendarOpeningHour } from "./blazeoCalendarRelationMethods.js";
2
+ import { addParticipantToCalendar, saveCalendarOpeningHoursBatch } from "./blazeoCalendarRelationMethods.js";
3
3
  import { createCalendarAsync, updateCalendarAsync, deleteCalendarAsync } from "./createCalendar.js";
4
4
  function isFailureStatus(res) {
5
5
  return res.status !== "success" && res.status !== "Success";
@@ -90,8 +90,10 @@ async function runMembersAndOpeningHoursAfterCalendarSave(calendar, calendarNode
90
90
  }
91
91
  membersAdded += 1;
92
92
  }
93
- let openingHoursSaved = 0;
94
- for (const oh of calendar.openingHours ?? []) {
93
+ // 2. Save Opening Hours (Plan V2: Grouped by participant with explicit off-days per slot)
94
+ const openingHours = calendar.openingHours ?? [];
95
+ const hoursByParticipant = new Map();
96
+ for (const oh of openingHours) {
95
97
  const participantId = resolveParticipantIdForOpeningHour(oh);
96
98
  if (!participantId) {
97
99
  return {
@@ -99,8 +101,17 @@ async function runMembersAndOpeningHoursAfterCalendarSave(calendar, calendarNode
99
101
  error: `Opening hour id ${oh.id}: participantId is required.`,
100
102
  };
101
103
  }
102
- for (const day of oh.days ?? []) {
103
- const payload = {
104
+ if (!hoursByParticipant.has(participantId)) {
105
+ hoursByParticipant.set(participantId, []);
106
+ }
107
+ // Plan V2 Logic: For every opening hour object, generate EXACTLY 7 entries (days 0-6).
108
+ // If the day is in oh.days, it's ON. If not, it's OFF.
109
+ const activeDays = oh.days ?? [];
110
+ const openingHourId = oh.openingHourId?.trim() || newOpeningHourId();
111
+ for (let day = 0; day <= 6; day++) {
112
+ const isIncluded = activeDays.includes(day);
113
+ const isOff = isIncluded ? !!oh.off : true; // If not in days array, it's explicitly OFF
114
+ hoursByParticipant.get(participantId)?.push({
104
115
  calendarId: calendarIdStr,
105
116
  participantId,
106
117
  day,
@@ -108,22 +119,28 @@ async function runMembersAndOpeningHoursAfterCalendarSave(calendar, calendarNode
108
119
  startMinute: oh.startMinute,
109
120
  endHour: oh.endHour,
110
121
  endMinute: oh.endMinute,
111
- off: oh.off,
112
- openingHourId: oh.openingHourId?.trim() || newOpeningHourId(),
122
+ off: isOff,
123
+ openingHourId: openingHourId,
124
+ });
125
+ }
126
+ }
127
+ let openingHoursSaved = 0;
128
+ for (const [participantId, payload] of hoursByParticipant.entries()) {
129
+ if (payload.length === 0)
130
+ continue;
131
+ // Use the batch save method (plural)
132
+ const res = await saveCalendarOpeningHoursBatch(calendarNode, payload);
133
+ if (isFailureStatus(res)) {
134
+ const msg = res.message ??
135
+ (typeof res.data === "string" ? res.data : undefined) ??
136
+ JSON.stringify(res);
137
+ return {
138
+ ok: false,
139
+ error: `saveOpeningHours batch failed for participant ${participantId}: ${msg}`,
140
+ apiResponse: res,
113
141
  };
114
- const res = await saveCalendarOpeningHour(calendarNode, payload);
115
- if (isFailureStatus(res)) {
116
- const msg = res.message ??
117
- (typeof res.data === "string" ? res.data : undefined) ??
118
- JSON.stringify(res);
119
- return {
120
- ok: false,
121
- error: `saveOpeningHour failed (opening hour ${oh.id}, day ${day}): ${msg}`,
122
- apiResponse: res,
123
- };
124
- }
125
- openingHoursSaved += 1;
126
142
  }
143
+ openingHoursSaved += payload.length;
127
144
  }
128
145
  return {
129
146
  ...baseSuccess,
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Fetches all calendars for a company and populates each with its members (participants).
3
+ * Fetches both Participant List and Participant Info to ensure names and emails are included,
4
+ * while still skipping heavy data like opening hours.
5
+ */
6
+ export declare function getCalendarsByCompany(companyKey: string, connection?: {
7
+ baseUrl?: string;
8
+ consumer?: string;
9
+ }): Promise<any[]>;
@@ -0,0 +1,107 @@
1
+ import { CalendarModel, CalendarParticipantModel } from "@blazeo.com/calendar-client";
2
+ import { ensureBlazeoHttpReady } from "../config/ensureBlazeoHttpReady.js";
3
+ /**
4
+ * Fetches all calendars for a company and populates each with its members (participants).
5
+ * Fetches both Participant List and Participant Info to ensure names and emails are included,
6
+ * while still skipping heavy data like opening hours.
7
+ */
8
+ export async function getCalendarsByCompany(companyKey, connection = {}) {
9
+ const ready = ensureBlazeoHttpReady(connection);
10
+ if (!ready.ok) {
11
+ throw new Error(ready.error);
12
+ }
13
+ // 1. Get all calendars for the company
14
+ const result = await CalendarModel.getByCompany(companyKey);
15
+ const calendars = Array.isArray(result) ? result : result?.calendars ?? [];
16
+ if (!calendars || calendars.length === 0) {
17
+ return [];
18
+ }
19
+ // 2. Fetch lightweight members for each calendar in parallel
20
+ const enrichedCalendars = await Promise.all(calendars.map(async (cal) => {
21
+ const calendarId = cal.calendarId ?? String(cal.id ?? "");
22
+ if (!calendarId)
23
+ return null;
24
+ try {
25
+ // We need both List and Info to get the names/emails
26
+ const [partsRaw, infoRaw] = await Promise.all([
27
+ CalendarParticipantModel.getByCalendar(calendarId),
28
+ CalendarParticipantModel.getInfoByCalendar(calendarId)
29
+ ]);
30
+ const parts = Array.isArray(partsRaw) ? partsRaw : partsRaw?.participants ?? [];
31
+ const info = Array.isArray(infoRaw) ? infoRaw : infoRaw?.info ?? [];
32
+ // Merge logic to ensure names are matched to IDs
33
+ const membersMap = new Map();
34
+ // Use participantId GUID as the primary key
35
+ const getAnyId = (obj) => obj.participantId ?? obj.ParticipantId ?? obj.participant_id ?? obj.id ?? obj.Id;
36
+ // 1. Initialize with basic participant data
37
+ parts.forEach((p) => {
38
+ const mid = getAnyId(p);
39
+ if (mid) {
40
+ membersMap.set(String(mid).toLowerCase(), {
41
+ id: mid,
42
+ name: p.name ?? p.Name ?? "Member",
43
+ email: p.email ?? p.Email,
44
+ status: p.status ?? p.Status ?? 1,
45
+ uuId: mid
46
+ });
47
+ }
48
+ });
49
+ // 2. Enrich with detailed info (Name, Email, Alias)
50
+ info.forEach((i) => {
51
+ const mid = getAnyId(i);
52
+ if (!mid)
53
+ return;
54
+ const key = String(mid).toLowerCase();
55
+ const existing = membersMap.get(key);
56
+ const resolvedEmail = i.email ?? i.Email ?? i.userSsoEmail ?? i.UserSsoEmail ?? existing?.email;
57
+ const memberData = {
58
+ id: mid,
59
+ name: i.name ?? i.Name ?? i.alias ?? i.Alias ?? (existing?.name || "Member"),
60
+ email: resolvedEmail,
61
+ alias: i.alias ?? i.Alias ?? i.name ?? i.Name,
62
+ userSsoEmail: resolvedEmail,
63
+ uuId: mid,
64
+ status: i.status ?? i.Status ?? existing?.status ?? 1
65
+ };
66
+ if (!existing) {
67
+ membersMap.set(key, memberData);
68
+ }
69
+ else {
70
+ Object.assign(existing, memberData);
71
+ }
72
+ });
73
+ const members = Array.from(membersMap.values());
74
+ // Map to the EXACT schema requested by the user
75
+ return {
76
+ id: cal.id ?? cal.Id,
77
+ calendarLink: cal.calendarLink ?? cal.CalendarLink ?? "",
78
+ uuid: calendarId,
79
+ createdOn: cal.createdOn ?? cal.CreatedOn,
80
+ name: cal.name ?? cal.Name,
81
+ timeZoneId: cal.timeZoneId ?? cal.TimeZoneId,
82
+ description: cal.description ?? cal.Description ?? "",
83
+ assignmentType: cal.assignmentMethod ?? cal.AssignmentMethod ?? cal.assignmentType,
84
+ status: cal.status ?? cal.Status ?? 1,
85
+ location: cal.location ?? cal.Location ?? "",
86
+ members
87
+ };
88
+ }
89
+ catch (err) {
90
+ console.error(`[getCalendarsByCompany] Error fetching members for ${calendarId}:`, err);
91
+ return {
92
+ id: cal.id ?? cal.Id,
93
+ calendarLink: cal.calendarLink ?? cal.CalendarLink ?? "",
94
+ uuid: calendarId,
95
+ createdOn: cal.createdOn ?? cal.CreatedOn,
96
+ name: cal.name ?? cal.Name,
97
+ timeZoneId: cal.timeZoneId ?? cal.TimeZoneId,
98
+ description: cal.description ?? cal.Description ?? "",
99
+ assignmentType: cal.assignmentMethod ?? cal.AssignmentMethod ?? cal.assignmentType,
100
+ status: cal.status ?? cal.Status ?? 1,
101
+ location: cal.location ?? cal.Location ?? "",
102
+ members: []
103
+ };
104
+ }
105
+ }));
106
+ return enrichedCalendars.filter(c => c !== null);
107
+ }
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { blazeoClientConfig } from "./config/blazeoClientDefaults.js";
6
6
  export { applyBlazeoClientConfig } from "./config/applyBlazeoDefaults.js";
7
7
  export { createCalendarRoot, CalendarRootModel, CalendarSlotModel, EventModel, ParticipantModel } from "./models/CalendarRootModel.js";
8
8
  export { fetchCalendarDetails, fetchCalendarBundle, normalizeOpeningHours } from "./calendar/fetchCalendarDetails.js";
9
+ export { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
9
10
  export { buildUnifiedCalendarView, type UnifiedCalendarMember, type UnifiedCalendarView, type UnifiedOpeningHourRow, type UnifiedParticipantWithHours, } from "./calendar/buildUnifiedCalendarView.js";
10
11
  export { fetchCalendarWithOpeningHours, unwrapCalendarGetData, pickOpeningHoursArrayFromCalendarPayload, normalizeParticipantOpeningHoursResponse } from "./calendar/fetchCalendarWithOpeningHours.js";
11
12
  export { getOpeningHours } from "./calendar/getOpeningHours.js";
@@ -18,7 +19,8 @@ export { CalendarCreation, createCalendarWithRelationsAsync, updateCalendarWithR
18
19
  export { addParticipantToCalendar, removeParticipantFromCalendar, saveCalendarOpeningHour, saveCalendarOpeningHoursBatch } from "./calendar/blazeoCalendarRelationMethods.js";
19
20
  export { createAppointmentEventAsync, rescheduleAppointmentEventAsync, cancelAppointmentEventAsync } from "./events/appointmentEventFacade.js";
20
21
  export { mapAppointmentToEventSnapshot } from "./events/mapAppointmentToEventSnapshot.js";
21
- export { CalendarModel, EventModel as CoreEventModel, ParticipantModel as CoreParticipantModel, CalendarParticipantModel, configure, getConfig } from "@blazeo.com/calendar-client";
22
+ import { CalendarModel as CoreCalendarModel, EventModel as CoreEventModel, ParticipantModel as CoreParticipantModel, CalendarParticipantModel as CoreCalendarParticipantModel, configure, getConfig } from "@blazeo.com/calendar-client";
23
+ export { CoreCalendarModel as CalendarModel, CoreEventModel as CoreEventModel, CoreParticipantModel as CoreParticipantModel, CoreCalendarParticipantModel as CalendarParticipantModel, configure, getConfig };
22
24
  export declare const packageName = "@blazeo.com/appointment-client";
23
25
  export declare class CalendarClient {
24
26
  name: string;
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export { blazeoClientConfig } from "./config/blazeoClientDefaults.js";
6
6
  export { applyBlazeoClientConfig } from "./config/applyBlazeoDefaults.js";
7
7
  export { createCalendarRoot, CalendarRootModel, CalendarSlotModel, EventModel, ParticipantModel } from "./models/CalendarRootModel.js";
8
8
  export { fetchCalendarDetails, fetchCalendarBundle, normalizeOpeningHours } from "./calendar/fetchCalendarDetails.js";
9
+ export { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
9
10
  export { buildUnifiedCalendarView, } from "./calendar/buildUnifiedCalendarView.js";
10
11
  export { fetchCalendarWithOpeningHours, unwrapCalendarGetData, pickOpeningHoursArrayFromCalendarPayload, normalizeParticipantOpeningHoursResponse } from "./calendar/fetchCalendarWithOpeningHours.js";
11
12
  export { getOpeningHours } from "./calendar/getOpeningHours.js";
@@ -19,7 +20,16 @@ export { CalendarCreation, createCalendarWithRelationsAsync, updateCalendarWithR
19
20
  export { addParticipantToCalendar, removeParticipantFromCalendar, saveCalendarOpeningHour, saveCalendarOpeningHoursBatch } from "./calendar/blazeoCalendarRelationMethods.js";
20
21
  export { createAppointmentEventAsync, rescheduleAppointmentEventAsync, cancelAppointmentEventAsync } from "./events/appointmentEventFacade.js";
21
22
  export { mapAppointmentToEventSnapshot } from "./events/mapAppointmentToEventSnapshot.js";
22
- export { CalendarModel, EventModel as CoreEventModel, ParticipantModel as CoreParticipantModel, CalendarParticipantModel, configure, getConfig } from "@blazeo.com/calendar-client";
23
+ import { fetchCalendarDetails, fetchCalendarBundle } from "./calendar/fetchCalendarDetails.js";
24
+ import { fetchCalendarWithOpeningHours } from "./calendar/fetchCalendarWithOpeningHours.js";
25
+ import { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
26
+ import { CalendarModel as CoreCalendarModel, EventModel as CoreEventModel, ParticipantModel as CoreParticipantModel, CalendarParticipantModel as CoreCalendarParticipantModel, configure, getConfig } from "@blazeo.com/calendar-client";
27
+ // Attach new methods to CalendarModel for easier access
28
+ CoreCalendarModel.fetchCalendarDetails = fetchCalendarDetails;
29
+ CoreCalendarModel.fetchCalendarBundle = fetchCalendarBundle;
30
+ CoreCalendarModel.fetchCalendarWithOpeningHours = fetchCalendarWithOpeningHours;
31
+ CoreCalendarModel.getCalendarsByCompany = getCalendarsByCompany;
32
+ export { CoreCalendarModel as CalendarModel, CoreEventModel as CoreEventModel, CoreParticipantModel as CoreParticipantModel, CoreCalendarParticipantModel as CalendarParticipantModel, configure, getConfig };
23
33
  export const packageName = "@blazeo.com/appointment-client";
24
34
  export class CalendarClient {
25
35
  name = "CalendarClient";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blazeo.com/appointment-client",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -291,41 +291,18 @@ export function FetchCalendarTab() {
291
291
 
292
292
  setBusy(true);
293
293
  try {
294
- const byCompany = await CalendarModel.getByCompany(key);
295
- const list = byCompany?.calendars ?? byCompany;
296
- if (list == null || !Array.isArray(list) || list.length === 0) {
297
- setNote(
298
- "getByCompany returned null or an empty list. (calendar-client ≥1.0.17 returns { calendars, totalCount }.)"
299
- );
300
- setOutput(toDisplayJson(byCompany));
294
+ // Use the new optimized method that includes members automatically
295
+ const enriched = await CalendarModel.getCalendarsByCompany(key, {
296
+ baseUrl: effective.baseUrl,
297
+ ...(effective.consumer ? { consumer: effective.consumer } : {}),
298
+ });
299
+
300
+ if (!enriched || enriched.length === 0) {
301
+ setNote("getCalendarsByCompany returned an empty list.");
302
+ setOutput("[]");
301
303
  } else {
302
- const enriched = await Promise.all(
303
- list.map(async (c) => {
304
- const id = c.calendarId ?? String(c.id ?? "");
305
- if (!id) return { calendar: getSnapshot(c), openingHours: [], meta: { error: "no id" } };
306
- try {
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" } };
315
- } catch (err) {
316
- return {
317
- calendar: getSnapshot(c),
318
- openingHours: [],
319
- meta: {
320
- error: err instanceof Error ? err.message : String(err),
321
- },
322
- };
323
- }
324
- })
325
- );
326
- const total = byCompany?.totalCount ?? list.length;
327
304
  setNote(
328
- `Loaded ${list.length} calendar(s) (totalCount=${total}); opening hours (embed → participant API) + participants per calendar.`
305
+ `Loaded ${enriched.length} calendar(s) with members using optimized getCalendarsByCompany.`
329
306
  );
330
307
  setOutput(toDisplayJson(enriched));
331
308
  }
@@ -1,5 +1,5 @@
1
1
  import { getSnapshot } from "mobx-state-tree";
2
- import { addParticipantToCalendar, saveCalendarOpeningHour } from "./blazeoCalendarRelationMethods.js";
2
+ import { addParticipantToCalendar, saveCalendarOpeningHour, saveCalendarOpeningHoursBatch } from "./blazeoCalendarRelationMethods.js";
3
3
  import { createCalendarAsync, updateCalendarAsync, deleteCalendarAsync } from "./createCalendar.js";
4
4
 
5
5
  function isFailureStatus(res: any) {
@@ -97,8 +97,11 @@ async function runMembersAndOpeningHoursAfterCalendarSave(calendar: any, calenda
97
97
  membersAdded += 1;
98
98
  }
99
99
 
100
- let openingHoursSaved = 0;
101
- for (const oh of calendar.openingHours ?? []) {
100
+ // 2. Save Opening Hours (Plan V2: Grouped by participant with explicit off-days per slot)
101
+ const openingHours = calendar.openingHours ?? [];
102
+ const hoursByParticipant = new Map<string, any[]>();
103
+
104
+ for (const oh of openingHours) {
102
105
  const participantId = resolveParticipantIdForOpeningHour(oh);
103
106
  if (!participantId) {
104
107
  return {
@@ -106,8 +109,21 @@ async function runMembersAndOpeningHoursAfterCalendarSave(calendar: any, calenda
106
109
  error: `Opening hour id ${oh.id}: participantId is required.`,
107
110
  };
108
111
  }
109
- for (const day of oh.days ?? []) {
110
- const payload = {
112
+
113
+ if (!hoursByParticipant.has(participantId)) {
114
+ hoursByParticipant.set(participantId, []);
115
+ }
116
+
117
+ // Plan V2 Logic: For every opening hour object, generate EXACTLY 7 entries (days 0-6).
118
+ // If the day is in oh.days, it's ON. If not, it's OFF.
119
+ const activeDays = oh.days ?? [];
120
+ const openingHourId = oh.openingHourId?.trim() || newOpeningHourId();
121
+
122
+ for (let day = 0; day <= 6; day++) {
123
+ const isIncluded = activeDays.includes(day);
124
+ const isOff = isIncluded ? !!oh.off : true; // If not in days array, it's explicitly OFF
125
+
126
+ hoursByParticipant.get(participantId)?.push({
111
127
  calendarId: calendarIdStr,
112
128
  participantId,
113
129
  day,
@@ -115,23 +131,31 @@ async function runMembersAndOpeningHoursAfterCalendarSave(calendar: any, calenda
115
131
  startMinute: oh.startMinute,
116
132
  endHour: oh.endHour,
117
133
  endMinute: oh.endMinute,
118
- off: oh.off,
119
- openingHourId: oh.openingHourId?.trim() || newOpeningHourId(),
134
+ off: isOff,
135
+ openingHourId: openingHourId,
136
+ });
137
+ }
138
+ }
139
+
140
+ let openingHoursSaved = 0;
141
+ for (const [participantId, payload] of hoursByParticipant.entries()) {
142
+ if (payload.length === 0) continue;
143
+
144
+ // Use the batch save method (plural)
145
+ const res = await saveCalendarOpeningHoursBatch(calendarNode, payload);
146
+
147
+ if (isFailureStatus(res)) {
148
+ const msg =
149
+ res.message ??
150
+ (typeof res.data === "string" ? res.data : undefined) ??
151
+ JSON.stringify(res);
152
+ return {
153
+ ok: false,
154
+ error: `saveOpeningHours batch failed for participant ${participantId}: ${msg}`,
155
+ apiResponse: res,
120
156
  };
121
- const res = await saveCalendarOpeningHour(calendarNode, payload);
122
- if (isFailureStatus(res)) {
123
- const msg =
124
- res.message ??
125
- (typeof res.data === "string" ? res.data : undefined) ??
126
- JSON.stringify(res);
127
- return {
128
- ok: false,
129
- error: `saveOpeningHour failed (opening hour ${oh.id}, day ${day}): ${msg}`,
130
- apiResponse: res,
131
- };
132
- }
133
- openingHoursSaved += 1;
134
157
  }
158
+ openingHoursSaved += payload.length;
135
159
  }
136
160
 
137
161
  return {
@@ -0,0 +1,125 @@
1
+ import { CalendarModel, CalendarParticipantModel } from "@blazeo.com/calendar-client";
2
+ import { ensureBlazeoHttpReady } from "../config/ensureBlazeoHttpReady.js";
3
+
4
+ /**
5
+ * Fetches all calendars for a company and populates each with its members (participants).
6
+ * Fetches both Participant List and Participant Info to ensure names and emails are included,
7
+ * while still skipping heavy data like opening hours.
8
+ */
9
+ export async function getCalendarsByCompany(
10
+ companyKey: string,
11
+ connection: { baseUrl?: string; consumer?: string } = {}
12
+ ) {
13
+ const ready = ensureBlazeoHttpReady(connection);
14
+ if (!ready.ok) {
15
+ throw new Error(ready.error);
16
+ }
17
+
18
+ // 1. Get all calendars for the company
19
+ const result = await CalendarModel.getByCompany(companyKey);
20
+ const calendars = Array.isArray(result) ? result : (result as any)?.calendars ?? [];
21
+
22
+ if (!calendars || calendars.length === 0) {
23
+ return [];
24
+ }
25
+
26
+ // 2. Fetch lightweight members for each calendar in parallel
27
+ const enrichedCalendars = await Promise.all(
28
+ calendars.map(async (cal: any) => {
29
+ const calendarId = cal.calendarId ?? String(cal.id ?? "");
30
+ if (!calendarId) return null;
31
+
32
+ try {
33
+ // We need both List and Info to get the names/emails
34
+ const [partsRaw, infoRaw] = await Promise.all([
35
+ CalendarParticipantModel.getByCalendar(calendarId),
36
+ CalendarParticipantModel.getInfoByCalendar(calendarId)
37
+ ]);
38
+
39
+ const parts = Array.isArray(partsRaw) ? partsRaw : (partsRaw as any)?.participants ?? [];
40
+ const info = Array.isArray(infoRaw) ? infoRaw : (infoRaw as any)?.info ?? [];
41
+
42
+ // Merge logic to ensure names are matched to IDs
43
+ const membersMap = new Map<string, any>();
44
+
45
+ // Use participantId GUID as the primary key
46
+ const getAnyId = (obj: any) =>
47
+ obj.participantId ?? obj.ParticipantId ?? obj.participant_id ?? obj.id ?? obj.Id;
48
+
49
+ // 1. Initialize with basic participant data
50
+ parts.forEach((p: any) => {
51
+ const mid = getAnyId(p);
52
+ if (mid) {
53
+ membersMap.set(String(mid).toLowerCase(), {
54
+ id: mid,
55
+ name: p.name ?? p.Name ?? "Member",
56
+ email: p.email ?? p.Email,
57
+ status: p.status ?? p.Status ?? 1,
58
+ uuId: mid
59
+ });
60
+ }
61
+ });
62
+
63
+ // 2. Enrich with detailed info (Name, Email, Alias)
64
+ info.forEach((i: any) => {
65
+ const mid = getAnyId(i);
66
+ if (!mid) return;
67
+ const key = String(mid).toLowerCase();
68
+ const existing = membersMap.get(key);
69
+
70
+ const resolvedEmail = i.email ?? i.Email ?? i.userSsoEmail ?? i.UserSsoEmail ?? existing?.email;
71
+
72
+ const memberData = {
73
+ id: mid,
74
+ name: i.name ?? i.Name ?? i.alias ?? i.Alias ?? (existing?.name || "Member"),
75
+ email: resolvedEmail,
76
+ alias: i.alias ?? i.Alias ?? i.name ?? i.Name,
77
+ userSsoEmail: resolvedEmail,
78
+ uuId: mid,
79
+ status: i.status ?? i.Status ?? existing?.status ?? 1
80
+ };
81
+
82
+ if (!existing) {
83
+ membersMap.set(key, memberData);
84
+ } else {
85
+ Object.assign(existing, memberData);
86
+ }
87
+ });
88
+
89
+ const members = Array.from(membersMap.values());
90
+
91
+ // Map to the EXACT schema requested by the user
92
+ return {
93
+ id: cal.id ?? cal.Id,
94
+ calendarLink: cal.calendarLink ?? cal.CalendarLink ?? "",
95
+ uuid: calendarId,
96
+ createdOn: cal.createdOn ?? cal.CreatedOn,
97
+ name: cal.name ?? cal.Name,
98
+ timeZoneId: cal.timeZoneId ?? cal.TimeZoneId,
99
+ description: cal.description ?? cal.Description ?? "",
100
+ assignmentType: cal.assignmentMethod ?? cal.AssignmentMethod ?? cal.assignmentType,
101
+ status: cal.status ?? cal.Status ?? 1,
102
+ location: cal.location ?? cal.Location ?? "",
103
+ members
104
+ };
105
+ } catch (err) {
106
+ console.error(`[getCalendarsByCompany] Error fetching members for ${calendarId}:`, err);
107
+ return {
108
+ id: cal.id ?? cal.Id,
109
+ calendarLink: cal.calendarLink ?? cal.CalendarLink ?? "",
110
+ uuid: calendarId,
111
+ createdOn: cal.createdOn ?? cal.CreatedOn,
112
+ name: cal.name ?? cal.Name,
113
+ timeZoneId: cal.timeZoneId ?? cal.TimeZoneId,
114
+ description: cal.description ?? cal.Description ?? "",
115
+ assignmentType: cal.assignmentMethod ?? cal.AssignmentMethod ?? cal.assignmentType,
116
+ status: cal.status ?? cal.Status ?? 1,
117
+ location: cal.location ?? cal.Location ?? "",
118
+ members: []
119
+ };
120
+ }
121
+ })
122
+ );
123
+
124
+ return enrichedCalendars.filter(c => c !== null);
125
+ }
package/src/index.ts CHANGED
@@ -1,51 +1,72 @@
1
- import { getExampleSlots } from "./exampleData.js";
2
-
3
- export {
4
- initializeAppointmentClient,
5
- isAppointmentClientConfigured,
6
- } from "./config/initializeAppointmentClient.js";
7
- export { syncBlazeoConnection } from "./config/syncBlazeoConnection.js";
8
- export { ensureBlazeoHttpReady } from "./config/ensureBlazeoHttpReady.js";
9
- export type { EnsureBlazeoHttpOptions } from "./config/ensureBlazeoHttpReady.js";
10
- export { blazeoClientConfig } from "./config/blazeoClientDefaults.js";
11
- export { applyBlazeoClientConfig } from "./config/applyBlazeoDefaults.js";
12
- export { createCalendarRoot, CalendarRootModel, CalendarSlotModel, EventModel, ParticipantModel } from "./models/CalendarRootModel.js";
13
- export { fetchCalendarDetails, fetchCalendarBundle, normalizeOpeningHours } from "./calendar/fetchCalendarDetails.js";
14
- export {
15
- buildUnifiedCalendarView,
16
- type UnifiedCalendarMember,
17
- type UnifiedCalendarView,
18
- type UnifiedOpeningHourRow,
19
- type UnifiedParticipantWithHours,
20
- } from "./calendar/buildUnifiedCalendarView.js";
21
- export { fetchCalendarWithOpeningHours, unwrapCalendarGetData, pickOpeningHoursArrayFromCalendarPayload, normalizeParticipantOpeningHoursResponse } from "./calendar/fetchCalendarWithOpeningHours.js";
22
- export { getOpeningHours } from "./calendar/getOpeningHours.js";
23
- export { getParticipantOpeningHours } from "./calendar/getParticipantOpeningHours.js";
24
- export { getAllParticipantOpeningHours } from "./calendar/getAllParticipantOpeningHours.js";
25
- export { getParticipants } from "./calendar/getParticipants.js";
26
- export { getExampleSlots, getExampleEvents, getExampleParticipants, getExampleCalendarRoot, getExampleCalendarRootSnapshot } from "./exampleData.js";
27
-
28
- // Re-export core models from calendar-client for convenience
29
- export { createCalendarAsync, updateCalendarAsync, deleteCalendarAsync, resolveBlazeoConnection } from "./calendar/createCalendar.js";
30
- export { CalendarCreation, createCalendarWithRelationsAsync, updateCalendarWithRelationsAsync, resolveParticipantIdForOpeningHour } from "./calendar/calendarCreation.js";
31
- export { addParticipantToCalendar, removeParticipantFromCalendar, saveCalendarOpeningHour, saveCalendarOpeningHoursBatch } from "./calendar/blazeoCalendarRelationMethods.js";
32
- export { createAppointmentEventAsync, rescheduleAppointmentEventAsync, cancelAppointmentEventAsync } from "./events/appointmentEventFacade.js";
33
- export { mapAppointmentToEventSnapshot } from "./events/mapAppointmentToEventSnapshot.js";
34
-
35
- export {
36
- CalendarModel,
37
- EventModel as CoreEventModel,
38
- ParticipantModel as CoreParticipantModel,
39
- CalendarParticipantModel,
40
- configure,
41
- getConfig
42
- } from "@blazeo.com/calendar-client";
43
-
44
- export const packageName = "@blazeo.com/appointment-client";
45
-
46
- export class CalendarClient {
47
- name = "CalendarClient";
48
- getExampleSlots() {
49
- return getExampleSlots();
50
- }
51
- }
1
+ import { getExampleSlots } from "./exampleData.js";
2
+
3
+ export {
4
+ initializeAppointmentClient,
5
+ isAppointmentClientConfigured,
6
+ } from "./config/initializeAppointmentClient.js";
7
+ export { syncBlazeoConnection } from "./config/syncBlazeoConnection.js";
8
+ export { ensureBlazeoHttpReady } from "./config/ensureBlazeoHttpReady.js";
9
+ export type { EnsureBlazeoHttpOptions } from "./config/ensureBlazeoHttpReady.js";
10
+ export { blazeoClientConfig } from "./config/blazeoClientDefaults.js";
11
+ export { applyBlazeoClientConfig } from "./config/applyBlazeoDefaults.js";
12
+ export { createCalendarRoot, CalendarRootModel, CalendarSlotModel, EventModel, ParticipantModel } from "./models/CalendarRootModel.js";
13
+ export { fetchCalendarDetails, fetchCalendarBundle, normalizeOpeningHours } from "./calendar/fetchCalendarDetails.js";
14
+ export { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
15
+ export {
16
+ buildUnifiedCalendarView,
17
+ type UnifiedCalendarMember,
18
+ type UnifiedCalendarView,
19
+ type UnifiedOpeningHourRow,
20
+ type UnifiedParticipantWithHours,
21
+ } from "./calendar/buildUnifiedCalendarView.js";
22
+ export { fetchCalendarWithOpeningHours, unwrapCalendarGetData, pickOpeningHoursArrayFromCalendarPayload, normalizeParticipantOpeningHoursResponse } from "./calendar/fetchCalendarWithOpeningHours.js";
23
+ export { getOpeningHours } from "./calendar/getOpeningHours.js";
24
+ export { getParticipantOpeningHours } from "./calendar/getParticipantOpeningHours.js";
25
+ export { getAllParticipantOpeningHours } from "./calendar/getAllParticipantOpeningHours.js";
26
+ export { getParticipants } from "./calendar/getParticipants.js";
27
+ export { getExampleSlots, getExampleEvents, getExampleParticipants, getExampleCalendarRoot, getExampleCalendarRootSnapshot } from "./exampleData.js";
28
+
29
+ // Re-export core models from calendar-client for convenience
30
+ export { createCalendarAsync, updateCalendarAsync, deleteCalendarAsync, resolveBlazeoConnection } from "./calendar/createCalendar.js";
31
+ export { CalendarCreation, createCalendarWithRelationsAsync, updateCalendarWithRelationsAsync, resolveParticipantIdForOpeningHour } from "./calendar/calendarCreation.js";
32
+ export { addParticipantToCalendar, removeParticipantFromCalendar, saveCalendarOpeningHour, saveCalendarOpeningHoursBatch } from "./calendar/blazeoCalendarRelationMethods.js";
33
+ export { createAppointmentEventAsync, rescheduleAppointmentEventAsync, cancelAppointmentEventAsync } from "./events/appointmentEventFacade.js";
34
+ export { mapAppointmentToEventSnapshot } from "./events/mapAppointmentToEventSnapshot.js";
35
+
36
+ import { fetchCalendarDetails, fetchCalendarBundle } from "./calendar/fetchCalendarDetails.js";
37
+ import { fetchCalendarWithOpeningHours } from "./calendar/fetchCalendarWithOpeningHours.js";
38
+ import { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
39
+
40
+ import {
41
+ CalendarModel as CoreCalendarModel,
42
+ EventModel as CoreEventModel,
43
+ ParticipantModel as CoreParticipantModel,
44
+ CalendarParticipantModel as CoreCalendarParticipantModel,
45
+ configure,
46
+ getConfig
47
+ } from "@blazeo.com/calendar-client";
48
+
49
+ // Attach new methods to CalendarModel for easier access
50
+ (CoreCalendarModel as any).fetchCalendarDetails = fetchCalendarDetails;
51
+ (CoreCalendarModel as any).fetchCalendarBundle = fetchCalendarBundle;
52
+ (CoreCalendarModel as any).fetchCalendarWithOpeningHours = fetchCalendarWithOpeningHours;
53
+ (CoreCalendarModel as any).getCalendarsByCompany = getCalendarsByCompany;
54
+
55
+ export {
56
+ CoreCalendarModel as CalendarModel,
57
+ CoreEventModel as CoreEventModel,
58
+ CoreParticipantModel as CoreParticipantModel,
59
+ CoreCalendarParticipantModel as CalendarParticipantModel,
60
+ configure,
61
+ getConfig
62
+ };
63
+
64
+
65
+ export const packageName = "@blazeo.com/appointment-client";
66
+
67
+ export class CalendarClient {
68
+ name = "CalendarClient";
69
+ getExampleSlots() {
70
+ return getExampleSlots();
71
+ }
72
+ }