@blackcode_sa/metaestetics-api 1.12.7 → 1.12.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.
- package/dist/admin/index.d.mts +51 -33
- package/dist/admin/index.d.ts +51 -33
- package/dist/admin/index.js +30 -9
- package/dist/admin/index.mjs +30 -9
- package/dist/index.d.mts +79 -65
- package/dist/index.d.ts +79 -65
- package/dist/index.js +301 -616
- package/dist/index.mjs +315 -631
- package/package.json +1 -1
- package/src/admin/aggregation/forms/README.md +13 -0
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +71 -51
- package/src/services/appointment/README.md +17 -0
- package/src/services/appointment/appointment.service.ts +233 -386
- package/src/services/auth/auth.service.ts +243 -466
- package/src/services/clinic/billing-transactions.service.ts +4 -1
- package/src/services/clinic/index.ts +5 -5
- package/src/types/clinic/preferences.types.ts +115 -97
package/dist/index.js
CHANGED
|
@@ -794,49 +794,67 @@ var Language = /* @__PURE__ */ ((Language2) => {
|
|
|
794
794
|
var ClinicTag = /* @__PURE__ */ ((ClinicTag6) => {
|
|
795
795
|
ClinicTag6["PARKING"] = "parking";
|
|
796
796
|
ClinicTag6["WIFI"] = "wifi";
|
|
797
|
-
ClinicTag6["
|
|
798
|
-
ClinicTag6["
|
|
799
|
-
ClinicTag6["
|
|
800
|
-
ClinicTag6["
|
|
797
|
+
ClinicTag6["LUXURY_WAITING"] = "luxury_waiting";
|
|
798
|
+
ClinicTag6["REFRESHMENTS"] = "refreshments";
|
|
799
|
+
ClinicTag6["PRIVATE_ROOMS"] = "private_rooms";
|
|
800
|
+
ClinicTag6["RECOVERY_AREA"] = "recovery_area";
|
|
801
801
|
ClinicTag6["CARD_PAYMENT"] = "card_payment";
|
|
802
|
-
ClinicTag6["
|
|
803
|
-
ClinicTag6["
|
|
804
|
-
ClinicTag6["
|
|
805
|
-
ClinicTag6["
|
|
806
|
-
ClinicTag6["
|
|
807
|
-
ClinicTag6["
|
|
808
|
-
ClinicTag6["
|
|
809
|
-
ClinicTag6["
|
|
810
|
-
ClinicTag6["
|
|
811
|
-
ClinicTag6["
|
|
812
|
-
ClinicTag6["
|
|
813
|
-
ClinicTag6["
|
|
814
|
-
ClinicTag6["
|
|
815
|
-
ClinicTag6["
|
|
816
|
-
ClinicTag6["
|
|
817
|
-
ClinicTag6["
|
|
818
|
-
ClinicTag6["
|
|
819
|
-
ClinicTag6["
|
|
820
|
-
ClinicTag6["
|
|
821
|
-
ClinicTag6["
|
|
822
|
-
ClinicTag6["
|
|
823
|
-
ClinicTag6["
|
|
824
|
-
ClinicTag6["
|
|
825
|
-
ClinicTag6["
|
|
802
|
+
ClinicTag6["FINANCING"] = "financing";
|
|
803
|
+
ClinicTag6["FREE_CONSULTATION"] = "free_consultation";
|
|
804
|
+
ClinicTag6["VIRTUAL_CONSULTATION"] = "virtual_consultation";
|
|
805
|
+
ClinicTag6["BEFORE_AFTER_PHOTOS"] = "before_after_photos";
|
|
806
|
+
ClinicTag6["AFTERCARE_SUPPORT"] = "aftercare_support";
|
|
807
|
+
ClinicTag6["BOTOX"] = "botox";
|
|
808
|
+
ClinicTag6["DERMAL_FILLERS"] = "dermal_fillers";
|
|
809
|
+
ClinicTag6["LASER_HAIR_REMOVAL"] = "laser_hair_removal";
|
|
810
|
+
ClinicTag6["LASER_SKIN_RESURFACING"] = "laser_skin_resurfacing";
|
|
811
|
+
ClinicTag6["CHEMICAL_PEELS"] = "chemical_peels";
|
|
812
|
+
ClinicTag6["MICRONEEDLING"] = "microneedling";
|
|
813
|
+
ClinicTag6["COOLSCULPTING"] = "coolsculpting";
|
|
814
|
+
ClinicTag6["THREAD_LIFT"] = "thread_lift";
|
|
815
|
+
ClinicTag6["LIP_ENHANCEMENT"] = "lip_enhancement";
|
|
816
|
+
ClinicTag6["RHINOPLASTY"] = "rhinoplasty";
|
|
817
|
+
ClinicTag6["SKIN_TIGHTENING"] = "skin_tightening";
|
|
818
|
+
ClinicTag6["FAT_DISSOLVING"] = "fat_dissolving";
|
|
819
|
+
ClinicTag6["PRP_TREATMENT"] = "prp_treatment";
|
|
820
|
+
ClinicTag6["HYDRAFACIAL"] = "hydrafacial";
|
|
821
|
+
ClinicTag6["IPL_PHOTOFACIAL"] = "ipl_photofacial";
|
|
822
|
+
ClinicTag6["BODY_CONTOURING"] = "body_contouring";
|
|
823
|
+
ClinicTag6["FACELIFT"] = "facelift";
|
|
824
|
+
ClinicTag6["RHINOPLASTY_SURGICAL"] = "rhinoplasty_surgical";
|
|
825
|
+
ClinicTag6["BREAST_AUGMENTATION"] = "breast_augmentation";
|
|
826
|
+
ClinicTag6["BREAST_REDUCTION"] = "breast_reduction";
|
|
827
|
+
ClinicTag6["BREAST_LIFT"] = "breast_lift";
|
|
828
|
+
ClinicTag6["TUMMY_TUCK"] = "tummy_tuck";
|
|
829
|
+
ClinicTag6["LIPOSUCTION"] = "liposuction";
|
|
830
|
+
ClinicTag6["BBL"] = "bbl";
|
|
831
|
+
ClinicTag6["MOMMY_MAKEOVER"] = "mommy_makeover";
|
|
832
|
+
ClinicTag6["ARM_LIFT"] = "arm_lift";
|
|
833
|
+
ClinicTag6["THIGH_LIFT"] = "thigh_lift";
|
|
834
|
+
ClinicTag6["EYELID_SURGERY"] = "eyelid_surgery";
|
|
835
|
+
ClinicTag6["BROW_LIFT"] = "brow_lift";
|
|
836
|
+
ClinicTag6["NECK_LIFT"] = "neck_lift";
|
|
837
|
+
ClinicTag6["OTOPLASTY"] = "otoplasty";
|
|
838
|
+
ClinicTag6["LABIAPLASTY"] = "labiaplasty";
|
|
826
839
|
ClinicTag6["ONLINE_BOOKING"] = "online_booking";
|
|
827
840
|
ClinicTag6["MOBILE_APP"] = "mobile_app";
|
|
828
841
|
ClinicTag6["SMS_NOTIFICATIONS"] = "sms_notifications";
|
|
829
842
|
ClinicTag6["EMAIL_NOTIFICATIONS"] = "email_notifications";
|
|
843
|
+
ClinicTag6["VIRTUAL_TRY_ON"] = "virtual_try_on";
|
|
844
|
+
ClinicTag6["SKIN_ANALYSIS"] = "skin_analysis";
|
|
845
|
+
ClinicTag6["TREATMENT_TRACKING"] = "treatment_tracking";
|
|
846
|
+
ClinicTag6["LOYALTY_PROGRAM"] = "loyalty_program";
|
|
830
847
|
ClinicTag6["ENGLISH"] = "english";
|
|
831
|
-
ClinicTag6["SERBIAN"] = "serbian";
|
|
832
848
|
ClinicTag6["GERMAN"] = "german";
|
|
833
|
-
ClinicTag6["RUSSIAN"] = "russian";
|
|
834
|
-
ClinicTag6["CHINESE"] = "chinese";
|
|
835
|
-
ClinicTag6["SPANISH"] = "spanish";
|
|
836
849
|
ClinicTag6["FRENCH"] = "french";
|
|
850
|
+
ClinicTag6["SPANISH"] = "spanish";
|
|
851
|
+
ClinicTag6["ITALIAN"] = "italian";
|
|
852
|
+
ClinicTag6["DUTCH"] = "dutch";
|
|
853
|
+
ClinicTag6["RUSSIAN"] = "russian";
|
|
854
|
+
ClinicTag6["PORTUGUESE"] = "portuguese";
|
|
837
855
|
ClinicTag6["OPEN_24_7"] = "open_24_7";
|
|
838
856
|
ClinicTag6["WEEKEND_HOURS"] = "weekend_hours";
|
|
839
|
-
ClinicTag6["
|
|
857
|
+
ClinicTag6["EXTENDED_HOURS"] = "extended_hours";
|
|
840
858
|
ClinicTag6["HOLIDAY_HOURS"] = "holiday_hours";
|
|
841
859
|
return ClinicTag6;
|
|
842
860
|
})(ClinicTag || {});
|
|
@@ -1172,24 +1190,18 @@ var AppointmentService = class extends BaseService {
|
|
|
1172
1190
|
`[APPOINTMENT_SERVICE] Getting available booking slots via HTTP for clinic: ${clinicId}, practitioner: ${practitionerId}, procedure: ${procedureId}`
|
|
1173
1191
|
);
|
|
1174
1192
|
if (!clinicId || !practitionerId || !procedureId || !startDate || !endDate) {
|
|
1175
|
-
throw new Error(
|
|
1176
|
-
"Missing required parameters for booking slots calculation"
|
|
1177
|
-
);
|
|
1193
|
+
throw new Error("Missing required parameters for booking slots calculation");
|
|
1178
1194
|
}
|
|
1179
1195
|
if (endDate <= startDate) {
|
|
1180
1196
|
throw new Error("End date must be after start date");
|
|
1181
1197
|
}
|
|
1182
1198
|
const currentUser = this.auth.currentUser;
|
|
1183
1199
|
if (!currentUser) {
|
|
1184
|
-
throw new Error(
|
|
1185
|
-
"User must be authenticated to get available booking slots"
|
|
1186
|
-
);
|
|
1200
|
+
throw new Error("User must be authenticated to get available booking slots");
|
|
1187
1201
|
}
|
|
1188
1202
|
const functionUrl = `https://europe-west6-metaestetics.cloudfunctions.net/bookingApi/getAvailableBookingSlots`;
|
|
1189
1203
|
const idToken = await currentUser.getIdToken();
|
|
1190
|
-
console.log(
|
|
1191
|
-
`[APPOINTMENT_SERVICE] Got user token, user ID: ${currentUser.uid}`
|
|
1192
|
-
);
|
|
1204
|
+
console.log(`[APPOINTMENT_SERVICE] Got user token, user ID: ${currentUser.uid}`);
|
|
1193
1205
|
const requestData = {
|
|
1194
1206
|
clinicId,
|
|
1195
1207
|
practitionerId,
|
|
@@ -1200,9 +1212,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1200
1212
|
end: endDate.getTime()
|
|
1201
1213
|
}
|
|
1202
1214
|
};
|
|
1203
|
-
console.log(
|
|
1204
|
-
`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`
|
|
1205
|
-
);
|
|
1215
|
+
console.log(`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`);
|
|
1206
1216
|
const response = await fetch(functionUrl, {
|
|
1207
1217
|
method: "POST",
|
|
1208
1218
|
mode: "cors",
|
|
@@ -1224,9 +1234,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1224
1234
|
);
|
|
1225
1235
|
if (!response.ok) {
|
|
1226
1236
|
const errorText = await response.text();
|
|
1227
|
-
console.error(
|
|
1228
|
-
`[APPOINTMENT_SERVICE] Error response details: ${errorText}`
|
|
1229
|
-
);
|
|
1237
|
+
console.error(`[APPOINTMENT_SERVICE] Error response details: ${errorText}`);
|
|
1230
1238
|
throw new Error(
|
|
1231
1239
|
`Failed to get available booking slots: ${response.status} ${response.statusText} - ${errorText}`
|
|
1232
1240
|
);
|
|
@@ -1234,24 +1242,15 @@ var AppointmentService = class extends BaseService {
|
|
|
1234
1242
|
const result = await response.json();
|
|
1235
1243
|
console.log(`[APPOINTMENT_SERVICE] Response parsed successfully`, result);
|
|
1236
1244
|
if (!result.success) {
|
|
1237
|
-
throw new Error(
|
|
1238
|
-
result.error || "Failed to get available booking slots"
|
|
1239
|
-
);
|
|
1245
|
+
throw new Error(result.error || "Failed to get available booking slots");
|
|
1240
1246
|
}
|
|
1241
|
-
const slots = result.availableSlots.map(
|
|
1242
|
-
(slot)
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
);
|
|
1246
|
-
console.log(
|
|
1247
|
-
`[APPOINTMENT_SERVICE] Found ${slots.length} available booking slots via HTTP`
|
|
1248
|
-
);
|
|
1247
|
+
const slots = result.availableSlots.map((slot) => ({
|
|
1248
|
+
start: new Date(slot.start)
|
|
1249
|
+
}));
|
|
1250
|
+
console.log(`[APPOINTMENT_SERVICE] Found ${slots.length} available booking slots via HTTP`);
|
|
1249
1251
|
return slots;
|
|
1250
1252
|
} catch (error) {
|
|
1251
|
-
console.error(
|
|
1252
|
-
"[APPOINTMENT_SERVICE] Error getting available booking slots via HTTP:",
|
|
1253
|
-
error
|
|
1254
|
-
);
|
|
1253
|
+
console.error("[APPOINTMENT_SERVICE] Error getting available booking slots via HTTP:", error);
|
|
1255
1254
|
throw error;
|
|
1256
1255
|
}
|
|
1257
1256
|
}
|
|
@@ -1263,9 +1262,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1263
1262
|
*/
|
|
1264
1263
|
async createAppointmentHttp(data) {
|
|
1265
1264
|
try {
|
|
1266
|
-
console.log(
|
|
1267
|
-
"[APPOINTMENT_SERVICE] Creating appointment via cloud function"
|
|
1268
|
-
);
|
|
1265
|
+
console.log("[APPOINTMENT_SERVICE] Creating appointment via cloud function");
|
|
1269
1266
|
const currentUser = this.auth.currentUser;
|
|
1270
1267
|
if (!currentUser) {
|
|
1271
1268
|
throw new Error("User must be authenticated to create an appointment");
|
|
@@ -1279,9 +1276,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1279
1276
|
appointmentEndTime: data.appointmentEndTime.toMillis ? data.appointmentEndTime.toMillis() : new Date(data.appointmentEndTime).getTime(),
|
|
1280
1277
|
patientNotes: (data == null ? void 0 : data.patientNotes) || null
|
|
1281
1278
|
};
|
|
1282
|
-
console.log(
|
|
1283
|
-
`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`
|
|
1284
|
-
);
|
|
1279
|
+
console.log(`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`);
|
|
1285
1280
|
const response = await fetch(functionUrl, {
|
|
1286
1281
|
method: "POST",
|
|
1287
1282
|
mode: "cors",
|
|
@@ -1300,9 +1295,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1300
1295
|
);
|
|
1301
1296
|
if (!response.ok) {
|
|
1302
1297
|
const errorText = await response.text();
|
|
1303
|
-
console.error(
|
|
1304
|
-
`[APPOINTMENT_SERVICE] Error response details: ${errorText}`
|
|
1305
|
-
);
|
|
1298
|
+
console.error(`[APPOINTMENT_SERVICE] Error response details: ${errorText}`);
|
|
1306
1299
|
throw new Error(
|
|
1307
1300
|
`Failed to create appointment: ${response.status} ${response.statusText} - ${errorText}`
|
|
1308
1301
|
);
|
|
@@ -1312,25 +1305,16 @@ var AppointmentService = class extends BaseService {
|
|
|
1312
1305
|
throw new Error(result.error || "Failed to create appointment");
|
|
1313
1306
|
}
|
|
1314
1307
|
if (result.appointmentData) {
|
|
1315
|
-
console.log(
|
|
1316
|
-
`[APPOINTMENT_SERVICE] Appointment created with ID: ${result.appointmentId}`
|
|
1317
|
-
);
|
|
1308
|
+
console.log(`[APPOINTMENT_SERVICE] Appointment created with ID: ${result.appointmentId}`);
|
|
1318
1309
|
return result.appointmentData;
|
|
1319
1310
|
}
|
|
1320
|
-
const createdAppointment = await this.getAppointmentById(
|
|
1321
|
-
result.appointmentId
|
|
1322
|
-
);
|
|
1311
|
+
const createdAppointment = await this.getAppointmentById(result.appointmentId);
|
|
1323
1312
|
if (!createdAppointment) {
|
|
1324
|
-
throw new Error(
|
|
1325
|
-
`Failed to retrieve created appointment with ID: ${result.appointmentId}`
|
|
1326
|
-
);
|
|
1313
|
+
throw new Error(`Failed to retrieve created appointment with ID: ${result.appointmentId}`);
|
|
1327
1314
|
}
|
|
1328
1315
|
return createdAppointment;
|
|
1329
1316
|
} catch (error) {
|
|
1330
|
-
console.error(
|
|
1331
|
-
"[APPOINTMENT_SERVICE] Error creating appointment via cloud function:",
|
|
1332
|
-
error
|
|
1333
|
-
);
|
|
1317
|
+
console.error("[APPOINTMENT_SERVICE] Error creating appointment via cloud function:", error);
|
|
1334
1318
|
throw error;
|
|
1335
1319
|
}
|
|
1336
1320
|
}
|
|
@@ -1342,19 +1326,14 @@ var AppointmentService = class extends BaseService {
|
|
|
1342
1326
|
*/
|
|
1343
1327
|
async getAppointmentById(appointmentId) {
|
|
1344
1328
|
try {
|
|
1345
|
-
console.log(
|
|
1346
|
-
`[APPOINTMENT_SERVICE] Getting appointment with ID: ${appointmentId}`
|
|
1347
|
-
);
|
|
1329
|
+
console.log(`[APPOINTMENT_SERVICE] Getting appointment with ID: ${appointmentId}`);
|
|
1348
1330
|
const appointment = await getAppointmentByIdUtil(this.db, appointmentId);
|
|
1349
1331
|
console.log(
|
|
1350
1332
|
`[APPOINTMENT_SERVICE] Appointment ${appointmentId} ${appointment ? "found" : "not found"}`
|
|
1351
1333
|
);
|
|
1352
1334
|
return appointment;
|
|
1353
1335
|
} catch (error) {
|
|
1354
|
-
console.error(
|
|
1355
|
-
`[APPOINTMENT_SERVICE] Error getting appointment ${appointmentId}:`,
|
|
1356
|
-
error
|
|
1357
|
-
);
|
|
1336
|
+
console.error(`[APPOINTMENT_SERVICE] Error getting appointment ${appointmentId}:`, error);
|
|
1358
1337
|
throw error;
|
|
1359
1338
|
}
|
|
1360
1339
|
}
|
|
@@ -1367,24 +1346,13 @@ var AppointmentService = class extends BaseService {
|
|
|
1367
1346
|
*/
|
|
1368
1347
|
async updateAppointment(appointmentId, data) {
|
|
1369
1348
|
try {
|
|
1370
|
-
console.log(
|
|
1371
|
-
`[APPOINTMENT_SERVICE] Updating appointment with ID: ${appointmentId}`
|
|
1372
|
-
);
|
|
1349
|
+
console.log(`[APPOINTMENT_SERVICE] Updating appointment with ID: ${appointmentId}`);
|
|
1373
1350
|
const validatedData = await updateAppointmentSchema.parseAsync(data);
|
|
1374
|
-
const updatedAppointment = await updateAppointmentUtil(
|
|
1375
|
-
|
|
1376
|
-
appointmentId,
|
|
1377
|
-
validatedData
|
|
1378
|
-
);
|
|
1379
|
-
console.log(
|
|
1380
|
-
`[APPOINTMENT_SERVICE] Appointment ${appointmentId} updated successfully`
|
|
1381
|
-
);
|
|
1351
|
+
const updatedAppointment = await updateAppointmentUtil(this.db, appointmentId, validatedData);
|
|
1352
|
+
console.log(`[APPOINTMENT_SERVICE] Appointment ${appointmentId} updated successfully`);
|
|
1382
1353
|
return updatedAppointment;
|
|
1383
1354
|
} catch (error) {
|
|
1384
|
-
console.error(
|
|
1385
|
-
`[APPOINTMENT_SERVICE] Error updating appointment ${appointmentId}:`,
|
|
1386
|
-
error
|
|
1387
|
-
);
|
|
1355
|
+
console.error(`[APPOINTMENT_SERVICE] Error updating appointment ${appointmentId}:`, error);
|
|
1388
1356
|
throw error;
|
|
1389
1357
|
}
|
|
1390
1358
|
}
|
|
@@ -1396,21 +1364,13 @@ var AppointmentService = class extends BaseService {
|
|
|
1396
1364
|
*/
|
|
1397
1365
|
async searchAppointments(params) {
|
|
1398
1366
|
try {
|
|
1399
|
-
console.log(
|
|
1400
|
-
"[APPOINTMENT_SERVICE] Searching appointments with params:",
|
|
1401
|
-
params
|
|
1402
|
-
);
|
|
1367
|
+
console.log("[APPOINTMENT_SERVICE] Searching appointments with params:", params);
|
|
1403
1368
|
await searchAppointmentsSchema.parseAsync(params);
|
|
1404
1369
|
const result = await searchAppointmentsUtil(this.db, params);
|
|
1405
|
-
console.log(
|
|
1406
|
-
`[APPOINTMENT_SERVICE] Found ${result.appointments.length} appointments`
|
|
1407
|
-
);
|
|
1370
|
+
console.log(`[APPOINTMENT_SERVICE] Found ${result.appointments.length} appointments`);
|
|
1408
1371
|
return result;
|
|
1409
1372
|
} catch (error) {
|
|
1410
|
-
console.error(
|
|
1411
|
-
"[APPOINTMENT_SERVICE] Error searching appointments:",
|
|
1412
|
-
error
|
|
1413
|
-
);
|
|
1373
|
+
console.error("[APPOINTMENT_SERVICE] Error searching appointments:", error);
|
|
1414
1374
|
throw error;
|
|
1415
1375
|
}
|
|
1416
1376
|
}
|
|
@@ -1422,9 +1382,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1422
1382
|
* @returns Found appointments and the last document for pagination
|
|
1423
1383
|
*/
|
|
1424
1384
|
async getPatientAppointments(patientId, options) {
|
|
1425
|
-
console.log(
|
|
1426
|
-
`[APPOINTMENT_SERVICE] Getting appointments for patient: ${patientId}`
|
|
1427
|
-
);
|
|
1385
|
+
console.log(`[APPOINTMENT_SERVICE] Getting appointments for patient: ${patientId}`);
|
|
1428
1386
|
const searchParams = {
|
|
1429
1387
|
patientId,
|
|
1430
1388
|
startDate: options == null ? void 0 : options.startDate,
|
|
@@ -1443,9 +1401,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1443
1401
|
* @returns Found appointments and the last document for pagination
|
|
1444
1402
|
*/
|
|
1445
1403
|
async getPractitionerAppointments(practitionerId, options) {
|
|
1446
|
-
console.log(
|
|
1447
|
-
`[APPOINTMENT_SERVICE] Getting appointments for practitioner: ${practitionerId}`
|
|
1448
|
-
);
|
|
1404
|
+
console.log(`[APPOINTMENT_SERVICE] Getting appointments for practitioner: ${practitionerId}`);
|
|
1449
1405
|
const searchParams = {
|
|
1450
1406
|
practitionerId,
|
|
1451
1407
|
startDate: options == null ? void 0 : options.startDate,
|
|
@@ -1464,9 +1420,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1464
1420
|
* @returns Found appointments and the last document for pagination
|
|
1465
1421
|
*/
|
|
1466
1422
|
async getClinicAppointments(clinicBranchId, options) {
|
|
1467
|
-
console.log(
|
|
1468
|
-
`[APPOINTMENT_SERVICE] Getting appointments for clinic: ${clinicBranchId}`
|
|
1469
|
-
);
|
|
1423
|
+
console.log(`[APPOINTMENT_SERVICE] Getting appointments for clinic: ${clinicBranchId}`);
|
|
1470
1424
|
const searchParams = {
|
|
1471
1425
|
clinicBranchId,
|
|
1472
1426
|
practitionerId: options == null ? void 0 : options.practitionerId,
|
|
@@ -1517,68 +1471,42 @@ var AppointmentService = class extends BaseService {
|
|
|
1517
1471
|
* Confirms a PENDING appointment by an Admin/Clinic.
|
|
1518
1472
|
*/
|
|
1519
1473
|
async confirmAppointmentAdmin(appointmentId) {
|
|
1520
|
-
console.log(
|
|
1521
|
-
`[APPOINTMENT_SERVICE] Admin confirming appointment: ${appointmentId}`
|
|
1522
|
-
);
|
|
1474
|
+
console.log(`[APPOINTMENT_SERVICE] Admin confirming appointment: ${appointmentId}`);
|
|
1523
1475
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1524
|
-
if (!appointment)
|
|
1525
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1476
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1526
1477
|
if (appointment.status !== "pending" /* PENDING */) {
|
|
1527
|
-
throw new Error(
|
|
1528
|
-
`Appointment ${appointmentId} is not in PENDING state to be confirmed.`
|
|
1529
|
-
);
|
|
1478
|
+
throw new Error(`Appointment ${appointmentId} is not in PENDING state to be confirmed.`);
|
|
1530
1479
|
}
|
|
1531
|
-
return this.updateAppointmentStatus(
|
|
1532
|
-
appointmentId,
|
|
1533
|
-
"confirmed" /* CONFIRMED */
|
|
1534
|
-
);
|
|
1480
|
+
return this.updateAppointmentStatus(appointmentId, "confirmed" /* CONFIRMED */);
|
|
1535
1481
|
}
|
|
1536
1482
|
/**
|
|
1537
1483
|
* Cancels an appointment by the User (Patient).
|
|
1538
1484
|
*/
|
|
1539
1485
|
async cancelAppointmentUser(appointmentId, reason) {
|
|
1540
|
-
console.log(
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
"canceled_patient" /* CANCELED_PATIENT */,
|
|
1546
|
-
{
|
|
1547
|
-
cancellationReason: reason,
|
|
1548
|
-
canceledBy: "patient"
|
|
1549
|
-
}
|
|
1550
|
-
);
|
|
1486
|
+
console.log(`[APPOINTMENT_SERVICE] User canceling appointment: ${appointmentId}`);
|
|
1487
|
+
return this.updateAppointmentStatus(appointmentId, "canceled_patient" /* CANCELED_PATIENT */, {
|
|
1488
|
+
cancellationReason: reason,
|
|
1489
|
+
canceledBy: "patient"
|
|
1490
|
+
});
|
|
1551
1491
|
}
|
|
1552
1492
|
/**
|
|
1553
1493
|
* Cancels an appointment by an Admin/Clinic.
|
|
1554
1494
|
*/
|
|
1555
1495
|
async cancelAppointmentAdmin(appointmentId, reason) {
|
|
1556
|
-
console.log(
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
"canceled_clinic" /* CANCELED_CLINIC */,
|
|
1562
|
-
{
|
|
1563
|
-
cancellationReason: reason,
|
|
1564
|
-
canceledBy: "clinic"
|
|
1565
|
-
}
|
|
1566
|
-
);
|
|
1496
|
+
console.log(`[APPOINTMENT_SERVICE] Admin canceling appointment: ${appointmentId}`);
|
|
1497
|
+
return this.updateAppointmentStatus(appointmentId, "canceled_clinic" /* CANCELED_CLINIC */, {
|
|
1498
|
+
cancellationReason: reason,
|
|
1499
|
+
canceledBy: "clinic"
|
|
1500
|
+
});
|
|
1567
1501
|
}
|
|
1568
1502
|
/**
|
|
1569
1503
|
* Admin proposes to reschedule an appointment.
|
|
1570
1504
|
* Sets status to RESCHEDULED_BY_CLINIC and updates times.
|
|
1571
1505
|
*/
|
|
1572
1506
|
async rescheduleAppointmentAdmin(params) {
|
|
1573
|
-
console.log(
|
|
1574
|
-
|
|
1575
|
-
);
|
|
1576
|
-
const validatedParams = await rescheduleAppointmentSchema.parseAsync(
|
|
1577
|
-
params
|
|
1578
|
-
);
|
|
1579
|
-
const startTimestamp = this.convertToTimestamp(
|
|
1580
|
-
validatedParams.newStartTime
|
|
1581
|
-
);
|
|
1507
|
+
console.log(`[APPOINTMENT_SERVICE] Admin rescheduling appointment: ${params.appointmentId}`);
|
|
1508
|
+
const validatedParams = await rescheduleAppointmentSchema.parseAsync(params);
|
|
1509
|
+
const startTimestamp = this.convertToTimestamp(validatedParams.newStartTime);
|
|
1582
1510
|
const endTimestamp = this.convertToTimestamp(validatedParams.newEndTime);
|
|
1583
1511
|
if (endTimestamp.toMillis() <= startTimestamp.toMillis()) {
|
|
1584
1512
|
throw new Error("New end time must be after new start time.");
|
|
@@ -1621,48 +1549,31 @@ var AppointmentService = class extends BaseService {
|
|
|
1621
1549
|
if (value && typeof value.seconds === "number") {
|
|
1622
1550
|
return new import_firestore2.Timestamp(value.seconds, value.nanoseconds || 0);
|
|
1623
1551
|
}
|
|
1624
|
-
throw new Error(
|
|
1625
|
-
`Invalid timestamp format: ${typeof value}, value: ${JSON.stringify(
|
|
1626
|
-
value
|
|
1627
|
-
)}`
|
|
1628
|
-
);
|
|
1552
|
+
throw new Error(`Invalid timestamp format: ${typeof value}, value: ${JSON.stringify(value)}`);
|
|
1629
1553
|
}
|
|
1630
1554
|
/**
|
|
1631
1555
|
* User confirms a reschedule proposed by the clinic.
|
|
1632
1556
|
* Status changes from RESCHEDULED_BY_CLINIC to CONFIRMED.
|
|
1633
1557
|
*/
|
|
1634
1558
|
async rescheduleAppointmentConfirmUser(appointmentId) {
|
|
1635
|
-
console.log(
|
|
1636
|
-
`[APPOINTMENT_SERVICE] User confirming reschedule for: ${appointmentId}`
|
|
1637
|
-
);
|
|
1559
|
+
console.log(`[APPOINTMENT_SERVICE] User confirming reschedule for: ${appointmentId}`);
|
|
1638
1560
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1639
|
-
if (!appointment)
|
|
1640
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1561
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1641
1562
|
if (appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
1642
|
-
throw new Error(
|
|
1643
|
-
`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`
|
|
1644
|
-
);
|
|
1563
|
+
throw new Error(`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`);
|
|
1645
1564
|
}
|
|
1646
|
-
return this.updateAppointmentStatus(
|
|
1647
|
-
appointmentId,
|
|
1648
|
-
"confirmed" /* CONFIRMED */
|
|
1649
|
-
);
|
|
1565
|
+
return this.updateAppointmentStatus(appointmentId, "confirmed" /* CONFIRMED */);
|
|
1650
1566
|
}
|
|
1651
1567
|
/**
|
|
1652
1568
|
* User rejects a reschedule proposed by the clinic.
|
|
1653
1569
|
* Status changes from RESCHEDULED_BY_CLINIC to CANCELED_PATIENT_RESCHEDULED.
|
|
1654
1570
|
*/
|
|
1655
1571
|
async rescheduleAppointmentRejectUser(appointmentId, reason) {
|
|
1656
|
-
console.log(
|
|
1657
|
-
`[APPOINTMENT_SERVICE] User rejecting reschedule for: ${appointmentId}`
|
|
1658
|
-
);
|
|
1572
|
+
console.log(`[APPOINTMENT_SERVICE] User rejecting reschedule for: ${appointmentId}`);
|
|
1659
1573
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1660
|
-
if (!appointment)
|
|
1661
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1574
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1662
1575
|
if (appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
1663
|
-
throw new Error(
|
|
1664
|
-
`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`
|
|
1665
|
-
);
|
|
1576
|
+
throw new Error(`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`);
|
|
1666
1577
|
}
|
|
1667
1578
|
return this.updateAppointmentStatus(
|
|
1668
1579
|
appointmentId,
|
|
@@ -1678,17 +1589,12 @@ var AppointmentService = class extends BaseService {
|
|
|
1678
1589
|
* Requires all pending user forms to be completed.
|
|
1679
1590
|
*/
|
|
1680
1591
|
async checkInPatientAdmin(appointmentId) {
|
|
1681
|
-
console.log(
|
|
1682
|
-
`[APPOINTMENT_SERVICE] Admin checking in patient for: ${appointmentId}`
|
|
1683
|
-
);
|
|
1592
|
+
console.log(`[APPOINTMENT_SERVICE] Admin checking in patient for: ${appointmentId}`);
|
|
1684
1593
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1685
|
-
if (!appointment)
|
|
1686
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1594
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1687
1595
|
if (appointment.pendingUserFormsIds && appointment.pendingUserFormsIds.length > 0) {
|
|
1688
1596
|
throw new Error(
|
|
1689
|
-
`Cannot check in: Patient has ${appointment.pendingUserFormsIds.length} pending required form(s). IDs: ${appointment.pendingUserFormsIds.join(
|
|
1690
|
-
", "
|
|
1691
|
-
)}`
|
|
1597
|
+
`Cannot check in: Patient has ${appointment.pendingUserFormsIds.length} pending required form(s). IDs: ${appointment.pendingUserFormsIds.join(", ")}`
|
|
1692
1598
|
);
|
|
1693
1599
|
}
|
|
1694
1600
|
if (appointment.status !== "confirmed" /* CONFIRMED */ && appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
@@ -1696,25 +1602,17 @@ var AppointmentService = class extends BaseService {
|
|
|
1696
1602
|
`Checking in appointment ${appointmentId} with status ${appointment.status}. Ensure this is intended.`
|
|
1697
1603
|
);
|
|
1698
1604
|
}
|
|
1699
|
-
return this.updateAppointmentStatus(
|
|
1700
|
-
appointmentId,
|
|
1701
|
-
"checked_in" /* CHECKED_IN */
|
|
1702
|
-
);
|
|
1605
|
+
return this.updateAppointmentStatus(appointmentId, "checked_in" /* CHECKED_IN */);
|
|
1703
1606
|
}
|
|
1704
1607
|
/**
|
|
1705
1608
|
* Doctor starts the appointment procedure.
|
|
1706
1609
|
*/
|
|
1707
1610
|
async startAppointmentDoctor(appointmentId) {
|
|
1708
|
-
console.log(
|
|
1709
|
-
`[APPOINTMENT_SERVICE] Doctor starting appointment: ${appointmentId}`
|
|
1710
|
-
);
|
|
1611
|
+
console.log(`[APPOINTMENT_SERVICE] Doctor starting appointment: ${appointmentId}`);
|
|
1711
1612
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1712
|
-
if (!appointment)
|
|
1713
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1613
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1714
1614
|
if (appointment.status !== "checked_in" /* CHECKED_IN */) {
|
|
1715
|
-
throw new Error(
|
|
1716
|
-
`Appointment ${appointmentId} must be CHECKED_IN to start.`
|
|
1717
|
-
);
|
|
1615
|
+
throw new Error(`Appointment ${appointmentId} must be CHECKED_IN to start.`);
|
|
1718
1616
|
}
|
|
1719
1617
|
const updateData = {
|
|
1720
1618
|
status: "in_progress" /* IN_PROGRESS */,
|
|
@@ -1728,24 +1626,18 @@ var AppointmentService = class extends BaseService {
|
|
|
1728
1626
|
* Doctor completes and finalizes the appointment.
|
|
1729
1627
|
*/
|
|
1730
1628
|
async completeAppointmentDoctor(appointmentId, finalizationNotes, actualDurationMinutesInput) {
|
|
1731
|
-
console.log(
|
|
1732
|
-
`[APPOINTMENT_SERVICE] Doctor completing appointment: ${appointmentId}`
|
|
1733
|
-
);
|
|
1629
|
+
console.log(`[APPOINTMENT_SERVICE] Doctor completing appointment: ${appointmentId}`);
|
|
1734
1630
|
const currentUser = this.auth.currentUser;
|
|
1735
|
-
if (!currentUser)
|
|
1736
|
-
throw new Error("Authentication required to complete appointment.");
|
|
1631
|
+
if (!currentUser) throw new Error("Authentication required to complete appointment.");
|
|
1737
1632
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1738
|
-
if (!appointment)
|
|
1739
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1633
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1740
1634
|
let calculatedDurationMinutes = actualDurationMinutesInput;
|
|
1741
1635
|
const procedureCompletionTime = import_firestore2.Timestamp.now();
|
|
1742
1636
|
if (calculatedDurationMinutes === void 0 && appointment.procedureActualStartTime) {
|
|
1743
1637
|
const startTimeMillis = appointment.procedureActualStartTime.toMillis();
|
|
1744
1638
|
const endTimeMillis = procedureCompletionTime.toMillis();
|
|
1745
1639
|
if (endTimeMillis > startTimeMillis) {
|
|
1746
|
-
calculatedDurationMinutes = Math.round(
|
|
1747
|
-
(endTimeMillis - startTimeMillis) / 6e4
|
|
1748
|
-
);
|
|
1640
|
+
calculatedDurationMinutes = Math.round((endTimeMillis - startTimeMillis) / 6e4);
|
|
1749
1641
|
}
|
|
1750
1642
|
}
|
|
1751
1643
|
const updateData = {
|
|
@@ -1769,31 +1661,22 @@ var AppointmentService = class extends BaseService {
|
|
|
1769
1661
|
* Admin marks an appointment as No-Show.
|
|
1770
1662
|
*/
|
|
1771
1663
|
async markNoShowAdmin(appointmentId) {
|
|
1772
|
-
console.log(
|
|
1773
|
-
`[APPOINTMENT_SERVICE] Admin marking no-show for: ${appointmentId}`
|
|
1774
|
-
);
|
|
1664
|
+
console.log(`[APPOINTMENT_SERVICE] Admin marking no-show for: ${appointmentId}`);
|
|
1775
1665
|
const appointment = await this.getAppointmentById(appointmentId);
|
|
1776
|
-
if (!appointment)
|
|
1777
|
-
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1666
|
+
if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
|
|
1778
1667
|
if (import_firestore2.Timestamp.now().toMillis() < appointment.appointmentStartTime.toMillis()) {
|
|
1779
1668
|
throw new Error("Cannot mark no-show before appointment start time.");
|
|
1780
1669
|
}
|
|
1781
|
-
return this.updateAppointmentStatus(
|
|
1782
|
-
|
|
1783
|
-
"
|
|
1784
|
-
|
|
1785
|
-
cancellationReason: "Patient did not show up for the appointment.",
|
|
1786
|
-
canceledBy: "clinic"
|
|
1787
|
-
}
|
|
1788
|
-
);
|
|
1670
|
+
return this.updateAppointmentStatus(appointmentId, "no_show" /* NO_SHOW */, {
|
|
1671
|
+
cancellationReason: "Patient did not show up for the appointment.",
|
|
1672
|
+
canceledBy: "clinic"
|
|
1673
|
+
});
|
|
1789
1674
|
}
|
|
1790
1675
|
/**
|
|
1791
1676
|
* Adds a media item to an appointment.
|
|
1792
1677
|
*/
|
|
1793
1678
|
async addMediaToAppointment(appointmentId, mediaItemData) {
|
|
1794
|
-
console.log(
|
|
1795
|
-
`[APPOINTMENT_SERVICE] Adding media to appointment ${appointmentId}`
|
|
1796
|
-
);
|
|
1679
|
+
console.log(`[APPOINTMENT_SERVICE] Adding media to appointment ${appointmentId}`);
|
|
1797
1680
|
const currentUser = this.auth.currentUser;
|
|
1798
1681
|
if (!currentUser) throw new Error("Authentication required.");
|
|
1799
1682
|
const newMediaItem = {
|
|
@@ -1833,9 +1716,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1833
1716
|
* Adds or updates review information for an appointment.
|
|
1834
1717
|
*/
|
|
1835
1718
|
async addReviewToAppointment(appointmentId, reviewData) {
|
|
1836
|
-
console.log(
|
|
1837
|
-
`[APPOINTMENT_SERVICE] Adding review to appointment ${appointmentId}`
|
|
1838
|
-
);
|
|
1719
|
+
console.log(`[APPOINTMENT_SERVICE] Adding review to appointment ${appointmentId}`);
|
|
1839
1720
|
const newReviewInfo = {
|
|
1840
1721
|
...reviewData,
|
|
1841
1722
|
reviewId: this.generateId(),
|
|
@@ -1869,9 +1750,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1869
1750
|
* @returns The updated appointment
|
|
1870
1751
|
*/
|
|
1871
1752
|
async updateInternalNotes(appointmentId, notes) {
|
|
1872
|
-
console.log(
|
|
1873
|
-
`[APPOINTMENT_SERVICE] Updating internal notes for appointment: ${appointmentId}`
|
|
1874
|
-
);
|
|
1753
|
+
console.log(`[APPOINTMENT_SERVICE] Updating internal notes for appointment: ${appointmentId}`);
|
|
1875
1754
|
const updateData = {
|
|
1876
1755
|
internalNotes: notes
|
|
1877
1756
|
};
|
|
@@ -1887,9 +1766,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1887
1766
|
*/
|
|
1888
1767
|
async getUpcomingPatientAppointments(patientId, options) {
|
|
1889
1768
|
try {
|
|
1890
|
-
console.log(
|
|
1891
|
-
`[APPOINTMENT_SERVICE] Getting upcoming appointments for patient: ${patientId}`
|
|
1892
|
-
);
|
|
1769
|
+
console.log(`[APPOINTMENT_SERVICE] Getting upcoming appointments for patient: ${patientId}`);
|
|
1893
1770
|
const effectiveStartDate = (options == null ? void 0 : options.startDate) || /* @__PURE__ */ new Date();
|
|
1894
1771
|
const upcomingStatuses = [
|
|
1895
1772
|
"pending" /* PENDING */,
|
|
@@ -1901,21 +1778,9 @@ var AppointmentService = class extends BaseService {
|
|
|
1901
1778
|
const constraints = [];
|
|
1902
1779
|
constraints.push((0, import_firestore2.where)("patientId", "==", patientId));
|
|
1903
1780
|
constraints.push((0, import_firestore2.where)("status", "in", upcomingStatuses));
|
|
1904
|
-
constraints.push(
|
|
1905
|
-
(0, import_firestore2.where)(
|
|
1906
|
-
"appointmentStartTime",
|
|
1907
|
-
">=",
|
|
1908
|
-
import_firestore2.Timestamp.fromDate(effectiveStartDate)
|
|
1909
|
-
)
|
|
1910
|
-
);
|
|
1781
|
+
constraints.push((0, import_firestore2.where)("appointmentStartTime", ">=", import_firestore2.Timestamp.fromDate(effectiveStartDate)));
|
|
1911
1782
|
if (options == null ? void 0 : options.endDate) {
|
|
1912
|
-
constraints.push(
|
|
1913
|
-
(0, import_firestore2.where)(
|
|
1914
|
-
"appointmentStartTime",
|
|
1915
|
-
"<=",
|
|
1916
|
-
import_firestore2.Timestamp.fromDate(options.endDate)
|
|
1917
|
-
)
|
|
1918
|
-
);
|
|
1783
|
+
constraints.push((0, import_firestore2.where)("appointmentStartTime", "<=", import_firestore2.Timestamp.fromDate(options.endDate)));
|
|
1919
1784
|
}
|
|
1920
1785
|
constraints.push((0, import_firestore2.orderBy)("appointmentStartTime", "asc"));
|
|
1921
1786
|
if (options == null ? void 0 : options.limit) {
|
|
@@ -1924,14 +1789,9 @@ var AppointmentService = class extends BaseService {
|
|
|
1924
1789
|
if (options == null ? void 0 : options.startAfter) {
|
|
1925
1790
|
constraints.push((0, import_firestore2.startAfter)(options.startAfter));
|
|
1926
1791
|
}
|
|
1927
|
-
const q = (0, import_firestore2.query)(
|
|
1928
|
-
(0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION),
|
|
1929
|
-
...constraints
|
|
1930
|
-
);
|
|
1792
|
+
const q = (0, import_firestore2.query)((0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
1931
1793
|
const querySnapshot = await (0, import_firestore2.getDocs)(q);
|
|
1932
|
-
const appointments = querySnapshot.docs.map(
|
|
1933
|
-
(doc38) => doc38.data()
|
|
1934
|
-
);
|
|
1794
|
+
const appointments = querySnapshot.docs.map((doc38) => doc38.data());
|
|
1935
1795
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
1936
1796
|
console.log(
|
|
1937
1797
|
`[APPOINTMENT_SERVICE] Found ${appointments.length} upcoming appointments for patient ${patientId}`
|
|
@@ -1956,9 +1816,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1956
1816
|
*/
|
|
1957
1817
|
async getPastPatientAppointments(patientId, options) {
|
|
1958
1818
|
try {
|
|
1959
|
-
console.log(
|
|
1960
|
-
`[APPOINTMENT_SERVICE] Getting past appointments for patient: ${patientId}`
|
|
1961
|
-
);
|
|
1819
|
+
console.log(`[APPOINTMENT_SERVICE] Getting past appointments for patient: ${patientId}`);
|
|
1962
1820
|
const effectiveEndDate = (options == null ? void 0 : options.endDate) || /* @__PURE__ */ new Date();
|
|
1963
1821
|
const pastStatuses = ["completed" /* COMPLETED */];
|
|
1964
1822
|
if (options == null ? void 0 : options.showCanceled) {
|
|
@@ -1976,20 +1834,10 @@ var AppointmentService = class extends BaseService {
|
|
|
1976
1834
|
constraints.push((0, import_firestore2.where)("status", "in", pastStatuses));
|
|
1977
1835
|
if (options == null ? void 0 : options.startDate) {
|
|
1978
1836
|
constraints.push(
|
|
1979
|
-
(0, import_firestore2.where)(
|
|
1980
|
-
"appointmentStartTime",
|
|
1981
|
-
">=",
|
|
1982
|
-
import_firestore2.Timestamp.fromDate(options.startDate)
|
|
1983
|
-
)
|
|
1837
|
+
(0, import_firestore2.where)("appointmentStartTime", ">=", import_firestore2.Timestamp.fromDate(options.startDate))
|
|
1984
1838
|
);
|
|
1985
1839
|
}
|
|
1986
|
-
constraints.push(
|
|
1987
|
-
(0, import_firestore2.where)(
|
|
1988
|
-
"appointmentStartTime",
|
|
1989
|
-
"<=",
|
|
1990
|
-
import_firestore2.Timestamp.fromDate(effectiveEndDate)
|
|
1991
|
-
)
|
|
1992
|
-
);
|
|
1840
|
+
constraints.push((0, import_firestore2.where)("appointmentStartTime", "<=", import_firestore2.Timestamp.fromDate(effectiveEndDate)));
|
|
1993
1841
|
constraints.push((0, import_firestore2.orderBy)("appointmentStartTime", "desc"));
|
|
1994
1842
|
if (options == null ? void 0 : options.limit) {
|
|
1995
1843
|
constraints.push((0, import_firestore2.limit)(options.limit));
|
|
@@ -1997,14 +1845,9 @@ var AppointmentService = class extends BaseService {
|
|
|
1997
1845
|
if (options == null ? void 0 : options.startAfter) {
|
|
1998
1846
|
constraints.push((0, import_firestore2.startAfter)(options.startAfter));
|
|
1999
1847
|
}
|
|
2000
|
-
const q = (0, import_firestore2.query)(
|
|
2001
|
-
(0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION),
|
|
2002
|
-
...constraints
|
|
2003
|
-
);
|
|
1848
|
+
const q = (0, import_firestore2.query)((0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
2004
1849
|
const querySnapshot = await (0, import_firestore2.getDocs)(q);
|
|
2005
|
-
const appointments = querySnapshot.docs.map(
|
|
2006
|
-
(doc38) => doc38.data()
|
|
2007
|
-
);
|
|
1850
|
+
const appointments = querySnapshot.docs.map((doc38) => doc38.data());
|
|
2008
1851
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
2009
1852
|
console.log(
|
|
2010
1853
|
`[APPOINTMENT_SERVICE] Found ${appointments.length} past appointments for patient ${patientId}`
|
|
@@ -2018,6 +1861,46 @@ var AppointmentService = class extends BaseService {
|
|
|
2018
1861
|
throw error;
|
|
2019
1862
|
}
|
|
2020
1863
|
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Counts completed appointments for a patient with optional clinic filtering.
|
|
1866
|
+
*
|
|
1867
|
+
* @param patientId ID of the patient.
|
|
1868
|
+
* @param clinicBranchId Optional ID of the clinic branch to either include or exclude.
|
|
1869
|
+
* @param excludeClinic Optional boolean. If true (default), excludes the specified clinic. If false, includes only that clinic.
|
|
1870
|
+
* @returns The count of completed appointments.
|
|
1871
|
+
*/
|
|
1872
|
+
async countCompletedAppointments(patientId, clinicBranchId, excludeClinic = true) {
|
|
1873
|
+
try {
|
|
1874
|
+
console.log(
|
|
1875
|
+
`[APPOINTMENT_SERVICE] Counting completed appointments for patient: ${patientId}`,
|
|
1876
|
+
{ clinicBranchId, excludeClinic }
|
|
1877
|
+
);
|
|
1878
|
+
const constraints = [
|
|
1879
|
+
(0, import_firestore2.where)("patientId", "==", patientId),
|
|
1880
|
+
(0, import_firestore2.where)("status", "==", "completed" /* COMPLETED */)
|
|
1881
|
+
];
|
|
1882
|
+
if (clinicBranchId) {
|
|
1883
|
+
if (excludeClinic) {
|
|
1884
|
+
constraints.push((0, import_firestore2.where)("clinicBranchId", "!=", clinicBranchId));
|
|
1885
|
+
} else {
|
|
1886
|
+
constraints.push((0, import_firestore2.where)("clinicBranchId", "==", clinicBranchId));
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
const q = (0, import_firestore2.query)((0, import_firestore2.collection)(this.db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
1890
|
+
const snapshot = await (0, import_firestore2.getCountFromServer)(q);
|
|
1891
|
+
const count = snapshot.data().count;
|
|
1892
|
+
console.log(
|
|
1893
|
+
`[APPOINTMENT_SERVICE] Found ${count} completed appointments for patient ${patientId}`
|
|
1894
|
+
);
|
|
1895
|
+
return count;
|
|
1896
|
+
} catch (error) {
|
|
1897
|
+
console.error(
|
|
1898
|
+
`[APPOINTMENT_SERVICE] Error counting completed appointments for patient ${patientId}:`,
|
|
1899
|
+
error
|
|
1900
|
+
);
|
|
1901
|
+
throw error;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
2021
1904
|
};
|
|
2022
1905
|
|
|
2023
1906
|
// src/services/auth/auth.service.ts
|
|
@@ -7880,7 +7763,10 @@ var BillingTransactionsService = class extends BaseService {
|
|
|
7880
7763
|
"subscription_reactivated" /* SUBSCRIPTION_REACTIVATED */,
|
|
7881
7764
|
"subscription_deleted" /* SUBSCRIPTION_DELETED */
|
|
7882
7765
|
];
|
|
7883
|
-
const constraints = [
|
|
7766
|
+
const constraints = [
|
|
7767
|
+
(0, import_firestore23.where)("type", "in", subscriptionTypes),
|
|
7768
|
+
(0, import_firestore23.orderBy)("timestamp", "desc")
|
|
7769
|
+
];
|
|
7884
7770
|
if (startAfterDoc) {
|
|
7885
7771
|
constraints.push((0, import_firestore23.startAfter)(startAfterDoc));
|
|
7886
7772
|
}
|
|
@@ -9888,19 +9774,13 @@ var AuthService = class extends BaseService {
|
|
|
9888
9774
|
constructor(db, auth, app, userService) {
|
|
9889
9775
|
super(db, auth, app);
|
|
9890
9776
|
this.googleProvider = new import_auth7.GoogleAuthProvider();
|
|
9891
|
-
this.facebookProvider = new import_auth7.FacebookAuthProvider();
|
|
9892
|
-
this.appleProvider = new import_auth7.OAuthProvider("apple.com");
|
|
9893
9777
|
this.userService = userService || new UserService(db, auth, app);
|
|
9894
9778
|
}
|
|
9895
9779
|
/**
|
|
9896
9780
|
* Registruje novog korisnika sa email-om i lozinkom
|
|
9897
9781
|
*/
|
|
9898
9782
|
async signUp(email, password, initialRole = "patient" /* PATIENT */, options) {
|
|
9899
|
-
const { user: firebaseUser } = await (0, import_auth7.createUserWithEmailAndPassword)(
|
|
9900
|
-
this.auth,
|
|
9901
|
-
email,
|
|
9902
|
-
password
|
|
9903
|
-
);
|
|
9783
|
+
const { user: firebaseUser } = await (0, import_auth7.createUserWithEmailAndPassword)(this.auth, email, password);
|
|
9904
9784
|
return this.userService.createUser(firebaseUser, [initialRole], options);
|
|
9905
9785
|
}
|
|
9906
9786
|
/**
|
|
@@ -9919,20 +9799,13 @@ var AuthService = class extends BaseService {
|
|
|
9919
9799
|
await clinicAdminSignupSchema.parseAsync(data);
|
|
9920
9800
|
console.log("[AUTH] Clinic admin signup data validation passed");
|
|
9921
9801
|
} catch (validationError) {
|
|
9922
|
-
console.error(
|
|
9923
|
-
"[AUTH] Validation error in signUpClinicAdmin:",
|
|
9924
|
-
validationError
|
|
9925
|
-
);
|
|
9802
|
+
console.error("[AUTH] Validation error in signUpClinicAdmin:", validationError);
|
|
9926
9803
|
throw validationError;
|
|
9927
9804
|
}
|
|
9928
9805
|
console.log("[AUTH] Creating Firebase user");
|
|
9929
9806
|
let firebaseUser;
|
|
9930
9807
|
try {
|
|
9931
|
-
const result = await (0, import_auth7.createUserWithEmailAndPassword)(
|
|
9932
|
-
this.auth,
|
|
9933
|
-
data.email,
|
|
9934
|
-
data.password
|
|
9935
|
-
);
|
|
9808
|
+
const result = await (0, import_auth7.createUserWithEmailAndPassword)(this.auth, data.email, data.password);
|
|
9936
9809
|
firebaseUser = result.user;
|
|
9937
9810
|
console.log("[AUTH] Firebase user created successfully", {
|
|
9938
9811
|
uid: firebaseUser.uid
|
|
@@ -9944,13 +9817,9 @@ var AuthService = class extends BaseService {
|
|
|
9944
9817
|
console.log("[AUTH] Creating user with CLINIC_ADMIN role");
|
|
9945
9818
|
let user;
|
|
9946
9819
|
try {
|
|
9947
|
-
user = await this.userService.createUser(
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
{
|
|
9951
|
-
skipProfileCreation: true
|
|
9952
|
-
}
|
|
9953
|
-
);
|
|
9820
|
+
user = await this.userService.createUser(firebaseUser, ["clinic_admin" /* CLINIC_ADMIN */], {
|
|
9821
|
+
skipProfileCreation: true
|
|
9822
|
+
});
|
|
9954
9823
|
console.log("[AUTH] User with CLINIC_ADMIN role created successfully", {
|
|
9955
9824
|
userId: user.uid
|
|
9956
9825
|
});
|
|
@@ -9967,11 +9836,7 @@ var AuthService = class extends BaseService {
|
|
|
9967
9836
|
};
|
|
9968
9837
|
console.log("[AUTH] Contact person object created");
|
|
9969
9838
|
console.log("[AUTH] Initializing clinic services");
|
|
9970
|
-
const clinicAdminService = new ClinicAdminService(
|
|
9971
|
-
this.db,
|
|
9972
|
-
this.auth,
|
|
9973
|
-
this.app
|
|
9974
|
-
);
|
|
9839
|
+
const clinicAdminService = new ClinicAdminService(this.db, this.auth, this.app);
|
|
9975
9840
|
const clinicGroupService = new ClinicGroupService(
|
|
9976
9841
|
this.db,
|
|
9977
9842
|
this.auth,
|
|
@@ -9988,18 +9853,14 @@ var AuthService = class extends BaseService {
|
|
|
9988
9853
|
mediaService
|
|
9989
9854
|
);
|
|
9990
9855
|
clinicAdminService.setServices(clinicGroupService, clinicService);
|
|
9991
|
-
console.log(
|
|
9992
|
-
"[AUTH] Services initialized and circular dependencies resolved"
|
|
9993
|
-
);
|
|
9856
|
+
console.log("[AUTH] Services initialized and circular dependencies resolved");
|
|
9994
9857
|
let clinicGroup = null;
|
|
9995
9858
|
let adminProfile = null;
|
|
9996
9859
|
if (data.isCreatingNewGroup) {
|
|
9997
9860
|
console.log("[AUTH] Creating new clinic group flow");
|
|
9998
9861
|
if (!data.clinicGroupData) {
|
|
9999
9862
|
console.error("[AUTH] Clinic group data is missing");
|
|
10000
|
-
throw new Error(
|
|
10001
|
-
"Clinic group data is required when creating a new group"
|
|
10002
|
-
);
|
|
9863
|
+
throw new Error("Clinic group data is required when creating a new group");
|
|
10003
9864
|
}
|
|
10004
9865
|
console.log("[AUTH] Creating clinic admin first (without group)");
|
|
10005
9866
|
const createClinicAdminData = {
|
|
@@ -10012,17 +9873,12 @@ var AuthService = class extends BaseService {
|
|
|
10012
9873
|
// No clinicGroupId yet
|
|
10013
9874
|
};
|
|
10014
9875
|
try {
|
|
10015
|
-
adminProfile = await clinicAdminService.createClinicAdmin(
|
|
10016
|
-
createClinicAdminData
|
|
10017
|
-
);
|
|
9876
|
+
adminProfile = await clinicAdminService.createClinicAdmin(createClinicAdminData);
|
|
10018
9877
|
console.log("[AUTH] Clinic admin created successfully", {
|
|
10019
9878
|
adminId: adminProfile.id
|
|
10020
9879
|
});
|
|
10021
9880
|
} catch (adminCreationError) {
|
|
10022
|
-
console.error(
|
|
10023
|
-
"[AUTH] Clinic admin creation failed:",
|
|
10024
|
-
adminCreationError
|
|
10025
|
-
);
|
|
9881
|
+
console.error("[AUTH] Clinic admin creation failed:", adminCreationError);
|
|
10026
9882
|
throw adminCreationError;
|
|
10027
9883
|
}
|
|
10028
9884
|
try {
|
|
@@ -10030,14 +9886,9 @@ var AuthService = class extends BaseService {
|
|
|
10030
9886
|
user = await this.userService.updateUser(firebaseUser.uid, {
|
|
10031
9887
|
adminProfile: adminProfile.id
|
|
10032
9888
|
});
|
|
10033
|
-
console.log(
|
|
10034
|
-
"[AUTH] User updated with admin profile reference successfully"
|
|
10035
|
-
);
|
|
9889
|
+
console.log("[AUTH] User updated with admin profile reference successfully");
|
|
10036
9890
|
} catch (userUpdateError) {
|
|
10037
|
-
console.error(
|
|
10038
|
-
"[AUTH] Failed to update user with admin profile:",
|
|
10039
|
-
userUpdateError
|
|
10040
|
-
);
|
|
9891
|
+
console.error("[AUTH] Failed to update user with admin profile:", userUpdateError);
|
|
10041
9892
|
throw userUpdateError;
|
|
10042
9893
|
}
|
|
10043
9894
|
const createClinicGroupData = {
|
|
@@ -10075,23 +9926,16 @@ var AuthService = class extends BaseService {
|
|
|
10075
9926
|
clinicGroupId: clinicGroup.id
|
|
10076
9927
|
});
|
|
10077
9928
|
console.log("[AUTH] Admin updated with clinic group ID successfully");
|
|
10078
|
-
adminProfile = await clinicAdminService.getClinicAdmin(
|
|
10079
|
-
adminProfile.id
|
|
10080
|
-
);
|
|
9929
|
+
adminProfile = await clinicAdminService.getClinicAdmin(adminProfile.id);
|
|
10081
9930
|
} catch (groupCreationError) {
|
|
10082
|
-
console.error(
|
|
10083
|
-
"[AUTH] Clinic group creation failed:",
|
|
10084
|
-
groupCreationError
|
|
10085
|
-
);
|
|
9931
|
+
console.error("[AUTH] Clinic group creation failed:", groupCreationError);
|
|
10086
9932
|
throw groupCreationError;
|
|
10087
9933
|
}
|
|
10088
9934
|
} else {
|
|
10089
9935
|
console.log("[AUTH] Joining existing clinic group flow");
|
|
10090
9936
|
if (!data.inviteToken) {
|
|
10091
9937
|
console.error("[AUTH] Invite token is missing");
|
|
10092
|
-
throw new Error(
|
|
10093
|
-
"Invite token is required when joining an existing group"
|
|
10094
|
-
);
|
|
9938
|
+
throw new Error("Invite token is required when joining an existing group");
|
|
10095
9939
|
}
|
|
10096
9940
|
console.log("[AUTH] Invite token provided", {
|
|
10097
9941
|
token: data.inviteToken
|
|
@@ -10102,11 +9946,7 @@ var AuthService = class extends BaseService {
|
|
|
10102
9946
|
const querySnapshot = await (0, import_firestore29.getDocs)(q);
|
|
10103
9947
|
let foundGroup = null;
|
|
10104
9948
|
let foundToken = null;
|
|
10105
|
-
console.log(
|
|
10106
|
-
"[AUTH] Found",
|
|
10107
|
-
querySnapshot.size,
|
|
10108
|
-
"clinic groups to check"
|
|
10109
|
-
);
|
|
9949
|
+
console.log("[AUTH] Found", querySnapshot.size, "clinic groups to check");
|
|
10110
9950
|
for (const docSnapshot of querySnapshot.docs) {
|
|
10111
9951
|
const group = docSnapshot.data();
|
|
10112
9952
|
console.log("[AUTH] Checking group", {
|
|
@@ -10152,17 +9992,12 @@ var AuthService = class extends BaseService {
|
|
|
10152
9992
|
isActive: true
|
|
10153
9993
|
};
|
|
10154
9994
|
try {
|
|
10155
|
-
adminProfile = await clinicAdminService.createClinicAdmin(
|
|
10156
|
-
createClinicAdminData
|
|
10157
|
-
);
|
|
9995
|
+
adminProfile = await clinicAdminService.createClinicAdmin(createClinicAdminData);
|
|
10158
9996
|
console.log("[AUTH] Clinic admin created successfully", {
|
|
10159
9997
|
adminId: adminProfile.id
|
|
10160
9998
|
});
|
|
10161
9999
|
} catch (adminCreationError) {
|
|
10162
|
-
console.error(
|
|
10163
|
-
"[AUTH] Clinic admin creation failed:",
|
|
10164
|
-
adminCreationError
|
|
10165
|
-
);
|
|
10000
|
+
console.error("[AUTH] Clinic admin creation failed:", adminCreationError);
|
|
10166
10001
|
throw adminCreationError;
|
|
10167
10002
|
}
|
|
10168
10003
|
try {
|
|
@@ -10183,9 +10018,7 @@ var AuthService = class extends BaseService {
|
|
|
10183
10018
|
clinicAdminId: (adminProfile == null ? void 0 : adminProfile.id) || "unknown"
|
|
10184
10019
|
});
|
|
10185
10020
|
if (!clinicGroup || !adminProfile) {
|
|
10186
|
-
throw new Error(
|
|
10187
|
-
"Failed to create or retrieve clinic group or admin profile"
|
|
10188
|
-
);
|
|
10021
|
+
throw new Error("Failed to create or retrieve clinic group or admin profile");
|
|
10189
10022
|
}
|
|
10190
10023
|
return {
|
|
10191
10024
|
user,
|
|
@@ -10213,11 +10046,7 @@ var AuthService = class extends BaseService {
|
|
|
10213
10046
|
* Prijavljuje korisnika sa email-om i lozinkom
|
|
10214
10047
|
*/
|
|
10215
10048
|
async signIn(email, password) {
|
|
10216
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(
|
|
10217
|
-
this.auth,
|
|
10218
|
-
email,
|
|
10219
|
-
password
|
|
10220
|
-
);
|
|
10049
|
+
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(this.auth, email, password);
|
|
10221
10050
|
return this.userService.getOrCreateUser(firebaseUser);
|
|
10222
10051
|
}
|
|
10223
10052
|
/**
|
|
@@ -10231,11 +10060,7 @@ var AuthService = class extends BaseService {
|
|
|
10231
10060
|
async signInClinicAdmin(email, password) {
|
|
10232
10061
|
var _a;
|
|
10233
10062
|
try {
|
|
10234
|
-
const clinicAdminService = new ClinicAdminService(
|
|
10235
|
-
this.db,
|
|
10236
|
-
this.auth,
|
|
10237
|
-
this.app
|
|
10238
|
-
);
|
|
10063
|
+
const clinicAdminService = new ClinicAdminService(this.db, this.auth, this.app);
|
|
10239
10064
|
const clinicGroupService = new ClinicGroupService(
|
|
10240
10065
|
this.db,
|
|
10241
10066
|
this.auth,
|
|
@@ -10252,11 +10077,7 @@ var AuthService = class extends BaseService {
|
|
|
10252
10077
|
mediaService
|
|
10253
10078
|
);
|
|
10254
10079
|
clinicAdminService.setServices(clinicGroupService, clinicService);
|
|
10255
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(
|
|
10256
|
-
this.auth,
|
|
10257
|
-
email,
|
|
10258
|
-
password
|
|
10259
|
-
);
|
|
10080
|
+
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(this.auth, email, password);
|
|
10260
10081
|
const user = await this.userService.getOrCreateUser(firebaseUser);
|
|
10261
10082
|
if (!((_a = user.roles) == null ? void 0 : _a.includes("clinic_admin" /* CLINIC_ADMIN */))) {
|
|
10262
10083
|
console.error("[AUTH] User is not a clinic admin:", user.uid);
|
|
@@ -10266,21 +10087,14 @@ var AuthService = class extends BaseService {
|
|
|
10266
10087
|
console.error("[AUTH] User has no admin profile:", user.uid);
|
|
10267
10088
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10268
10089
|
}
|
|
10269
|
-
const adminProfile = await clinicAdminService.getClinicAdmin(
|
|
10270
|
-
user.adminProfile
|
|
10271
|
-
);
|
|
10090
|
+
const adminProfile = await clinicAdminService.getClinicAdmin(user.adminProfile);
|
|
10272
10091
|
if (!adminProfile) {
|
|
10273
10092
|
console.error("[AUTH] Admin profile not found:", user.adminProfile);
|
|
10274
10093
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10275
10094
|
}
|
|
10276
|
-
const clinicGroup = await clinicGroupService.getClinicGroup(
|
|
10277
|
-
adminProfile.clinicGroupId
|
|
10278
|
-
);
|
|
10095
|
+
const clinicGroup = await clinicGroupService.getClinicGroup(adminProfile.clinicGroupId);
|
|
10279
10096
|
if (!clinicGroup) {
|
|
10280
|
-
console.error(
|
|
10281
|
-
"[AUTH] Clinic group not found:",
|
|
10282
|
-
adminProfile.clinicGroupId
|
|
10283
|
-
);
|
|
10097
|
+
console.error("[AUTH] Clinic group not found:", adminProfile.clinicGroupId);
|
|
10284
10098
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10285
10099
|
}
|
|
10286
10100
|
return {
|
|
@@ -10293,39 +10107,6 @@ var AuthService = class extends BaseService {
|
|
|
10293
10107
|
throw error;
|
|
10294
10108
|
}
|
|
10295
10109
|
}
|
|
10296
|
-
/**
|
|
10297
|
-
* Prijavljuje korisnika sa Facebook-om
|
|
10298
|
-
*/
|
|
10299
|
-
async signInWithFacebook() {
|
|
10300
|
-
const provider = new import_auth7.FacebookAuthProvider();
|
|
10301
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithPopup)(this.auth, provider);
|
|
10302
|
-
return this.userService.getOrCreateUser(firebaseUser);
|
|
10303
|
-
}
|
|
10304
|
-
/**
|
|
10305
|
-
* Prijavljuje korisnika sa Google nalogom
|
|
10306
|
-
*/
|
|
10307
|
-
async signInWithGoogle(initialRole = "patient" /* PATIENT */, options) {
|
|
10308
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithPopup)(
|
|
10309
|
-
this.auth,
|
|
10310
|
-
this.googleProvider
|
|
10311
|
-
);
|
|
10312
|
-
const existingUser = await this.userService.getUserByEmail(
|
|
10313
|
-
firebaseUser.email
|
|
10314
|
-
);
|
|
10315
|
-
if (existingUser) {
|
|
10316
|
-
await this.userService.updateUserLoginTimestamp(existingUser.uid);
|
|
10317
|
-
return existingUser;
|
|
10318
|
-
}
|
|
10319
|
-
return this.userService.createUser(firebaseUser, [initialRole], options);
|
|
10320
|
-
}
|
|
10321
|
-
/**
|
|
10322
|
-
* Prijavljuje korisnika sa Apple-om
|
|
10323
|
-
*/
|
|
10324
|
-
async signInWithApple() {
|
|
10325
|
-
const provider = new import_auth7.OAuthProvider("apple.com");
|
|
10326
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithPopup)(this.auth, provider);
|
|
10327
|
-
return this.userService.getOrCreateUser(firebaseUser);
|
|
10328
|
-
}
|
|
10329
10110
|
/**
|
|
10330
10111
|
* Prijavljuje korisnika anonimno
|
|
10331
10112
|
*/
|
|
@@ -10362,18 +10143,11 @@ var AuthService = class extends BaseService {
|
|
|
10362
10143
|
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10363
10144
|
}
|
|
10364
10145
|
if (!currentUser.isAnonymous) {
|
|
10365
|
-
throw new AuthError(
|
|
10366
|
-
"User is not anonymous",
|
|
10367
|
-
"AUTH/NOT_ANONYMOUS_USER",
|
|
10368
|
-
400
|
|
10369
|
-
);
|
|
10146
|
+
throw new AuthError("User is not anonymous", "AUTH/NOT_ANONYMOUS_USER", 400);
|
|
10370
10147
|
}
|
|
10371
10148
|
const credential = import_auth7.EmailAuthProvider.credential(email, password);
|
|
10372
10149
|
await (0, import_auth7.linkWithCredential)(currentUser, credential);
|
|
10373
|
-
return await this.userService.upgradeAnonymousUser(
|
|
10374
|
-
currentUser.uid,
|
|
10375
|
-
email
|
|
10376
|
-
);
|
|
10150
|
+
return await this.userService.upgradeAnonymousUser(currentUser.uid, email);
|
|
10377
10151
|
} catch (error) {
|
|
10378
10152
|
if (error instanceof import_zod23.z.ZodError) {
|
|
10379
10153
|
throw AUTH_ERRORS.VALIDATION_ERROR;
|
|
@@ -10385,109 +10159,6 @@ var AuthService = class extends BaseService {
|
|
|
10385
10159
|
throw error;
|
|
10386
10160
|
}
|
|
10387
10161
|
}
|
|
10388
|
-
/**
|
|
10389
|
-
* Upgrades an anonymous user to a regular user by signing in with a Google account.
|
|
10390
|
-
*
|
|
10391
|
-
* @throws {AuthError} If the user is not anonymous.
|
|
10392
|
-
* @throws {AuthError} If the user is not authenticated.
|
|
10393
|
-
* @throws {AuthError} If the popup window is closed by the user.
|
|
10394
|
-
* @throws {FirebaseError} If any other Firebase error occurs.
|
|
10395
|
-
*
|
|
10396
|
-
* @returns {Promise<User>} The upgraded user.
|
|
10397
|
-
*/
|
|
10398
|
-
async upgradeAnonymousUserWithGoogle() {
|
|
10399
|
-
try {
|
|
10400
|
-
const currentUser = this.auth.currentUser;
|
|
10401
|
-
if (!currentUser) {
|
|
10402
|
-
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10403
|
-
}
|
|
10404
|
-
if (!currentUser.isAnonymous) {
|
|
10405
|
-
throw new AuthError(
|
|
10406
|
-
"User is not anonymous",
|
|
10407
|
-
"AUTH/NOT_ANONYMOUS_USER",
|
|
10408
|
-
400
|
|
10409
|
-
);
|
|
10410
|
-
}
|
|
10411
|
-
const userCredential = await (0, import_auth7.signInWithPopup)(
|
|
10412
|
-
this.auth,
|
|
10413
|
-
this.googleProvider
|
|
10414
|
-
);
|
|
10415
|
-
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
10416
|
-
return await this.userService.upgradeAnonymousUser(
|
|
10417
|
-
currentUser.uid,
|
|
10418
|
-
userCredential.user.email
|
|
10419
|
-
);
|
|
10420
|
-
} catch (error) {
|
|
10421
|
-
const firebaseError = error;
|
|
10422
|
-
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
10423
|
-
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
10424
|
-
}
|
|
10425
|
-
throw error;
|
|
10426
|
-
}
|
|
10427
|
-
}
|
|
10428
|
-
async upgradeAnonymousUserWithFacebook() {
|
|
10429
|
-
try {
|
|
10430
|
-
const currentUser = this.auth.currentUser;
|
|
10431
|
-
if (!currentUser) {
|
|
10432
|
-
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10433
|
-
}
|
|
10434
|
-
if (!currentUser.isAnonymous) {
|
|
10435
|
-
throw new AuthError(
|
|
10436
|
-
"User is not anonymous",
|
|
10437
|
-
"AUTH/NOT_ANONYMOUS_USER",
|
|
10438
|
-
400
|
|
10439
|
-
);
|
|
10440
|
-
}
|
|
10441
|
-
this.facebookProvider.addScope("email");
|
|
10442
|
-
const userCredential = await (0, import_auth7.signInWithPopup)(
|
|
10443
|
-
this.auth,
|
|
10444
|
-
this.facebookProvider
|
|
10445
|
-
);
|
|
10446
|
-
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
10447
|
-
return await this.userService.upgradeAnonymousUser(
|
|
10448
|
-
currentUser.uid,
|
|
10449
|
-
userCredential.user.email
|
|
10450
|
-
);
|
|
10451
|
-
} catch (error) {
|
|
10452
|
-
const firebaseError = error;
|
|
10453
|
-
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
10454
|
-
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
10455
|
-
}
|
|
10456
|
-
throw error;
|
|
10457
|
-
}
|
|
10458
|
-
}
|
|
10459
|
-
async upgradeAnonymousUserWithApple() {
|
|
10460
|
-
try {
|
|
10461
|
-
const currentUser = this.auth.currentUser;
|
|
10462
|
-
if (!currentUser) {
|
|
10463
|
-
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10464
|
-
}
|
|
10465
|
-
if (!currentUser.isAnonymous) {
|
|
10466
|
-
throw new AuthError(
|
|
10467
|
-
"User is not anonymous",
|
|
10468
|
-
"AUTH/NOT_ANONYMOUS_USER",
|
|
10469
|
-
400
|
|
10470
|
-
);
|
|
10471
|
-
}
|
|
10472
|
-
this.appleProvider.addScope("email");
|
|
10473
|
-
this.appleProvider.addScope("name");
|
|
10474
|
-
const userCredential = await (0, import_auth7.signInWithPopup)(
|
|
10475
|
-
this.auth,
|
|
10476
|
-
this.appleProvider
|
|
10477
|
-
);
|
|
10478
|
-
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
10479
|
-
return await this.userService.upgradeAnonymousUser(
|
|
10480
|
-
currentUser.uid,
|
|
10481
|
-
userCredential.user.email
|
|
10482
|
-
);
|
|
10483
|
-
} catch (error) {
|
|
10484
|
-
const firebaseError = error;
|
|
10485
|
-
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
10486
|
-
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
10487
|
-
}
|
|
10488
|
-
throw error;
|
|
10489
|
-
}
|
|
10490
|
-
}
|
|
10491
10162
|
/**
|
|
10492
10163
|
* Šalje email za resetovanje lozinke korisniku
|
|
10493
10164
|
* @param email Email adresa korisnika
|
|
@@ -10568,11 +10239,7 @@ var AuthService = class extends BaseService {
|
|
|
10568
10239
|
await this.validateSignupData(data);
|
|
10569
10240
|
console.log("[AUTH] Creating Firebase user");
|
|
10570
10241
|
try {
|
|
10571
|
-
const result = await (0, import_auth7.createUserWithEmailAndPassword)(
|
|
10572
|
-
this.auth,
|
|
10573
|
-
data.email,
|
|
10574
|
-
data.password
|
|
10575
|
-
);
|
|
10242
|
+
const result = await (0, import_auth7.createUserWithEmailAndPassword)(this.auth, data.email, data.password);
|
|
10576
10243
|
firebaseUser = result.user;
|
|
10577
10244
|
console.log("[AUTH] Firebase user created successfully", {
|
|
10578
10245
|
uid: firebaseUser.uid
|
|
@@ -10582,64 +10249,43 @@ var AuthService = class extends BaseService {
|
|
|
10582
10249
|
throw handleFirebaseError(firebaseError);
|
|
10583
10250
|
}
|
|
10584
10251
|
console.log("[AUTH] Starting Firestore transaction");
|
|
10585
|
-
const transactionResult = await (0, import_firestore29.runTransaction)(
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
firebaseUser,
|
|
10599
|
-
["practitioner" /* PRACTITIONER */],
|
|
10600
|
-
{ skipProfileCreation: true }
|
|
10252
|
+
const transactionResult = await (0, import_firestore29.runTransaction)(this.db, async (transaction) => {
|
|
10253
|
+
console.log("[AUTH] Transaction started - creating user and practitioner");
|
|
10254
|
+
const practitionerService = new PractitionerService(this.db, this.auth, this.app);
|
|
10255
|
+
console.log("[AUTH] Creating user document");
|
|
10256
|
+
const user = await this.userService.createUser(firebaseUser, ["practitioner" /* PRACTITIONER */], {
|
|
10257
|
+
skipProfileCreation: true
|
|
10258
|
+
});
|
|
10259
|
+
let practitioner;
|
|
10260
|
+
if (data.token) {
|
|
10261
|
+
console.log("[AUTH] Claiming existing practitioner profile with token");
|
|
10262
|
+
const claimedPractitioner = await practitionerService.validateTokenAndClaimProfile(
|
|
10263
|
+
data.token,
|
|
10264
|
+
firebaseUser.uid
|
|
10601
10265
|
);
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
console.log(
|
|
10605
|
-
"[AUTH] Claiming existing practitioner profile with token"
|
|
10606
|
-
);
|
|
10607
|
-
const claimedPractitioner = await practitionerService.validateTokenAndClaimProfile(
|
|
10608
|
-
data.token,
|
|
10609
|
-
firebaseUser.uid
|
|
10610
|
-
);
|
|
10611
|
-
if (!claimedPractitioner) {
|
|
10612
|
-
throw new Error("Invalid or expired invitation token");
|
|
10613
|
-
}
|
|
10614
|
-
practitioner = claimedPractitioner;
|
|
10615
|
-
} else {
|
|
10616
|
-
console.log("[AUTH] Creating new practitioner profile");
|
|
10617
|
-
const practitionerData = buildPractitionerData(
|
|
10618
|
-
data,
|
|
10619
|
-
firebaseUser.uid
|
|
10620
|
-
);
|
|
10621
|
-
practitioner = await practitionerService.createPractitioner(
|
|
10622
|
-
practitionerData
|
|
10623
|
-
);
|
|
10266
|
+
if (!claimedPractitioner) {
|
|
10267
|
+
throw new Error("Invalid or expired invitation token");
|
|
10624
10268
|
}
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
return { user, practitioner };
|
|
10269
|
+
practitioner = claimedPractitioner;
|
|
10270
|
+
} else {
|
|
10271
|
+
console.log("[AUTH] Creating new practitioner profile");
|
|
10272
|
+
const practitionerData = buildPractitionerData(data, firebaseUser.uid);
|
|
10273
|
+
practitioner = await practitionerService.createPractitioner(practitionerData);
|
|
10631
10274
|
}
|
|
10632
|
-
|
|
10275
|
+
console.log("[AUTH] Linking practitioner to user");
|
|
10276
|
+
await this.userService.updateUser(firebaseUser.uid, {
|
|
10277
|
+
practitionerProfile: practitioner.id
|
|
10278
|
+
});
|
|
10279
|
+
console.log("[AUTH] Transaction operations completed successfully");
|
|
10280
|
+
return { user, practitioner };
|
|
10281
|
+
});
|
|
10633
10282
|
console.log("[AUTH] Atomic practitioner signup completed successfully", {
|
|
10634
10283
|
userId: transactionResult.user.uid,
|
|
10635
10284
|
practitionerId: transactionResult.practitioner.id
|
|
10636
10285
|
});
|
|
10637
10286
|
return transactionResult;
|
|
10638
10287
|
} catch (error) {
|
|
10639
|
-
console.error(
|
|
10640
|
-
"[AUTH] Atomic signup failed, initiating cleanup...",
|
|
10641
|
-
error
|
|
10642
|
-
);
|
|
10288
|
+
console.error("[AUTH] Atomic signup failed, initiating cleanup...", error);
|
|
10643
10289
|
if (firebaseUser) {
|
|
10644
10290
|
await cleanupFirebaseUser(firebaseUser);
|
|
10645
10291
|
}
|
|
@@ -10662,14 +10308,8 @@ var AuthService = class extends BaseService {
|
|
|
10662
10308
|
}
|
|
10663
10309
|
console.log("[AUTH] Email availability confirmed");
|
|
10664
10310
|
if (data.token) {
|
|
10665
|
-
const practitionerService = new PractitionerService(
|
|
10666
|
-
|
|
10667
|
-
this.auth,
|
|
10668
|
-
this.app
|
|
10669
|
-
);
|
|
10670
|
-
const isValidToken = await practitionerService.validateToken(
|
|
10671
|
-
data.token
|
|
10672
|
-
);
|
|
10311
|
+
const practitionerService = new PractitionerService(this.db, this.auth, this.app);
|
|
10312
|
+
const isValidToken = await practitionerService.validateToken(data.token);
|
|
10673
10313
|
if (!isValidToken) {
|
|
10674
10314
|
console.log("[AUTH] Invalid token provided:", data.token);
|
|
10675
10315
|
throw new Error("Invalid or expired invitation token");
|
|
@@ -10700,16 +10340,8 @@ var AuthService = class extends BaseService {
|
|
|
10700
10340
|
console.log("[AUTH] Starting practitioner signin process", {
|
|
10701
10341
|
email
|
|
10702
10342
|
});
|
|
10703
|
-
const practitionerService = new PractitionerService(
|
|
10704
|
-
|
|
10705
|
-
this.auth,
|
|
10706
|
-
this.app
|
|
10707
|
-
);
|
|
10708
|
-
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(
|
|
10709
|
-
this.auth,
|
|
10710
|
-
email,
|
|
10711
|
-
password
|
|
10712
|
-
);
|
|
10343
|
+
const practitionerService = new PractitionerService(this.db, this.auth, this.app);
|
|
10344
|
+
const { user: firebaseUser } = await (0, import_auth7.signInWithEmailAndPassword)(this.auth, email, password);
|
|
10713
10345
|
const user = await this.userService.getOrCreateUser(firebaseUser);
|
|
10714
10346
|
console.log("[AUTH] User retrieved", { uid: user.uid });
|
|
10715
10347
|
if (!((_a = user.roles) == null ? void 0 : _a.includes("practitioner" /* PRACTITIONER */))) {
|
|
@@ -10720,14 +10352,9 @@ var AuthService = class extends BaseService {
|
|
|
10720
10352
|
console.error("[AUTH] User has no practitioner profile:", user.uid);
|
|
10721
10353
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10722
10354
|
}
|
|
10723
|
-
const practitioner = await practitionerService.getPractitioner(
|
|
10724
|
-
user.practitionerProfile
|
|
10725
|
-
);
|
|
10355
|
+
const practitioner = await practitionerService.getPractitioner(user.practitionerProfile);
|
|
10726
10356
|
if (!practitioner) {
|
|
10727
|
-
console.error(
|
|
10728
|
-
"[AUTH] Practitioner profile not found:",
|
|
10729
|
-
user.practitionerProfile
|
|
10730
|
-
);
|
|
10357
|
+
console.error("[AUTH] Practitioner profile not found:", user.practitionerProfile);
|
|
10731
10358
|
throw AUTH_ERRORS.NOT_FOUND;
|
|
10732
10359
|
}
|
|
10733
10360
|
console.log("[AUTH] Practitioner signin completed successfully", {
|
|
@@ -10743,6 +10370,64 @@ var AuthService = class extends BaseService {
|
|
|
10743
10370
|
throw error;
|
|
10744
10371
|
}
|
|
10745
10372
|
}
|
|
10373
|
+
/**
|
|
10374
|
+
* Signs in a user with a Google ID token from a mobile client.
|
|
10375
|
+
* If the user does not exist, a new user is created.
|
|
10376
|
+
* @param idToken - The Google ID token obtained from the mobile app.
|
|
10377
|
+
* @param initialRole - The role to assign to the user if they are being created.
|
|
10378
|
+
* @returns The signed-in or newly created user.
|
|
10379
|
+
*/
|
|
10380
|
+
async signInWithGoogleIdToken(idToken, initialRole = "patient" /* PATIENT */) {
|
|
10381
|
+
try {
|
|
10382
|
+
console.log("[AUTH] Signing in with Google ID Token");
|
|
10383
|
+
const credential = import_auth7.GoogleAuthProvider.credential(idToken);
|
|
10384
|
+
const { user: firebaseUser } = await (0, import_auth7.signInWithCredential)(this.auth, credential);
|
|
10385
|
+
console.log("[AUTH] Firebase user signed in:", firebaseUser.uid);
|
|
10386
|
+
const existingUser = await this.userService.getUserById(firebaseUser.uid);
|
|
10387
|
+
if (existingUser) {
|
|
10388
|
+
console.log("[AUTH] Existing user found, returning profile:", existingUser.uid);
|
|
10389
|
+
return existingUser;
|
|
10390
|
+
}
|
|
10391
|
+
console.log("[AUTH] No existing user found, creating new profile for:", firebaseUser.uid);
|
|
10392
|
+
return this.userService.createUser(firebaseUser, [initialRole]);
|
|
10393
|
+
} catch (error) {
|
|
10394
|
+
console.error("[AUTH] Error in signInWithGoogleIdToken:", error);
|
|
10395
|
+
throw handleFirebaseError(error);
|
|
10396
|
+
}
|
|
10397
|
+
}
|
|
10398
|
+
/**
|
|
10399
|
+
* Links a Google account to the currently signed-in user using an ID token.
|
|
10400
|
+
* This is used to upgrade an anonymous user or to allow an existing user
|
|
10401
|
+
* to sign in with Google in the future.
|
|
10402
|
+
* @param idToken - The Google ID token obtained from the mobile app.
|
|
10403
|
+
* @returns The updated user profile.
|
|
10404
|
+
*/
|
|
10405
|
+
async linkGoogleAccount(idToken) {
|
|
10406
|
+
try {
|
|
10407
|
+
console.log("[AUTH] Linking Google account with ID Token");
|
|
10408
|
+
const currentUser = this.auth.currentUser;
|
|
10409
|
+
if (!currentUser) {
|
|
10410
|
+
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
10411
|
+
}
|
|
10412
|
+
const wasAnonymous = currentUser.isAnonymous;
|
|
10413
|
+
console.log(`[AUTH] Current user is ${wasAnonymous ? "anonymous" : "not anonymous"}`);
|
|
10414
|
+
const credential = import_auth7.GoogleAuthProvider.credential(idToken);
|
|
10415
|
+
const userCredential = await (0, import_auth7.linkWithCredential)(currentUser, credential);
|
|
10416
|
+
const linkedFirebaseUser = userCredential.user;
|
|
10417
|
+
console.log("[AUTH] Google account linked successfully to user:", linkedFirebaseUser.uid);
|
|
10418
|
+
if (wasAnonymous) {
|
|
10419
|
+
console.log("[AUTH] Upgrading anonymous user profile");
|
|
10420
|
+
return await this.userService.upgradeAnonymousUser(
|
|
10421
|
+
linkedFirebaseUser.uid,
|
|
10422
|
+
linkedFirebaseUser.email
|
|
10423
|
+
);
|
|
10424
|
+
}
|
|
10425
|
+
return await this.userService.getUserById(linkedFirebaseUser.uid);
|
|
10426
|
+
} catch (error) {
|
|
10427
|
+
console.error("[AUTH] Error in linkGoogleAccount:", error);
|
|
10428
|
+
throw handleFirebaseError(error);
|
|
10429
|
+
}
|
|
10430
|
+
}
|
|
10746
10431
|
};
|
|
10747
10432
|
|
|
10748
10433
|
// src/services/calendar/calendar.v2.service.ts
|