@blackcode_sa/metaestetics-api 1.4.17 → 1.5.0

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.
Files changed (31) hide show
  1. package/dist/index.d.mts +9633 -7023
  2. package/dist/index.d.ts +9633 -7023
  3. package/dist/index.js +2773 -150
  4. package/dist/index.mjs +2809 -150
  5. package/package.json +4 -3
  6. package/src/index.ts +48 -1
  7. package/src/services/calendar/calendar-refactored.service.ts +1531 -0
  8. package/src/services/calendar/calendar.service.ts +1077 -0
  9. package/src/services/calendar/synced-calendars.service.ts +743 -0
  10. package/src/services/calendar/utils/appointment.utils.ts +314 -0
  11. package/src/services/calendar/utils/calendar-event.utils.ts +510 -0
  12. package/src/services/calendar/utils/clinic.utils.ts +237 -0
  13. package/src/services/calendar/utils/docs.utils.ts +157 -0
  14. package/src/services/calendar/utils/google-calendar.utils.ts +697 -0
  15. package/src/services/calendar/utils/index.ts +8 -0
  16. package/src/services/calendar/utils/patient.utils.ts +198 -0
  17. package/src/services/calendar/utils/practitioner.utils.ts +221 -0
  18. package/src/services/calendar/utils/synced-calendar.utils.ts +472 -0
  19. package/src/services/clinic/clinic.service.ts +2 -2
  20. package/src/services/clinic/utils/clinic.utils.ts +49 -47
  21. package/src/services/practitioner/practitioner.service.ts +1 -0
  22. package/src/types/calendar/index.ts +187 -0
  23. package/src/types/calendar/synced-calendar.types.ts +66 -0
  24. package/src/types/clinic/index.ts +4 -15
  25. package/src/types/index.ts +4 -0
  26. package/src/types/practitioner/index.ts +21 -0
  27. package/src/types/profile/index.ts +39 -0
  28. package/src/validations/calendar.schema.ts +223 -0
  29. package/src/validations/clinic.schema.ts +3 -3
  30. package/src/validations/practitioner.schema.ts +21 -0
  31. package/src/validations/profile-info.schema.ts +41 -0
package/dist/index.mjs CHANGED
@@ -108,6 +108,31 @@ var FilledDocumentStatus = /* @__PURE__ */ ((FilledDocumentStatus2) => {
108
108
  return FilledDocumentStatus2;
109
109
  })(FilledDocumentStatus || {});
110
110
 
111
+ // src/types/calendar/index.ts
112
+ var CalendarEventStatus = /* @__PURE__ */ ((CalendarEventStatus3) => {
113
+ CalendarEventStatus3["PENDING"] = "pending";
114
+ CalendarEventStatus3["CONFIRMED"] = "confirmed";
115
+ CalendarEventStatus3["REJECTED"] = "rejected";
116
+ CalendarEventStatus3["CANCELED"] = "canceled";
117
+ CalendarEventStatus3["RESCHEDULED"] = "rescheduled";
118
+ CalendarEventStatus3["COMPLETED"] = "completed";
119
+ return CalendarEventStatus3;
120
+ })(CalendarEventStatus || {});
121
+ var CalendarSyncStatus = /* @__PURE__ */ ((CalendarSyncStatus3) => {
122
+ CalendarSyncStatus3["INTERNAL"] = "internal";
123
+ CalendarSyncStatus3["EXTERNAL"] = "external";
124
+ return CalendarSyncStatus3;
125
+ })(CalendarSyncStatus || {});
126
+ var CalendarEventType = /* @__PURE__ */ ((CalendarEventType2) => {
127
+ CalendarEventType2["APPOINTMENT"] = "appointment";
128
+ CalendarEventType2["BLOCKING"] = "blocking";
129
+ CalendarEventType2["BREAK"] = "break";
130
+ CalendarEventType2["FREE_DAY"] = "free_day";
131
+ CalendarEventType2["OTHER"] = "other";
132
+ return CalendarEventType2;
133
+ })(CalendarEventType || {});
134
+ var CALENDAR_COLLECTION = "calendar";
135
+
111
136
  // src/types/index.ts
112
137
  var UserRole = /* @__PURE__ */ ((UserRole2) => {
113
138
  UserRole2["PATIENT"] = "patient";
@@ -1276,9 +1301,9 @@ var addAllergyUtil = async (db, patientId, data, userRef) => {
1276
1301
  var updateAllergyUtil = async (db, patientId, data, userRef) => {
1277
1302
  const validatedData = updateAllergySchema.parse(data);
1278
1303
  const { allergyIndex, ...updateData } = validatedData;
1279
- const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1280
- if (!doc14.exists()) throw new Error("Medical info not found");
1281
- const medicalInfo = doc14.data();
1304
+ const doc20 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1305
+ if (!doc20.exists()) throw new Error("Medical info not found");
1306
+ const medicalInfo = doc20.data();
1282
1307
  if (allergyIndex >= medicalInfo.allergies.length) {
1283
1308
  throw new Error("Invalid allergy index");
1284
1309
  }
@@ -1294,9 +1319,9 @@ var updateAllergyUtil = async (db, patientId, data, userRef) => {
1294
1319
  });
1295
1320
  };
1296
1321
  var removeAllergyUtil = async (db, patientId, allergyIndex, userRef) => {
1297
- const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1298
- if (!doc14.exists()) throw new Error("Medical info not found");
1299
- const medicalInfo = doc14.data();
1322
+ const doc20 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1323
+ if (!doc20.exists()) throw new Error("Medical info not found");
1324
+ const medicalInfo = doc20.data();
1300
1325
  if (allergyIndex >= medicalInfo.allergies.length) {
1301
1326
  throw new Error("Invalid allergy index");
1302
1327
  }
@@ -1321,9 +1346,9 @@ var addBlockingConditionUtil = async (db, patientId, data, userRef) => {
1321
1346
  var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
1322
1347
  const validatedData = updateBlockingConditionSchema.parse(data);
1323
1348
  const { conditionIndex, ...updateData } = validatedData;
1324
- const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1325
- if (!doc14.exists()) throw new Error("Medical info not found");
1326
- const medicalInfo = doc14.data();
1349
+ const doc20 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1350
+ if (!doc20.exists()) throw new Error("Medical info not found");
1351
+ const medicalInfo = doc20.data();
1327
1352
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
1328
1353
  throw new Error("Invalid blocking condition index");
1329
1354
  }
@@ -1339,9 +1364,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
1339
1364
  });
1340
1365
  };
1341
1366
  var removeBlockingConditionUtil = async (db, patientId, conditionIndex, userRef) => {
1342
- const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1343
- if (!doc14.exists()) throw new Error("Medical info not found");
1344
- const medicalInfo = doc14.data();
1367
+ const doc20 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1368
+ if (!doc20.exists()) throw new Error("Medical info not found");
1369
+ const medicalInfo = doc20.data();
1345
1370
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
1346
1371
  throw new Error("Invalid blocking condition index");
1347
1372
  }
@@ -1366,9 +1391,9 @@ var addContraindicationUtil = async (db, patientId, data, userRef) => {
1366
1391
  var updateContraindicationUtil = async (db, patientId, data, userRef) => {
1367
1392
  const validatedData = updateContraindicationSchema.parse(data);
1368
1393
  const { contraindicationIndex, ...updateData } = validatedData;
1369
- const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1370
- if (!doc14.exists()) throw new Error("Medical info not found");
1371
- const medicalInfo = doc14.data();
1394
+ const doc20 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1395
+ if (!doc20.exists()) throw new Error("Medical info not found");
1396
+ const medicalInfo = doc20.data();
1372
1397
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
1373
1398
  throw new Error("Invalid contraindication index");
1374
1399
  }
@@ -1384,9 +1409,9 @@ var updateContraindicationUtil = async (db, patientId, data, userRef) => {
1384
1409
  });
1385
1410
  };
1386
1411
  var removeContraindicationUtil = async (db, patientId, contraindicationIndex, userRef) => {
1387
- const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1388
- if (!doc14.exists()) throw new Error("Medical info not found");
1389
- const medicalInfo = doc14.data();
1412
+ const doc20 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1413
+ if (!doc20.exists()) throw new Error("Medical info not found");
1414
+ const medicalInfo = doc20.data();
1390
1415
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
1391
1416
  throw new Error("Invalid contraindication index");
1392
1417
  }
@@ -1411,9 +1436,9 @@ var addMedicationUtil = async (db, patientId, data, userRef) => {
1411
1436
  var updateMedicationUtil = async (db, patientId, data, userRef) => {
1412
1437
  const validatedData = updateMedicationSchema.parse(data);
1413
1438
  const { medicationIndex, ...updateData } = validatedData;
1414
- const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1415
- if (!doc14.exists()) throw new Error("Medical info not found");
1416
- const medicalInfo = doc14.data();
1439
+ const doc20 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1440
+ if (!doc20.exists()) throw new Error("Medical info not found");
1441
+ const medicalInfo = doc20.data();
1417
1442
  if (medicationIndex >= medicalInfo.currentMedications.length) {
1418
1443
  throw new Error("Invalid medication index");
1419
1444
  }
@@ -1429,9 +1454,9 @@ var updateMedicationUtil = async (db, patientId, data, userRef) => {
1429
1454
  });
1430
1455
  };
1431
1456
  var removeMedicationUtil = async (db, patientId, medicationIndex, userRef) => {
1432
- const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1433
- if (!doc14.exists()) throw new Error("Medical info not found");
1434
- const medicalInfo = doc14.data();
1457
+ const doc20 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1458
+ if (!doc20.exists()) throw new Error("Medical info not found");
1459
+ const medicalInfo = doc20.data();
1435
1460
  if (medicationIndex >= medicalInfo.currentMedications.length) {
1436
1461
  throw new Error("Invalid medication index");
1437
1462
  }
@@ -2247,14 +2272,14 @@ var TreatmentBenefit = /* @__PURE__ */ ((TreatmentBenefit2) => {
2247
2272
  })(TreatmentBenefit || {});
2248
2273
 
2249
2274
  // src/backoffice/types/static/pricing.types.ts
2250
- var PricingMeasure = /* @__PURE__ */ ((PricingMeasure2) => {
2251
- PricingMeasure2["PER_ML"] = "per_ml";
2252
- PricingMeasure2["PER_ZONE"] = "per_zone";
2253
- PricingMeasure2["PER_AREA"] = "per_area";
2254
- PricingMeasure2["PER_SESSION"] = "per_session";
2255
- PricingMeasure2["PER_TREATMENT"] = "per_treatment";
2256
- PricingMeasure2["PER_PACKAGE"] = "per_package";
2257
- return PricingMeasure2;
2275
+ var PricingMeasure = /* @__PURE__ */ ((PricingMeasure3) => {
2276
+ PricingMeasure3["PER_ML"] = "per_ml";
2277
+ PricingMeasure3["PER_ZONE"] = "per_zone";
2278
+ PricingMeasure3["PER_AREA"] = "per_area";
2279
+ PricingMeasure3["PER_SESSION"] = "per_session";
2280
+ PricingMeasure3["PER_TREATMENT"] = "per_treatment";
2281
+ PricingMeasure3["PER_PACKAGE"] = "per_package";
2282
+ return PricingMeasure3;
2258
2283
  })(PricingMeasure || {});
2259
2284
  var Currency = /* @__PURE__ */ ((Currency2) => {
2260
2285
  Currency2["EUR"] = "EUR";
@@ -2434,7 +2459,7 @@ var clinicSchema = z9.object({
2434
2459
  workingHours: workingHoursSchema,
2435
2460
  tags: z9.array(z9.nativeEnum(ClinicTag)),
2436
2461
  featuredPhotos: z9.array(z9.string()),
2437
- photos: z9.array(z9.string()),
2462
+ coverPhoto: z9.string().nullable(),
2438
2463
  photosWithTags: z9.array(
2439
2464
  z9.object({
2440
2465
  url: z9.string(),
@@ -2493,7 +2518,7 @@ var createClinicSchema = z9.object({
2493
2518
  contactInfo: clinicContactInfoSchema,
2494
2519
  workingHours: workingHoursSchema,
2495
2520
  tags: z9.array(z9.nativeEnum(ClinicTag)),
2496
- photos: z9.array(z9.string()),
2521
+ coverPhoto: z9.string().nullable(),
2497
2522
  photosWithTags: z9.array(
2498
2523
  z9.object({
2499
2524
  url: z9.string(),
@@ -2554,7 +2579,7 @@ var clinicBranchSetupSchema = z9.object({
2554
2579
  workingHours: workingHoursSchema,
2555
2580
  tags: z9.array(z9.nativeEnum(ClinicTag)),
2556
2581
  logo: z9.string().optional(),
2557
- photos: z9.array(z9.string()),
2582
+ coverPhoto: z9.string().nullable(),
2558
2583
  photosWithTags: z9.array(
2559
2584
  z9.object({
2560
2585
  url: z9.string(),
@@ -2748,7 +2773,7 @@ async function getClinicAdminsByGroup(db, clinicGroupId) {
2748
2773
  where2("clinicGroupId", "==", clinicGroupId)
2749
2774
  );
2750
2775
  const querySnapshot = await getDocs2(q);
2751
- return querySnapshot.docs.map((doc14) => doc14.data());
2776
+ return querySnapshot.docs.map((doc20) => doc20.data());
2752
2777
  }
2753
2778
  async function updateClinicAdmin(db, adminId, data) {
2754
2779
  const admin = await getClinicAdmin(db, adminId);
@@ -3096,6 +3121,21 @@ var practitionerWorkingHoursSchema = z10.object({
3096
3121
  createdAt: z10.instanceof(Timestamp7),
3097
3122
  updatedAt: z10.instanceof(Timestamp7)
3098
3123
  });
3124
+ var practitionerClinicWorkingHoursSchema = z10.object({
3125
+ clinicId: z10.string().min(1),
3126
+ workingHours: z10.object({
3127
+ monday: timeSlotSchema,
3128
+ tuesday: timeSlotSchema,
3129
+ wednesday: timeSlotSchema,
3130
+ thursday: timeSlotSchema,
3131
+ friday: timeSlotSchema,
3132
+ saturday: timeSlotSchema,
3133
+ sunday: timeSlotSchema
3134
+ }),
3135
+ isActive: z10.boolean(),
3136
+ createdAt: z10.instanceof(Timestamp7),
3137
+ updatedAt: z10.instanceof(Timestamp7)
3138
+ });
3099
3139
  var practitionerReviewSchema = z10.object({
3100
3140
  id: z10.string().min(1),
3101
3141
  practitionerId: z10.string().min(1),
@@ -3121,6 +3161,7 @@ var practitionerSchema = z10.object({
3121
3161
  basicInfo: practitionerBasicInfoSchema,
3122
3162
  certification: practitionerCertificationSchema,
3123
3163
  clinics: z10.array(z10.string()),
3164
+ clinicWorkingHours: z10.array(practitionerClinicWorkingHoursSchema),
3124
3165
  isActive: z10.boolean(),
3125
3166
  isVerified: z10.boolean(),
3126
3167
  createdAt: z10.instanceof(Timestamp7),
@@ -3131,6 +3172,7 @@ var createPractitionerSchema = z10.object({
3131
3172
  basicInfo: practitionerBasicInfoSchema,
3132
3173
  certification: practitionerCertificationSchema,
3133
3174
  clinics: z10.array(z10.string()).optional(),
3175
+ clinicWorkingHours: z10.array(practitionerClinicWorkingHoursSchema).optional(),
3134
3176
  isActive: z10.boolean(),
3135
3177
  isVerified: z10.boolean()
3136
3178
  });
@@ -3180,6 +3222,7 @@ var PractitionerService = class extends BaseService {
3180
3222
  basicInfo: validatedData.basicInfo,
3181
3223
  certification: validatedData.certification,
3182
3224
  clinics: validatedData.clinics || [],
3225
+ clinicWorkingHours: validatedData.clinicWorkingHours || [],
3183
3226
  isActive: validatedData.isActive,
3184
3227
  isVerified: validatedData.isVerified,
3185
3228
  createdAt: serverTimestamp9(),
@@ -3242,7 +3285,7 @@ var PractitionerService = class extends BaseService {
3242
3285
  where3("isActive", "==", true)
3243
3286
  );
3244
3287
  const querySnapshot = await getDocs3(q);
3245
- return querySnapshot.docs.map((doc14) => doc14.data());
3288
+ return querySnapshot.docs.map((doc20) => doc20.data());
3246
3289
  }
3247
3290
  /**
3248
3291
  * Ažurira profil zdravstvenog radnika
@@ -3522,7 +3565,7 @@ var UserService = class extends BaseService {
3522
3565
  ];
3523
3566
  const q = query4(collection4(this.db, USERS_COLLECTION), ...constraints);
3524
3567
  const querySnapshot = await getDocs4(q);
3525
- const users = querySnapshot.docs.map((doc14) => doc14.data());
3568
+ const users = querySnapshot.docs.map((doc20) => doc20.data());
3526
3569
  return Promise.all(users.map((userData) => userSchema.parse(userData)));
3527
3570
  }
3528
3571
  /**
@@ -3921,7 +3964,7 @@ async function getAllActiveGroups(db) {
3921
3964
  where5("isActive", "==", true)
3922
3965
  );
3923
3966
  const querySnapshot = await getDocs5(q);
3924
- return querySnapshot.docs.map((doc14) => doc14.data());
3967
+ return querySnapshot.docs.map((doc20) => doc20.data());
3925
3968
  }
3926
3969
  async function updateClinicGroup(db, groupId, data, app) {
3927
3970
  console.log("[CLINIC_GROUP] Updating clinic group", { groupId });
@@ -4388,25 +4431,23 @@ async function createClinic(db, data, creatorAdminId, clinicGroupService, clinic
4388
4431
  console.error("[CLINIC] Error processing logo:", logoError);
4389
4432
  }
4390
4433
  }
4391
- let processedPhotos = [];
4392
- if (validatedData.photos && validatedData.photos.length > 0) {
4393
- console.log("[CLINIC] Processing regular photos");
4434
+ let processedCoverPhoto = null;
4435
+ if (validatedData.coverPhoto) {
4436
+ console.log("[CLINIC] Processing cover photo");
4394
4437
  try {
4395
- processedPhotos = await uploadMultiplePhotos(
4396
- validatedData.photos,
4438
+ processedCoverPhoto = await uploadPhoto(
4439
+ validatedData.coverPhoto,
4397
4440
  "clinics",
4398
4441
  clinicId,
4399
- "photo",
4442
+ "cover",
4400
4443
  app
4401
4444
  );
4402
- console.log("[CLINIC] Regular photos processed", {
4403
- count: processedPhotos.length
4445
+ console.log("[CLINIC] Cover photo processed", {
4446
+ coverPhoto: processedCoverPhoto
4404
4447
  });
4405
- } catch (photosError) {
4406
- console.error("[CLINIC] Error processing regular photos:", photosError);
4407
- processedPhotos = validatedData.photos.filter(
4408
- (photo) => !photo.startsWith("data:")
4409
- );
4448
+ } catch (coverPhotoError) {
4449
+ console.error("[CLINIC] Error processing cover photo:", coverPhotoError);
4450
+ processedCoverPhoto = validatedData.coverPhoto.startsWith("data:") ? null : validatedData.coverPhoto;
4410
4451
  }
4411
4452
  }
4412
4453
  let processedFeaturedPhotos = [];
@@ -4492,7 +4533,7 @@ async function createClinic(db, data, creatorAdminId, clinicGroupService, clinic
4492
4533
  logo: logoUrl || "",
4493
4534
  tags: validatedData.tags || [],
4494
4535
  featuredPhotos: processedFeaturedPhotos || [],
4495
- photos: processedPhotos || [],
4536
+ coverPhoto: processedCoverPhoto,
4496
4537
  photosWithTags: processedPhotosWithTags,
4497
4538
  doctors: [],
4498
4539
  doctorsInfo: [],
@@ -4593,7 +4634,7 @@ async function getClinicsByGroup(db, groupId) {
4593
4634
  where6("isActive", "==", true)
4594
4635
  );
4595
4636
  const querySnapshot = await getDocs6(q);
4596
- return querySnapshot.docs.map((doc14) => doc14.data());
4637
+ return querySnapshot.docs.map((doc20) => doc20.data());
4597
4638
  }
4598
4639
  async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app) {
4599
4640
  console.log("[CLINIC] Starting clinic update", { clinicId, adminId });
@@ -4647,36 +4688,32 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app
4647
4688
  console.error("[CLINIC] Error processing logo update:", logoError);
4648
4689
  }
4649
4690
  }
4650
- if (data.photos && data.photos.length > 0) {
4651
- console.log("[CLINIC] Processing regular photos update");
4691
+ if (data.coverPhoto) {
4692
+ console.log("[CLINIC] Processing cover photo update");
4652
4693
  try {
4653
- const dataUrlPhotos = data.photos.filter(
4654
- (photo) => typeof photo === "string" && photo.startsWith("data:")
4655
- );
4656
- const existingPhotos = data.photos.filter(
4657
- (photo) => typeof photo === "string" && !photo.startsWith("data:")
4658
- );
4659
- if (dataUrlPhotos.length > 0) {
4660
- const uploadedPhotos = await uploadMultiplePhotos(
4661
- dataUrlPhotos,
4694
+ if (typeof data.coverPhoto === "string" && data.coverPhoto.startsWith("data:")) {
4695
+ const uploadedPhoto = await uploadPhoto(
4696
+ data.coverPhoto,
4662
4697
  "clinics",
4663
4698
  clinicId,
4664
- "photo",
4699
+ "cover",
4665
4700
  app
4666
4701
  );
4667
- console.log("[CLINIC] Regular photos update processed", {
4668
- count: uploadedPhotos.length
4669
- });
4670
- updatedData.photos = [...existingPhotos, ...uploadedPhotos];
4702
+ if (uploadedPhoto) {
4703
+ updatedData.coverPhoto = uploadedPhoto;
4704
+ }
4705
+ } else {
4706
+ updatedData.coverPhoto = data.coverPhoto;
4671
4707
  }
4672
- } catch (photosError) {
4708
+ console.log("[CLINIC] Cover photo update processed");
4709
+ } catch (photoError) {
4673
4710
  console.error(
4674
- "[CLINIC] Error processing regular photos update:",
4675
- photosError
4676
- );
4677
- updatedData.photos = data.photos.filter(
4678
- (photo) => typeof photo === "string" && !photo.startsWith("data:")
4711
+ "[CLINIC] Error processing cover photo update:",
4712
+ photoError
4679
4713
  );
4714
+ if (typeof data.coverPhoto === "string" && !data.coverPhoto.startsWith("data:")) {
4715
+ updatedData.coverPhoto = data.coverPhoto;
4716
+ }
4680
4717
  }
4681
4718
  }
4682
4719
  if (data.featuredPhotos && data.featuredPhotos.length > 0) {
@@ -4700,6 +4737,8 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app
4700
4737
  count: uploadedPhotos.length
4701
4738
  });
4702
4739
  updatedData.featuredPhotos = [...existingPhotos, ...uploadedPhotos];
4740
+ } else {
4741
+ updatedData.featuredPhotos = existingPhotos;
4703
4742
  }
4704
4743
  } catch (featuredError) {
4705
4744
  console.error(
@@ -4807,7 +4846,7 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
4807
4846
  }
4808
4847
  const q = query6(collection6(db, CLINICS_COLLECTION), ...constraints);
4809
4848
  const querySnapshot = await getDocs6(q);
4810
- return querySnapshot.docs.map((doc14) => doc14.data());
4849
+ return querySnapshot.docs.map((doc20) => doc20.data());
4811
4850
  }
4812
4851
  async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
4813
4852
  return getClinicsByAdmin(
@@ -4960,8 +4999,8 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
4960
4999
  }
4961
5000
  const q = query7(collection8(db, CLINICS_COLLECTION), ...constraints);
4962
5001
  const querySnapshot = await getDocs7(q);
4963
- for (const doc14 of querySnapshot.docs) {
4964
- const clinic = doc14.data();
5002
+ for (const doc20 of querySnapshot.docs) {
5003
+ const clinic = doc20.data();
4965
5004
  const distance = distanceBetween(
4966
5005
  [center.latitude, center.longitude],
4967
5006
  [clinic.location.latitude, clinic.location.longitude]
@@ -5139,7 +5178,7 @@ var ClinicService = class extends BaseService {
5139
5178
  contactInfo: setupData.contactInfo,
5140
5179
  workingHours: setupData.workingHours,
5141
5180
  tags: setupData.tags,
5142
- photos: setupData.photos || [],
5181
+ coverPhoto: setupData.coverPhoto || null,
5143
5182
  photosWithTags: setupData.photosWithTags || [],
5144
5183
  doctors: [],
5145
5184
  services: [],
@@ -5152,7 +5191,7 @@ var ClinicService = class extends BaseService {
5152
5191
  console.log("[CLINIC_SERVICE] Creating clinic branch with data", {
5153
5192
  name: createClinicData.name,
5154
5193
  hasLogo: !!createClinicData.logo,
5155
- photosCount: createClinicData.photos.length,
5194
+ hasCoverPhoto: !!createClinicData.coverPhoto,
5156
5195
  featuredPhotosCount: ((_a = createClinicData.featuredPhotos) == null ? void 0 : _a.length) || 0,
5157
5196
  photosWithTagsCount: ((_b = createClinicData.photosWithTags) == null ? void 0 : _b.length) || 0
5158
5197
  });
@@ -5835,9 +5874,9 @@ var NotificationService = class extends BaseService {
5835
5874
  orderBy("notificationTime", "desc")
5836
5875
  );
5837
5876
  const querySnapshot = await getDocs9(q);
5838
- return querySnapshot.docs.map((doc14) => ({
5839
- id: doc14.id,
5840
- ...doc14.data()
5877
+ return querySnapshot.docs.map((doc20) => ({
5878
+ id: doc20.id,
5879
+ ...doc20.data()
5841
5880
  }));
5842
5881
  }
5843
5882
  /**
@@ -5851,9 +5890,9 @@ var NotificationService = class extends BaseService {
5851
5890
  orderBy("notificationTime", "desc")
5852
5891
  );
5853
5892
  const querySnapshot = await getDocs9(q);
5854
- return querySnapshot.docs.map((doc14) => ({
5855
- id: doc14.id,
5856
- ...doc14.data()
5893
+ return querySnapshot.docs.map((doc20) => ({
5894
+ id: doc20.id,
5895
+ ...doc20.data()
5857
5896
  }));
5858
5897
  }
5859
5898
  /**
@@ -5925,9 +5964,9 @@ var NotificationService = class extends BaseService {
5925
5964
  orderBy("notificationTime", "desc")
5926
5965
  );
5927
5966
  const querySnapshot = await getDocs9(q);
5928
- return querySnapshot.docs.map((doc14) => ({
5929
- id: doc14.id,
5930
- ...doc14.data()
5967
+ return querySnapshot.docs.map((doc20) => ({
5968
+ id: doc20.id,
5969
+ ...doc20.data()
5931
5970
  }));
5932
5971
  }
5933
5972
  /**
@@ -5940,9 +5979,9 @@ var NotificationService = class extends BaseService {
5940
5979
  orderBy("notificationTime", "desc")
5941
5980
  );
5942
5981
  const querySnapshot = await getDocs9(q);
5943
- return querySnapshot.docs.map((doc14) => ({
5944
- id: doc14.id,
5945
- ...doc14.data()
5982
+ return querySnapshot.docs.map((doc20) => ({
5983
+ id: doc20.id,
5984
+ ...doc20.data()
5946
5985
  }));
5947
5986
  }
5948
5987
  };
@@ -6072,9 +6111,9 @@ var DocumentationTemplateService = class extends BaseService {
6072
6111
  const querySnapshot = await getDocs10(q);
6073
6112
  const templates = [];
6074
6113
  let lastVisible = null;
6075
- querySnapshot.forEach((doc14) => {
6076
- templates.push(doc14.data());
6077
- lastVisible = doc14;
6114
+ querySnapshot.forEach((doc20) => {
6115
+ templates.push(doc20.data());
6116
+ lastVisible = doc20;
6078
6117
  });
6079
6118
  return {
6080
6119
  templates,
@@ -6102,9 +6141,9 @@ var DocumentationTemplateService = class extends BaseService {
6102
6141
  const querySnapshot = await getDocs10(q);
6103
6142
  const templates = [];
6104
6143
  let lastVisible = null;
6105
- querySnapshot.forEach((doc14) => {
6106
- templates.push(doc14.data());
6107
- lastVisible = doc14;
6144
+ querySnapshot.forEach((doc20) => {
6145
+ templates.push(doc20.data());
6146
+ lastVisible = doc20;
6108
6147
  });
6109
6148
  return {
6110
6149
  templates,
@@ -6131,9 +6170,9 @@ var DocumentationTemplateService = class extends BaseService {
6131
6170
  const querySnapshot = await getDocs10(q);
6132
6171
  const templates = [];
6133
6172
  let lastVisible = null;
6134
- querySnapshot.forEach((doc14) => {
6135
- templates.push(doc14.data());
6136
- lastVisible = doc14;
6173
+ querySnapshot.forEach((doc20) => {
6174
+ templates.push(doc20.data());
6175
+ lastVisible = doc20;
6137
6176
  });
6138
6177
  return {
6139
6178
  templates,
@@ -6258,9 +6297,9 @@ var FilledDocumentService = class extends BaseService {
6258
6297
  const querySnapshot = await getDocs11(q);
6259
6298
  const documents = [];
6260
6299
  let lastVisible = null;
6261
- querySnapshot.forEach((doc14) => {
6262
- documents.push(doc14.data());
6263
- lastVisible = doc14;
6300
+ querySnapshot.forEach((doc20) => {
6301
+ documents.push(doc20.data());
6302
+ lastVisible = doc20;
6264
6303
  });
6265
6304
  return {
6266
6305
  documents,
@@ -6287,9 +6326,9 @@ var FilledDocumentService = class extends BaseService {
6287
6326
  const querySnapshot = await getDocs11(q);
6288
6327
  const documents = [];
6289
6328
  let lastVisible = null;
6290
- querySnapshot.forEach((doc14) => {
6291
- documents.push(doc14.data());
6292
- lastVisible = doc14;
6329
+ querySnapshot.forEach((doc20) => {
6330
+ documents.push(doc20.data());
6331
+ lastVisible = doc20;
6293
6332
  });
6294
6333
  return {
6295
6334
  documents,
@@ -6316,9 +6355,9 @@ var FilledDocumentService = class extends BaseService {
6316
6355
  const querySnapshot = await getDocs11(q);
6317
6356
  const documents = [];
6318
6357
  let lastVisible = null;
6319
- querySnapshot.forEach((doc14) => {
6320
- documents.push(doc14.data());
6321
- lastVisible = doc14;
6358
+ querySnapshot.forEach((doc20) => {
6359
+ documents.push(doc20.data());
6360
+ lastVisible = doc20;
6322
6361
  });
6323
6362
  return {
6324
6363
  documents,
@@ -6345,9 +6384,9 @@ var FilledDocumentService = class extends BaseService {
6345
6384
  const querySnapshot = await getDocs11(q);
6346
6385
  const documents = [];
6347
6386
  let lastVisible = null;
6348
- querySnapshot.forEach((doc14) => {
6349
- documents.push(doc14.data());
6350
- lastVisible = doc14;
6387
+ querySnapshot.forEach((doc20) => {
6388
+ documents.push(doc20.data());
6389
+ lastVisible = doc20;
6351
6390
  });
6352
6391
  return {
6353
6392
  documents,
@@ -6374,9 +6413,9 @@ var FilledDocumentService = class extends BaseService {
6374
6413
  const querySnapshot = await getDocs11(q);
6375
6414
  const documents = [];
6376
6415
  let lastVisible = null;
6377
- querySnapshot.forEach((doc14) => {
6378
- documents.push(doc14.data());
6379
- lastVisible = doc14;
6416
+ querySnapshot.forEach((doc20) => {
6417
+ documents.push(doc20.data());
6418
+ lastVisible = doc20;
6380
6419
  });
6381
6420
  return {
6382
6421
  documents,
@@ -6385,55 +6424,2654 @@ var FilledDocumentService = class extends BaseService {
6385
6424
  }
6386
6425
  };
6387
6426
 
6388
- // src/validations/notification.schema.ts
6427
+ // src/services/calendar/calendar-refactored.service.ts
6428
+ import { Timestamp as Timestamp23, serverTimestamp as serverTimestamp18 } from "firebase/firestore";
6429
+
6430
+ // src/types/calendar/synced-calendar.types.ts
6431
+ var SyncedCalendarProvider = /* @__PURE__ */ ((SyncedCalendarProvider3) => {
6432
+ SyncedCalendarProvider3["GOOGLE"] = "google";
6433
+ SyncedCalendarProvider3["OUTLOOK"] = "outlook";
6434
+ SyncedCalendarProvider3["APPLE"] = "apple";
6435
+ return SyncedCalendarProvider3;
6436
+ })(SyncedCalendarProvider || {});
6437
+ var SYNCED_CALENDARS_COLLECTION = "syncedCalendars";
6438
+
6439
+ // src/services/calendar/calendar-refactored.service.ts
6440
+ import {
6441
+ doc as doc19,
6442
+ getDoc as getDoc22,
6443
+ collection as collection17,
6444
+ query as query16,
6445
+ where as where16,
6446
+ getDocs as getDocs16,
6447
+ setDoc as setDoc19,
6448
+ updateDoc as updateDoc20
6449
+ } from "firebase/firestore";
6450
+
6451
+ // src/validations/calendar.schema.ts
6452
+ import { z as z17 } from "zod";
6453
+ import { Timestamp as Timestamp17 } from "firebase/firestore";
6454
+
6455
+ // src/validations/profile-info.schema.ts
6389
6456
  import { z as z16 } from "zod";
6390
- var baseNotificationSchema = z16.object({
6391
- id: z16.string().optional(),
6392
- userId: z16.string(),
6393
- notificationTime: z16.any(),
6457
+ import { Timestamp as Timestamp16 } from "firebase/firestore";
6458
+ var clinicInfoSchema2 = z16.object({
6459
+ id: z16.string(),
6460
+ featuredPhoto: z16.string(),
6461
+ name: z16.string(),
6462
+ description: z16.string(),
6463
+ location: clinicLocationSchema,
6464
+ contactInfo: clinicContactInfoSchema
6465
+ });
6466
+ var practitionerProfileInfoSchema = z16.object({
6467
+ id: z16.string(),
6468
+ practitionerPhoto: z16.string().nullable(),
6469
+ name: z16.string(),
6470
+ email: z16.string().email(),
6471
+ phone: z16.string().nullable(),
6472
+ certification: practitionerCertificationSchema
6473
+ });
6474
+ var patientProfileInfoSchema = z16.object({
6475
+ id: z16.string(),
6476
+ fullName: z16.string(),
6477
+ email: z16.string().email(),
6478
+ phone: z16.string().nullable(),
6479
+ dateOfBirth: z16.instanceof(Timestamp16),
6480
+ gender: z16.nativeEnum(Gender)
6481
+ });
6482
+
6483
+ // src/validations/calendar.schema.ts
6484
+ var MIN_APPOINTMENT_DURATION = 15;
6485
+ var calendarEventTimeSchema = z17.object({
6486
+ start: z17.instanceof(Date).or(z17.instanceof(Timestamp17)),
6487
+ end: z17.instanceof(Date).or(z17.instanceof(Timestamp17))
6488
+ }).refine(
6489
+ (data) => {
6490
+ const startDate = data.start instanceof Timestamp17 ? data.start.toDate() : data.start;
6491
+ const endDate = data.end instanceof Timestamp17 ? data.end.toDate() : data.end;
6492
+ return startDate < endDate;
6493
+ },
6494
+ {
6495
+ message: "End time must be after start time",
6496
+ path: ["end"]
6497
+ }
6498
+ ).refine(
6499
+ (data) => {
6500
+ const startDate = data.start instanceof Timestamp17 ? data.start.toDate() : data.start;
6501
+ return startDate > /* @__PURE__ */ new Date();
6502
+ },
6503
+ {
6504
+ message: "Appointment must be scheduled in the future",
6505
+ path: ["start"]
6506
+ }
6507
+ );
6508
+ var timeSlotSchema2 = z17.object({
6509
+ start: z17.date(),
6510
+ end: z17.date(),
6511
+ isAvailable: z17.boolean()
6512
+ }).refine((data) => data.start < data.end, {
6513
+ message: "End time must be after start time",
6514
+ path: ["end"]
6515
+ });
6516
+ var syncedCalendarEventSchema = z17.object({
6517
+ eventId: z17.string(),
6518
+ syncedCalendarProvider: z17.nativeEnum(SyncedCalendarProvider),
6519
+ syncedAt: z17.instanceof(Date).or(z17.instanceof(Timestamp17))
6520
+ });
6521
+ var procedureInfoSchema = z17.object({
6522
+ name: z17.string(),
6523
+ description: z17.string(),
6524
+ duration: z17.number().min(MIN_APPOINTMENT_DURATION),
6525
+ price: z17.number().min(0),
6526
+ currency: z17.nativeEnum(Currency)
6527
+ });
6528
+ var procedureCategorizationSchema = z17.object({
6529
+ procedureFamily: z17.string(),
6530
+ // Replace with proper enum when available
6531
+ procedureCategory: z17.string(),
6532
+ // Replace with proper enum when available
6533
+ procedureSubcategory: z17.string(),
6534
+ // Replace with proper enum when available
6535
+ procedureTechnology: z17.string(),
6536
+ // Replace with proper enum when available
6537
+ procedureProduct: z17.string()
6538
+ // Replace with proper enum when available
6539
+ });
6540
+ var createAppointmentSchema = z17.object({
6541
+ clinicId: z17.string().min(1, "Clinic ID is required"),
6542
+ doctorId: z17.string().min(1, "Doctor ID is required"),
6543
+ patientId: z17.string().min(1, "Patient ID is required"),
6544
+ procedureId: z17.string().min(1, "Procedure ID is required"),
6545
+ eventLocation: clinicLocationSchema,
6546
+ eventTime: calendarEventTimeSchema,
6547
+ description: z17.string().optional()
6548
+ }).refine(
6549
+ (data) => {
6550
+ return true;
6551
+ },
6552
+ {
6553
+ message: "Invalid appointment parameters"
6554
+ }
6555
+ );
6556
+ var updateAppointmentSchema = z17.object({
6557
+ appointmentId: z17.string().min(1, "Appointment ID is required"),
6558
+ clinicId: z17.string().min(1, "Clinic ID is required"),
6559
+ doctorId: z17.string().min(1, "Doctor ID is required"),
6560
+ patientId: z17.string().min(1, "Patient ID is required"),
6561
+ eventTime: calendarEventTimeSchema.optional(),
6562
+ description: z17.string().optional(),
6563
+ status: z17.nativeEnum(CalendarEventStatus).optional()
6564
+ });
6565
+ var createCalendarEventSchema = z17.object({
6566
+ id: z17.string(),
6567
+ clinicBranchId: z17.string().nullable().optional(),
6568
+ clinicBranchInfo: z17.any().nullable().optional(),
6569
+ practitionerProfileId: z17.string().nullable().optional(),
6570
+ practitionerProfileInfo: practitionerProfileInfoSchema.nullable().optional(),
6571
+ patientProfileId: z17.string().nullable().optional(),
6572
+ patientProfileInfo: patientProfileInfoSchema.nullable().optional(),
6573
+ procedureId: z17.string().nullable().optional(),
6574
+ appointmentId: z17.string().nullable().optional(),
6575
+ syncedCalendarEventId: z17.array(syncedCalendarEventSchema).nullable().optional(),
6576
+ eventName: z17.string().min(1, "Event name is required"),
6577
+ eventLocation: clinicLocationSchema.optional(),
6578
+ eventTime: calendarEventTimeSchema,
6579
+ description: z17.string().optional(),
6580
+ status: z17.nativeEnum(CalendarEventStatus),
6581
+ syncStatus: z17.nativeEnum(CalendarSyncStatus),
6582
+ eventType: z17.nativeEnum(CalendarEventType),
6583
+ createdAt: z17.any(),
6584
+ // FieldValue for server timestamp
6585
+ updatedAt: z17.any()
6586
+ // FieldValue for server timestamp
6587
+ });
6588
+ var updateCalendarEventSchema = z17.object({
6589
+ syncedCalendarEventId: z17.array(syncedCalendarEventSchema).nullable().optional(),
6590
+ appointmentId: z17.string().nullable().optional(),
6591
+ eventName: z17.string().optional(),
6592
+ eventTime: calendarEventTimeSchema.optional(),
6593
+ description: z17.string().optional(),
6594
+ status: z17.nativeEnum(CalendarEventStatus).optional(),
6595
+ syncStatus: z17.nativeEnum(CalendarSyncStatus).optional(),
6596
+ eventType: z17.nativeEnum(CalendarEventType).optional(),
6597
+ updatedAt: z17.any()
6598
+ // FieldValue for server timestamp
6599
+ });
6600
+ var calendarEventSchema = z17.object({
6601
+ id: z17.string(),
6602
+ clinicBranchId: z17.string().nullable().optional(),
6603
+ clinicBranchInfo: z17.any().nullable().optional(),
6604
+ // Will be replaced with proper clinic info schema
6605
+ practitionerProfileId: z17.string().nullable().optional(),
6606
+ practitionerProfileInfo: practitionerProfileInfoSchema.nullable().optional(),
6607
+ patientProfileId: z17.string().nullable().optional(),
6608
+ patientProfileInfo: patientProfileInfoSchema.nullable().optional(),
6609
+ procedureId: z17.string().nullable().optional(),
6610
+ procedureInfo: procedureInfoSchema.nullable().optional(),
6611
+ procedureCategorization: procedureCategorizationSchema.nullable().optional(),
6612
+ appointmentId: z17.string().nullable().optional(),
6613
+ syncedCalendarEventId: z17.array(syncedCalendarEventSchema).nullable().optional(),
6614
+ eventName: z17.string(),
6615
+ eventLocation: clinicLocationSchema.optional(),
6616
+ eventTime: calendarEventTimeSchema,
6617
+ description: z17.string().optional(),
6618
+ status: z17.nativeEnum(CalendarEventStatus),
6619
+ syncStatus: z17.nativeEnum(CalendarSyncStatus),
6620
+ eventType: z17.nativeEnum(CalendarEventType),
6621
+ createdAt: z17.instanceof(Date).or(z17.instanceof(Timestamp17)),
6622
+ updatedAt: z17.instanceof(Date).or(z17.instanceof(Timestamp17))
6623
+ });
6624
+
6625
+ // src/services/calendar/utils/clinic.utils.ts
6626
+ import {
6627
+ collection as collection13,
6628
+ doc as doc15,
6629
+ getDoc as getDoc18,
6630
+ getDocs as getDocs12,
6631
+ setDoc as setDoc15,
6632
+ updateDoc as updateDoc16,
6633
+ deleteDoc as deleteDoc8,
6634
+ query as query12,
6635
+ where as where12,
6636
+ orderBy as orderBy4,
6637
+ Timestamp as Timestamp18,
6638
+ serverTimestamp as serverTimestamp14
6639
+ } from "firebase/firestore";
6640
+
6641
+ // src/services/calendar/utils/docs.utils.ts
6642
+ import { doc as doc14 } from "firebase/firestore";
6643
+ function getPractitionerCalendarEventDocRef(db, practitionerId, eventId) {
6644
+ return doc14(
6645
+ db,
6646
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
6647
+ );
6648
+ }
6649
+ function getPatientCalendarEventDocRef(db, patientId, eventId) {
6650
+ return doc14(
6651
+ db,
6652
+ `${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
6653
+ );
6654
+ }
6655
+ function getClinicCalendarEventDocRef(db, clinicId, eventId) {
6656
+ return doc14(
6657
+ db,
6658
+ `${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
6659
+ );
6660
+ }
6661
+ function getPractitionerSyncedCalendarDocRef(db, practitionerId, syncedCalendarId) {
6662
+ return doc14(
6663
+ db,
6664
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/syncedCalendars/${syncedCalendarId}`
6665
+ );
6666
+ }
6667
+ function getPatientSyncedCalendarDocRef(db, patientId, syncedCalendarId) {
6668
+ return doc14(
6669
+ db,
6670
+ `${PATIENTS_COLLECTION}/${patientId}/syncedCalendars/${syncedCalendarId}`
6671
+ );
6672
+ }
6673
+ function getClinicSyncedCalendarDocRef(db, clinicId, syncedCalendarId) {
6674
+ return doc14(
6675
+ db,
6676
+ `${CLINICS_COLLECTION}/${clinicId}/syncedCalendars/${syncedCalendarId}`
6677
+ );
6678
+ }
6679
+
6680
+ // src/services/calendar/utils/clinic.utils.ts
6681
+ async function createClinicCalendarEventUtil(db, clinicId, eventData, generateId2) {
6682
+ const eventId = generateId2();
6683
+ const eventRef = getClinicCalendarEventDocRef(db, clinicId, eventId);
6684
+ const newEvent = {
6685
+ id: eventId,
6686
+ ...eventData,
6687
+ createdAt: serverTimestamp14(),
6688
+ updatedAt: serverTimestamp14()
6689
+ };
6690
+ await setDoc15(eventRef, newEvent);
6691
+ return {
6692
+ ...newEvent,
6693
+ createdAt: Timestamp18.now(),
6694
+ updatedAt: Timestamp18.now()
6695
+ };
6696
+ }
6697
+ async function updateClinicCalendarEventUtil(db, clinicId, eventId, updateData) {
6698
+ const eventRef = getClinicCalendarEventDocRef(db, clinicId, eventId);
6699
+ const updates = {
6700
+ ...updateData,
6701
+ updatedAt: serverTimestamp14()
6702
+ };
6703
+ await updateDoc16(eventRef, updates);
6704
+ const updatedDoc = await getDoc18(eventRef);
6705
+ if (!updatedDoc.exists()) {
6706
+ throw new Error("Event not found after update");
6707
+ }
6708
+ return updatedDoc.data();
6709
+ }
6710
+ async function checkAutoConfirmAppointmentsUtil(db, clinicId) {
6711
+ const clinicDoc = await getDoc18(doc15(db, `clinics/${clinicId}`));
6712
+ if (!clinicDoc.exists()) {
6713
+ throw new Error(`Clinic with ID ${clinicId} not found`);
6714
+ }
6715
+ const clinicData = clinicDoc.data();
6716
+ const clinicGroupId = clinicData.clinicGroupId;
6717
+ if (!clinicGroupId) {
6718
+ return false;
6719
+ }
6720
+ const clinicGroupDoc = await getDoc18(
6721
+ doc15(db, `${CLINIC_GROUPS_COLLECTION}/${clinicGroupId}`)
6722
+ );
6723
+ if (!clinicGroupDoc.exists()) {
6724
+ return false;
6725
+ }
6726
+ const clinicGroupData = clinicGroupDoc.data();
6727
+ return !!clinicGroupData.autoConfirmAppointments;
6728
+ }
6729
+
6730
+ // src/services/calendar/utils/patient.utils.ts
6731
+ import {
6732
+ collection as collection14,
6733
+ getDoc as getDoc19,
6734
+ getDocs as getDocs13,
6735
+ setDoc as setDoc16,
6736
+ updateDoc as updateDoc17,
6737
+ deleteDoc as deleteDoc9,
6738
+ query as query13,
6739
+ where as where13,
6740
+ orderBy as orderBy5,
6741
+ Timestamp as Timestamp19,
6742
+ serverTimestamp as serverTimestamp15
6743
+ } from "firebase/firestore";
6744
+ async function createPatientCalendarEventUtil(db, patientId, eventData, generateId2) {
6745
+ const eventId = generateId2();
6746
+ const eventRef = getPatientCalendarEventDocRef(db, patientId, eventId);
6747
+ const newEvent = {
6748
+ id: eventId,
6749
+ ...eventData,
6750
+ createdAt: serverTimestamp15(),
6751
+ updatedAt: serverTimestamp15()
6752
+ };
6753
+ await setDoc16(eventRef, newEvent);
6754
+ return {
6755
+ ...newEvent,
6756
+ createdAt: Timestamp19.now(),
6757
+ updatedAt: Timestamp19.now()
6758
+ };
6759
+ }
6760
+ async function updatePatientCalendarEventUtil(db, patientId, eventId, updateData) {
6761
+ const eventRef = getPatientCalendarEventDocRef(db, patientId, eventId);
6762
+ const updates = {
6763
+ ...updateData,
6764
+ updatedAt: serverTimestamp15()
6765
+ };
6766
+ await updateDoc17(eventRef, updates);
6767
+ const updatedDoc = await getDoc19(eventRef);
6768
+ if (!updatedDoc.exists()) {
6769
+ throw new Error("Event not found after update");
6770
+ }
6771
+ return updatedDoc.data();
6772
+ }
6773
+
6774
+ // src/services/calendar/utils/practitioner.utils.ts
6775
+ import {
6776
+ collection as collection15,
6777
+ getDoc as getDoc20,
6778
+ getDocs as getDocs14,
6779
+ setDoc as setDoc17,
6780
+ updateDoc as updateDoc18,
6781
+ deleteDoc as deleteDoc10,
6782
+ query as query14,
6783
+ where as where14,
6784
+ orderBy as orderBy6,
6785
+ Timestamp as Timestamp20,
6786
+ serverTimestamp as serverTimestamp16
6787
+ } from "firebase/firestore";
6788
+ async function createPractitionerCalendarEventUtil(db, practitionerId, eventData, generateId2) {
6789
+ const eventId = generateId2();
6790
+ const eventRef = getPractitionerCalendarEventDocRef(
6791
+ db,
6792
+ practitionerId,
6793
+ eventId
6794
+ );
6795
+ const newEvent = {
6796
+ id: eventId,
6797
+ ...eventData,
6798
+ createdAt: serverTimestamp16(),
6799
+ updatedAt: serverTimestamp16()
6800
+ };
6801
+ await setDoc17(eventRef, newEvent);
6802
+ return {
6803
+ ...newEvent,
6804
+ createdAt: Timestamp20.now(),
6805
+ updatedAt: Timestamp20.now()
6806
+ };
6807
+ }
6808
+ async function updatePractitionerCalendarEventUtil(db, practitionerId, eventId, updateData) {
6809
+ const eventRef = getPractitionerCalendarEventDocRef(
6810
+ db,
6811
+ practitionerId,
6812
+ eventId
6813
+ );
6814
+ const updates = {
6815
+ ...updateData,
6816
+ updatedAt: serverTimestamp16()
6817
+ };
6818
+ await updateDoc18(eventRef, updates);
6819
+ const updatedDoc = await getDoc20(eventRef);
6820
+ if (!updatedDoc.exists()) {
6821
+ throw new Error("Event not found after update");
6822
+ }
6823
+ return updatedDoc.data();
6824
+ }
6825
+
6826
+ // src/services/calendar/utils/appointment.utils.ts
6827
+ async function createAppointmentUtil(db, clinicId, practitionerId, patientId, eventData, generateId2) {
6828
+ const eventId = generateId2();
6829
+ const autoConfirm = await checkAutoConfirmAppointmentsUtil(db, clinicId);
6830
+ const initialStatus = autoConfirm ? "confirmed" /* CONFIRMED */ : "pending" /* PENDING */;
6831
+ const appointmentData = {
6832
+ ...eventData,
6833
+ clinicBranchId: clinicId,
6834
+ practitionerProfileId: practitionerId,
6835
+ patientProfileId: patientId,
6836
+ eventType: "appointment" /* APPOINTMENT */,
6837
+ status: eventData.status || initialStatus
6838
+ };
6839
+ const clinicPromise = createClinicCalendarEventUtil(
6840
+ db,
6841
+ clinicId,
6842
+ appointmentData,
6843
+ () => eventId
6844
+ // Use the same ID for all calendars
6845
+ );
6846
+ const practitionerPromise = createPractitionerCalendarEventUtil(
6847
+ db,
6848
+ practitionerId,
6849
+ appointmentData,
6850
+ () => eventId
6851
+ // Use the same ID for all calendars
6852
+ );
6853
+ const patientPromise = createPatientCalendarEventUtil(
6854
+ db,
6855
+ patientId,
6856
+ appointmentData,
6857
+ () => eventId
6858
+ // Use the same ID for all calendars
6859
+ );
6860
+ const [clinicEvent] = await Promise.all([
6861
+ clinicPromise,
6862
+ practitionerPromise,
6863
+ patientPromise
6864
+ ]);
6865
+ return clinicEvent;
6866
+ }
6867
+ async function updateAppointmentUtil(db, clinicId, practitionerId, patientId, eventId, updateData) {
6868
+ const clinicPromise = updateClinicCalendarEventUtil(
6869
+ db,
6870
+ clinicId,
6871
+ eventId,
6872
+ updateData
6873
+ );
6874
+ const practitionerPromise = updatePractitionerCalendarEventUtil(
6875
+ db,
6876
+ practitionerId,
6877
+ eventId,
6878
+ updateData
6879
+ );
6880
+ const patientPromise = updatePatientCalendarEventUtil(
6881
+ db,
6882
+ patientId,
6883
+ eventId,
6884
+ updateData
6885
+ );
6886
+ const [clinicEvent] = await Promise.all([
6887
+ clinicPromise,
6888
+ practitionerPromise,
6889
+ patientPromise
6890
+ ]);
6891
+ return clinicEvent;
6892
+ }
6893
+
6894
+ // src/services/calendar/utils/synced-calendar.utils.ts
6895
+ import {
6896
+ collection as collection16,
6897
+ getDoc as getDoc21,
6898
+ getDocs as getDocs15,
6899
+ setDoc as setDoc18,
6900
+ updateDoc as updateDoc19,
6901
+ deleteDoc as deleteDoc11,
6902
+ query as query15,
6903
+ orderBy as orderBy7,
6904
+ Timestamp as Timestamp21,
6905
+ serverTimestamp as serverTimestamp17
6906
+ } from "firebase/firestore";
6907
+ async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendarData, generateId2) {
6908
+ const calendarId = generateId2();
6909
+ const calendarRef = getPractitionerSyncedCalendarDocRef(
6910
+ db,
6911
+ practitionerId,
6912
+ calendarId
6913
+ );
6914
+ const newCalendar = {
6915
+ id: calendarId,
6916
+ ...calendarData,
6917
+ createdAt: serverTimestamp17(),
6918
+ updatedAt: serverTimestamp17()
6919
+ };
6920
+ await setDoc18(calendarRef, newCalendar);
6921
+ return {
6922
+ ...newCalendar,
6923
+ createdAt: Timestamp21.now(),
6924
+ updatedAt: Timestamp21.now()
6925
+ };
6926
+ }
6927
+ async function createPatientSyncedCalendarUtil(db, patientId, calendarData, generateId2) {
6928
+ const calendarId = generateId2();
6929
+ const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
6930
+ const newCalendar = {
6931
+ id: calendarId,
6932
+ ...calendarData,
6933
+ createdAt: serverTimestamp17(),
6934
+ updatedAt: serverTimestamp17()
6935
+ };
6936
+ await setDoc18(calendarRef, newCalendar);
6937
+ return {
6938
+ ...newCalendar,
6939
+ createdAt: Timestamp21.now(),
6940
+ updatedAt: Timestamp21.now()
6941
+ };
6942
+ }
6943
+ async function createClinicSyncedCalendarUtil(db, clinicId, calendarData, generateId2) {
6944
+ const calendarId = generateId2();
6945
+ const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
6946
+ const newCalendar = {
6947
+ id: calendarId,
6948
+ ...calendarData,
6949
+ createdAt: serverTimestamp17(),
6950
+ updatedAt: serverTimestamp17()
6951
+ };
6952
+ await setDoc18(calendarRef, newCalendar);
6953
+ return {
6954
+ ...newCalendar,
6955
+ createdAt: Timestamp21.now(),
6956
+ updatedAt: Timestamp21.now()
6957
+ };
6958
+ }
6959
+ async function getPractitionerSyncedCalendarUtil(db, practitionerId, calendarId) {
6960
+ const calendarRef = getPractitionerSyncedCalendarDocRef(
6961
+ db,
6962
+ practitionerId,
6963
+ calendarId
6964
+ );
6965
+ const calendarDoc = await getDoc21(calendarRef);
6966
+ if (!calendarDoc.exists()) {
6967
+ return null;
6968
+ }
6969
+ return calendarDoc.data();
6970
+ }
6971
+ async function getPractitionerSyncedCalendarsUtil(db, practitionerId) {
6972
+ const calendarsRef = collection16(
6973
+ db,
6974
+ `practitioners/${practitionerId}/${SYNCED_CALENDARS_COLLECTION}`
6975
+ );
6976
+ const q = query15(calendarsRef, orderBy7("createdAt", "desc"));
6977
+ const querySnapshot = await getDocs15(q);
6978
+ return querySnapshot.docs.map((doc20) => doc20.data());
6979
+ }
6980
+ async function getPatientSyncedCalendarUtil(db, patientId, calendarId) {
6981
+ const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
6982
+ const calendarDoc = await getDoc21(calendarRef);
6983
+ if (!calendarDoc.exists()) {
6984
+ return null;
6985
+ }
6986
+ return calendarDoc.data();
6987
+ }
6988
+ async function getPatientSyncedCalendarsUtil(db, patientId) {
6989
+ const calendarsRef = collection16(
6990
+ db,
6991
+ `patients/${patientId}/${SYNCED_CALENDARS_COLLECTION}`
6992
+ );
6993
+ const q = query15(calendarsRef, orderBy7("createdAt", "desc"));
6994
+ const querySnapshot = await getDocs15(q);
6995
+ return querySnapshot.docs.map((doc20) => doc20.data());
6996
+ }
6997
+ async function getClinicSyncedCalendarUtil(db, clinicId, calendarId) {
6998
+ const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
6999
+ const calendarDoc = await getDoc21(calendarRef);
7000
+ if (!calendarDoc.exists()) {
7001
+ return null;
7002
+ }
7003
+ return calendarDoc.data();
7004
+ }
7005
+ async function getClinicSyncedCalendarsUtil(db, clinicId) {
7006
+ const calendarsRef = collection16(
7007
+ db,
7008
+ `clinics/${clinicId}/${SYNCED_CALENDARS_COLLECTION}`
7009
+ );
7010
+ const q = query15(calendarsRef, orderBy7("createdAt", "desc"));
7011
+ const querySnapshot = await getDocs15(q);
7012
+ return querySnapshot.docs.map((doc20) => doc20.data());
7013
+ }
7014
+ async function updatePractitionerSyncedCalendarUtil(db, practitionerId, calendarId, updateData) {
7015
+ const calendarRef = getPractitionerSyncedCalendarDocRef(
7016
+ db,
7017
+ practitionerId,
7018
+ calendarId
7019
+ );
7020
+ const updates = {
7021
+ ...updateData,
7022
+ updatedAt: serverTimestamp17()
7023
+ };
7024
+ await updateDoc19(calendarRef, updates);
7025
+ const updatedDoc = await getDoc21(calendarRef);
7026
+ if (!updatedDoc.exists()) {
7027
+ throw new Error("Synced calendar not found after update");
7028
+ }
7029
+ return updatedDoc.data();
7030
+ }
7031
+ async function updatePatientSyncedCalendarUtil(db, patientId, calendarId, updateData) {
7032
+ const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
7033
+ const updates = {
7034
+ ...updateData,
7035
+ updatedAt: serverTimestamp17()
7036
+ };
7037
+ await updateDoc19(calendarRef, updates);
7038
+ const updatedDoc = await getDoc21(calendarRef);
7039
+ if (!updatedDoc.exists()) {
7040
+ throw new Error("Synced calendar not found after update");
7041
+ }
7042
+ return updatedDoc.data();
7043
+ }
7044
+ async function updateClinicSyncedCalendarUtil(db, clinicId, calendarId, updateData) {
7045
+ const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
7046
+ const updates = {
7047
+ ...updateData,
7048
+ updatedAt: serverTimestamp17()
7049
+ };
7050
+ await updateDoc19(calendarRef, updates);
7051
+ const updatedDoc = await getDoc21(calendarRef);
7052
+ if (!updatedDoc.exists()) {
7053
+ throw new Error("Synced calendar not found after update");
7054
+ }
7055
+ return updatedDoc.data();
7056
+ }
7057
+ async function deletePractitionerSyncedCalendarUtil(db, practitionerId, calendarId) {
7058
+ const calendarRef = getPractitionerSyncedCalendarDocRef(
7059
+ db,
7060
+ practitionerId,
7061
+ calendarId
7062
+ );
7063
+ await deleteDoc11(calendarRef);
7064
+ }
7065
+ async function deletePatientSyncedCalendarUtil(db, patientId, calendarId) {
7066
+ const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
7067
+ await deleteDoc11(calendarRef);
7068
+ }
7069
+ async function deleteClinicSyncedCalendarUtil(db, clinicId, calendarId) {
7070
+ const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
7071
+ await deleteDoc11(calendarRef);
7072
+ }
7073
+ async function updateLastSyncedTimestampUtil(db, entityType, entityId, calendarId) {
7074
+ const updateData = {
7075
+ lastSyncedAt: Timestamp21.now()
7076
+ };
7077
+ switch (entityType) {
7078
+ case "practitioner":
7079
+ return updatePractitionerSyncedCalendarUtil(
7080
+ db,
7081
+ entityId,
7082
+ calendarId,
7083
+ updateData
7084
+ );
7085
+ case "patient":
7086
+ return updatePatientSyncedCalendarUtil(
7087
+ db,
7088
+ entityId,
7089
+ calendarId,
7090
+ updateData
7091
+ );
7092
+ case "clinic":
7093
+ return updateClinicSyncedCalendarUtil(
7094
+ db,
7095
+ entityId,
7096
+ calendarId,
7097
+ updateData
7098
+ );
7099
+ default:
7100
+ throw new Error(`Invalid entity type: ${entityType}`);
7101
+ }
7102
+ }
7103
+
7104
+ // src/services/calendar/utils/google-calendar.utils.ts
7105
+ import { Timestamp as Timestamp22 } from "firebase/firestore";
7106
+ var GOOGLE_CALENDAR_API_URL = "https://www.googleapis.com/calendar/v3";
7107
+ var GOOGLE_OAUTH_URL = "https://oauth2.googleapis.com/token";
7108
+ var CLIENT_ID = "your-client-id";
7109
+ var CLIENT_SECRET = "your-client-secret";
7110
+ var REDIRECT_URI = "your-redirect-uri";
7111
+ async function makeRequest(method, url, headers, data, params) {
7112
+ const queryParams = params ? "?" + Object.entries(params).map(
7113
+ ([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
7114
+ ).join("&") : "";
7115
+ const finalUrl = url + queryParams;
7116
+ const options = {
7117
+ method,
7118
+ headers,
7119
+ body: data ? JSON.stringify(data) : void 0
7120
+ };
7121
+ const response = await fetch(finalUrl, options);
7122
+ if (!response.ok) {
7123
+ const error = new Error(
7124
+ `Request failed with status ${response.status}`
7125
+ );
7126
+ error.response = response;
7127
+ throw error;
7128
+ }
7129
+ return response.json();
7130
+ }
7131
+ async function authenticateWithGoogleCalendarUtil(authCode) {
7132
+ try {
7133
+ const data = {
7134
+ code: authCode,
7135
+ client_id: CLIENT_ID,
7136
+ client_secret: CLIENT_SECRET,
7137
+ redirect_uri: REDIRECT_URI,
7138
+ grant_type: "authorization_code"
7139
+ };
7140
+ const response = await makeRequest(
7141
+ "post",
7142
+ GOOGLE_OAUTH_URL,
7143
+ { "Content-Type": "application/json" },
7144
+ data
7145
+ );
7146
+ return {
7147
+ accessToken: response.access_token,
7148
+ refreshToken: response.refresh_token,
7149
+ expiresIn: response.expires_in
7150
+ };
7151
+ } catch (error) {
7152
+ const apiError = error;
7153
+ console.error(
7154
+ "Error authenticating with Google Calendar:",
7155
+ apiError.message || "Unknown error"
7156
+ );
7157
+ throw new Error(
7158
+ `Failed to authenticate with Google Calendar: ${apiError.message || "Unknown error"}`
7159
+ );
7160
+ }
7161
+ }
7162
+ async function refreshGoogleCalendarTokenUtil(refreshToken) {
7163
+ try {
7164
+ const data = {
7165
+ refresh_token: refreshToken,
7166
+ client_id: CLIENT_ID,
7167
+ client_secret: CLIENT_SECRET,
7168
+ grant_type: "refresh_token"
7169
+ };
7170
+ const response = await makeRequest(
7171
+ "post",
7172
+ GOOGLE_OAUTH_URL,
7173
+ { "Content-Type": "application/json" },
7174
+ data
7175
+ );
7176
+ return {
7177
+ accessToken: response.access_token,
7178
+ expiresIn: response.expires_in
7179
+ };
7180
+ } catch (error) {
7181
+ const apiError = error;
7182
+ console.error(
7183
+ "Error refreshing Google Calendar token:",
7184
+ apiError.message || "Unknown error"
7185
+ );
7186
+ throw new Error(
7187
+ `Failed to refresh Google Calendar token: ${apiError.message || "Unknown error"}`
7188
+ );
7189
+ }
7190
+ }
7191
+ async function listGoogleCalendarsUtil(accessToken) {
7192
+ try {
7193
+ const response = await makeRequest(
7194
+ "get",
7195
+ `${GOOGLE_CALENDAR_API_URL}/users/me/calendarList`,
7196
+ { Authorization: `Bearer ${accessToken}` }
7197
+ );
7198
+ return response.items.map((calendar) => ({
7199
+ id: calendar.id,
7200
+ name: calendar.summary
7201
+ }));
7202
+ } catch (error) {
7203
+ const apiError = error;
7204
+ console.error(
7205
+ "Error listing Google Calendars:",
7206
+ apiError.message || "Unknown error"
7207
+ );
7208
+ throw new Error(
7209
+ `Failed to list Google Calendars: ${apiError.message || "Unknown error"}`
7210
+ );
7211
+ }
7212
+ }
7213
+ async function ensureValidToken(db, entityType, entityId, syncedCalendar) {
7214
+ const expiryTime = syncedCalendar.tokenExpiry.toDate();
7215
+ const now = /* @__PURE__ */ new Date();
7216
+ const fiveMinutesFromNow = new Date(now.getTime() + 5 * 60 * 1e3);
7217
+ if (expiryTime < fiveMinutesFromNow) {
7218
+ const { accessToken, expiresIn } = await refreshGoogleCalendarTokenUtil(
7219
+ syncedCalendar.refreshToken
7220
+ );
7221
+ const tokenExpiry = /* @__PURE__ */ new Date();
7222
+ tokenExpiry.setSeconds(tokenExpiry.getSeconds() + expiresIn);
7223
+ const updateData = {
7224
+ accessToken,
7225
+ tokenExpiry: Timestamp22.fromDate(tokenExpiry)
7226
+ };
7227
+ switch (entityType) {
7228
+ case "practitioner":
7229
+ await updatePractitionerSyncedCalendarUtil(
7230
+ db,
7231
+ entityId,
7232
+ syncedCalendar.id,
7233
+ updateData
7234
+ );
7235
+ break;
7236
+ case "patient":
7237
+ await updatePatientSyncedCalendarUtil(
7238
+ db,
7239
+ entityId,
7240
+ syncedCalendar.id,
7241
+ updateData
7242
+ );
7243
+ break;
7244
+ case "clinic":
7245
+ await updateClinicSyncedCalendarUtil(
7246
+ db,
7247
+ entityId,
7248
+ syncedCalendar.id,
7249
+ updateData
7250
+ );
7251
+ break;
7252
+ }
7253
+ return accessToken;
7254
+ }
7255
+ return syncedCalendar.accessToken;
7256
+ }
7257
+ async function syncEventsToGoogleCalendarUtil(db, entityType, entityId, syncedCalendar, events, existingSyncId) {
7258
+ var _a, _b;
7259
+ try {
7260
+ const { accessToken } = await refreshGoogleCalendarTokenUtil(
7261
+ syncedCalendar.refreshToken
7262
+ );
7263
+ let syncedCount = 0;
7264
+ const errors = [];
7265
+ const eventIds = [];
7266
+ for (const event of events) {
7267
+ try {
7268
+ if (event.syncStatus === "external" /* EXTERNAL */) {
7269
+ continue;
7270
+ }
7271
+ if (entityType === "practitioner" && event.status !== "confirmed" /* CONFIRMED */) {
7272
+ continue;
7273
+ }
7274
+ if (entityType === "patient" && (event.status === "canceled" /* CANCELED */ || event.status === "rejected" /* REJECTED */)) {
7275
+ continue;
7276
+ }
7277
+ if (entityType === "clinic") {
7278
+ continue;
7279
+ }
7280
+ const googleEvent = convertCalendarEventToGoogleEventUtil(event);
7281
+ const headers = {
7282
+ Authorization: `Bearer ${accessToken}`,
7283
+ "Content-Type": "application/json"
7284
+ };
7285
+ let responseId = "";
7286
+ if (existingSyncId) {
7287
+ const response = await makeRequest(
7288
+ "put",
7289
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${existingSyncId}`,
7290
+ headers,
7291
+ googleEvent
7292
+ );
7293
+ responseId = response.id;
7294
+ } else {
7295
+ const existingSync = (_a = event.syncedCalendarEventId) == null ? void 0 : _a.find(
7296
+ (sync) => sync.syncedCalendarProvider === "google" /* GOOGLE */ && // We should check if this is the same calendar we're syncing with, but that information isn't stored
7297
+ // For now, we'll just use the first Google Calendar sync ID
7298
+ sync.syncedCalendarProvider === syncedCalendar.provider
7299
+ );
7300
+ if (existingSync) {
7301
+ const response = await makeRequest(
7302
+ "put",
7303
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${existingSync.eventId}`,
7304
+ headers,
7305
+ googleEvent
7306
+ );
7307
+ responseId = response.id;
7308
+ } else {
7309
+ const response = await makeRequest(
7310
+ "post",
7311
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events`,
7312
+ headers,
7313
+ googleEvent
7314
+ );
7315
+ responseId = response.id;
7316
+ }
7317
+ }
7318
+ if (responseId) {
7319
+ eventIds.push(responseId);
7320
+ syncedCount++;
7321
+ }
7322
+ } catch (error) {
7323
+ const apiError = error;
7324
+ errors.push({
7325
+ eventId: event.id,
7326
+ error: apiError.message || "Unknown error",
7327
+ status: (_b = apiError.response) == null ? void 0 : _b.status
7328
+ });
7329
+ }
7330
+ }
7331
+ await updateLastSyncedTimestampUtil(
7332
+ db,
7333
+ entityType,
7334
+ entityId,
7335
+ syncedCalendar.id
7336
+ );
7337
+ return {
7338
+ success: errors.length === 0,
7339
+ syncedEvents: syncedCount,
7340
+ errors,
7341
+ eventIds
7342
+ };
7343
+ } catch (error) {
7344
+ console.error("Error syncing with Google Calendar:", error);
7345
+ return {
7346
+ success: false,
7347
+ syncedEvents: 0,
7348
+ errors: [{ error: error.message || "Unknown error" }],
7349
+ eventIds: []
7350
+ };
7351
+ }
7352
+ }
7353
+ async function fetchEventsFromGoogleCalendarUtil(db, entityType, entityId, syncedCalendar, startDate, endDate) {
7354
+ try {
7355
+ const accessToken = await ensureValidToken(
7356
+ db,
7357
+ entityType,
7358
+ entityId,
7359
+ syncedCalendar
7360
+ );
7361
+ const timeMin = startDate.toISOString();
7362
+ const timeMax = endDate.toISOString();
7363
+ const response = await makeRequest(
7364
+ "get",
7365
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events`,
7366
+ { Authorization: `Bearer ${accessToken}` },
7367
+ void 0,
7368
+ {
7369
+ timeMin,
7370
+ timeMax,
7371
+ singleEvents: "true",
7372
+ orderBy: "startTime"
7373
+ }
7374
+ );
7375
+ await updateLastSyncedTimestampUtil(
7376
+ db,
7377
+ entityType,
7378
+ entityId,
7379
+ syncedCalendar.id
7380
+ );
7381
+ return response.items;
7382
+ } catch (error) {
7383
+ const apiError = error;
7384
+ console.error(
7385
+ "Error fetching events from Google Calendar:",
7386
+ apiError.message || "Unknown error"
7387
+ );
7388
+ throw new Error(
7389
+ `Failed to fetch events from Google Calendar: ${apiError.message || "Unknown error"}`
7390
+ );
7391
+ }
7392
+ }
7393
+ function convertGoogleEventToCalendarEventUtil(googleEvent, entityId, entityType) {
7394
+ const start = googleEvent.start.dateTime ? new Date(googleEvent.start.dateTime) : new Date(googleEvent.start.date);
7395
+ const end = googleEvent.end.dateTime ? new Date(googleEvent.end.dateTime) : new Date(googleEvent.end.date);
7396
+ const calendarEvent = {
7397
+ eventName: googleEvent.summary || "External Event",
7398
+ eventLocation: googleEvent.location,
7399
+ eventTime: {
7400
+ start: Timestamp22.fromDate(start),
7401
+ end: Timestamp22.fromDate(end)
7402
+ },
7403
+ description: googleEvent.description || "",
7404
+ // External events are always set as CONFIRMED - status updates will happen externally
7405
+ status: "confirmed" /* CONFIRMED */,
7406
+ // All external events are marked as EXTERNAL to indicate they originated outside our system
7407
+ syncStatus: "external" /* EXTERNAL */,
7408
+ // All external events are treated as BLOCKING events
7409
+ eventType: "blocking" /* BLOCKING */,
7410
+ // Store the original Google Calendar event ID
7411
+ syncedCalendarEventId: [
7412
+ {
7413
+ eventId: googleEvent.id,
7414
+ syncedCalendarProvider: "google" /* GOOGLE */,
7415
+ syncedAt: Timestamp22.now()
7416
+ }
7417
+ ]
7418
+ };
7419
+ switch (entityType) {
7420
+ case "practitioner":
7421
+ calendarEvent.practitionerProfileId = entityId;
7422
+ break;
7423
+ case "patient":
7424
+ calendarEvent.patientProfileId = entityId;
7425
+ break;
7426
+ case "clinic":
7427
+ calendarEvent.clinicBranchId = entityId;
7428
+ break;
7429
+ }
7430
+ return calendarEvent;
7431
+ }
7432
+ function convertCalendarEventToGoogleEventUtil(calendarEvent) {
7433
+ const googleEvent = {
7434
+ summary: calendarEvent.eventName,
7435
+ location: calendarEvent.eventLocation,
7436
+ description: calendarEvent.description,
7437
+ start: {
7438
+ dateTime: calendarEvent.eventTime.start.toDate().toISOString(),
7439
+ timeZone: "UTC"
7440
+ },
7441
+ end: {
7442
+ dateTime: calendarEvent.eventTime.end.toDate().toISOString(),
7443
+ timeZone: "UTC"
7444
+ },
7445
+ // Add reminders
7446
+ reminders: {
7447
+ useDefault: false,
7448
+ overrides: [
7449
+ { method: "email", minutes: 24 * 60 },
7450
+ // 1 day before
7451
+ { method: "popup", minutes: 30 }
7452
+ // 30 minutes before
7453
+ ]
7454
+ }
7455
+ };
7456
+ switch (calendarEvent.status) {
7457
+ case "confirmed" /* CONFIRMED */:
7458
+ googleEvent.status = "confirmed";
7459
+ break;
7460
+ case "canceled" /* CANCELED */:
7461
+ googleEvent.status = "cancelled";
7462
+ break;
7463
+ case "pending" /* PENDING */:
7464
+ googleEvent.status = "tentative";
7465
+ break;
7466
+ default:
7467
+ googleEvent.status = "confirmed";
7468
+ }
7469
+ if (calendarEvent.eventType === "appointment" /* APPOINTMENT */) {
7470
+ googleEvent.attendees = [];
7471
+ if (calendarEvent.practitionerProfileId) {
7472
+ googleEvent.attendees.push({
7473
+ email: "practitioner@example.com",
7474
+ // This would be fetched from the practitioner profile
7475
+ displayName: "Dr. Practitioner",
7476
+ // This would be fetched from the practitioner profile
7477
+ responseStatus: "accepted"
7478
+ });
7479
+ }
7480
+ if (calendarEvent.patientProfileId) {
7481
+ googleEvent.attendees.push({
7482
+ email: "patient@example.com",
7483
+ // This would be fetched from the patient profile
7484
+ displayName: "Patient",
7485
+ // This would be fetched from the patient profile
7486
+ responseStatus: "needsAction"
7487
+ });
7488
+ }
7489
+ }
7490
+ return googleEvent;
7491
+ }
7492
+ async function deleteGoogleCalendarEventUtil(db, entityType, entityId, syncedCalendar, eventId) {
7493
+ try {
7494
+ const accessToken = await ensureValidToken(
7495
+ db,
7496
+ entityType,
7497
+ entityId,
7498
+ syncedCalendar
7499
+ );
7500
+ await makeRequest(
7501
+ "delete",
7502
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${eventId}`,
7503
+ { Authorization: `Bearer ${accessToken}` }
7504
+ );
7505
+ return true;
7506
+ } catch (error) {
7507
+ const apiError = error;
7508
+ console.error(
7509
+ "Error deleting event from Google Calendar:",
7510
+ apiError.message || "Unknown error"
7511
+ );
7512
+ throw new Error(
7513
+ `Failed to delete event from Google Calendar: ${apiError.message || "Unknown error"}`
7514
+ );
7515
+ }
7516
+ }
7517
+ function getGoogleCalendarOAuthUrlUtil(scopes = ["https://www.googleapis.com/auth/calendar"]) {
7518
+ const scopeString = encodeURIComponent(scopes.join(" "));
7519
+ return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(
7520
+ REDIRECT_URI
7521
+ )}&response_type=code&scope=${scopeString}&access_type=offline&prompt=consent`;
7522
+ }
7523
+
7524
+ // src/services/calendar/synced-calendars.service.ts
7525
+ var SyncedCalendarsService = class extends BaseService {
7526
+ /**
7527
+ * Creates a new SyncedCalendarsService instance
7528
+ * @param db - Firestore instance
7529
+ * @param auth - Firebase Auth instance
7530
+ * @param app - Firebase App instance
7531
+ */
7532
+ constructor(db, auth, app) {
7533
+ super(db, auth, app);
7534
+ }
7535
+ // ===== Practitioner Synced Calendars =====
7536
+ /**
7537
+ * Creates a synced calendar for a practitioner
7538
+ * @param practitionerId - ID of the practitioner
7539
+ * @param calendarData - Synced calendar data
7540
+ * @returns Created synced calendar
7541
+ */
7542
+ async createPractitionerSyncedCalendar(practitionerId, calendarData) {
7543
+ return createPractitionerSyncedCalendarUtil(
7544
+ this.db,
7545
+ practitionerId,
7546
+ calendarData,
7547
+ this.generateId.bind(this)
7548
+ );
7549
+ }
7550
+ /**
7551
+ * Gets a synced calendar for a practitioner
7552
+ * @param practitionerId - ID of the practitioner
7553
+ * @param calendarId - ID of the synced calendar
7554
+ * @returns Synced calendar or null if not found
7555
+ */
7556
+ async getPractitionerSyncedCalendar(practitionerId, calendarId) {
7557
+ return getPractitionerSyncedCalendarUtil(
7558
+ this.db,
7559
+ practitionerId,
7560
+ calendarId
7561
+ );
7562
+ }
7563
+ /**
7564
+ * Gets all synced calendars for a practitioner
7565
+ * @param practitionerId - ID of the practitioner
7566
+ * @returns Array of synced calendars
7567
+ */
7568
+ async getPractitionerSyncedCalendars(practitionerId) {
7569
+ return getPractitionerSyncedCalendarsUtil(this.db, practitionerId);
7570
+ }
7571
+ /**
7572
+ * Updates a synced calendar for a practitioner
7573
+ * @param practitionerId - ID of the practitioner
7574
+ * @param calendarId - ID of the synced calendar
7575
+ * @param updateData - Data to update
7576
+ * @returns Updated synced calendar
7577
+ */
7578
+ async updatePractitionerSyncedCalendar(practitionerId, calendarId, updateData) {
7579
+ return updatePractitionerSyncedCalendarUtil(
7580
+ this.db,
7581
+ practitionerId,
7582
+ calendarId,
7583
+ updateData
7584
+ );
7585
+ }
7586
+ /**
7587
+ * Deletes a synced calendar for a practitioner
7588
+ * @param practitionerId - ID of the practitioner
7589
+ * @param calendarId - ID of the synced calendar
7590
+ */
7591
+ async deletePractitionerSyncedCalendar(practitionerId, calendarId) {
7592
+ return deletePractitionerSyncedCalendarUtil(
7593
+ this.db,
7594
+ practitionerId,
7595
+ calendarId
7596
+ );
7597
+ }
7598
+ // ===== Patient Synced Calendars =====
7599
+ /**
7600
+ * Creates a synced calendar for a patient
7601
+ * @param patientId - ID of the patient
7602
+ * @param calendarData - Synced calendar data
7603
+ * @returns Created synced calendar
7604
+ */
7605
+ async createPatientSyncedCalendar(patientId, calendarData) {
7606
+ return createPatientSyncedCalendarUtil(
7607
+ this.db,
7608
+ patientId,
7609
+ calendarData,
7610
+ this.generateId.bind(this)
7611
+ );
7612
+ }
7613
+ /**
7614
+ * Gets a synced calendar for a patient
7615
+ * @param patientId - ID of the patient
7616
+ * @param calendarId - ID of the synced calendar
7617
+ * @returns Synced calendar or null if not found
7618
+ */
7619
+ async getPatientSyncedCalendar(patientId, calendarId) {
7620
+ return getPatientSyncedCalendarUtil(this.db, patientId, calendarId);
7621
+ }
7622
+ /**
7623
+ * Gets all synced calendars for a patient
7624
+ * @param patientId - ID of the patient
7625
+ * @returns Array of synced calendars
7626
+ */
7627
+ async getPatientSyncedCalendars(patientId) {
7628
+ return getPatientSyncedCalendarsUtil(this.db, patientId);
7629
+ }
7630
+ /**
7631
+ * Updates a synced calendar for a patient
7632
+ * @param patientId - ID of the patient
7633
+ * @param calendarId - ID of the synced calendar
7634
+ * @param updateData - Data to update
7635
+ * @returns Updated synced calendar
7636
+ */
7637
+ async updatePatientSyncedCalendar(patientId, calendarId, updateData) {
7638
+ return updatePatientSyncedCalendarUtil(
7639
+ this.db,
7640
+ patientId,
7641
+ calendarId,
7642
+ updateData
7643
+ );
7644
+ }
7645
+ /**
7646
+ * Deletes a synced calendar for a patient
7647
+ * @param patientId - ID of the patient
7648
+ * @param calendarId - ID of the synced calendar
7649
+ */
7650
+ async deletePatientSyncedCalendar(patientId, calendarId) {
7651
+ return deletePatientSyncedCalendarUtil(this.db, patientId, calendarId);
7652
+ }
7653
+ // ===== Clinic Synced Calendars =====
7654
+ /**
7655
+ * Creates a synced calendar for a clinic
7656
+ * @param clinicId - ID of the clinic
7657
+ * @param calendarData - Synced calendar data
7658
+ * @returns Created synced calendar
7659
+ */
7660
+ async createClinicSyncedCalendar(clinicId, calendarData) {
7661
+ return createClinicSyncedCalendarUtil(
7662
+ this.db,
7663
+ clinicId,
7664
+ calendarData,
7665
+ this.generateId.bind(this)
7666
+ );
7667
+ }
7668
+ /**
7669
+ * Gets a synced calendar for a clinic
7670
+ * @param clinicId - ID of the clinic
7671
+ * @param calendarId - ID of the synced calendar
7672
+ * @returns Synced calendar or null if not found
7673
+ */
7674
+ async getClinicSyncedCalendar(clinicId, calendarId) {
7675
+ return getClinicSyncedCalendarUtil(this.db, clinicId, calendarId);
7676
+ }
7677
+ /**
7678
+ * Gets all synced calendars for a clinic
7679
+ * @param clinicId - ID of the clinic
7680
+ * @returns Array of synced calendars
7681
+ */
7682
+ async getClinicSyncedCalendars(clinicId) {
7683
+ return getClinicSyncedCalendarsUtil(this.db, clinicId);
7684
+ }
7685
+ /**
7686
+ * Updates a synced calendar for a clinic
7687
+ * @param clinicId - ID of the clinic
7688
+ * @param calendarId - ID of the synced calendar
7689
+ * @param updateData - Data to update
7690
+ * @returns Updated synced calendar
7691
+ */
7692
+ async updateClinicSyncedCalendar(clinicId, calendarId, updateData) {
7693
+ return updateClinicSyncedCalendarUtil(
7694
+ this.db,
7695
+ clinicId,
7696
+ calendarId,
7697
+ updateData
7698
+ );
7699
+ }
7700
+ /**
7701
+ * Deletes a synced calendar for a clinic
7702
+ * @param clinicId - ID of the clinic
7703
+ * @param calendarId - ID of the synced calendar
7704
+ */
7705
+ async deleteClinicSyncedCalendar(clinicId, calendarId) {
7706
+ return deleteClinicSyncedCalendarUtil(this.db, clinicId, calendarId);
7707
+ }
7708
+ // ===== Google Calendar Integration =====
7709
+ /**
7710
+ * Gets the OAuth URL for Google Calendar
7711
+ * @param scopes - OAuth scopes to request
7712
+ * @returns OAuth URL
7713
+ */
7714
+ getGoogleCalendarOAuthUrl(scopes = ["https://www.googleapis.com/auth/calendar"]) {
7715
+ return getGoogleCalendarOAuthUrlUtil(scopes);
7716
+ }
7717
+ /**
7718
+ * Authenticates with Google Calendar using an authorization code
7719
+ * @param authCode - Authorization code from Google OAuth
7720
+ * @returns Access token, refresh token, and expiration time
7721
+ */
7722
+ async authenticateWithGoogleCalendar(authCode) {
7723
+ return authenticateWithGoogleCalendarUtil(authCode);
7724
+ }
7725
+ /**
7726
+ * Lists available Google Calendars for a user
7727
+ * @param accessToken - Google API access token
7728
+ * @returns List of available calendars
7729
+ */
7730
+ async listGoogleCalendars(accessToken) {
7731
+ return listGoogleCalendarsUtil(accessToken);
7732
+ }
7733
+ /**
7734
+ * Syncs events from our system to Google Calendar for a practitioner
7735
+ * @param practitionerId - ID of the practitioner
7736
+ * @param calendarId - ID of the synced calendar
7737
+ * @param events - Events to sync
7738
+ * @param existingSyncId - Optional existing sync ID for updating an event
7739
+ * @returns Result of the sync operation
7740
+ */
7741
+ async syncPractitionerEventsToGoogleCalendar(practitionerId, calendarId, events, existingSyncId) {
7742
+ const syncedCalendar = await this.getPractitionerSyncedCalendar(
7743
+ practitionerId,
7744
+ calendarId
7745
+ );
7746
+ if (!syncedCalendar) {
7747
+ throw new Error("Synced calendar not found");
7748
+ }
7749
+ return syncEventsToGoogleCalendarUtil(
7750
+ this.db,
7751
+ "practitioner",
7752
+ practitionerId,
7753
+ syncedCalendar,
7754
+ events,
7755
+ existingSyncId
7756
+ );
7757
+ }
7758
+ /**
7759
+ * Syncs events from our system to Google Calendar for a patient
7760
+ * @param patientId - ID of the patient
7761
+ * @param calendarId - ID of the synced calendar
7762
+ * @param events - Events to sync
7763
+ * @param existingSyncId - Optional existing sync ID for updating an event
7764
+ * @returns Result of the sync operation
7765
+ */
7766
+ async syncPatientEventsToGoogleCalendar(patientId, calendarId, events, existingSyncId) {
7767
+ const syncedCalendar = await this.getPatientSyncedCalendar(
7768
+ patientId,
7769
+ calendarId
7770
+ );
7771
+ if (!syncedCalendar) {
7772
+ throw new Error("Synced calendar not found");
7773
+ }
7774
+ return syncEventsToGoogleCalendarUtil(
7775
+ this.db,
7776
+ "patient",
7777
+ patientId,
7778
+ syncedCalendar,
7779
+ events,
7780
+ existingSyncId
7781
+ );
7782
+ }
7783
+ /**
7784
+ * Syncs events from our system to Google Calendar for a clinic
7785
+ * @param clinicId - ID of the clinic
7786
+ * @param calendarId - ID of the synced calendar
7787
+ * @param events - Events to sync
7788
+ * @returns Result of the sync operation
7789
+ */
7790
+ async syncClinicEventsToGoogleCalendar(clinicId, calendarId, events) {
7791
+ const syncedCalendar = await this.getClinicSyncedCalendar(
7792
+ clinicId,
7793
+ calendarId
7794
+ );
7795
+ if (!syncedCalendar) {
7796
+ throw new Error("Synced calendar not found");
7797
+ }
7798
+ return syncEventsToGoogleCalendarUtil(
7799
+ this.db,
7800
+ "clinic",
7801
+ clinicId,
7802
+ syncedCalendar,
7803
+ events
7804
+ );
7805
+ }
7806
+ /**
7807
+ * Fetches events from Google Calendar for a practitioner
7808
+ * @param practitionerId - ID of the practitioner
7809
+ * @param calendarId - ID of the synced calendar
7810
+ * @param startDate - Start date for fetching events
7811
+ * @param endDate - End date for fetching events
7812
+ * @returns Events fetched from Google Calendar
7813
+ */
7814
+ async fetchEventsFromPractitionerGoogleCalendar(practitionerId, calendarId, startDate, endDate) {
7815
+ const syncedCalendar = await this.getPractitionerSyncedCalendar(
7816
+ practitionerId,
7817
+ calendarId
7818
+ );
7819
+ if (!syncedCalendar) {
7820
+ throw new Error("Synced calendar not found");
7821
+ }
7822
+ return fetchEventsFromGoogleCalendarUtil(
7823
+ this.db,
7824
+ "practitioner",
7825
+ practitionerId,
7826
+ syncedCalendar,
7827
+ startDate,
7828
+ endDate
7829
+ );
7830
+ }
7831
+ /**
7832
+ * Fetches events from Google Calendar for a patient
7833
+ * @param patientId - ID of the patient
7834
+ * @param calendarId - ID of the synced calendar
7835
+ * @param startDate - Start date for fetching events
7836
+ * @param endDate - End date for fetching events
7837
+ * @returns Events fetched from Google Calendar
7838
+ */
7839
+ async fetchEventsFromPatientGoogleCalendar(patientId, calendarId, startDate, endDate) {
7840
+ const syncedCalendar = await this.getPatientSyncedCalendar(
7841
+ patientId,
7842
+ calendarId
7843
+ );
7844
+ if (!syncedCalendar) {
7845
+ throw new Error("Synced calendar not found");
7846
+ }
7847
+ return fetchEventsFromGoogleCalendarUtil(
7848
+ this.db,
7849
+ "patient",
7850
+ patientId,
7851
+ syncedCalendar,
7852
+ startDate,
7853
+ endDate
7854
+ );
7855
+ }
7856
+ /**
7857
+ * Fetches events from Google Calendar for a clinic
7858
+ * @param clinicId - ID of the clinic
7859
+ * @param calendarId - ID of the synced calendar
7860
+ * @param startDate - Start date for fetching events
7861
+ * @param endDate - End date for fetching events
7862
+ * @returns Events fetched from Google Calendar
7863
+ */
7864
+ async fetchEventsFromClinicGoogleCalendar(clinicId, calendarId, startDate, endDate) {
7865
+ const syncedCalendar = await this.getClinicSyncedCalendar(
7866
+ clinicId,
7867
+ calendarId
7868
+ );
7869
+ if (!syncedCalendar) {
7870
+ throw new Error("Synced calendar not found");
7871
+ }
7872
+ return fetchEventsFromGoogleCalendarUtil(
7873
+ this.db,
7874
+ "clinic",
7875
+ clinicId,
7876
+ syncedCalendar,
7877
+ startDate,
7878
+ endDate
7879
+ );
7880
+ }
7881
+ /**
7882
+ * Deletes an event from Google Calendar for a practitioner
7883
+ * @param practitionerId - ID of the practitioner
7884
+ * @param calendarId - ID of the synced calendar
7885
+ * @param eventId - ID of the event in Google Calendar
7886
+ * @returns Success status
7887
+ */
7888
+ async deletePractitionerGoogleCalendarEvent(practitionerId, calendarId, eventId) {
7889
+ const syncedCalendar = await this.getPractitionerSyncedCalendar(
7890
+ practitionerId,
7891
+ calendarId
7892
+ );
7893
+ if (!syncedCalendar) {
7894
+ throw new Error("Synced calendar not found");
7895
+ }
7896
+ return deleteGoogleCalendarEventUtil(
7897
+ this.db,
7898
+ "practitioner",
7899
+ practitionerId,
7900
+ syncedCalendar,
7901
+ eventId
7902
+ );
7903
+ }
7904
+ /**
7905
+ * Deletes an event from Google Calendar for a patient
7906
+ * @param patientId - ID of the patient
7907
+ * @param calendarId - ID of the synced calendar
7908
+ * @param eventId - ID of the event in Google Calendar
7909
+ * @returns Success status
7910
+ */
7911
+ async deletePatientGoogleCalendarEvent(patientId, calendarId, eventId) {
7912
+ const syncedCalendar = await this.getPatientSyncedCalendar(
7913
+ patientId,
7914
+ calendarId
7915
+ );
7916
+ if (!syncedCalendar) {
7917
+ throw new Error("Synced calendar not found");
7918
+ }
7919
+ return deleteGoogleCalendarEventUtil(
7920
+ this.db,
7921
+ "patient",
7922
+ patientId,
7923
+ syncedCalendar,
7924
+ eventId
7925
+ );
7926
+ }
7927
+ /**
7928
+ * Deletes an event from Google Calendar for a clinic
7929
+ * @param clinicId - ID of the clinic
7930
+ * @param calendarId - ID of the synced calendar
7931
+ * @param eventId - ID of the event in Google Calendar
7932
+ * @returns Success status
7933
+ */
7934
+ async deleteClinicGoogleCalendarEvent(clinicId, calendarId, eventId) {
7935
+ const syncedCalendar = await this.getClinicSyncedCalendar(
7936
+ clinicId,
7937
+ calendarId
7938
+ );
7939
+ if (!syncedCalendar) {
7940
+ throw new Error("Synced calendar not found");
7941
+ }
7942
+ return deleteGoogleCalendarEventUtil(
7943
+ this.db,
7944
+ "clinic",
7945
+ clinicId,
7946
+ syncedCalendar,
7947
+ eventId
7948
+ );
7949
+ }
7950
+ /**
7951
+ * Converts Google Calendar events to our system's format for a practitioner
7952
+ * @param practitionerId - ID of the practitioner
7953
+ * @param googleEvents - Google Calendar events
7954
+ * @returns Converted calendar events
7955
+ */
7956
+ convertGoogleEventsToPractitionerEvents(practitionerId, googleEvents) {
7957
+ return googleEvents.map(
7958
+ (event) => convertGoogleEventToCalendarEventUtil(
7959
+ event,
7960
+ practitionerId,
7961
+ "practitioner"
7962
+ )
7963
+ );
7964
+ }
7965
+ /**
7966
+ * Converts Google Calendar events to our system's format for a patient
7967
+ * @param patientId - ID of the patient
7968
+ * @param googleEvents - Google Calendar events
7969
+ * @returns Converted calendar events
7970
+ */
7971
+ convertGoogleEventsToPatientEvents(patientId, googleEvents) {
7972
+ return googleEvents.map(
7973
+ (event) => convertGoogleEventToCalendarEventUtil(event, patientId, "patient")
7974
+ );
7975
+ }
7976
+ /**
7977
+ * Converts Google Calendar events to our system's format for a clinic
7978
+ * @param clinicId - ID of the clinic
7979
+ * @param googleEvents - Google Calendar events
7980
+ * @returns Converted calendar events
7981
+ */
7982
+ convertGoogleEventsToClinicEvents(clinicId, googleEvents) {
7983
+ return googleEvents.map(
7984
+ (event) => convertGoogleEventToCalendarEventUtil(event, clinicId, "clinic")
7985
+ );
7986
+ }
7987
+ /**
7988
+ * Fetches a single event from Google Calendar for a practitioner
7989
+ * @param practitionerId - ID of the practitioner
7990
+ * @param calendarId - ID of the synced calendar
7991
+ * @param eventId - ID of the event in Google Calendar
7992
+ * @returns The event data or null if not found
7993
+ */
7994
+ async fetchEventFromPractitionerGoogleCalendar(practitionerId, calendarId, eventId) {
7995
+ var _a;
7996
+ const syncedCalendar = await this.getPractitionerSyncedCalendar(
7997
+ practitionerId,
7998
+ calendarId
7999
+ );
8000
+ if (!syncedCalendar) {
8001
+ throw new Error("Synced calendar not found");
8002
+ }
8003
+ try {
8004
+ const { accessToken } = await refreshGoogleCalendarTokenUtil(
8005
+ syncedCalendar.refreshToken
8006
+ );
8007
+ const response = await makeRequest(
8008
+ "get",
8009
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${eventId}`,
8010
+ { Authorization: `Bearer ${accessToken}` }
8011
+ );
8012
+ await updateLastSyncedTimestampUtil(
8013
+ this.db,
8014
+ "practitioner",
8015
+ practitionerId,
8016
+ syncedCalendar.id
8017
+ );
8018
+ return response;
8019
+ } catch (error) {
8020
+ if (((_a = error.response) == null ? void 0 : _a.status) === 404) {
8021
+ return null;
8022
+ }
8023
+ console.error(
8024
+ `Error fetching event from Google Calendar: ${error.message}`
8025
+ );
8026
+ throw error;
8027
+ }
8028
+ }
8029
+ };
8030
+
8031
+ // src/services/calendar/calendar-refactored.service.ts
8032
+ var MIN_APPOINTMENT_DURATION2 = 15;
8033
+ var CalendarServiceV2 = class extends BaseService {
8034
+ /**
8035
+ * Creates a new CalendarService instance
8036
+ * @param db - Firestore instance
8037
+ * @param auth - Firebase Auth instance
8038
+ * @param app - Firebase App instance
8039
+ */
8040
+ constructor(db, auth, app) {
8041
+ super(db, auth, app);
8042
+ this.syncedCalendarsService = new SyncedCalendarsService(db, auth, app);
8043
+ }
8044
+ // #region Public API Methods
8045
+ /**
8046
+ * Creates a new appointment with proper validation and scheduling rules
8047
+ * @param params - Appointment creation parameters
8048
+ * @returns Created calendar event
8049
+ */
8050
+ async createAppointment(params) {
8051
+ await this.validateAppointmentParams(params);
8052
+ await this.validateClinicWorkingHours(params.clinicId, params.eventTime);
8053
+ await this.validateDoctorAvailability(
8054
+ params.doctorId,
8055
+ params.eventTime,
8056
+ params.clinicId
8057
+ );
8058
+ const { clinicInfo, practitionerInfo, patientInfo } = await this.fetchProfileInfoCards(
8059
+ params.clinicId,
8060
+ params.doctorId,
8061
+ params.patientId
8062
+ );
8063
+ const appointmentData = {
8064
+ clinicBranchId: params.clinicId,
8065
+ clinicBranchInfo: clinicInfo,
8066
+ practitionerProfileId: params.doctorId,
8067
+ practitionerProfileInfo: practitionerInfo,
8068
+ patientProfileId: params.patientId,
8069
+ patientProfileInfo: patientInfo,
8070
+ procedureId: params.procedureId,
8071
+ eventLocation: params.eventLocation,
8072
+ eventName: "Appointment",
8073
+ // TODO: Add procedure name when procedure model is available
8074
+ eventTime: params.eventTime,
8075
+ description: params.description || "",
8076
+ status: "pending" /* PENDING */,
8077
+ syncStatus: "internal" /* INTERNAL */,
8078
+ eventType: "appointment" /* APPOINTMENT */
8079
+ };
8080
+ const appointment = await createAppointmentUtil(
8081
+ this.db,
8082
+ params.clinicId,
8083
+ params.doctorId,
8084
+ params.patientId,
8085
+ appointmentData,
8086
+ this.generateId.bind(this)
8087
+ );
8088
+ await this.syncAppointmentWithExternalCalendars(appointment);
8089
+ return appointment;
8090
+ }
8091
+ /**
8092
+ * Updates an existing appointment
8093
+ * @param params - Appointment update parameters
8094
+ * @returns Updated calendar event
8095
+ */
8096
+ async updateAppointment(params) {
8097
+ await this.validateUpdatePermissions(params);
8098
+ const updateData = {
8099
+ eventTime: params.eventTime,
8100
+ description: params.description,
8101
+ status: params.status
8102
+ };
8103
+ const appointment = await updateAppointmentUtil(
8104
+ this.db,
8105
+ params.clinicId,
8106
+ params.doctorId,
8107
+ params.patientId,
8108
+ params.appointmentId,
8109
+ updateData
8110
+ );
8111
+ await this.syncAppointmentWithExternalCalendars(appointment);
8112
+ return appointment;
8113
+ }
8114
+ /**
8115
+ * Gets available appointment slots for a doctor at a clinic
8116
+ * @param clinicId - ID of the clinic
8117
+ * @param doctorId - ID of the doctor
8118
+ * @param date - Date to check availability for
8119
+ * @returns Array of available time slots
8120
+ */
8121
+ async getAvailableSlots(clinicId, doctorId, date) {
8122
+ const workingHours = await this.getClinicWorkingHours(clinicId, date);
8123
+ const doctorSchedule = await this.getDoctorSchedule(doctorId, date);
8124
+ const existingAppointments = await this.getDoctorAppointments(
8125
+ doctorId,
8126
+ date
8127
+ );
8128
+ return this.calculateAvailableSlots(
8129
+ workingHours,
8130
+ doctorSchedule,
8131
+ existingAppointments
8132
+ );
8133
+ }
8134
+ /**
8135
+ * Confirms an appointment
8136
+ * @param appointmentId - ID of the appointment
8137
+ * @param clinicId - ID of the clinic
8138
+ * @returns Confirmed calendar event
8139
+ */
8140
+ async confirmAppointment(appointmentId, clinicId) {
8141
+ return this.updateAppointmentStatus(
8142
+ appointmentId,
8143
+ clinicId,
8144
+ "confirmed" /* CONFIRMED */
8145
+ );
8146
+ }
8147
+ /**
8148
+ * Rejects an appointment
8149
+ * @param appointmentId - ID of the appointment
8150
+ * @param clinicId - ID of the clinic
8151
+ * @returns Rejected calendar event
8152
+ */
8153
+ async rejectAppointment(appointmentId, clinicId) {
8154
+ return this.updateAppointmentStatus(
8155
+ appointmentId,
8156
+ clinicId,
8157
+ "rejected" /* REJECTED */
8158
+ );
8159
+ }
8160
+ /**
8161
+ * Cancels an appointment
8162
+ * @param appointmentId - ID of the appointment
8163
+ * @param clinicId - ID of the clinic
8164
+ * @returns Canceled calendar event
8165
+ */
8166
+ async cancelAppointment(appointmentId, clinicId) {
8167
+ return this.updateAppointmentStatus(
8168
+ appointmentId,
8169
+ clinicId,
8170
+ "canceled" /* CANCELED */
8171
+ );
8172
+ }
8173
+ /**
8174
+ * Imports events from external calendars
8175
+ * @param entityType - Type of entity (practitioner or patient)
8176
+ * @param entityId - ID of the entity
8177
+ * @param startDate - Start date for fetching events
8178
+ * @param endDate - End date for fetching events
8179
+ * @returns Number of events imported
8180
+ */
8181
+ async importEventsFromExternalCalendars(entityType, entityId, startDate, endDate) {
8182
+ if (entityType === "patient") {
8183
+ return 0;
8184
+ }
8185
+ const syncedCalendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
8186
+ entityId
8187
+ );
8188
+ const activeCalendars = syncedCalendars.filter((cal) => cal.isActive);
8189
+ if (activeCalendars.length === 0) {
8190
+ return 0;
8191
+ }
8192
+ let importedEventsCount = 0;
8193
+ const currentTime = Timestamp23.now();
8194
+ for (const calendar of activeCalendars) {
8195
+ try {
8196
+ let externalEvents = [];
8197
+ if (calendar.provider === "google" /* GOOGLE */) {
8198
+ externalEvents = await this.syncedCalendarsService.fetchEventsFromPractitionerGoogleCalendar(
8199
+ entityId,
8200
+ calendar.id,
8201
+ startDate,
8202
+ endDate
8203
+ );
8204
+ }
8205
+ for (const externalEvent of externalEvents) {
8206
+ try {
8207
+ const convertedEvent = this.syncedCalendarsService.convertGoogleEventsToPractitionerEvents(
8208
+ entityId,
8209
+ [externalEvent]
8210
+ )[0];
8211
+ if (!convertedEvent.eventTime) {
8212
+ continue;
8213
+ }
8214
+ const eventData = {
8215
+ // Ensure all required fields are set
8216
+ eventName: convertedEvent.eventName || "External Event",
8217
+ eventTime: convertedEvent.eventTime,
8218
+ description: convertedEvent.description || "",
8219
+ status: "confirmed" /* CONFIRMED */,
8220
+ syncStatus: "external" /* EXTERNAL */,
8221
+ eventType: "blocking" /* BLOCKING */,
8222
+ practitionerProfileId: entityId,
8223
+ syncedCalendarEventId: [
8224
+ {
8225
+ eventId: externalEvent.id,
8226
+ syncedCalendarProvider: calendar.provider,
8227
+ syncedAt: currentTime
8228
+ }
8229
+ ]
8230
+ };
8231
+ const doctorEvent = await this.createDoctorBlockingEvent(
8232
+ entityId,
8233
+ eventData
8234
+ );
8235
+ if (doctorEvent) {
8236
+ importedEventsCount++;
8237
+ }
8238
+ } catch (eventError) {
8239
+ console.error("Error importing event:", eventError);
8240
+ }
8241
+ }
8242
+ } catch (calendarError) {
8243
+ console.error(
8244
+ `Error fetching events from calendar ${calendar.id}:`,
8245
+ calendarError
8246
+ );
8247
+ }
8248
+ }
8249
+ return importedEventsCount;
8250
+ }
8251
+ /**
8252
+ * Creates a blocking event in a doctor's calendar
8253
+ * @param doctorId - ID of the doctor
8254
+ * @param eventData - Calendar event data
8255
+ * @returns Created calendar event
8256
+ */
8257
+ async createDoctorBlockingEvent(doctorId, eventData) {
8258
+ try {
8259
+ const eventId = this.generateId();
8260
+ const eventRef = doc19(
8261
+ this.db,
8262
+ PRACTITIONERS_COLLECTION,
8263
+ doctorId,
8264
+ CALENDAR_COLLECTION,
8265
+ eventId
8266
+ );
8267
+ const newEvent = {
8268
+ id: eventId,
8269
+ ...eventData,
8270
+ createdAt: serverTimestamp18(),
8271
+ updatedAt: serverTimestamp18()
8272
+ };
8273
+ await setDoc19(eventRef, newEvent);
8274
+ return {
8275
+ ...newEvent,
8276
+ createdAt: Timestamp23.now(),
8277
+ updatedAt: Timestamp23.now()
8278
+ };
8279
+ } catch (error) {
8280
+ console.error(
8281
+ `Error creating blocking event for doctor ${doctorId}:`,
8282
+ error
8283
+ );
8284
+ return null;
8285
+ }
8286
+ }
8287
+ /**
8288
+ * Periodically syncs events from external calendars for doctors
8289
+ * This would be called via a scheduled Cloud Function
8290
+ * @param lookbackDays - Number of days to look back for events
8291
+ * @param lookforwardDays - Number of days to look forward for events
8292
+ */
8293
+ async synchronizeExternalCalendars(lookbackDays = 7, lookforwardDays = 30) {
8294
+ try {
8295
+ const practitionersRef = collection17(this.db, PRACTITIONERS_COLLECTION);
8296
+ const practitionersSnapshot = await getDocs16(practitionersRef);
8297
+ const startDate = /* @__PURE__ */ new Date();
8298
+ startDate.setDate(startDate.getDate() - lookbackDays);
8299
+ const endDate = /* @__PURE__ */ new Date();
8300
+ endDate.setDate(endDate.getDate() + lookforwardDays);
8301
+ const syncPromises = [];
8302
+ for (const docSnapshot of practitionersSnapshot.docs) {
8303
+ const practitionerId = docSnapshot.id;
8304
+ syncPromises.push(
8305
+ this.importEventsFromExternalCalendars(
8306
+ "doctor",
8307
+ practitionerId,
8308
+ startDate,
8309
+ endDate
8310
+ ).then((count) => {
8311
+ console.log(
8312
+ `Imported ${count} events for doctor ${practitionerId}`
8313
+ );
8314
+ }).catch((error) => {
8315
+ console.error(
8316
+ `Error importing events for doctor ${practitionerId}:`,
8317
+ error
8318
+ );
8319
+ })
8320
+ );
8321
+ syncPromises.push(
8322
+ this.updateExistingEventsFromExternalCalendars(
8323
+ practitionerId,
8324
+ startDate,
8325
+ endDate
8326
+ ).then((count) => {
8327
+ console.log(
8328
+ `Updated ${count} events for doctor ${practitionerId}`
8329
+ );
8330
+ }).catch((error) => {
8331
+ console.error(
8332
+ `Error updating events for doctor ${practitionerId}:`,
8333
+ error
8334
+ );
8335
+ })
8336
+ );
8337
+ }
8338
+ await Promise.all(syncPromises);
8339
+ console.log("Completed external calendar synchronization");
8340
+ } catch (error) {
8341
+ console.error("Error synchronizing external calendars:", error);
8342
+ }
8343
+ }
8344
+ /**
8345
+ * Updates existing events that were synced from external calendars
8346
+ * @param doctorId - ID of the doctor
8347
+ * @param startDate - Start date for fetching events
8348
+ * @param endDate - End date for fetching events
8349
+ * @returns Number of events updated
8350
+ */
8351
+ async updateExistingEventsFromExternalCalendars(doctorId, startDate, endDate) {
8352
+ var _a;
8353
+ try {
8354
+ const eventsRef = collection17(
8355
+ this.db,
8356
+ PRACTITIONERS_COLLECTION,
8357
+ doctorId,
8358
+ CALENDAR_COLLECTION
8359
+ );
8360
+ const q = query16(
8361
+ eventsRef,
8362
+ where16("syncStatus", "==", "external" /* EXTERNAL */),
8363
+ where16("eventTime.start", ">=", Timestamp23.fromDate(startDate)),
8364
+ where16("eventTime.start", "<=", Timestamp23.fromDate(endDate))
8365
+ );
8366
+ const eventsSnapshot = await getDocs16(q);
8367
+ const events = eventsSnapshot.docs.map((doc20) => ({
8368
+ id: doc20.id,
8369
+ ...doc20.data()
8370
+ }));
8371
+ const calendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
8372
+ doctorId
8373
+ );
8374
+ const activeCalendars = calendars.filter((cal) => cal.isActive);
8375
+ if (activeCalendars.length === 0 || events.length === 0) {
8376
+ return 0;
8377
+ }
8378
+ let updatedCount = 0;
8379
+ for (const event of events) {
8380
+ if (!((_a = event.syncedCalendarEventId) == null ? void 0 : _a.length)) continue;
8381
+ for (const syncId of event.syncedCalendarEventId) {
8382
+ const calendar = activeCalendars.find(
8383
+ (cal) => cal.provider === syncId.syncedCalendarProvider
8384
+ );
8385
+ if (!calendar) continue;
8386
+ if (syncId.syncedCalendarProvider === "google" /* GOOGLE */) {
8387
+ try {
8388
+ const externalEvent = await this.fetchExternalEvent(
8389
+ doctorId,
8390
+ calendar,
8391
+ syncId.eventId
8392
+ );
8393
+ if (externalEvent) {
8394
+ const externalStartTime = new Date(
8395
+ externalEvent.start.dateTime || externalEvent.start.date
8396
+ ).getTime();
8397
+ const externalEndTime = new Date(
8398
+ externalEvent.end.dateTime || externalEvent.end.date
8399
+ ).getTime();
8400
+ const localStartTime = event.eventTime.start.toDate().getTime();
8401
+ const localEndTime = event.eventTime.end.toDate().getTime();
8402
+ if (externalStartTime !== localStartTime || externalEndTime !== localEndTime || externalEvent.summary !== event.eventName || externalEvent.description !== event.description) {
8403
+ await this.updateLocalEventFromExternal(
8404
+ doctorId,
8405
+ event.id,
8406
+ externalEvent
8407
+ );
8408
+ updatedCount++;
8409
+ }
8410
+ } else {
8411
+ await this.updateEventStatus(
8412
+ doctorId,
8413
+ event.id,
8414
+ "canceled" /* CANCELED */
8415
+ );
8416
+ updatedCount++;
8417
+ }
8418
+ } catch (error) {
8419
+ console.error(
8420
+ `Error updating external event ${event.id}:`,
8421
+ error
8422
+ );
8423
+ }
8424
+ }
8425
+ }
8426
+ }
8427
+ return updatedCount;
8428
+ } catch (error) {
8429
+ console.error(
8430
+ "Error updating existing events from external calendars:",
8431
+ error
8432
+ );
8433
+ return 0;
8434
+ }
8435
+ }
8436
+ /**
8437
+ * Fetches a single external event from Google Calendar
8438
+ * @param doctorId - ID of the doctor
8439
+ * @param calendar - Calendar information
8440
+ * @param externalEventId - ID of the external event
8441
+ * @returns External event data or null if not found
8442
+ */
8443
+ async fetchExternalEvent(doctorId, calendar, externalEventId) {
8444
+ try {
8445
+ if (calendar.provider === "google" /* GOOGLE */) {
8446
+ const result = await this.syncedCalendarsService.fetchEventFromPractitionerGoogleCalendar(
8447
+ doctorId,
8448
+ calendar.id,
8449
+ externalEventId
8450
+ );
8451
+ return result;
8452
+ }
8453
+ return null;
8454
+ } catch (error) {
8455
+ console.error(`Error fetching external event ${externalEventId}:`, error);
8456
+ return null;
8457
+ }
8458
+ }
8459
+ /**
8460
+ * Updates a local event with data from an external event
8461
+ * @param doctorId - ID of the doctor
8462
+ * @param eventId - ID of the local event
8463
+ * @param externalEvent - External event data
8464
+ */
8465
+ async updateLocalEventFromExternal(doctorId, eventId, externalEvent) {
8466
+ try {
8467
+ const startTime = new Date(
8468
+ externalEvent.start.dateTime || externalEvent.start.date
8469
+ );
8470
+ const endTime = new Date(
8471
+ externalEvent.end.dateTime || externalEvent.end.date
8472
+ );
8473
+ const eventRef = doc19(
8474
+ this.db,
8475
+ PRACTITIONERS_COLLECTION,
8476
+ doctorId,
8477
+ CALENDAR_COLLECTION,
8478
+ eventId
8479
+ );
8480
+ await updateDoc20(eventRef, {
8481
+ eventName: externalEvent.summary || "External Event",
8482
+ eventTime: {
8483
+ start: Timestamp23.fromDate(startTime),
8484
+ end: Timestamp23.fromDate(endTime)
8485
+ },
8486
+ description: externalEvent.description || "",
8487
+ updatedAt: serverTimestamp18()
8488
+ });
8489
+ console.log(`Updated local event ${eventId} from external event`);
8490
+ } catch (error) {
8491
+ console.error(
8492
+ `Error updating local event ${eventId} from external:`,
8493
+ error
8494
+ );
8495
+ }
8496
+ }
8497
+ /**
8498
+ * Updates an event's status
8499
+ * @param doctorId - ID of the doctor
8500
+ * @param eventId - ID of the event
8501
+ * @param status - New status
8502
+ */
8503
+ async updateEventStatus(doctorId, eventId, status) {
8504
+ try {
8505
+ const eventRef = doc19(
8506
+ this.db,
8507
+ PRACTITIONERS_COLLECTION,
8508
+ doctorId,
8509
+ CALENDAR_COLLECTION,
8510
+ eventId
8511
+ );
8512
+ await updateDoc20(eventRef, {
8513
+ status,
8514
+ updatedAt: serverTimestamp18()
8515
+ });
8516
+ console.log(`Updated event ${eventId} status to ${status}`);
8517
+ } catch (error) {
8518
+ console.error(`Error updating event ${eventId} status:`, error);
8519
+ }
8520
+ }
8521
+ /**
8522
+ * Creates a scheduled job to periodically sync external calendars
8523
+ * Note: This would be implemented using Cloud Functions in a real application
8524
+ * This is a sample implementation to show how it could be set up
8525
+ * @param interval - Interval in hours
8526
+ */
8527
+ createScheduledSyncJob(interval = 3) {
8528
+ console.log(
8529
+ `Setting up scheduled calendar sync job every ${interval} hours`
8530
+ );
8531
+ }
8532
+ // #endregion
8533
+ // #region Private Helper Methods
8534
+ /**
8535
+ * Validates appointment creation parameters
8536
+ * @param params - Appointment parameters to validate
8537
+ * @throws Error if validation fails
8538
+ */
8539
+ async validateAppointmentParams(params) {
8540
+ await createAppointmentSchema.parseAsync(params);
8541
+ }
8542
+ /**
8543
+ * Validates if the event time falls within clinic working hours
8544
+ * @param clinicId - ID of the clinic
8545
+ * @param eventTime - Event time to validate
8546
+ * @throws Error if validation fails
8547
+ */
8548
+ async validateClinicWorkingHours(clinicId, eventTime) {
8549
+ const startDate = eventTime.start.toDate();
8550
+ const workingHours = await this.getClinicWorkingHours(clinicId, startDate);
8551
+ if (workingHours.length === 0) {
8552
+ throw new Error("Clinic is not open on this day");
8553
+ }
8554
+ const startTime = startDate;
8555
+ const endTime = eventTime.end.toDate();
8556
+ const isWithinWorkingHours = workingHours.some((slot) => {
8557
+ return slot.start <= startTime && slot.end >= endTime && slot.isAvailable;
8558
+ });
8559
+ if (!isWithinWorkingHours) {
8560
+ throw new Error("Appointment time is outside clinic working hours");
8561
+ }
8562
+ }
8563
+ /**
8564
+ * Validates if the doctor is available during the event time
8565
+ * @param doctorId - ID of the doctor
8566
+ * @param eventTime - Event time to validate
8567
+ * @param clinicId - ID of the clinic where the appointment is being booked
8568
+ * @throws Error if validation fails
8569
+ */
8570
+ async validateDoctorAvailability(doctorId, eventTime, clinicId) {
8571
+ var _a;
8572
+ const startDate = eventTime.start.toDate();
8573
+ const startTime = startDate;
8574
+ const endTime = eventTime.end.toDate();
8575
+ const practitionerRef = doc19(this.db, PRACTITIONERS_COLLECTION, doctorId);
8576
+ const practitionerDoc = await getDoc22(practitionerRef);
8577
+ if (!practitionerDoc.exists()) {
8578
+ throw new Error(`Doctor with ID ${doctorId} not found`);
8579
+ }
8580
+ const practitioner = practitionerDoc.data();
8581
+ if (!practitioner.clinics.includes(clinicId)) {
8582
+ throw new Error("Doctor does not work at this clinic");
8583
+ }
8584
+ const clinicWorkingHours = (_a = practitioner.clinicWorkingHours) == null ? void 0 : _a.find(
8585
+ (hours) => hours.clinicId === clinicId && hours.isActive
8586
+ );
8587
+ if (!clinicWorkingHours) {
8588
+ throw new Error("Doctor does not have working hours set for this clinic");
8589
+ }
8590
+ const dayOfWeek = startDate.getDay();
8591
+ const dayKey = [
8592
+ "sunday",
8593
+ "monday",
8594
+ "tuesday",
8595
+ "wednesday",
8596
+ "thursday",
8597
+ "friday",
8598
+ "saturday"
8599
+ ][dayOfWeek];
8600
+ const daySchedule = clinicWorkingHours.workingHours[dayKey];
8601
+ if (!daySchedule) {
8602
+ throw new Error("Doctor is not working on this day at this clinic");
8603
+ }
8604
+ const [startHour, startMinute] = daySchedule.start.split(":").map(Number);
8605
+ const [endHour, endMinute] = daySchedule.end.split(":").map(Number);
8606
+ const scheduleStart = new Date(startDate);
8607
+ scheduleStart.setHours(startHour, startMinute, 0, 0);
8608
+ const scheduleEnd = new Date(startDate);
8609
+ scheduleEnd.setHours(endHour, endMinute, 0, 0);
8610
+ if (startTime < scheduleStart || endTime > scheduleEnd) {
8611
+ throw new Error(
8612
+ "Appointment time is outside doctor's working hours at this clinic"
8613
+ );
8614
+ }
8615
+ const appointments = await this.getDoctorAppointments(doctorId, startDate);
8616
+ const hasOverlap = appointments.some((appointment) => {
8617
+ const appointmentStart = appointment.eventTime.start.toDate();
8618
+ const appointmentEnd = appointment.eventTime.end.toDate();
8619
+ return startTime >= appointmentStart && startTime < appointmentEnd || endTime > appointmentStart && endTime <= appointmentEnd || startTime <= appointmentStart && endTime >= appointmentEnd;
8620
+ });
8621
+ if (hasOverlap) {
8622
+ throw new Error("Doctor has another appointment during this time");
8623
+ }
8624
+ }
8625
+ /**
8626
+ * Updates appointment status
8627
+ * @param appointmentId - ID of the appointment
8628
+ * @param clinicId - ID of the clinic
8629
+ * @param status - New status
8630
+ * @returns Updated calendar event
8631
+ */
8632
+ async updateAppointmentStatus(appointmentId, clinicId, status) {
8633
+ const appointmentRef = doc19(this.db, CALENDAR_COLLECTION, appointmentId);
8634
+ const appointmentDoc = await getDoc22(appointmentRef);
8635
+ if (!appointmentDoc.exists()) {
8636
+ throw new Error(`Appointment with ID ${appointmentId} not found`);
8637
+ }
8638
+ const appointment = appointmentDoc.data();
8639
+ if (appointment.clinicBranchId !== clinicId) {
8640
+ throw new Error("Appointment does not belong to the specified clinic");
8641
+ }
8642
+ this.validateStatusTransition(appointment.status, status);
8643
+ const updateParams = {
8644
+ appointmentId,
8645
+ clinicId,
8646
+ doctorId: appointment.practitionerProfileId || "",
8647
+ patientId: appointment.patientProfileId || "",
8648
+ status
8649
+ };
8650
+ await this.validateUpdatePermissions(updateParams);
8651
+ return this.updateAppointment(updateParams);
8652
+ }
8653
+ /**
8654
+ * Validates status transition
8655
+ * @param currentStatus - Current status
8656
+ * @param newStatus - New status
8657
+ * @throws Error if transition is invalid
8658
+ */
8659
+ validateStatusTransition(currentStatus, newStatus) {
8660
+ const validTransitions = {
8661
+ ["pending" /* PENDING */]: [
8662
+ "confirmed" /* CONFIRMED */,
8663
+ "rejected" /* REJECTED */,
8664
+ "canceled" /* CANCELED */
8665
+ ],
8666
+ ["confirmed" /* CONFIRMED */]: [
8667
+ "canceled" /* CANCELED */,
8668
+ "completed" /* COMPLETED */,
8669
+ "rescheduled" /* RESCHEDULED */
8670
+ ],
8671
+ ["rejected" /* REJECTED */]: [],
8672
+ ["canceled" /* CANCELED */]: [],
8673
+ ["rescheduled" /* RESCHEDULED */]: [
8674
+ "confirmed" /* CONFIRMED */,
8675
+ "canceled" /* CANCELED */
8676
+ ],
8677
+ ["completed" /* COMPLETED */]: []
8678
+ };
8679
+ if (!validTransitions[currentStatus].includes(newStatus)) {
8680
+ throw new Error(
8681
+ `Invalid status transition from ${currentStatus} to ${newStatus}`
8682
+ );
8683
+ }
8684
+ }
8685
+ /**
8686
+ * Syncs appointment with external calendars based on entity type and status
8687
+ * @param appointment - Calendar event to sync
8688
+ */
8689
+ async syncAppointmentWithExternalCalendars(appointment) {
8690
+ if (!appointment.practitionerProfileId || !appointment.patientProfileId) {
8691
+ return;
8692
+ }
8693
+ try {
8694
+ const [doctorCalendars, patientCalendars] = await Promise.all([
8695
+ this.syncedCalendarsService.getPractitionerSyncedCalendars(
8696
+ appointment.practitionerProfileId
8697
+ ),
8698
+ this.syncedCalendarsService.getPatientSyncedCalendars(
8699
+ appointment.patientProfileId
8700
+ )
8701
+ ]);
8702
+ const activeDoctorCalendars = doctorCalendars.filter(
8703
+ (cal) => cal.isActive
8704
+ );
8705
+ const activePatientCalendars = patientCalendars.filter(
8706
+ (cal) => cal.isActive
8707
+ );
8708
+ if (activeDoctorCalendars.length === 0 && activePatientCalendars.length === 0) {
8709
+ return;
8710
+ }
8711
+ if (appointment.syncStatus !== "internal" /* INTERNAL */) {
8712
+ return;
8713
+ }
8714
+ if (appointment.status === "confirmed" /* CONFIRMED */ && activeDoctorCalendars.length > 0) {
8715
+ await Promise.all(
8716
+ activeDoctorCalendars.map(
8717
+ (calendar) => this.syncEventToExternalCalendar(appointment, calendar, "doctor")
8718
+ )
8719
+ );
8720
+ }
8721
+ if (appointment.status !== "canceled" /* CANCELED */ && appointment.status !== "rejected" /* REJECTED */ && activePatientCalendars.length > 0) {
8722
+ await Promise.all(
8723
+ activePatientCalendars.map(
8724
+ (calendar) => this.syncEventToExternalCalendar(appointment, calendar, "patient")
8725
+ )
8726
+ );
8727
+ }
8728
+ } catch (error) {
8729
+ console.error("Error syncing with external calendars:", error);
8730
+ }
8731
+ }
8732
+ /**
8733
+ * Syncs a single event to an external calendar
8734
+ * @param appointment - Calendar event to sync
8735
+ * @param calendar - External calendar to sync with
8736
+ * @param entityType - Type of entity owning the calendar
8737
+ */
8738
+ async syncEventToExternalCalendar(appointment, calendar, entityType) {
8739
+ var _a, _b, _c, _d, _e;
8740
+ try {
8741
+ const eventToSync = { ...appointment };
8742
+ let eventTitle = appointment.eventName;
8743
+ const clinicName = ((_a = appointment.clinicBranchInfo) == null ? void 0 : _a.name) || "Clinic";
8744
+ if (entityType === "patient") {
8745
+ eventTitle = `[${appointment.status}] ${eventTitle} @ ${clinicName}`;
8746
+ } else {
8747
+ eventTitle = `${eventTitle} - Patient: ${((_b = appointment.patientProfileInfo) == null ? void 0 : _b.fullName) || "Unknown"} @ ${clinicName}`;
8748
+ }
8749
+ eventToSync.eventName = eventTitle;
8750
+ const existingSyncId = (_d = (_c = appointment.syncedCalendarEventId) == null ? void 0 : _c.find(
8751
+ (sync) => sync.syncedCalendarProvider === calendar.provider
8752
+ )) == null ? void 0 : _d.eventId;
8753
+ if (calendar.provider === "google" /* GOOGLE */) {
8754
+ const result = await this.syncedCalendarsService.syncPractitionerEventsToGoogleCalendar(
8755
+ entityType === "doctor" ? appointment.practitionerProfileId : appointment.patientProfileId,
8756
+ calendar.id,
8757
+ [eventToSync],
8758
+ existingSyncId
8759
+ // Pass existing sync ID if we have one
8760
+ );
8761
+ if (result.success && ((_e = result.eventIds) == null ? void 0 : _e.length) && !existingSyncId) {
8762
+ const newSyncEvent = {
8763
+ eventId: result.eventIds[0],
8764
+ syncedCalendarProvider: calendar.provider,
8765
+ syncedAt: Timestamp23.now()
8766
+ };
8767
+ await this.updateEventWithSyncId(
8768
+ entityType === "doctor" ? appointment.practitionerProfileId : appointment.patientProfileId,
8769
+ entityType,
8770
+ appointment.id,
8771
+ newSyncEvent
8772
+ );
8773
+ }
8774
+ }
8775
+ } catch (error) {
8776
+ console.error(`Error syncing with ${entityType}'s calendar:`, error);
8777
+ }
8778
+ }
8779
+ /**
8780
+ * Updates an event with a new sync ID
8781
+ * @param entityId - ID of the entity (doctor or patient)
8782
+ * @param entityType - Type of entity
8783
+ * @param eventId - ID of the event
8784
+ * @param syncEvent - Sync event information
8785
+ */
8786
+ async updateEventWithSyncId(entityId, entityType, eventId, syncEvent) {
8787
+ try {
8788
+ const collectionPath = entityType === "doctor" ? `${PRACTITIONERS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}` : `${PATIENTS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
8789
+ const eventRef = doc19(this.db, collectionPath, eventId);
8790
+ const eventDoc = await getDoc22(eventRef);
8791
+ if (eventDoc.exists()) {
8792
+ const event = eventDoc.data();
8793
+ const syncIds = [...event.syncedCalendarEventId || []];
8794
+ const existingSyncIndex = syncIds.findIndex(
8795
+ (sync) => sync.syncedCalendarProvider === syncEvent.syncedCalendarProvider
8796
+ );
8797
+ if (existingSyncIndex >= 0) {
8798
+ syncIds[existingSyncIndex] = syncEvent;
8799
+ } else {
8800
+ syncIds.push(syncEvent);
8801
+ }
8802
+ await updateDoc20(eventRef, {
8803
+ syncedCalendarEventId: syncIds,
8804
+ updatedAt: serverTimestamp18()
8805
+ });
8806
+ console.log(
8807
+ `Updated event ${eventId} with sync ID ${syncEvent.eventId}`
8808
+ );
8809
+ }
8810
+ } catch (error) {
8811
+ console.error("Error updating event with sync ID:", error);
8812
+ }
8813
+ }
8814
+ /**
8815
+ * Validates update permissions and parameters
8816
+ * @param params - Update parameters to validate
8817
+ */
8818
+ async validateUpdatePermissions(params) {
8819
+ await updateAppointmentSchema.parseAsync(params);
8820
+ }
8821
+ /**
8822
+ * Gets clinic working hours for a specific date
8823
+ * @param clinicId - ID of the clinic
8824
+ * @param date - Date to get working hours for
8825
+ * @returns Working hours for the clinic
8826
+ */
8827
+ async getClinicWorkingHours(clinicId, date) {
8828
+ const clinicRef = doc19(this.db, CLINICS_COLLECTION, clinicId);
8829
+ const clinicDoc = await getDoc22(clinicRef);
8830
+ if (!clinicDoc.exists()) {
8831
+ throw new Error(`Clinic with ID ${clinicId} not found`);
8832
+ }
8833
+ const workingHours = [];
8834
+ const dayOfWeek = date.getDay();
8835
+ if (dayOfWeek === 0 || dayOfWeek === 6) {
8836
+ return workingHours;
8837
+ }
8838
+ const workingDate = new Date(date);
8839
+ workingDate.setHours(9, 0, 0, 0);
8840
+ const startTime = new Date(workingDate);
8841
+ workingDate.setHours(17, 0, 0, 0);
8842
+ const endTime = new Date(workingDate);
8843
+ workingHours.push({
8844
+ start: startTime,
8845
+ end: endTime,
8846
+ isAvailable: true
8847
+ });
8848
+ return workingHours;
8849
+ }
8850
+ /**
8851
+ * Gets doctor's schedule for a specific date
8852
+ * @param doctorId - ID of the doctor
8853
+ * @param date - Date to get schedule for
8854
+ * @returns Doctor's schedule
8855
+ */
8856
+ async getDoctorSchedule(doctorId, date) {
8857
+ const practitionerRef = doc19(this.db, PRACTITIONERS_COLLECTION, doctorId);
8858
+ const practitionerDoc = await getDoc22(practitionerRef);
8859
+ if (!practitionerDoc.exists()) {
8860
+ throw new Error(`Doctor with ID ${doctorId} not found`);
8861
+ }
8862
+ const schedule = [];
8863
+ const dayOfWeek = date.getDay();
8864
+ if (dayOfWeek === 0 || dayOfWeek === 6) {
8865
+ return schedule;
8866
+ }
8867
+ const scheduleDate = new Date(date);
8868
+ scheduleDate.setHours(9, 0, 0, 0);
8869
+ const startTime = new Date(scheduleDate);
8870
+ scheduleDate.setHours(17, 0, 0, 0);
8871
+ const endTime = new Date(scheduleDate);
8872
+ schedule.push({
8873
+ start: startTime,
8874
+ end: endTime,
8875
+ isAvailable: true
8876
+ });
8877
+ return schedule;
8878
+ }
8879
+ /**
8880
+ * Gets doctor's appointments for a specific date
8881
+ * @param doctorId - ID of the doctor
8882
+ * @param date - Date to get appointments for
8883
+ * @returns Array of calendar events
8884
+ */
8885
+ async getDoctorAppointments(doctorId, date) {
8886
+ const startOfDay = new Date(date);
8887
+ startOfDay.setHours(0, 0, 0, 0);
8888
+ const endOfDay = new Date(date);
8889
+ endOfDay.setHours(23, 59, 59, 999);
8890
+ const appointmentsRef = collection17(this.db, CALENDAR_COLLECTION);
8891
+ const q = query16(
8892
+ appointmentsRef,
8893
+ where16("practitionerProfileId", "==", doctorId),
8894
+ where16("eventTime.start", ">=", Timestamp23.fromDate(startOfDay)),
8895
+ where16("eventTime.start", "<=", Timestamp23.fromDate(endOfDay)),
8896
+ where16("status", "in", [
8897
+ "confirmed" /* CONFIRMED */,
8898
+ "pending" /* PENDING */
8899
+ ])
8900
+ );
8901
+ const querySnapshot = await getDocs16(q);
8902
+ return querySnapshot.docs.map((doc20) => doc20.data());
8903
+ }
8904
+ /**
8905
+ * Calculates available time slots based on working hours, schedule and existing appointments
8906
+ * @param workingHours - Clinic working hours
8907
+ * @param doctorSchedule - Doctor's schedule
8908
+ * @param existingAppointments - Existing appointments
8909
+ * @returns Array of available time slots
8910
+ */
8911
+ calculateAvailableSlots(workingHours, doctorSchedule, existingAppointments) {
8912
+ const availableSlots = [];
8913
+ for (const workingHour of workingHours) {
8914
+ for (const scheduleSlot of doctorSchedule) {
8915
+ const overlapStart = new Date(
8916
+ Math.max(workingHour.start.getTime(), scheduleSlot.start.getTime())
8917
+ );
8918
+ const overlapEnd = new Date(
8919
+ Math.min(workingHour.end.getTime(), scheduleSlot.end.getTime())
8920
+ );
8921
+ if (overlapStart < overlapEnd && workingHour.isAvailable && scheduleSlot.isAvailable) {
8922
+ let slotStart = new Date(overlapStart);
8923
+ while (slotStart < overlapEnd) {
8924
+ const slotEnd = new Date(
8925
+ slotStart.getTime() + MIN_APPOINTMENT_DURATION2 * 60 * 1e3
8926
+ );
8927
+ const hasOverlap = existingAppointments.some((appointment) => {
8928
+ const appointmentStart = appointment.eventTime.start.toDate();
8929
+ const appointmentEnd = appointment.eventTime.end.toDate();
8930
+ return slotStart >= appointmentStart && slotStart < appointmentEnd || slotEnd > appointmentStart && slotEnd <= appointmentEnd;
8931
+ });
8932
+ if (!hasOverlap && slotEnd <= overlapEnd) {
8933
+ availableSlots.push({
8934
+ start: new Date(slotStart),
8935
+ end: new Date(slotEnd),
8936
+ isAvailable: true
8937
+ });
8938
+ }
8939
+ slotStart = new Date(
8940
+ slotStart.getTime() + MIN_APPOINTMENT_DURATION2 * 60 * 1e3
8941
+ );
8942
+ }
8943
+ }
8944
+ }
8945
+ }
8946
+ return availableSlots;
8947
+ }
8948
+ /**
8949
+ * Fetches and creates info cards for clinic, doctor, and patient profiles
8950
+ * @param clinicId - ID of the clinic
8951
+ * @param doctorId - ID of the doctor
8952
+ * @param patientId - ID of the patient
8953
+ * @returns Object containing info cards for all profiles
8954
+ */
8955
+ async fetchProfileInfoCards(clinicId, doctorId, patientId) {
8956
+ var _a;
8957
+ try {
8958
+ const [clinicDoc, practitionerDoc, patientDoc, patientSensitiveInfoDoc] = await Promise.all([
8959
+ getDoc22(doc19(this.db, CLINICS_COLLECTION, clinicId)),
8960
+ getDoc22(doc19(this.db, PRACTITIONERS_COLLECTION, doctorId)),
8961
+ getDoc22(doc19(this.db, PATIENTS_COLLECTION, patientId)),
8962
+ getDoc22(
8963
+ doc19(
8964
+ this.db,
8965
+ PATIENTS_COLLECTION,
8966
+ patientId,
8967
+ PATIENT_SENSITIVE_INFO_COLLECTION,
8968
+ patientId
8969
+ )
8970
+ )
8971
+ ]);
8972
+ const clinicInfo = clinicDoc.exists() ? {
8973
+ id: clinicDoc.id,
8974
+ featuredPhoto: clinicDoc.data().featuredPhoto || "",
8975
+ name: clinicDoc.data().name,
8976
+ description: clinicDoc.data().description || "",
8977
+ location: clinicDoc.data().location,
8978
+ contactInfo: clinicDoc.data().contactInfo
8979
+ } : null;
8980
+ const practitionerInfo = practitionerDoc.exists() ? {
8981
+ id: practitionerDoc.id,
8982
+ practitionerPhoto: practitionerDoc.data().basicInfo.profileImageUrl || null,
8983
+ name: `${practitionerDoc.data().basicInfo.firstName} ${practitionerDoc.data().basicInfo.lastName}`,
8984
+ email: practitionerDoc.data().basicInfo.email,
8985
+ phone: practitionerDoc.data().basicInfo.phoneNumber || null,
8986
+ certification: practitionerDoc.data().certification
8987
+ } : null;
8988
+ let patientInfo = null;
8989
+ if (patientSensitiveInfoDoc.exists()) {
8990
+ const sensitiveData = patientSensitiveInfoDoc.data();
8991
+ patientInfo = {
8992
+ id: patientId,
8993
+ fullName: `${sensitiveData.firstName} ${sensitiveData.lastName}`,
8994
+ email: sensitiveData.email || "",
8995
+ phone: sensitiveData.phoneNumber || null,
8996
+ dateOfBirth: sensitiveData.dateOfBirth || Timestamp23.now(),
8997
+ gender: sensitiveData.gender || "other" /* OTHER */
8998
+ };
8999
+ } else if (patientDoc.exists()) {
9000
+ patientInfo = {
9001
+ id: patientDoc.id,
9002
+ fullName: patientDoc.data().displayName,
9003
+ email: ((_a = patientDoc.data().contactInfo) == null ? void 0 : _a.email) || "",
9004
+ phone: patientDoc.data().phoneNumber || null,
9005
+ dateOfBirth: patientDoc.data().dateOfBirth || Timestamp23.now(),
9006
+ gender: patientDoc.data().gender || "other" /* OTHER */
9007
+ };
9008
+ }
9009
+ return {
9010
+ clinicInfo,
9011
+ practitionerInfo,
9012
+ patientInfo
9013
+ };
9014
+ } catch (error) {
9015
+ console.error("Error fetching profile info cards:", error);
9016
+ return {
9017
+ clinicInfo: null,
9018
+ practitionerInfo: null,
9019
+ patientInfo: null
9020
+ };
9021
+ }
9022
+ }
9023
+ // #endregion
9024
+ };
9025
+
9026
+ // src/validations/notification.schema.ts
9027
+ import { z as z18 } from "zod";
9028
+ var baseNotificationSchema = z18.object({
9029
+ id: z18.string().optional(),
9030
+ userId: z18.string(),
9031
+ notificationTime: z18.any(),
6394
9032
  // Timestamp
6395
- notificationType: z16.nativeEnum(NotificationType),
6396
- notificationTokens: z16.array(z16.string()),
6397
- status: z16.nativeEnum(NotificationStatus),
6398
- createdAt: z16.any().optional(),
9033
+ notificationType: z18.nativeEnum(NotificationType),
9034
+ notificationTokens: z18.array(z18.string()),
9035
+ status: z18.nativeEnum(NotificationStatus),
9036
+ createdAt: z18.any().optional(),
6399
9037
  // Timestamp
6400
- updatedAt: z16.any().optional(),
9038
+ updatedAt: z18.any().optional(),
6401
9039
  // Timestamp
6402
- title: z16.string(),
6403
- body: z16.string(),
6404
- isRead: z16.boolean(),
6405
- userRole: z16.nativeEnum(UserRole)
9040
+ title: z18.string(),
9041
+ body: z18.string(),
9042
+ isRead: z18.boolean(),
9043
+ userRole: z18.nativeEnum(UserRole)
6406
9044
  });
6407
9045
  var preRequirementNotificationSchema = baseNotificationSchema.extend({
6408
- notificationType: z16.literal("preRequirement" /* PRE_REQUIREMENT */),
6409
- treatmentId: z16.string(),
6410
- requirements: z16.array(z16.string()),
6411
- deadline: z16.any()
9046
+ notificationType: z18.literal("preRequirement" /* PRE_REQUIREMENT */),
9047
+ treatmentId: z18.string(),
9048
+ requirements: z18.array(z18.string()),
9049
+ deadline: z18.any()
6412
9050
  // Timestamp
6413
9051
  });
6414
9052
  var postRequirementNotificationSchema = baseNotificationSchema.extend({
6415
- notificationType: z16.literal("postRequirement" /* POST_REQUIREMENT */),
6416
- treatmentId: z16.string(),
6417
- requirements: z16.array(z16.string()),
6418
- deadline: z16.any()
9053
+ notificationType: z18.literal("postRequirement" /* POST_REQUIREMENT */),
9054
+ treatmentId: z18.string(),
9055
+ requirements: z18.array(z18.string()),
9056
+ deadline: z18.any()
6419
9057
  // Timestamp
6420
9058
  });
6421
9059
  var appointmentReminderNotificationSchema = baseNotificationSchema.extend({
6422
- notificationType: z16.literal("appointmentReminder" /* APPOINTMENT_REMINDER */),
6423
- appointmentId: z16.string(),
6424
- appointmentTime: z16.any(),
9060
+ notificationType: z18.literal("appointmentReminder" /* APPOINTMENT_REMINDER */),
9061
+ appointmentId: z18.string(),
9062
+ appointmentTime: z18.any(),
6425
9063
  // Timestamp
6426
- treatmentType: z16.string(),
6427
- doctorName: z16.string()
9064
+ treatmentType: z18.string(),
9065
+ doctorName: z18.string()
6428
9066
  });
6429
9067
  var appointmentNotificationSchema = baseNotificationSchema.extend({
6430
- notificationType: z16.literal("appointmentNotification" /* APPOINTMENT_NOTIFICATION */),
6431
- appointmentId: z16.string(),
6432
- appointmentStatus: z16.string(),
6433
- previousStatus: z16.string(),
6434
- reason: z16.string().optional()
9068
+ notificationType: z18.literal("appointmentNotification" /* APPOINTMENT_NOTIFICATION */),
9069
+ appointmentId: z18.string(),
9070
+ appointmentStatus: z18.string(),
9071
+ previousStatus: z18.string(),
9072
+ reason: z18.string().optional()
6435
9073
  });
6436
- var notificationSchema = z16.discriminatedUnion("notificationType", [
9074
+ var notificationSchema = z18.discriminatedUnion("notificationType", [
6437
9075
  preRequirementNotificationSchema,
6438
9076
  postRequirementNotificationSchema,
6439
9077
  appointmentReminderNotificationSchema,
@@ -6445,9 +9083,14 @@ export {
6445
9083
  AllergyType,
6446
9084
  AuthService,
6447
9085
  BlockingCondition,
9086
+ CALENDAR_COLLECTION,
6448
9087
  CLINICS_COLLECTION,
6449
9088
  CLINIC_ADMINS_COLLECTION,
6450
9089
  CLINIC_GROUPS_COLLECTION,
9090
+ CalendarEventStatus,
9091
+ CalendarEventType,
9092
+ CalendarServiceV2,
9093
+ CalendarSyncStatus,
6451
9094
  CertificationLevel,
6452
9095
  CertificationSpecialty,
6453
9096
  ClinicAdminService,
@@ -6488,7 +9131,10 @@ export {
6488
9131
  PractitionerService,
6489
9132
  PricingMeasure,
6490
9133
  ProcedureFamily,
9134
+ SYNCED_CALENDARS_COLLECTION,
6491
9135
  SubscriptionModel,
9136
+ SyncedCalendarProvider,
9137
+ SyncedCalendarsService,
6492
9138
  TreatmentBenefit,
6493
9139
  USER_ERRORS,
6494
9140
  UserService,
@@ -6505,6 +9151,8 @@ export {
6505
9151
  appointmentReminderNotificationSchema,
6506
9152
  baseNotificationSchema,
6507
9153
  blockingConditionSchema,
9154
+ calendarEventSchema,
9155
+ calendarEventTimeSchema,
6508
9156
  clinicAdminOptionsSchema,
6509
9157
  clinicAdminSchema,
6510
9158
  clinicAdminSignupSchema,
@@ -6520,6 +9168,8 @@ export {
6520
9168
  contactPersonSchema,
6521
9169
  contraindicationSchema,
6522
9170
  createAdminTokenSchema,
9171
+ createAppointmentSchema,
9172
+ createCalendarEventSchema,
6523
9173
  createClinicAdminSchema,
6524
9174
  createClinicGroupSchema,
6525
9175
  createClinicSchema,
@@ -6551,21 +9201,30 @@ export {
6551
9201
  patientDoctorSchema,
6552
9202
  patientLocationInfoSchema,
6553
9203
  patientMedicalInfoSchema,
9204
+ patientProfileInfoSchema,
6554
9205
  patientProfileSchema,
6555
9206
  patientSensitiveInfoSchema,
6556
9207
  postRequirementNotificationSchema,
6557
9208
  practitionerBasicInfoSchema,
6558
9209
  practitionerCertificationSchema,
6559
9210
  practitionerClinicProceduresSchema,
9211
+ practitionerClinicWorkingHoursSchema,
9212
+ practitionerProfileInfoSchema,
6560
9213
  practitionerReviewSchema,
6561
9214
  practitionerSchema,
6562
9215
  practitionerWorkingHoursSchema,
6563
9216
  preRequirementNotificationSchema,
9217
+ procedureCategorizationSchema,
9218
+ procedureInfoSchema,
6564
9219
  reviewInfoSchema,
6565
9220
  serviceInfoSchema,
9221
+ syncedCalendarEventSchema,
9222
+ timeSlotSchema2 as timeSlotSchema,
6566
9223
  timestampSchema,
6567
9224
  updateAllergySchema,
9225
+ updateAppointmentSchema,
6568
9226
  updateBlockingConditionSchema,
9227
+ updateCalendarEventSchema,
6569
9228
  updateClinicAdminSchema,
6570
9229
  updateClinicGroupSchema,
6571
9230
  updateClinicSchema,