@blazeo.com/appointment-client 1.0.10 → 1.0.13
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.11.tgz +0 -0
- package/blazeo.com-appointment-client-1.0.13.tgz +0 -0
- package/dist/calendar/buildUnifiedCalendarView.js +19 -4
- package/dist/calendar/calendarCreation.js +8 -1
- package/dist/calendar/fetchCalendarDetails.js +5 -2
- package/dist/calendar/getCalendarsByCompany.d.ts +2 -2
- package/dist/calendar/getCalendarsByCompany.js +16 -61
- package/dist/calendar/mapToDesiredResponse.d.ts +2 -16
- package/dist/calendar/mapToDesiredResponse.js +29 -17
- package/dist/index.d.ts +43 -4
- package/dist/index.js +10 -10
- package/dist/lead/fetchLeadDetails.d.ts +56 -0
- package/dist/lead/fetchLeadDetails.js +61 -0
- package/package.json +1 -1
- package/sample/src/FetchCalendarTab.jsx +19 -33
- package/sample/src/LeadTab.jsx +335 -0
- package/src/calendar/buildUnifiedCalendarView.ts +22 -4
- package/src/calendar/calendarCreation.ts +9 -1
- package/src/calendar/fetchCalendarDetails.ts +10 -2
- package/src/calendar/getCalendarsByCompany.ts +17 -64
- package/src/calendar/mapToDesiredResponse.ts +31 -17
- package/src/index.ts +9 -11
- package/src/lead/fetchLeadDetails.ts +106 -0
- package/blazeo.com-appointment-client-1.0.10.tgz +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -63,18 +63,33 @@ function dayOrderIndex(d) {
|
|
|
63
63
|
const i = DAY_NAMES.indexOf(u);
|
|
64
64
|
return i >= 0 ? i : 999;
|
|
65
65
|
}
|
|
66
|
-
/** Merge rows that share participant + time span
|
|
66
|
+
/** Merge rows that share participant + time span into one row with combined active `days`. */
|
|
67
67
|
function mergeOpeningHoursBySlot(rows) {
|
|
68
68
|
const map = new Map();
|
|
69
69
|
for (const r of rows) {
|
|
70
|
-
|
|
70
|
+
// Key excludes 'off' because we want to merge ON and OFF records for the same time slot
|
|
71
|
+
const key = [r.member, r.startHour, r.startMinute, r.endHour, r.endMinute].join("|");
|
|
71
72
|
const existing = map.get(key);
|
|
73
|
+
// We only want to add the day to the 'days' array if it is NOT marked as OFF.
|
|
74
|
+
// If the whole record was marked as OFF, we still process it to establish the slot,
|
|
75
|
+
// but its days won't be listed as 'active'.
|
|
76
|
+
const activeDaysFromThisRow = r.off ? [] : r.days;
|
|
72
77
|
if (!existing) {
|
|
73
|
-
map.set(key, { ...r, days: [...
|
|
78
|
+
map.set(key, { ...r, days: [...activeDaysFromThisRow], off: false });
|
|
74
79
|
}
|
|
75
80
|
else {
|
|
76
|
-
const set = new Set([...existing.days, ...
|
|
81
|
+
const set = new Set([...existing.days, ...activeDaysFromThisRow]);
|
|
77
82
|
existing.days = Array.from(set).sort((a, b) => dayOrderIndex(a) - dayOrderIndex(b));
|
|
83
|
+
// If we encounter any record that is NOT off, the whole merged slot is NOT off.
|
|
84
|
+
if (!r.off)
|
|
85
|
+
existing.off = false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Final Pass: If a slot has NO active days, it should be marked as off: true
|
|
89
|
+
// (though in Plan V2, these usually just disappear from the UI's 'days' list)
|
|
90
|
+
for (const group of map.values()) {
|
|
91
|
+
if (group.days.length === 0) {
|
|
92
|
+
group.off = true;
|
|
78
93
|
}
|
|
79
94
|
}
|
|
80
95
|
rows.length = 0;
|
|
@@ -120,7 +120,10 @@ async function runMembersAndOpeningHoursAfterCalendarSave(calendar, calendarNode
|
|
|
120
120
|
endHour: oh.endHour,
|
|
121
121
|
endMinute: oh.endMinute,
|
|
122
122
|
off: isOff,
|
|
123
|
-
|
|
123
|
+
// Plan V2 Optimization: Generate a unique ID for EVERY day record.
|
|
124
|
+
// This prevents the backend from deduplicating/overwriting when multiple
|
|
125
|
+
// records for the same participant + slot are sent in one batch.
|
|
126
|
+
openingHourId: newOpeningHourId(),
|
|
124
127
|
});
|
|
125
128
|
}
|
|
126
129
|
}
|
|
@@ -128,6 +131,10 @@ async function runMembersAndOpeningHoursAfterCalendarSave(calendar, calendarNode
|
|
|
128
131
|
for (const [participantId, payload] of hoursByParticipant.entries()) {
|
|
129
132
|
if (payload.length === 0)
|
|
130
133
|
continue;
|
|
134
|
+
// Plan V2 Optimization: Clear existing records for this participant first.
|
|
135
|
+
// This ensures that when we save the new batch (with unique per-day IDs),
|
|
136
|
+
// we don't leak orphaned records or create duplicates during updates.
|
|
137
|
+
await calendarNode.removeParticipantOpeningHours(participantId);
|
|
131
138
|
// Use the batch save method (plural)
|
|
132
139
|
const res = await saveCalendarOpeningHoursBatch(calendarNode, payload);
|
|
133
140
|
if (isFailureStatus(res)) {
|
|
@@ -224,9 +224,12 @@ export async function fetchCalendarDetails(calendarId, options = {}) {
|
|
|
224
224
|
allParticipantOpeningHours.length > 0;
|
|
225
225
|
if (!calendarView)
|
|
226
226
|
return null;
|
|
227
|
+
// Use the mapper to normalize the final output, ensuring all fields like duration,
|
|
228
|
+
// bookingPageTitle, calendarId, etc. are correctly picked and named.
|
|
229
|
+
const finalView = mapToDesiredCalendarResponse(payload, calendarView.openingHours, calendarView.members);
|
|
227
230
|
// Attach metadata as non-enumerable properties so they don't show up in JSON.stringify
|
|
228
231
|
// but are still accessible for debugging if needed.
|
|
229
|
-
Object.defineProperties(
|
|
232
|
+
Object.defineProperties(finalView, {
|
|
230
233
|
_cal: { value: cal, enumerable: false },
|
|
231
234
|
_participants: { value: participantList, enumerable: false },
|
|
232
235
|
_openingHours: { value: openingHours, enumerable: false },
|
|
@@ -241,7 +244,7 @@ export async function fetchCalendarDetails(calendarId, options = {}) {
|
|
|
241
244
|
enumerable: false
|
|
242
245
|
},
|
|
243
246
|
});
|
|
244
|
-
return
|
|
247
|
+
return finalView;
|
|
245
248
|
}
|
|
246
249
|
/**
|
|
247
250
|
* Single return value only: unified calendar **`calendarView`** —
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fetches all calendars for a company and populates each with its members (participants).
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Uses a highly optimized single-request approach per calendar to ensure speed in list views.
|
|
4
|
+
* Results are normalized via mapToDesiredCalendarResponse.
|
|
5
5
|
*/
|
|
6
6
|
export declare function getCalendarsByCompany(companyKey: string, connection?: {
|
|
7
7
|
baseUrl?: string;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { CalendarModel, CalendarParticipantModel } from "@blazeo.com/calendar-client";
|
|
2
2
|
import { ensureBlazeoHttpReady } from "../config/ensureBlazeoHttpReady.js";
|
|
3
|
+
import { mapToDesiredCalendarResponse } from "./mapToDesiredResponse.js";
|
|
3
4
|
/**
|
|
4
5
|
* Fetches all calendars for a company and populates each with its members (participants).
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Uses a highly optimized single-request approach per calendar to ensure speed in list views.
|
|
7
|
+
* Results are normalized via mapToDesiredCalendarResponse.
|
|
7
8
|
*/
|
|
8
9
|
export async function getCalendarsByCompany(companyKey, connection = {}) {
|
|
9
10
|
const ready = ensureBlazeoHttpReady(connection);
|
|
@@ -22,86 +23,40 @@ export async function getCalendarsByCompany(companyKey, connection = {}) {
|
|
|
22
23
|
if (!calendarId)
|
|
23
24
|
return null;
|
|
24
25
|
try {
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
CalendarParticipantModel.getInfoByCalendar(calendarId)
|
|
29
|
-
]);
|
|
30
|
-
const parts = Array.isArray(partsRaw) ? partsRaw : partsRaw?.participants ?? [];
|
|
26
|
+
// Optimization: Use only getInfoByCalendar to get names/emails in a single request.
|
|
27
|
+
// This is much faster for a list view than fetching both list and info records.
|
|
28
|
+
const infoRaw = await CalendarParticipantModel.getInfoByCalendar(calendarId);
|
|
31
29
|
const info = Array.isArray(infoRaw) ? infoRaw : infoRaw?.info ?? [];
|
|
32
30
|
// Merge logic to ensure names are matched to IDs
|
|
33
31
|
const membersMap = new Map();
|
|
34
32
|
// Use participantId GUID as the primary key
|
|
35
33
|
const getAnyId = (obj) => obj.participantId ?? obj.ParticipantId ?? obj.participant_id ?? obj.id ?? obj.Id;
|
|
36
|
-
// 1.
|
|
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)
|
|
34
|
+
// 1. Process info list (Name, Email, Alias)
|
|
50
35
|
info.forEach((i) => {
|
|
51
36
|
const mid = getAnyId(i);
|
|
52
37
|
if (!mid)
|
|
53
38
|
return;
|
|
54
39
|
const key = String(mid).toLowerCase();
|
|
55
|
-
const
|
|
56
|
-
const resolvedEmail = i.email ?? i.Email ?? i.userSsoEmail ?? i.UserSsoEmail ?? existing?.email;
|
|
40
|
+
const resolvedEmail = i.email ?? i.Email ?? i.userSsoEmail ?? i.UserSsoEmail;
|
|
57
41
|
const memberData = {
|
|
58
42
|
id: mid,
|
|
59
|
-
name: i.name ?? i.Name ?? i.alias ?? i.Alias ??
|
|
43
|
+
name: i.name ?? i.Name ?? i.alias ?? i.Alias ?? "Member",
|
|
60
44
|
email: resolvedEmail,
|
|
61
|
-
|
|
62
|
-
userSsoEmail: resolvedEmail,
|
|
63
|
-
uuId: mid,
|
|
64
|
-
status: i.status ?? i.Status ?? existing?.status ?? 1
|
|
45
|
+
status: i.status ?? i.Status ?? 1,
|
|
65
46
|
};
|
|
66
|
-
if (!
|
|
47
|
+
if (!membersMap.has(key)) {
|
|
67
48
|
membersMap.set(key, memberData);
|
|
68
49
|
}
|
|
69
|
-
else {
|
|
70
|
-
Object.assign(existing, memberData);
|
|
71
|
-
}
|
|
72
50
|
});
|
|
73
51
|
const members = Array.from(membersMap.values());
|
|
74
|
-
//
|
|
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
|
-
};
|
|
52
|
+
// Use the unified mapper to ensure all properties (duration, calendarId, etc.) are included
|
|
53
|
+
return mapToDesiredCalendarResponse(cal, [], members);
|
|
88
54
|
}
|
|
89
55
|
catch (err) {
|
|
90
56
|
console.error(`[getCalendarsByCompany] Error fetching members for ${calendarId}:`, err);
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
};
|
|
57
|
+
// Fallback to minimal mapping if enrichment fails
|
|
58
|
+
return mapToDesiredCalendarResponse(cal, [], []);
|
|
104
59
|
}
|
|
105
60
|
}));
|
|
106
|
-
return enrichedCalendars.filter(
|
|
61
|
+
return enrichedCalendars.filter(Boolean);
|
|
107
62
|
}
|
|
@@ -15,6 +15,7 @@ export declare function mapToDesiredCalendarResponse(payload: any, openingHours?
|
|
|
15
15
|
bufferTimeUnit: number | null;
|
|
16
16
|
calendarLink: any;
|
|
17
17
|
uuid: any;
|
|
18
|
+
calendarId: any;
|
|
18
19
|
location: any;
|
|
19
20
|
bookingPageTitle: any;
|
|
20
21
|
reminderChannelStatuses: {
|
|
@@ -49,22 +50,7 @@ export declare function mapToDesiredCalendarResponse(payload: any, openingHours?
|
|
|
49
50
|
logoUrl: any;
|
|
50
51
|
__typename: string;
|
|
51
52
|
} | null;
|
|
52
|
-
openingHours:
|
|
53
|
-
id: any;
|
|
54
|
-
createdOn: any;
|
|
55
|
-
modifiedOn: any;
|
|
56
|
-
member: any;
|
|
57
|
-
openingHourId: any;
|
|
58
|
-
calendarId: any;
|
|
59
|
-
participantId: any;
|
|
60
|
-
days: any;
|
|
61
|
-
startHour: any;
|
|
62
|
-
startMinute: any;
|
|
63
|
-
endHour: any;
|
|
64
|
-
endMinute: any;
|
|
65
|
-
off: boolean;
|
|
66
|
-
__typename: string;
|
|
67
|
-
}[];
|
|
53
|
+
openingHours: any[];
|
|
68
54
|
appointmentUserDefinedFields: any;
|
|
69
55
|
__typename: string;
|
|
70
56
|
} | null;
|
|
@@ -22,22 +22,32 @@ export function mapToDesiredCalendarResponse(payload, openingHours = [], members
|
|
|
22
22
|
__typename: "Member"
|
|
23
23
|
}));
|
|
24
24
|
// Map opening hours with typename and raw fields
|
|
25
|
-
const mappedOpeningHours = openingHours.map(oh =>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
const mappedOpeningHours = openingHours.map(oh => {
|
|
26
|
+
// If it's already a unified object (has day/start/end), preserve it but ensure __typename
|
|
27
|
+
if (oh.day !== undefined && oh.start !== undefined && oh.end !== undefined) {
|
|
28
|
+
return {
|
|
29
|
+
...oh,
|
|
30
|
+
__typename: "OpeningHour"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Otherwise, map from raw PascalCase or camelCase
|
|
34
|
+
return {
|
|
35
|
+
id: pick(oh, "id", "Id") ?? 0,
|
|
36
|
+
createdOn: pick(oh, "createdOn", "CreatedOn", "created_on") ?? "0001-01-01T00:00:00.000Z",
|
|
37
|
+
modifiedOn: pick(oh, "modifiedOn", "ModifiedOn", "modified_on") ?? "0001-01-01T00:00:00.000Z",
|
|
38
|
+
member: pick(oh, "member", "Member"),
|
|
39
|
+
openingHourId: pick(oh, "openingHourId", "OpeningHourId", "opening_hour_id") ?? "",
|
|
40
|
+
calendarId: pick(oh, "calendarId", "CalendarId", "calendar_id") ?? "",
|
|
41
|
+
participantId: pick(oh, "participantId", "ParticipantId", "participant_id") ?? "",
|
|
42
|
+
days: oh.days ?? [],
|
|
43
|
+
startHour: oh.startHour ?? pick(oh, "startHour", "StartHour") ?? 0,
|
|
44
|
+
startMinute: oh.startMinute ?? pick(oh, "startMinute", "StartMinute") ?? 0,
|
|
45
|
+
endHour: oh.endHour ?? pick(oh, "endHour", "EndHour") ?? 0,
|
|
46
|
+
endMinute: oh.endMinute ?? pick(oh, "endMinute", "EndMinute") ?? 0,
|
|
47
|
+
off: !!(oh.off ?? pick(oh, "off", "Off")),
|
|
48
|
+
__typename: "OpeningHour"
|
|
49
|
+
};
|
|
50
|
+
});
|
|
41
51
|
// Map theme
|
|
42
52
|
const rawTheme = pick(payload, "theme", "Theme");
|
|
43
53
|
const theme = rawTheme ? {
|
|
@@ -63,6 +73,7 @@ export function mapToDesiredCalendarResponse(payload, openingHours = [], members
|
|
|
63
73
|
})),
|
|
64
74
|
__typename: "ReminderChannelStatus"
|
|
65
75
|
})) : [];
|
|
76
|
+
const uuid = pick(payload, "uuid", "Uuid", "calendarId", "CalendarId");
|
|
66
77
|
return {
|
|
67
78
|
id: n(pick(payload, "id", "Id")),
|
|
68
79
|
durationUnit: n(pick(payload, "durationUnit", "DurationUnit")),
|
|
@@ -75,7 +86,8 @@ export function mapToDesiredCalendarResponse(payload, openingHours = [], members
|
|
|
75
86
|
bufferTime: n(pick(payload, "bufferTime", "BufferTime")),
|
|
76
87
|
bufferTimeUnit: n(pick(payload, "bufferTimeUnit", "BufferTimeUnit")),
|
|
77
88
|
calendarLink: pick(payload, "calendarLink", "CalendarLink"),
|
|
78
|
-
uuid:
|
|
89
|
+
uuid: uuid,
|
|
90
|
+
calendarId: uuid, // Explicit alias requested by user
|
|
79
91
|
location: pick(payload, "location", "Location") ?? "",
|
|
80
92
|
bookingPageTitle: pick(payload, "bookingPageTitle", "BookingPageTitle") ?? null,
|
|
81
93
|
reminderChannelStatuses,
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,7 @@ export type { EnsureBlazeoHttpOptions } from "./config/ensureBlazeoHttpReady.js"
|
|
|
5
5
|
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
|
-
export {
|
|
9
|
-
export { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
|
|
8
|
+
export { fetchCalendarBundle, normalizeOpeningHours } from "./calendar/fetchCalendarDetails.js";
|
|
10
9
|
export { buildUnifiedCalendarView, type UnifiedCalendarMember, type UnifiedCalendarView, type UnifiedOpeningHourRow, type UnifiedParticipantWithHours, } from "./calendar/buildUnifiedCalendarView.js";
|
|
11
10
|
export { fetchCalendarWithOpeningHours, unwrapCalendarGetData, pickOpeningHoursArrayFromCalendarPayload, normalizeParticipantOpeningHoursResponse } from "./calendar/fetchCalendarWithOpeningHours.js";
|
|
12
11
|
export { getOpeningHours } from "./calendar/getOpeningHours.js";
|
|
@@ -19,8 +18,48 @@ export { CalendarCreation, createCalendarWithRelationsAsync, updateCalendarWithR
|
|
|
19
18
|
export { addParticipantToCalendar, removeParticipantFromCalendar, saveCalendarOpeningHour, saveCalendarOpeningHoursBatch } from "./calendar/blazeoCalendarRelationMethods.js";
|
|
20
19
|
export { createAppointmentEventAsync, rescheduleAppointmentEventAsync, cancelAppointmentEventAsync } from "./events/appointmentEventFacade.js";
|
|
21
20
|
export { mapAppointmentToEventSnapshot } from "./events/mapAppointmentToEventSnapshot.js";
|
|
22
|
-
import {
|
|
23
|
-
|
|
21
|
+
import { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
|
|
22
|
+
import { fetchCalendarDetails } from "./calendar/fetchCalendarDetails.js";
|
|
23
|
+
export { getCalendarsByCompany, fetchCalendarDetails };
|
|
24
|
+
import { EventModel as CoreEventModel, ParticipantModel as CoreParticipantModel, CalendarParticipantModel as CoreCalendarParticipantModel, configure, getConfig } from "@blazeo.com/calendar-client";
|
|
25
|
+
export declare const CalendarModel: {
|
|
26
|
+
getCalendarsByCompany: typeof getCalendarsByCompany;
|
|
27
|
+
fetchCalendarDetails: typeof fetchCalendarDetails;
|
|
28
|
+
get(calendarId: string): Promise<unknown>;
|
|
29
|
+
getRaw(calendarId: string): Promise<{
|
|
30
|
+
status: string;
|
|
31
|
+
data?: unknown;
|
|
32
|
+
message?: string;
|
|
33
|
+
}>;
|
|
34
|
+
getByCompany(companyKey: string, opts?: {
|
|
35
|
+
skip?: number;
|
|
36
|
+
take?: number;
|
|
37
|
+
sortBy?: string;
|
|
38
|
+
sortOrder?: "ASC" | "DESC" | "asc" | "desc" | string;
|
|
39
|
+
sort?: string;
|
|
40
|
+
sort_column?: string;
|
|
41
|
+
sort_dir?: "asc" | "desc" | string;
|
|
42
|
+
page?: number;
|
|
43
|
+
page_size?: number;
|
|
44
|
+
}): Promise<{
|
|
45
|
+
calendars: unknown[];
|
|
46
|
+
totalCount: number;
|
|
47
|
+
} | null>;
|
|
48
|
+
getTimeZones(): Promise<unknown>;
|
|
49
|
+
getTimeZone(timezoneId: string): Promise<unknown>;
|
|
50
|
+
getParticipants(calendarId: string): Promise<unknown>;
|
|
51
|
+
getAllParticipantOpeningHours(calendarId: string): Promise<unknown[] | null>;
|
|
52
|
+
getCalendarParticipant(calendarId: string): Promise<unknown>;
|
|
53
|
+
getParticipantsInfo(calendarId: string): Promise<unknown>;
|
|
54
|
+
getMonth(calendarId: string, year: number, month: number): Promise<unknown>;
|
|
55
|
+
getEvents(calendarId: string): Promise<unknown>;
|
|
56
|
+
createWithParticipants(name: string, companyKey: string, participantIds: string[], description: string, calendarId?: string): Promise<unknown>;
|
|
57
|
+
editWithParticipants(calendarId: string, name: string, participantIds: string[], description: string): Promise<unknown>;
|
|
58
|
+
create(snapshot: object, options?: {
|
|
59
|
+
env?: object;
|
|
60
|
+
}): unknown;
|
|
61
|
+
};
|
|
62
|
+
export { CoreEventModel as CoreEventModel, CoreParticipantModel as CoreParticipantModel, CoreCalendarParticipantModel as CalendarParticipantModel, configure, getConfig };
|
|
24
63
|
export declare const packageName = "@blazeo.com/appointment-client";
|
|
25
64
|
export declare class CalendarClient {
|
|
26
65
|
name: string;
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,7 @@ export { ensureBlazeoHttpReady } from "./config/ensureBlazeoHttpReady.js";
|
|
|
5
5
|
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
|
-
export {
|
|
9
|
-
export { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
|
|
8
|
+
export { fetchCalendarBundle, normalizeOpeningHours } from "./calendar/fetchCalendarDetails.js";
|
|
10
9
|
export { buildUnifiedCalendarView, } from "./calendar/buildUnifiedCalendarView.js";
|
|
11
10
|
export { fetchCalendarWithOpeningHours, unwrapCalendarGetData, pickOpeningHoursArrayFromCalendarPayload, normalizeParticipantOpeningHoursResponse } from "./calendar/fetchCalendarWithOpeningHours.js";
|
|
12
11
|
export { getOpeningHours } from "./calendar/getOpeningHours.js";
|
|
@@ -20,16 +19,17 @@ export { CalendarCreation, createCalendarWithRelationsAsync, updateCalendarWithR
|
|
|
20
19
|
export { addParticipantToCalendar, removeParticipantFromCalendar, saveCalendarOpeningHour, saveCalendarOpeningHoursBatch } from "./calendar/blazeoCalendarRelationMethods.js";
|
|
21
20
|
export { createAppointmentEventAsync, rescheduleAppointmentEventAsync, cancelAppointmentEventAsync } from "./events/appointmentEventFacade.js";
|
|
22
21
|
export { mapAppointmentToEventSnapshot } from "./events/mapAppointmentToEventSnapshot.js";
|
|
23
|
-
import { fetchCalendarDetails, fetchCalendarBundle } from "./calendar/fetchCalendarDetails.js";
|
|
24
|
-
import { fetchCalendarWithOpeningHours } from "./calendar/fetchCalendarWithOpeningHours.js";
|
|
25
22
|
import { getCalendarsByCompany } from "./calendar/getCalendarsByCompany.js";
|
|
23
|
+
import { fetchCalendarDetails } from "./calendar/fetchCalendarDetails.js";
|
|
24
|
+
export { getCalendarsByCompany, fetchCalendarDetails };
|
|
26
25
|
import { CalendarModel as CoreCalendarModel, EventModel as CoreEventModel, ParticipantModel as CoreParticipantModel, CalendarParticipantModel as CoreCalendarParticipantModel, configure, getConfig } from "@blazeo.com/calendar-client";
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
CoreCalendarModel
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
// Enriched CalendarModel
|
|
27
|
+
export const CalendarModel = {
|
|
28
|
+
...CoreCalendarModel,
|
|
29
|
+
getCalendarsByCompany,
|
|
30
|
+
fetchCalendarDetails
|
|
31
|
+
};
|
|
32
|
+
export { CoreEventModel as CoreEventModel, CoreParticipantModel as CoreParticipantModel, CoreCalendarParticipantModel as CalendarParticipantModel, configure, getConfig };
|
|
33
33
|
export const packageName = "@blazeo.com/appointment-client";
|
|
34
34
|
export class CalendarClient {
|
|
35
35
|
name = "CalendarClient";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type BlazeoLeadConnection = {
|
|
2
|
+
baseUrl?: string;
|
|
3
|
+
consumer?: string;
|
|
4
|
+
};
|
|
5
|
+
/** Paging / sort options forwarded to `LeadModel.getByCompany` → `GET /lead/company/get`. */
|
|
6
|
+
export type LeadsByCompanyListOpts = {
|
|
7
|
+
skip?: number;
|
|
8
|
+
take?: number;
|
|
9
|
+
sortBy?: string;
|
|
10
|
+
sortOrder?: "ASC" | "DESC" | "asc" | "desc" | string;
|
|
11
|
+
sort?: string;
|
|
12
|
+
sort_column?: string;
|
|
13
|
+
sort_dir?: "asc" | "desc" | string;
|
|
14
|
+
page?: number;
|
|
15
|
+
page_size?: number;
|
|
16
|
+
searchColumn?: string;
|
|
17
|
+
search_column?: string;
|
|
18
|
+
searchText?: string;
|
|
19
|
+
search_text?: string;
|
|
20
|
+
search?: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Lead by id: `LeadModel.getRaw` / `get` → `GET /lead/get?lead_id=…`.
|
|
24
|
+
* Returns the mapped MST snapshot when successful, plus the raw API envelope from `getRaw`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fetchLeadDetails(leadId: string, connection?: BlazeoLeadConnection): Promise<{
|
|
27
|
+
ok: true;
|
|
28
|
+
lead: Record<string, unknown> | null;
|
|
29
|
+
rawGet: unknown;
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
reason: "missing_base_url";
|
|
33
|
+
detail: string;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Single lead by email + company: `GET /lead/getbyemail`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function fetchLeadByEmail(email: string, companyKey: string, connection?: BlazeoLeadConnection): Promise<{
|
|
39
|
+
ok: true;
|
|
40
|
+
lead: Record<string, unknown> | null;
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
reason: "missing_base_url";
|
|
44
|
+
detail: string;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Paged list: `LeadModel.getByCompany` → `GET /lead/company/get`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function fetchLeadsByCompany(companyKey: string, listOpts?: LeadsByCompanyListOpts, connection?: BlazeoLeadConnection): Promise<{
|
|
50
|
+
ok: true;
|
|
51
|
+
leads: Record<string, unknown>[];
|
|
52
|
+
} | {
|
|
53
|
+
ok: false;
|
|
54
|
+
reason: "missing_base_url";
|
|
55
|
+
detail: string;
|
|
56
|
+
}>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { LeadModel } from "@blazeo.com/calendar-client";
|
|
2
|
+
import { getSnapshot, isStateTreeNode } from "mobx-state-tree";
|
|
3
|
+
import { ensureBlazeoHttpReady } from "../config/ensureBlazeoHttpReady.js";
|
|
4
|
+
function leadToPlain(lead) {
|
|
5
|
+
if (lead == null)
|
|
6
|
+
return null;
|
|
7
|
+
if (isStateTreeNode(lead)) {
|
|
8
|
+
return getSnapshot(lead);
|
|
9
|
+
}
|
|
10
|
+
if (typeof lead === "object")
|
|
11
|
+
return lead;
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
function leadsToPlain(list) {
|
|
15
|
+
if (!Array.isArray(list))
|
|
16
|
+
return [];
|
|
17
|
+
return list.map((x) => leadToPlain(x)).filter((x) => x != null);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Lead by id: `LeadModel.getRaw` / `get` → `GET /lead/get?lead_id=…`.
|
|
21
|
+
* Returns the mapped MST snapshot when successful, plus the raw API envelope from `getRaw`.
|
|
22
|
+
*/
|
|
23
|
+
export async function fetchLeadDetails(leadId, connection = {}) {
|
|
24
|
+
const ready = ensureBlazeoHttpReady(connection);
|
|
25
|
+
if (!ready.ok) {
|
|
26
|
+
return { ok: false, reason: "missing_base_url", detail: ready.error };
|
|
27
|
+
}
|
|
28
|
+
const id = String(leadId ?? "").trim();
|
|
29
|
+
if (!id) {
|
|
30
|
+
return {
|
|
31
|
+
ok: true,
|
|
32
|
+
lead: null,
|
|
33
|
+
rawGet: { status: "failure", message: "leadId is empty" },
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const rawGet = await LeadModel.getRaw(id);
|
|
37
|
+
const model = await LeadModel.get(id);
|
|
38
|
+
return { ok: true, lead: leadToPlain(model), rawGet };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Single lead by email + company: `GET /lead/getbyemail`.
|
|
42
|
+
*/
|
|
43
|
+
export async function fetchLeadByEmail(email, companyKey, connection = {}) {
|
|
44
|
+
const ready = ensureBlazeoHttpReady(connection);
|
|
45
|
+
if (!ready.ok) {
|
|
46
|
+
return { ok: false, reason: "missing_base_url", detail: ready.error };
|
|
47
|
+
}
|
|
48
|
+
const model = await LeadModel.getByEmail(String(email).trim(), String(companyKey).trim());
|
|
49
|
+
return { ok: true, lead: leadToPlain(model) };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Paged list: `LeadModel.getByCompany` → `GET /lead/company/get`.
|
|
53
|
+
*/
|
|
54
|
+
export async function fetchLeadsByCompany(companyKey, listOpts = {}, connection = {}) {
|
|
55
|
+
const ready = ensureBlazeoHttpReady(connection);
|
|
56
|
+
if (!ready.ok) {
|
|
57
|
+
return { ok: false, reason: "missing_base_url", detail: ready.error };
|
|
58
|
+
}
|
|
59
|
+
const models = await LeadModel.getByCompany(String(companyKey).trim(), listOpts);
|
|
60
|
+
return { ok: true, leads: leadsToPlain(models ?? []) };
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useMemo, useState } from "react";
|
|
2
2
|
import {
|
|
3
|
+
CalendarCreation,
|
|
3
4
|
CalendarModel,
|
|
4
5
|
deleteCalendarAsync,
|
|
5
6
|
ensureBlazeoHttpReady,
|
|
6
|
-
fetchCalendarDetails,
|
|
7
7
|
updateCalendarAsync,
|
|
8
8
|
} from "appointment-client";
|
|
9
9
|
import { getSnapshot, isStateTreeNode } from "mobx-state-tree";
|
|
@@ -229,40 +229,24 @@ export function FetchCalendarTab() {
|
|
|
229
229
|
|
|
230
230
|
setBusy(true);
|
|
231
231
|
try {
|
|
232
|
-
const
|
|
232
|
+
const result = await CalendarModel.fetchCalendarDetails(id, {
|
|
233
233
|
...connectionOpts,
|
|
234
234
|
baseUrl: effective.baseUrl,
|
|
235
235
|
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
236
236
|
});
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
setNote("CalendarModel.get returned null. Showing CalendarModel.getRaw only.");
|
|
251
|
-
setOutput(toDisplayJson(raw));
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const snap = details._cal ? getSnapshot(details._cal) : null;
|
|
256
|
-
if (snap) {
|
|
257
|
-
setLastFetchUpdatePayload(JSON.stringify(calendarSnapshotToUpdatePayload(snap), null, 2));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// If it's the new flat response, just use it directly for output.
|
|
261
|
-
setOutput(toDisplayJson(details));
|
|
262
|
-
|
|
263
|
-
// We can still try to extract meta for the UI if needed
|
|
264
|
-
if (meta) {
|
|
265
|
-
setNote(`Source: ${meta.calendarViewUsedAllParticipantOpeningHours ? "AllParticipantOpeningHours" : "Embedded/ParticipantApi"}`);
|
|
238
|
+
if (result) {
|
|
239
|
+
setOutput(toDisplayJson(result));
|
|
240
|
+
setNote(`CalendarModel.fetchCalendarDetails → Enriched Unified View (ID: ${id})`);
|
|
241
|
+
setUpdateJson(toDisplayJson(result));
|
|
242
|
+
|
|
243
|
+
const snap = result._cal ? getSnapshot(result._cal) : null;
|
|
244
|
+
if (snap) {
|
|
245
|
+
setLastFetchUpdatePayload(JSON.stringify(calendarSnapshotToUpdatePayload(snap), null, 2));
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
setNote(`Calendar not found or error fetching details for ID: ${id}`);
|
|
249
|
+
setOutput("{}");
|
|
266
250
|
}
|
|
267
251
|
} catch (err) {
|
|
268
252
|
setError(explainFetchFailure(err, effective.baseUrl));
|
|
@@ -360,17 +344,19 @@ export function FetchCalendarTab() {
|
|
|
360
344
|
}
|
|
361
345
|
setBusy(true);
|
|
362
346
|
try {
|
|
363
|
-
const result = await
|
|
347
|
+
const result = await CalendarCreation.updateWithRelationsAsync(payload, {
|
|
364
348
|
...connectionOpts,
|
|
365
349
|
baseUrl: effective.baseUrl,
|
|
366
350
|
...(effective.consumer ? { consumer: effective.consumer } : {}),
|
|
367
351
|
});
|
|
368
352
|
if (result.ok) {
|
|
369
|
-
setMutateNote("
|
|
353
|
+
setMutateNote("updateWithRelationsAsync → POST /Calendar/Event/Update + Batch Opening Hours");
|
|
370
354
|
setMutateOutput(
|
|
371
355
|
JSON.stringify(
|
|
372
356
|
{
|
|
373
|
-
snapshot: getSnapshot(result.calendar),
|
|
357
|
+
snapshot: result.calendar ? getSnapshot(result.calendar) : null,
|
|
358
|
+
membersAdded: result.membersAdded,
|
|
359
|
+
openingHoursSaved: result.openingHoursSaved,
|
|
374
360
|
apiResponse: result.apiResponse ?? null,
|
|
375
361
|
},
|
|
376
362
|
null,
|