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