@blackcode_sa/metaestetics-api 1.5.32 → 1.5.33
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/dist/admin/index.d.mts +337 -1
- package/dist/admin/index.d.ts +337 -1
- package/dist/admin/index.js +597 -14
- package/dist/admin/index.mjs +595 -14
- package/dist/backoffice/index.d.mts +2 -0
- package/dist/backoffice/index.d.ts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/package.json +1 -1
- package/src/admin/booking/booking.admin.ts +234 -0
- package/src/admin/booking/booking.calculator.ts +686 -0
- package/src/admin/booking/booking.types.ts +56 -0
- package/src/admin/booking/index.ts +3 -0
- package/src/admin/index.ts +3 -0
- package/src/services/appointment/appointment.service.ts +603 -0
- package/src/services/appointment/index.ts +2 -0
- package/src/services/appointment/utils/appointment.utils.ts +590 -0
- package/src/types/appointment/index.ts +161 -0
- package/src/types/procedure/index.ts +2 -0
- package/src/validations/appointment.schema.ts +125 -0
package/dist/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import * as admin from "firebase-admin";
|
|
2
|
+
import {
|
|
3
|
+
BookingAvailabilityCalculator,
|
|
4
|
+
BookingAvailabilityRequest,
|
|
5
|
+
BookingAvailabilityResponse,
|
|
6
|
+
AvailableSlot,
|
|
7
|
+
} from "./";
|
|
8
|
+
import { CalendarEventStatus, CalendarEventType } from "../../types/calendar";
|
|
9
|
+
import { Clinic } from "../../types/clinic";
|
|
10
|
+
import { Practitioner } from "../../types/practitioner";
|
|
11
|
+
import { Procedure } from "../../types/procedure";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Admin service for handling booking-related operations.
|
|
15
|
+
* This is the cloud-based implementation that will be used in Cloud Functions.
|
|
16
|
+
*/
|
|
17
|
+
export class BookingAdmin {
|
|
18
|
+
private db: admin.firestore.Firestore;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new BookingAdmin instance
|
|
22
|
+
* @param firestore - Firestore instance provided by the caller
|
|
23
|
+
*/
|
|
24
|
+
constructor(firestore?: admin.firestore.Firestore) {
|
|
25
|
+
this.db = firestore || admin.firestore();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Gets available booking time slots for a specific clinic, practitioner, and procedure
|
|
30
|
+
*
|
|
31
|
+
* @param clinicId - ID of the clinic
|
|
32
|
+
* @param practitionerId - ID of the practitioner
|
|
33
|
+
* @param procedureId - ID of the procedure
|
|
34
|
+
* @param timeframe - Time range to check for availability
|
|
35
|
+
* @returns Promise resolving to an array of available booking slots
|
|
36
|
+
*/
|
|
37
|
+
async getAvailableBookingSlots(
|
|
38
|
+
clinicId: string,
|
|
39
|
+
practitionerId: string,
|
|
40
|
+
procedureId: string,
|
|
41
|
+
timeframe: {
|
|
42
|
+
start: Date | admin.firestore.Timestamp;
|
|
43
|
+
end: Date | admin.firestore.Timestamp;
|
|
44
|
+
}
|
|
45
|
+
): Promise<{ availableSlots: { start: admin.firestore.Timestamp }[] }> {
|
|
46
|
+
try {
|
|
47
|
+
console.log(
|
|
48
|
+
`[BookingAdmin] Getting available slots for clinic ${clinicId}, practitioner ${practitionerId}, procedure ${procedureId}`
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Convert timeframe dates to Firestore Timestamps if needed
|
|
52
|
+
const start =
|
|
53
|
+
timeframe.start instanceof Date
|
|
54
|
+
? admin.firestore.Timestamp.fromDate(timeframe.start)
|
|
55
|
+
: timeframe.start;
|
|
56
|
+
|
|
57
|
+
const end =
|
|
58
|
+
timeframe.end instanceof Date
|
|
59
|
+
? admin.firestore.Timestamp.fromDate(timeframe.end)
|
|
60
|
+
: timeframe.end;
|
|
61
|
+
|
|
62
|
+
// 1. Fetch clinic data
|
|
63
|
+
const clinicDoc = await this.db.collection("clinics").doc(clinicId).get();
|
|
64
|
+
if (!clinicDoc.exists) {
|
|
65
|
+
throw new Error(`Clinic ${clinicId} not found`);
|
|
66
|
+
}
|
|
67
|
+
const clinic = clinicDoc.data() as unknown as Clinic;
|
|
68
|
+
|
|
69
|
+
// 2. Fetch practitioner data
|
|
70
|
+
const practitionerDoc = await this.db
|
|
71
|
+
.collection("practitioners")
|
|
72
|
+
.doc(practitionerId)
|
|
73
|
+
.get();
|
|
74
|
+
if (!practitionerDoc.exists) {
|
|
75
|
+
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
76
|
+
}
|
|
77
|
+
const practitioner = practitionerDoc.data() as unknown as Practitioner;
|
|
78
|
+
|
|
79
|
+
// 3. Fetch procedure data
|
|
80
|
+
const procedureDoc = await this.db
|
|
81
|
+
.collection("procedures")
|
|
82
|
+
.doc(procedureId)
|
|
83
|
+
.get();
|
|
84
|
+
if (!procedureDoc.exists) {
|
|
85
|
+
throw new Error(`Procedure ${procedureId} not found`);
|
|
86
|
+
}
|
|
87
|
+
const procedure = procedureDoc.data() as unknown as Procedure;
|
|
88
|
+
|
|
89
|
+
// 4. Fetch clinic calendar events
|
|
90
|
+
const clinicCalendarEvents = await this.getClinicCalendarEvents(
|
|
91
|
+
clinicId,
|
|
92
|
+
start,
|
|
93
|
+
end
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// 5. Fetch practitioner calendar events
|
|
97
|
+
const practitionerCalendarEvents =
|
|
98
|
+
await this.getPractitionerCalendarEvents(practitionerId, start, end);
|
|
99
|
+
|
|
100
|
+
// Since we're working with two different Timestamp implementations (admin vs client),
|
|
101
|
+
// we need to convert our timestamps to the client-side format expected by the calculator
|
|
102
|
+
// Create client Timestamp objects from admin Timestamp objects
|
|
103
|
+
const convertedTimeframe = {
|
|
104
|
+
start: this.adminTimestampToClientTimestamp(start),
|
|
105
|
+
end: this.adminTimestampToClientTimestamp(end),
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Create the request object for the calculator
|
|
109
|
+
const request: BookingAvailabilityRequest = {
|
|
110
|
+
clinic,
|
|
111
|
+
practitioner,
|
|
112
|
+
procedure,
|
|
113
|
+
timeframe: convertedTimeframe,
|
|
114
|
+
clinicCalendarEvents:
|
|
115
|
+
this.convertEventsTimestamps(clinicCalendarEvents),
|
|
116
|
+
practitionerCalendarEvents: this.convertEventsTimestamps(
|
|
117
|
+
practitionerCalendarEvents
|
|
118
|
+
),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Use the calculator to compute available slots
|
|
122
|
+
const result = BookingAvailabilityCalculator.calculateSlots(request);
|
|
123
|
+
|
|
124
|
+
// Convert the client Timestamps to admin Timestamps before returning
|
|
125
|
+
return {
|
|
126
|
+
availableSlots: result.availableSlots.map((slot) => ({
|
|
127
|
+
start: admin.firestore.Timestamp.fromMillis(slot.start.toMillis()),
|
|
128
|
+
})),
|
|
129
|
+
};
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error("[BookingAdmin] Error getting available slots:", error);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Converts an admin Firestore Timestamp to a client Firestore Timestamp
|
|
138
|
+
*/
|
|
139
|
+
private adminTimestampToClientTimestamp(
|
|
140
|
+
timestamp: admin.firestore.Timestamp
|
|
141
|
+
): any {
|
|
142
|
+
// Create a client Timestamp with the same seconds and nanoseconds
|
|
143
|
+
return {
|
|
144
|
+
seconds: timestamp.seconds,
|
|
145
|
+
nanoseconds: timestamp.nanoseconds,
|
|
146
|
+
toDate: () => timestamp.toDate(),
|
|
147
|
+
toMillis: () => timestamp.toMillis(),
|
|
148
|
+
valueOf: () => timestamp.valueOf(),
|
|
149
|
+
// Add any other required methods/properties
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Converts timestamps in calendar events from admin Firestore Timestamps to client Firestore Timestamps
|
|
155
|
+
*/
|
|
156
|
+
private convertEventsTimestamps(events: any[]): any[] {
|
|
157
|
+
return events.map((event) => ({
|
|
158
|
+
...event,
|
|
159
|
+
eventTime: {
|
|
160
|
+
start: this.adminTimestampToClientTimestamp(event.eventTime.start),
|
|
161
|
+
end: this.adminTimestampToClientTimestamp(event.eventTime.end),
|
|
162
|
+
},
|
|
163
|
+
// Convert any other timestamps in the event if needed
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Fetches clinic calendar events for a specific time range
|
|
169
|
+
*
|
|
170
|
+
* @param clinicId - ID of the clinic
|
|
171
|
+
* @param start - Start time of the range
|
|
172
|
+
* @param end - End time of the range
|
|
173
|
+
* @returns Promise resolving to an array of calendar events
|
|
174
|
+
*/
|
|
175
|
+
private async getClinicCalendarEvents(
|
|
176
|
+
clinicId: string,
|
|
177
|
+
start: admin.firestore.Timestamp,
|
|
178
|
+
end: admin.firestore.Timestamp
|
|
179
|
+
): Promise<any[]> {
|
|
180
|
+
try {
|
|
181
|
+
const eventsRef = this.db
|
|
182
|
+
.collection(`clinics/${clinicId}/calendar`)
|
|
183
|
+
.where("eventTime.start", ">=", start)
|
|
184
|
+
.where("eventTime.start", "<=", end);
|
|
185
|
+
|
|
186
|
+
const snapshot = await eventsRef.get();
|
|
187
|
+
|
|
188
|
+
return snapshot.docs.map((doc) => ({
|
|
189
|
+
...doc.data(),
|
|
190
|
+
id: doc.id,
|
|
191
|
+
}));
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error(
|
|
194
|
+
`[BookingAdmin] Error fetching clinic calendar events:`,
|
|
195
|
+
error
|
|
196
|
+
);
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Fetches practitioner calendar events for a specific time range
|
|
203
|
+
*
|
|
204
|
+
* @param practitionerId - ID of the practitioner
|
|
205
|
+
* @param start - Start time of the range
|
|
206
|
+
* @param end - End time of the range
|
|
207
|
+
* @returns Promise resolving to an array of calendar events
|
|
208
|
+
*/
|
|
209
|
+
private async getPractitionerCalendarEvents(
|
|
210
|
+
practitionerId: string,
|
|
211
|
+
start: admin.firestore.Timestamp,
|
|
212
|
+
end: admin.firestore.Timestamp
|
|
213
|
+
): Promise<any[]> {
|
|
214
|
+
try {
|
|
215
|
+
const eventsRef = this.db
|
|
216
|
+
.collection(`practitioners/${practitionerId}/calendar`)
|
|
217
|
+
.where("eventTime.start", ">=", start)
|
|
218
|
+
.where("eventTime.start", "<=", end);
|
|
219
|
+
|
|
220
|
+
const snapshot = await eventsRef.get();
|
|
221
|
+
|
|
222
|
+
return snapshot.docs.map((doc) => ({
|
|
223
|
+
...doc.data(),
|
|
224
|
+
id: doc.id,
|
|
225
|
+
}));
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error(
|
|
228
|
+
`[BookingAdmin] Error fetching practitioner calendar events:`,
|
|
229
|
+
error
|
|
230
|
+
);
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|