@blackcode_sa/metaestetics-api 1.6.4 → 1.6.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.
- package/dist/admin/index.d.mts +211 -0
- package/dist/admin/index.d.ts +211 -0
- package/dist/admin/index.js +1234 -994
- package/dist/admin/index.mjs +1236 -996
- package/dist/backoffice/index.d.mts +2 -0
- package/dist/backoffice/index.d.ts +2 -0
- package/dist/index.d.mts +42 -75
- package/dist/index.d.ts +42 -75
- package/dist/index.js +74 -305
- package/dist/index.mjs +75 -306
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +321 -0
- package/src/admin/booking/booking.admin.ts +376 -3
- package/src/backoffice/types/product.types.ts +2 -0
- package/src/services/appointment/appointment.service.ts +89 -182
- package/src/services/procedure/procedure.service.ts +1 -0
- package/src/types/appointment/index.ts +2 -1
- package/src/types/procedure/index.ts +7 -0
- package/src/validations/appointment.schema.ts +1 -3
- package/src/validations/procedure.schema.ts +3 -0
|
@@ -93,100 +93,6 @@ export class AppointmentService extends BaseService {
|
|
|
93
93
|
this.functions = getFunctions(app, "europe-west6"); // Initialize Firebase Functions with the correct region
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/**
|
|
97
|
-
* Test method using the callable function version of getAvailableBookingSlots
|
|
98
|
-
* For development and testing purposes only - not for production use
|
|
99
|
-
*
|
|
100
|
-
* @param clinicId ID of the clinic
|
|
101
|
-
* @param practitionerId ID of the practitioner
|
|
102
|
-
* @param procedureId ID of the procedure
|
|
103
|
-
* @param startDate Start date of the time range to check
|
|
104
|
-
* @param endDate End date of the time range to check
|
|
105
|
-
* @returns Test result from the callable function
|
|
106
|
-
*/
|
|
107
|
-
async testGetAvailableBookingSlots(
|
|
108
|
-
clinicId: string,
|
|
109
|
-
practitionerId: string,
|
|
110
|
-
procedureId: string,
|
|
111
|
-
startDate: Date,
|
|
112
|
-
endDate: Date
|
|
113
|
-
): Promise<any> {
|
|
114
|
-
try {
|
|
115
|
-
console.log(
|
|
116
|
-
`[APPOINTMENT_SERVICE] Testing callable function for clinic: ${clinicId}, practitioner: ${practitionerId}, procedure: ${procedureId}`
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
// Get the callable function
|
|
120
|
-
const getAvailableBookingSlotsCallable = httpsCallable(
|
|
121
|
-
this.functions,
|
|
122
|
-
"getAvailableBookingSlots"
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Call the function with the required parameters
|
|
126
|
-
const result = await getAvailableBookingSlotsCallable({
|
|
127
|
-
clinicId,
|
|
128
|
-
practitionerId,
|
|
129
|
-
procedureId,
|
|
130
|
-
timeframe: {
|
|
131
|
-
start: startDate.getTime(),
|
|
132
|
-
end: endDate.getTime(),
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
console.log(
|
|
137
|
-
"[APPOINTMENT_SERVICE] Callable function test result:",
|
|
138
|
-
result.data
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
return result.data;
|
|
142
|
-
} catch (error) {
|
|
143
|
-
console.error(
|
|
144
|
-
"[APPOINTMENT_SERVICE] Error testing callable function:",
|
|
145
|
-
error
|
|
146
|
-
);
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Gets available booking slots for a specific clinic, practitioner, and procedure.
|
|
153
|
-
*
|
|
154
|
-
* @param clinicId ID of the clinic
|
|
155
|
-
* @param practitionerId ID of the practitioner
|
|
156
|
-
* @param procedureId ID of the procedure
|
|
157
|
-
* @param startDate Start date of the time range to check
|
|
158
|
-
* @param endDate End date of the time range to check
|
|
159
|
-
* @returns Array of available booking slots
|
|
160
|
-
*/
|
|
161
|
-
async getAvailableBookingSlots(
|
|
162
|
-
clinicId: string,
|
|
163
|
-
practitionerId: string,
|
|
164
|
-
procedureId: string,
|
|
165
|
-
startDate: Date,
|
|
166
|
-
endDate: Date
|
|
167
|
-
): Promise<AvailableSlot[]> {
|
|
168
|
-
try {
|
|
169
|
-
console.log(
|
|
170
|
-
`[APPOINTMENT_SERVICE] Getting available booking slots for clinic: ${clinicId}, practitioner: ${practitionerId}, procedure: ${procedureId}`
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
// Just call our HTTP implementation since the callable function isn't working in the browser
|
|
174
|
-
return this.getAvailableBookingSlotsHttp(
|
|
175
|
-
clinicId,
|
|
176
|
-
practitionerId,
|
|
177
|
-
procedureId,
|
|
178
|
-
startDate,
|
|
179
|
-
endDate
|
|
180
|
-
);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.error(
|
|
183
|
-
"[APPOINTMENT_SERVICE] Error getting available booking slots:",
|
|
184
|
-
error
|
|
185
|
-
);
|
|
186
|
-
throw error;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
96
|
/**
|
|
191
97
|
* Gets available booking slots for a specific clinic, practitioner, and procedure using HTTP request.
|
|
192
98
|
* This is an alternative implementation using direct HTTP request instead of callable function.
|
|
@@ -328,42 +234,108 @@ export class AppointmentService extends BaseService {
|
|
|
328
234
|
}
|
|
329
235
|
|
|
330
236
|
/**
|
|
331
|
-
* Creates
|
|
237
|
+
* Creates an appointment via the Cloud Function orchestrateAppointmentCreation
|
|
332
238
|
*
|
|
333
|
-
* @param data
|
|
239
|
+
* @param data - CreateAppointmentData object
|
|
334
240
|
* @returns The created appointment
|
|
335
241
|
*/
|
|
336
|
-
async
|
|
242
|
+
async createAppointmentHttp(
|
|
243
|
+
data: CreateAppointmentData
|
|
244
|
+
): Promise<Appointment> {
|
|
337
245
|
try {
|
|
338
|
-
console.log(
|
|
246
|
+
console.log(
|
|
247
|
+
"[APPOINTMENT_SERVICE] Creating appointment via cloud function"
|
|
248
|
+
);
|
|
339
249
|
|
|
340
|
-
//
|
|
341
|
-
const
|
|
250
|
+
// Get the authenticated user's ID token
|
|
251
|
+
const currentUser = this.auth.currentUser;
|
|
252
|
+
if (!currentUser) {
|
|
253
|
+
throw new Error("User must be authenticated to create an appointment");
|
|
254
|
+
}
|
|
255
|
+
const idToken = await currentUser.getIdToken();
|
|
342
256
|
|
|
343
|
-
//
|
|
344
|
-
const
|
|
345
|
-
this.db,
|
|
346
|
-
validatedData.clinicBranchId,
|
|
347
|
-
validatedData.practitionerId,
|
|
348
|
-
validatedData.patientId,
|
|
349
|
-
validatedData.procedureId
|
|
350
|
-
);
|
|
257
|
+
// Construct the function URL for the Express app endpoint
|
|
258
|
+
const functionUrl = `https://europe-west6-metaestetics.cloudfunctions.net/bookingApi/orchestrateAppointmentCreation`;
|
|
351
259
|
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
260
|
+
// Prepare request data for the Cloud Function
|
|
261
|
+
// Map CreateAppointmentData to OrchestrateAppointmentCreationData format
|
|
262
|
+
const requestData = {
|
|
263
|
+
patientId: data.patientId,
|
|
264
|
+
procedureId: data.procedureId,
|
|
265
|
+
appointmentStartTime: data.appointmentStartTime.toMillis
|
|
266
|
+
? data.appointmentStartTime.toMillis()
|
|
267
|
+
: new Date(data.appointmentStartTime as any).getTime(),
|
|
268
|
+
appointmentEndTime: data.appointmentEndTime.toMillis
|
|
269
|
+
? data.appointmentEndTime.toMillis()
|
|
270
|
+
: new Date(data.appointmentEndTime as any).getTime(),
|
|
271
|
+
patientNotes: data.patientNotes || null,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
console.log(
|
|
275
|
+
`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`
|
|
358
276
|
);
|
|
359
277
|
|
|
278
|
+
// Make the HTTP request with expanded CORS options for browser
|
|
279
|
+
const response = await fetch(functionUrl, {
|
|
280
|
+
method: "POST",
|
|
281
|
+
mode: "cors",
|
|
282
|
+
cache: "no-cache",
|
|
283
|
+
credentials: "omit",
|
|
284
|
+
headers: {
|
|
285
|
+
"Content-Type": "application/json",
|
|
286
|
+
Authorization: `Bearer ${idToken}`,
|
|
287
|
+
},
|
|
288
|
+
redirect: "follow",
|
|
289
|
+
referrerPolicy: "no-referrer",
|
|
290
|
+
body: JSON.stringify(requestData),
|
|
291
|
+
});
|
|
292
|
+
|
|
360
293
|
console.log(
|
|
361
|
-
`[APPOINTMENT_SERVICE]
|
|
294
|
+
`[APPOINTMENT_SERVICE] Received response ${response.status}: ${response.statusText}`
|
|
362
295
|
);
|
|
363
296
|
|
|
364
|
-
|
|
297
|
+
// Check if the request was successful
|
|
298
|
+
if (!response.ok) {
|
|
299
|
+
const errorText = await response.text();
|
|
300
|
+
console.error(
|
|
301
|
+
`[APPOINTMENT_SERVICE] Error response details: ${errorText}`
|
|
302
|
+
);
|
|
303
|
+
throw new Error(
|
|
304
|
+
`Failed to create appointment: ${response.status} ${response.statusText} - ${errorText}`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Parse the response
|
|
309
|
+
const result = await response.json();
|
|
310
|
+
|
|
311
|
+
if (!result.success) {
|
|
312
|
+
throw new Error(result.error || "Failed to create appointment");
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// If the backend returns the full appointment data
|
|
316
|
+
if (result.appointmentData) {
|
|
317
|
+
console.log(
|
|
318
|
+
`[APPOINTMENT_SERVICE] Appointment created with ID: ${result.appointmentId}`
|
|
319
|
+
);
|
|
320
|
+
return result.appointmentData;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// If only the ID is returned, fetch the complete appointment
|
|
324
|
+
const createdAppointment = await this.getAppointmentById(
|
|
325
|
+
result.appointmentId
|
|
326
|
+
);
|
|
327
|
+
if (!createdAppointment) {
|
|
328
|
+
throw new Error(
|
|
329
|
+
`Failed to retrieve created appointment with ID: ${result.appointmentId}`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return createdAppointment;
|
|
365
334
|
} catch (error) {
|
|
366
|
-
console.error(
|
|
335
|
+
console.error(
|
|
336
|
+
"[APPOINTMENT_SERVICE] Error creating appointment via cloud function:",
|
|
337
|
+
error
|
|
338
|
+
);
|
|
367
339
|
throw error;
|
|
368
340
|
}
|
|
369
341
|
}
|
|
@@ -1008,50 +980,6 @@ export class AppointmentService extends BaseService {
|
|
|
1008
980
|
return this.updateAppointment(appointmentId, updateData);
|
|
1009
981
|
}
|
|
1010
982
|
|
|
1011
|
-
/**
|
|
1012
|
-
* Marks pre-procedure requirements as completed.
|
|
1013
|
-
*
|
|
1014
|
-
* @param appointmentId ID of the appointment
|
|
1015
|
-
* @param requirementIds IDs of the requirements to mark as completed
|
|
1016
|
-
* @returns The updated appointment
|
|
1017
|
-
*/
|
|
1018
|
-
async completePreRequirements(
|
|
1019
|
-
appointmentId: string,
|
|
1020
|
-
requirementIds: string[]
|
|
1021
|
-
): Promise<Appointment> {
|
|
1022
|
-
console.log(
|
|
1023
|
-
`[APPOINTMENT_SERVICE] Marking pre-requirements as completed for appointment: ${appointmentId}`
|
|
1024
|
-
);
|
|
1025
|
-
|
|
1026
|
-
const updateData: UpdateAppointmentData = {
|
|
1027
|
-
completedPreRequirements: requirementIds,
|
|
1028
|
-
};
|
|
1029
|
-
|
|
1030
|
-
return this.updateAppointment(appointmentId, updateData);
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
/**
|
|
1034
|
-
* Marks post-procedure requirements as completed.
|
|
1035
|
-
*
|
|
1036
|
-
* @param appointmentId ID of the appointment
|
|
1037
|
-
* @param requirementIds IDs of the requirements to mark as completed
|
|
1038
|
-
* @returns The updated appointment
|
|
1039
|
-
*/
|
|
1040
|
-
async completePostRequirements(
|
|
1041
|
-
appointmentId: string,
|
|
1042
|
-
requirementIds: string[]
|
|
1043
|
-
): Promise<Appointment> {
|
|
1044
|
-
console.log(
|
|
1045
|
-
`[APPOINTMENT_SERVICE] Marking post-requirements as completed for appointment: ${appointmentId}`
|
|
1046
|
-
);
|
|
1047
|
-
|
|
1048
|
-
const updateData: UpdateAppointmentData = {
|
|
1049
|
-
completedPostRequirements: requirementIds,
|
|
1050
|
-
};
|
|
1051
|
-
|
|
1052
|
-
return this.updateAppointment(appointmentId, updateData);
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
983
|
/**
|
|
1056
984
|
* Updates the internal notes of an appointment.
|
|
1057
985
|
*
|
|
@@ -1073,25 +1001,4 @@ export class AppointmentService extends BaseService {
|
|
|
1073
1001
|
|
|
1074
1002
|
return this.updateAppointment(appointmentId, updateData);
|
|
1075
1003
|
}
|
|
1076
|
-
|
|
1077
|
-
/**
|
|
1078
|
-
* Debug helper: Get the current user's ID token for testing purposes
|
|
1079
|
-
* Use this token in Postman with Authorization: Bearer TOKEN
|
|
1080
|
-
*/
|
|
1081
|
-
async getDebugToken(): Promise<string | null> {
|
|
1082
|
-
try {
|
|
1083
|
-
const currentUser = this.auth.currentUser;
|
|
1084
|
-
if (!currentUser) {
|
|
1085
|
-
console.log("[APPOINTMENT_SERVICE] No user is signed in");
|
|
1086
|
-
return null;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
const idToken = await currentUser.getIdToken();
|
|
1090
|
-
console.log("[APPOINTMENT_SERVICE] Debug token:", idToken);
|
|
1091
|
-
return idToken;
|
|
1092
|
-
} catch (error) {
|
|
1093
|
-
console.error("[APPOINTMENT_SERVICE] Error getting debug token:", error);
|
|
1094
|
-
return null;
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
1004
|
}
|
|
@@ -179,6 +179,7 @@ export class ProcedureService extends BaseService {
|
|
|
179
179
|
technology,
|
|
180
180
|
product,
|
|
181
181
|
blockingConditions: technology.blockingConditions,
|
|
182
|
+
contraindications: technology.contraindications || [],
|
|
182
183
|
treatmentBenefits: technology.benefits,
|
|
183
184
|
preRequirements: technology.requirements.pre,
|
|
184
185
|
postRequirements: technology.requirements.post,
|
|
@@ -178,6 +178,7 @@ export interface Appointment {
|
|
|
178
178
|
completedPostRequirements?: string[]; // IDs of completed post-requirements
|
|
179
179
|
|
|
180
180
|
/** NEW: Linked forms (consent, procedure-specific forms, etc.) */
|
|
181
|
+
linkedFormIds?: string[];
|
|
181
182
|
linkedForms?: LinkedFormInfo[];
|
|
182
183
|
pendingUserFormsIds?: string[]; // Determines if there are any user forms that are pending for this appointment, blocks the appointment from being checked in (only for user forms with isRequired = true)
|
|
183
184
|
|
|
@@ -210,7 +211,6 @@ export interface Appointment {
|
|
|
210
211
|
* Data needed to create a new Appointment
|
|
211
212
|
*/
|
|
212
213
|
export interface CreateAppointmentData {
|
|
213
|
-
calendarEventId: string;
|
|
214
214
|
clinicBranchId: string;
|
|
215
215
|
practitionerId: string;
|
|
216
216
|
patientId: string;
|
|
@@ -250,6 +250,7 @@ export interface UpdateAppointmentData {
|
|
|
250
250
|
practitionerId?: string; // If practitioner is changed
|
|
251
251
|
|
|
252
252
|
/** NEW: For updating linked forms - typically managed by dedicated methods */
|
|
253
|
+
linkedFormIds?: string[] | FieldValue;
|
|
253
254
|
linkedForms?: LinkedFormInfo[] | FieldValue;
|
|
254
255
|
|
|
255
256
|
/** NEW: For updating media items - typically managed by dedicated methods */
|
|
@@ -16,6 +16,7 @@ import { ClinicInfo } from "../profile";
|
|
|
16
16
|
import { DoctorInfo } from "../clinic";
|
|
17
17
|
import { PRACTITIONERS_COLLECTION } from "../practitioner";
|
|
18
18
|
import { ProcedureReviewInfo } from "../reviews";
|
|
19
|
+
import type { Contraindication } from "../../backoffice/types/static/contraindication.types";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
|
|
@@ -26,6 +27,8 @@ export interface Procedure {
|
|
|
26
27
|
id: string;
|
|
27
28
|
/** Name of the procedure */
|
|
28
29
|
name: string;
|
|
30
|
+
/** Photos of the procedure */
|
|
31
|
+
photos?: string[];
|
|
29
32
|
/** Detailed description of the procedure */
|
|
30
33
|
description: string;
|
|
31
34
|
/** Family of procedures this belongs to (aesthetics/surgery) */
|
|
@@ -50,6 +53,8 @@ export interface Procedure {
|
|
|
50
53
|
blockingConditions: BlockingCondition[];
|
|
51
54
|
/** Treatment benefits of this procedure */
|
|
52
55
|
treatmentBenefits: TreatmentBenefit[];
|
|
56
|
+
/** Contraindications of this procedure */
|
|
57
|
+
contraindications: Contraindication[];
|
|
53
58
|
/** Pre-procedure requirements */
|
|
54
59
|
preRequirements: Requirement[];
|
|
55
60
|
/** Post-procedure requirements */
|
|
@@ -93,6 +98,7 @@ export interface CreateProcedureData {
|
|
|
93
98
|
duration: number;
|
|
94
99
|
practitionerId: string;
|
|
95
100
|
clinicBranchId: string;
|
|
101
|
+
photos?: string[];
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
/**
|
|
@@ -112,6 +118,7 @@ export interface UpdateProcedureData {
|
|
|
112
118
|
technologyId?: string;
|
|
113
119
|
productId?: string;
|
|
114
120
|
clinicBranchId?: string;
|
|
121
|
+
photos?: string[];
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
/**
|
|
@@ -138,9 +138,6 @@ export const finalizedDetailsSchema = z.object({
|
|
|
138
138
|
*/
|
|
139
139
|
export const createAppointmentSchema = z
|
|
140
140
|
.object({
|
|
141
|
-
calendarEventId: z
|
|
142
|
-
.string()
|
|
143
|
-
.min(MIN_STRING_LENGTH, "Calendar event ID is required"),
|
|
144
141
|
clinicBranchId: z
|
|
145
142
|
.string()
|
|
146
143
|
.min(MIN_STRING_LENGTH, "Clinic branch ID is required"),
|
|
@@ -221,6 +218,7 @@ export const updateAppointmentSchema = z
|
|
|
221
218
|
completedPostRequirements: z
|
|
222
219
|
.union([z.array(z.string()), z.any()])
|
|
223
220
|
.optional(),
|
|
221
|
+
linkedFormIds: z.union([z.array(z.string()), z.any()]).optional(),
|
|
224
222
|
pendingUserFormsIds: z.union([z.array(z.string()), z.any()]).optional(),
|
|
225
223
|
appointmentStartTime: z
|
|
226
224
|
.any()
|
|
@@ -24,6 +24,7 @@ export const createProcedureSchema = z.object({
|
|
|
24
24
|
duration: z.number().min(1).max(480), // Max 8 hours
|
|
25
25
|
practitionerId: z.string().min(1),
|
|
26
26
|
clinicBranchId: z.string().min(1),
|
|
27
|
+
photos: z.array(z.string()).optional(),
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -43,6 +44,7 @@ export const updateProcedureSchema = z.object({
|
|
|
43
44
|
technologyId: z.string().optional(),
|
|
44
45
|
productId: z.string().optional(),
|
|
45
46
|
clinicBranchId: z.string().optional(),
|
|
47
|
+
photos: z.array(z.string()).optional(),
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
/**
|
|
@@ -55,6 +57,7 @@ export const procedureSchema = createProcedureSchema.extend({
|
|
|
55
57
|
technology: z.any(), // We'll validate the full technology object separately
|
|
56
58
|
product: z.any(), // We'll validate the full product object separately
|
|
57
59
|
blockingConditions: z.array(z.any()), // We'll validate blocking conditions separately
|
|
60
|
+
contraindications: z.array(z.any()), // We'll validate contraindications separately
|
|
58
61
|
treatmentBenefits: z.array(z.any()), // We'll validate treatment benefits separately
|
|
59
62
|
preRequirements: z.array(z.any()), // We'll validate requirements separately
|
|
60
63
|
postRequirements: z.array(z.any()), // We'll validate requirements separately
|