@blackcode_sa/metaestetics-api 1.4.18 → 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.
package/dist/index.js CHANGED
@@ -25,9 +25,14 @@ __export(index_exports, {
25
25
  AllergyType: () => AllergyType,
26
26
  AuthService: () => AuthService,
27
27
  BlockingCondition: () => BlockingCondition,
28
+ CALENDAR_COLLECTION: () => CALENDAR_COLLECTION,
28
29
  CLINICS_COLLECTION: () => CLINICS_COLLECTION,
29
30
  CLINIC_ADMINS_COLLECTION: () => CLINIC_ADMINS_COLLECTION,
30
31
  CLINIC_GROUPS_COLLECTION: () => CLINIC_GROUPS_COLLECTION,
32
+ CalendarEventStatus: () => CalendarEventStatus,
33
+ CalendarEventType: () => CalendarEventType,
34
+ CalendarServiceV2: () => CalendarServiceV2,
35
+ CalendarSyncStatus: () => CalendarSyncStatus,
31
36
  CertificationLevel: () => CertificationLevel,
32
37
  CertificationSpecialty: () => CertificationSpecialty,
33
38
  ClinicAdminService: () => ClinicAdminService,
@@ -68,7 +73,10 @@ __export(index_exports, {
68
73
  PractitionerService: () => PractitionerService,
69
74
  PricingMeasure: () => PricingMeasure,
70
75
  ProcedureFamily: () => ProcedureFamily,
76
+ SYNCED_CALENDARS_COLLECTION: () => SYNCED_CALENDARS_COLLECTION,
71
77
  SubscriptionModel: () => SubscriptionModel,
78
+ SyncedCalendarProvider: () => SyncedCalendarProvider,
79
+ SyncedCalendarsService: () => SyncedCalendarsService,
72
80
  TreatmentBenefit: () => TreatmentBenefit,
73
81
  USER_ERRORS: () => USER_ERRORS,
74
82
  UserService: () => UserService,
@@ -85,6 +93,8 @@ __export(index_exports, {
85
93
  appointmentReminderNotificationSchema: () => appointmentReminderNotificationSchema,
86
94
  baseNotificationSchema: () => baseNotificationSchema,
87
95
  blockingConditionSchema: () => blockingConditionSchema,
96
+ calendarEventSchema: () => calendarEventSchema,
97
+ calendarEventTimeSchema: () => calendarEventTimeSchema,
88
98
  clinicAdminOptionsSchema: () => clinicAdminOptionsSchema,
89
99
  clinicAdminSchema: () => clinicAdminSchema,
90
100
  clinicAdminSignupSchema: () => clinicAdminSignupSchema,
@@ -100,6 +110,8 @@ __export(index_exports, {
100
110
  contactPersonSchema: () => contactPersonSchema,
101
111
  contraindicationSchema: () => contraindicationSchema,
102
112
  createAdminTokenSchema: () => createAdminTokenSchema,
113
+ createAppointmentSchema: () => createAppointmentSchema,
114
+ createCalendarEventSchema: () => createCalendarEventSchema,
103
115
  createClinicAdminSchema: () => createClinicAdminSchema,
104
116
  createClinicGroupSchema: () => createClinicGroupSchema,
105
117
  createClinicSchema: () => createClinicSchema,
@@ -131,21 +143,30 @@ __export(index_exports, {
131
143
  patientDoctorSchema: () => patientDoctorSchema,
132
144
  patientLocationInfoSchema: () => patientLocationInfoSchema,
133
145
  patientMedicalInfoSchema: () => patientMedicalInfoSchema,
146
+ patientProfileInfoSchema: () => patientProfileInfoSchema,
134
147
  patientProfileSchema: () => patientProfileSchema,
135
148
  patientSensitiveInfoSchema: () => patientSensitiveInfoSchema,
136
149
  postRequirementNotificationSchema: () => postRequirementNotificationSchema,
137
150
  practitionerBasicInfoSchema: () => practitionerBasicInfoSchema,
138
151
  practitionerCertificationSchema: () => practitionerCertificationSchema,
139
152
  practitionerClinicProceduresSchema: () => practitionerClinicProceduresSchema,
153
+ practitionerClinicWorkingHoursSchema: () => practitionerClinicWorkingHoursSchema,
154
+ practitionerProfileInfoSchema: () => practitionerProfileInfoSchema,
140
155
  practitionerReviewSchema: () => practitionerReviewSchema,
141
156
  practitionerSchema: () => practitionerSchema,
142
157
  practitionerWorkingHoursSchema: () => practitionerWorkingHoursSchema,
143
158
  preRequirementNotificationSchema: () => preRequirementNotificationSchema,
159
+ procedureCategorizationSchema: () => procedureCategorizationSchema,
160
+ procedureInfoSchema: () => procedureInfoSchema,
144
161
  reviewInfoSchema: () => reviewInfoSchema,
145
162
  serviceInfoSchema: () => serviceInfoSchema,
163
+ syncedCalendarEventSchema: () => syncedCalendarEventSchema,
164
+ timeSlotSchema: () => timeSlotSchema2,
146
165
  timestampSchema: () => timestampSchema,
147
166
  updateAllergySchema: () => updateAllergySchema,
167
+ updateAppointmentSchema: () => updateAppointmentSchema,
148
168
  updateBlockingConditionSchema: () => updateBlockingConditionSchema,
169
+ updateCalendarEventSchema: () => updateCalendarEventSchema,
149
170
  updateClinicAdminSchema: () => updateClinicAdminSchema,
150
171
  updateClinicGroupSchema: () => updateClinicGroupSchema,
151
172
  updateClinicSchema: () => updateClinicSchema,
@@ -253,6 +274,31 @@ var FilledDocumentStatus = /* @__PURE__ */ ((FilledDocumentStatus2) => {
253
274
  return FilledDocumentStatus2;
254
275
  })(FilledDocumentStatus || {});
255
276
 
277
+ // src/types/calendar/index.ts
278
+ var CalendarEventStatus = /* @__PURE__ */ ((CalendarEventStatus3) => {
279
+ CalendarEventStatus3["PENDING"] = "pending";
280
+ CalendarEventStatus3["CONFIRMED"] = "confirmed";
281
+ CalendarEventStatus3["REJECTED"] = "rejected";
282
+ CalendarEventStatus3["CANCELED"] = "canceled";
283
+ CalendarEventStatus3["RESCHEDULED"] = "rescheduled";
284
+ CalendarEventStatus3["COMPLETED"] = "completed";
285
+ return CalendarEventStatus3;
286
+ })(CalendarEventStatus || {});
287
+ var CalendarSyncStatus = /* @__PURE__ */ ((CalendarSyncStatus3) => {
288
+ CalendarSyncStatus3["INTERNAL"] = "internal";
289
+ CalendarSyncStatus3["EXTERNAL"] = "external";
290
+ return CalendarSyncStatus3;
291
+ })(CalendarSyncStatus || {});
292
+ var CalendarEventType = /* @__PURE__ */ ((CalendarEventType2) => {
293
+ CalendarEventType2["APPOINTMENT"] = "appointment";
294
+ CalendarEventType2["BLOCKING"] = "blocking";
295
+ CalendarEventType2["BREAK"] = "break";
296
+ CalendarEventType2["FREE_DAY"] = "free_day";
297
+ CalendarEventType2["OTHER"] = "other";
298
+ return CalendarEventType2;
299
+ })(CalendarEventType || {});
300
+ var CALENDAR_COLLECTION = "calendar";
301
+
256
302
  // src/types/index.ts
257
303
  var UserRole = /* @__PURE__ */ ((UserRole2) => {
258
304
  UserRole2["PATIENT"] = "patient";
@@ -1372,9 +1418,9 @@ var addAllergyUtil = async (db, patientId, data, userRef) => {
1372
1418
  var updateAllergyUtil = async (db, patientId, data, userRef) => {
1373
1419
  const validatedData = updateAllergySchema.parse(data);
1374
1420
  const { allergyIndex, ...updateData } = validatedData;
1375
- const doc14 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1376
- if (!doc14.exists()) throw new Error("Medical info not found");
1377
- const medicalInfo = doc14.data();
1421
+ const doc20 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1422
+ if (!doc20.exists()) throw new Error("Medical info not found");
1423
+ const medicalInfo = doc20.data();
1378
1424
  if (allergyIndex >= medicalInfo.allergies.length) {
1379
1425
  throw new Error("Invalid allergy index");
1380
1426
  }
@@ -1390,9 +1436,9 @@ var updateAllergyUtil = async (db, patientId, data, userRef) => {
1390
1436
  });
1391
1437
  };
1392
1438
  var removeAllergyUtil = async (db, patientId, allergyIndex, userRef) => {
1393
- const doc14 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1394
- if (!doc14.exists()) throw new Error("Medical info not found");
1395
- const medicalInfo = doc14.data();
1439
+ const doc20 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1440
+ if (!doc20.exists()) throw new Error("Medical info not found");
1441
+ const medicalInfo = doc20.data();
1396
1442
  if (allergyIndex >= medicalInfo.allergies.length) {
1397
1443
  throw new Error("Invalid allergy index");
1398
1444
  }
@@ -1417,9 +1463,9 @@ var addBlockingConditionUtil = async (db, patientId, data, userRef) => {
1417
1463
  var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
1418
1464
  const validatedData = updateBlockingConditionSchema.parse(data);
1419
1465
  const { conditionIndex, ...updateData } = validatedData;
1420
- const doc14 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1421
- if (!doc14.exists()) throw new Error("Medical info not found");
1422
- const medicalInfo = doc14.data();
1466
+ const doc20 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1467
+ if (!doc20.exists()) throw new Error("Medical info not found");
1468
+ const medicalInfo = doc20.data();
1423
1469
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
1424
1470
  throw new Error("Invalid blocking condition index");
1425
1471
  }
@@ -1435,9 +1481,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
1435
1481
  });
1436
1482
  };
1437
1483
  var removeBlockingConditionUtil = async (db, patientId, conditionIndex, userRef) => {
1438
- const doc14 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1439
- if (!doc14.exists()) throw new Error("Medical info not found");
1440
- const medicalInfo = doc14.data();
1484
+ const doc20 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1485
+ if (!doc20.exists()) throw new Error("Medical info not found");
1486
+ const medicalInfo = doc20.data();
1441
1487
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
1442
1488
  throw new Error("Invalid blocking condition index");
1443
1489
  }
@@ -1462,9 +1508,9 @@ var addContraindicationUtil = async (db, patientId, data, userRef) => {
1462
1508
  var updateContraindicationUtil = async (db, patientId, data, userRef) => {
1463
1509
  const validatedData = updateContraindicationSchema.parse(data);
1464
1510
  const { contraindicationIndex, ...updateData } = validatedData;
1465
- const doc14 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1466
- if (!doc14.exists()) throw new Error("Medical info not found");
1467
- const medicalInfo = doc14.data();
1511
+ const doc20 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1512
+ if (!doc20.exists()) throw new Error("Medical info not found");
1513
+ const medicalInfo = doc20.data();
1468
1514
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
1469
1515
  throw new Error("Invalid contraindication index");
1470
1516
  }
@@ -1480,9 +1526,9 @@ var updateContraindicationUtil = async (db, patientId, data, userRef) => {
1480
1526
  });
1481
1527
  };
1482
1528
  var removeContraindicationUtil = async (db, patientId, contraindicationIndex, userRef) => {
1483
- const doc14 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1484
- if (!doc14.exists()) throw new Error("Medical info not found");
1485
- const medicalInfo = doc14.data();
1529
+ const doc20 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1530
+ if (!doc20.exists()) throw new Error("Medical info not found");
1531
+ const medicalInfo = doc20.data();
1486
1532
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
1487
1533
  throw new Error("Invalid contraindication index");
1488
1534
  }
@@ -1507,9 +1553,9 @@ var addMedicationUtil = async (db, patientId, data, userRef) => {
1507
1553
  var updateMedicationUtil = async (db, patientId, data, userRef) => {
1508
1554
  const validatedData = updateMedicationSchema.parse(data);
1509
1555
  const { medicationIndex, ...updateData } = validatedData;
1510
- const doc14 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1511
- if (!doc14.exists()) throw new Error("Medical info not found");
1512
- const medicalInfo = doc14.data();
1556
+ const doc20 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1557
+ if (!doc20.exists()) throw new Error("Medical info not found");
1558
+ const medicalInfo = doc20.data();
1513
1559
  if (medicationIndex >= medicalInfo.currentMedications.length) {
1514
1560
  throw new Error("Invalid medication index");
1515
1561
  }
@@ -1525,9 +1571,9 @@ var updateMedicationUtil = async (db, patientId, data, userRef) => {
1525
1571
  });
1526
1572
  };
1527
1573
  var removeMedicationUtil = async (db, patientId, medicationIndex, userRef) => {
1528
- const doc14 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1529
- if (!doc14.exists()) throw new Error("Medical info not found");
1530
- const medicalInfo = doc14.data();
1574
+ const doc20 = await (0, import_firestore5.getDoc)(getMedicalInfoDocRef(db, patientId));
1575
+ if (!doc20.exists()) throw new Error("Medical info not found");
1576
+ const medicalInfo = doc20.data();
1531
1577
  if (medicationIndex >= medicalInfo.currentMedications.length) {
1532
1578
  throw new Error("Invalid medication index");
1533
1579
  }
@@ -2320,14 +2366,14 @@ var TreatmentBenefit = /* @__PURE__ */ ((TreatmentBenefit2) => {
2320
2366
  })(TreatmentBenefit || {});
2321
2367
 
2322
2368
  // src/backoffice/types/static/pricing.types.ts
2323
- var PricingMeasure = /* @__PURE__ */ ((PricingMeasure2) => {
2324
- PricingMeasure2["PER_ML"] = "per_ml";
2325
- PricingMeasure2["PER_ZONE"] = "per_zone";
2326
- PricingMeasure2["PER_AREA"] = "per_area";
2327
- PricingMeasure2["PER_SESSION"] = "per_session";
2328
- PricingMeasure2["PER_TREATMENT"] = "per_treatment";
2329
- PricingMeasure2["PER_PACKAGE"] = "per_package";
2330
- return PricingMeasure2;
2369
+ var PricingMeasure = /* @__PURE__ */ ((PricingMeasure3) => {
2370
+ PricingMeasure3["PER_ML"] = "per_ml";
2371
+ PricingMeasure3["PER_ZONE"] = "per_zone";
2372
+ PricingMeasure3["PER_AREA"] = "per_area";
2373
+ PricingMeasure3["PER_SESSION"] = "per_session";
2374
+ PricingMeasure3["PER_TREATMENT"] = "per_treatment";
2375
+ PricingMeasure3["PER_PACKAGE"] = "per_package";
2376
+ return PricingMeasure3;
2331
2377
  })(PricingMeasure || {});
2332
2378
  var Currency = /* @__PURE__ */ ((Currency2) => {
2333
2379
  Currency2["EUR"] = "EUR";
@@ -2821,7 +2867,7 @@ async function getClinicAdminsByGroup(db, clinicGroupId) {
2821
2867
  (0, import_firestore11.where)("clinicGroupId", "==", clinicGroupId)
2822
2868
  );
2823
2869
  const querySnapshot = await (0, import_firestore11.getDocs)(q);
2824
- return querySnapshot.docs.map((doc14) => doc14.data());
2870
+ return querySnapshot.docs.map((doc20) => doc20.data());
2825
2871
  }
2826
2872
  async function updateClinicAdmin(db, adminId, data) {
2827
2873
  const admin = await getClinicAdmin(db, adminId);
@@ -3157,6 +3203,21 @@ var practitionerWorkingHoursSchema = import_zod10.z.object({
3157
3203
  createdAt: import_zod10.z.instanceof(import_firestore12.Timestamp),
3158
3204
  updatedAt: import_zod10.z.instanceof(import_firestore12.Timestamp)
3159
3205
  });
3206
+ var practitionerClinicWorkingHoursSchema = import_zod10.z.object({
3207
+ clinicId: import_zod10.z.string().min(1),
3208
+ workingHours: import_zod10.z.object({
3209
+ monday: timeSlotSchema,
3210
+ tuesday: timeSlotSchema,
3211
+ wednesday: timeSlotSchema,
3212
+ thursday: timeSlotSchema,
3213
+ friday: timeSlotSchema,
3214
+ saturday: timeSlotSchema,
3215
+ sunday: timeSlotSchema
3216
+ }),
3217
+ isActive: import_zod10.z.boolean(),
3218
+ createdAt: import_zod10.z.instanceof(import_firestore12.Timestamp),
3219
+ updatedAt: import_zod10.z.instanceof(import_firestore12.Timestamp)
3220
+ });
3160
3221
  var practitionerReviewSchema = import_zod10.z.object({
3161
3222
  id: import_zod10.z.string().min(1),
3162
3223
  practitionerId: import_zod10.z.string().min(1),
@@ -3182,6 +3243,7 @@ var practitionerSchema = import_zod10.z.object({
3182
3243
  basicInfo: practitionerBasicInfoSchema,
3183
3244
  certification: practitionerCertificationSchema,
3184
3245
  clinics: import_zod10.z.array(import_zod10.z.string()),
3246
+ clinicWorkingHours: import_zod10.z.array(practitionerClinicWorkingHoursSchema),
3185
3247
  isActive: import_zod10.z.boolean(),
3186
3248
  isVerified: import_zod10.z.boolean(),
3187
3249
  createdAt: import_zod10.z.instanceof(import_firestore12.Timestamp),
@@ -3192,6 +3254,7 @@ var createPractitionerSchema = import_zod10.z.object({
3192
3254
  basicInfo: practitionerBasicInfoSchema,
3193
3255
  certification: practitionerCertificationSchema,
3194
3256
  clinics: import_zod10.z.array(import_zod10.z.string()).optional(),
3257
+ clinicWorkingHours: import_zod10.z.array(practitionerClinicWorkingHoursSchema).optional(),
3195
3258
  isActive: import_zod10.z.boolean(),
3196
3259
  isVerified: import_zod10.z.boolean()
3197
3260
  });
@@ -3241,6 +3304,7 @@ var PractitionerService = class extends BaseService {
3241
3304
  basicInfo: validatedData.basicInfo,
3242
3305
  certification: validatedData.certification,
3243
3306
  clinics: validatedData.clinics || [],
3307
+ clinicWorkingHours: validatedData.clinicWorkingHours || [],
3244
3308
  isActive: validatedData.isActive,
3245
3309
  isVerified: validatedData.isVerified,
3246
3310
  createdAt: (0, import_firestore13.serverTimestamp)(),
@@ -3303,7 +3367,7 @@ var PractitionerService = class extends BaseService {
3303
3367
  (0, import_firestore13.where)("isActive", "==", true)
3304
3368
  );
3305
3369
  const querySnapshot = await (0, import_firestore13.getDocs)(q);
3306
- return querySnapshot.docs.map((doc14) => doc14.data());
3370
+ return querySnapshot.docs.map((doc20) => doc20.data());
3307
3371
  }
3308
3372
  /**
3309
3373
  * Ažurira profil zdravstvenog radnika
@@ -3583,7 +3647,7 @@ var UserService = class extends BaseService {
3583
3647
  ];
3584
3648
  const q = (0, import_firestore14.query)((0, import_firestore14.collection)(this.db, USERS_COLLECTION), ...constraints);
3585
3649
  const querySnapshot = await (0, import_firestore14.getDocs)(q);
3586
- const users = querySnapshot.docs.map((doc14) => doc14.data());
3650
+ const users = querySnapshot.docs.map((doc20) => doc20.data());
3587
3651
  return Promise.all(users.map((userData) => userSchema.parse(userData)));
3588
3652
  }
3589
3653
  /**
@@ -3966,7 +4030,7 @@ async function getAllActiveGroups(db) {
3966
4030
  (0, import_firestore15.where)("isActive", "==", true)
3967
4031
  );
3968
4032
  const querySnapshot = await (0, import_firestore15.getDocs)(q);
3969
- return querySnapshot.docs.map((doc14) => doc14.data());
4033
+ return querySnapshot.docs.map((doc20) => doc20.data());
3970
4034
  }
3971
4035
  async function updateClinicGroup(db, groupId, data, app) {
3972
4036
  console.log("[CLINIC_GROUP] Updating clinic group", { groupId });
@@ -4626,7 +4690,7 @@ async function getClinicsByGroup(db, groupId) {
4626
4690
  (0, import_firestore16.where)("isActive", "==", true)
4627
4691
  );
4628
4692
  const querySnapshot = await (0, import_firestore16.getDocs)(q);
4629
- return querySnapshot.docs.map((doc14) => doc14.data());
4693
+ return querySnapshot.docs.map((doc20) => doc20.data());
4630
4694
  }
4631
4695
  async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app) {
4632
4696
  console.log("[CLINIC] Starting clinic update", { clinicId, adminId });
@@ -4838,7 +4902,7 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
4838
4902
  }
4839
4903
  const q = (0, import_firestore16.query)((0, import_firestore16.collection)(db, CLINICS_COLLECTION), ...constraints);
4840
4904
  const querySnapshot = await (0, import_firestore16.getDocs)(q);
4841
- return querySnapshot.docs.map((doc14) => doc14.data());
4905
+ return querySnapshot.docs.map((doc20) => doc20.data());
4842
4906
  }
4843
4907
  async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
4844
4908
  return getClinicsByAdmin(
@@ -4980,8 +5044,8 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
4980
5044
  }
4981
5045
  const q = (0, import_firestore18.query)((0, import_firestore18.collection)(db, CLINICS_COLLECTION), ...constraints);
4982
5046
  const querySnapshot = await (0, import_firestore18.getDocs)(q);
4983
- for (const doc14 of querySnapshot.docs) {
4984
- const clinic = doc14.data();
5047
+ for (const doc20 of querySnapshot.docs) {
5048
+ const clinic = doc20.data();
4985
5049
  const distance = (0, import_geofire_common4.distanceBetween)(
4986
5050
  [center.latitude, center.longitude],
4987
5051
  [clinic.location.latitude, clinic.location.longitude]
@@ -5842,9 +5906,9 @@ var NotificationService = class extends BaseService {
5842
5906
  (0, import_firestore20.orderBy)("notificationTime", "desc")
5843
5907
  );
5844
5908
  const querySnapshot = await (0, import_firestore20.getDocs)(q);
5845
- return querySnapshot.docs.map((doc14) => ({
5846
- id: doc14.id,
5847
- ...doc14.data()
5909
+ return querySnapshot.docs.map((doc20) => ({
5910
+ id: doc20.id,
5911
+ ...doc20.data()
5848
5912
  }));
5849
5913
  }
5850
5914
  /**
@@ -5858,9 +5922,9 @@ var NotificationService = class extends BaseService {
5858
5922
  (0, import_firestore20.orderBy)("notificationTime", "desc")
5859
5923
  );
5860
5924
  const querySnapshot = await (0, import_firestore20.getDocs)(q);
5861
- return querySnapshot.docs.map((doc14) => ({
5862
- id: doc14.id,
5863
- ...doc14.data()
5925
+ return querySnapshot.docs.map((doc20) => ({
5926
+ id: doc20.id,
5927
+ ...doc20.data()
5864
5928
  }));
5865
5929
  }
5866
5930
  /**
@@ -5932,9 +5996,9 @@ var NotificationService = class extends BaseService {
5932
5996
  (0, import_firestore20.orderBy)("notificationTime", "desc")
5933
5997
  );
5934
5998
  const querySnapshot = await (0, import_firestore20.getDocs)(q);
5935
- return querySnapshot.docs.map((doc14) => ({
5936
- id: doc14.id,
5937
- ...doc14.data()
5999
+ return querySnapshot.docs.map((doc20) => ({
6000
+ id: doc20.id,
6001
+ ...doc20.data()
5938
6002
  }));
5939
6003
  }
5940
6004
  /**
@@ -5947,9 +6011,9 @@ var NotificationService = class extends BaseService {
5947
6011
  (0, import_firestore20.orderBy)("notificationTime", "desc")
5948
6012
  );
5949
6013
  const querySnapshot = await (0, import_firestore20.getDocs)(q);
5950
- return querySnapshot.docs.map((doc14) => ({
5951
- id: doc14.id,
5952
- ...doc14.data()
6014
+ return querySnapshot.docs.map((doc20) => ({
6015
+ id: doc20.id,
6016
+ ...doc20.data()
5953
6017
  }));
5954
6018
  }
5955
6019
  };
@@ -6066,9 +6130,9 @@ var DocumentationTemplateService = class extends BaseService {
6066
6130
  const querySnapshot = await (0, import_firestore21.getDocs)(q);
6067
6131
  const templates = [];
6068
6132
  let lastVisible = null;
6069
- querySnapshot.forEach((doc14) => {
6070
- templates.push(doc14.data());
6071
- lastVisible = doc14;
6133
+ querySnapshot.forEach((doc20) => {
6134
+ templates.push(doc20.data());
6135
+ lastVisible = doc20;
6072
6136
  });
6073
6137
  return {
6074
6138
  templates,
@@ -6096,9 +6160,9 @@ var DocumentationTemplateService = class extends BaseService {
6096
6160
  const querySnapshot = await (0, import_firestore21.getDocs)(q);
6097
6161
  const templates = [];
6098
6162
  let lastVisible = null;
6099
- querySnapshot.forEach((doc14) => {
6100
- templates.push(doc14.data());
6101
- lastVisible = doc14;
6163
+ querySnapshot.forEach((doc20) => {
6164
+ templates.push(doc20.data());
6165
+ lastVisible = doc20;
6102
6166
  });
6103
6167
  return {
6104
6168
  templates,
@@ -6125,9 +6189,9 @@ var DocumentationTemplateService = class extends BaseService {
6125
6189
  const querySnapshot = await (0, import_firestore21.getDocs)(q);
6126
6190
  const templates = [];
6127
6191
  let lastVisible = null;
6128
- querySnapshot.forEach((doc14) => {
6129
- templates.push(doc14.data());
6130
- lastVisible = doc14;
6192
+ querySnapshot.forEach((doc20) => {
6193
+ templates.push(doc20.data());
6194
+ lastVisible = doc20;
6131
6195
  });
6132
6196
  return {
6133
6197
  templates,
@@ -6240,9 +6304,9 @@ var FilledDocumentService = class extends BaseService {
6240
6304
  const querySnapshot = await (0, import_firestore22.getDocs)(q);
6241
6305
  const documents = [];
6242
6306
  let lastVisible = null;
6243
- querySnapshot.forEach((doc14) => {
6244
- documents.push(doc14.data());
6245
- lastVisible = doc14;
6307
+ querySnapshot.forEach((doc20) => {
6308
+ documents.push(doc20.data());
6309
+ lastVisible = doc20;
6246
6310
  });
6247
6311
  return {
6248
6312
  documents,
@@ -6269,9 +6333,9 @@ var FilledDocumentService = class extends BaseService {
6269
6333
  const querySnapshot = await (0, import_firestore22.getDocs)(q);
6270
6334
  const documents = [];
6271
6335
  let lastVisible = null;
6272
- querySnapshot.forEach((doc14) => {
6273
- documents.push(doc14.data());
6274
- lastVisible = doc14;
6336
+ querySnapshot.forEach((doc20) => {
6337
+ documents.push(doc20.data());
6338
+ lastVisible = doc20;
6275
6339
  });
6276
6340
  return {
6277
6341
  documents,
@@ -6298,9 +6362,9 @@ var FilledDocumentService = class extends BaseService {
6298
6362
  const querySnapshot = await (0, import_firestore22.getDocs)(q);
6299
6363
  const documents = [];
6300
6364
  let lastVisible = null;
6301
- querySnapshot.forEach((doc14) => {
6302
- documents.push(doc14.data());
6303
- lastVisible = doc14;
6365
+ querySnapshot.forEach((doc20) => {
6366
+ documents.push(doc20.data());
6367
+ lastVisible = doc20;
6304
6368
  });
6305
6369
  return {
6306
6370
  documents,
@@ -6327,9 +6391,9 @@ var FilledDocumentService = class extends BaseService {
6327
6391
  const querySnapshot = await (0, import_firestore22.getDocs)(q);
6328
6392
  const documents = [];
6329
6393
  let lastVisible = null;
6330
- querySnapshot.forEach((doc14) => {
6331
- documents.push(doc14.data());
6332
- lastVisible = doc14;
6394
+ querySnapshot.forEach((doc20) => {
6395
+ documents.push(doc20.data());
6396
+ lastVisible = doc20;
6333
6397
  });
6334
6398
  return {
6335
6399
  documents,
@@ -6356,9 +6420,9 @@ var FilledDocumentService = class extends BaseService {
6356
6420
  const querySnapshot = await (0, import_firestore22.getDocs)(q);
6357
6421
  const documents = [];
6358
6422
  let lastVisible = null;
6359
- querySnapshot.forEach((doc14) => {
6360
- documents.push(doc14.data());
6361
- lastVisible = doc14;
6423
+ querySnapshot.forEach((doc20) => {
6424
+ documents.push(doc20.data());
6425
+ lastVisible = doc20;
6362
6426
  });
6363
6427
  return {
6364
6428
  documents,
@@ -6367,55 +6431,2597 @@ var FilledDocumentService = class extends BaseService {
6367
6431
  }
6368
6432
  };
6369
6433
 
6370
- // src/validations/notification.schema.ts
6434
+ // src/services/calendar/calendar-refactored.service.ts
6435
+ var import_firestore31 = require("firebase/firestore");
6436
+
6437
+ // src/types/calendar/synced-calendar.types.ts
6438
+ var SyncedCalendarProvider = /* @__PURE__ */ ((SyncedCalendarProvider3) => {
6439
+ SyncedCalendarProvider3["GOOGLE"] = "google";
6440
+ SyncedCalendarProvider3["OUTLOOK"] = "outlook";
6441
+ SyncedCalendarProvider3["APPLE"] = "apple";
6442
+ return SyncedCalendarProvider3;
6443
+ })(SyncedCalendarProvider || {});
6444
+ var SYNCED_CALENDARS_COLLECTION = "syncedCalendars";
6445
+
6446
+ // src/services/calendar/calendar-refactored.service.ts
6447
+ var import_firestore32 = require("firebase/firestore");
6448
+
6449
+ // src/validations/calendar.schema.ts
6450
+ var import_zod17 = require("zod");
6451
+ var import_firestore24 = require("firebase/firestore");
6452
+
6453
+ // src/validations/profile-info.schema.ts
6371
6454
  var import_zod16 = require("zod");
6372
- var baseNotificationSchema = import_zod16.z.object({
6373
- id: import_zod16.z.string().optional(),
6374
- userId: import_zod16.z.string(),
6375
- notificationTime: import_zod16.z.any(),
6455
+ var import_firestore23 = require("firebase/firestore");
6456
+ var clinicInfoSchema2 = import_zod16.z.object({
6457
+ id: import_zod16.z.string(),
6458
+ featuredPhoto: import_zod16.z.string(),
6459
+ name: import_zod16.z.string(),
6460
+ description: import_zod16.z.string(),
6461
+ location: clinicLocationSchema,
6462
+ contactInfo: clinicContactInfoSchema
6463
+ });
6464
+ var practitionerProfileInfoSchema = import_zod16.z.object({
6465
+ id: import_zod16.z.string(),
6466
+ practitionerPhoto: import_zod16.z.string().nullable(),
6467
+ name: import_zod16.z.string(),
6468
+ email: import_zod16.z.string().email(),
6469
+ phone: import_zod16.z.string().nullable(),
6470
+ certification: practitionerCertificationSchema
6471
+ });
6472
+ var patientProfileInfoSchema = import_zod16.z.object({
6473
+ id: import_zod16.z.string(),
6474
+ fullName: import_zod16.z.string(),
6475
+ email: import_zod16.z.string().email(),
6476
+ phone: import_zod16.z.string().nullable(),
6477
+ dateOfBirth: import_zod16.z.instanceof(import_firestore23.Timestamp),
6478
+ gender: import_zod16.z.nativeEnum(Gender)
6479
+ });
6480
+
6481
+ // src/validations/calendar.schema.ts
6482
+ var MIN_APPOINTMENT_DURATION = 15;
6483
+ var calendarEventTimeSchema = import_zod17.z.object({
6484
+ start: import_zod17.z.instanceof(Date).or(import_zod17.z.instanceof(import_firestore24.Timestamp)),
6485
+ end: import_zod17.z.instanceof(Date).or(import_zod17.z.instanceof(import_firestore24.Timestamp))
6486
+ }).refine(
6487
+ (data) => {
6488
+ const startDate = data.start instanceof import_firestore24.Timestamp ? data.start.toDate() : data.start;
6489
+ const endDate = data.end instanceof import_firestore24.Timestamp ? data.end.toDate() : data.end;
6490
+ return startDate < endDate;
6491
+ },
6492
+ {
6493
+ message: "End time must be after start time",
6494
+ path: ["end"]
6495
+ }
6496
+ ).refine(
6497
+ (data) => {
6498
+ const startDate = data.start instanceof import_firestore24.Timestamp ? data.start.toDate() : data.start;
6499
+ return startDate > /* @__PURE__ */ new Date();
6500
+ },
6501
+ {
6502
+ message: "Appointment must be scheduled in the future",
6503
+ path: ["start"]
6504
+ }
6505
+ );
6506
+ var timeSlotSchema2 = import_zod17.z.object({
6507
+ start: import_zod17.z.date(),
6508
+ end: import_zod17.z.date(),
6509
+ isAvailable: import_zod17.z.boolean()
6510
+ }).refine((data) => data.start < data.end, {
6511
+ message: "End time must be after start time",
6512
+ path: ["end"]
6513
+ });
6514
+ var syncedCalendarEventSchema = import_zod17.z.object({
6515
+ eventId: import_zod17.z.string(),
6516
+ syncedCalendarProvider: import_zod17.z.nativeEnum(SyncedCalendarProvider),
6517
+ syncedAt: import_zod17.z.instanceof(Date).or(import_zod17.z.instanceof(import_firestore24.Timestamp))
6518
+ });
6519
+ var procedureInfoSchema = import_zod17.z.object({
6520
+ name: import_zod17.z.string(),
6521
+ description: import_zod17.z.string(),
6522
+ duration: import_zod17.z.number().min(MIN_APPOINTMENT_DURATION),
6523
+ price: import_zod17.z.number().min(0),
6524
+ currency: import_zod17.z.nativeEnum(Currency)
6525
+ });
6526
+ var procedureCategorizationSchema = import_zod17.z.object({
6527
+ procedureFamily: import_zod17.z.string(),
6528
+ // Replace with proper enum when available
6529
+ procedureCategory: import_zod17.z.string(),
6530
+ // Replace with proper enum when available
6531
+ procedureSubcategory: import_zod17.z.string(),
6532
+ // Replace with proper enum when available
6533
+ procedureTechnology: import_zod17.z.string(),
6534
+ // Replace with proper enum when available
6535
+ procedureProduct: import_zod17.z.string()
6536
+ // Replace with proper enum when available
6537
+ });
6538
+ var createAppointmentSchema = import_zod17.z.object({
6539
+ clinicId: import_zod17.z.string().min(1, "Clinic ID is required"),
6540
+ doctorId: import_zod17.z.string().min(1, "Doctor ID is required"),
6541
+ patientId: import_zod17.z.string().min(1, "Patient ID is required"),
6542
+ procedureId: import_zod17.z.string().min(1, "Procedure ID is required"),
6543
+ eventLocation: clinicLocationSchema,
6544
+ eventTime: calendarEventTimeSchema,
6545
+ description: import_zod17.z.string().optional()
6546
+ }).refine(
6547
+ (data) => {
6548
+ return true;
6549
+ },
6550
+ {
6551
+ message: "Invalid appointment parameters"
6552
+ }
6553
+ );
6554
+ var updateAppointmentSchema = import_zod17.z.object({
6555
+ appointmentId: import_zod17.z.string().min(1, "Appointment ID is required"),
6556
+ clinicId: import_zod17.z.string().min(1, "Clinic ID is required"),
6557
+ doctorId: import_zod17.z.string().min(1, "Doctor ID is required"),
6558
+ patientId: import_zod17.z.string().min(1, "Patient ID is required"),
6559
+ eventTime: calendarEventTimeSchema.optional(),
6560
+ description: import_zod17.z.string().optional(),
6561
+ status: import_zod17.z.nativeEnum(CalendarEventStatus).optional()
6562
+ });
6563
+ var createCalendarEventSchema = import_zod17.z.object({
6564
+ id: import_zod17.z.string(),
6565
+ clinicBranchId: import_zod17.z.string().nullable().optional(),
6566
+ clinicBranchInfo: import_zod17.z.any().nullable().optional(),
6567
+ practitionerProfileId: import_zod17.z.string().nullable().optional(),
6568
+ practitionerProfileInfo: practitionerProfileInfoSchema.nullable().optional(),
6569
+ patientProfileId: import_zod17.z.string().nullable().optional(),
6570
+ patientProfileInfo: patientProfileInfoSchema.nullable().optional(),
6571
+ procedureId: import_zod17.z.string().nullable().optional(),
6572
+ appointmentId: import_zod17.z.string().nullable().optional(),
6573
+ syncedCalendarEventId: import_zod17.z.array(syncedCalendarEventSchema).nullable().optional(),
6574
+ eventName: import_zod17.z.string().min(1, "Event name is required"),
6575
+ eventLocation: clinicLocationSchema.optional(),
6576
+ eventTime: calendarEventTimeSchema,
6577
+ description: import_zod17.z.string().optional(),
6578
+ status: import_zod17.z.nativeEnum(CalendarEventStatus),
6579
+ syncStatus: import_zod17.z.nativeEnum(CalendarSyncStatus),
6580
+ eventType: import_zod17.z.nativeEnum(CalendarEventType),
6581
+ createdAt: import_zod17.z.any(),
6582
+ // FieldValue for server timestamp
6583
+ updatedAt: import_zod17.z.any()
6584
+ // FieldValue for server timestamp
6585
+ });
6586
+ var updateCalendarEventSchema = import_zod17.z.object({
6587
+ syncedCalendarEventId: import_zod17.z.array(syncedCalendarEventSchema).nullable().optional(),
6588
+ appointmentId: import_zod17.z.string().nullable().optional(),
6589
+ eventName: import_zod17.z.string().optional(),
6590
+ eventTime: calendarEventTimeSchema.optional(),
6591
+ description: import_zod17.z.string().optional(),
6592
+ status: import_zod17.z.nativeEnum(CalendarEventStatus).optional(),
6593
+ syncStatus: import_zod17.z.nativeEnum(CalendarSyncStatus).optional(),
6594
+ eventType: import_zod17.z.nativeEnum(CalendarEventType).optional(),
6595
+ updatedAt: import_zod17.z.any()
6596
+ // FieldValue for server timestamp
6597
+ });
6598
+ var calendarEventSchema = import_zod17.z.object({
6599
+ id: import_zod17.z.string(),
6600
+ clinicBranchId: import_zod17.z.string().nullable().optional(),
6601
+ clinicBranchInfo: import_zod17.z.any().nullable().optional(),
6602
+ // Will be replaced with proper clinic info schema
6603
+ practitionerProfileId: import_zod17.z.string().nullable().optional(),
6604
+ practitionerProfileInfo: practitionerProfileInfoSchema.nullable().optional(),
6605
+ patientProfileId: import_zod17.z.string().nullable().optional(),
6606
+ patientProfileInfo: patientProfileInfoSchema.nullable().optional(),
6607
+ procedureId: import_zod17.z.string().nullable().optional(),
6608
+ procedureInfo: procedureInfoSchema.nullable().optional(),
6609
+ procedureCategorization: procedureCategorizationSchema.nullable().optional(),
6610
+ appointmentId: import_zod17.z.string().nullable().optional(),
6611
+ syncedCalendarEventId: import_zod17.z.array(syncedCalendarEventSchema).nullable().optional(),
6612
+ eventName: import_zod17.z.string(),
6613
+ eventLocation: clinicLocationSchema.optional(),
6614
+ eventTime: calendarEventTimeSchema,
6615
+ description: import_zod17.z.string().optional(),
6616
+ status: import_zod17.z.nativeEnum(CalendarEventStatus),
6617
+ syncStatus: import_zod17.z.nativeEnum(CalendarSyncStatus),
6618
+ eventType: import_zod17.z.nativeEnum(CalendarEventType),
6619
+ createdAt: import_zod17.z.instanceof(Date).or(import_zod17.z.instanceof(import_firestore24.Timestamp)),
6620
+ updatedAt: import_zod17.z.instanceof(Date).or(import_zod17.z.instanceof(import_firestore24.Timestamp))
6621
+ });
6622
+
6623
+ // src/services/calendar/utils/clinic.utils.ts
6624
+ var import_firestore26 = require("firebase/firestore");
6625
+
6626
+ // src/services/calendar/utils/docs.utils.ts
6627
+ var import_firestore25 = require("firebase/firestore");
6628
+ function getPractitionerCalendarEventDocRef(db, practitionerId, eventId) {
6629
+ return (0, import_firestore25.doc)(
6630
+ db,
6631
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
6632
+ );
6633
+ }
6634
+ function getPatientCalendarEventDocRef(db, patientId, eventId) {
6635
+ return (0, import_firestore25.doc)(
6636
+ db,
6637
+ `${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
6638
+ );
6639
+ }
6640
+ function getClinicCalendarEventDocRef(db, clinicId, eventId) {
6641
+ return (0, import_firestore25.doc)(
6642
+ db,
6643
+ `${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
6644
+ );
6645
+ }
6646
+ function getPractitionerSyncedCalendarDocRef(db, practitionerId, syncedCalendarId) {
6647
+ return (0, import_firestore25.doc)(
6648
+ db,
6649
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/syncedCalendars/${syncedCalendarId}`
6650
+ );
6651
+ }
6652
+ function getPatientSyncedCalendarDocRef(db, patientId, syncedCalendarId) {
6653
+ return (0, import_firestore25.doc)(
6654
+ db,
6655
+ `${PATIENTS_COLLECTION}/${patientId}/syncedCalendars/${syncedCalendarId}`
6656
+ );
6657
+ }
6658
+ function getClinicSyncedCalendarDocRef(db, clinicId, syncedCalendarId) {
6659
+ return (0, import_firestore25.doc)(
6660
+ db,
6661
+ `${CLINICS_COLLECTION}/${clinicId}/syncedCalendars/${syncedCalendarId}`
6662
+ );
6663
+ }
6664
+
6665
+ // src/services/calendar/utils/clinic.utils.ts
6666
+ async function createClinicCalendarEventUtil(db, clinicId, eventData, generateId2) {
6667
+ const eventId = generateId2();
6668
+ const eventRef = getClinicCalendarEventDocRef(db, clinicId, eventId);
6669
+ const newEvent = {
6670
+ id: eventId,
6671
+ ...eventData,
6672
+ createdAt: (0, import_firestore26.serverTimestamp)(),
6673
+ updatedAt: (0, import_firestore26.serverTimestamp)()
6674
+ };
6675
+ await (0, import_firestore26.setDoc)(eventRef, newEvent);
6676
+ return {
6677
+ ...newEvent,
6678
+ createdAt: import_firestore26.Timestamp.now(),
6679
+ updatedAt: import_firestore26.Timestamp.now()
6680
+ };
6681
+ }
6682
+ async function updateClinicCalendarEventUtil(db, clinicId, eventId, updateData) {
6683
+ const eventRef = getClinicCalendarEventDocRef(db, clinicId, eventId);
6684
+ const updates = {
6685
+ ...updateData,
6686
+ updatedAt: (0, import_firestore26.serverTimestamp)()
6687
+ };
6688
+ await (0, import_firestore26.updateDoc)(eventRef, updates);
6689
+ const updatedDoc = await (0, import_firestore26.getDoc)(eventRef);
6690
+ if (!updatedDoc.exists()) {
6691
+ throw new Error("Event not found after update");
6692
+ }
6693
+ return updatedDoc.data();
6694
+ }
6695
+ async function checkAutoConfirmAppointmentsUtil(db, clinicId) {
6696
+ const clinicDoc = await (0, import_firestore26.getDoc)((0, import_firestore26.doc)(db, `clinics/${clinicId}`));
6697
+ if (!clinicDoc.exists()) {
6698
+ throw new Error(`Clinic with ID ${clinicId} not found`);
6699
+ }
6700
+ const clinicData = clinicDoc.data();
6701
+ const clinicGroupId = clinicData.clinicGroupId;
6702
+ if (!clinicGroupId) {
6703
+ return false;
6704
+ }
6705
+ const clinicGroupDoc = await (0, import_firestore26.getDoc)(
6706
+ (0, import_firestore26.doc)(db, `${CLINIC_GROUPS_COLLECTION}/${clinicGroupId}`)
6707
+ );
6708
+ if (!clinicGroupDoc.exists()) {
6709
+ return false;
6710
+ }
6711
+ const clinicGroupData = clinicGroupDoc.data();
6712
+ return !!clinicGroupData.autoConfirmAppointments;
6713
+ }
6714
+
6715
+ // src/services/calendar/utils/patient.utils.ts
6716
+ var import_firestore27 = require("firebase/firestore");
6717
+ async function createPatientCalendarEventUtil(db, patientId, eventData, generateId2) {
6718
+ const eventId = generateId2();
6719
+ const eventRef = getPatientCalendarEventDocRef(db, patientId, eventId);
6720
+ const newEvent = {
6721
+ id: eventId,
6722
+ ...eventData,
6723
+ createdAt: (0, import_firestore27.serverTimestamp)(),
6724
+ updatedAt: (0, import_firestore27.serverTimestamp)()
6725
+ };
6726
+ await (0, import_firestore27.setDoc)(eventRef, newEvent);
6727
+ return {
6728
+ ...newEvent,
6729
+ createdAt: import_firestore27.Timestamp.now(),
6730
+ updatedAt: import_firestore27.Timestamp.now()
6731
+ };
6732
+ }
6733
+ async function updatePatientCalendarEventUtil(db, patientId, eventId, updateData) {
6734
+ const eventRef = getPatientCalendarEventDocRef(db, patientId, eventId);
6735
+ const updates = {
6736
+ ...updateData,
6737
+ updatedAt: (0, import_firestore27.serverTimestamp)()
6738
+ };
6739
+ await (0, import_firestore27.updateDoc)(eventRef, updates);
6740
+ const updatedDoc = await (0, import_firestore27.getDoc)(eventRef);
6741
+ if (!updatedDoc.exists()) {
6742
+ throw new Error("Event not found after update");
6743
+ }
6744
+ return updatedDoc.data();
6745
+ }
6746
+
6747
+ // src/services/calendar/utils/practitioner.utils.ts
6748
+ var import_firestore28 = require("firebase/firestore");
6749
+ async function createPractitionerCalendarEventUtil(db, practitionerId, eventData, generateId2) {
6750
+ const eventId = generateId2();
6751
+ const eventRef = getPractitionerCalendarEventDocRef(
6752
+ db,
6753
+ practitionerId,
6754
+ eventId
6755
+ );
6756
+ const newEvent = {
6757
+ id: eventId,
6758
+ ...eventData,
6759
+ createdAt: (0, import_firestore28.serverTimestamp)(),
6760
+ updatedAt: (0, import_firestore28.serverTimestamp)()
6761
+ };
6762
+ await (0, import_firestore28.setDoc)(eventRef, newEvent);
6763
+ return {
6764
+ ...newEvent,
6765
+ createdAt: import_firestore28.Timestamp.now(),
6766
+ updatedAt: import_firestore28.Timestamp.now()
6767
+ };
6768
+ }
6769
+ async function updatePractitionerCalendarEventUtil(db, practitionerId, eventId, updateData) {
6770
+ const eventRef = getPractitionerCalendarEventDocRef(
6771
+ db,
6772
+ practitionerId,
6773
+ eventId
6774
+ );
6775
+ const updates = {
6776
+ ...updateData,
6777
+ updatedAt: (0, import_firestore28.serverTimestamp)()
6778
+ };
6779
+ await (0, import_firestore28.updateDoc)(eventRef, updates);
6780
+ const updatedDoc = await (0, import_firestore28.getDoc)(eventRef);
6781
+ if (!updatedDoc.exists()) {
6782
+ throw new Error("Event not found after update");
6783
+ }
6784
+ return updatedDoc.data();
6785
+ }
6786
+
6787
+ // src/services/calendar/utils/appointment.utils.ts
6788
+ async function createAppointmentUtil(db, clinicId, practitionerId, patientId, eventData, generateId2) {
6789
+ const eventId = generateId2();
6790
+ const autoConfirm = await checkAutoConfirmAppointmentsUtil(db, clinicId);
6791
+ const initialStatus = autoConfirm ? "confirmed" /* CONFIRMED */ : "pending" /* PENDING */;
6792
+ const appointmentData = {
6793
+ ...eventData,
6794
+ clinicBranchId: clinicId,
6795
+ practitionerProfileId: practitionerId,
6796
+ patientProfileId: patientId,
6797
+ eventType: "appointment" /* APPOINTMENT */,
6798
+ status: eventData.status || initialStatus
6799
+ };
6800
+ const clinicPromise = createClinicCalendarEventUtil(
6801
+ db,
6802
+ clinicId,
6803
+ appointmentData,
6804
+ () => eventId
6805
+ // Use the same ID for all calendars
6806
+ );
6807
+ const practitionerPromise = createPractitionerCalendarEventUtil(
6808
+ db,
6809
+ practitionerId,
6810
+ appointmentData,
6811
+ () => eventId
6812
+ // Use the same ID for all calendars
6813
+ );
6814
+ const patientPromise = createPatientCalendarEventUtil(
6815
+ db,
6816
+ patientId,
6817
+ appointmentData,
6818
+ () => eventId
6819
+ // Use the same ID for all calendars
6820
+ );
6821
+ const [clinicEvent] = await Promise.all([
6822
+ clinicPromise,
6823
+ practitionerPromise,
6824
+ patientPromise
6825
+ ]);
6826
+ return clinicEvent;
6827
+ }
6828
+ async function updateAppointmentUtil(db, clinicId, practitionerId, patientId, eventId, updateData) {
6829
+ const clinicPromise = updateClinicCalendarEventUtil(
6830
+ db,
6831
+ clinicId,
6832
+ eventId,
6833
+ updateData
6834
+ );
6835
+ const practitionerPromise = updatePractitionerCalendarEventUtil(
6836
+ db,
6837
+ practitionerId,
6838
+ eventId,
6839
+ updateData
6840
+ );
6841
+ const patientPromise = updatePatientCalendarEventUtil(
6842
+ db,
6843
+ patientId,
6844
+ eventId,
6845
+ updateData
6846
+ );
6847
+ const [clinicEvent] = await Promise.all([
6848
+ clinicPromise,
6849
+ practitionerPromise,
6850
+ patientPromise
6851
+ ]);
6852
+ return clinicEvent;
6853
+ }
6854
+
6855
+ // src/services/calendar/utils/synced-calendar.utils.ts
6856
+ var import_firestore29 = require("firebase/firestore");
6857
+ async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendarData, generateId2) {
6858
+ const calendarId = generateId2();
6859
+ const calendarRef = getPractitionerSyncedCalendarDocRef(
6860
+ db,
6861
+ practitionerId,
6862
+ calendarId
6863
+ );
6864
+ const newCalendar = {
6865
+ id: calendarId,
6866
+ ...calendarData,
6867
+ createdAt: (0, import_firestore29.serverTimestamp)(),
6868
+ updatedAt: (0, import_firestore29.serverTimestamp)()
6869
+ };
6870
+ await (0, import_firestore29.setDoc)(calendarRef, newCalendar);
6871
+ return {
6872
+ ...newCalendar,
6873
+ createdAt: import_firestore29.Timestamp.now(),
6874
+ updatedAt: import_firestore29.Timestamp.now()
6875
+ };
6876
+ }
6877
+ async function createPatientSyncedCalendarUtil(db, patientId, calendarData, generateId2) {
6878
+ const calendarId = generateId2();
6879
+ const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
6880
+ const newCalendar = {
6881
+ id: calendarId,
6882
+ ...calendarData,
6883
+ createdAt: (0, import_firestore29.serverTimestamp)(),
6884
+ updatedAt: (0, import_firestore29.serverTimestamp)()
6885
+ };
6886
+ await (0, import_firestore29.setDoc)(calendarRef, newCalendar);
6887
+ return {
6888
+ ...newCalendar,
6889
+ createdAt: import_firestore29.Timestamp.now(),
6890
+ updatedAt: import_firestore29.Timestamp.now()
6891
+ };
6892
+ }
6893
+ async function createClinicSyncedCalendarUtil(db, clinicId, calendarData, generateId2) {
6894
+ const calendarId = generateId2();
6895
+ const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
6896
+ const newCalendar = {
6897
+ id: calendarId,
6898
+ ...calendarData,
6899
+ createdAt: (0, import_firestore29.serverTimestamp)(),
6900
+ updatedAt: (0, import_firestore29.serverTimestamp)()
6901
+ };
6902
+ await (0, import_firestore29.setDoc)(calendarRef, newCalendar);
6903
+ return {
6904
+ ...newCalendar,
6905
+ createdAt: import_firestore29.Timestamp.now(),
6906
+ updatedAt: import_firestore29.Timestamp.now()
6907
+ };
6908
+ }
6909
+ async function getPractitionerSyncedCalendarUtil(db, practitionerId, calendarId) {
6910
+ const calendarRef = getPractitionerSyncedCalendarDocRef(
6911
+ db,
6912
+ practitionerId,
6913
+ calendarId
6914
+ );
6915
+ const calendarDoc = await (0, import_firestore29.getDoc)(calendarRef);
6916
+ if (!calendarDoc.exists()) {
6917
+ return null;
6918
+ }
6919
+ return calendarDoc.data();
6920
+ }
6921
+ async function getPractitionerSyncedCalendarsUtil(db, practitionerId) {
6922
+ const calendarsRef = (0, import_firestore29.collection)(
6923
+ db,
6924
+ `practitioners/${practitionerId}/${SYNCED_CALENDARS_COLLECTION}`
6925
+ );
6926
+ const q = (0, import_firestore29.query)(calendarsRef, (0, import_firestore29.orderBy)("createdAt", "desc"));
6927
+ const querySnapshot = await (0, import_firestore29.getDocs)(q);
6928
+ return querySnapshot.docs.map((doc20) => doc20.data());
6929
+ }
6930
+ async function getPatientSyncedCalendarUtil(db, patientId, calendarId) {
6931
+ const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
6932
+ const calendarDoc = await (0, import_firestore29.getDoc)(calendarRef);
6933
+ if (!calendarDoc.exists()) {
6934
+ return null;
6935
+ }
6936
+ return calendarDoc.data();
6937
+ }
6938
+ async function getPatientSyncedCalendarsUtil(db, patientId) {
6939
+ const calendarsRef = (0, import_firestore29.collection)(
6940
+ db,
6941
+ `patients/${patientId}/${SYNCED_CALENDARS_COLLECTION}`
6942
+ );
6943
+ const q = (0, import_firestore29.query)(calendarsRef, (0, import_firestore29.orderBy)("createdAt", "desc"));
6944
+ const querySnapshot = await (0, import_firestore29.getDocs)(q);
6945
+ return querySnapshot.docs.map((doc20) => doc20.data());
6946
+ }
6947
+ async function getClinicSyncedCalendarUtil(db, clinicId, calendarId) {
6948
+ const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
6949
+ const calendarDoc = await (0, import_firestore29.getDoc)(calendarRef);
6950
+ if (!calendarDoc.exists()) {
6951
+ return null;
6952
+ }
6953
+ return calendarDoc.data();
6954
+ }
6955
+ async function getClinicSyncedCalendarsUtil(db, clinicId) {
6956
+ const calendarsRef = (0, import_firestore29.collection)(
6957
+ db,
6958
+ `clinics/${clinicId}/${SYNCED_CALENDARS_COLLECTION}`
6959
+ );
6960
+ const q = (0, import_firestore29.query)(calendarsRef, (0, import_firestore29.orderBy)("createdAt", "desc"));
6961
+ const querySnapshot = await (0, import_firestore29.getDocs)(q);
6962
+ return querySnapshot.docs.map((doc20) => doc20.data());
6963
+ }
6964
+ async function updatePractitionerSyncedCalendarUtil(db, practitionerId, calendarId, updateData) {
6965
+ const calendarRef = getPractitionerSyncedCalendarDocRef(
6966
+ db,
6967
+ practitionerId,
6968
+ calendarId
6969
+ );
6970
+ const updates = {
6971
+ ...updateData,
6972
+ updatedAt: (0, import_firestore29.serverTimestamp)()
6973
+ };
6974
+ await (0, import_firestore29.updateDoc)(calendarRef, updates);
6975
+ const updatedDoc = await (0, import_firestore29.getDoc)(calendarRef);
6976
+ if (!updatedDoc.exists()) {
6977
+ throw new Error("Synced calendar not found after update");
6978
+ }
6979
+ return updatedDoc.data();
6980
+ }
6981
+ async function updatePatientSyncedCalendarUtil(db, patientId, calendarId, updateData) {
6982
+ const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
6983
+ const updates = {
6984
+ ...updateData,
6985
+ updatedAt: (0, import_firestore29.serverTimestamp)()
6986
+ };
6987
+ await (0, import_firestore29.updateDoc)(calendarRef, updates);
6988
+ const updatedDoc = await (0, import_firestore29.getDoc)(calendarRef);
6989
+ if (!updatedDoc.exists()) {
6990
+ throw new Error("Synced calendar not found after update");
6991
+ }
6992
+ return updatedDoc.data();
6993
+ }
6994
+ async function updateClinicSyncedCalendarUtil(db, clinicId, calendarId, updateData) {
6995
+ const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
6996
+ const updates = {
6997
+ ...updateData,
6998
+ updatedAt: (0, import_firestore29.serverTimestamp)()
6999
+ };
7000
+ await (0, import_firestore29.updateDoc)(calendarRef, updates);
7001
+ const updatedDoc = await (0, import_firestore29.getDoc)(calendarRef);
7002
+ if (!updatedDoc.exists()) {
7003
+ throw new Error("Synced calendar not found after update");
7004
+ }
7005
+ return updatedDoc.data();
7006
+ }
7007
+ async function deletePractitionerSyncedCalendarUtil(db, practitionerId, calendarId) {
7008
+ const calendarRef = getPractitionerSyncedCalendarDocRef(
7009
+ db,
7010
+ practitionerId,
7011
+ calendarId
7012
+ );
7013
+ await (0, import_firestore29.deleteDoc)(calendarRef);
7014
+ }
7015
+ async function deletePatientSyncedCalendarUtil(db, patientId, calendarId) {
7016
+ const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
7017
+ await (0, import_firestore29.deleteDoc)(calendarRef);
7018
+ }
7019
+ async function deleteClinicSyncedCalendarUtil(db, clinicId, calendarId) {
7020
+ const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
7021
+ await (0, import_firestore29.deleteDoc)(calendarRef);
7022
+ }
7023
+ async function updateLastSyncedTimestampUtil(db, entityType, entityId, calendarId) {
7024
+ const updateData = {
7025
+ lastSyncedAt: import_firestore29.Timestamp.now()
7026
+ };
7027
+ switch (entityType) {
7028
+ case "practitioner":
7029
+ return updatePractitionerSyncedCalendarUtil(
7030
+ db,
7031
+ entityId,
7032
+ calendarId,
7033
+ updateData
7034
+ );
7035
+ case "patient":
7036
+ return updatePatientSyncedCalendarUtil(
7037
+ db,
7038
+ entityId,
7039
+ calendarId,
7040
+ updateData
7041
+ );
7042
+ case "clinic":
7043
+ return updateClinicSyncedCalendarUtil(
7044
+ db,
7045
+ entityId,
7046
+ calendarId,
7047
+ updateData
7048
+ );
7049
+ default:
7050
+ throw new Error(`Invalid entity type: ${entityType}`);
7051
+ }
7052
+ }
7053
+
7054
+ // src/services/calendar/utils/google-calendar.utils.ts
7055
+ var import_firestore30 = require("firebase/firestore");
7056
+ var GOOGLE_CALENDAR_API_URL = "https://www.googleapis.com/calendar/v3";
7057
+ var GOOGLE_OAUTH_URL = "https://oauth2.googleapis.com/token";
7058
+ var CLIENT_ID = "your-client-id";
7059
+ var CLIENT_SECRET = "your-client-secret";
7060
+ var REDIRECT_URI = "your-redirect-uri";
7061
+ async function makeRequest(method, url, headers, data, params) {
7062
+ const queryParams = params ? "?" + Object.entries(params).map(
7063
+ ([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
7064
+ ).join("&") : "";
7065
+ const finalUrl = url + queryParams;
7066
+ const options = {
7067
+ method,
7068
+ headers,
7069
+ body: data ? JSON.stringify(data) : void 0
7070
+ };
7071
+ const response = await fetch(finalUrl, options);
7072
+ if (!response.ok) {
7073
+ const error = new Error(
7074
+ `Request failed with status ${response.status}`
7075
+ );
7076
+ error.response = response;
7077
+ throw error;
7078
+ }
7079
+ return response.json();
7080
+ }
7081
+ async function authenticateWithGoogleCalendarUtil(authCode) {
7082
+ try {
7083
+ const data = {
7084
+ code: authCode,
7085
+ client_id: CLIENT_ID,
7086
+ client_secret: CLIENT_SECRET,
7087
+ redirect_uri: REDIRECT_URI,
7088
+ grant_type: "authorization_code"
7089
+ };
7090
+ const response = await makeRequest(
7091
+ "post",
7092
+ GOOGLE_OAUTH_URL,
7093
+ { "Content-Type": "application/json" },
7094
+ data
7095
+ );
7096
+ return {
7097
+ accessToken: response.access_token,
7098
+ refreshToken: response.refresh_token,
7099
+ expiresIn: response.expires_in
7100
+ };
7101
+ } catch (error) {
7102
+ const apiError = error;
7103
+ console.error(
7104
+ "Error authenticating with Google Calendar:",
7105
+ apiError.message || "Unknown error"
7106
+ );
7107
+ throw new Error(
7108
+ `Failed to authenticate with Google Calendar: ${apiError.message || "Unknown error"}`
7109
+ );
7110
+ }
7111
+ }
7112
+ async function refreshGoogleCalendarTokenUtil(refreshToken) {
7113
+ try {
7114
+ const data = {
7115
+ refresh_token: refreshToken,
7116
+ client_id: CLIENT_ID,
7117
+ client_secret: CLIENT_SECRET,
7118
+ grant_type: "refresh_token"
7119
+ };
7120
+ const response = await makeRequest(
7121
+ "post",
7122
+ GOOGLE_OAUTH_URL,
7123
+ { "Content-Type": "application/json" },
7124
+ data
7125
+ );
7126
+ return {
7127
+ accessToken: response.access_token,
7128
+ expiresIn: response.expires_in
7129
+ };
7130
+ } catch (error) {
7131
+ const apiError = error;
7132
+ console.error(
7133
+ "Error refreshing Google Calendar token:",
7134
+ apiError.message || "Unknown error"
7135
+ );
7136
+ throw new Error(
7137
+ `Failed to refresh Google Calendar token: ${apiError.message || "Unknown error"}`
7138
+ );
7139
+ }
7140
+ }
7141
+ async function listGoogleCalendarsUtil(accessToken) {
7142
+ try {
7143
+ const response = await makeRequest(
7144
+ "get",
7145
+ `${GOOGLE_CALENDAR_API_URL}/users/me/calendarList`,
7146
+ { Authorization: `Bearer ${accessToken}` }
7147
+ );
7148
+ return response.items.map((calendar) => ({
7149
+ id: calendar.id,
7150
+ name: calendar.summary
7151
+ }));
7152
+ } catch (error) {
7153
+ const apiError = error;
7154
+ console.error(
7155
+ "Error listing Google Calendars:",
7156
+ apiError.message || "Unknown error"
7157
+ );
7158
+ throw new Error(
7159
+ `Failed to list Google Calendars: ${apiError.message || "Unknown error"}`
7160
+ );
7161
+ }
7162
+ }
7163
+ async function ensureValidToken(db, entityType, entityId, syncedCalendar) {
7164
+ const expiryTime = syncedCalendar.tokenExpiry.toDate();
7165
+ const now = /* @__PURE__ */ new Date();
7166
+ const fiveMinutesFromNow = new Date(now.getTime() + 5 * 60 * 1e3);
7167
+ if (expiryTime < fiveMinutesFromNow) {
7168
+ const { accessToken, expiresIn } = await refreshGoogleCalendarTokenUtil(
7169
+ syncedCalendar.refreshToken
7170
+ );
7171
+ const tokenExpiry = /* @__PURE__ */ new Date();
7172
+ tokenExpiry.setSeconds(tokenExpiry.getSeconds() + expiresIn);
7173
+ const updateData = {
7174
+ accessToken,
7175
+ tokenExpiry: import_firestore30.Timestamp.fromDate(tokenExpiry)
7176
+ };
7177
+ switch (entityType) {
7178
+ case "practitioner":
7179
+ await updatePractitionerSyncedCalendarUtil(
7180
+ db,
7181
+ entityId,
7182
+ syncedCalendar.id,
7183
+ updateData
7184
+ );
7185
+ break;
7186
+ case "patient":
7187
+ await updatePatientSyncedCalendarUtil(
7188
+ db,
7189
+ entityId,
7190
+ syncedCalendar.id,
7191
+ updateData
7192
+ );
7193
+ break;
7194
+ case "clinic":
7195
+ await updateClinicSyncedCalendarUtil(
7196
+ db,
7197
+ entityId,
7198
+ syncedCalendar.id,
7199
+ updateData
7200
+ );
7201
+ break;
7202
+ }
7203
+ return accessToken;
7204
+ }
7205
+ return syncedCalendar.accessToken;
7206
+ }
7207
+ async function syncEventsToGoogleCalendarUtil(db, entityType, entityId, syncedCalendar, events, existingSyncId) {
7208
+ var _a, _b;
7209
+ try {
7210
+ const { accessToken } = await refreshGoogleCalendarTokenUtil(
7211
+ syncedCalendar.refreshToken
7212
+ );
7213
+ let syncedCount = 0;
7214
+ const errors = [];
7215
+ const eventIds = [];
7216
+ for (const event of events) {
7217
+ try {
7218
+ if (event.syncStatus === "external" /* EXTERNAL */) {
7219
+ continue;
7220
+ }
7221
+ if (entityType === "practitioner" && event.status !== "confirmed" /* CONFIRMED */) {
7222
+ continue;
7223
+ }
7224
+ if (entityType === "patient" && (event.status === "canceled" /* CANCELED */ || event.status === "rejected" /* REJECTED */)) {
7225
+ continue;
7226
+ }
7227
+ if (entityType === "clinic") {
7228
+ continue;
7229
+ }
7230
+ const googleEvent = convertCalendarEventToGoogleEventUtil(event);
7231
+ const headers = {
7232
+ Authorization: `Bearer ${accessToken}`,
7233
+ "Content-Type": "application/json"
7234
+ };
7235
+ let responseId = "";
7236
+ if (existingSyncId) {
7237
+ const response = await makeRequest(
7238
+ "put",
7239
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${existingSyncId}`,
7240
+ headers,
7241
+ googleEvent
7242
+ );
7243
+ responseId = response.id;
7244
+ } else {
7245
+ const existingSync = (_a = event.syncedCalendarEventId) == null ? void 0 : _a.find(
7246
+ (sync) => sync.syncedCalendarProvider === "google" /* GOOGLE */ && // We should check if this is the same calendar we're syncing with, but that information isn't stored
7247
+ // For now, we'll just use the first Google Calendar sync ID
7248
+ sync.syncedCalendarProvider === syncedCalendar.provider
7249
+ );
7250
+ if (existingSync) {
7251
+ const response = await makeRequest(
7252
+ "put",
7253
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${existingSync.eventId}`,
7254
+ headers,
7255
+ googleEvent
7256
+ );
7257
+ responseId = response.id;
7258
+ } else {
7259
+ const response = await makeRequest(
7260
+ "post",
7261
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events`,
7262
+ headers,
7263
+ googleEvent
7264
+ );
7265
+ responseId = response.id;
7266
+ }
7267
+ }
7268
+ if (responseId) {
7269
+ eventIds.push(responseId);
7270
+ syncedCount++;
7271
+ }
7272
+ } catch (error) {
7273
+ const apiError = error;
7274
+ errors.push({
7275
+ eventId: event.id,
7276
+ error: apiError.message || "Unknown error",
7277
+ status: (_b = apiError.response) == null ? void 0 : _b.status
7278
+ });
7279
+ }
7280
+ }
7281
+ await updateLastSyncedTimestampUtil(
7282
+ db,
7283
+ entityType,
7284
+ entityId,
7285
+ syncedCalendar.id
7286
+ );
7287
+ return {
7288
+ success: errors.length === 0,
7289
+ syncedEvents: syncedCount,
7290
+ errors,
7291
+ eventIds
7292
+ };
7293
+ } catch (error) {
7294
+ console.error("Error syncing with Google Calendar:", error);
7295
+ return {
7296
+ success: false,
7297
+ syncedEvents: 0,
7298
+ errors: [{ error: error.message || "Unknown error" }],
7299
+ eventIds: []
7300
+ };
7301
+ }
7302
+ }
7303
+ async function fetchEventsFromGoogleCalendarUtil(db, entityType, entityId, syncedCalendar, startDate, endDate) {
7304
+ try {
7305
+ const accessToken = await ensureValidToken(
7306
+ db,
7307
+ entityType,
7308
+ entityId,
7309
+ syncedCalendar
7310
+ );
7311
+ const timeMin = startDate.toISOString();
7312
+ const timeMax = endDate.toISOString();
7313
+ const response = await makeRequest(
7314
+ "get",
7315
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events`,
7316
+ { Authorization: `Bearer ${accessToken}` },
7317
+ void 0,
7318
+ {
7319
+ timeMin,
7320
+ timeMax,
7321
+ singleEvents: "true",
7322
+ orderBy: "startTime"
7323
+ }
7324
+ );
7325
+ await updateLastSyncedTimestampUtil(
7326
+ db,
7327
+ entityType,
7328
+ entityId,
7329
+ syncedCalendar.id
7330
+ );
7331
+ return response.items;
7332
+ } catch (error) {
7333
+ const apiError = error;
7334
+ console.error(
7335
+ "Error fetching events from Google Calendar:",
7336
+ apiError.message || "Unknown error"
7337
+ );
7338
+ throw new Error(
7339
+ `Failed to fetch events from Google Calendar: ${apiError.message || "Unknown error"}`
7340
+ );
7341
+ }
7342
+ }
7343
+ function convertGoogleEventToCalendarEventUtil(googleEvent, entityId, entityType) {
7344
+ const start = googleEvent.start.dateTime ? new Date(googleEvent.start.dateTime) : new Date(googleEvent.start.date);
7345
+ const end = googleEvent.end.dateTime ? new Date(googleEvent.end.dateTime) : new Date(googleEvent.end.date);
7346
+ const calendarEvent = {
7347
+ eventName: googleEvent.summary || "External Event",
7348
+ eventLocation: googleEvent.location,
7349
+ eventTime: {
7350
+ start: import_firestore30.Timestamp.fromDate(start),
7351
+ end: import_firestore30.Timestamp.fromDate(end)
7352
+ },
7353
+ description: googleEvent.description || "",
7354
+ // External events are always set as CONFIRMED - status updates will happen externally
7355
+ status: "confirmed" /* CONFIRMED */,
7356
+ // All external events are marked as EXTERNAL to indicate they originated outside our system
7357
+ syncStatus: "external" /* EXTERNAL */,
7358
+ // All external events are treated as BLOCKING events
7359
+ eventType: "blocking" /* BLOCKING */,
7360
+ // Store the original Google Calendar event ID
7361
+ syncedCalendarEventId: [
7362
+ {
7363
+ eventId: googleEvent.id,
7364
+ syncedCalendarProvider: "google" /* GOOGLE */,
7365
+ syncedAt: import_firestore30.Timestamp.now()
7366
+ }
7367
+ ]
7368
+ };
7369
+ switch (entityType) {
7370
+ case "practitioner":
7371
+ calendarEvent.practitionerProfileId = entityId;
7372
+ break;
7373
+ case "patient":
7374
+ calendarEvent.patientProfileId = entityId;
7375
+ break;
7376
+ case "clinic":
7377
+ calendarEvent.clinicBranchId = entityId;
7378
+ break;
7379
+ }
7380
+ return calendarEvent;
7381
+ }
7382
+ function convertCalendarEventToGoogleEventUtil(calendarEvent) {
7383
+ const googleEvent = {
7384
+ summary: calendarEvent.eventName,
7385
+ location: calendarEvent.eventLocation,
7386
+ description: calendarEvent.description,
7387
+ start: {
7388
+ dateTime: calendarEvent.eventTime.start.toDate().toISOString(),
7389
+ timeZone: "UTC"
7390
+ },
7391
+ end: {
7392
+ dateTime: calendarEvent.eventTime.end.toDate().toISOString(),
7393
+ timeZone: "UTC"
7394
+ },
7395
+ // Add reminders
7396
+ reminders: {
7397
+ useDefault: false,
7398
+ overrides: [
7399
+ { method: "email", minutes: 24 * 60 },
7400
+ // 1 day before
7401
+ { method: "popup", minutes: 30 }
7402
+ // 30 minutes before
7403
+ ]
7404
+ }
7405
+ };
7406
+ switch (calendarEvent.status) {
7407
+ case "confirmed" /* CONFIRMED */:
7408
+ googleEvent.status = "confirmed";
7409
+ break;
7410
+ case "canceled" /* CANCELED */:
7411
+ googleEvent.status = "cancelled";
7412
+ break;
7413
+ case "pending" /* PENDING */:
7414
+ googleEvent.status = "tentative";
7415
+ break;
7416
+ default:
7417
+ googleEvent.status = "confirmed";
7418
+ }
7419
+ if (calendarEvent.eventType === "appointment" /* APPOINTMENT */) {
7420
+ googleEvent.attendees = [];
7421
+ if (calendarEvent.practitionerProfileId) {
7422
+ googleEvent.attendees.push({
7423
+ email: "practitioner@example.com",
7424
+ // This would be fetched from the practitioner profile
7425
+ displayName: "Dr. Practitioner",
7426
+ // This would be fetched from the practitioner profile
7427
+ responseStatus: "accepted"
7428
+ });
7429
+ }
7430
+ if (calendarEvent.patientProfileId) {
7431
+ googleEvent.attendees.push({
7432
+ email: "patient@example.com",
7433
+ // This would be fetched from the patient profile
7434
+ displayName: "Patient",
7435
+ // This would be fetched from the patient profile
7436
+ responseStatus: "needsAction"
7437
+ });
7438
+ }
7439
+ }
7440
+ return googleEvent;
7441
+ }
7442
+ async function deleteGoogleCalendarEventUtil(db, entityType, entityId, syncedCalendar, eventId) {
7443
+ try {
7444
+ const accessToken = await ensureValidToken(
7445
+ db,
7446
+ entityType,
7447
+ entityId,
7448
+ syncedCalendar
7449
+ );
7450
+ await makeRequest(
7451
+ "delete",
7452
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${eventId}`,
7453
+ { Authorization: `Bearer ${accessToken}` }
7454
+ );
7455
+ return true;
7456
+ } catch (error) {
7457
+ const apiError = error;
7458
+ console.error(
7459
+ "Error deleting event from Google Calendar:",
7460
+ apiError.message || "Unknown error"
7461
+ );
7462
+ throw new Error(
7463
+ `Failed to delete event from Google Calendar: ${apiError.message || "Unknown error"}`
7464
+ );
7465
+ }
7466
+ }
7467
+ function getGoogleCalendarOAuthUrlUtil(scopes = ["https://www.googleapis.com/auth/calendar"]) {
7468
+ const scopeString = encodeURIComponent(scopes.join(" "));
7469
+ return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(
7470
+ REDIRECT_URI
7471
+ )}&response_type=code&scope=${scopeString}&access_type=offline&prompt=consent`;
7472
+ }
7473
+
7474
+ // src/services/calendar/synced-calendars.service.ts
7475
+ var SyncedCalendarsService = class extends BaseService {
7476
+ /**
7477
+ * Creates a new SyncedCalendarsService instance
7478
+ * @param db - Firestore instance
7479
+ * @param auth - Firebase Auth instance
7480
+ * @param app - Firebase App instance
7481
+ */
7482
+ constructor(db, auth, app) {
7483
+ super(db, auth, app);
7484
+ }
7485
+ // ===== Practitioner Synced Calendars =====
7486
+ /**
7487
+ * Creates a synced calendar for a practitioner
7488
+ * @param practitionerId - ID of the practitioner
7489
+ * @param calendarData - Synced calendar data
7490
+ * @returns Created synced calendar
7491
+ */
7492
+ async createPractitionerSyncedCalendar(practitionerId, calendarData) {
7493
+ return createPractitionerSyncedCalendarUtil(
7494
+ this.db,
7495
+ practitionerId,
7496
+ calendarData,
7497
+ this.generateId.bind(this)
7498
+ );
7499
+ }
7500
+ /**
7501
+ * Gets a synced calendar for a practitioner
7502
+ * @param practitionerId - ID of the practitioner
7503
+ * @param calendarId - ID of the synced calendar
7504
+ * @returns Synced calendar or null if not found
7505
+ */
7506
+ async getPractitionerSyncedCalendar(practitionerId, calendarId) {
7507
+ return getPractitionerSyncedCalendarUtil(
7508
+ this.db,
7509
+ practitionerId,
7510
+ calendarId
7511
+ );
7512
+ }
7513
+ /**
7514
+ * Gets all synced calendars for a practitioner
7515
+ * @param practitionerId - ID of the practitioner
7516
+ * @returns Array of synced calendars
7517
+ */
7518
+ async getPractitionerSyncedCalendars(practitionerId) {
7519
+ return getPractitionerSyncedCalendarsUtil(this.db, practitionerId);
7520
+ }
7521
+ /**
7522
+ * Updates a synced calendar for a practitioner
7523
+ * @param practitionerId - ID of the practitioner
7524
+ * @param calendarId - ID of the synced calendar
7525
+ * @param updateData - Data to update
7526
+ * @returns Updated synced calendar
7527
+ */
7528
+ async updatePractitionerSyncedCalendar(practitionerId, calendarId, updateData) {
7529
+ return updatePractitionerSyncedCalendarUtil(
7530
+ this.db,
7531
+ practitionerId,
7532
+ calendarId,
7533
+ updateData
7534
+ );
7535
+ }
7536
+ /**
7537
+ * Deletes a synced calendar for a practitioner
7538
+ * @param practitionerId - ID of the practitioner
7539
+ * @param calendarId - ID of the synced calendar
7540
+ */
7541
+ async deletePractitionerSyncedCalendar(practitionerId, calendarId) {
7542
+ return deletePractitionerSyncedCalendarUtil(
7543
+ this.db,
7544
+ practitionerId,
7545
+ calendarId
7546
+ );
7547
+ }
7548
+ // ===== Patient Synced Calendars =====
7549
+ /**
7550
+ * Creates a synced calendar for a patient
7551
+ * @param patientId - ID of the patient
7552
+ * @param calendarData - Synced calendar data
7553
+ * @returns Created synced calendar
7554
+ */
7555
+ async createPatientSyncedCalendar(patientId, calendarData) {
7556
+ return createPatientSyncedCalendarUtil(
7557
+ this.db,
7558
+ patientId,
7559
+ calendarData,
7560
+ this.generateId.bind(this)
7561
+ );
7562
+ }
7563
+ /**
7564
+ * Gets a synced calendar for a patient
7565
+ * @param patientId - ID of the patient
7566
+ * @param calendarId - ID of the synced calendar
7567
+ * @returns Synced calendar or null if not found
7568
+ */
7569
+ async getPatientSyncedCalendar(patientId, calendarId) {
7570
+ return getPatientSyncedCalendarUtil(this.db, patientId, calendarId);
7571
+ }
7572
+ /**
7573
+ * Gets all synced calendars for a patient
7574
+ * @param patientId - ID of the patient
7575
+ * @returns Array of synced calendars
7576
+ */
7577
+ async getPatientSyncedCalendars(patientId) {
7578
+ return getPatientSyncedCalendarsUtil(this.db, patientId);
7579
+ }
7580
+ /**
7581
+ * Updates a synced calendar for a patient
7582
+ * @param patientId - ID of the patient
7583
+ * @param calendarId - ID of the synced calendar
7584
+ * @param updateData - Data to update
7585
+ * @returns Updated synced calendar
7586
+ */
7587
+ async updatePatientSyncedCalendar(patientId, calendarId, updateData) {
7588
+ return updatePatientSyncedCalendarUtil(
7589
+ this.db,
7590
+ patientId,
7591
+ calendarId,
7592
+ updateData
7593
+ );
7594
+ }
7595
+ /**
7596
+ * Deletes a synced calendar for a patient
7597
+ * @param patientId - ID of the patient
7598
+ * @param calendarId - ID of the synced calendar
7599
+ */
7600
+ async deletePatientSyncedCalendar(patientId, calendarId) {
7601
+ return deletePatientSyncedCalendarUtil(this.db, patientId, calendarId);
7602
+ }
7603
+ // ===== Clinic Synced Calendars =====
7604
+ /**
7605
+ * Creates a synced calendar for a clinic
7606
+ * @param clinicId - ID of the clinic
7607
+ * @param calendarData - Synced calendar data
7608
+ * @returns Created synced calendar
7609
+ */
7610
+ async createClinicSyncedCalendar(clinicId, calendarData) {
7611
+ return createClinicSyncedCalendarUtil(
7612
+ this.db,
7613
+ clinicId,
7614
+ calendarData,
7615
+ this.generateId.bind(this)
7616
+ );
7617
+ }
7618
+ /**
7619
+ * Gets a synced calendar for a clinic
7620
+ * @param clinicId - ID of the clinic
7621
+ * @param calendarId - ID of the synced calendar
7622
+ * @returns Synced calendar or null if not found
7623
+ */
7624
+ async getClinicSyncedCalendar(clinicId, calendarId) {
7625
+ return getClinicSyncedCalendarUtil(this.db, clinicId, calendarId);
7626
+ }
7627
+ /**
7628
+ * Gets all synced calendars for a clinic
7629
+ * @param clinicId - ID of the clinic
7630
+ * @returns Array of synced calendars
7631
+ */
7632
+ async getClinicSyncedCalendars(clinicId) {
7633
+ return getClinicSyncedCalendarsUtil(this.db, clinicId);
7634
+ }
7635
+ /**
7636
+ * Updates a synced calendar for a clinic
7637
+ * @param clinicId - ID of the clinic
7638
+ * @param calendarId - ID of the synced calendar
7639
+ * @param updateData - Data to update
7640
+ * @returns Updated synced calendar
7641
+ */
7642
+ async updateClinicSyncedCalendar(clinicId, calendarId, updateData) {
7643
+ return updateClinicSyncedCalendarUtil(
7644
+ this.db,
7645
+ clinicId,
7646
+ calendarId,
7647
+ updateData
7648
+ );
7649
+ }
7650
+ /**
7651
+ * Deletes a synced calendar for a clinic
7652
+ * @param clinicId - ID of the clinic
7653
+ * @param calendarId - ID of the synced calendar
7654
+ */
7655
+ async deleteClinicSyncedCalendar(clinicId, calendarId) {
7656
+ return deleteClinicSyncedCalendarUtil(this.db, clinicId, calendarId);
7657
+ }
7658
+ // ===== Google Calendar Integration =====
7659
+ /**
7660
+ * Gets the OAuth URL for Google Calendar
7661
+ * @param scopes - OAuth scopes to request
7662
+ * @returns OAuth URL
7663
+ */
7664
+ getGoogleCalendarOAuthUrl(scopes = ["https://www.googleapis.com/auth/calendar"]) {
7665
+ return getGoogleCalendarOAuthUrlUtil(scopes);
7666
+ }
7667
+ /**
7668
+ * Authenticates with Google Calendar using an authorization code
7669
+ * @param authCode - Authorization code from Google OAuth
7670
+ * @returns Access token, refresh token, and expiration time
7671
+ */
7672
+ async authenticateWithGoogleCalendar(authCode) {
7673
+ return authenticateWithGoogleCalendarUtil(authCode);
7674
+ }
7675
+ /**
7676
+ * Lists available Google Calendars for a user
7677
+ * @param accessToken - Google API access token
7678
+ * @returns List of available calendars
7679
+ */
7680
+ async listGoogleCalendars(accessToken) {
7681
+ return listGoogleCalendarsUtil(accessToken);
7682
+ }
7683
+ /**
7684
+ * Syncs events from our system to Google Calendar for a practitioner
7685
+ * @param practitionerId - ID of the practitioner
7686
+ * @param calendarId - ID of the synced calendar
7687
+ * @param events - Events to sync
7688
+ * @param existingSyncId - Optional existing sync ID for updating an event
7689
+ * @returns Result of the sync operation
7690
+ */
7691
+ async syncPractitionerEventsToGoogleCalendar(practitionerId, calendarId, events, existingSyncId) {
7692
+ const syncedCalendar = await this.getPractitionerSyncedCalendar(
7693
+ practitionerId,
7694
+ calendarId
7695
+ );
7696
+ if (!syncedCalendar) {
7697
+ throw new Error("Synced calendar not found");
7698
+ }
7699
+ return syncEventsToGoogleCalendarUtil(
7700
+ this.db,
7701
+ "practitioner",
7702
+ practitionerId,
7703
+ syncedCalendar,
7704
+ events,
7705
+ existingSyncId
7706
+ );
7707
+ }
7708
+ /**
7709
+ * Syncs events from our system to Google Calendar for a patient
7710
+ * @param patientId - ID of the patient
7711
+ * @param calendarId - ID of the synced calendar
7712
+ * @param events - Events to sync
7713
+ * @param existingSyncId - Optional existing sync ID for updating an event
7714
+ * @returns Result of the sync operation
7715
+ */
7716
+ async syncPatientEventsToGoogleCalendar(patientId, calendarId, events, existingSyncId) {
7717
+ const syncedCalendar = await this.getPatientSyncedCalendar(
7718
+ patientId,
7719
+ calendarId
7720
+ );
7721
+ if (!syncedCalendar) {
7722
+ throw new Error("Synced calendar not found");
7723
+ }
7724
+ return syncEventsToGoogleCalendarUtil(
7725
+ this.db,
7726
+ "patient",
7727
+ patientId,
7728
+ syncedCalendar,
7729
+ events,
7730
+ existingSyncId
7731
+ );
7732
+ }
7733
+ /**
7734
+ * Syncs events from our system to Google Calendar for a clinic
7735
+ * @param clinicId - ID of the clinic
7736
+ * @param calendarId - ID of the synced calendar
7737
+ * @param events - Events to sync
7738
+ * @returns Result of the sync operation
7739
+ */
7740
+ async syncClinicEventsToGoogleCalendar(clinicId, calendarId, events) {
7741
+ const syncedCalendar = await this.getClinicSyncedCalendar(
7742
+ clinicId,
7743
+ calendarId
7744
+ );
7745
+ if (!syncedCalendar) {
7746
+ throw new Error("Synced calendar not found");
7747
+ }
7748
+ return syncEventsToGoogleCalendarUtil(
7749
+ this.db,
7750
+ "clinic",
7751
+ clinicId,
7752
+ syncedCalendar,
7753
+ events
7754
+ );
7755
+ }
7756
+ /**
7757
+ * Fetches events from Google Calendar for a practitioner
7758
+ * @param practitionerId - ID of the practitioner
7759
+ * @param calendarId - ID of the synced calendar
7760
+ * @param startDate - Start date for fetching events
7761
+ * @param endDate - End date for fetching events
7762
+ * @returns Events fetched from Google Calendar
7763
+ */
7764
+ async fetchEventsFromPractitionerGoogleCalendar(practitionerId, calendarId, startDate, endDate) {
7765
+ const syncedCalendar = await this.getPractitionerSyncedCalendar(
7766
+ practitionerId,
7767
+ calendarId
7768
+ );
7769
+ if (!syncedCalendar) {
7770
+ throw new Error("Synced calendar not found");
7771
+ }
7772
+ return fetchEventsFromGoogleCalendarUtil(
7773
+ this.db,
7774
+ "practitioner",
7775
+ practitionerId,
7776
+ syncedCalendar,
7777
+ startDate,
7778
+ endDate
7779
+ );
7780
+ }
7781
+ /**
7782
+ * Fetches events from Google Calendar for a patient
7783
+ * @param patientId - ID of the patient
7784
+ * @param calendarId - ID of the synced calendar
7785
+ * @param startDate - Start date for fetching events
7786
+ * @param endDate - End date for fetching events
7787
+ * @returns Events fetched from Google Calendar
7788
+ */
7789
+ async fetchEventsFromPatientGoogleCalendar(patientId, calendarId, startDate, endDate) {
7790
+ const syncedCalendar = await this.getPatientSyncedCalendar(
7791
+ patientId,
7792
+ calendarId
7793
+ );
7794
+ if (!syncedCalendar) {
7795
+ throw new Error("Synced calendar not found");
7796
+ }
7797
+ return fetchEventsFromGoogleCalendarUtil(
7798
+ this.db,
7799
+ "patient",
7800
+ patientId,
7801
+ syncedCalendar,
7802
+ startDate,
7803
+ endDate
7804
+ );
7805
+ }
7806
+ /**
7807
+ * Fetches events from Google Calendar for a clinic
7808
+ * @param clinicId - ID of the clinic
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 fetchEventsFromClinicGoogleCalendar(clinicId, calendarId, startDate, endDate) {
7815
+ const syncedCalendar = await this.getClinicSyncedCalendar(
7816
+ clinicId,
7817
+ calendarId
7818
+ );
7819
+ if (!syncedCalendar) {
7820
+ throw new Error("Synced calendar not found");
7821
+ }
7822
+ return fetchEventsFromGoogleCalendarUtil(
7823
+ this.db,
7824
+ "clinic",
7825
+ clinicId,
7826
+ syncedCalendar,
7827
+ startDate,
7828
+ endDate
7829
+ );
7830
+ }
7831
+ /**
7832
+ * Deletes an event from Google Calendar for a practitioner
7833
+ * @param practitionerId - ID of the practitioner
7834
+ * @param calendarId - ID of the synced calendar
7835
+ * @param eventId - ID of the event in Google Calendar
7836
+ * @returns Success status
7837
+ */
7838
+ async deletePractitionerGoogleCalendarEvent(practitionerId, calendarId, eventId) {
7839
+ const syncedCalendar = await this.getPractitionerSyncedCalendar(
7840
+ practitionerId,
7841
+ calendarId
7842
+ );
7843
+ if (!syncedCalendar) {
7844
+ throw new Error("Synced calendar not found");
7845
+ }
7846
+ return deleteGoogleCalendarEventUtil(
7847
+ this.db,
7848
+ "practitioner",
7849
+ practitionerId,
7850
+ syncedCalendar,
7851
+ eventId
7852
+ );
7853
+ }
7854
+ /**
7855
+ * Deletes an event from Google Calendar for a patient
7856
+ * @param patientId - ID of the patient
7857
+ * @param calendarId - ID of the synced calendar
7858
+ * @param eventId - ID of the event in Google Calendar
7859
+ * @returns Success status
7860
+ */
7861
+ async deletePatientGoogleCalendarEvent(patientId, calendarId, eventId) {
7862
+ const syncedCalendar = await this.getPatientSyncedCalendar(
7863
+ patientId,
7864
+ calendarId
7865
+ );
7866
+ if (!syncedCalendar) {
7867
+ throw new Error("Synced calendar not found");
7868
+ }
7869
+ return deleteGoogleCalendarEventUtil(
7870
+ this.db,
7871
+ "patient",
7872
+ patientId,
7873
+ syncedCalendar,
7874
+ eventId
7875
+ );
7876
+ }
7877
+ /**
7878
+ * Deletes an event from Google Calendar for a clinic
7879
+ * @param clinicId - ID of the clinic
7880
+ * @param calendarId - ID of the synced calendar
7881
+ * @param eventId - ID of the event in Google Calendar
7882
+ * @returns Success status
7883
+ */
7884
+ async deleteClinicGoogleCalendarEvent(clinicId, calendarId, eventId) {
7885
+ const syncedCalendar = await this.getClinicSyncedCalendar(
7886
+ clinicId,
7887
+ calendarId
7888
+ );
7889
+ if (!syncedCalendar) {
7890
+ throw new Error("Synced calendar not found");
7891
+ }
7892
+ return deleteGoogleCalendarEventUtil(
7893
+ this.db,
7894
+ "clinic",
7895
+ clinicId,
7896
+ syncedCalendar,
7897
+ eventId
7898
+ );
7899
+ }
7900
+ /**
7901
+ * Converts Google Calendar events to our system's format for a practitioner
7902
+ * @param practitionerId - ID of the practitioner
7903
+ * @param googleEvents - Google Calendar events
7904
+ * @returns Converted calendar events
7905
+ */
7906
+ convertGoogleEventsToPractitionerEvents(practitionerId, googleEvents) {
7907
+ return googleEvents.map(
7908
+ (event) => convertGoogleEventToCalendarEventUtil(
7909
+ event,
7910
+ practitionerId,
7911
+ "practitioner"
7912
+ )
7913
+ );
7914
+ }
7915
+ /**
7916
+ * Converts Google Calendar events to our system's format for a patient
7917
+ * @param patientId - ID of the patient
7918
+ * @param googleEvents - Google Calendar events
7919
+ * @returns Converted calendar events
7920
+ */
7921
+ convertGoogleEventsToPatientEvents(patientId, googleEvents) {
7922
+ return googleEvents.map(
7923
+ (event) => convertGoogleEventToCalendarEventUtil(event, patientId, "patient")
7924
+ );
7925
+ }
7926
+ /**
7927
+ * Converts Google Calendar events to our system's format for a clinic
7928
+ * @param clinicId - ID of the clinic
7929
+ * @param googleEvents - Google Calendar events
7930
+ * @returns Converted calendar events
7931
+ */
7932
+ convertGoogleEventsToClinicEvents(clinicId, googleEvents) {
7933
+ return googleEvents.map(
7934
+ (event) => convertGoogleEventToCalendarEventUtil(event, clinicId, "clinic")
7935
+ );
7936
+ }
7937
+ /**
7938
+ * Fetches a single event from Google Calendar for a practitioner
7939
+ * @param practitionerId - ID of the practitioner
7940
+ * @param calendarId - ID of the synced calendar
7941
+ * @param eventId - ID of the event in Google Calendar
7942
+ * @returns The event data or null if not found
7943
+ */
7944
+ async fetchEventFromPractitionerGoogleCalendar(practitionerId, calendarId, eventId) {
7945
+ var _a;
7946
+ const syncedCalendar = await this.getPractitionerSyncedCalendar(
7947
+ practitionerId,
7948
+ calendarId
7949
+ );
7950
+ if (!syncedCalendar) {
7951
+ throw new Error("Synced calendar not found");
7952
+ }
7953
+ try {
7954
+ const { accessToken } = await refreshGoogleCalendarTokenUtil(
7955
+ syncedCalendar.refreshToken
7956
+ );
7957
+ const response = await makeRequest(
7958
+ "get",
7959
+ `${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${eventId}`,
7960
+ { Authorization: `Bearer ${accessToken}` }
7961
+ );
7962
+ await updateLastSyncedTimestampUtil(
7963
+ this.db,
7964
+ "practitioner",
7965
+ practitionerId,
7966
+ syncedCalendar.id
7967
+ );
7968
+ return response;
7969
+ } catch (error) {
7970
+ if (((_a = error.response) == null ? void 0 : _a.status) === 404) {
7971
+ return null;
7972
+ }
7973
+ console.error(
7974
+ `Error fetching event from Google Calendar: ${error.message}`
7975
+ );
7976
+ throw error;
7977
+ }
7978
+ }
7979
+ };
7980
+
7981
+ // src/services/calendar/calendar-refactored.service.ts
7982
+ var MIN_APPOINTMENT_DURATION2 = 15;
7983
+ var CalendarServiceV2 = class extends BaseService {
7984
+ /**
7985
+ * Creates a new CalendarService instance
7986
+ * @param db - Firestore instance
7987
+ * @param auth - Firebase Auth instance
7988
+ * @param app - Firebase App instance
7989
+ */
7990
+ constructor(db, auth, app) {
7991
+ super(db, auth, app);
7992
+ this.syncedCalendarsService = new SyncedCalendarsService(db, auth, app);
7993
+ }
7994
+ // #region Public API Methods
7995
+ /**
7996
+ * Creates a new appointment with proper validation and scheduling rules
7997
+ * @param params - Appointment creation parameters
7998
+ * @returns Created calendar event
7999
+ */
8000
+ async createAppointment(params) {
8001
+ await this.validateAppointmentParams(params);
8002
+ await this.validateClinicWorkingHours(params.clinicId, params.eventTime);
8003
+ await this.validateDoctorAvailability(
8004
+ params.doctorId,
8005
+ params.eventTime,
8006
+ params.clinicId
8007
+ );
8008
+ const { clinicInfo, practitionerInfo, patientInfo } = await this.fetchProfileInfoCards(
8009
+ params.clinicId,
8010
+ params.doctorId,
8011
+ params.patientId
8012
+ );
8013
+ const appointmentData = {
8014
+ clinicBranchId: params.clinicId,
8015
+ clinicBranchInfo: clinicInfo,
8016
+ practitionerProfileId: params.doctorId,
8017
+ practitionerProfileInfo: practitionerInfo,
8018
+ patientProfileId: params.patientId,
8019
+ patientProfileInfo: patientInfo,
8020
+ procedureId: params.procedureId,
8021
+ eventLocation: params.eventLocation,
8022
+ eventName: "Appointment",
8023
+ // TODO: Add procedure name when procedure model is available
8024
+ eventTime: params.eventTime,
8025
+ description: params.description || "",
8026
+ status: "pending" /* PENDING */,
8027
+ syncStatus: "internal" /* INTERNAL */,
8028
+ eventType: "appointment" /* APPOINTMENT */
8029
+ };
8030
+ const appointment = await createAppointmentUtil(
8031
+ this.db,
8032
+ params.clinicId,
8033
+ params.doctorId,
8034
+ params.patientId,
8035
+ appointmentData,
8036
+ this.generateId.bind(this)
8037
+ );
8038
+ await this.syncAppointmentWithExternalCalendars(appointment);
8039
+ return appointment;
8040
+ }
8041
+ /**
8042
+ * Updates an existing appointment
8043
+ * @param params - Appointment update parameters
8044
+ * @returns Updated calendar event
8045
+ */
8046
+ async updateAppointment(params) {
8047
+ await this.validateUpdatePermissions(params);
8048
+ const updateData = {
8049
+ eventTime: params.eventTime,
8050
+ description: params.description,
8051
+ status: params.status
8052
+ };
8053
+ const appointment = await updateAppointmentUtil(
8054
+ this.db,
8055
+ params.clinicId,
8056
+ params.doctorId,
8057
+ params.patientId,
8058
+ params.appointmentId,
8059
+ updateData
8060
+ );
8061
+ await this.syncAppointmentWithExternalCalendars(appointment);
8062
+ return appointment;
8063
+ }
8064
+ /**
8065
+ * Gets available appointment slots for a doctor at a clinic
8066
+ * @param clinicId - ID of the clinic
8067
+ * @param doctorId - ID of the doctor
8068
+ * @param date - Date to check availability for
8069
+ * @returns Array of available time slots
8070
+ */
8071
+ async getAvailableSlots(clinicId, doctorId, date) {
8072
+ const workingHours = await this.getClinicWorkingHours(clinicId, date);
8073
+ const doctorSchedule = await this.getDoctorSchedule(doctorId, date);
8074
+ const existingAppointments = await this.getDoctorAppointments(
8075
+ doctorId,
8076
+ date
8077
+ );
8078
+ return this.calculateAvailableSlots(
8079
+ workingHours,
8080
+ doctorSchedule,
8081
+ existingAppointments
8082
+ );
8083
+ }
8084
+ /**
8085
+ * Confirms an appointment
8086
+ * @param appointmentId - ID of the appointment
8087
+ * @param clinicId - ID of the clinic
8088
+ * @returns Confirmed calendar event
8089
+ */
8090
+ async confirmAppointment(appointmentId, clinicId) {
8091
+ return this.updateAppointmentStatus(
8092
+ appointmentId,
8093
+ clinicId,
8094
+ "confirmed" /* CONFIRMED */
8095
+ );
8096
+ }
8097
+ /**
8098
+ * Rejects an appointment
8099
+ * @param appointmentId - ID of the appointment
8100
+ * @param clinicId - ID of the clinic
8101
+ * @returns Rejected calendar event
8102
+ */
8103
+ async rejectAppointment(appointmentId, clinicId) {
8104
+ return this.updateAppointmentStatus(
8105
+ appointmentId,
8106
+ clinicId,
8107
+ "rejected" /* REJECTED */
8108
+ );
8109
+ }
8110
+ /**
8111
+ * Cancels an appointment
8112
+ * @param appointmentId - ID of the appointment
8113
+ * @param clinicId - ID of the clinic
8114
+ * @returns Canceled calendar event
8115
+ */
8116
+ async cancelAppointment(appointmentId, clinicId) {
8117
+ return this.updateAppointmentStatus(
8118
+ appointmentId,
8119
+ clinicId,
8120
+ "canceled" /* CANCELED */
8121
+ );
8122
+ }
8123
+ /**
8124
+ * Imports events from external calendars
8125
+ * @param entityType - Type of entity (practitioner or patient)
8126
+ * @param entityId - ID of the entity
8127
+ * @param startDate - Start date for fetching events
8128
+ * @param endDate - End date for fetching events
8129
+ * @returns Number of events imported
8130
+ */
8131
+ async importEventsFromExternalCalendars(entityType, entityId, startDate, endDate) {
8132
+ if (entityType === "patient") {
8133
+ return 0;
8134
+ }
8135
+ const syncedCalendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
8136
+ entityId
8137
+ );
8138
+ const activeCalendars = syncedCalendars.filter((cal) => cal.isActive);
8139
+ if (activeCalendars.length === 0) {
8140
+ return 0;
8141
+ }
8142
+ let importedEventsCount = 0;
8143
+ const currentTime = import_firestore31.Timestamp.now();
8144
+ for (const calendar of activeCalendars) {
8145
+ try {
8146
+ let externalEvents = [];
8147
+ if (calendar.provider === "google" /* GOOGLE */) {
8148
+ externalEvents = await this.syncedCalendarsService.fetchEventsFromPractitionerGoogleCalendar(
8149
+ entityId,
8150
+ calendar.id,
8151
+ startDate,
8152
+ endDate
8153
+ );
8154
+ }
8155
+ for (const externalEvent of externalEvents) {
8156
+ try {
8157
+ const convertedEvent = this.syncedCalendarsService.convertGoogleEventsToPractitionerEvents(
8158
+ entityId,
8159
+ [externalEvent]
8160
+ )[0];
8161
+ if (!convertedEvent.eventTime) {
8162
+ continue;
8163
+ }
8164
+ const eventData = {
8165
+ // Ensure all required fields are set
8166
+ eventName: convertedEvent.eventName || "External Event",
8167
+ eventTime: convertedEvent.eventTime,
8168
+ description: convertedEvent.description || "",
8169
+ status: "confirmed" /* CONFIRMED */,
8170
+ syncStatus: "external" /* EXTERNAL */,
8171
+ eventType: "blocking" /* BLOCKING */,
8172
+ practitionerProfileId: entityId,
8173
+ syncedCalendarEventId: [
8174
+ {
8175
+ eventId: externalEvent.id,
8176
+ syncedCalendarProvider: calendar.provider,
8177
+ syncedAt: currentTime
8178
+ }
8179
+ ]
8180
+ };
8181
+ const doctorEvent = await this.createDoctorBlockingEvent(
8182
+ entityId,
8183
+ eventData
8184
+ );
8185
+ if (doctorEvent) {
8186
+ importedEventsCount++;
8187
+ }
8188
+ } catch (eventError) {
8189
+ console.error("Error importing event:", eventError);
8190
+ }
8191
+ }
8192
+ } catch (calendarError) {
8193
+ console.error(
8194
+ `Error fetching events from calendar ${calendar.id}:`,
8195
+ calendarError
8196
+ );
8197
+ }
8198
+ }
8199
+ return importedEventsCount;
8200
+ }
8201
+ /**
8202
+ * Creates a blocking event in a doctor's calendar
8203
+ * @param doctorId - ID of the doctor
8204
+ * @param eventData - Calendar event data
8205
+ * @returns Created calendar event
8206
+ */
8207
+ async createDoctorBlockingEvent(doctorId, eventData) {
8208
+ try {
8209
+ const eventId = this.generateId();
8210
+ const eventRef = (0, import_firestore32.doc)(
8211
+ this.db,
8212
+ PRACTITIONERS_COLLECTION,
8213
+ doctorId,
8214
+ CALENDAR_COLLECTION,
8215
+ eventId
8216
+ );
8217
+ const newEvent = {
8218
+ id: eventId,
8219
+ ...eventData,
8220
+ createdAt: (0, import_firestore31.serverTimestamp)(),
8221
+ updatedAt: (0, import_firestore31.serverTimestamp)()
8222
+ };
8223
+ await (0, import_firestore32.setDoc)(eventRef, newEvent);
8224
+ return {
8225
+ ...newEvent,
8226
+ createdAt: import_firestore31.Timestamp.now(),
8227
+ updatedAt: import_firestore31.Timestamp.now()
8228
+ };
8229
+ } catch (error) {
8230
+ console.error(
8231
+ `Error creating blocking event for doctor ${doctorId}:`,
8232
+ error
8233
+ );
8234
+ return null;
8235
+ }
8236
+ }
8237
+ /**
8238
+ * Periodically syncs events from external calendars for doctors
8239
+ * This would be called via a scheduled Cloud Function
8240
+ * @param lookbackDays - Number of days to look back for events
8241
+ * @param lookforwardDays - Number of days to look forward for events
8242
+ */
8243
+ async synchronizeExternalCalendars(lookbackDays = 7, lookforwardDays = 30) {
8244
+ try {
8245
+ const practitionersRef = (0, import_firestore32.collection)(this.db, PRACTITIONERS_COLLECTION);
8246
+ const practitionersSnapshot = await (0, import_firestore32.getDocs)(practitionersRef);
8247
+ const startDate = /* @__PURE__ */ new Date();
8248
+ startDate.setDate(startDate.getDate() - lookbackDays);
8249
+ const endDate = /* @__PURE__ */ new Date();
8250
+ endDate.setDate(endDate.getDate() + lookforwardDays);
8251
+ const syncPromises = [];
8252
+ for (const docSnapshot of practitionersSnapshot.docs) {
8253
+ const practitionerId = docSnapshot.id;
8254
+ syncPromises.push(
8255
+ this.importEventsFromExternalCalendars(
8256
+ "doctor",
8257
+ practitionerId,
8258
+ startDate,
8259
+ endDate
8260
+ ).then((count) => {
8261
+ console.log(
8262
+ `Imported ${count} events for doctor ${practitionerId}`
8263
+ );
8264
+ }).catch((error) => {
8265
+ console.error(
8266
+ `Error importing events for doctor ${practitionerId}:`,
8267
+ error
8268
+ );
8269
+ })
8270
+ );
8271
+ syncPromises.push(
8272
+ this.updateExistingEventsFromExternalCalendars(
8273
+ practitionerId,
8274
+ startDate,
8275
+ endDate
8276
+ ).then((count) => {
8277
+ console.log(
8278
+ `Updated ${count} events for doctor ${practitionerId}`
8279
+ );
8280
+ }).catch((error) => {
8281
+ console.error(
8282
+ `Error updating events for doctor ${practitionerId}:`,
8283
+ error
8284
+ );
8285
+ })
8286
+ );
8287
+ }
8288
+ await Promise.all(syncPromises);
8289
+ console.log("Completed external calendar synchronization");
8290
+ } catch (error) {
8291
+ console.error("Error synchronizing external calendars:", error);
8292
+ }
8293
+ }
8294
+ /**
8295
+ * Updates existing events that were synced from external calendars
8296
+ * @param doctorId - ID of the doctor
8297
+ * @param startDate - Start date for fetching events
8298
+ * @param endDate - End date for fetching events
8299
+ * @returns Number of events updated
8300
+ */
8301
+ async updateExistingEventsFromExternalCalendars(doctorId, startDate, endDate) {
8302
+ var _a;
8303
+ try {
8304
+ const eventsRef = (0, import_firestore32.collection)(
8305
+ this.db,
8306
+ PRACTITIONERS_COLLECTION,
8307
+ doctorId,
8308
+ CALENDAR_COLLECTION
8309
+ );
8310
+ const q = (0, import_firestore32.query)(
8311
+ eventsRef,
8312
+ (0, import_firestore32.where)("syncStatus", "==", "external" /* EXTERNAL */),
8313
+ (0, import_firestore32.where)("eventTime.start", ">=", import_firestore31.Timestamp.fromDate(startDate)),
8314
+ (0, import_firestore32.where)("eventTime.start", "<=", import_firestore31.Timestamp.fromDate(endDate))
8315
+ );
8316
+ const eventsSnapshot = await (0, import_firestore32.getDocs)(q);
8317
+ const events = eventsSnapshot.docs.map((doc20) => ({
8318
+ id: doc20.id,
8319
+ ...doc20.data()
8320
+ }));
8321
+ const calendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
8322
+ doctorId
8323
+ );
8324
+ const activeCalendars = calendars.filter((cal) => cal.isActive);
8325
+ if (activeCalendars.length === 0 || events.length === 0) {
8326
+ return 0;
8327
+ }
8328
+ let updatedCount = 0;
8329
+ for (const event of events) {
8330
+ if (!((_a = event.syncedCalendarEventId) == null ? void 0 : _a.length)) continue;
8331
+ for (const syncId of event.syncedCalendarEventId) {
8332
+ const calendar = activeCalendars.find(
8333
+ (cal) => cal.provider === syncId.syncedCalendarProvider
8334
+ );
8335
+ if (!calendar) continue;
8336
+ if (syncId.syncedCalendarProvider === "google" /* GOOGLE */) {
8337
+ try {
8338
+ const externalEvent = await this.fetchExternalEvent(
8339
+ doctorId,
8340
+ calendar,
8341
+ syncId.eventId
8342
+ );
8343
+ if (externalEvent) {
8344
+ const externalStartTime = new Date(
8345
+ externalEvent.start.dateTime || externalEvent.start.date
8346
+ ).getTime();
8347
+ const externalEndTime = new Date(
8348
+ externalEvent.end.dateTime || externalEvent.end.date
8349
+ ).getTime();
8350
+ const localStartTime = event.eventTime.start.toDate().getTime();
8351
+ const localEndTime = event.eventTime.end.toDate().getTime();
8352
+ if (externalStartTime !== localStartTime || externalEndTime !== localEndTime || externalEvent.summary !== event.eventName || externalEvent.description !== event.description) {
8353
+ await this.updateLocalEventFromExternal(
8354
+ doctorId,
8355
+ event.id,
8356
+ externalEvent
8357
+ );
8358
+ updatedCount++;
8359
+ }
8360
+ } else {
8361
+ await this.updateEventStatus(
8362
+ doctorId,
8363
+ event.id,
8364
+ "canceled" /* CANCELED */
8365
+ );
8366
+ updatedCount++;
8367
+ }
8368
+ } catch (error) {
8369
+ console.error(
8370
+ `Error updating external event ${event.id}:`,
8371
+ error
8372
+ );
8373
+ }
8374
+ }
8375
+ }
8376
+ }
8377
+ return updatedCount;
8378
+ } catch (error) {
8379
+ console.error(
8380
+ "Error updating existing events from external calendars:",
8381
+ error
8382
+ );
8383
+ return 0;
8384
+ }
8385
+ }
8386
+ /**
8387
+ * Fetches a single external event from Google Calendar
8388
+ * @param doctorId - ID of the doctor
8389
+ * @param calendar - Calendar information
8390
+ * @param externalEventId - ID of the external event
8391
+ * @returns External event data or null if not found
8392
+ */
8393
+ async fetchExternalEvent(doctorId, calendar, externalEventId) {
8394
+ try {
8395
+ if (calendar.provider === "google" /* GOOGLE */) {
8396
+ const result = await this.syncedCalendarsService.fetchEventFromPractitionerGoogleCalendar(
8397
+ doctorId,
8398
+ calendar.id,
8399
+ externalEventId
8400
+ );
8401
+ return result;
8402
+ }
8403
+ return null;
8404
+ } catch (error) {
8405
+ console.error(`Error fetching external event ${externalEventId}:`, error);
8406
+ return null;
8407
+ }
8408
+ }
8409
+ /**
8410
+ * Updates a local event with data from an external event
8411
+ * @param doctorId - ID of the doctor
8412
+ * @param eventId - ID of the local event
8413
+ * @param externalEvent - External event data
8414
+ */
8415
+ async updateLocalEventFromExternal(doctorId, eventId, externalEvent) {
8416
+ try {
8417
+ const startTime = new Date(
8418
+ externalEvent.start.dateTime || externalEvent.start.date
8419
+ );
8420
+ const endTime = new Date(
8421
+ externalEvent.end.dateTime || externalEvent.end.date
8422
+ );
8423
+ const eventRef = (0, import_firestore32.doc)(
8424
+ this.db,
8425
+ PRACTITIONERS_COLLECTION,
8426
+ doctorId,
8427
+ CALENDAR_COLLECTION,
8428
+ eventId
8429
+ );
8430
+ await (0, import_firestore32.updateDoc)(eventRef, {
8431
+ eventName: externalEvent.summary || "External Event",
8432
+ eventTime: {
8433
+ start: import_firestore31.Timestamp.fromDate(startTime),
8434
+ end: import_firestore31.Timestamp.fromDate(endTime)
8435
+ },
8436
+ description: externalEvent.description || "",
8437
+ updatedAt: (0, import_firestore31.serverTimestamp)()
8438
+ });
8439
+ console.log(`Updated local event ${eventId} from external event`);
8440
+ } catch (error) {
8441
+ console.error(
8442
+ `Error updating local event ${eventId} from external:`,
8443
+ error
8444
+ );
8445
+ }
8446
+ }
8447
+ /**
8448
+ * Updates an event's status
8449
+ * @param doctorId - ID of the doctor
8450
+ * @param eventId - ID of the event
8451
+ * @param status - New status
8452
+ */
8453
+ async updateEventStatus(doctorId, eventId, status) {
8454
+ try {
8455
+ const eventRef = (0, import_firestore32.doc)(
8456
+ this.db,
8457
+ PRACTITIONERS_COLLECTION,
8458
+ doctorId,
8459
+ CALENDAR_COLLECTION,
8460
+ eventId
8461
+ );
8462
+ await (0, import_firestore32.updateDoc)(eventRef, {
8463
+ status,
8464
+ updatedAt: (0, import_firestore31.serverTimestamp)()
8465
+ });
8466
+ console.log(`Updated event ${eventId} status to ${status}`);
8467
+ } catch (error) {
8468
+ console.error(`Error updating event ${eventId} status:`, error);
8469
+ }
8470
+ }
8471
+ /**
8472
+ * Creates a scheduled job to periodically sync external calendars
8473
+ * Note: This would be implemented using Cloud Functions in a real application
8474
+ * This is a sample implementation to show how it could be set up
8475
+ * @param interval - Interval in hours
8476
+ */
8477
+ createScheduledSyncJob(interval = 3) {
8478
+ console.log(
8479
+ `Setting up scheduled calendar sync job every ${interval} hours`
8480
+ );
8481
+ }
8482
+ // #endregion
8483
+ // #region Private Helper Methods
8484
+ /**
8485
+ * Validates appointment creation parameters
8486
+ * @param params - Appointment parameters to validate
8487
+ * @throws Error if validation fails
8488
+ */
8489
+ async validateAppointmentParams(params) {
8490
+ await createAppointmentSchema.parseAsync(params);
8491
+ }
8492
+ /**
8493
+ * Validates if the event time falls within clinic working hours
8494
+ * @param clinicId - ID of the clinic
8495
+ * @param eventTime - Event time to validate
8496
+ * @throws Error if validation fails
8497
+ */
8498
+ async validateClinicWorkingHours(clinicId, eventTime) {
8499
+ const startDate = eventTime.start.toDate();
8500
+ const workingHours = await this.getClinicWorkingHours(clinicId, startDate);
8501
+ if (workingHours.length === 0) {
8502
+ throw new Error("Clinic is not open on this day");
8503
+ }
8504
+ const startTime = startDate;
8505
+ const endTime = eventTime.end.toDate();
8506
+ const isWithinWorkingHours = workingHours.some((slot) => {
8507
+ return slot.start <= startTime && slot.end >= endTime && slot.isAvailable;
8508
+ });
8509
+ if (!isWithinWorkingHours) {
8510
+ throw new Error("Appointment time is outside clinic working hours");
8511
+ }
8512
+ }
8513
+ /**
8514
+ * Validates if the doctor is available during the event time
8515
+ * @param doctorId - ID of the doctor
8516
+ * @param eventTime - Event time to validate
8517
+ * @param clinicId - ID of the clinic where the appointment is being booked
8518
+ * @throws Error if validation fails
8519
+ */
8520
+ async validateDoctorAvailability(doctorId, eventTime, clinicId) {
8521
+ var _a;
8522
+ const startDate = eventTime.start.toDate();
8523
+ const startTime = startDate;
8524
+ const endTime = eventTime.end.toDate();
8525
+ const practitionerRef = (0, import_firestore32.doc)(this.db, PRACTITIONERS_COLLECTION, doctorId);
8526
+ const practitionerDoc = await (0, import_firestore32.getDoc)(practitionerRef);
8527
+ if (!practitionerDoc.exists()) {
8528
+ throw new Error(`Doctor with ID ${doctorId} not found`);
8529
+ }
8530
+ const practitioner = practitionerDoc.data();
8531
+ if (!practitioner.clinics.includes(clinicId)) {
8532
+ throw new Error("Doctor does not work at this clinic");
8533
+ }
8534
+ const clinicWorkingHours = (_a = practitioner.clinicWorkingHours) == null ? void 0 : _a.find(
8535
+ (hours) => hours.clinicId === clinicId && hours.isActive
8536
+ );
8537
+ if (!clinicWorkingHours) {
8538
+ throw new Error("Doctor does not have working hours set for this clinic");
8539
+ }
8540
+ const dayOfWeek = startDate.getDay();
8541
+ const dayKey = [
8542
+ "sunday",
8543
+ "monday",
8544
+ "tuesday",
8545
+ "wednesday",
8546
+ "thursday",
8547
+ "friday",
8548
+ "saturday"
8549
+ ][dayOfWeek];
8550
+ const daySchedule = clinicWorkingHours.workingHours[dayKey];
8551
+ if (!daySchedule) {
8552
+ throw new Error("Doctor is not working on this day at this clinic");
8553
+ }
8554
+ const [startHour, startMinute] = daySchedule.start.split(":").map(Number);
8555
+ const [endHour, endMinute] = daySchedule.end.split(":").map(Number);
8556
+ const scheduleStart = new Date(startDate);
8557
+ scheduleStart.setHours(startHour, startMinute, 0, 0);
8558
+ const scheduleEnd = new Date(startDate);
8559
+ scheduleEnd.setHours(endHour, endMinute, 0, 0);
8560
+ if (startTime < scheduleStart || endTime > scheduleEnd) {
8561
+ throw new Error(
8562
+ "Appointment time is outside doctor's working hours at this clinic"
8563
+ );
8564
+ }
8565
+ const appointments = await this.getDoctorAppointments(doctorId, startDate);
8566
+ const hasOverlap = appointments.some((appointment) => {
8567
+ const appointmentStart = appointment.eventTime.start.toDate();
8568
+ const appointmentEnd = appointment.eventTime.end.toDate();
8569
+ return startTime >= appointmentStart && startTime < appointmentEnd || endTime > appointmentStart && endTime <= appointmentEnd || startTime <= appointmentStart && endTime >= appointmentEnd;
8570
+ });
8571
+ if (hasOverlap) {
8572
+ throw new Error("Doctor has another appointment during this time");
8573
+ }
8574
+ }
8575
+ /**
8576
+ * Updates appointment status
8577
+ * @param appointmentId - ID of the appointment
8578
+ * @param clinicId - ID of the clinic
8579
+ * @param status - New status
8580
+ * @returns Updated calendar event
8581
+ */
8582
+ async updateAppointmentStatus(appointmentId, clinicId, status) {
8583
+ const appointmentRef = (0, import_firestore32.doc)(this.db, CALENDAR_COLLECTION, appointmentId);
8584
+ const appointmentDoc = await (0, import_firestore32.getDoc)(appointmentRef);
8585
+ if (!appointmentDoc.exists()) {
8586
+ throw new Error(`Appointment with ID ${appointmentId} not found`);
8587
+ }
8588
+ const appointment = appointmentDoc.data();
8589
+ if (appointment.clinicBranchId !== clinicId) {
8590
+ throw new Error("Appointment does not belong to the specified clinic");
8591
+ }
8592
+ this.validateStatusTransition(appointment.status, status);
8593
+ const updateParams = {
8594
+ appointmentId,
8595
+ clinicId,
8596
+ doctorId: appointment.practitionerProfileId || "",
8597
+ patientId: appointment.patientProfileId || "",
8598
+ status
8599
+ };
8600
+ await this.validateUpdatePermissions(updateParams);
8601
+ return this.updateAppointment(updateParams);
8602
+ }
8603
+ /**
8604
+ * Validates status transition
8605
+ * @param currentStatus - Current status
8606
+ * @param newStatus - New status
8607
+ * @throws Error if transition is invalid
8608
+ */
8609
+ validateStatusTransition(currentStatus, newStatus) {
8610
+ const validTransitions = {
8611
+ ["pending" /* PENDING */]: [
8612
+ "confirmed" /* CONFIRMED */,
8613
+ "rejected" /* REJECTED */,
8614
+ "canceled" /* CANCELED */
8615
+ ],
8616
+ ["confirmed" /* CONFIRMED */]: [
8617
+ "canceled" /* CANCELED */,
8618
+ "completed" /* COMPLETED */,
8619
+ "rescheduled" /* RESCHEDULED */
8620
+ ],
8621
+ ["rejected" /* REJECTED */]: [],
8622
+ ["canceled" /* CANCELED */]: [],
8623
+ ["rescheduled" /* RESCHEDULED */]: [
8624
+ "confirmed" /* CONFIRMED */,
8625
+ "canceled" /* CANCELED */
8626
+ ],
8627
+ ["completed" /* COMPLETED */]: []
8628
+ };
8629
+ if (!validTransitions[currentStatus].includes(newStatus)) {
8630
+ throw new Error(
8631
+ `Invalid status transition from ${currentStatus} to ${newStatus}`
8632
+ );
8633
+ }
8634
+ }
8635
+ /**
8636
+ * Syncs appointment with external calendars based on entity type and status
8637
+ * @param appointment - Calendar event to sync
8638
+ */
8639
+ async syncAppointmentWithExternalCalendars(appointment) {
8640
+ if (!appointment.practitionerProfileId || !appointment.patientProfileId) {
8641
+ return;
8642
+ }
8643
+ try {
8644
+ const [doctorCalendars, patientCalendars] = await Promise.all([
8645
+ this.syncedCalendarsService.getPractitionerSyncedCalendars(
8646
+ appointment.practitionerProfileId
8647
+ ),
8648
+ this.syncedCalendarsService.getPatientSyncedCalendars(
8649
+ appointment.patientProfileId
8650
+ )
8651
+ ]);
8652
+ const activeDoctorCalendars = doctorCalendars.filter(
8653
+ (cal) => cal.isActive
8654
+ );
8655
+ const activePatientCalendars = patientCalendars.filter(
8656
+ (cal) => cal.isActive
8657
+ );
8658
+ if (activeDoctorCalendars.length === 0 && activePatientCalendars.length === 0) {
8659
+ return;
8660
+ }
8661
+ if (appointment.syncStatus !== "internal" /* INTERNAL */) {
8662
+ return;
8663
+ }
8664
+ if (appointment.status === "confirmed" /* CONFIRMED */ && activeDoctorCalendars.length > 0) {
8665
+ await Promise.all(
8666
+ activeDoctorCalendars.map(
8667
+ (calendar) => this.syncEventToExternalCalendar(appointment, calendar, "doctor")
8668
+ )
8669
+ );
8670
+ }
8671
+ if (appointment.status !== "canceled" /* CANCELED */ && appointment.status !== "rejected" /* REJECTED */ && activePatientCalendars.length > 0) {
8672
+ await Promise.all(
8673
+ activePatientCalendars.map(
8674
+ (calendar) => this.syncEventToExternalCalendar(appointment, calendar, "patient")
8675
+ )
8676
+ );
8677
+ }
8678
+ } catch (error) {
8679
+ console.error("Error syncing with external calendars:", error);
8680
+ }
8681
+ }
8682
+ /**
8683
+ * Syncs a single event to an external calendar
8684
+ * @param appointment - Calendar event to sync
8685
+ * @param calendar - External calendar to sync with
8686
+ * @param entityType - Type of entity owning the calendar
8687
+ */
8688
+ async syncEventToExternalCalendar(appointment, calendar, entityType) {
8689
+ var _a, _b, _c, _d, _e;
8690
+ try {
8691
+ const eventToSync = { ...appointment };
8692
+ let eventTitle = appointment.eventName;
8693
+ const clinicName = ((_a = appointment.clinicBranchInfo) == null ? void 0 : _a.name) || "Clinic";
8694
+ if (entityType === "patient") {
8695
+ eventTitle = `[${appointment.status}] ${eventTitle} @ ${clinicName}`;
8696
+ } else {
8697
+ eventTitle = `${eventTitle} - Patient: ${((_b = appointment.patientProfileInfo) == null ? void 0 : _b.fullName) || "Unknown"} @ ${clinicName}`;
8698
+ }
8699
+ eventToSync.eventName = eventTitle;
8700
+ const existingSyncId = (_d = (_c = appointment.syncedCalendarEventId) == null ? void 0 : _c.find(
8701
+ (sync) => sync.syncedCalendarProvider === calendar.provider
8702
+ )) == null ? void 0 : _d.eventId;
8703
+ if (calendar.provider === "google" /* GOOGLE */) {
8704
+ const result = await this.syncedCalendarsService.syncPractitionerEventsToGoogleCalendar(
8705
+ entityType === "doctor" ? appointment.practitionerProfileId : appointment.patientProfileId,
8706
+ calendar.id,
8707
+ [eventToSync],
8708
+ existingSyncId
8709
+ // Pass existing sync ID if we have one
8710
+ );
8711
+ if (result.success && ((_e = result.eventIds) == null ? void 0 : _e.length) && !existingSyncId) {
8712
+ const newSyncEvent = {
8713
+ eventId: result.eventIds[0],
8714
+ syncedCalendarProvider: calendar.provider,
8715
+ syncedAt: import_firestore31.Timestamp.now()
8716
+ };
8717
+ await this.updateEventWithSyncId(
8718
+ entityType === "doctor" ? appointment.practitionerProfileId : appointment.patientProfileId,
8719
+ entityType,
8720
+ appointment.id,
8721
+ newSyncEvent
8722
+ );
8723
+ }
8724
+ }
8725
+ } catch (error) {
8726
+ console.error(`Error syncing with ${entityType}'s calendar:`, error);
8727
+ }
8728
+ }
8729
+ /**
8730
+ * Updates an event with a new sync ID
8731
+ * @param entityId - ID of the entity (doctor or patient)
8732
+ * @param entityType - Type of entity
8733
+ * @param eventId - ID of the event
8734
+ * @param syncEvent - Sync event information
8735
+ */
8736
+ async updateEventWithSyncId(entityId, entityType, eventId, syncEvent) {
8737
+ try {
8738
+ const collectionPath = entityType === "doctor" ? `${PRACTITIONERS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}` : `${PATIENTS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
8739
+ const eventRef = (0, import_firestore32.doc)(this.db, collectionPath, eventId);
8740
+ const eventDoc = await (0, import_firestore32.getDoc)(eventRef);
8741
+ if (eventDoc.exists()) {
8742
+ const event = eventDoc.data();
8743
+ const syncIds = [...event.syncedCalendarEventId || []];
8744
+ const existingSyncIndex = syncIds.findIndex(
8745
+ (sync) => sync.syncedCalendarProvider === syncEvent.syncedCalendarProvider
8746
+ );
8747
+ if (existingSyncIndex >= 0) {
8748
+ syncIds[existingSyncIndex] = syncEvent;
8749
+ } else {
8750
+ syncIds.push(syncEvent);
8751
+ }
8752
+ await (0, import_firestore32.updateDoc)(eventRef, {
8753
+ syncedCalendarEventId: syncIds,
8754
+ updatedAt: (0, import_firestore31.serverTimestamp)()
8755
+ });
8756
+ console.log(
8757
+ `Updated event ${eventId} with sync ID ${syncEvent.eventId}`
8758
+ );
8759
+ }
8760
+ } catch (error) {
8761
+ console.error("Error updating event with sync ID:", error);
8762
+ }
8763
+ }
8764
+ /**
8765
+ * Validates update permissions and parameters
8766
+ * @param params - Update parameters to validate
8767
+ */
8768
+ async validateUpdatePermissions(params) {
8769
+ await updateAppointmentSchema.parseAsync(params);
8770
+ }
8771
+ /**
8772
+ * Gets clinic working hours for a specific date
8773
+ * @param clinicId - ID of the clinic
8774
+ * @param date - Date to get working hours for
8775
+ * @returns Working hours for the clinic
8776
+ */
8777
+ async getClinicWorkingHours(clinicId, date) {
8778
+ const clinicRef = (0, import_firestore32.doc)(this.db, CLINICS_COLLECTION, clinicId);
8779
+ const clinicDoc = await (0, import_firestore32.getDoc)(clinicRef);
8780
+ if (!clinicDoc.exists()) {
8781
+ throw new Error(`Clinic with ID ${clinicId} not found`);
8782
+ }
8783
+ const workingHours = [];
8784
+ const dayOfWeek = date.getDay();
8785
+ if (dayOfWeek === 0 || dayOfWeek === 6) {
8786
+ return workingHours;
8787
+ }
8788
+ const workingDate = new Date(date);
8789
+ workingDate.setHours(9, 0, 0, 0);
8790
+ const startTime = new Date(workingDate);
8791
+ workingDate.setHours(17, 0, 0, 0);
8792
+ const endTime = new Date(workingDate);
8793
+ workingHours.push({
8794
+ start: startTime,
8795
+ end: endTime,
8796
+ isAvailable: true
8797
+ });
8798
+ return workingHours;
8799
+ }
8800
+ /**
8801
+ * Gets doctor's schedule for a specific date
8802
+ * @param doctorId - ID of the doctor
8803
+ * @param date - Date to get schedule for
8804
+ * @returns Doctor's schedule
8805
+ */
8806
+ async getDoctorSchedule(doctorId, date) {
8807
+ const practitionerRef = (0, import_firestore32.doc)(this.db, PRACTITIONERS_COLLECTION, doctorId);
8808
+ const practitionerDoc = await (0, import_firestore32.getDoc)(practitionerRef);
8809
+ if (!practitionerDoc.exists()) {
8810
+ throw new Error(`Doctor with ID ${doctorId} not found`);
8811
+ }
8812
+ const schedule = [];
8813
+ const dayOfWeek = date.getDay();
8814
+ if (dayOfWeek === 0 || dayOfWeek === 6) {
8815
+ return schedule;
8816
+ }
8817
+ const scheduleDate = new Date(date);
8818
+ scheduleDate.setHours(9, 0, 0, 0);
8819
+ const startTime = new Date(scheduleDate);
8820
+ scheduleDate.setHours(17, 0, 0, 0);
8821
+ const endTime = new Date(scheduleDate);
8822
+ schedule.push({
8823
+ start: startTime,
8824
+ end: endTime,
8825
+ isAvailable: true
8826
+ });
8827
+ return schedule;
8828
+ }
8829
+ /**
8830
+ * Gets doctor's appointments for a specific date
8831
+ * @param doctorId - ID of the doctor
8832
+ * @param date - Date to get appointments for
8833
+ * @returns Array of calendar events
8834
+ */
8835
+ async getDoctorAppointments(doctorId, date) {
8836
+ const startOfDay = new Date(date);
8837
+ startOfDay.setHours(0, 0, 0, 0);
8838
+ const endOfDay = new Date(date);
8839
+ endOfDay.setHours(23, 59, 59, 999);
8840
+ const appointmentsRef = (0, import_firestore32.collection)(this.db, CALENDAR_COLLECTION);
8841
+ const q = (0, import_firestore32.query)(
8842
+ appointmentsRef,
8843
+ (0, import_firestore32.where)("practitionerProfileId", "==", doctorId),
8844
+ (0, import_firestore32.where)("eventTime.start", ">=", import_firestore31.Timestamp.fromDate(startOfDay)),
8845
+ (0, import_firestore32.where)("eventTime.start", "<=", import_firestore31.Timestamp.fromDate(endOfDay)),
8846
+ (0, import_firestore32.where)("status", "in", [
8847
+ "confirmed" /* CONFIRMED */,
8848
+ "pending" /* PENDING */
8849
+ ])
8850
+ );
8851
+ const querySnapshot = await (0, import_firestore32.getDocs)(q);
8852
+ return querySnapshot.docs.map((doc20) => doc20.data());
8853
+ }
8854
+ /**
8855
+ * Calculates available time slots based on working hours, schedule and existing appointments
8856
+ * @param workingHours - Clinic working hours
8857
+ * @param doctorSchedule - Doctor's schedule
8858
+ * @param existingAppointments - Existing appointments
8859
+ * @returns Array of available time slots
8860
+ */
8861
+ calculateAvailableSlots(workingHours, doctorSchedule, existingAppointments) {
8862
+ const availableSlots = [];
8863
+ for (const workingHour of workingHours) {
8864
+ for (const scheduleSlot of doctorSchedule) {
8865
+ const overlapStart = new Date(
8866
+ Math.max(workingHour.start.getTime(), scheduleSlot.start.getTime())
8867
+ );
8868
+ const overlapEnd = new Date(
8869
+ Math.min(workingHour.end.getTime(), scheduleSlot.end.getTime())
8870
+ );
8871
+ if (overlapStart < overlapEnd && workingHour.isAvailable && scheduleSlot.isAvailable) {
8872
+ let slotStart = new Date(overlapStart);
8873
+ while (slotStart < overlapEnd) {
8874
+ const slotEnd = new Date(
8875
+ slotStart.getTime() + MIN_APPOINTMENT_DURATION2 * 60 * 1e3
8876
+ );
8877
+ const hasOverlap = existingAppointments.some((appointment) => {
8878
+ const appointmentStart = appointment.eventTime.start.toDate();
8879
+ const appointmentEnd = appointment.eventTime.end.toDate();
8880
+ return slotStart >= appointmentStart && slotStart < appointmentEnd || slotEnd > appointmentStart && slotEnd <= appointmentEnd;
8881
+ });
8882
+ if (!hasOverlap && slotEnd <= overlapEnd) {
8883
+ availableSlots.push({
8884
+ start: new Date(slotStart),
8885
+ end: new Date(slotEnd),
8886
+ isAvailable: true
8887
+ });
8888
+ }
8889
+ slotStart = new Date(
8890
+ slotStart.getTime() + MIN_APPOINTMENT_DURATION2 * 60 * 1e3
8891
+ );
8892
+ }
8893
+ }
8894
+ }
8895
+ }
8896
+ return availableSlots;
8897
+ }
8898
+ /**
8899
+ * Fetches and creates info cards for clinic, doctor, and patient profiles
8900
+ * @param clinicId - ID of the clinic
8901
+ * @param doctorId - ID of the doctor
8902
+ * @param patientId - ID of the patient
8903
+ * @returns Object containing info cards for all profiles
8904
+ */
8905
+ async fetchProfileInfoCards(clinicId, doctorId, patientId) {
8906
+ var _a;
8907
+ try {
8908
+ const [clinicDoc, practitionerDoc, patientDoc, patientSensitiveInfoDoc] = await Promise.all([
8909
+ (0, import_firestore32.getDoc)((0, import_firestore32.doc)(this.db, CLINICS_COLLECTION, clinicId)),
8910
+ (0, import_firestore32.getDoc)((0, import_firestore32.doc)(this.db, PRACTITIONERS_COLLECTION, doctorId)),
8911
+ (0, import_firestore32.getDoc)((0, import_firestore32.doc)(this.db, PATIENTS_COLLECTION, patientId)),
8912
+ (0, import_firestore32.getDoc)(
8913
+ (0, import_firestore32.doc)(
8914
+ this.db,
8915
+ PATIENTS_COLLECTION,
8916
+ patientId,
8917
+ PATIENT_SENSITIVE_INFO_COLLECTION,
8918
+ patientId
8919
+ )
8920
+ )
8921
+ ]);
8922
+ const clinicInfo = clinicDoc.exists() ? {
8923
+ id: clinicDoc.id,
8924
+ featuredPhoto: clinicDoc.data().featuredPhoto || "",
8925
+ name: clinicDoc.data().name,
8926
+ description: clinicDoc.data().description || "",
8927
+ location: clinicDoc.data().location,
8928
+ contactInfo: clinicDoc.data().contactInfo
8929
+ } : null;
8930
+ const practitionerInfo = practitionerDoc.exists() ? {
8931
+ id: practitionerDoc.id,
8932
+ practitionerPhoto: practitionerDoc.data().basicInfo.profileImageUrl || null,
8933
+ name: `${practitionerDoc.data().basicInfo.firstName} ${practitionerDoc.data().basicInfo.lastName}`,
8934
+ email: practitionerDoc.data().basicInfo.email,
8935
+ phone: practitionerDoc.data().basicInfo.phoneNumber || null,
8936
+ certification: practitionerDoc.data().certification
8937
+ } : null;
8938
+ let patientInfo = null;
8939
+ if (patientSensitiveInfoDoc.exists()) {
8940
+ const sensitiveData = patientSensitiveInfoDoc.data();
8941
+ patientInfo = {
8942
+ id: patientId,
8943
+ fullName: `${sensitiveData.firstName} ${sensitiveData.lastName}`,
8944
+ email: sensitiveData.email || "",
8945
+ phone: sensitiveData.phoneNumber || null,
8946
+ dateOfBirth: sensitiveData.dateOfBirth || import_firestore31.Timestamp.now(),
8947
+ gender: sensitiveData.gender || "other" /* OTHER */
8948
+ };
8949
+ } else if (patientDoc.exists()) {
8950
+ patientInfo = {
8951
+ id: patientDoc.id,
8952
+ fullName: patientDoc.data().displayName,
8953
+ email: ((_a = patientDoc.data().contactInfo) == null ? void 0 : _a.email) || "",
8954
+ phone: patientDoc.data().phoneNumber || null,
8955
+ dateOfBirth: patientDoc.data().dateOfBirth || import_firestore31.Timestamp.now(),
8956
+ gender: patientDoc.data().gender || "other" /* OTHER */
8957
+ };
8958
+ }
8959
+ return {
8960
+ clinicInfo,
8961
+ practitionerInfo,
8962
+ patientInfo
8963
+ };
8964
+ } catch (error) {
8965
+ console.error("Error fetching profile info cards:", error);
8966
+ return {
8967
+ clinicInfo: null,
8968
+ practitionerInfo: null,
8969
+ patientInfo: null
8970
+ };
8971
+ }
8972
+ }
8973
+ // #endregion
8974
+ };
8975
+
8976
+ // src/validations/notification.schema.ts
8977
+ var import_zod18 = require("zod");
8978
+ var baseNotificationSchema = import_zod18.z.object({
8979
+ id: import_zod18.z.string().optional(),
8980
+ userId: import_zod18.z.string(),
8981
+ notificationTime: import_zod18.z.any(),
6376
8982
  // Timestamp
6377
- notificationType: import_zod16.z.nativeEnum(NotificationType),
6378
- notificationTokens: import_zod16.z.array(import_zod16.z.string()),
6379
- status: import_zod16.z.nativeEnum(NotificationStatus),
6380
- createdAt: import_zod16.z.any().optional(),
8983
+ notificationType: import_zod18.z.nativeEnum(NotificationType),
8984
+ notificationTokens: import_zod18.z.array(import_zod18.z.string()),
8985
+ status: import_zod18.z.nativeEnum(NotificationStatus),
8986
+ createdAt: import_zod18.z.any().optional(),
6381
8987
  // Timestamp
6382
- updatedAt: import_zod16.z.any().optional(),
8988
+ updatedAt: import_zod18.z.any().optional(),
6383
8989
  // Timestamp
6384
- title: import_zod16.z.string(),
6385
- body: import_zod16.z.string(),
6386
- isRead: import_zod16.z.boolean(),
6387
- userRole: import_zod16.z.nativeEnum(UserRole)
8990
+ title: import_zod18.z.string(),
8991
+ body: import_zod18.z.string(),
8992
+ isRead: import_zod18.z.boolean(),
8993
+ userRole: import_zod18.z.nativeEnum(UserRole)
6388
8994
  });
6389
8995
  var preRequirementNotificationSchema = baseNotificationSchema.extend({
6390
- notificationType: import_zod16.z.literal("preRequirement" /* PRE_REQUIREMENT */),
6391
- treatmentId: import_zod16.z.string(),
6392
- requirements: import_zod16.z.array(import_zod16.z.string()),
6393
- deadline: import_zod16.z.any()
8996
+ notificationType: import_zod18.z.literal("preRequirement" /* PRE_REQUIREMENT */),
8997
+ treatmentId: import_zod18.z.string(),
8998
+ requirements: import_zod18.z.array(import_zod18.z.string()),
8999
+ deadline: import_zod18.z.any()
6394
9000
  // Timestamp
6395
9001
  });
6396
9002
  var postRequirementNotificationSchema = baseNotificationSchema.extend({
6397
- notificationType: import_zod16.z.literal("postRequirement" /* POST_REQUIREMENT */),
6398
- treatmentId: import_zod16.z.string(),
6399
- requirements: import_zod16.z.array(import_zod16.z.string()),
6400
- deadline: import_zod16.z.any()
9003
+ notificationType: import_zod18.z.literal("postRequirement" /* POST_REQUIREMENT */),
9004
+ treatmentId: import_zod18.z.string(),
9005
+ requirements: import_zod18.z.array(import_zod18.z.string()),
9006
+ deadline: import_zod18.z.any()
6401
9007
  // Timestamp
6402
9008
  });
6403
9009
  var appointmentReminderNotificationSchema = baseNotificationSchema.extend({
6404
- notificationType: import_zod16.z.literal("appointmentReminder" /* APPOINTMENT_REMINDER */),
6405
- appointmentId: import_zod16.z.string(),
6406
- appointmentTime: import_zod16.z.any(),
9010
+ notificationType: import_zod18.z.literal("appointmentReminder" /* APPOINTMENT_REMINDER */),
9011
+ appointmentId: import_zod18.z.string(),
9012
+ appointmentTime: import_zod18.z.any(),
6407
9013
  // Timestamp
6408
- treatmentType: import_zod16.z.string(),
6409
- doctorName: import_zod16.z.string()
9014
+ treatmentType: import_zod18.z.string(),
9015
+ doctorName: import_zod18.z.string()
6410
9016
  });
6411
9017
  var appointmentNotificationSchema = baseNotificationSchema.extend({
6412
- notificationType: import_zod16.z.literal("appointmentNotification" /* APPOINTMENT_NOTIFICATION */),
6413
- appointmentId: import_zod16.z.string(),
6414
- appointmentStatus: import_zod16.z.string(),
6415
- previousStatus: import_zod16.z.string(),
6416
- reason: import_zod16.z.string().optional()
9018
+ notificationType: import_zod18.z.literal("appointmentNotification" /* APPOINTMENT_NOTIFICATION */),
9019
+ appointmentId: import_zod18.z.string(),
9020
+ appointmentStatus: import_zod18.z.string(),
9021
+ previousStatus: import_zod18.z.string(),
9022
+ reason: import_zod18.z.string().optional()
6417
9023
  });
6418
- var notificationSchema = import_zod16.z.discriminatedUnion("notificationType", [
9024
+ var notificationSchema = import_zod18.z.discriminatedUnion("notificationType", [
6419
9025
  preRequirementNotificationSchema,
6420
9026
  postRequirementNotificationSchema,
6421
9027
  appointmentReminderNotificationSchema,
@@ -6428,9 +9034,14 @@ var notificationSchema = import_zod16.z.discriminatedUnion("notificationType", [
6428
9034
  AllergyType,
6429
9035
  AuthService,
6430
9036
  BlockingCondition,
9037
+ CALENDAR_COLLECTION,
6431
9038
  CLINICS_COLLECTION,
6432
9039
  CLINIC_ADMINS_COLLECTION,
6433
9040
  CLINIC_GROUPS_COLLECTION,
9041
+ CalendarEventStatus,
9042
+ CalendarEventType,
9043
+ CalendarServiceV2,
9044
+ CalendarSyncStatus,
6434
9045
  CertificationLevel,
6435
9046
  CertificationSpecialty,
6436
9047
  ClinicAdminService,
@@ -6471,7 +9082,10 @@ var notificationSchema = import_zod16.z.discriminatedUnion("notificationType", [
6471
9082
  PractitionerService,
6472
9083
  PricingMeasure,
6473
9084
  ProcedureFamily,
9085
+ SYNCED_CALENDARS_COLLECTION,
6474
9086
  SubscriptionModel,
9087
+ SyncedCalendarProvider,
9088
+ SyncedCalendarsService,
6475
9089
  TreatmentBenefit,
6476
9090
  USER_ERRORS,
6477
9091
  UserService,
@@ -6488,6 +9102,8 @@ var notificationSchema = import_zod16.z.discriminatedUnion("notificationType", [
6488
9102
  appointmentReminderNotificationSchema,
6489
9103
  baseNotificationSchema,
6490
9104
  blockingConditionSchema,
9105
+ calendarEventSchema,
9106
+ calendarEventTimeSchema,
6491
9107
  clinicAdminOptionsSchema,
6492
9108
  clinicAdminSchema,
6493
9109
  clinicAdminSignupSchema,
@@ -6503,6 +9119,8 @@ var notificationSchema = import_zod16.z.discriminatedUnion("notificationType", [
6503
9119
  contactPersonSchema,
6504
9120
  contraindicationSchema,
6505
9121
  createAdminTokenSchema,
9122
+ createAppointmentSchema,
9123
+ createCalendarEventSchema,
6506
9124
  createClinicAdminSchema,
6507
9125
  createClinicGroupSchema,
6508
9126
  createClinicSchema,
@@ -6534,21 +9152,30 @@ var notificationSchema = import_zod16.z.discriminatedUnion("notificationType", [
6534
9152
  patientDoctorSchema,
6535
9153
  patientLocationInfoSchema,
6536
9154
  patientMedicalInfoSchema,
9155
+ patientProfileInfoSchema,
6537
9156
  patientProfileSchema,
6538
9157
  patientSensitiveInfoSchema,
6539
9158
  postRequirementNotificationSchema,
6540
9159
  practitionerBasicInfoSchema,
6541
9160
  practitionerCertificationSchema,
6542
9161
  practitionerClinicProceduresSchema,
9162
+ practitionerClinicWorkingHoursSchema,
9163
+ practitionerProfileInfoSchema,
6543
9164
  practitionerReviewSchema,
6544
9165
  practitionerSchema,
6545
9166
  practitionerWorkingHoursSchema,
6546
9167
  preRequirementNotificationSchema,
9168
+ procedureCategorizationSchema,
9169
+ procedureInfoSchema,
6547
9170
  reviewInfoSchema,
6548
9171
  serviceInfoSchema,
9172
+ syncedCalendarEventSchema,
9173
+ timeSlotSchema,
6549
9174
  timestampSchema,
6550
9175
  updateAllergySchema,
9176
+ updateAppointmentSchema,
6551
9177
  updateBlockingConditionSchema,
9178
+ updateCalendarEventSchema,
6552
9179
  updateClinicAdminSchema,
6553
9180
  updateClinicGroupSchema,
6554
9181
  updateClinicSchema,