@blackcode_sa/metaestetics-api 1.8.1 → 1.8.2
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.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2956 -514
- package/dist/index.mjs +3052 -555
- package/package.json +1 -1
- package/src/index.backup.ts +1 -1
- package/src/services/appointment/appointment.service.ts +1 -1
- package/src/services/calendar/index.ts +1 -1
- package/src/services/index.ts +1 -0
- /package/src/services/calendar/{calendar-refactored.service.ts → calendar.v2.service.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
CLINIC_GROUPS_COLLECTION: () => CLINIC_GROUPS_COLLECTION,
|
|
44
44
|
CalendarEventStatus: () => CalendarEventStatus,
|
|
45
45
|
CalendarEventType: () => CalendarEventType,
|
|
46
|
+
CalendarServiceV2: () => CalendarServiceV2,
|
|
46
47
|
CalendarServiceV3: () => CalendarServiceV3,
|
|
47
48
|
CalendarSyncStatus: () => CalendarSyncStatus,
|
|
48
49
|
ClinicAdminService: () => ClinicAdminService,
|
|
@@ -111,6 +112,7 @@ __export(index_exports, {
|
|
|
111
112
|
USERS_COLLECTION: () => USERS_COLLECTION,
|
|
112
113
|
USER_FORMS_SUBCOLLECTION: () => USER_FORMS_SUBCOLLECTION,
|
|
113
114
|
UserRole: () => UserRole,
|
|
115
|
+
UserService: () => UserService,
|
|
114
116
|
addAllergySchema: () => addAllergySchema,
|
|
115
117
|
addBlockingConditionSchema: () => addBlockingConditionSchema,
|
|
116
118
|
addContraindicationSchema: () => addContraindicationSchema,
|
|
@@ -843,20 +845,20 @@ var rescheduleAppointmentSchema = import_zod3.z.object({
|
|
|
843
845
|
var import_firestore = require("firebase/firestore");
|
|
844
846
|
|
|
845
847
|
// src/types/calendar/index.ts
|
|
846
|
-
var CalendarEventStatus = /* @__PURE__ */ ((
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
return
|
|
848
|
+
var CalendarEventStatus = /* @__PURE__ */ ((CalendarEventStatus4) => {
|
|
849
|
+
CalendarEventStatus4["PENDING"] = "pending";
|
|
850
|
+
CalendarEventStatus4["CONFIRMED"] = "confirmed";
|
|
851
|
+
CalendarEventStatus4["REJECTED"] = "rejected";
|
|
852
|
+
CalendarEventStatus4["CANCELED"] = "canceled";
|
|
853
|
+
CalendarEventStatus4["RESCHEDULED"] = "rescheduled";
|
|
854
|
+
CalendarEventStatus4["COMPLETED"] = "completed";
|
|
855
|
+
CalendarEventStatus4["NO_SHOW"] = "no_show";
|
|
856
|
+
return CalendarEventStatus4;
|
|
855
857
|
})(CalendarEventStatus || {});
|
|
856
|
-
var CalendarSyncStatus = /* @__PURE__ */ ((
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
return
|
|
858
|
+
var CalendarSyncStatus = /* @__PURE__ */ ((CalendarSyncStatus4) => {
|
|
859
|
+
CalendarSyncStatus4["INTERNAL"] = "internal";
|
|
860
|
+
CalendarSyncStatus4["EXTERNAL"] = "external";
|
|
861
|
+
return CalendarSyncStatus4;
|
|
860
862
|
})(CalendarSyncStatus || {});
|
|
861
863
|
var CalendarEventType = /* @__PURE__ */ ((CalendarEventType3) => {
|
|
862
864
|
CalendarEventType3["APPOINTMENT"] = "appointment";
|
|
@@ -1228,7 +1230,7 @@ async function searchAppointmentsUtil(db, params) {
|
|
|
1228
1230
|
const q = (0, import_firestore.query)((0, import_firestore.collection)(db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
1229
1231
|
const querySnapshot = await (0, import_firestore.getDocs)(q);
|
|
1230
1232
|
const appointments = querySnapshot.docs.map(
|
|
1231
|
-
(
|
|
1233
|
+
(doc32) => doc32.data()
|
|
1232
1234
|
);
|
|
1233
1235
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
1234
1236
|
return { appointments, lastDoc };
|
|
@@ -2035,7 +2037,7 @@ var AppointmentService = class extends BaseService {
|
|
|
2035
2037
|
);
|
|
2036
2038
|
const querySnapshot = await (0, import_firestore2.getDocs)(q);
|
|
2037
2039
|
const appointments = querySnapshot.docs.map(
|
|
2038
|
-
(
|
|
2040
|
+
(doc32) => doc32.data()
|
|
2039
2041
|
);
|
|
2040
2042
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
2041
2043
|
console.log(
|
|
@@ -2108,7 +2110,7 @@ var AppointmentService = class extends BaseService {
|
|
|
2108
2110
|
);
|
|
2109
2111
|
const querySnapshot = await (0, import_firestore2.getDocs)(q);
|
|
2110
2112
|
const appointments = querySnapshot.docs.map(
|
|
2111
|
-
(
|
|
2113
|
+
(doc32) => doc32.data()
|
|
2112
2114
|
);
|
|
2113
2115
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
2114
2116
|
console.log(
|
|
@@ -2819,11 +2821,11 @@ var UserRole = /* @__PURE__ */ ((UserRole2) => {
|
|
|
2819
2821
|
var USERS_COLLECTION = "users";
|
|
2820
2822
|
|
|
2821
2823
|
// src/types/calendar/synced-calendar.types.ts
|
|
2822
|
-
var SyncedCalendarProvider = /* @__PURE__ */ ((
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
return
|
|
2824
|
+
var SyncedCalendarProvider = /* @__PURE__ */ ((SyncedCalendarProvider3) => {
|
|
2825
|
+
SyncedCalendarProvider3["GOOGLE"] = "google";
|
|
2826
|
+
SyncedCalendarProvider3["OUTLOOK"] = "outlook";
|
|
2827
|
+
SyncedCalendarProvider3["APPLE"] = "apple";
|
|
2828
|
+
return SyncedCalendarProvider3;
|
|
2827
2829
|
})(SyncedCalendarProvider || {});
|
|
2828
2830
|
var SYNCED_CALENDARS_COLLECTION = "syncedCalendars";
|
|
2829
2831
|
|
|
@@ -3361,7 +3363,7 @@ var MediaService = class extends BaseService {
|
|
|
3361
3363
|
try {
|
|
3362
3364
|
const querySnapshot = await (0, import_firestore5.getDocs)(finalQuery);
|
|
3363
3365
|
const mediaList = querySnapshot.docs.map(
|
|
3364
|
-
(
|
|
3366
|
+
(doc32) => doc32.data()
|
|
3365
3367
|
);
|
|
3366
3368
|
console.log(`[MediaService] Found ${mediaList.length} media items.`);
|
|
3367
3369
|
return mediaList;
|
|
@@ -3415,8 +3417,8 @@ var getPatientsByClinicUtil = async (db, clinicId, options) => {
|
|
|
3415
3417
|
}
|
|
3416
3418
|
const patientsSnapshot = await (0, import_firestore6.getDocs)(q);
|
|
3417
3419
|
const patients = [];
|
|
3418
|
-
patientsSnapshot.forEach((
|
|
3419
|
-
patients.push(
|
|
3420
|
+
patientsSnapshot.forEach((doc32) => {
|
|
3421
|
+
patients.push(doc32.data());
|
|
3420
3422
|
});
|
|
3421
3423
|
console.log(
|
|
3422
3424
|
`[getPatientsByClinicUtil] Found ${patients.length} patients for clinic ID: ${clinicId}`
|
|
@@ -3769,8 +3771,8 @@ var getPatientsByPractitionerUtil = async (db, practitionerId, options) => {
|
|
|
3769
3771
|
}
|
|
3770
3772
|
const patientsSnapshot = await (0, import_firestore9.getDocs)(q);
|
|
3771
3773
|
const patients = [];
|
|
3772
|
-
patientsSnapshot.forEach((
|
|
3773
|
-
patients.push(
|
|
3774
|
+
patientsSnapshot.forEach((doc32) => {
|
|
3775
|
+
patients.push(doc32.data());
|
|
3774
3776
|
});
|
|
3775
3777
|
console.log(
|
|
3776
3778
|
`[getPatientsByPractitionerUtil] Found ${patients.length} patients for practitioner ID: ${practitionerId}`
|
|
@@ -4338,7 +4340,7 @@ async function getClinicAdminsByGroup(db, clinicGroupId) {
|
|
|
4338
4340
|
(0, import_firestore11.where)("clinicGroupId", "==", clinicGroupId)
|
|
4339
4341
|
);
|
|
4340
4342
|
const querySnapshot = await (0, import_firestore11.getDocs)(q);
|
|
4341
|
-
return querySnapshot.docs.map((
|
|
4343
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
4342
4344
|
}
|
|
4343
4345
|
async function updateClinicAdmin(db, adminId, data) {
|
|
4344
4346
|
const admin2 = await getClinicAdmin(db, adminId);
|
|
@@ -5001,9 +5003,9 @@ var updateAllergyUtil = async (db, patientId, data, requesterId, requesterRoles)
|
|
|
5001
5003
|
};
|
|
5002
5004
|
var removeAllergyUtil = async (db, patientId, allergyIndex, requesterId, requesterRoles) => {
|
|
5003
5005
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
5004
|
-
const
|
|
5005
|
-
if (!
|
|
5006
|
-
const medicalInfo =
|
|
5006
|
+
const doc32 = await (0, import_firestore16.getDoc)(getMedicalInfoDocRef(db, patientId));
|
|
5007
|
+
if (!doc32.exists()) throw new Error("Medical info not found");
|
|
5008
|
+
const medicalInfo = doc32.data();
|
|
5007
5009
|
if (allergyIndex >= medicalInfo.allergies.length) {
|
|
5008
5010
|
throw new Error("Invalid allergy index");
|
|
5009
5011
|
}
|
|
@@ -5030,9 +5032,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, requesterId, reque
|
|
|
5030
5032
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
5031
5033
|
const validatedData = updateBlockingConditionSchema.parse(data);
|
|
5032
5034
|
const { conditionIndex, ...updateData } = validatedData;
|
|
5033
|
-
const
|
|
5034
|
-
if (!
|
|
5035
|
-
const medicalInfo =
|
|
5035
|
+
const doc32 = await (0, import_firestore16.getDoc)(getMedicalInfoDocRef(db, patientId));
|
|
5036
|
+
if (!doc32.exists()) throw new Error("Medical info not found");
|
|
5037
|
+
const medicalInfo = doc32.data();
|
|
5036
5038
|
if (conditionIndex >= medicalInfo.blockingConditions.length) {
|
|
5037
5039
|
throw new Error("Invalid blocking condition index");
|
|
5038
5040
|
}
|
|
@@ -5049,9 +5051,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, requesterId, reque
|
|
|
5049
5051
|
};
|
|
5050
5052
|
var removeBlockingConditionUtil = async (db, patientId, conditionIndex, requesterId, requesterRoles) => {
|
|
5051
5053
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
5052
|
-
const
|
|
5053
|
-
if (!
|
|
5054
|
-
const medicalInfo =
|
|
5054
|
+
const doc32 = await (0, import_firestore16.getDoc)(getMedicalInfoDocRef(db, patientId));
|
|
5055
|
+
if (!doc32.exists()) throw new Error("Medical info not found");
|
|
5056
|
+
const medicalInfo = doc32.data();
|
|
5055
5057
|
if (conditionIndex >= medicalInfo.blockingConditions.length) {
|
|
5056
5058
|
throw new Error("Invalid blocking condition index");
|
|
5057
5059
|
}
|
|
@@ -5078,9 +5080,9 @@ var updateContraindicationUtil = async (db, patientId, data, requesterId, reques
|
|
|
5078
5080
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
5079
5081
|
const validatedData = updateContraindicationSchema.parse(data);
|
|
5080
5082
|
const { contraindicationIndex, ...updateData } = validatedData;
|
|
5081
|
-
const
|
|
5082
|
-
if (!
|
|
5083
|
-
const medicalInfo =
|
|
5083
|
+
const doc32 = await (0, import_firestore16.getDoc)(getMedicalInfoDocRef(db, patientId));
|
|
5084
|
+
if (!doc32.exists()) throw new Error("Medical info not found");
|
|
5085
|
+
const medicalInfo = doc32.data();
|
|
5084
5086
|
if (contraindicationIndex >= medicalInfo.contraindications.length) {
|
|
5085
5087
|
throw new Error("Invalid contraindication index");
|
|
5086
5088
|
}
|
|
@@ -5097,9 +5099,9 @@ var updateContraindicationUtil = async (db, patientId, data, requesterId, reques
|
|
|
5097
5099
|
};
|
|
5098
5100
|
var removeContraindicationUtil = async (db, patientId, contraindicationIndex, requesterId, requesterRoles) => {
|
|
5099
5101
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
5100
|
-
const
|
|
5101
|
-
if (!
|
|
5102
|
-
const medicalInfo =
|
|
5102
|
+
const doc32 = await (0, import_firestore16.getDoc)(getMedicalInfoDocRef(db, patientId));
|
|
5103
|
+
if (!doc32.exists()) throw new Error("Medical info not found");
|
|
5104
|
+
const medicalInfo = doc32.data();
|
|
5103
5105
|
if (contraindicationIndex >= medicalInfo.contraindications.length) {
|
|
5104
5106
|
throw new Error("Invalid contraindication index");
|
|
5105
5107
|
}
|
|
@@ -5126,9 +5128,9 @@ var updateMedicationUtil = async (db, patientId, data, requesterId, requesterRol
|
|
|
5126
5128
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
5127
5129
|
const validatedData = updateMedicationSchema.parse(data);
|
|
5128
5130
|
const { medicationIndex, ...updateData } = validatedData;
|
|
5129
|
-
const
|
|
5130
|
-
if (!
|
|
5131
|
-
const medicalInfo =
|
|
5131
|
+
const doc32 = await (0, import_firestore16.getDoc)(getMedicalInfoDocRef(db, patientId));
|
|
5132
|
+
if (!doc32.exists()) throw new Error("Medical info not found");
|
|
5133
|
+
const medicalInfo = doc32.data();
|
|
5132
5134
|
if (medicationIndex >= medicalInfo.currentMedications.length) {
|
|
5133
5135
|
throw new Error("Invalid medication index");
|
|
5134
5136
|
}
|
|
@@ -5145,9 +5147,9 @@ var updateMedicationUtil = async (db, patientId, data, requesterId, requesterRol
|
|
|
5145
5147
|
};
|
|
5146
5148
|
var removeMedicationUtil = async (db, patientId, medicationIndex, requesterId, requesterRoles) => {
|
|
5147
5149
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
5148
|
-
const
|
|
5149
|
-
if (!
|
|
5150
|
-
const medicalInfo =
|
|
5150
|
+
const doc32 = await (0, import_firestore16.getDoc)(getMedicalInfoDocRef(db, patientId));
|
|
5151
|
+
if (!doc32.exists()) throw new Error("Medical info not found");
|
|
5152
|
+
const medicalInfo = doc32.data();
|
|
5151
5153
|
if (medicationIndex >= medicalInfo.currentMedications.length) {
|
|
5152
5154
|
throw new Error("Invalid medication index");
|
|
5153
5155
|
}
|
|
@@ -5434,7 +5436,7 @@ var searchPatientsUtil = async (db, params, requester) => {
|
|
|
5434
5436
|
const finalQuery = (0, import_firestore17.query)(patientsCollectionRef, ...constraints);
|
|
5435
5437
|
const querySnapshot = await (0, import_firestore17.getDocs)(finalQuery);
|
|
5436
5438
|
const patients = querySnapshot.docs.map(
|
|
5437
|
-
(
|
|
5439
|
+
(doc32) => doc32.data()
|
|
5438
5440
|
);
|
|
5439
5441
|
console.log(
|
|
5440
5442
|
`[searchPatientsUtil] Found ${patients.length} patients matching criteria.`
|
|
@@ -5466,8 +5468,8 @@ var getAllPatientsUtil = async (db, options) => {
|
|
|
5466
5468
|
}
|
|
5467
5469
|
const patientsSnapshot = await (0, import_firestore17.getDocs)(q);
|
|
5468
5470
|
const patients = [];
|
|
5469
|
-
patientsSnapshot.forEach((
|
|
5470
|
-
patients.push(
|
|
5471
|
+
patientsSnapshot.forEach((doc32) => {
|
|
5472
|
+
patients.push(doc32.data());
|
|
5471
5473
|
});
|
|
5472
5474
|
console.log(`[getAllPatientsUtil] Found ${patients.length} patients`);
|
|
5473
5475
|
return patients;
|
|
@@ -5589,7 +5591,7 @@ var getActiveInviteTokensByClinicUtil = async (db, clinicId) => {
|
|
|
5589
5591
|
if (querySnapshot.empty) {
|
|
5590
5592
|
return [];
|
|
5591
5593
|
}
|
|
5592
|
-
return querySnapshot.docs.map((
|
|
5594
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
5593
5595
|
};
|
|
5594
5596
|
var getActiveInviteTokensByPatientUtil = async (db, patientId) => {
|
|
5595
5597
|
const tokensRef = (0, import_firestore18.collection)(
|
|
@@ -5607,7 +5609,7 @@ var getActiveInviteTokensByPatientUtil = async (db, patientId) => {
|
|
|
5607
5609
|
if (querySnapshot.empty) {
|
|
5608
5610
|
return [];
|
|
5609
5611
|
}
|
|
5610
|
-
return querySnapshot.docs.map((
|
|
5612
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
5611
5613
|
};
|
|
5612
5614
|
|
|
5613
5615
|
// src/services/patient/patient.service.ts
|
|
@@ -6713,7 +6715,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6713
6715
|
(0, import_firestore21.where)("expiresAt", ">", import_firestore21.Timestamp.now())
|
|
6714
6716
|
);
|
|
6715
6717
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6716
|
-
return querySnapshot.docs.map((
|
|
6718
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
6717
6719
|
}
|
|
6718
6720
|
/**
|
|
6719
6721
|
* Gets a token by its string value and validates it
|
|
@@ -6823,7 +6825,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6823
6825
|
(0, import_firestore21.where)("status", "==", "active" /* ACTIVE */)
|
|
6824
6826
|
);
|
|
6825
6827
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6826
|
-
return querySnapshot.docs.map((
|
|
6828
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
6827
6829
|
}
|
|
6828
6830
|
/**
|
|
6829
6831
|
* Dohvata sve zdravstvene radnike za određenu kliniku
|
|
@@ -6835,7 +6837,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6835
6837
|
(0, import_firestore21.where)("isActive", "==", true)
|
|
6836
6838
|
);
|
|
6837
6839
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6838
|
-
return querySnapshot.docs.map((
|
|
6840
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
6839
6841
|
}
|
|
6840
6842
|
/**
|
|
6841
6843
|
* Dohvata sve draft zdravstvene radnike za određenu kliniku sa statusom DRAFT
|
|
@@ -6847,7 +6849,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6847
6849
|
(0, import_firestore21.where)("status", "==", "draft" /* DRAFT */)
|
|
6848
6850
|
);
|
|
6849
6851
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6850
|
-
return querySnapshot.docs.map((
|
|
6852
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
6851
6853
|
}
|
|
6852
6854
|
/**
|
|
6853
6855
|
* Updates a practitioner
|
|
@@ -7061,7 +7063,7 @@ var PractitionerService = class extends BaseService {
|
|
|
7061
7063
|
);
|
|
7062
7064
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
7063
7065
|
const practitioners = querySnapshot.docs.map(
|
|
7064
|
-
(
|
|
7066
|
+
(doc32) => doc32.data()
|
|
7065
7067
|
);
|
|
7066
7068
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
7067
7069
|
return {
|
|
@@ -7132,8 +7134,8 @@ var PractitionerService = class extends BaseService {
|
|
|
7132
7134
|
console.log(
|
|
7133
7135
|
`[PRACTITIONER_SERVICE] Found ${querySnapshot.docs.length} practitioners with base query`
|
|
7134
7136
|
);
|
|
7135
|
-
let practitioners = querySnapshot.docs.map((
|
|
7136
|
-
return { ...
|
|
7137
|
+
let practitioners = querySnapshot.docs.map((doc32) => {
|
|
7138
|
+
return { ...doc32.data(), id: doc32.id };
|
|
7137
7139
|
});
|
|
7138
7140
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
7139
7141
|
if (filters.nameSearch && filters.nameSearch.trim() !== "") {
|
|
@@ -7617,7 +7619,7 @@ var UserService = class extends BaseService {
|
|
|
7617
7619
|
];
|
|
7618
7620
|
const q = (0, import_firestore22.query)((0, import_firestore22.collection)(this.db, USERS_COLLECTION), ...constraints);
|
|
7619
7621
|
const querySnapshot = await (0, import_firestore22.getDocs)(q);
|
|
7620
|
-
const users = querySnapshot.docs.map((
|
|
7622
|
+
const users = querySnapshot.docs.map((doc32) => doc32.data());
|
|
7621
7623
|
return users.map((userData) => userSchema.parse(userData));
|
|
7622
7624
|
}
|
|
7623
7625
|
/**
|
|
@@ -7981,7 +7983,7 @@ async function getAllActiveGroups(db) {
|
|
|
7981
7983
|
(0, import_firestore23.where)("isActive", "==", true)
|
|
7982
7984
|
);
|
|
7983
7985
|
const querySnapshot = await (0, import_firestore23.getDocs)(q);
|
|
7984
|
-
return querySnapshot.docs.map((
|
|
7986
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
7985
7987
|
}
|
|
7986
7988
|
async function updateClinicGroup(db, groupId, data, app) {
|
|
7987
7989
|
console.log("[CLINIC_GROUP] Updating clinic group", { groupId });
|
|
@@ -8421,7 +8423,7 @@ async function getClinicsByGroup(db, groupId) {
|
|
|
8421
8423
|
(0, import_firestore24.where)("isActive", "==", true)
|
|
8422
8424
|
);
|
|
8423
8425
|
const querySnapshot = await (0, import_firestore24.getDocs)(q);
|
|
8424
|
-
return querySnapshot.docs.map((
|
|
8426
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
8425
8427
|
}
|
|
8426
8428
|
async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app) {
|
|
8427
8429
|
console.log("[CLINIC] Starting clinic update", { clinicId, adminId });
|
|
@@ -8615,7 +8617,7 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
|
|
|
8615
8617
|
}
|
|
8616
8618
|
const q = (0, import_firestore24.query)((0, import_firestore24.collection)(db, CLINICS_COLLECTION), ...constraints);
|
|
8617
8619
|
const querySnapshot = await (0, import_firestore24.getDocs)(q);
|
|
8618
|
-
return querySnapshot.docs.map((
|
|
8620
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
8619
8621
|
}
|
|
8620
8622
|
async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
|
|
8621
8623
|
return getClinicsByAdmin(
|
|
@@ -8660,11 +8662,11 @@ async function getAllClinics(db, pagination, lastDoc) {
|
|
|
8660
8662
|
}
|
|
8661
8663
|
const clinicsSnapshot = await (0, import_firestore24.getDocs)(clinicsQuery);
|
|
8662
8664
|
const lastVisible = clinicsSnapshot.docs[clinicsSnapshot.docs.length - 1];
|
|
8663
|
-
const clinics = clinicsSnapshot.docs.map((
|
|
8664
|
-
const data =
|
|
8665
|
+
const clinics = clinicsSnapshot.docs.map((doc32) => {
|
|
8666
|
+
const data = doc32.data();
|
|
8665
8667
|
return {
|
|
8666
8668
|
...data,
|
|
8667
|
-
id:
|
|
8669
|
+
id: doc32.id
|
|
8668
8670
|
};
|
|
8669
8671
|
});
|
|
8670
8672
|
return {
|
|
@@ -8691,8 +8693,8 @@ async function getAllClinicsInRange(db, center, rangeInKm, pagination, lastDoc)
|
|
|
8691
8693
|
];
|
|
8692
8694
|
const q = (0, import_firestore24.query)((0, import_firestore24.collection)(db, CLINICS_COLLECTION), ...constraints);
|
|
8693
8695
|
const querySnapshot = await (0, import_firestore24.getDocs)(q);
|
|
8694
|
-
for (const
|
|
8695
|
-
const clinic =
|
|
8696
|
+
for (const doc32 of querySnapshot.docs) {
|
|
8697
|
+
const clinic = doc32.data();
|
|
8696
8698
|
const distance = (0, import_geofire_common4.distanceBetween)(
|
|
8697
8699
|
[center.latitude, center.longitude],
|
|
8698
8700
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -8809,8 +8811,8 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
|
|
|
8809
8811
|
}
|
|
8810
8812
|
const q = (0, import_firestore25.query)((0, import_firestore25.collection)(db, CLINICS_COLLECTION), ...constraints);
|
|
8811
8813
|
const querySnapshot = await (0, import_firestore25.getDocs)(q);
|
|
8812
|
-
for (const
|
|
8813
|
-
const clinic =
|
|
8814
|
+
for (const doc32 of querySnapshot.docs) {
|
|
8815
|
+
const clinic = doc32.data();
|
|
8814
8816
|
const distance = (0, import_geofire_common5.distanceBetween)(
|
|
8815
8817
|
[center.latitude, center.longitude],
|
|
8816
8818
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -8898,8 +8900,8 @@ async function getClinicsByFilters(db, filters) {
|
|
|
8898
8900
|
console.log(
|
|
8899
8901
|
`[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics in geo bound`
|
|
8900
8902
|
);
|
|
8901
|
-
for (const
|
|
8902
|
-
const clinic = { ...
|
|
8903
|
+
for (const doc32 of querySnapshot.docs) {
|
|
8904
|
+
const clinic = { ...doc32.data(), id: doc32.id };
|
|
8903
8905
|
const distance = (0, import_geofire_common6.distanceBetween)(
|
|
8904
8906
|
[center.latitude, center.longitude],
|
|
8905
8907
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -8955,8 +8957,8 @@ async function getClinicsByFilters(db, filters) {
|
|
|
8955
8957
|
console.log(
|
|
8956
8958
|
`[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics with regular query`
|
|
8957
8959
|
);
|
|
8958
|
-
const clinics = querySnapshot.docs.map((
|
|
8959
|
-
return { ...
|
|
8960
|
+
const clinics = querySnapshot.docs.map((doc32) => {
|
|
8961
|
+
return { ...doc32.data(), id: doc32.id };
|
|
8960
8962
|
});
|
|
8961
8963
|
let filteredClinics = clinics;
|
|
8962
8964
|
if (filters.center) {
|
|
@@ -10274,12 +10276,226 @@ var AuthService = class extends BaseService {
|
|
|
10274
10276
|
}
|
|
10275
10277
|
};
|
|
10276
10278
|
|
|
10277
|
-
// src/services/calendar/calendar.
|
|
10279
|
+
// src/services/calendar/calendar.v2.service.ts
|
|
10280
|
+
var import_firestore36 = require("firebase/firestore");
|
|
10281
|
+
var import_firestore37 = require("firebase/firestore");
|
|
10282
|
+
|
|
10283
|
+
// src/services/calendar/utils/clinic.utils.ts
|
|
10278
10284
|
var import_firestore30 = require("firebase/firestore");
|
|
10285
|
+
|
|
10286
|
+
// src/services/calendar/utils/docs.utils.ts
|
|
10287
|
+
var import_firestore29 = require("firebase/firestore");
|
|
10288
|
+
function getPractitionerCalendarEventDocRef(db, practitionerId, eventId) {
|
|
10289
|
+
return (0, import_firestore29.doc)(
|
|
10290
|
+
db,
|
|
10291
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
10292
|
+
);
|
|
10293
|
+
}
|
|
10294
|
+
function getPatientCalendarEventDocRef(db, patientId, eventId) {
|
|
10295
|
+
return (0, import_firestore29.doc)(
|
|
10296
|
+
db,
|
|
10297
|
+
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
10298
|
+
);
|
|
10299
|
+
}
|
|
10300
|
+
function getClinicCalendarEventDocRef(db, clinicId, eventId) {
|
|
10301
|
+
return (0, import_firestore29.doc)(
|
|
10302
|
+
db,
|
|
10303
|
+
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
10304
|
+
);
|
|
10305
|
+
}
|
|
10306
|
+
function getPractitionerSyncedCalendarDocRef(db, practitionerId, syncedCalendarId) {
|
|
10307
|
+
return (0, import_firestore29.doc)(
|
|
10308
|
+
db,
|
|
10309
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/syncedCalendars/${syncedCalendarId}`
|
|
10310
|
+
);
|
|
10311
|
+
}
|
|
10312
|
+
function getPatientSyncedCalendarDocRef(db, patientId, syncedCalendarId) {
|
|
10313
|
+
return (0, import_firestore29.doc)(
|
|
10314
|
+
db,
|
|
10315
|
+
`${PATIENTS_COLLECTION}/${patientId}/syncedCalendars/${syncedCalendarId}`
|
|
10316
|
+
);
|
|
10317
|
+
}
|
|
10318
|
+
function getClinicSyncedCalendarDocRef(db, clinicId, syncedCalendarId) {
|
|
10319
|
+
return (0, import_firestore29.doc)(
|
|
10320
|
+
db,
|
|
10321
|
+
`${CLINICS_COLLECTION}/${clinicId}/syncedCalendars/${syncedCalendarId}`
|
|
10322
|
+
);
|
|
10323
|
+
}
|
|
10324
|
+
|
|
10325
|
+
// src/services/calendar/utils/clinic.utils.ts
|
|
10326
|
+
async function createClinicCalendarEventUtil(db, clinicId, eventData, generateId2) {
|
|
10327
|
+
const eventId = generateId2();
|
|
10328
|
+
const eventRef = getClinicCalendarEventDocRef(db, clinicId, eventId);
|
|
10329
|
+
const newEvent = {
|
|
10330
|
+
id: eventId,
|
|
10331
|
+
...eventData,
|
|
10332
|
+
createdAt: (0, import_firestore30.serverTimestamp)(),
|
|
10333
|
+
updatedAt: (0, import_firestore30.serverTimestamp)()
|
|
10334
|
+
};
|
|
10335
|
+
await (0, import_firestore30.setDoc)(eventRef, newEvent);
|
|
10336
|
+
return {
|
|
10337
|
+
...newEvent,
|
|
10338
|
+
createdAt: import_firestore30.Timestamp.now(),
|
|
10339
|
+
updatedAt: import_firestore30.Timestamp.now()
|
|
10340
|
+
};
|
|
10341
|
+
}
|
|
10342
|
+
async function updateClinicCalendarEventUtil(db, clinicId, eventId, updateData) {
|
|
10343
|
+
const eventRef = getClinicCalendarEventDocRef(db, clinicId, eventId);
|
|
10344
|
+
const updates = {
|
|
10345
|
+
...updateData,
|
|
10346
|
+
updatedAt: (0, import_firestore30.serverTimestamp)()
|
|
10347
|
+
};
|
|
10348
|
+
await (0, import_firestore30.updateDoc)(eventRef, updates);
|
|
10349
|
+
const updatedDoc = await (0, import_firestore30.getDoc)(eventRef);
|
|
10350
|
+
if (!updatedDoc.exists()) {
|
|
10351
|
+
throw new Error("Event not found after update");
|
|
10352
|
+
}
|
|
10353
|
+
return updatedDoc.data();
|
|
10354
|
+
}
|
|
10355
|
+
async function checkAutoConfirmAppointmentsUtil(db, clinicId) {
|
|
10356
|
+
const clinicDoc = await (0, import_firestore30.getDoc)((0, import_firestore30.doc)(db, `clinics/${clinicId}`));
|
|
10357
|
+
if (!clinicDoc.exists()) {
|
|
10358
|
+
throw new Error(`Clinic with ID ${clinicId} not found`);
|
|
10359
|
+
}
|
|
10360
|
+
const clinicData = clinicDoc.data();
|
|
10361
|
+
const clinicGroupId = clinicData.clinicGroupId;
|
|
10362
|
+
if (!clinicGroupId) {
|
|
10363
|
+
return false;
|
|
10364
|
+
}
|
|
10365
|
+
const clinicGroupDoc = await (0, import_firestore30.getDoc)(
|
|
10366
|
+
(0, import_firestore30.doc)(db, `${CLINIC_GROUPS_COLLECTION}/${clinicGroupId}`)
|
|
10367
|
+
);
|
|
10368
|
+
if (!clinicGroupDoc.exists()) {
|
|
10369
|
+
return false;
|
|
10370
|
+
}
|
|
10371
|
+
const clinicGroupData = clinicGroupDoc.data();
|
|
10372
|
+
return !!clinicGroupData.autoConfirmAppointments;
|
|
10373
|
+
}
|
|
10374
|
+
|
|
10375
|
+
// src/services/calendar/utils/patient.utils.ts
|
|
10279
10376
|
var import_firestore31 = require("firebase/firestore");
|
|
10377
|
+
async function createPatientCalendarEventUtil(db, patientId, eventData, generateId2) {
|
|
10378
|
+
const eventId = generateId2();
|
|
10379
|
+
const eventRef = getPatientCalendarEventDocRef(db, patientId, eventId);
|
|
10380
|
+
const newEvent = {
|
|
10381
|
+
id: eventId,
|
|
10382
|
+
...eventData,
|
|
10383
|
+
createdAt: (0, import_firestore31.serverTimestamp)(),
|
|
10384
|
+
updatedAt: (0, import_firestore31.serverTimestamp)()
|
|
10385
|
+
};
|
|
10386
|
+
await (0, import_firestore31.setDoc)(eventRef, newEvent);
|
|
10387
|
+
return {
|
|
10388
|
+
...newEvent,
|
|
10389
|
+
createdAt: import_firestore31.Timestamp.now(),
|
|
10390
|
+
updatedAt: import_firestore31.Timestamp.now()
|
|
10391
|
+
};
|
|
10392
|
+
}
|
|
10393
|
+
async function updatePatientCalendarEventUtil(db, patientId, eventId, updateData) {
|
|
10394
|
+
const eventRef = getPatientCalendarEventDocRef(db, patientId, eventId);
|
|
10395
|
+
const updates = {
|
|
10396
|
+
...updateData,
|
|
10397
|
+
updatedAt: (0, import_firestore31.serverTimestamp)()
|
|
10398
|
+
};
|
|
10399
|
+
await (0, import_firestore31.updateDoc)(eventRef, updates);
|
|
10400
|
+
const updatedDoc = await (0, import_firestore31.getDoc)(eventRef);
|
|
10401
|
+
if (!updatedDoc.exists()) {
|
|
10402
|
+
throw new Error("Event not found after update");
|
|
10403
|
+
}
|
|
10404
|
+
return updatedDoc.data();
|
|
10405
|
+
}
|
|
10406
|
+
|
|
10407
|
+
// src/services/calendar/utils/practitioner.utils.ts
|
|
10408
|
+
var import_firestore32 = require("firebase/firestore");
|
|
10409
|
+
async function createPractitionerCalendarEventUtil(db, practitionerId, eventData, generateId2) {
|
|
10410
|
+
const eventId = generateId2();
|
|
10411
|
+
const eventRef = getPractitionerCalendarEventDocRef(
|
|
10412
|
+
db,
|
|
10413
|
+
practitionerId,
|
|
10414
|
+
eventId
|
|
10415
|
+
);
|
|
10416
|
+
const newEvent = {
|
|
10417
|
+
id: eventId,
|
|
10418
|
+
...eventData,
|
|
10419
|
+
createdAt: (0, import_firestore32.serverTimestamp)(),
|
|
10420
|
+
updatedAt: (0, import_firestore32.serverTimestamp)()
|
|
10421
|
+
};
|
|
10422
|
+
await (0, import_firestore32.setDoc)(eventRef, newEvent);
|
|
10423
|
+
return {
|
|
10424
|
+
...newEvent,
|
|
10425
|
+
createdAt: import_firestore32.Timestamp.now(),
|
|
10426
|
+
updatedAt: import_firestore32.Timestamp.now()
|
|
10427
|
+
};
|
|
10428
|
+
}
|
|
10429
|
+
async function updatePractitionerCalendarEventUtil(db, practitionerId, eventId, updateData) {
|
|
10430
|
+
const eventRef = getPractitionerCalendarEventDocRef(
|
|
10431
|
+
db,
|
|
10432
|
+
practitionerId,
|
|
10433
|
+
eventId
|
|
10434
|
+
);
|
|
10435
|
+
const updates = {
|
|
10436
|
+
...updateData,
|
|
10437
|
+
updatedAt: (0, import_firestore32.serverTimestamp)()
|
|
10438
|
+
};
|
|
10439
|
+
await (0, import_firestore32.updateDoc)(eventRef, updates);
|
|
10440
|
+
const updatedDoc = await (0, import_firestore32.getDoc)(eventRef);
|
|
10441
|
+
if (!updatedDoc.exists()) {
|
|
10442
|
+
throw new Error("Event not found after update");
|
|
10443
|
+
}
|
|
10444
|
+
return updatedDoc.data();
|
|
10445
|
+
}
|
|
10446
|
+
|
|
10447
|
+
// src/services/calendar/utils/appointment.utils.ts
|
|
10448
|
+
async function createAppointmentUtil2(db, clinicId, practitionerId, patientId, eventData, generateId2) {
|
|
10449
|
+
const eventId = generateId2();
|
|
10450
|
+
const autoConfirm = await checkAutoConfirmAppointmentsUtil(db, clinicId);
|
|
10451
|
+
const initialStatus = autoConfirm ? "confirmed" /* CONFIRMED */ : "pending" /* PENDING */;
|
|
10452
|
+
const appointmentData = {
|
|
10453
|
+
...eventData,
|
|
10454
|
+
clinicBranchId: clinicId,
|
|
10455
|
+
practitionerProfileId: practitionerId,
|
|
10456
|
+
patientProfileId: patientId,
|
|
10457
|
+
eventType: "appointment" /* APPOINTMENT */,
|
|
10458
|
+
status: eventData.status || initialStatus
|
|
10459
|
+
};
|
|
10460
|
+
const clinicPromise = createClinicCalendarEventUtil(
|
|
10461
|
+
db,
|
|
10462
|
+
clinicId,
|
|
10463
|
+
appointmentData,
|
|
10464
|
+
() => eventId
|
|
10465
|
+
// Use the same ID for all calendars
|
|
10466
|
+
);
|
|
10467
|
+
const practitionerPromise = createPractitionerCalendarEventUtil(
|
|
10468
|
+
db,
|
|
10469
|
+
practitionerId,
|
|
10470
|
+
appointmentData,
|
|
10471
|
+
() => eventId
|
|
10472
|
+
// Use the same ID for all calendars
|
|
10473
|
+
);
|
|
10474
|
+
const patientPromise = createPatientCalendarEventUtil(
|
|
10475
|
+
db,
|
|
10476
|
+
patientId,
|
|
10477
|
+
appointmentData,
|
|
10478
|
+
() => eventId
|
|
10479
|
+
// Use the same ID for all calendars
|
|
10480
|
+
);
|
|
10481
|
+
const [clinicEvent] = await Promise.all([clinicPromise, practitionerPromise, patientPromise]);
|
|
10482
|
+
return clinicEvent;
|
|
10483
|
+
}
|
|
10484
|
+
async function updateAppointmentUtil2(db, clinicId, practitionerId, patientId, eventId, updateData) {
|
|
10485
|
+
const clinicPromise = updateClinicCalendarEventUtil(db, clinicId, eventId, updateData);
|
|
10486
|
+
const practitionerPromise = updatePractitionerCalendarEventUtil(
|
|
10487
|
+
db,
|
|
10488
|
+
practitionerId,
|
|
10489
|
+
eventId,
|
|
10490
|
+
updateData
|
|
10491
|
+
);
|
|
10492
|
+
const patientPromise = updatePatientCalendarEventUtil(db, patientId, eventId, updateData);
|
|
10493
|
+
const [clinicEvent] = await Promise.all([clinicPromise, practitionerPromise, patientPromise]);
|
|
10494
|
+
return clinicEvent;
|
|
10495
|
+
}
|
|
10280
10496
|
|
|
10281
10497
|
// src/services/calendar/utils/calendar-event.utils.ts
|
|
10282
|
-
var
|
|
10498
|
+
var import_firestore33 = require("firebase/firestore");
|
|
10283
10499
|
async function searchCalendarEventsUtil(db, params) {
|
|
10284
10500
|
const { searchLocation, entityId, ...filters } = params;
|
|
10285
10501
|
let baseCollectionPath;
|
|
@@ -10291,88 +10507,2312 @@ async function searchCalendarEventsUtil(db, params) {
|
|
|
10291
10507
|
"Practitioner ID (entityId) is required when searching practitioner calendar."
|
|
10292
10508
|
);
|
|
10293
10509
|
}
|
|
10294
|
-
baseCollectionPath = `${PRACTITIONERS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
10295
|
-
if (filters.practitionerId && filters.practitionerId !== entityId) {
|
|
10296
|
-
console.warn(
|
|
10297
|
-
`Provided practitionerId filter (${filters.practitionerId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
10298
|
-
);
|
|
10299
|
-
return [];
|
|
10510
|
+
baseCollectionPath = `${PRACTITIONERS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
10511
|
+
if (filters.practitionerId && filters.practitionerId !== entityId) {
|
|
10512
|
+
console.warn(
|
|
10513
|
+
`Provided practitionerId filter (${filters.practitionerId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
10514
|
+
);
|
|
10515
|
+
return [];
|
|
10516
|
+
}
|
|
10517
|
+
filters.practitionerId = void 0;
|
|
10518
|
+
break;
|
|
10519
|
+
case "patient" /* PATIENT */:
|
|
10520
|
+
if (!entityId) {
|
|
10521
|
+
throw new Error(
|
|
10522
|
+
"Patient ID (entityId) is required when searching patient calendar."
|
|
10523
|
+
);
|
|
10524
|
+
}
|
|
10525
|
+
baseCollectionPath = `${PATIENTS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
10526
|
+
if (filters.patientId && filters.patientId !== entityId) {
|
|
10527
|
+
console.warn(
|
|
10528
|
+
`Provided patientId filter (${filters.patientId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
10529
|
+
);
|
|
10530
|
+
return [];
|
|
10531
|
+
}
|
|
10532
|
+
filters.patientId = void 0;
|
|
10533
|
+
break;
|
|
10534
|
+
case "clinic" /* CLINIC */:
|
|
10535
|
+
if (!entityId) {
|
|
10536
|
+
throw new Error(
|
|
10537
|
+
"Clinic ID (entityId) is required when searching clinic-related events."
|
|
10538
|
+
);
|
|
10539
|
+
}
|
|
10540
|
+
baseCollectionPath = `${CLINICS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
10541
|
+
constraints.push((0, import_firestore33.where)("clinicBranchId", "==", entityId));
|
|
10542
|
+
if (filters.clinicId && filters.clinicId !== entityId) {
|
|
10543
|
+
console.warn(
|
|
10544
|
+
`Provided clinicId filter (${filters.clinicId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
10545
|
+
);
|
|
10546
|
+
return [];
|
|
10547
|
+
}
|
|
10548
|
+
filters.clinicId = void 0;
|
|
10549
|
+
break;
|
|
10550
|
+
default:
|
|
10551
|
+
throw new Error(`Invalid search location: ${searchLocation}`);
|
|
10552
|
+
}
|
|
10553
|
+
const collectionRef = (0, import_firestore33.collection)(db, baseCollectionPath);
|
|
10554
|
+
if (filters.clinicId) {
|
|
10555
|
+
constraints.push((0, import_firestore33.where)("clinicBranchId", "==", filters.clinicId));
|
|
10556
|
+
}
|
|
10557
|
+
if (filters.practitionerId) {
|
|
10558
|
+
constraints.push(
|
|
10559
|
+
(0, import_firestore33.where)("practitionerProfileId", "==", filters.practitionerId)
|
|
10560
|
+
);
|
|
10561
|
+
}
|
|
10562
|
+
if (filters.patientId) {
|
|
10563
|
+
constraints.push((0, import_firestore33.where)("patientProfileId", "==", filters.patientId));
|
|
10564
|
+
}
|
|
10565
|
+
if (filters.procedureId) {
|
|
10566
|
+
constraints.push((0, import_firestore33.where)("procedureId", "==", filters.procedureId));
|
|
10567
|
+
}
|
|
10568
|
+
if (filters.eventStatus) {
|
|
10569
|
+
constraints.push((0, import_firestore33.where)("status", "==", filters.eventStatus));
|
|
10570
|
+
}
|
|
10571
|
+
if (filters.eventType) {
|
|
10572
|
+
constraints.push((0, import_firestore33.where)("eventType", "==", filters.eventType));
|
|
10573
|
+
}
|
|
10574
|
+
if (filters.dateRange) {
|
|
10575
|
+
constraints.push((0, import_firestore33.where)("eventTime.start", ">=", filters.dateRange.start));
|
|
10576
|
+
constraints.push((0, import_firestore33.where)("eventTime.start", "<=", filters.dateRange.end));
|
|
10577
|
+
}
|
|
10578
|
+
try {
|
|
10579
|
+
const finalQuery = (0, import_firestore33.query)(collectionRef, ...constraints);
|
|
10580
|
+
const querySnapshot = await (0, import_firestore33.getDocs)(finalQuery);
|
|
10581
|
+
const events = querySnapshot.docs.map(
|
|
10582
|
+
(doc32) => ({ id: doc32.id, ...doc32.data() })
|
|
10583
|
+
);
|
|
10584
|
+
return events;
|
|
10585
|
+
} catch (error) {
|
|
10586
|
+
console.error("Error searching calendar events:", error);
|
|
10587
|
+
return [];
|
|
10588
|
+
}
|
|
10589
|
+
}
|
|
10590
|
+
|
|
10591
|
+
// src/services/calendar/utils/synced-calendar.utils.ts
|
|
10592
|
+
var import_firestore34 = require("firebase/firestore");
|
|
10593
|
+
async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendarData, generateId2) {
|
|
10594
|
+
const calendarId = generateId2();
|
|
10595
|
+
const calendarRef = getPractitionerSyncedCalendarDocRef(
|
|
10596
|
+
db,
|
|
10597
|
+
practitionerId,
|
|
10598
|
+
calendarId
|
|
10599
|
+
);
|
|
10600
|
+
const newCalendar = {
|
|
10601
|
+
id: calendarId,
|
|
10602
|
+
...calendarData,
|
|
10603
|
+
createdAt: (0, import_firestore34.serverTimestamp)(),
|
|
10604
|
+
updatedAt: (0, import_firestore34.serverTimestamp)()
|
|
10605
|
+
};
|
|
10606
|
+
await (0, import_firestore34.setDoc)(calendarRef, newCalendar);
|
|
10607
|
+
return {
|
|
10608
|
+
...newCalendar,
|
|
10609
|
+
createdAt: import_firestore34.Timestamp.now(),
|
|
10610
|
+
updatedAt: import_firestore34.Timestamp.now()
|
|
10611
|
+
};
|
|
10612
|
+
}
|
|
10613
|
+
async function createPatientSyncedCalendarUtil(db, patientId, calendarData, generateId2) {
|
|
10614
|
+
const calendarId = generateId2();
|
|
10615
|
+
const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
|
|
10616
|
+
const newCalendar = {
|
|
10617
|
+
id: calendarId,
|
|
10618
|
+
...calendarData,
|
|
10619
|
+
createdAt: (0, import_firestore34.serverTimestamp)(),
|
|
10620
|
+
updatedAt: (0, import_firestore34.serverTimestamp)()
|
|
10621
|
+
};
|
|
10622
|
+
await (0, import_firestore34.setDoc)(calendarRef, newCalendar);
|
|
10623
|
+
return {
|
|
10624
|
+
...newCalendar,
|
|
10625
|
+
createdAt: import_firestore34.Timestamp.now(),
|
|
10626
|
+
updatedAt: import_firestore34.Timestamp.now()
|
|
10627
|
+
};
|
|
10628
|
+
}
|
|
10629
|
+
async function createClinicSyncedCalendarUtil(db, clinicId, calendarData, generateId2) {
|
|
10630
|
+
const calendarId = generateId2();
|
|
10631
|
+
const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
|
|
10632
|
+
const newCalendar = {
|
|
10633
|
+
id: calendarId,
|
|
10634
|
+
...calendarData,
|
|
10635
|
+
createdAt: (0, import_firestore34.serverTimestamp)(),
|
|
10636
|
+
updatedAt: (0, import_firestore34.serverTimestamp)()
|
|
10637
|
+
};
|
|
10638
|
+
await (0, import_firestore34.setDoc)(calendarRef, newCalendar);
|
|
10639
|
+
return {
|
|
10640
|
+
...newCalendar,
|
|
10641
|
+
createdAt: import_firestore34.Timestamp.now(),
|
|
10642
|
+
updatedAt: import_firestore34.Timestamp.now()
|
|
10643
|
+
};
|
|
10644
|
+
}
|
|
10645
|
+
async function getPractitionerSyncedCalendarUtil(db, practitionerId, calendarId) {
|
|
10646
|
+
const calendarRef = getPractitionerSyncedCalendarDocRef(
|
|
10647
|
+
db,
|
|
10648
|
+
practitionerId,
|
|
10649
|
+
calendarId
|
|
10650
|
+
);
|
|
10651
|
+
const calendarDoc = await (0, import_firestore34.getDoc)(calendarRef);
|
|
10652
|
+
if (!calendarDoc.exists()) {
|
|
10653
|
+
return null;
|
|
10654
|
+
}
|
|
10655
|
+
return calendarDoc.data();
|
|
10656
|
+
}
|
|
10657
|
+
async function getPractitionerSyncedCalendarsUtil(db, practitionerId) {
|
|
10658
|
+
const calendarsRef = (0, import_firestore34.collection)(
|
|
10659
|
+
db,
|
|
10660
|
+
`practitioners/${practitionerId}/${SYNCED_CALENDARS_COLLECTION}`
|
|
10661
|
+
);
|
|
10662
|
+
const q = (0, import_firestore34.query)(calendarsRef, (0, import_firestore34.orderBy)("createdAt", "desc"));
|
|
10663
|
+
const querySnapshot = await (0, import_firestore34.getDocs)(q);
|
|
10664
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
10665
|
+
}
|
|
10666
|
+
async function getPatientSyncedCalendarUtil(db, patientId, calendarId) {
|
|
10667
|
+
const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
|
|
10668
|
+
const calendarDoc = await (0, import_firestore34.getDoc)(calendarRef);
|
|
10669
|
+
if (!calendarDoc.exists()) {
|
|
10670
|
+
return null;
|
|
10671
|
+
}
|
|
10672
|
+
return calendarDoc.data();
|
|
10673
|
+
}
|
|
10674
|
+
async function getPatientSyncedCalendarsUtil(db, patientId) {
|
|
10675
|
+
const calendarsRef = (0, import_firestore34.collection)(
|
|
10676
|
+
db,
|
|
10677
|
+
`patients/${patientId}/${SYNCED_CALENDARS_COLLECTION}`
|
|
10678
|
+
);
|
|
10679
|
+
const q = (0, import_firestore34.query)(calendarsRef, (0, import_firestore34.orderBy)("createdAt", "desc"));
|
|
10680
|
+
const querySnapshot = await (0, import_firestore34.getDocs)(q);
|
|
10681
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
10682
|
+
}
|
|
10683
|
+
async function getClinicSyncedCalendarUtil(db, clinicId, calendarId) {
|
|
10684
|
+
const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
|
|
10685
|
+
const calendarDoc = await (0, import_firestore34.getDoc)(calendarRef);
|
|
10686
|
+
if (!calendarDoc.exists()) {
|
|
10687
|
+
return null;
|
|
10688
|
+
}
|
|
10689
|
+
return calendarDoc.data();
|
|
10690
|
+
}
|
|
10691
|
+
async function getClinicSyncedCalendarsUtil(db, clinicId) {
|
|
10692
|
+
const calendarsRef = (0, import_firestore34.collection)(
|
|
10693
|
+
db,
|
|
10694
|
+
`clinics/${clinicId}/${SYNCED_CALENDARS_COLLECTION}`
|
|
10695
|
+
);
|
|
10696
|
+
const q = (0, import_firestore34.query)(calendarsRef, (0, import_firestore34.orderBy)("createdAt", "desc"));
|
|
10697
|
+
const querySnapshot = await (0, import_firestore34.getDocs)(q);
|
|
10698
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
10699
|
+
}
|
|
10700
|
+
async function updatePractitionerSyncedCalendarUtil(db, practitionerId, calendarId, updateData) {
|
|
10701
|
+
const calendarRef = getPractitionerSyncedCalendarDocRef(
|
|
10702
|
+
db,
|
|
10703
|
+
practitionerId,
|
|
10704
|
+
calendarId
|
|
10705
|
+
);
|
|
10706
|
+
const updates = {
|
|
10707
|
+
...updateData,
|
|
10708
|
+
updatedAt: (0, import_firestore34.serverTimestamp)()
|
|
10709
|
+
};
|
|
10710
|
+
await (0, import_firestore34.updateDoc)(calendarRef, updates);
|
|
10711
|
+
const updatedDoc = await (0, import_firestore34.getDoc)(calendarRef);
|
|
10712
|
+
if (!updatedDoc.exists()) {
|
|
10713
|
+
throw new Error("Synced calendar not found after update");
|
|
10714
|
+
}
|
|
10715
|
+
return updatedDoc.data();
|
|
10716
|
+
}
|
|
10717
|
+
async function updatePatientSyncedCalendarUtil(db, patientId, calendarId, updateData) {
|
|
10718
|
+
const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
|
|
10719
|
+
const updates = {
|
|
10720
|
+
...updateData,
|
|
10721
|
+
updatedAt: (0, import_firestore34.serverTimestamp)()
|
|
10722
|
+
};
|
|
10723
|
+
await (0, import_firestore34.updateDoc)(calendarRef, updates);
|
|
10724
|
+
const updatedDoc = await (0, import_firestore34.getDoc)(calendarRef);
|
|
10725
|
+
if (!updatedDoc.exists()) {
|
|
10726
|
+
throw new Error("Synced calendar not found after update");
|
|
10727
|
+
}
|
|
10728
|
+
return updatedDoc.data();
|
|
10729
|
+
}
|
|
10730
|
+
async function updateClinicSyncedCalendarUtil(db, clinicId, calendarId, updateData) {
|
|
10731
|
+
const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
|
|
10732
|
+
const updates = {
|
|
10733
|
+
...updateData,
|
|
10734
|
+
updatedAt: (0, import_firestore34.serverTimestamp)()
|
|
10735
|
+
};
|
|
10736
|
+
await (0, import_firestore34.updateDoc)(calendarRef, updates);
|
|
10737
|
+
const updatedDoc = await (0, import_firestore34.getDoc)(calendarRef);
|
|
10738
|
+
if (!updatedDoc.exists()) {
|
|
10739
|
+
throw new Error("Synced calendar not found after update");
|
|
10740
|
+
}
|
|
10741
|
+
return updatedDoc.data();
|
|
10742
|
+
}
|
|
10743
|
+
async function deletePractitionerSyncedCalendarUtil(db, practitionerId, calendarId) {
|
|
10744
|
+
const calendarRef = getPractitionerSyncedCalendarDocRef(
|
|
10745
|
+
db,
|
|
10746
|
+
practitionerId,
|
|
10747
|
+
calendarId
|
|
10748
|
+
);
|
|
10749
|
+
await (0, import_firestore34.deleteDoc)(calendarRef);
|
|
10750
|
+
}
|
|
10751
|
+
async function deletePatientSyncedCalendarUtil(db, patientId, calendarId) {
|
|
10752
|
+
const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
|
|
10753
|
+
await (0, import_firestore34.deleteDoc)(calendarRef);
|
|
10754
|
+
}
|
|
10755
|
+
async function deleteClinicSyncedCalendarUtil(db, clinicId, calendarId) {
|
|
10756
|
+
const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
|
|
10757
|
+
await (0, import_firestore34.deleteDoc)(calendarRef);
|
|
10758
|
+
}
|
|
10759
|
+
async function updateLastSyncedTimestampUtil(db, entityType, entityId, calendarId) {
|
|
10760
|
+
const updateData = {
|
|
10761
|
+
lastSyncedAt: import_firestore34.Timestamp.now()
|
|
10762
|
+
};
|
|
10763
|
+
switch (entityType) {
|
|
10764
|
+
case "practitioner":
|
|
10765
|
+
return updatePractitionerSyncedCalendarUtil(
|
|
10766
|
+
db,
|
|
10767
|
+
entityId,
|
|
10768
|
+
calendarId,
|
|
10769
|
+
updateData
|
|
10770
|
+
);
|
|
10771
|
+
case "patient":
|
|
10772
|
+
return updatePatientSyncedCalendarUtil(
|
|
10773
|
+
db,
|
|
10774
|
+
entityId,
|
|
10775
|
+
calendarId,
|
|
10776
|
+
updateData
|
|
10777
|
+
);
|
|
10778
|
+
case "clinic":
|
|
10779
|
+
return updateClinicSyncedCalendarUtil(
|
|
10780
|
+
db,
|
|
10781
|
+
entityId,
|
|
10782
|
+
calendarId,
|
|
10783
|
+
updateData
|
|
10784
|
+
);
|
|
10785
|
+
default:
|
|
10786
|
+
throw new Error(`Invalid entity type: ${entityType}`);
|
|
10787
|
+
}
|
|
10788
|
+
}
|
|
10789
|
+
|
|
10790
|
+
// src/services/calendar/utils/google-calendar.utils.ts
|
|
10791
|
+
var import_firestore35 = require("firebase/firestore");
|
|
10792
|
+
var GOOGLE_CALENDAR_API_URL = "https://www.googleapis.com/calendar/v3";
|
|
10793
|
+
var GOOGLE_OAUTH_URL = "https://oauth2.googleapis.com/token";
|
|
10794
|
+
var CLIENT_ID = "your-client-id";
|
|
10795
|
+
var CLIENT_SECRET = "your-client-secret";
|
|
10796
|
+
var REDIRECT_URI = "your-redirect-uri";
|
|
10797
|
+
async function makeRequest(method, url, headers, data, params) {
|
|
10798
|
+
const queryParams = params ? "?" + Object.entries(params).map(
|
|
10799
|
+
([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
10800
|
+
).join("&") : "";
|
|
10801
|
+
const finalUrl = url + queryParams;
|
|
10802
|
+
const options = {
|
|
10803
|
+
method,
|
|
10804
|
+
headers,
|
|
10805
|
+
body: data ? JSON.stringify(data) : void 0
|
|
10806
|
+
};
|
|
10807
|
+
const response = await fetch(finalUrl, options);
|
|
10808
|
+
if (!response.ok) {
|
|
10809
|
+
const error = new Error(
|
|
10810
|
+
`Request failed with status ${response.status}`
|
|
10811
|
+
);
|
|
10812
|
+
error.response = response;
|
|
10813
|
+
throw error;
|
|
10814
|
+
}
|
|
10815
|
+
return response.json();
|
|
10816
|
+
}
|
|
10817
|
+
async function authenticateWithGoogleCalendarUtil(authCode) {
|
|
10818
|
+
try {
|
|
10819
|
+
const data = {
|
|
10820
|
+
code: authCode,
|
|
10821
|
+
client_id: CLIENT_ID,
|
|
10822
|
+
client_secret: CLIENT_SECRET,
|
|
10823
|
+
redirect_uri: REDIRECT_URI,
|
|
10824
|
+
grant_type: "authorization_code"
|
|
10825
|
+
};
|
|
10826
|
+
const response = await makeRequest(
|
|
10827
|
+
"post",
|
|
10828
|
+
GOOGLE_OAUTH_URL,
|
|
10829
|
+
{ "Content-Type": "application/json" },
|
|
10830
|
+
data
|
|
10831
|
+
);
|
|
10832
|
+
return {
|
|
10833
|
+
accessToken: response.access_token,
|
|
10834
|
+
refreshToken: response.refresh_token,
|
|
10835
|
+
expiresIn: response.expires_in
|
|
10836
|
+
};
|
|
10837
|
+
} catch (error) {
|
|
10838
|
+
const apiError = error;
|
|
10839
|
+
console.error(
|
|
10840
|
+
"Error authenticating with Google Calendar:",
|
|
10841
|
+
apiError.message || "Unknown error"
|
|
10842
|
+
);
|
|
10843
|
+
throw new Error(
|
|
10844
|
+
`Failed to authenticate with Google Calendar: ${apiError.message || "Unknown error"}`
|
|
10845
|
+
);
|
|
10846
|
+
}
|
|
10847
|
+
}
|
|
10848
|
+
async function refreshGoogleCalendarTokenUtil(refreshToken) {
|
|
10849
|
+
try {
|
|
10850
|
+
const data = {
|
|
10851
|
+
refresh_token: refreshToken,
|
|
10852
|
+
client_id: CLIENT_ID,
|
|
10853
|
+
client_secret: CLIENT_SECRET,
|
|
10854
|
+
grant_type: "refresh_token"
|
|
10855
|
+
};
|
|
10856
|
+
const response = await makeRequest(
|
|
10857
|
+
"post",
|
|
10858
|
+
GOOGLE_OAUTH_URL,
|
|
10859
|
+
{ "Content-Type": "application/json" },
|
|
10860
|
+
data
|
|
10861
|
+
);
|
|
10862
|
+
return {
|
|
10863
|
+
accessToken: response.access_token,
|
|
10864
|
+
expiresIn: response.expires_in
|
|
10865
|
+
};
|
|
10866
|
+
} catch (error) {
|
|
10867
|
+
const apiError = error;
|
|
10868
|
+
console.error(
|
|
10869
|
+
"Error refreshing Google Calendar token:",
|
|
10870
|
+
apiError.message || "Unknown error"
|
|
10871
|
+
);
|
|
10872
|
+
throw new Error(
|
|
10873
|
+
`Failed to refresh Google Calendar token: ${apiError.message || "Unknown error"}`
|
|
10874
|
+
);
|
|
10875
|
+
}
|
|
10876
|
+
}
|
|
10877
|
+
async function listGoogleCalendarsUtil(accessToken) {
|
|
10878
|
+
try {
|
|
10879
|
+
const response = await makeRequest(
|
|
10880
|
+
"get",
|
|
10881
|
+
`${GOOGLE_CALENDAR_API_URL}/users/me/calendarList`,
|
|
10882
|
+
{ Authorization: `Bearer ${accessToken}` }
|
|
10883
|
+
);
|
|
10884
|
+
return response.items.map((calendar) => ({
|
|
10885
|
+
id: calendar.id,
|
|
10886
|
+
name: calendar.summary
|
|
10887
|
+
}));
|
|
10888
|
+
} catch (error) {
|
|
10889
|
+
const apiError = error;
|
|
10890
|
+
console.error(
|
|
10891
|
+
"Error listing Google Calendars:",
|
|
10892
|
+
apiError.message || "Unknown error"
|
|
10893
|
+
);
|
|
10894
|
+
throw new Error(
|
|
10895
|
+
`Failed to list Google Calendars: ${apiError.message || "Unknown error"}`
|
|
10896
|
+
);
|
|
10897
|
+
}
|
|
10898
|
+
}
|
|
10899
|
+
async function ensureValidToken(db, entityType, entityId, syncedCalendar) {
|
|
10900
|
+
const expiryTime = syncedCalendar.tokenExpiry.toDate();
|
|
10901
|
+
const now = /* @__PURE__ */ new Date();
|
|
10902
|
+
const fiveMinutesFromNow = new Date(now.getTime() + 5 * 60 * 1e3);
|
|
10903
|
+
if (expiryTime < fiveMinutesFromNow) {
|
|
10904
|
+
const { accessToken, expiresIn } = await refreshGoogleCalendarTokenUtil(
|
|
10905
|
+
syncedCalendar.refreshToken
|
|
10906
|
+
);
|
|
10907
|
+
const tokenExpiry = /* @__PURE__ */ new Date();
|
|
10908
|
+
tokenExpiry.setSeconds(tokenExpiry.getSeconds() + expiresIn);
|
|
10909
|
+
const updateData = {
|
|
10910
|
+
accessToken,
|
|
10911
|
+
tokenExpiry: import_firestore35.Timestamp.fromDate(tokenExpiry)
|
|
10912
|
+
};
|
|
10913
|
+
switch (entityType) {
|
|
10914
|
+
case "practitioner":
|
|
10915
|
+
await updatePractitionerSyncedCalendarUtil(
|
|
10916
|
+
db,
|
|
10917
|
+
entityId,
|
|
10918
|
+
syncedCalendar.id,
|
|
10919
|
+
updateData
|
|
10920
|
+
);
|
|
10921
|
+
break;
|
|
10922
|
+
case "patient":
|
|
10923
|
+
await updatePatientSyncedCalendarUtil(
|
|
10924
|
+
db,
|
|
10925
|
+
entityId,
|
|
10926
|
+
syncedCalendar.id,
|
|
10927
|
+
updateData
|
|
10928
|
+
);
|
|
10929
|
+
break;
|
|
10930
|
+
case "clinic":
|
|
10931
|
+
await updateClinicSyncedCalendarUtil(
|
|
10932
|
+
db,
|
|
10933
|
+
entityId,
|
|
10934
|
+
syncedCalendar.id,
|
|
10935
|
+
updateData
|
|
10936
|
+
);
|
|
10937
|
+
break;
|
|
10938
|
+
}
|
|
10939
|
+
return accessToken;
|
|
10940
|
+
}
|
|
10941
|
+
return syncedCalendar.accessToken;
|
|
10942
|
+
}
|
|
10943
|
+
async function syncEventsToGoogleCalendarUtil(db, entityType, entityId, syncedCalendar, events, existingSyncId) {
|
|
10944
|
+
var _a, _b;
|
|
10945
|
+
try {
|
|
10946
|
+
const { accessToken } = await refreshGoogleCalendarTokenUtil(
|
|
10947
|
+
syncedCalendar.refreshToken
|
|
10948
|
+
);
|
|
10949
|
+
let syncedCount = 0;
|
|
10950
|
+
const errors = [];
|
|
10951
|
+
const eventIds = [];
|
|
10952
|
+
for (const event of events) {
|
|
10953
|
+
try {
|
|
10954
|
+
if (event.syncStatus === "external" /* EXTERNAL */) {
|
|
10955
|
+
continue;
|
|
10956
|
+
}
|
|
10957
|
+
if (entityType === "practitioner" && event.status !== "confirmed" /* CONFIRMED */) {
|
|
10958
|
+
continue;
|
|
10959
|
+
}
|
|
10960
|
+
if (entityType === "patient" && (event.status === "canceled" /* CANCELED */ || event.status === "rejected" /* REJECTED */)) {
|
|
10961
|
+
continue;
|
|
10962
|
+
}
|
|
10963
|
+
if (entityType === "clinic") {
|
|
10964
|
+
continue;
|
|
10965
|
+
}
|
|
10966
|
+
const googleEvent = convertCalendarEventToGoogleEventUtil(event);
|
|
10967
|
+
const headers = {
|
|
10968
|
+
Authorization: `Bearer ${accessToken}`,
|
|
10969
|
+
"Content-Type": "application/json"
|
|
10970
|
+
};
|
|
10971
|
+
let responseId = "";
|
|
10972
|
+
if (existingSyncId) {
|
|
10973
|
+
const response = await makeRequest(
|
|
10974
|
+
"put",
|
|
10975
|
+
`${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${existingSyncId}`,
|
|
10976
|
+
headers,
|
|
10977
|
+
googleEvent
|
|
10978
|
+
);
|
|
10979
|
+
responseId = response.id;
|
|
10980
|
+
} else {
|
|
10981
|
+
const existingSync = (_a = event.syncedCalendarEventId) == null ? void 0 : _a.find(
|
|
10982
|
+
(sync) => sync.syncedCalendarProvider === "google" /* GOOGLE */ && // We should check if this is the same calendar we're syncing with, but that information isn't stored
|
|
10983
|
+
// For now, we'll just use the first Google Calendar sync ID
|
|
10984
|
+
sync.syncedCalendarProvider === syncedCalendar.provider
|
|
10985
|
+
);
|
|
10986
|
+
if (existingSync) {
|
|
10987
|
+
const response = await makeRequest(
|
|
10988
|
+
"put",
|
|
10989
|
+
`${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${existingSync.eventId}`,
|
|
10990
|
+
headers,
|
|
10991
|
+
googleEvent
|
|
10992
|
+
);
|
|
10993
|
+
responseId = response.id;
|
|
10994
|
+
} else {
|
|
10995
|
+
const response = await makeRequest(
|
|
10996
|
+
"post",
|
|
10997
|
+
`${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events`,
|
|
10998
|
+
headers,
|
|
10999
|
+
googleEvent
|
|
11000
|
+
);
|
|
11001
|
+
responseId = response.id;
|
|
11002
|
+
}
|
|
11003
|
+
}
|
|
11004
|
+
if (responseId) {
|
|
11005
|
+
eventIds.push(responseId);
|
|
11006
|
+
syncedCount++;
|
|
11007
|
+
}
|
|
11008
|
+
} catch (error) {
|
|
11009
|
+
const apiError = error;
|
|
11010
|
+
errors.push({
|
|
11011
|
+
eventId: event.id,
|
|
11012
|
+
error: apiError.message || "Unknown error",
|
|
11013
|
+
status: (_b = apiError.response) == null ? void 0 : _b.status
|
|
11014
|
+
});
|
|
11015
|
+
}
|
|
11016
|
+
}
|
|
11017
|
+
await updateLastSyncedTimestampUtil(
|
|
11018
|
+
db,
|
|
11019
|
+
entityType,
|
|
11020
|
+
entityId,
|
|
11021
|
+
syncedCalendar.id
|
|
11022
|
+
);
|
|
11023
|
+
return {
|
|
11024
|
+
success: errors.length === 0,
|
|
11025
|
+
syncedEvents: syncedCount,
|
|
11026
|
+
errors,
|
|
11027
|
+
eventIds
|
|
11028
|
+
};
|
|
11029
|
+
} catch (error) {
|
|
11030
|
+
console.error("Error syncing with Google Calendar:", error);
|
|
11031
|
+
return {
|
|
11032
|
+
success: false,
|
|
11033
|
+
syncedEvents: 0,
|
|
11034
|
+
errors: [{ error: error.message || "Unknown error" }],
|
|
11035
|
+
eventIds: []
|
|
11036
|
+
};
|
|
11037
|
+
}
|
|
11038
|
+
}
|
|
11039
|
+
async function fetchEventsFromGoogleCalendarUtil(db, entityType, entityId, syncedCalendar, startDate, endDate) {
|
|
11040
|
+
try {
|
|
11041
|
+
const accessToken = await ensureValidToken(
|
|
11042
|
+
db,
|
|
11043
|
+
entityType,
|
|
11044
|
+
entityId,
|
|
11045
|
+
syncedCalendar
|
|
11046
|
+
);
|
|
11047
|
+
const timeMin = startDate.toISOString();
|
|
11048
|
+
const timeMax = endDate.toISOString();
|
|
11049
|
+
const response = await makeRequest(
|
|
11050
|
+
"get",
|
|
11051
|
+
`${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events`,
|
|
11052
|
+
{ Authorization: `Bearer ${accessToken}` },
|
|
11053
|
+
void 0,
|
|
11054
|
+
{
|
|
11055
|
+
timeMin,
|
|
11056
|
+
timeMax,
|
|
11057
|
+
singleEvents: "true",
|
|
11058
|
+
orderBy: "startTime"
|
|
11059
|
+
}
|
|
11060
|
+
);
|
|
11061
|
+
await updateLastSyncedTimestampUtil(
|
|
11062
|
+
db,
|
|
11063
|
+
entityType,
|
|
11064
|
+
entityId,
|
|
11065
|
+
syncedCalendar.id
|
|
11066
|
+
);
|
|
11067
|
+
return response.items;
|
|
11068
|
+
} catch (error) {
|
|
11069
|
+
const apiError = error;
|
|
11070
|
+
console.error(
|
|
11071
|
+
"Error fetching events from Google Calendar:",
|
|
11072
|
+
apiError.message || "Unknown error"
|
|
11073
|
+
);
|
|
11074
|
+
throw new Error(
|
|
11075
|
+
`Failed to fetch events from Google Calendar: ${apiError.message || "Unknown error"}`
|
|
11076
|
+
);
|
|
11077
|
+
}
|
|
11078
|
+
}
|
|
11079
|
+
function convertGoogleEventToCalendarEventUtil(googleEvent, entityId, entityType) {
|
|
11080
|
+
const start = googleEvent.start.dateTime ? new Date(googleEvent.start.dateTime) : new Date(googleEvent.start.date);
|
|
11081
|
+
const end = googleEvent.end.dateTime ? new Date(googleEvent.end.dateTime) : new Date(googleEvent.end.date);
|
|
11082
|
+
const calendarEvent = {
|
|
11083
|
+
eventName: googleEvent.summary || "External Event",
|
|
11084
|
+
eventLocation: googleEvent.location,
|
|
11085
|
+
eventTime: {
|
|
11086
|
+
start: import_firestore35.Timestamp.fromDate(start),
|
|
11087
|
+
end: import_firestore35.Timestamp.fromDate(end)
|
|
11088
|
+
},
|
|
11089
|
+
description: googleEvent.description || "",
|
|
11090
|
+
// External events are always set as CONFIRMED - status updates will happen externally
|
|
11091
|
+
status: "confirmed" /* CONFIRMED */,
|
|
11092
|
+
// All external events are marked as EXTERNAL to indicate they originated outside our system
|
|
11093
|
+
syncStatus: "external" /* EXTERNAL */,
|
|
11094
|
+
// All external events are treated as BLOCKING events
|
|
11095
|
+
eventType: "blocking" /* BLOCKING */,
|
|
11096
|
+
// Store the original Google Calendar event ID
|
|
11097
|
+
syncedCalendarEventId: [
|
|
11098
|
+
{
|
|
11099
|
+
eventId: googleEvent.id,
|
|
11100
|
+
syncedCalendarProvider: "google" /* GOOGLE */,
|
|
11101
|
+
syncedAt: import_firestore35.Timestamp.now()
|
|
11102
|
+
}
|
|
11103
|
+
]
|
|
11104
|
+
};
|
|
11105
|
+
switch (entityType) {
|
|
11106
|
+
case "practitioner":
|
|
11107
|
+
calendarEvent.practitionerProfileId = entityId;
|
|
11108
|
+
break;
|
|
11109
|
+
case "patient":
|
|
11110
|
+
calendarEvent.patientProfileId = entityId;
|
|
11111
|
+
break;
|
|
11112
|
+
case "clinic":
|
|
11113
|
+
calendarEvent.clinicBranchId = entityId;
|
|
11114
|
+
break;
|
|
11115
|
+
}
|
|
11116
|
+
return calendarEvent;
|
|
11117
|
+
}
|
|
11118
|
+
function convertCalendarEventToGoogleEventUtil(calendarEvent) {
|
|
11119
|
+
const googleEvent = {
|
|
11120
|
+
summary: calendarEvent.eventName,
|
|
11121
|
+
location: calendarEvent.eventLocation,
|
|
11122
|
+
description: calendarEvent.description,
|
|
11123
|
+
start: {
|
|
11124
|
+
dateTime: calendarEvent.eventTime.start.toDate().toISOString(),
|
|
11125
|
+
timeZone: "UTC"
|
|
11126
|
+
},
|
|
11127
|
+
end: {
|
|
11128
|
+
dateTime: calendarEvent.eventTime.end.toDate().toISOString(),
|
|
11129
|
+
timeZone: "UTC"
|
|
11130
|
+
},
|
|
11131
|
+
// Add reminders
|
|
11132
|
+
reminders: {
|
|
11133
|
+
useDefault: false,
|
|
11134
|
+
overrides: [
|
|
11135
|
+
{ method: "email", minutes: 24 * 60 },
|
|
11136
|
+
// 1 day before
|
|
11137
|
+
{ method: "popup", minutes: 30 }
|
|
11138
|
+
// 30 minutes before
|
|
11139
|
+
]
|
|
11140
|
+
}
|
|
11141
|
+
};
|
|
11142
|
+
switch (calendarEvent.status) {
|
|
11143
|
+
case "confirmed" /* CONFIRMED */:
|
|
11144
|
+
googleEvent.status = "confirmed";
|
|
11145
|
+
break;
|
|
11146
|
+
case "canceled" /* CANCELED */:
|
|
11147
|
+
googleEvent.status = "cancelled";
|
|
11148
|
+
break;
|
|
11149
|
+
case "pending" /* PENDING */:
|
|
11150
|
+
googleEvent.status = "tentative";
|
|
11151
|
+
break;
|
|
11152
|
+
default:
|
|
11153
|
+
googleEvent.status = "confirmed";
|
|
11154
|
+
}
|
|
11155
|
+
if (calendarEvent.eventType === "appointment" /* APPOINTMENT */) {
|
|
11156
|
+
googleEvent.attendees = [];
|
|
11157
|
+
if (calendarEvent.practitionerProfileId) {
|
|
11158
|
+
googleEvent.attendees.push({
|
|
11159
|
+
email: "practitioner@example.com",
|
|
11160
|
+
// This would be fetched from the practitioner profile
|
|
11161
|
+
displayName: "Dr. Practitioner",
|
|
11162
|
+
// This would be fetched from the practitioner profile
|
|
11163
|
+
responseStatus: "accepted"
|
|
11164
|
+
});
|
|
11165
|
+
}
|
|
11166
|
+
if (calendarEvent.patientProfileId) {
|
|
11167
|
+
googleEvent.attendees.push({
|
|
11168
|
+
email: "patient@example.com",
|
|
11169
|
+
// This would be fetched from the patient profile
|
|
11170
|
+
displayName: "Patient",
|
|
11171
|
+
// This would be fetched from the patient profile
|
|
11172
|
+
responseStatus: "needsAction"
|
|
11173
|
+
});
|
|
11174
|
+
}
|
|
11175
|
+
}
|
|
11176
|
+
return googleEvent;
|
|
11177
|
+
}
|
|
11178
|
+
async function deleteGoogleCalendarEventUtil(db, entityType, entityId, syncedCalendar, eventId) {
|
|
11179
|
+
try {
|
|
11180
|
+
const accessToken = await ensureValidToken(
|
|
11181
|
+
db,
|
|
11182
|
+
entityType,
|
|
11183
|
+
entityId,
|
|
11184
|
+
syncedCalendar
|
|
11185
|
+
);
|
|
11186
|
+
await makeRequest(
|
|
11187
|
+
"delete",
|
|
11188
|
+
`${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${eventId}`,
|
|
11189
|
+
{ Authorization: `Bearer ${accessToken}` }
|
|
11190
|
+
);
|
|
11191
|
+
return true;
|
|
11192
|
+
} catch (error) {
|
|
11193
|
+
const apiError = error;
|
|
11194
|
+
console.error(
|
|
11195
|
+
"Error deleting event from Google Calendar:",
|
|
11196
|
+
apiError.message || "Unknown error"
|
|
11197
|
+
);
|
|
11198
|
+
throw new Error(
|
|
11199
|
+
`Failed to delete event from Google Calendar: ${apiError.message || "Unknown error"}`
|
|
11200
|
+
);
|
|
11201
|
+
}
|
|
11202
|
+
}
|
|
11203
|
+
function getGoogleCalendarOAuthUrlUtil(scopes = ["https://www.googleapis.com/auth/calendar"]) {
|
|
11204
|
+
const scopeString = encodeURIComponent(scopes.join(" "));
|
|
11205
|
+
return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(
|
|
11206
|
+
REDIRECT_URI
|
|
11207
|
+
)}&response_type=code&scope=${scopeString}&access_type=offline&prompt=consent`;
|
|
11208
|
+
}
|
|
11209
|
+
|
|
11210
|
+
// src/services/calendar/synced-calendars.service.ts
|
|
11211
|
+
var SyncedCalendarsService = class extends BaseService {
|
|
11212
|
+
/**
|
|
11213
|
+
* Creates a new SyncedCalendarsService instance
|
|
11214
|
+
* @param db - Firestore instance
|
|
11215
|
+
* @param auth - Firebase Auth instance
|
|
11216
|
+
* @param app - Firebase App instance
|
|
11217
|
+
*/
|
|
11218
|
+
constructor(db, auth, app) {
|
|
11219
|
+
super(db, auth, app);
|
|
11220
|
+
}
|
|
11221
|
+
// ===== Practitioner Synced Calendars =====
|
|
11222
|
+
/**
|
|
11223
|
+
* Creates a synced calendar for a practitioner
|
|
11224
|
+
* @param practitionerId - ID of the practitioner
|
|
11225
|
+
* @param calendarData - Synced calendar data
|
|
11226
|
+
* @returns Created synced calendar
|
|
11227
|
+
*/
|
|
11228
|
+
async createPractitionerSyncedCalendar(practitionerId, calendarData) {
|
|
11229
|
+
return createPractitionerSyncedCalendarUtil(
|
|
11230
|
+
this.db,
|
|
11231
|
+
practitionerId,
|
|
11232
|
+
calendarData,
|
|
11233
|
+
this.generateId.bind(this)
|
|
11234
|
+
);
|
|
11235
|
+
}
|
|
11236
|
+
/**
|
|
11237
|
+
* Gets a synced calendar for a practitioner
|
|
11238
|
+
* @param practitionerId - ID of the practitioner
|
|
11239
|
+
* @param calendarId - ID of the synced calendar
|
|
11240
|
+
* @returns Synced calendar or null if not found
|
|
11241
|
+
*/
|
|
11242
|
+
async getPractitionerSyncedCalendar(practitionerId, calendarId) {
|
|
11243
|
+
return getPractitionerSyncedCalendarUtil(
|
|
11244
|
+
this.db,
|
|
11245
|
+
practitionerId,
|
|
11246
|
+
calendarId
|
|
11247
|
+
);
|
|
11248
|
+
}
|
|
11249
|
+
/**
|
|
11250
|
+
* Gets all synced calendars for a practitioner
|
|
11251
|
+
* @param practitionerId - ID of the practitioner
|
|
11252
|
+
* @returns Array of synced calendars
|
|
11253
|
+
*/
|
|
11254
|
+
async getPractitionerSyncedCalendars(practitionerId) {
|
|
11255
|
+
return getPractitionerSyncedCalendarsUtil(this.db, practitionerId);
|
|
11256
|
+
}
|
|
11257
|
+
/**
|
|
11258
|
+
* Updates a synced calendar for a practitioner
|
|
11259
|
+
* @param practitionerId - ID of the practitioner
|
|
11260
|
+
* @param calendarId - ID of the synced calendar
|
|
11261
|
+
* @param updateData - Data to update
|
|
11262
|
+
* @returns Updated synced calendar
|
|
11263
|
+
*/
|
|
11264
|
+
async updatePractitionerSyncedCalendar(practitionerId, calendarId, updateData) {
|
|
11265
|
+
return updatePractitionerSyncedCalendarUtil(
|
|
11266
|
+
this.db,
|
|
11267
|
+
practitionerId,
|
|
11268
|
+
calendarId,
|
|
11269
|
+
updateData
|
|
11270
|
+
);
|
|
11271
|
+
}
|
|
11272
|
+
/**
|
|
11273
|
+
* Deletes a synced calendar for a practitioner
|
|
11274
|
+
* @param practitionerId - ID of the practitioner
|
|
11275
|
+
* @param calendarId - ID of the synced calendar
|
|
11276
|
+
*/
|
|
11277
|
+
async deletePractitionerSyncedCalendar(practitionerId, calendarId) {
|
|
11278
|
+
return deletePractitionerSyncedCalendarUtil(
|
|
11279
|
+
this.db,
|
|
11280
|
+
practitionerId,
|
|
11281
|
+
calendarId
|
|
11282
|
+
);
|
|
11283
|
+
}
|
|
11284
|
+
// ===== Patient Synced Calendars =====
|
|
11285
|
+
/**
|
|
11286
|
+
* Creates a synced calendar for a patient
|
|
11287
|
+
* @param patientId - ID of the patient
|
|
11288
|
+
* @param calendarData - Synced calendar data
|
|
11289
|
+
* @returns Created synced calendar
|
|
11290
|
+
*/
|
|
11291
|
+
async createPatientSyncedCalendar(patientId, calendarData) {
|
|
11292
|
+
return createPatientSyncedCalendarUtil(
|
|
11293
|
+
this.db,
|
|
11294
|
+
patientId,
|
|
11295
|
+
calendarData,
|
|
11296
|
+
this.generateId.bind(this)
|
|
11297
|
+
);
|
|
11298
|
+
}
|
|
11299
|
+
/**
|
|
11300
|
+
* Gets a synced calendar for a patient
|
|
11301
|
+
* @param patientId - ID of the patient
|
|
11302
|
+
* @param calendarId - ID of the synced calendar
|
|
11303
|
+
* @returns Synced calendar or null if not found
|
|
11304
|
+
*/
|
|
11305
|
+
async getPatientSyncedCalendar(patientId, calendarId) {
|
|
11306
|
+
return getPatientSyncedCalendarUtil(this.db, patientId, calendarId);
|
|
11307
|
+
}
|
|
11308
|
+
/**
|
|
11309
|
+
* Gets all synced calendars for a patient
|
|
11310
|
+
* @param patientId - ID of the patient
|
|
11311
|
+
* @returns Array of synced calendars
|
|
11312
|
+
*/
|
|
11313
|
+
async getPatientSyncedCalendars(patientId) {
|
|
11314
|
+
return getPatientSyncedCalendarsUtil(this.db, patientId);
|
|
11315
|
+
}
|
|
11316
|
+
/**
|
|
11317
|
+
* Updates a synced calendar for a patient
|
|
11318
|
+
* @param patientId - ID of the patient
|
|
11319
|
+
* @param calendarId - ID of the synced calendar
|
|
11320
|
+
* @param updateData - Data to update
|
|
11321
|
+
* @returns Updated synced calendar
|
|
11322
|
+
*/
|
|
11323
|
+
async updatePatientSyncedCalendar(patientId, calendarId, updateData) {
|
|
11324
|
+
return updatePatientSyncedCalendarUtil(
|
|
11325
|
+
this.db,
|
|
11326
|
+
patientId,
|
|
11327
|
+
calendarId,
|
|
11328
|
+
updateData
|
|
11329
|
+
);
|
|
11330
|
+
}
|
|
11331
|
+
/**
|
|
11332
|
+
* Deletes a synced calendar for a patient
|
|
11333
|
+
* @param patientId - ID of the patient
|
|
11334
|
+
* @param calendarId - ID of the synced calendar
|
|
11335
|
+
*/
|
|
11336
|
+
async deletePatientSyncedCalendar(patientId, calendarId) {
|
|
11337
|
+
return deletePatientSyncedCalendarUtil(this.db, patientId, calendarId);
|
|
11338
|
+
}
|
|
11339
|
+
// ===== Clinic Synced Calendars =====
|
|
11340
|
+
/**
|
|
11341
|
+
* Creates a synced calendar for a clinic
|
|
11342
|
+
* @param clinicId - ID of the clinic
|
|
11343
|
+
* @param calendarData - Synced calendar data
|
|
11344
|
+
* @returns Created synced calendar
|
|
11345
|
+
*/
|
|
11346
|
+
async createClinicSyncedCalendar(clinicId, calendarData) {
|
|
11347
|
+
return createClinicSyncedCalendarUtil(
|
|
11348
|
+
this.db,
|
|
11349
|
+
clinicId,
|
|
11350
|
+
calendarData,
|
|
11351
|
+
this.generateId.bind(this)
|
|
11352
|
+
);
|
|
11353
|
+
}
|
|
11354
|
+
/**
|
|
11355
|
+
* Gets a synced calendar for a clinic
|
|
11356
|
+
* @param clinicId - ID of the clinic
|
|
11357
|
+
* @param calendarId - ID of the synced calendar
|
|
11358
|
+
* @returns Synced calendar or null if not found
|
|
11359
|
+
*/
|
|
11360
|
+
async getClinicSyncedCalendar(clinicId, calendarId) {
|
|
11361
|
+
return getClinicSyncedCalendarUtil(this.db, clinicId, calendarId);
|
|
11362
|
+
}
|
|
11363
|
+
/**
|
|
11364
|
+
* Gets all synced calendars for a clinic
|
|
11365
|
+
* @param clinicId - ID of the clinic
|
|
11366
|
+
* @returns Array of synced calendars
|
|
11367
|
+
*/
|
|
11368
|
+
async getClinicSyncedCalendars(clinicId) {
|
|
11369
|
+
return getClinicSyncedCalendarsUtil(this.db, clinicId);
|
|
11370
|
+
}
|
|
11371
|
+
/**
|
|
11372
|
+
* Updates a synced calendar for a clinic
|
|
11373
|
+
* @param clinicId - ID of the clinic
|
|
11374
|
+
* @param calendarId - ID of the synced calendar
|
|
11375
|
+
* @param updateData - Data to update
|
|
11376
|
+
* @returns Updated synced calendar
|
|
11377
|
+
*/
|
|
11378
|
+
async updateClinicSyncedCalendar(clinicId, calendarId, updateData) {
|
|
11379
|
+
return updateClinicSyncedCalendarUtil(
|
|
11380
|
+
this.db,
|
|
11381
|
+
clinicId,
|
|
11382
|
+
calendarId,
|
|
11383
|
+
updateData
|
|
11384
|
+
);
|
|
11385
|
+
}
|
|
11386
|
+
/**
|
|
11387
|
+
* Deletes a synced calendar for a clinic
|
|
11388
|
+
* @param clinicId - ID of the clinic
|
|
11389
|
+
* @param calendarId - ID of the synced calendar
|
|
11390
|
+
*/
|
|
11391
|
+
async deleteClinicSyncedCalendar(clinicId, calendarId) {
|
|
11392
|
+
return deleteClinicSyncedCalendarUtil(this.db, clinicId, calendarId);
|
|
11393
|
+
}
|
|
11394
|
+
// ===== Google Calendar Integration =====
|
|
11395
|
+
/**
|
|
11396
|
+
* Gets the OAuth URL for Google Calendar
|
|
11397
|
+
* @param scopes - OAuth scopes to request
|
|
11398
|
+
* @returns OAuth URL
|
|
11399
|
+
*/
|
|
11400
|
+
getGoogleCalendarOAuthUrl(scopes = ["https://www.googleapis.com/auth/calendar"]) {
|
|
11401
|
+
return getGoogleCalendarOAuthUrlUtil(scopes);
|
|
11402
|
+
}
|
|
11403
|
+
/**
|
|
11404
|
+
* Authenticates with Google Calendar using an authorization code
|
|
11405
|
+
* @param authCode - Authorization code from Google OAuth
|
|
11406
|
+
* @returns Access token, refresh token, and expiration time
|
|
11407
|
+
*/
|
|
11408
|
+
async authenticateWithGoogleCalendar(authCode) {
|
|
11409
|
+
return authenticateWithGoogleCalendarUtil(authCode);
|
|
11410
|
+
}
|
|
11411
|
+
/**
|
|
11412
|
+
* Lists available Google Calendars for a user
|
|
11413
|
+
* @param accessToken - Google API access token
|
|
11414
|
+
* @returns List of available calendars
|
|
11415
|
+
*/
|
|
11416
|
+
async listGoogleCalendars(accessToken) {
|
|
11417
|
+
return listGoogleCalendarsUtil(accessToken);
|
|
11418
|
+
}
|
|
11419
|
+
/**
|
|
11420
|
+
* Syncs events from our system to Google Calendar for a practitioner
|
|
11421
|
+
* @param practitionerId - ID of the practitioner
|
|
11422
|
+
* @param calendarId - ID of the synced calendar
|
|
11423
|
+
* @param events - Events to sync
|
|
11424
|
+
* @param existingSyncId - Optional existing sync ID for updating an event
|
|
11425
|
+
* @returns Result of the sync operation
|
|
11426
|
+
*/
|
|
11427
|
+
async syncPractitionerEventsToGoogleCalendar(practitionerId, calendarId, events, existingSyncId) {
|
|
11428
|
+
const syncedCalendar = await this.getPractitionerSyncedCalendar(
|
|
11429
|
+
practitionerId,
|
|
11430
|
+
calendarId
|
|
11431
|
+
);
|
|
11432
|
+
if (!syncedCalendar) {
|
|
11433
|
+
throw new Error("Synced calendar not found");
|
|
11434
|
+
}
|
|
11435
|
+
return syncEventsToGoogleCalendarUtil(
|
|
11436
|
+
this.db,
|
|
11437
|
+
"practitioner",
|
|
11438
|
+
practitionerId,
|
|
11439
|
+
syncedCalendar,
|
|
11440
|
+
events,
|
|
11441
|
+
existingSyncId
|
|
11442
|
+
);
|
|
11443
|
+
}
|
|
11444
|
+
/**
|
|
11445
|
+
* Syncs events from our system to Google Calendar for a patient
|
|
11446
|
+
* @param patientId - ID of the patient
|
|
11447
|
+
* @param calendarId - ID of the synced calendar
|
|
11448
|
+
* @param events - Events to sync
|
|
11449
|
+
* @param existingSyncId - Optional existing sync ID for updating an event
|
|
11450
|
+
* @returns Result of the sync operation
|
|
11451
|
+
*/
|
|
11452
|
+
async syncPatientEventsToGoogleCalendar(patientId, calendarId, events, existingSyncId) {
|
|
11453
|
+
const syncedCalendar = await this.getPatientSyncedCalendar(
|
|
11454
|
+
patientId,
|
|
11455
|
+
calendarId
|
|
11456
|
+
);
|
|
11457
|
+
if (!syncedCalendar) {
|
|
11458
|
+
throw new Error("Synced calendar not found");
|
|
11459
|
+
}
|
|
11460
|
+
return syncEventsToGoogleCalendarUtil(
|
|
11461
|
+
this.db,
|
|
11462
|
+
"patient",
|
|
11463
|
+
patientId,
|
|
11464
|
+
syncedCalendar,
|
|
11465
|
+
events,
|
|
11466
|
+
existingSyncId
|
|
11467
|
+
);
|
|
11468
|
+
}
|
|
11469
|
+
/**
|
|
11470
|
+
* Syncs events from our system to Google Calendar for a clinic
|
|
11471
|
+
* @param clinicId - ID of the clinic
|
|
11472
|
+
* @param calendarId - ID of the synced calendar
|
|
11473
|
+
* @param events - Events to sync
|
|
11474
|
+
* @returns Result of the sync operation
|
|
11475
|
+
*/
|
|
11476
|
+
async syncClinicEventsToGoogleCalendar(clinicId, calendarId, events) {
|
|
11477
|
+
const syncedCalendar = await this.getClinicSyncedCalendar(
|
|
11478
|
+
clinicId,
|
|
11479
|
+
calendarId
|
|
11480
|
+
);
|
|
11481
|
+
if (!syncedCalendar) {
|
|
11482
|
+
throw new Error("Synced calendar not found");
|
|
11483
|
+
}
|
|
11484
|
+
return syncEventsToGoogleCalendarUtil(
|
|
11485
|
+
this.db,
|
|
11486
|
+
"clinic",
|
|
11487
|
+
clinicId,
|
|
11488
|
+
syncedCalendar,
|
|
11489
|
+
events
|
|
11490
|
+
);
|
|
11491
|
+
}
|
|
11492
|
+
/**
|
|
11493
|
+
* Fetches events from Google Calendar for a practitioner
|
|
11494
|
+
* @param practitionerId - ID of the practitioner
|
|
11495
|
+
* @param calendarId - ID of the synced calendar
|
|
11496
|
+
* @param startDate - Start date for fetching events
|
|
11497
|
+
* @param endDate - End date for fetching events
|
|
11498
|
+
* @returns Events fetched from Google Calendar
|
|
11499
|
+
*/
|
|
11500
|
+
async fetchEventsFromPractitionerGoogleCalendar(practitionerId, calendarId, startDate, endDate) {
|
|
11501
|
+
const syncedCalendar = await this.getPractitionerSyncedCalendar(
|
|
11502
|
+
practitionerId,
|
|
11503
|
+
calendarId
|
|
11504
|
+
);
|
|
11505
|
+
if (!syncedCalendar) {
|
|
11506
|
+
throw new Error("Synced calendar not found");
|
|
11507
|
+
}
|
|
11508
|
+
return fetchEventsFromGoogleCalendarUtil(
|
|
11509
|
+
this.db,
|
|
11510
|
+
"practitioner",
|
|
11511
|
+
practitionerId,
|
|
11512
|
+
syncedCalendar,
|
|
11513
|
+
startDate,
|
|
11514
|
+
endDate
|
|
11515
|
+
);
|
|
11516
|
+
}
|
|
11517
|
+
/**
|
|
11518
|
+
* Fetches events from Google Calendar for a patient
|
|
11519
|
+
* @param patientId - ID of the patient
|
|
11520
|
+
* @param calendarId - ID of the synced calendar
|
|
11521
|
+
* @param startDate - Start date for fetching events
|
|
11522
|
+
* @param endDate - End date for fetching events
|
|
11523
|
+
* @returns Events fetched from Google Calendar
|
|
11524
|
+
*/
|
|
11525
|
+
async fetchEventsFromPatientGoogleCalendar(patientId, calendarId, startDate, endDate) {
|
|
11526
|
+
const syncedCalendar = await this.getPatientSyncedCalendar(
|
|
11527
|
+
patientId,
|
|
11528
|
+
calendarId
|
|
11529
|
+
);
|
|
11530
|
+
if (!syncedCalendar) {
|
|
11531
|
+
throw new Error("Synced calendar not found");
|
|
11532
|
+
}
|
|
11533
|
+
return fetchEventsFromGoogleCalendarUtil(
|
|
11534
|
+
this.db,
|
|
11535
|
+
"patient",
|
|
11536
|
+
patientId,
|
|
11537
|
+
syncedCalendar,
|
|
11538
|
+
startDate,
|
|
11539
|
+
endDate
|
|
11540
|
+
);
|
|
11541
|
+
}
|
|
11542
|
+
/**
|
|
11543
|
+
* Fetches events from Google Calendar for a clinic
|
|
11544
|
+
* @param clinicId - ID of the clinic
|
|
11545
|
+
* @param calendarId - ID of the synced calendar
|
|
11546
|
+
* @param startDate - Start date for fetching events
|
|
11547
|
+
* @param endDate - End date for fetching events
|
|
11548
|
+
* @returns Events fetched from Google Calendar
|
|
11549
|
+
*/
|
|
11550
|
+
async fetchEventsFromClinicGoogleCalendar(clinicId, calendarId, startDate, endDate) {
|
|
11551
|
+
const syncedCalendar = await this.getClinicSyncedCalendar(
|
|
11552
|
+
clinicId,
|
|
11553
|
+
calendarId
|
|
11554
|
+
);
|
|
11555
|
+
if (!syncedCalendar) {
|
|
11556
|
+
throw new Error("Synced calendar not found");
|
|
11557
|
+
}
|
|
11558
|
+
return fetchEventsFromGoogleCalendarUtil(
|
|
11559
|
+
this.db,
|
|
11560
|
+
"clinic",
|
|
11561
|
+
clinicId,
|
|
11562
|
+
syncedCalendar,
|
|
11563
|
+
startDate,
|
|
11564
|
+
endDate
|
|
11565
|
+
);
|
|
11566
|
+
}
|
|
11567
|
+
/**
|
|
11568
|
+
* Deletes an event from Google Calendar for a practitioner
|
|
11569
|
+
* @param practitionerId - ID of the practitioner
|
|
11570
|
+
* @param calendarId - ID of the synced calendar
|
|
11571
|
+
* @param eventId - ID of the event in Google Calendar
|
|
11572
|
+
* @returns Success status
|
|
11573
|
+
*/
|
|
11574
|
+
async deletePractitionerGoogleCalendarEvent(practitionerId, calendarId, eventId) {
|
|
11575
|
+
const syncedCalendar = await this.getPractitionerSyncedCalendar(
|
|
11576
|
+
practitionerId,
|
|
11577
|
+
calendarId
|
|
11578
|
+
);
|
|
11579
|
+
if (!syncedCalendar) {
|
|
11580
|
+
throw new Error("Synced calendar not found");
|
|
11581
|
+
}
|
|
11582
|
+
return deleteGoogleCalendarEventUtil(
|
|
11583
|
+
this.db,
|
|
11584
|
+
"practitioner",
|
|
11585
|
+
practitionerId,
|
|
11586
|
+
syncedCalendar,
|
|
11587
|
+
eventId
|
|
11588
|
+
);
|
|
11589
|
+
}
|
|
11590
|
+
/**
|
|
11591
|
+
* Deletes an event from Google Calendar for a patient
|
|
11592
|
+
* @param patientId - ID of the patient
|
|
11593
|
+
* @param calendarId - ID of the synced calendar
|
|
11594
|
+
* @param eventId - ID of the event in Google Calendar
|
|
11595
|
+
* @returns Success status
|
|
11596
|
+
*/
|
|
11597
|
+
async deletePatientGoogleCalendarEvent(patientId, calendarId, eventId) {
|
|
11598
|
+
const syncedCalendar = await this.getPatientSyncedCalendar(
|
|
11599
|
+
patientId,
|
|
11600
|
+
calendarId
|
|
11601
|
+
);
|
|
11602
|
+
if (!syncedCalendar) {
|
|
11603
|
+
throw new Error("Synced calendar not found");
|
|
11604
|
+
}
|
|
11605
|
+
return deleteGoogleCalendarEventUtil(
|
|
11606
|
+
this.db,
|
|
11607
|
+
"patient",
|
|
11608
|
+
patientId,
|
|
11609
|
+
syncedCalendar,
|
|
11610
|
+
eventId
|
|
11611
|
+
);
|
|
11612
|
+
}
|
|
11613
|
+
/**
|
|
11614
|
+
* Deletes an event from Google Calendar for a clinic
|
|
11615
|
+
* @param clinicId - ID of the clinic
|
|
11616
|
+
* @param calendarId - ID of the synced calendar
|
|
11617
|
+
* @param eventId - ID of the event in Google Calendar
|
|
11618
|
+
* @returns Success status
|
|
11619
|
+
*/
|
|
11620
|
+
async deleteClinicGoogleCalendarEvent(clinicId, calendarId, eventId) {
|
|
11621
|
+
const syncedCalendar = await this.getClinicSyncedCalendar(
|
|
11622
|
+
clinicId,
|
|
11623
|
+
calendarId
|
|
11624
|
+
);
|
|
11625
|
+
if (!syncedCalendar) {
|
|
11626
|
+
throw new Error("Synced calendar not found");
|
|
11627
|
+
}
|
|
11628
|
+
return deleteGoogleCalendarEventUtil(
|
|
11629
|
+
this.db,
|
|
11630
|
+
"clinic",
|
|
11631
|
+
clinicId,
|
|
11632
|
+
syncedCalendar,
|
|
11633
|
+
eventId
|
|
11634
|
+
);
|
|
11635
|
+
}
|
|
11636
|
+
/**
|
|
11637
|
+
* Converts Google Calendar events to our system's format for a practitioner
|
|
11638
|
+
* @param practitionerId - ID of the practitioner
|
|
11639
|
+
* @param googleEvents - Google Calendar events
|
|
11640
|
+
* @returns Converted calendar events
|
|
11641
|
+
*/
|
|
11642
|
+
convertGoogleEventsToPractitionerEvents(practitionerId, googleEvents) {
|
|
11643
|
+
return googleEvents.map(
|
|
11644
|
+
(event) => convertGoogleEventToCalendarEventUtil(
|
|
11645
|
+
event,
|
|
11646
|
+
practitionerId,
|
|
11647
|
+
"practitioner"
|
|
11648
|
+
)
|
|
11649
|
+
);
|
|
11650
|
+
}
|
|
11651
|
+
/**
|
|
11652
|
+
* Converts Google Calendar events to our system's format for a patient
|
|
11653
|
+
* @param patientId - ID of the patient
|
|
11654
|
+
* @param googleEvents - Google Calendar events
|
|
11655
|
+
* @returns Converted calendar events
|
|
11656
|
+
*/
|
|
11657
|
+
convertGoogleEventsToPatientEvents(patientId, googleEvents) {
|
|
11658
|
+
return googleEvents.map(
|
|
11659
|
+
(event) => convertGoogleEventToCalendarEventUtil(event, patientId, "patient")
|
|
11660
|
+
);
|
|
11661
|
+
}
|
|
11662
|
+
/**
|
|
11663
|
+
* Converts Google Calendar events to our system's format for a clinic
|
|
11664
|
+
* @param clinicId - ID of the clinic
|
|
11665
|
+
* @param googleEvents - Google Calendar events
|
|
11666
|
+
* @returns Converted calendar events
|
|
11667
|
+
*/
|
|
11668
|
+
convertGoogleEventsToClinicEvents(clinicId, googleEvents) {
|
|
11669
|
+
return googleEvents.map(
|
|
11670
|
+
(event) => convertGoogleEventToCalendarEventUtil(event, clinicId, "clinic")
|
|
11671
|
+
);
|
|
11672
|
+
}
|
|
11673
|
+
/**
|
|
11674
|
+
* Fetches a single event from Google Calendar for a practitioner
|
|
11675
|
+
* @param practitionerId - ID of the practitioner
|
|
11676
|
+
* @param calendarId - ID of the synced calendar
|
|
11677
|
+
* @param eventId - ID of the event in Google Calendar
|
|
11678
|
+
* @returns The event data or null if not found
|
|
11679
|
+
*/
|
|
11680
|
+
async fetchEventFromPractitionerGoogleCalendar(practitionerId, calendarId, eventId) {
|
|
11681
|
+
var _a;
|
|
11682
|
+
const syncedCalendar = await this.getPractitionerSyncedCalendar(
|
|
11683
|
+
practitionerId,
|
|
11684
|
+
calendarId
|
|
11685
|
+
);
|
|
11686
|
+
if (!syncedCalendar) {
|
|
11687
|
+
throw new Error("Synced calendar not found");
|
|
11688
|
+
}
|
|
11689
|
+
try {
|
|
11690
|
+
const { accessToken } = await refreshGoogleCalendarTokenUtil(
|
|
11691
|
+
syncedCalendar.refreshToken
|
|
11692
|
+
);
|
|
11693
|
+
const response = await makeRequest(
|
|
11694
|
+
"get",
|
|
11695
|
+
`${GOOGLE_CALENDAR_API_URL}/calendars/${syncedCalendar.calendarId}/events/${eventId}`,
|
|
11696
|
+
{ Authorization: `Bearer ${accessToken}` }
|
|
11697
|
+
);
|
|
11698
|
+
await updateLastSyncedTimestampUtil(
|
|
11699
|
+
this.db,
|
|
11700
|
+
"practitioner",
|
|
11701
|
+
practitionerId,
|
|
11702
|
+
syncedCalendar.id
|
|
11703
|
+
);
|
|
11704
|
+
return response;
|
|
11705
|
+
} catch (error) {
|
|
11706
|
+
if (((_a = error.response) == null ? void 0 : _a.status) === 404) {
|
|
11707
|
+
return null;
|
|
11708
|
+
}
|
|
11709
|
+
console.error(
|
|
11710
|
+
`Error fetching event from Google Calendar: ${error.message}`
|
|
11711
|
+
);
|
|
11712
|
+
throw error;
|
|
11713
|
+
}
|
|
11714
|
+
}
|
|
11715
|
+
};
|
|
11716
|
+
|
|
11717
|
+
// src/services/calendar/calendar.v2.service.ts
|
|
11718
|
+
var MIN_APPOINTMENT_DURATION = 15;
|
|
11719
|
+
var CalendarServiceV2 = class extends BaseService {
|
|
11720
|
+
/**
|
|
11721
|
+
* Creates a new CalendarService instance
|
|
11722
|
+
* @param db - Firestore instance
|
|
11723
|
+
* @param auth - Firebase Auth instance
|
|
11724
|
+
* @param app - Firebase App instance
|
|
11725
|
+
*/
|
|
11726
|
+
constructor(db, auth, app) {
|
|
11727
|
+
super(db, auth, app);
|
|
11728
|
+
this.syncedCalendarsService = new SyncedCalendarsService(db, auth, app);
|
|
11729
|
+
}
|
|
11730
|
+
// #region Public API Methods
|
|
11731
|
+
/**
|
|
11732
|
+
* Creates a new appointment with proper validation and scheduling rules
|
|
11733
|
+
* @param params - Appointment creation parameters
|
|
11734
|
+
* @returns Created calendar event
|
|
11735
|
+
*/
|
|
11736
|
+
async createAppointment(params) {
|
|
11737
|
+
await this.validateAppointmentParams(params);
|
|
11738
|
+
await this.validateClinicWorkingHours(params.clinicId, params.eventTime);
|
|
11739
|
+
await this.validateDoctorAvailability(
|
|
11740
|
+
params.doctorId,
|
|
11741
|
+
params.eventTime,
|
|
11742
|
+
params.clinicId
|
|
11743
|
+
);
|
|
11744
|
+
const { clinicInfo, practitionerInfo, patientInfo } = await this.fetchProfileInfoCards(
|
|
11745
|
+
params.clinicId,
|
|
11746
|
+
params.doctorId,
|
|
11747
|
+
params.patientId
|
|
11748
|
+
);
|
|
11749
|
+
const appointmentData = {
|
|
11750
|
+
clinicBranchId: params.clinicId,
|
|
11751
|
+
clinicBranchInfo: clinicInfo,
|
|
11752
|
+
practitionerProfileId: params.doctorId,
|
|
11753
|
+
practitionerProfileInfo: practitionerInfo,
|
|
11754
|
+
patientProfileId: params.patientId,
|
|
11755
|
+
patientProfileInfo: patientInfo,
|
|
11756
|
+
procedureId: params.procedureId,
|
|
11757
|
+
eventLocation: params.eventLocation,
|
|
11758
|
+
eventName: "Appointment",
|
|
11759
|
+
// TODO: Add procedure name when procedure model is available
|
|
11760
|
+
eventTime: params.eventTime,
|
|
11761
|
+
description: params.description || "",
|
|
11762
|
+
status: "pending" /* PENDING */,
|
|
11763
|
+
syncStatus: "internal" /* INTERNAL */,
|
|
11764
|
+
eventType: "appointment" /* APPOINTMENT */
|
|
11765
|
+
};
|
|
11766
|
+
const appointment = await createAppointmentUtil2(
|
|
11767
|
+
this.db,
|
|
11768
|
+
params.clinicId,
|
|
11769
|
+
params.doctorId,
|
|
11770
|
+
params.patientId,
|
|
11771
|
+
appointmentData,
|
|
11772
|
+
this.generateId.bind(this)
|
|
11773
|
+
);
|
|
11774
|
+
await this.syncAppointmentWithExternalCalendars(appointment);
|
|
11775
|
+
return appointment;
|
|
11776
|
+
}
|
|
11777
|
+
/**
|
|
11778
|
+
* Updates an existing appointment
|
|
11779
|
+
* @param params - Appointment update parameters
|
|
11780
|
+
* @returns Updated calendar event
|
|
11781
|
+
*/
|
|
11782
|
+
async updateAppointment(params) {
|
|
11783
|
+
await this.validateUpdatePermissions(params);
|
|
11784
|
+
const updateData = {
|
|
11785
|
+
eventTime: params.eventTime,
|
|
11786
|
+
description: params.description,
|
|
11787
|
+
status: params.status
|
|
11788
|
+
};
|
|
11789
|
+
const appointment = await updateAppointmentUtil2(
|
|
11790
|
+
this.db,
|
|
11791
|
+
params.clinicId,
|
|
11792
|
+
params.doctorId,
|
|
11793
|
+
params.patientId,
|
|
11794
|
+
params.appointmentId,
|
|
11795
|
+
updateData
|
|
11796
|
+
);
|
|
11797
|
+
await this.syncAppointmentWithExternalCalendars(appointment);
|
|
11798
|
+
return appointment;
|
|
11799
|
+
}
|
|
11800
|
+
/**
|
|
11801
|
+
* Gets available appointment slots for a doctor at a clinic
|
|
11802
|
+
* @param clinicId - ID of the clinic
|
|
11803
|
+
* @param doctorId - ID of the doctor
|
|
11804
|
+
* @param date - Date to check availability for
|
|
11805
|
+
* @returns Array of available time slots
|
|
11806
|
+
*/
|
|
11807
|
+
async getAvailableSlots(clinicId, doctorId, date) {
|
|
11808
|
+
const workingHours = await this.getClinicWorkingHours(clinicId, date);
|
|
11809
|
+
const doctorSchedule = await this.getDoctorSchedule(doctorId, date);
|
|
11810
|
+
const existingAppointments = await this.getDoctorAppointments(
|
|
11811
|
+
doctorId,
|
|
11812
|
+
date
|
|
11813
|
+
);
|
|
11814
|
+
return this.calculateAvailableSlots(
|
|
11815
|
+
workingHours,
|
|
11816
|
+
doctorSchedule,
|
|
11817
|
+
existingAppointments
|
|
11818
|
+
);
|
|
11819
|
+
}
|
|
11820
|
+
/**
|
|
11821
|
+
* Confirms an appointment
|
|
11822
|
+
* @param appointmentId - ID of the appointment
|
|
11823
|
+
* @param clinicId - ID of the clinic
|
|
11824
|
+
* @returns Confirmed calendar event
|
|
11825
|
+
*/
|
|
11826
|
+
async confirmAppointment(appointmentId, clinicId) {
|
|
11827
|
+
return this.updateAppointmentStatus(
|
|
11828
|
+
appointmentId,
|
|
11829
|
+
clinicId,
|
|
11830
|
+
"confirmed" /* CONFIRMED */
|
|
11831
|
+
);
|
|
11832
|
+
}
|
|
11833
|
+
/**
|
|
11834
|
+
* Rejects an appointment
|
|
11835
|
+
* @param appointmentId - ID of the appointment
|
|
11836
|
+
* @param clinicId - ID of the clinic
|
|
11837
|
+
* @returns Rejected calendar event
|
|
11838
|
+
*/
|
|
11839
|
+
async rejectAppointment(appointmentId, clinicId) {
|
|
11840
|
+
return this.updateAppointmentStatus(
|
|
11841
|
+
appointmentId,
|
|
11842
|
+
clinicId,
|
|
11843
|
+
"rejected" /* REJECTED */
|
|
11844
|
+
);
|
|
11845
|
+
}
|
|
11846
|
+
/**
|
|
11847
|
+
* Cancels an appointment
|
|
11848
|
+
* @param appointmentId - ID of the appointment
|
|
11849
|
+
* @param clinicId - ID of the clinic
|
|
11850
|
+
* @returns Canceled calendar event
|
|
11851
|
+
*/
|
|
11852
|
+
async cancelAppointment(appointmentId, clinicId) {
|
|
11853
|
+
return this.updateAppointmentStatus(
|
|
11854
|
+
appointmentId,
|
|
11855
|
+
clinicId,
|
|
11856
|
+
"canceled" /* CANCELED */
|
|
11857
|
+
);
|
|
11858
|
+
}
|
|
11859
|
+
/**
|
|
11860
|
+
* Imports events from external calendars
|
|
11861
|
+
* @param entityType - Type of entity (practitioner or patient)
|
|
11862
|
+
* @param entityId - ID of the entity
|
|
11863
|
+
* @param startDate - Start date for fetching events
|
|
11864
|
+
* @param endDate - End date for fetching events
|
|
11865
|
+
* @returns Number of events imported
|
|
11866
|
+
*/
|
|
11867
|
+
async importEventsFromExternalCalendars(entityType, entityId, startDate, endDate) {
|
|
11868
|
+
if (entityType === "patient") {
|
|
11869
|
+
return 0;
|
|
11870
|
+
}
|
|
11871
|
+
const syncedCalendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
11872
|
+
entityId
|
|
11873
|
+
);
|
|
11874
|
+
const activeCalendars = syncedCalendars.filter((cal) => cal.isActive);
|
|
11875
|
+
if (activeCalendars.length === 0) {
|
|
11876
|
+
return 0;
|
|
11877
|
+
}
|
|
11878
|
+
let importedEventsCount = 0;
|
|
11879
|
+
const currentTime = import_firestore36.Timestamp.now();
|
|
11880
|
+
for (const calendar of activeCalendars) {
|
|
11881
|
+
try {
|
|
11882
|
+
let externalEvents = [];
|
|
11883
|
+
if (calendar.provider === "google" /* GOOGLE */) {
|
|
11884
|
+
externalEvents = await this.syncedCalendarsService.fetchEventsFromPractitionerGoogleCalendar(
|
|
11885
|
+
entityId,
|
|
11886
|
+
calendar.id,
|
|
11887
|
+
startDate,
|
|
11888
|
+
endDate
|
|
11889
|
+
);
|
|
11890
|
+
}
|
|
11891
|
+
for (const externalEvent of externalEvents) {
|
|
11892
|
+
try {
|
|
11893
|
+
const convertedEvent = this.syncedCalendarsService.convertGoogleEventsToPractitionerEvents(
|
|
11894
|
+
entityId,
|
|
11895
|
+
[externalEvent]
|
|
11896
|
+
)[0];
|
|
11897
|
+
if (!convertedEvent.eventTime) {
|
|
11898
|
+
continue;
|
|
11899
|
+
}
|
|
11900
|
+
const eventData = {
|
|
11901
|
+
// Ensure all required fields are set
|
|
11902
|
+
eventName: convertedEvent.eventName || "External Event",
|
|
11903
|
+
eventTime: convertedEvent.eventTime,
|
|
11904
|
+
description: convertedEvent.description || "",
|
|
11905
|
+
status: "confirmed" /* CONFIRMED */,
|
|
11906
|
+
syncStatus: "external" /* EXTERNAL */,
|
|
11907
|
+
eventType: "blocking" /* BLOCKING */,
|
|
11908
|
+
practitionerProfileId: entityId,
|
|
11909
|
+
syncedCalendarEventId: [
|
|
11910
|
+
{
|
|
11911
|
+
eventId: externalEvent.id,
|
|
11912
|
+
syncedCalendarProvider: calendar.provider,
|
|
11913
|
+
syncedAt: currentTime
|
|
11914
|
+
}
|
|
11915
|
+
]
|
|
11916
|
+
};
|
|
11917
|
+
const doctorEvent = await this.createDoctorBlockingEvent(
|
|
11918
|
+
entityId,
|
|
11919
|
+
eventData
|
|
11920
|
+
);
|
|
11921
|
+
if (doctorEvent) {
|
|
11922
|
+
importedEventsCount++;
|
|
11923
|
+
}
|
|
11924
|
+
} catch (eventError) {
|
|
11925
|
+
console.error("Error importing event:", eventError);
|
|
11926
|
+
}
|
|
11927
|
+
}
|
|
11928
|
+
} catch (calendarError) {
|
|
11929
|
+
console.error(
|
|
11930
|
+
`Error fetching events from calendar ${calendar.id}:`,
|
|
11931
|
+
calendarError
|
|
11932
|
+
);
|
|
11933
|
+
}
|
|
11934
|
+
}
|
|
11935
|
+
return importedEventsCount;
|
|
11936
|
+
}
|
|
11937
|
+
/**
|
|
11938
|
+
* Creates a blocking event in a doctor's calendar
|
|
11939
|
+
* @param doctorId - ID of the doctor
|
|
11940
|
+
* @param eventData - Calendar event data
|
|
11941
|
+
* @returns Created calendar event
|
|
11942
|
+
*/
|
|
11943
|
+
async createDoctorBlockingEvent(doctorId, eventData) {
|
|
11944
|
+
try {
|
|
11945
|
+
const eventId = this.generateId();
|
|
11946
|
+
const eventRef = (0, import_firestore37.doc)(
|
|
11947
|
+
this.db,
|
|
11948
|
+
PRACTITIONERS_COLLECTION,
|
|
11949
|
+
doctorId,
|
|
11950
|
+
CALENDAR_COLLECTION,
|
|
11951
|
+
eventId
|
|
11952
|
+
);
|
|
11953
|
+
const newEvent = {
|
|
11954
|
+
id: eventId,
|
|
11955
|
+
...eventData,
|
|
11956
|
+
createdAt: (0, import_firestore36.serverTimestamp)(),
|
|
11957
|
+
updatedAt: (0, import_firestore36.serverTimestamp)()
|
|
11958
|
+
};
|
|
11959
|
+
await (0, import_firestore37.setDoc)(eventRef, newEvent);
|
|
11960
|
+
return {
|
|
11961
|
+
...newEvent,
|
|
11962
|
+
createdAt: import_firestore36.Timestamp.now(),
|
|
11963
|
+
updatedAt: import_firestore36.Timestamp.now()
|
|
11964
|
+
};
|
|
11965
|
+
} catch (error) {
|
|
11966
|
+
console.error(
|
|
11967
|
+
`Error creating blocking event for doctor ${doctorId}:`,
|
|
11968
|
+
error
|
|
11969
|
+
);
|
|
11970
|
+
return null;
|
|
11971
|
+
}
|
|
11972
|
+
}
|
|
11973
|
+
/**
|
|
11974
|
+
* Periodically syncs events from external calendars for doctors
|
|
11975
|
+
* This would be called via a scheduled Cloud Function
|
|
11976
|
+
* @param lookbackDays - Number of days to look back for events
|
|
11977
|
+
* @param lookforwardDays - Number of days to look forward for events
|
|
11978
|
+
*/
|
|
11979
|
+
async synchronizeExternalCalendars(lookbackDays = 7, lookforwardDays = 30) {
|
|
11980
|
+
try {
|
|
11981
|
+
const practitionersRef = (0, import_firestore37.collection)(this.db, PRACTITIONERS_COLLECTION);
|
|
11982
|
+
const practitionersSnapshot = await (0, import_firestore37.getDocs)(practitionersRef);
|
|
11983
|
+
const startDate = /* @__PURE__ */ new Date();
|
|
11984
|
+
startDate.setDate(startDate.getDate() - lookbackDays);
|
|
11985
|
+
const endDate = /* @__PURE__ */ new Date();
|
|
11986
|
+
endDate.setDate(endDate.getDate() + lookforwardDays);
|
|
11987
|
+
const syncPromises = [];
|
|
11988
|
+
for (const docSnapshot of practitionersSnapshot.docs) {
|
|
11989
|
+
const practitionerId = docSnapshot.id;
|
|
11990
|
+
syncPromises.push(
|
|
11991
|
+
this.importEventsFromExternalCalendars(
|
|
11992
|
+
"doctor",
|
|
11993
|
+
practitionerId,
|
|
11994
|
+
startDate,
|
|
11995
|
+
endDate
|
|
11996
|
+
).then((count) => {
|
|
11997
|
+
console.log(
|
|
11998
|
+
`Imported ${count} events for doctor ${practitionerId}`
|
|
11999
|
+
);
|
|
12000
|
+
}).catch((error) => {
|
|
12001
|
+
console.error(
|
|
12002
|
+
`Error importing events for doctor ${practitionerId}:`,
|
|
12003
|
+
error
|
|
12004
|
+
);
|
|
12005
|
+
})
|
|
12006
|
+
);
|
|
12007
|
+
syncPromises.push(
|
|
12008
|
+
this.updateExistingEventsFromExternalCalendars(
|
|
12009
|
+
practitionerId,
|
|
12010
|
+
startDate,
|
|
12011
|
+
endDate
|
|
12012
|
+
).then((count) => {
|
|
12013
|
+
console.log(
|
|
12014
|
+
`Updated ${count} events for doctor ${practitionerId}`
|
|
12015
|
+
);
|
|
12016
|
+
}).catch((error) => {
|
|
12017
|
+
console.error(
|
|
12018
|
+
`Error updating events for doctor ${practitionerId}:`,
|
|
12019
|
+
error
|
|
12020
|
+
);
|
|
12021
|
+
})
|
|
12022
|
+
);
|
|
12023
|
+
}
|
|
12024
|
+
await Promise.all(syncPromises);
|
|
12025
|
+
console.log("Completed external calendar synchronization");
|
|
12026
|
+
} catch (error) {
|
|
12027
|
+
console.error("Error synchronizing external calendars:", error);
|
|
12028
|
+
}
|
|
12029
|
+
}
|
|
12030
|
+
/**
|
|
12031
|
+
* Updates existing events that were synced from external calendars
|
|
12032
|
+
* @param doctorId - ID of the doctor
|
|
12033
|
+
* @param startDate - Start date for fetching events
|
|
12034
|
+
* @param endDate - End date for fetching events
|
|
12035
|
+
* @returns Number of events updated
|
|
12036
|
+
*/
|
|
12037
|
+
async updateExistingEventsFromExternalCalendars(doctorId, startDate, endDate) {
|
|
12038
|
+
var _a;
|
|
12039
|
+
try {
|
|
12040
|
+
const eventsRef = (0, import_firestore37.collection)(
|
|
12041
|
+
this.db,
|
|
12042
|
+
PRACTITIONERS_COLLECTION,
|
|
12043
|
+
doctorId,
|
|
12044
|
+
CALENDAR_COLLECTION
|
|
12045
|
+
);
|
|
12046
|
+
const q = (0, import_firestore37.query)(
|
|
12047
|
+
eventsRef,
|
|
12048
|
+
(0, import_firestore37.where)("syncStatus", "==", "external" /* EXTERNAL */),
|
|
12049
|
+
(0, import_firestore37.where)("eventTime.start", ">=", import_firestore36.Timestamp.fromDate(startDate)),
|
|
12050
|
+
(0, import_firestore37.where)("eventTime.start", "<=", import_firestore36.Timestamp.fromDate(endDate))
|
|
12051
|
+
);
|
|
12052
|
+
const eventsSnapshot = await (0, import_firestore37.getDocs)(q);
|
|
12053
|
+
const events = eventsSnapshot.docs.map((doc32) => ({
|
|
12054
|
+
id: doc32.id,
|
|
12055
|
+
...doc32.data()
|
|
12056
|
+
}));
|
|
12057
|
+
const calendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
12058
|
+
doctorId
|
|
12059
|
+
);
|
|
12060
|
+
const activeCalendars = calendars.filter((cal) => cal.isActive);
|
|
12061
|
+
if (activeCalendars.length === 0 || events.length === 0) {
|
|
12062
|
+
return 0;
|
|
12063
|
+
}
|
|
12064
|
+
let updatedCount = 0;
|
|
12065
|
+
for (const event of events) {
|
|
12066
|
+
if (!((_a = event.syncedCalendarEventId) == null ? void 0 : _a.length)) continue;
|
|
12067
|
+
for (const syncId of event.syncedCalendarEventId) {
|
|
12068
|
+
const calendar = activeCalendars.find(
|
|
12069
|
+
(cal) => cal.provider === syncId.syncedCalendarProvider
|
|
12070
|
+
);
|
|
12071
|
+
if (!calendar) continue;
|
|
12072
|
+
if (syncId.syncedCalendarProvider === "google" /* GOOGLE */) {
|
|
12073
|
+
try {
|
|
12074
|
+
const externalEvent = await this.fetchExternalEvent(
|
|
12075
|
+
doctorId,
|
|
12076
|
+
calendar,
|
|
12077
|
+
syncId.eventId
|
|
12078
|
+
);
|
|
12079
|
+
if (externalEvent) {
|
|
12080
|
+
const externalStartTime = new Date(
|
|
12081
|
+
externalEvent.start.dateTime || externalEvent.start.date
|
|
12082
|
+
).getTime();
|
|
12083
|
+
const externalEndTime = new Date(
|
|
12084
|
+
externalEvent.end.dateTime || externalEvent.end.date
|
|
12085
|
+
).getTime();
|
|
12086
|
+
const localStartTime = event.eventTime.start.toDate().getTime();
|
|
12087
|
+
const localEndTime = event.eventTime.end.toDate().getTime();
|
|
12088
|
+
if (externalStartTime !== localStartTime || externalEndTime !== localEndTime || externalEvent.summary !== event.eventName || externalEvent.description !== event.description) {
|
|
12089
|
+
await this.updateLocalEventFromExternal(
|
|
12090
|
+
doctorId,
|
|
12091
|
+
event.id,
|
|
12092
|
+
externalEvent
|
|
12093
|
+
);
|
|
12094
|
+
updatedCount++;
|
|
12095
|
+
}
|
|
12096
|
+
} else {
|
|
12097
|
+
await this.updateEventStatus(
|
|
12098
|
+
doctorId,
|
|
12099
|
+
event.id,
|
|
12100
|
+
"canceled" /* CANCELED */
|
|
12101
|
+
);
|
|
12102
|
+
updatedCount++;
|
|
12103
|
+
}
|
|
12104
|
+
} catch (error) {
|
|
12105
|
+
console.error(
|
|
12106
|
+
`Error updating external event ${event.id}:`,
|
|
12107
|
+
error
|
|
12108
|
+
);
|
|
12109
|
+
}
|
|
12110
|
+
}
|
|
12111
|
+
}
|
|
12112
|
+
}
|
|
12113
|
+
return updatedCount;
|
|
12114
|
+
} catch (error) {
|
|
12115
|
+
console.error(
|
|
12116
|
+
"Error updating existing events from external calendars:",
|
|
12117
|
+
error
|
|
12118
|
+
);
|
|
12119
|
+
return 0;
|
|
12120
|
+
}
|
|
12121
|
+
}
|
|
12122
|
+
/**
|
|
12123
|
+
* Fetches a single external event from Google Calendar
|
|
12124
|
+
* @param doctorId - ID of the doctor
|
|
12125
|
+
* @param calendar - Calendar information
|
|
12126
|
+
* @param externalEventId - ID of the external event
|
|
12127
|
+
* @returns External event data or null if not found
|
|
12128
|
+
*/
|
|
12129
|
+
async fetchExternalEvent(doctorId, calendar, externalEventId) {
|
|
12130
|
+
try {
|
|
12131
|
+
if (calendar.provider === "google" /* GOOGLE */) {
|
|
12132
|
+
const result = await this.syncedCalendarsService.fetchEventFromPractitionerGoogleCalendar(
|
|
12133
|
+
doctorId,
|
|
12134
|
+
calendar.id,
|
|
12135
|
+
externalEventId
|
|
12136
|
+
);
|
|
12137
|
+
return result;
|
|
12138
|
+
}
|
|
12139
|
+
return null;
|
|
12140
|
+
} catch (error) {
|
|
12141
|
+
console.error(`Error fetching external event ${externalEventId}:`, error);
|
|
12142
|
+
return null;
|
|
12143
|
+
}
|
|
12144
|
+
}
|
|
12145
|
+
/**
|
|
12146
|
+
* Updates a local event with data from an external event
|
|
12147
|
+
* @param doctorId - ID of the doctor
|
|
12148
|
+
* @param eventId - ID of the local event
|
|
12149
|
+
* @param externalEvent - External event data
|
|
12150
|
+
*/
|
|
12151
|
+
async updateLocalEventFromExternal(doctorId, eventId, externalEvent) {
|
|
12152
|
+
try {
|
|
12153
|
+
const startTime = new Date(
|
|
12154
|
+
externalEvent.start.dateTime || externalEvent.start.date
|
|
12155
|
+
);
|
|
12156
|
+
const endTime = new Date(
|
|
12157
|
+
externalEvent.end.dateTime || externalEvent.end.date
|
|
12158
|
+
);
|
|
12159
|
+
const eventRef = (0, import_firestore37.doc)(
|
|
12160
|
+
this.db,
|
|
12161
|
+
PRACTITIONERS_COLLECTION,
|
|
12162
|
+
doctorId,
|
|
12163
|
+
CALENDAR_COLLECTION,
|
|
12164
|
+
eventId
|
|
12165
|
+
);
|
|
12166
|
+
await (0, import_firestore37.updateDoc)(eventRef, {
|
|
12167
|
+
eventName: externalEvent.summary || "External Event",
|
|
12168
|
+
eventTime: {
|
|
12169
|
+
start: import_firestore36.Timestamp.fromDate(startTime),
|
|
12170
|
+
end: import_firestore36.Timestamp.fromDate(endTime)
|
|
12171
|
+
},
|
|
12172
|
+
description: externalEvent.description || "",
|
|
12173
|
+
updatedAt: (0, import_firestore36.serverTimestamp)()
|
|
12174
|
+
});
|
|
12175
|
+
console.log(`Updated local event ${eventId} from external event`);
|
|
12176
|
+
} catch (error) {
|
|
12177
|
+
console.error(
|
|
12178
|
+
`Error updating local event ${eventId} from external:`,
|
|
12179
|
+
error
|
|
12180
|
+
);
|
|
12181
|
+
}
|
|
12182
|
+
}
|
|
12183
|
+
/**
|
|
12184
|
+
* Updates an event's status
|
|
12185
|
+
* @param doctorId - ID of the doctor
|
|
12186
|
+
* @param eventId - ID of the event
|
|
12187
|
+
* @param status - New status
|
|
12188
|
+
*/
|
|
12189
|
+
async updateEventStatus(doctorId, eventId, status) {
|
|
12190
|
+
try {
|
|
12191
|
+
const eventRef = (0, import_firestore37.doc)(
|
|
12192
|
+
this.db,
|
|
12193
|
+
PRACTITIONERS_COLLECTION,
|
|
12194
|
+
doctorId,
|
|
12195
|
+
CALENDAR_COLLECTION,
|
|
12196
|
+
eventId
|
|
12197
|
+
);
|
|
12198
|
+
await (0, import_firestore37.updateDoc)(eventRef, {
|
|
12199
|
+
status,
|
|
12200
|
+
updatedAt: (0, import_firestore36.serverTimestamp)()
|
|
12201
|
+
});
|
|
12202
|
+
console.log(`Updated event ${eventId} status to ${status}`);
|
|
12203
|
+
} catch (error) {
|
|
12204
|
+
console.error(`Error updating event ${eventId} status:`, error);
|
|
12205
|
+
}
|
|
12206
|
+
}
|
|
12207
|
+
/**
|
|
12208
|
+
* Creates a scheduled job to periodically sync external calendars
|
|
12209
|
+
* Note: This would be implemented using Cloud Functions in a real application
|
|
12210
|
+
* This is a sample implementation to show how it could be set up
|
|
12211
|
+
* @param interval - Interval in hours
|
|
12212
|
+
*/
|
|
12213
|
+
createScheduledSyncJob(interval = 3) {
|
|
12214
|
+
console.log(
|
|
12215
|
+
`Setting up scheduled calendar sync job every ${interval} hours`
|
|
12216
|
+
);
|
|
12217
|
+
}
|
|
12218
|
+
/**
|
|
12219
|
+
* Searches for calendar events based on specified criteria.
|
|
12220
|
+
*
|
|
12221
|
+
* @param {SearchCalendarEventsParams} params - The search parameters.
|
|
12222
|
+
* @param {SearchLocationEnum} params.searchLocation - The primary location to search (practitioner, patient, or clinic).
|
|
12223
|
+
* @param {string} params.entityId - The ID of the entity (practitioner, patient, or clinic) to search within/for.
|
|
12224
|
+
* @param {string} [params.clinicId] - Optional clinic ID to filter by.
|
|
12225
|
+
* @param {string} [params.practitionerId] - Optional practitioner ID to filter by.
|
|
12226
|
+
* @param {string} [params.patientId] - Optional patient ID to filter by.
|
|
12227
|
+
* @param {string} [params.procedureId] - Optional procedure ID to filter by.
|
|
12228
|
+
* @param {DateRange} [params.dateRange] - Optional date range to filter by (event start time).
|
|
12229
|
+
* @param {CalendarEventStatus} [params.eventStatus] - Optional event status to filter by.
|
|
12230
|
+
* @param {CalendarEventType} [params.eventType] - Optional event type to filter by.
|
|
12231
|
+
* @returns {Promise<CalendarEvent[]>} A promise that resolves to an array of matching calendar events.
|
|
12232
|
+
* @throws {Error} If the search location requires an entity ID that is not provided.
|
|
12233
|
+
*/
|
|
12234
|
+
async searchCalendarEvents(params) {
|
|
12235
|
+
return searchCalendarEventsUtil(this.db, params);
|
|
12236
|
+
}
|
|
12237
|
+
/**
|
|
12238
|
+
* Gets a doctor's upcoming appointments for a specific date range
|
|
12239
|
+
*
|
|
12240
|
+
* @param {string} doctorId - ID of the practitioner
|
|
12241
|
+
* @param {Date} startDate - Start date of the range
|
|
12242
|
+
* @param {Date} endDate - End date of the range
|
|
12243
|
+
* @param {CalendarEventStatus} [status] - Optional status filter (defaults to CONFIRMED)
|
|
12244
|
+
* @returns {Promise<CalendarEvent[]>} A promise that resolves to an array of appointments
|
|
12245
|
+
*/
|
|
12246
|
+
async getPractitionerUpcomingAppointments(doctorId, startDate, endDate, status = "confirmed" /* CONFIRMED */) {
|
|
12247
|
+
const dateRange = {
|
|
12248
|
+
start: import_firestore36.Timestamp.fromDate(startDate),
|
|
12249
|
+
end: import_firestore36.Timestamp.fromDate(endDate)
|
|
12250
|
+
};
|
|
12251
|
+
const searchParams = {
|
|
12252
|
+
searchLocation: "practitioner" /* PRACTITIONER */,
|
|
12253
|
+
entityId: doctorId,
|
|
12254
|
+
dateRange,
|
|
12255
|
+
eventStatus: status,
|
|
12256
|
+
eventType: "appointment" /* APPOINTMENT */
|
|
12257
|
+
};
|
|
12258
|
+
return this.searchCalendarEvents(searchParams);
|
|
12259
|
+
}
|
|
12260
|
+
/**
|
|
12261
|
+
* Gets a patient's appointments for a specific date range
|
|
12262
|
+
*
|
|
12263
|
+
* @param {string} patientId - ID of the patient
|
|
12264
|
+
* @param {Date} startDate - Start date of the range
|
|
12265
|
+
* @param {Date} endDate - End date of the range
|
|
12266
|
+
* @param {CalendarEventStatus} [status] - Optional status filter (defaults to all non-canceled appointments)
|
|
12267
|
+
* @returns {Promise<CalendarEvent[]>} A promise that resolves to an array of appointments
|
|
12268
|
+
*/
|
|
12269
|
+
async getPatientAppointments(patientId, startDate, endDate, status) {
|
|
12270
|
+
const dateRange = {
|
|
12271
|
+
start: import_firestore36.Timestamp.fromDate(startDate),
|
|
12272
|
+
end: import_firestore36.Timestamp.fromDate(endDate)
|
|
12273
|
+
};
|
|
12274
|
+
const searchParams = {
|
|
12275
|
+
searchLocation: "patient" /* PATIENT */,
|
|
12276
|
+
entityId: patientId,
|
|
12277
|
+
dateRange,
|
|
12278
|
+
eventType: "appointment" /* APPOINTMENT */
|
|
12279
|
+
};
|
|
12280
|
+
if (status) {
|
|
12281
|
+
searchParams.eventStatus = status;
|
|
12282
|
+
}
|
|
12283
|
+
return this.searchCalendarEvents(searchParams);
|
|
12284
|
+
}
|
|
12285
|
+
/**
|
|
12286
|
+
* Gets all appointments for a clinic within a specific date range
|
|
12287
|
+
*
|
|
12288
|
+
* @param {string} clinicId - ID of the clinic
|
|
12289
|
+
* @param {Date} startDate - Start date of the range
|
|
12290
|
+
* @param {Date} endDate - End date of the range
|
|
12291
|
+
* @param {string} [doctorId] - Optional doctor ID to filter by
|
|
12292
|
+
* @param {CalendarEventStatus} [status] - Optional status filter
|
|
12293
|
+
* @returns {Promise<CalendarEvent[]>} A promise that resolves to an array of appointments
|
|
12294
|
+
*/
|
|
12295
|
+
async getClinicAppointments(clinicId, startDate, endDate, doctorId, status) {
|
|
12296
|
+
const dateRange = {
|
|
12297
|
+
start: import_firestore36.Timestamp.fromDate(startDate),
|
|
12298
|
+
end: import_firestore36.Timestamp.fromDate(endDate)
|
|
12299
|
+
};
|
|
12300
|
+
const searchParams = {
|
|
12301
|
+
searchLocation: "clinic" /* CLINIC */,
|
|
12302
|
+
entityId: clinicId,
|
|
12303
|
+
dateRange,
|
|
12304
|
+
eventType: "appointment" /* APPOINTMENT */
|
|
12305
|
+
};
|
|
12306
|
+
if (doctorId) {
|
|
12307
|
+
searchParams.practitionerId = doctorId;
|
|
12308
|
+
}
|
|
12309
|
+
if (status) {
|
|
12310
|
+
searchParams.eventStatus = status;
|
|
12311
|
+
}
|
|
12312
|
+
return this.searchCalendarEvents(searchParams);
|
|
12313
|
+
}
|
|
12314
|
+
// #endregion
|
|
12315
|
+
// #region Private Helper Methods
|
|
12316
|
+
/**
|
|
12317
|
+
* Validates appointment creation parameters
|
|
12318
|
+
* @param params - Appointment parameters to validate
|
|
12319
|
+
* @throws Error if validation fails
|
|
12320
|
+
*/
|
|
12321
|
+
async validateAppointmentParams(params) {
|
|
12322
|
+
await createAppointmentSchema.parseAsync(params);
|
|
12323
|
+
}
|
|
12324
|
+
/**
|
|
12325
|
+
* Validates if the event time falls within clinic working hours
|
|
12326
|
+
* @param clinicId - ID of the clinic
|
|
12327
|
+
* @param eventTime - Event time to validate
|
|
12328
|
+
* @throws Error if validation fails
|
|
12329
|
+
*/
|
|
12330
|
+
async validateClinicWorkingHours(clinicId, eventTime) {
|
|
12331
|
+
const startDate = eventTime.start.toDate();
|
|
12332
|
+
const workingHours = await this.getClinicWorkingHours(clinicId, startDate);
|
|
12333
|
+
if (workingHours.length === 0) {
|
|
12334
|
+
throw new Error("Clinic is not open on this day");
|
|
12335
|
+
}
|
|
12336
|
+
const startTime = startDate;
|
|
12337
|
+
const endTime = eventTime.end.toDate();
|
|
12338
|
+
const isWithinWorkingHours = workingHours.some((slot) => {
|
|
12339
|
+
return slot.start <= startTime && slot.end >= endTime && slot.isAvailable;
|
|
12340
|
+
});
|
|
12341
|
+
if (!isWithinWorkingHours) {
|
|
12342
|
+
throw new Error("Appointment time is outside clinic working hours");
|
|
12343
|
+
}
|
|
12344
|
+
}
|
|
12345
|
+
/**
|
|
12346
|
+
* Validates if the doctor is available during the event time
|
|
12347
|
+
* @param doctorId - ID of the doctor
|
|
12348
|
+
* @param eventTime - Event time to validate
|
|
12349
|
+
* @param clinicId - ID of the clinic where the appointment is being booked
|
|
12350
|
+
* @throws Error if validation fails
|
|
12351
|
+
*/
|
|
12352
|
+
async validateDoctorAvailability(doctorId, eventTime, clinicId) {
|
|
12353
|
+
var _a;
|
|
12354
|
+
const startDate = eventTime.start.toDate();
|
|
12355
|
+
const startTime = startDate;
|
|
12356
|
+
const endTime = eventTime.end.toDate();
|
|
12357
|
+
const practitionerRef = (0, import_firestore37.doc)(this.db, PRACTITIONERS_COLLECTION, doctorId);
|
|
12358
|
+
const practitionerDoc = await (0, import_firestore37.getDoc)(practitionerRef);
|
|
12359
|
+
if (!practitionerDoc.exists()) {
|
|
12360
|
+
throw new Error(`Doctor with ID ${doctorId} not found`);
|
|
12361
|
+
}
|
|
12362
|
+
const practitioner = practitionerDoc.data();
|
|
12363
|
+
if (!practitioner.clinics.includes(clinicId)) {
|
|
12364
|
+
throw new Error("Doctor does not work at this clinic");
|
|
12365
|
+
}
|
|
12366
|
+
const clinicWorkingHours = (_a = practitioner.clinicWorkingHours) == null ? void 0 : _a.find(
|
|
12367
|
+
(hours) => hours.clinicId === clinicId && hours.isActive
|
|
12368
|
+
);
|
|
12369
|
+
if (!clinicWorkingHours) {
|
|
12370
|
+
throw new Error("Doctor does not have working hours set for this clinic");
|
|
12371
|
+
}
|
|
12372
|
+
const dayOfWeek = startDate.getDay();
|
|
12373
|
+
const dayKey = [
|
|
12374
|
+
"sunday",
|
|
12375
|
+
"monday",
|
|
12376
|
+
"tuesday",
|
|
12377
|
+
"wednesday",
|
|
12378
|
+
"thursday",
|
|
12379
|
+
"friday",
|
|
12380
|
+
"saturday"
|
|
12381
|
+
][dayOfWeek];
|
|
12382
|
+
const daySchedule = clinicWorkingHours.workingHours[dayKey];
|
|
12383
|
+
if (!daySchedule) {
|
|
12384
|
+
throw new Error("Doctor is not working on this day at this clinic");
|
|
12385
|
+
}
|
|
12386
|
+
const [startHour, startMinute] = daySchedule.start.split(":").map(Number);
|
|
12387
|
+
const [endHour, endMinute] = daySchedule.end.split(":").map(Number);
|
|
12388
|
+
const scheduleStart = new Date(startDate);
|
|
12389
|
+
scheduleStart.setHours(startHour, startMinute, 0, 0);
|
|
12390
|
+
const scheduleEnd = new Date(startDate);
|
|
12391
|
+
scheduleEnd.setHours(endHour, endMinute, 0, 0);
|
|
12392
|
+
if (startTime < scheduleStart || endTime > scheduleEnd) {
|
|
12393
|
+
throw new Error(
|
|
12394
|
+
"Appointment time is outside doctor's working hours at this clinic"
|
|
12395
|
+
);
|
|
12396
|
+
}
|
|
12397
|
+
const appointments = await this.getDoctorAppointments(doctorId, startDate);
|
|
12398
|
+
const hasOverlap = appointments.some((appointment) => {
|
|
12399
|
+
const appointmentStart = appointment.eventTime.start.toDate();
|
|
12400
|
+
const appointmentEnd = appointment.eventTime.end.toDate();
|
|
12401
|
+
return startTime >= appointmentStart && startTime < appointmentEnd || endTime > appointmentStart && endTime <= appointmentEnd || startTime <= appointmentStart && endTime >= appointmentEnd;
|
|
12402
|
+
});
|
|
12403
|
+
if (hasOverlap) {
|
|
12404
|
+
throw new Error("Doctor has another appointment during this time");
|
|
12405
|
+
}
|
|
12406
|
+
}
|
|
12407
|
+
/**
|
|
12408
|
+
* Updates appointment status
|
|
12409
|
+
* @param appointmentId - ID of the appointment
|
|
12410
|
+
* @param clinicId - ID of the clinic
|
|
12411
|
+
* @param status - New status
|
|
12412
|
+
* @returns Updated calendar event
|
|
12413
|
+
*/
|
|
12414
|
+
async updateAppointmentStatus(appointmentId, clinicId, status) {
|
|
12415
|
+
const baseCollectionPath = `${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}`;
|
|
12416
|
+
const appointmentRef = (0, import_firestore37.doc)(this.db, baseCollectionPath, appointmentId);
|
|
12417
|
+
const appointmentDoc = await (0, import_firestore37.getDoc)(appointmentRef);
|
|
12418
|
+
if (!appointmentDoc.exists()) {
|
|
12419
|
+
throw new Error(`Appointment with ID ${appointmentId} not found`);
|
|
12420
|
+
}
|
|
12421
|
+
const appointment = appointmentDoc.data();
|
|
12422
|
+
if (appointment.clinicBranchId !== clinicId) {
|
|
12423
|
+
throw new Error("Appointment does not belong to the specified clinic");
|
|
12424
|
+
}
|
|
12425
|
+
this.validateStatusTransition(appointment.status, status);
|
|
12426
|
+
const updateParams = {
|
|
12427
|
+
appointmentId,
|
|
12428
|
+
clinicId,
|
|
12429
|
+
eventTime: appointment.eventTime,
|
|
12430
|
+
description: appointment.description || "",
|
|
12431
|
+
doctorId: appointment.practitionerProfileId || "",
|
|
12432
|
+
patientId: appointment.patientProfileId || "",
|
|
12433
|
+
status
|
|
12434
|
+
};
|
|
12435
|
+
await this.validateUpdatePermissions(updateParams);
|
|
12436
|
+
return this.updateAppointment(updateParams);
|
|
12437
|
+
}
|
|
12438
|
+
/**
|
|
12439
|
+
* Validates status transition
|
|
12440
|
+
* @param currentStatus - Current status
|
|
12441
|
+
* @param newStatus - New status
|
|
12442
|
+
* @throws Error if transition is invalid
|
|
12443
|
+
*/
|
|
12444
|
+
validateStatusTransition(currentStatus, newStatus) {
|
|
12445
|
+
const validTransitions = {
|
|
12446
|
+
["pending" /* PENDING */]: [
|
|
12447
|
+
"confirmed" /* CONFIRMED */,
|
|
12448
|
+
"rejected" /* REJECTED */,
|
|
12449
|
+
"canceled" /* CANCELED */
|
|
12450
|
+
],
|
|
12451
|
+
["confirmed" /* CONFIRMED */]: [
|
|
12452
|
+
"canceled" /* CANCELED */,
|
|
12453
|
+
"completed" /* COMPLETED */,
|
|
12454
|
+
"rescheduled" /* RESCHEDULED */,
|
|
12455
|
+
"no_show" /* NO_SHOW */
|
|
12456
|
+
],
|
|
12457
|
+
["rejected" /* REJECTED */]: [],
|
|
12458
|
+
["canceled" /* CANCELED */]: [],
|
|
12459
|
+
["rescheduled" /* RESCHEDULED */]: [
|
|
12460
|
+
"confirmed" /* CONFIRMED */,
|
|
12461
|
+
"canceled" /* CANCELED */
|
|
12462
|
+
],
|
|
12463
|
+
["completed" /* COMPLETED */]: [],
|
|
12464
|
+
["no_show" /* NO_SHOW */]: []
|
|
12465
|
+
};
|
|
12466
|
+
if (!validTransitions[currentStatus].includes(newStatus)) {
|
|
12467
|
+
throw new Error(
|
|
12468
|
+
`Invalid status transition from ${currentStatus} to ${newStatus}`
|
|
12469
|
+
);
|
|
12470
|
+
}
|
|
12471
|
+
}
|
|
12472
|
+
/**
|
|
12473
|
+
* Syncs appointment with external calendars based on entity type and status
|
|
12474
|
+
* @param appointment - Calendar event to sync
|
|
12475
|
+
*/
|
|
12476
|
+
async syncAppointmentWithExternalCalendars(appointment) {
|
|
12477
|
+
if (!appointment.practitionerProfileId || !appointment.patientProfileId) {
|
|
12478
|
+
return;
|
|
12479
|
+
}
|
|
12480
|
+
try {
|
|
12481
|
+
const [doctorCalendars, patientCalendars] = await Promise.all([
|
|
12482
|
+
this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
12483
|
+
appointment.practitionerProfileId
|
|
12484
|
+
),
|
|
12485
|
+
this.syncedCalendarsService.getPatientSyncedCalendars(
|
|
12486
|
+
appointment.patientProfileId
|
|
12487
|
+
)
|
|
12488
|
+
]);
|
|
12489
|
+
const activeDoctorCalendars = doctorCalendars.filter(
|
|
12490
|
+
(cal) => cal.isActive
|
|
12491
|
+
);
|
|
12492
|
+
const activePatientCalendars = patientCalendars.filter(
|
|
12493
|
+
(cal) => cal.isActive
|
|
12494
|
+
);
|
|
12495
|
+
if (activeDoctorCalendars.length === 0 && activePatientCalendars.length === 0) {
|
|
12496
|
+
return;
|
|
12497
|
+
}
|
|
12498
|
+
if (appointment.syncStatus !== "internal" /* INTERNAL */) {
|
|
12499
|
+
return;
|
|
10300
12500
|
}
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
"Patient ID (entityId) is required when searching patient calendar."
|
|
12501
|
+
if (appointment.status === "confirmed" /* CONFIRMED */ && activeDoctorCalendars.length > 0) {
|
|
12502
|
+
await Promise.all(
|
|
12503
|
+
activeDoctorCalendars.map(
|
|
12504
|
+
(calendar) => this.syncEventToExternalCalendar(appointment, calendar, "doctor")
|
|
12505
|
+
)
|
|
10307
12506
|
);
|
|
10308
12507
|
}
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
12508
|
+
if (appointment.status !== "canceled" /* CANCELED */ && appointment.status !== "rejected" /* REJECTED */ && activePatientCalendars.length > 0) {
|
|
12509
|
+
await Promise.all(
|
|
12510
|
+
activePatientCalendars.map(
|
|
12511
|
+
(calendar) => this.syncEventToExternalCalendar(appointment, calendar, "patient")
|
|
12512
|
+
)
|
|
10313
12513
|
);
|
|
10314
|
-
return [];
|
|
10315
12514
|
}
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
12515
|
+
} catch (error) {
|
|
12516
|
+
console.error("Error syncing with external calendars:", error);
|
|
12517
|
+
}
|
|
12518
|
+
}
|
|
12519
|
+
/**
|
|
12520
|
+
* Syncs a single event to an external calendar
|
|
12521
|
+
* @param appointment - Calendar event to sync
|
|
12522
|
+
* @param calendar - External calendar to sync with
|
|
12523
|
+
* @param entityType - Type of entity owning the calendar
|
|
12524
|
+
*/
|
|
12525
|
+
async syncEventToExternalCalendar(appointment, calendar, entityType) {
|
|
12526
|
+
var _a, _b, _c, _d, _e;
|
|
12527
|
+
try {
|
|
12528
|
+
const eventToSync = { ...appointment };
|
|
12529
|
+
let eventTitle = appointment.eventName;
|
|
12530
|
+
const clinicName = ((_a = appointment.clinicBranchInfo) == null ? void 0 : _a.name) || "Clinic";
|
|
12531
|
+
if (entityType === "patient") {
|
|
12532
|
+
eventTitle = `[${appointment.status}] ${eventTitle} @ ${clinicName}`;
|
|
12533
|
+
} else {
|
|
12534
|
+
eventTitle = `${eventTitle} - Patient: ${((_b = appointment.patientProfileInfo) == null ? void 0 : _b.fullName) || "Unknown"} @ ${clinicName}`;
|
|
10323
12535
|
}
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
12536
|
+
eventToSync.eventName = eventTitle;
|
|
12537
|
+
const existingSyncId = (_d = (_c = appointment.syncedCalendarEventId) == null ? void 0 : _c.find(
|
|
12538
|
+
(sync) => sync.syncedCalendarProvider === calendar.provider
|
|
12539
|
+
)) == null ? void 0 : _d.eventId;
|
|
12540
|
+
if (calendar.provider === "google" /* GOOGLE */) {
|
|
12541
|
+
const result = await this.syncedCalendarsService.syncPractitionerEventsToGoogleCalendar(
|
|
12542
|
+
entityType === "doctor" ? appointment.practitionerProfileId : appointment.patientProfileId,
|
|
12543
|
+
calendar.id,
|
|
12544
|
+
[eventToSync],
|
|
12545
|
+
existingSyncId
|
|
12546
|
+
// Pass existing sync ID if we have one
|
|
10329
12547
|
);
|
|
10330
|
-
|
|
12548
|
+
if (result.success && ((_e = result.eventIds) == null ? void 0 : _e.length) && !existingSyncId) {
|
|
12549
|
+
const newSyncEvent = {
|
|
12550
|
+
eventId: result.eventIds[0],
|
|
12551
|
+
syncedCalendarProvider: calendar.provider,
|
|
12552
|
+
syncedAt: import_firestore36.Timestamp.now()
|
|
12553
|
+
};
|
|
12554
|
+
await this.updateEventWithSyncId(
|
|
12555
|
+
entityType === "doctor" ? appointment.practitionerProfileId : appointment.patientProfileId,
|
|
12556
|
+
entityType,
|
|
12557
|
+
appointment.id,
|
|
12558
|
+
newSyncEvent
|
|
12559
|
+
);
|
|
12560
|
+
}
|
|
10331
12561
|
}
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
throw new Error(`Invalid search location: ${searchLocation}`);
|
|
10336
|
-
}
|
|
10337
|
-
const collectionRef = (0, import_firestore29.collection)(db, baseCollectionPath);
|
|
10338
|
-
if (filters.clinicId) {
|
|
10339
|
-
constraints.push((0, import_firestore29.where)("clinicBranchId", "==", filters.clinicId));
|
|
12562
|
+
} catch (error) {
|
|
12563
|
+
console.error(`Error syncing with ${entityType}'s calendar:`, error);
|
|
12564
|
+
}
|
|
10340
12565
|
}
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
12566
|
+
/**
|
|
12567
|
+
* Updates an event with a new sync ID
|
|
12568
|
+
* @param entityId - ID of the entity (doctor or patient)
|
|
12569
|
+
* @param entityType - Type of entity
|
|
12570
|
+
* @param eventId - ID of the event
|
|
12571
|
+
* @param syncEvent - Sync event information
|
|
12572
|
+
*/
|
|
12573
|
+
async updateEventWithSyncId(entityId, entityType, eventId, syncEvent) {
|
|
12574
|
+
try {
|
|
12575
|
+
const collectionPath = entityType === "doctor" ? `${PRACTITIONERS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}` : `${PATIENTS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
12576
|
+
const eventRef = (0, import_firestore37.doc)(this.db, collectionPath, eventId);
|
|
12577
|
+
const eventDoc = await (0, import_firestore37.getDoc)(eventRef);
|
|
12578
|
+
if (eventDoc.exists()) {
|
|
12579
|
+
const event = eventDoc.data();
|
|
12580
|
+
const syncIds = [...event.syncedCalendarEventId || []];
|
|
12581
|
+
const existingSyncIndex = syncIds.findIndex(
|
|
12582
|
+
(sync) => sync.syncedCalendarProvider === syncEvent.syncedCalendarProvider
|
|
12583
|
+
);
|
|
12584
|
+
if (existingSyncIndex >= 0) {
|
|
12585
|
+
syncIds[existingSyncIndex] = syncEvent;
|
|
12586
|
+
} else {
|
|
12587
|
+
syncIds.push(syncEvent);
|
|
12588
|
+
}
|
|
12589
|
+
await (0, import_firestore37.updateDoc)(eventRef, {
|
|
12590
|
+
syncedCalendarEventId: syncIds,
|
|
12591
|
+
updatedAt: (0, import_firestore36.serverTimestamp)()
|
|
12592
|
+
});
|
|
12593
|
+
console.log(
|
|
12594
|
+
`Updated event ${eventId} with sync ID ${syncEvent.eventId}`
|
|
12595
|
+
);
|
|
12596
|
+
}
|
|
12597
|
+
} catch (error) {
|
|
12598
|
+
console.error("Error updating event with sync ID:", error);
|
|
12599
|
+
}
|
|
10345
12600
|
}
|
|
10346
|
-
|
|
10347
|
-
|
|
12601
|
+
/**
|
|
12602
|
+
* Validates update permissions and parameters
|
|
12603
|
+
* @param params - Update parameters to validate
|
|
12604
|
+
*/
|
|
12605
|
+
async validateUpdatePermissions(params) {
|
|
12606
|
+
await updateAppointmentSchema.parseAsync(params);
|
|
10348
12607
|
}
|
|
10349
|
-
|
|
10350
|
-
|
|
12608
|
+
/**
|
|
12609
|
+
* Gets clinic working hours for a specific date
|
|
12610
|
+
* @param clinicId - ID of the clinic
|
|
12611
|
+
* @param date - Date to get working hours for
|
|
12612
|
+
* @returns Working hours for the clinic
|
|
12613
|
+
*/
|
|
12614
|
+
async getClinicWorkingHours(clinicId, date) {
|
|
12615
|
+
const clinicRef = (0, import_firestore37.doc)(this.db, CLINICS_COLLECTION, clinicId);
|
|
12616
|
+
const clinicDoc = await (0, import_firestore37.getDoc)(clinicRef);
|
|
12617
|
+
if (!clinicDoc.exists()) {
|
|
12618
|
+
throw new Error(`Clinic with ID ${clinicId} not found`);
|
|
12619
|
+
}
|
|
12620
|
+
const workingHours = [];
|
|
12621
|
+
const dayOfWeek = date.getDay();
|
|
12622
|
+
if (dayOfWeek === 0 || dayOfWeek === 6) {
|
|
12623
|
+
return workingHours;
|
|
12624
|
+
}
|
|
12625
|
+
const workingDate = new Date(date);
|
|
12626
|
+
workingDate.setHours(9, 0, 0, 0);
|
|
12627
|
+
const startTime = new Date(workingDate);
|
|
12628
|
+
workingDate.setHours(17, 0, 0, 0);
|
|
12629
|
+
const endTime = new Date(workingDate);
|
|
12630
|
+
workingHours.push({
|
|
12631
|
+
start: startTime,
|
|
12632
|
+
end: endTime,
|
|
12633
|
+
isAvailable: true
|
|
12634
|
+
});
|
|
12635
|
+
return workingHours;
|
|
10351
12636
|
}
|
|
10352
|
-
|
|
10353
|
-
|
|
12637
|
+
/**
|
|
12638
|
+
* Gets doctor's schedule for a specific date
|
|
12639
|
+
* @param doctorId - ID of the doctor
|
|
12640
|
+
* @param date - Date to get schedule for
|
|
12641
|
+
* @returns Doctor's schedule
|
|
12642
|
+
*/
|
|
12643
|
+
async getDoctorSchedule(doctorId, date) {
|
|
12644
|
+
const practitionerRef = (0, import_firestore37.doc)(this.db, PRACTITIONERS_COLLECTION, doctorId);
|
|
12645
|
+
const practitionerDoc = await (0, import_firestore37.getDoc)(practitionerRef);
|
|
12646
|
+
if (!practitionerDoc.exists()) {
|
|
12647
|
+
throw new Error(`Doctor with ID ${doctorId} not found`);
|
|
12648
|
+
}
|
|
12649
|
+
const schedule = [];
|
|
12650
|
+
const dayOfWeek = date.getDay();
|
|
12651
|
+
if (dayOfWeek === 0 || dayOfWeek === 6) {
|
|
12652
|
+
return schedule;
|
|
12653
|
+
}
|
|
12654
|
+
const scheduleDate = new Date(date);
|
|
12655
|
+
scheduleDate.setHours(9, 0, 0, 0);
|
|
12656
|
+
const startTime = new Date(scheduleDate);
|
|
12657
|
+
scheduleDate.setHours(17, 0, 0, 0);
|
|
12658
|
+
const endTime = new Date(scheduleDate);
|
|
12659
|
+
schedule.push({
|
|
12660
|
+
start: startTime,
|
|
12661
|
+
end: endTime,
|
|
12662
|
+
isAvailable: true
|
|
12663
|
+
});
|
|
12664
|
+
return schedule;
|
|
10354
12665
|
}
|
|
10355
|
-
|
|
10356
|
-
|
|
12666
|
+
/**
|
|
12667
|
+
* Gets doctor's appointments for a specific date
|
|
12668
|
+
* @param doctorId - ID of the doctor
|
|
12669
|
+
* @param date - Date to get appointments for
|
|
12670
|
+
* @returns Array of calendar events
|
|
12671
|
+
*/
|
|
12672
|
+
async getDoctorAppointments(doctorId, date) {
|
|
12673
|
+
const startOfDay = new Date(date);
|
|
12674
|
+
startOfDay.setHours(0, 0, 0, 0);
|
|
12675
|
+
const endOfDay = new Date(date);
|
|
12676
|
+
endOfDay.setHours(23, 59, 59, 999);
|
|
12677
|
+
const appointmentsRef = (0, import_firestore37.collection)(this.db, CALENDAR_COLLECTION);
|
|
12678
|
+
const q = (0, import_firestore37.query)(
|
|
12679
|
+
appointmentsRef,
|
|
12680
|
+
(0, import_firestore37.where)("practitionerProfileId", "==", doctorId),
|
|
12681
|
+
(0, import_firestore37.where)("eventTime.start", ">=", import_firestore36.Timestamp.fromDate(startOfDay)),
|
|
12682
|
+
(0, import_firestore37.where)("eventTime.start", "<=", import_firestore36.Timestamp.fromDate(endOfDay)),
|
|
12683
|
+
(0, import_firestore37.where)("status", "in", [
|
|
12684
|
+
"confirmed" /* CONFIRMED */,
|
|
12685
|
+
"pending" /* PENDING */
|
|
12686
|
+
])
|
|
12687
|
+
);
|
|
12688
|
+
const querySnapshot = await (0, import_firestore37.getDocs)(q);
|
|
12689
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
10357
12690
|
}
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
12691
|
+
/**
|
|
12692
|
+
* Calculates available time slots based on working hours, schedule and existing appointments
|
|
12693
|
+
* @param workingHours - Clinic working hours
|
|
12694
|
+
* @param doctorSchedule - Doctor's schedule
|
|
12695
|
+
* @param existingAppointments - Existing appointments
|
|
12696
|
+
* @returns Array of available time slots
|
|
12697
|
+
*/
|
|
12698
|
+
calculateAvailableSlots(workingHours, doctorSchedule, existingAppointments) {
|
|
12699
|
+
const availableSlots = [];
|
|
12700
|
+
for (const workingHour of workingHours) {
|
|
12701
|
+
for (const scheduleSlot of doctorSchedule) {
|
|
12702
|
+
const overlapStart = new Date(
|
|
12703
|
+
Math.max(workingHour.start.getTime(), scheduleSlot.start.getTime())
|
|
12704
|
+
);
|
|
12705
|
+
const overlapEnd = new Date(
|
|
12706
|
+
Math.min(workingHour.end.getTime(), scheduleSlot.end.getTime())
|
|
12707
|
+
);
|
|
12708
|
+
if (overlapStart < overlapEnd && workingHour.isAvailable && scheduleSlot.isAvailable) {
|
|
12709
|
+
let slotStart = new Date(overlapStart);
|
|
12710
|
+
while (slotStart < overlapEnd) {
|
|
12711
|
+
const slotEnd = new Date(
|
|
12712
|
+
slotStart.getTime() + MIN_APPOINTMENT_DURATION * 60 * 1e3
|
|
12713
|
+
);
|
|
12714
|
+
const hasOverlap = existingAppointments.some((appointment) => {
|
|
12715
|
+
const appointmentStart = appointment.eventTime.start.toDate();
|
|
12716
|
+
const appointmentEnd = appointment.eventTime.end.toDate();
|
|
12717
|
+
return slotStart >= appointmentStart && slotStart < appointmentEnd || slotEnd > appointmentStart && slotEnd <= appointmentEnd;
|
|
12718
|
+
});
|
|
12719
|
+
if (!hasOverlap && slotEnd <= overlapEnd) {
|
|
12720
|
+
availableSlots.push({
|
|
12721
|
+
start: new Date(slotStart),
|
|
12722
|
+
end: new Date(slotEnd),
|
|
12723
|
+
isAvailable: true
|
|
12724
|
+
});
|
|
12725
|
+
}
|
|
12726
|
+
slotStart = new Date(
|
|
12727
|
+
slotStart.getTime() + MIN_APPOINTMENT_DURATION * 60 * 1e3
|
|
12728
|
+
);
|
|
12729
|
+
}
|
|
12730
|
+
}
|
|
12731
|
+
}
|
|
12732
|
+
}
|
|
12733
|
+
return availableSlots;
|
|
10361
12734
|
}
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
|
|
10367
|
-
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
|
|
12735
|
+
/**
|
|
12736
|
+
* Fetches and creates info cards for clinic, doctor, and patient profiles
|
|
12737
|
+
* @param clinicId - ID of the clinic
|
|
12738
|
+
* @param doctorId - ID of the doctor
|
|
12739
|
+
* @param patientId - ID of the patient
|
|
12740
|
+
* @returns Object containing info cards for all profiles
|
|
12741
|
+
*/
|
|
12742
|
+
async fetchProfileInfoCards(clinicId, doctorId, patientId) {
|
|
12743
|
+
var _a;
|
|
12744
|
+
try {
|
|
12745
|
+
const [clinicDoc, practitionerDoc, patientDoc, patientSensitiveInfoDoc] = await Promise.all([
|
|
12746
|
+
(0, import_firestore37.getDoc)((0, import_firestore37.doc)(this.db, CLINICS_COLLECTION, clinicId)),
|
|
12747
|
+
(0, import_firestore37.getDoc)((0, import_firestore37.doc)(this.db, PRACTITIONERS_COLLECTION, doctorId)),
|
|
12748
|
+
(0, import_firestore37.getDoc)((0, import_firestore37.doc)(this.db, PATIENTS_COLLECTION, patientId)),
|
|
12749
|
+
(0, import_firestore37.getDoc)(
|
|
12750
|
+
(0, import_firestore37.doc)(
|
|
12751
|
+
this.db,
|
|
12752
|
+
PATIENTS_COLLECTION,
|
|
12753
|
+
patientId,
|
|
12754
|
+
PATIENT_SENSITIVE_INFO_COLLECTION,
|
|
12755
|
+
patientId
|
|
12756
|
+
)
|
|
12757
|
+
)
|
|
12758
|
+
]);
|
|
12759
|
+
const clinicInfo = clinicDoc.exists() ? {
|
|
12760
|
+
id: clinicDoc.id,
|
|
12761
|
+
featuredPhoto: clinicDoc.data().featuredPhoto || "",
|
|
12762
|
+
name: clinicDoc.data().name,
|
|
12763
|
+
description: clinicDoc.data().description || "",
|
|
12764
|
+
location: clinicDoc.data().location,
|
|
12765
|
+
contactInfo: clinicDoc.data().contactInfo
|
|
12766
|
+
} : null;
|
|
12767
|
+
const practitionerInfo = practitionerDoc.exists() ? {
|
|
12768
|
+
id: practitionerDoc.id,
|
|
12769
|
+
practitionerPhoto: practitionerDoc.data().basicInfo.profileImageUrl || null,
|
|
12770
|
+
name: `${practitionerDoc.data().basicInfo.firstName} ${practitionerDoc.data().basicInfo.lastName}`,
|
|
12771
|
+
email: practitionerDoc.data().basicInfo.email,
|
|
12772
|
+
phone: practitionerDoc.data().basicInfo.phoneNumber || null,
|
|
12773
|
+
certification: practitionerDoc.data().certification
|
|
12774
|
+
} : null;
|
|
12775
|
+
let patientInfo = null;
|
|
12776
|
+
if (patientSensitiveInfoDoc.exists()) {
|
|
12777
|
+
const sensitiveData = patientSensitiveInfoDoc.data();
|
|
12778
|
+
patientInfo = {
|
|
12779
|
+
id: patientId,
|
|
12780
|
+
fullName: `${sensitiveData.firstName} ${sensitiveData.lastName}`,
|
|
12781
|
+
email: sensitiveData.email || "",
|
|
12782
|
+
phone: sensitiveData.phoneNumber || null,
|
|
12783
|
+
dateOfBirth: sensitiveData.dateOfBirth || import_firestore36.Timestamp.now(),
|
|
12784
|
+
gender: sensitiveData.gender || "other" /* OTHER */
|
|
12785
|
+
};
|
|
12786
|
+
} else if (patientDoc.exists()) {
|
|
12787
|
+
patientInfo = {
|
|
12788
|
+
id: patientDoc.id,
|
|
12789
|
+
fullName: patientDoc.data().displayName,
|
|
12790
|
+
email: ((_a = patientDoc.data().contactInfo) == null ? void 0 : _a.email) || "",
|
|
12791
|
+
phone: patientDoc.data().phoneNumber || null,
|
|
12792
|
+
dateOfBirth: patientDoc.data().dateOfBirth || import_firestore36.Timestamp.now(),
|
|
12793
|
+
gender: patientDoc.data().gender || "other" /* OTHER */
|
|
12794
|
+
};
|
|
12795
|
+
}
|
|
12796
|
+
return {
|
|
12797
|
+
clinicInfo,
|
|
12798
|
+
practitionerInfo,
|
|
12799
|
+
patientInfo
|
|
12800
|
+
};
|
|
12801
|
+
} catch (error) {
|
|
12802
|
+
console.error("Error fetching profile info cards:", error);
|
|
12803
|
+
return {
|
|
12804
|
+
clinicInfo: null,
|
|
12805
|
+
practitionerInfo: null,
|
|
12806
|
+
patientInfo: null
|
|
12807
|
+
};
|
|
12808
|
+
}
|
|
10372
12809
|
}
|
|
10373
|
-
|
|
12810
|
+
// #endregion
|
|
12811
|
+
};
|
|
10374
12812
|
|
|
10375
12813
|
// src/services/calendar/calendar.v3.service.ts
|
|
12814
|
+
var import_firestore38 = require("firebase/firestore");
|
|
12815
|
+
var import_firestore39 = require("firebase/firestore");
|
|
10376
12816
|
var CalendarServiceV3 = class extends BaseService {
|
|
10377
12817
|
/**
|
|
10378
12818
|
* Creates a new CalendarServiceV3 instance
|
|
@@ -10396,7 +12836,7 @@ var CalendarServiceV3 = class extends BaseService {
|
|
|
10396
12836
|
params.entityType,
|
|
10397
12837
|
params.entityId
|
|
10398
12838
|
);
|
|
10399
|
-
const eventRef = (0,
|
|
12839
|
+
const eventRef = (0, import_firestore39.doc)(this.db, collectionPath, eventId);
|
|
10400
12840
|
const eventData = {
|
|
10401
12841
|
id: eventId,
|
|
10402
12842
|
eventName: params.eventName,
|
|
@@ -10406,19 +12846,19 @@ var CalendarServiceV3 = class extends BaseService {
|
|
|
10406
12846
|
status: "confirmed" /* CONFIRMED */,
|
|
10407
12847
|
// Blocking events are always confirmed
|
|
10408
12848
|
syncStatus: "internal" /* INTERNAL */,
|
|
10409
|
-
createdAt: (0,
|
|
10410
|
-
updatedAt: (0,
|
|
12849
|
+
createdAt: (0, import_firestore38.serverTimestamp)(),
|
|
12850
|
+
updatedAt: (0, import_firestore38.serverTimestamp)()
|
|
10411
12851
|
};
|
|
10412
12852
|
if (params.entityType === "practitioner") {
|
|
10413
12853
|
eventData.practitionerProfileId = params.entityId;
|
|
10414
12854
|
} else {
|
|
10415
12855
|
eventData.clinicBranchId = params.entityId;
|
|
10416
12856
|
}
|
|
10417
|
-
await (0,
|
|
12857
|
+
await (0, import_firestore39.setDoc)(eventRef, eventData);
|
|
10418
12858
|
return {
|
|
10419
12859
|
...eventData,
|
|
10420
|
-
createdAt:
|
|
10421
|
-
updatedAt:
|
|
12860
|
+
createdAt: import_firestore38.Timestamp.now(),
|
|
12861
|
+
updatedAt: import_firestore38.Timestamp.now()
|
|
10422
12862
|
};
|
|
10423
12863
|
}
|
|
10424
12864
|
/**
|
|
@@ -10431,13 +12871,13 @@ var CalendarServiceV3 = class extends BaseService {
|
|
|
10431
12871
|
params.entityType,
|
|
10432
12872
|
params.entityId
|
|
10433
12873
|
);
|
|
10434
|
-
const eventRef = (0,
|
|
10435
|
-
const eventDoc = await (0,
|
|
12874
|
+
const eventRef = (0, import_firestore39.doc)(this.db, collectionPath, params.eventId);
|
|
12875
|
+
const eventDoc = await (0, import_firestore39.getDoc)(eventRef);
|
|
10436
12876
|
if (!eventDoc.exists()) {
|
|
10437
12877
|
throw new Error(`Blocking event with ID ${params.eventId} not found`);
|
|
10438
12878
|
}
|
|
10439
12879
|
const updateData = {
|
|
10440
|
-
updatedAt: (0,
|
|
12880
|
+
updatedAt: (0, import_firestore38.serverTimestamp)()
|
|
10441
12881
|
};
|
|
10442
12882
|
if (params.eventName !== void 0) {
|
|
10443
12883
|
updateData.eventName = params.eventName;
|
|
@@ -10451,8 +12891,8 @@ var CalendarServiceV3 = class extends BaseService {
|
|
|
10451
12891
|
if (params.status !== void 0) {
|
|
10452
12892
|
updateData.status = params.status;
|
|
10453
12893
|
}
|
|
10454
|
-
await (0,
|
|
10455
|
-
const updatedEventDoc = await (0,
|
|
12894
|
+
await (0, import_firestore39.updateDoc)(eventRef, updateData);
|
|
12895
|
+
const updatedEventDoc = await (0, import_firestore39.getDoc)(eventRef);
|
|
10456
12896
|
return updatedEventDoc.data();
|
|
10457
12897
|
}
|
|
10458
12898
|
/**
|
|
@@ -10463,12 +12903,12 @@ var CalendarServiceV3 = class extends BaseService {
|
|
|
10463
12903
|
*/
|
|
10464
12904
|
async deleteBlockingEvent(entityType, entityId, eventId) {
|
|
10465
12905
|
const collectionPath = this.getEntityCalendarPath(entityType, entityId);
|
|
10466
|
-
const eventRef = (0,
|
|
10467
|
-
const eventDoc = await (0,
|
|
12906
|
+
const eventRef = (0, import_firestore39.doc)(this.db, collectionPath, eventId);
|
|
12907
|
+
const eventDoc = await (0, import_firestore39.getDoc)(eventRef);
|
|
10468
12908
|
if (!eventDoc.exists()) {
|
|
10469
12909
|
throw new Error(`Blocking event with ID ${eventId} not found`);
|
|
10470
12910
|
}
|
|
10471
|
-
await (0,
|
|
12911
|
+
await (0, import_firestore39.deleteDoc)(eventRef);
|
|
10472
12912
|
}
|
|
10473
12913
|
/**
|
|
10474
12914
|
* Gets a specific blocking event
|
|
@@ -10479,8 +12919,8 @@ var CalendarServiceV3 = class extends BaseService {
|
|
|
10479
12919
|
*/
|
|
10480
12920
|
async getBlockingEvent(entityType, entityId, eventId) {
|
|
10481
12921
|
const collectionPath = this.getEntityCalendarPath(entityType, entityId);
|
|
10482
|
-
const eventRef = (0,
|
|
10483
|
-
const eventDoc = await (0,
|
|
12922
|
+
const eventRef = (0, import_firestore39.doc)(this.db, collectionPath, eventId);
|
|
12923
|
+
const eventDoc = await (0, import_firestore39.getDoc)(eventRef);
|
|
10484
12924
|
if (!eventDoc.exists()) {
|
|
10485
12925
|
return null;
|
|
10486
12926
|
}
|
|
@@ -10673,7 +13113,7 @@ var ExternalCalendarService = class extends BaseService {
|
|
|
10673
13113
|
};
|
|
10674
13114
|
|
|
10675
13115
|
// src/services/clinic/practitioner-invite.service.ts
|
|
10676
|
-
var
|
|
13116
|
+
var import_firestore40 = require("firebase/firestore");
|
|
10677
13117
|
var PractitionerInviteService = class extends BaseService {
|
|
10678
13118
|
constructor(db, auth, app) {
|
|
10679
13119
|
super(db, auth, app);
|
|
@@ -10735,7 +13175,7 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10735
13175
|
message: message || null,
|
|
10736
13176
|
status: "pending" /* PENDING */
|
|
10737
13177
|
};
|
|
10738
|
-
const now =
|
|
13178
|
+
const now = import_firestore40.Timestamp.now();
|
|
10739
13179
|
const invite = {
|
|
10740
13180
|
id: inviteId,
|
|
10741
13181
|
...inviteData,
|
|
@@ -10746,8 +13186,8 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10746
13186
|
rejectedAt: null,
|
|
10747
13187
|
cancelledAt: null
|
|
10748
13188
|
};
|
|
10749
|
-
const docRef = (0,
|
|
10750
|
-
await (0,
|
|
13189
|
+
const docRef = (0, import_firestore40.doc)(this.db, PRACTITIONER_INVITES_COLLECTION, inviteId);
|
|
13190
|
+
await (0, import_firestore40.setDoc)(docRef, invite);
|
|
10751
13191
|
return invite;
|
|
10752
13192
|
} catch (error) {
|
|
10753
13193
|
console.error(
|
|
@@ -10766,18 +13206,18 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10766
13206
|
async getAllInvitesDoctor(practitionerId, statusFilter) {
|
|
10767
13207
|
try {
|
|
10768
13208
|
const constraints = [
|
|
10769
|
-
(0,
|
|
10770
|
-
(0,
|
|
13209
|
+
(0, import_firestore40.where)("practitionerId", "==", practitionerId),
|
|
13210
|
+
(0, import_firestore40.orderBy)("createdAt", "desc")
|
|
10771
13211
|
];
|
|
10772
13212
|
if (statusFilter && statusFilter.length > 0) {
|
|
10773
|
-
constraints.push((0,
|
|
13213
|
+
constraints.push((0, import_firestore40.where)("status", "in", statusFilter));
|
|
10774
13214
|
}
|
|
10775
|
-
const q = (0,
|
|
10776
|
-
(0,
|
|
13215
|
+
const q = (0, import_firestore40.query)(
|
|
13216
|
+
(0, import_firestore40.collection)(this.db, PRACTITIONER_INVITES_COLLECTION),
|
|
10777
13217
|
...constraints
|
|
10778
13218
|
);
|
|
10779
|
-
const querySnapshot = await (0,
|
|
10780
|
-
return querySnapshot.docs.map((
|
|
13219
|
+
const querySnapshot = await (0, import_firestore40.getDocs)(q);
|
|
13220
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
10781
13221
|
} catch (error) {
|
|
10782
13222
|
console.error(
|
|
10783
13223
|
"[PractitionerInviteService] Error getting doctor invites:",
|
|
@@ -10795,18 +13235,18 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10795
13235
|
async getAllInvitesClinic(clinicId, statusFilter) {
|
|
10796
13236
|
try {
|
|
10797
13237
|
const constraints = [
|
|
10798
|
-
(0,
|
|
10799
|
-
(0,
|
|
13238
|
+
(0, import_firestore40.where)("clinicId", "==", clinicId),
|
|
13239
|
+
(0, import_firestore40.orderBy)("createdAt", "desc")
|
|
10800
13240
|
];
|
|
10801
13241
|
if (statusFilter && statusFilter.length > 0) {
|
|
10802
|
-
constraints.push((0,
|
|
13242
|
+
constraints.push((0, import_firestore40.where)("status", "in", statusFilter));
|
|
10803
13243
|
}
|
|
10804
|
-
const q = (0,
|
|
10805
|
-
(0,
|
|
13244
|
+
const q = (0, import_firestore40.query)(
|
|
13245
|
+
(0, import_firestore40.collection)(this.db, PRACTITIONER_INVITES_COLLECTION),
|
|
10806
13246
|
...constraints
|
|
10807
13247
|
);
|
|
10808
|
-
const querySnapshot = await (0,
|
|
10809
|
-
return querySnapshot.docs.map((
|
|
13248
|
+
const querySnapshot = await (0, import_firestore40.getDocs)(q);
|
|
13249
|
+
return querySnapshot.docs.map((doc32) => doc32.data());
|
|
10810
13250
|
} catch (error) {
|
|
10811
13251
|
console.error(
|
|
10812
13252
|
"[PractitionerInviteService] Error getting clinic invites:",
|
|
@@ -10831,11 +13271,11 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10831
13271
|
}
|
|
10832
13272
|
const updateData = {
|
|
10833
13273
|
status: "accepted" /* ACCEPTED */,
|
|
10834
|
-
acceptedAt:
|
|
10835
|
-
updatedAt: (0,
|
|
13274
|
+
acceptedAt: import_firestore40.Timestamp.now(),
|
|
13275
|
+
updatedAt: (0, import_firestore40.serverTimestamp)()
|
|
10836
13276
|
};
|
|
10837
|
-
const docRef = (0,
|
|
10838
|
-
await (0,
|
|
13277
|
+
const docRef = (0, import_firestore40.doc)(this.db, PRACTITIONER_INVITES_COLLECTION, inviteId);
|
|
13278
|
+
await (0, import_firestore40.updateDoc)(docRef, updateData);
|
|
10839
13279
|
return await this.getInviteById(inviteId);
|
|
10840
13280
|
} catch (error) {
|
|
10841
13281
|
console.error(
|
|
@@ -10863,11 +13303,11 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10863
13303
|
const updateData = {
|
|
10864
13304
|
status: "rejected" /* REJECTED */,
|
|
10865
13305
|
rejectionReason: rejectionReason || null,
|
|
10866
|
-
rejectedAt:
|
|
10867
|
-
updatedAt: (0,
|
|
13306
|
+
rejectedAt: import_firestore40.Timestamp.now(),
|
|
13307
|
+
updatedAt: (0, import_firestore40.serverTimestamp)()
|
|
10868
13308
|
};
|
|
10869
|
-
const docRef = (0,
|
|
10870
|
-
await (0,
|
|
13309
|
+
const docRef = (0, import_firestore40.doc)(this.db, PRACTITIONER_INVITES_COLLECTION, inviteId);
|
|
13310
|
+
await (0, import_firestore40.updateDoc)(docRef, updateData);
|
|
10871
13311
|
return await this.getInviteById(inviteId);
|
|
10872
13312
|
} catch (error) {
|
|
10873
13313
|
console.error(
|
|
@@ -10895,11 +13335,11 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10895
13335
|
const updateData = {
|
|
10896
13336
|
status: "cancelled" /* CANCELLED */,
|
|
10897
13337
|
cancelReason: cancelReason || null,
|
|
10898
|
-
cancelledAt:
|
|
10899
|
-
updatedAt: (0,
|
|
13338
|
+
cancelledAt: import_firestore40.Timestamp.now(),
|
|
13339
|
+
updatedAt: (0, import_firestore40.serverTimestamp)()
|
|
10900
13340
|
};
|
|
10901
|
-
const docRef = (0,
|
|
10902
|
-
await (0,
|
|
13341
|
+
const docRef = (0, import_firestore40.doc)(this.db, PRACTITIONER_INVITES_COLLECTION, inviteId);
|
|
13342
|
+
await (0, import_firestore40.updateDoc)(docRef, updateData);
|
|
10903
13343
|
return await this.getInviteById(inviteId);
|
|
10904
13344
|
} catch (error) {
|
|
10905
13345
|
console.error(
|
|
@@ -10916,8 +13356,8 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10916
13356
|
*/
|
|
10917
13357
|
async getInviteById(inviteId) {
|
|
10918
13358
|
try {
|
|
10919
|
-
const docRef = (0,
|
|
10920
|
-
const docSnap = await (0,
|
|
13359
|
+
const docRef = (0, import_firestore40.doc)(this.db, PRACTITIONER_INVITES_COLLECTION, inviteId);
|
|
13360
|
+
const docSnap = await (0, import_firestore40.getDoc)(docRef);
|
|
10921
13361
|
if (docSnap.exists()) {
|
|
10922
13362
|
return docSnap.data();
|
|
10923
13363
|
}
|
|
@@ -10939,30 +13379,30 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10939
13379
|
try {
|
|
10940
13380
|
const constraints = [];
|
|
10941
13381
|
if (filters.practitionerId) {
|
|
10942
|
-
constraints.push((0,
|
|
13382
|
+
constraints.push((0, import_firestore40.where)("practitionerId", "==", filters.practitionerId));
|
|
10943
13383
|
}
|
|
10944
13384
|
if (filters.clinicId) {
|
|
10945
|
-
constraints.push((0,
|
|
13385
|
+
constraints.push((0, import_firestore40.where)("clinicId", "==", filters.clinicId));
|
|
10946
13386
|
}
|
|
10947
13387
|
if (filters.invitedBy) {
|
|
10948
|
-
constraints.push((0,
|
|
13388
|
+
constraints.push((0, import_firestore40.where)("invitedBy", "==", filters.invitedBy));
|
|
10949
13389
|
}
|
|
10950
13390
|
if (filters.status && filters.status.length > 0) {
|
|
10951
|
-
constraints.push((0,
|
|
13391
|
+
constraints.push((0, import_firestore40.where)("status", "in", filters.status));
|
|
10952
13392
|
}
|
|
10953
13393
|
const orderField = filters.orderBy || "createdAt";
|
|
10954
13394
|
const orderDirection = filters.orderDirection || "desc";
|
|
10955
|
-
constraints.push((0,
|
|
13395
|
+
constraints.push((0, import_firestore40.orderBy)(orderField, orderDirection));
|
|
10956
13396
|
if (filters.limit) {
|
|
10957
|
-
constraints.push((0,
|
|
13397
|
+
constraints.push((0, import_firestore40.limit)(filters.limit));
|
|
10958
13398
|
}
|
|
10959
|
-
const q = (0,
|
|
10960
|
-
(0,
|
|
13399
|
+
const q = (0, import_firestore40.query)(
|
|
13400
|
+
(0, import_firestore40.collection)(this.db, PRACTITIONER_INVITES_COLLECTION),
|
|
10961
13401
|
...constraints
|
|
10962
13402
|
);
|
|
10963
|
-
const querySnapshot = await (0,
|
|
13403
|
+
const querySnapshot = await (0, import_firestore40.getDocs)(q);
|
|
10964
13404
|
let invites = querySnapshot.docs.map(
|
|
10965
|
-
(
|
|
13405
|
+
(doc32) => doc32.data()
|
|
10966
13406
|
);
|
|
10967
13407
|
if (filters.fromDate) {
|
|
10968
13408
|
invites = invites.filter(
|
|
@@ -10989,8 +13429,8 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
10989
13429
|
*/
|
|
10990
13430
|
async deleteInvite(inviteId) {
|
|
10991
13431
|
try {
|
|
10992
|
-
const docRef = (0,
|
|
10993
|
-
await (0,
|
|
13432
|
+
const docRef = (0, import_firestore40.doc)(this.db, PRACTITIONER_INVITES_COLLECTION, inviteId);
|
|
13433
|
+
await (0, import_firestore40.deleteDoc)(docRef);
|
|
10994
13434
|
} catch (error) {
|
|
10995
13435
|
console.error(
|
|
10996
13436
|
"[PractitionerInviteService] Error deleting invite:",
|
|
@@ -11007,8 +13447,8 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
11007
13447
|
*/
|
|
11008
13448
|
async getPractitionerById(practitionerId) {
|
|
11009
13449
|
try {
|
|
11010
|
-
const docRef = (0,
|
|
11011
|
-
const docSnap = await (0,
|
|
13450
|
+
const docRef = (0, import_firestore40.doc)(this.db, PRACTITIONERS_COLLECTION, practitionerId);
|
|
13451
|
+
const docSnap = await (0, import_firestore40.getDoc)(docRef);
|
|
11012
13452
|
return docSnap.exists() ? docSnap.data() : null;
|
|
11013
13453
|
} catch (error) {
|
|
11014
13454
|
console.error(
|
|
@@ -11025,8 +13465,8 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
11025
13465
|
*/
|
|
11026
13466
|
async getClinicById(clinicId) {
|
|
11027
13467
|
try {
|
|
11028
|
-
const docRef = (0,
|
|
11029
|
-
const docSnap = await (0,
|
|
13468
|
+
const docRef = (0, import_firestore40.doc)(this.db, CLINICS_COLLECTION, clinicId);
|
|
13469
|
+
const docSnap = await (0, import_firestore40.getDoc)(docRef);
|
|
11030
13470
|
return docSnap.exists() ? docSnap.data() : null;
|
|
11031
13471
|
} catch (error) {
|
|
11032
13472
|
console.error("[PractitionerInviteService] Error getting clinic:", error);
|
|
@@ -11041,14 +13481,14 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
11041
13481
|
*/
|
|
11042
13482
|
async findExistingInvite(practitionerId, clinicId) {
|
|
11043
13483
|
try {
|
|
11044
|
-
const q = (0,
|
|
11045
|
-
(0,
|
|
11046
|
-
(0,
|
|
11047
|
-
(0,
|
|
11048
|
-
(0,
|
|
11049
|
-
(0,
|
|
11050
|
-
);
|
|
11051
|
-
const querySnapshot = await (0,
|
|
13484
|
+
const q = (0, import_firestore40.query)(
|
|
13485
|
+
(0, import_firestore40.collection)(this.db, PRACTITIONER_INVITES_COLLECTION),
|
|
13486
|
+
(0, import_firestore40.where)("practitionerId", "==", practitionerId),
|
|
13487
|
+
(0, import_firestore40.where)("clinicId", "==", clinicId),
|
|
13488
|
+
(0, import_firestore40.orderBy)("createdAt", "desc"),
|
|
13489
|
+
(0, import_firestore40.limit)(1)
|
|
13490
|
+
);
|
|
13491
|
+
const querySnapshot = await (0, import_firestore40.getDocs)(q);
|
|
11052
13492
|
if (querySnapshot.empty) {
|
|
11053
13493
|
return null;
|
|
11054
13494
|
}
|
|
@@ -11064,11 +13504,11 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
11064
13504
|
};
|
|
11065
13505
|
|
|
11066
13506
|
// src/services/documentation-templates/documentation-template.service.ts
|
|
11067
|
-
var
|
|
13507
|
+
var import_firestore41 = require("firebase/firestore");
|
|
11068
13508
|
var DocumentationTemplateService = class extends BaseService {
|
|
11069
13509
|
constructor() {
|
|
11070
13510
|
super(...arguments);
|
|
11071
|
-
this.collectionRef = (0,
|
|
13511
|
+
this.collectionRef = (0, import_firestore41.collection)(
|
|
11072
13512
|
this.db,
|
|
11073
13513
|
DOCUMENTATION_TEMPLATES_COLLECTION
|
|
11074
13514
|
);
|
|
@@ -11102,8 +13542,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11102
13542
|
isRequired: validatedData.isRequired || false,
|
|
11103
13543
|
sortingOrder: validatedData.sortingOrder || 0
|
|
11104
13544
|
};
|
|
11105
|
-
const docRef = (0,
|
|
11106
|
-
await (0,
|
|
13545
|
+
const docRef = (0, import_firestore41.doc)(this.collectionRef, templateId);
|
|
13546
|
+
await (0, import_firestore41.setDoc)(docRef, template);
|
|
11107
13547
|
return template;
|
|
11108
13548
|
}
|
|
11109
13549
|
/**
|
|
@@ -11113,8 +13553,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11113
13553
|
* @returns The template or null if not found
|
|
11114
13554
|
*/
|
|
11115
13555
|
async getTemplateById(templateId, version) {
|
|
11116
|
-
const docRef = (0,
|
|
11117
|
-
const docSnap = await (0,
|
|
13556
|
+
const docRef = (0, import_firestore41.doc)(this.collectionRef, templateId);
|
|
13557
|
+
const docSnap = await (0, import_firestore41.getDoc)(docRef);
|
|
11118
13558
|
if (!docSnap.exists()) {
|
|
11119
13559
|
return null;
|
|
11120
13560
|
}
|
|
@@ -11152,15 +13592,15 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11152
13592
|
if (!template) {
|
|
11153
13593
|
throw new Error(`Template with ID ${templateId} not found`);
|
|
11154
13594
|
}
|
|
11155
|
-
const versionsCollectionRef = (0,
|
|
13595
|
+
const versionsCollectionRef = (0, import_firestore41.collection)(
|
|
11156
13596
|
this.db,
|
|
11157
13597
|
`${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions`
|
|
11158
13598
|
);
|
|
11159
|
-
const versionDocRef = (0,
|
|
13599
|
+
const versionDocRef = (0, import_firestore41.doc)(
|
|
11160
13600
|
versionsCollectionRef,
|
|
11161
13601
|
template.version.toString()
|
|
11162
13602
|
);
|
|
11163
|
-
await (0,
|
|
13603
|
+
await (0, import_firestore41.setDoc)(versionDocRef, template);
|
|
11164
13604
|
let updatedElements = template.elements;
|
|
11165
13605
|
if (validatedData.elements) {
|
|
11166
13606
|
updatedElements = validatedData.elements.map((element) => ({
|
|
@@ -11184,9 +13624,9 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11184
13624
|
updatePayload.isUserForm = (_a = validatedData.isUserForm) != null ? _a : false;
|
|
11185
13625
|
updatePayload.isRequired = (_b = validatedData.isRequired) != null ? _b : false;
|
|
11186
13626
|
updatePayload.sortingOrder = (_c = validatedData.sortingOrder) != null ? _c : 0;
|
|
11187
|
-
const docRef = (0,
|
|
13627
|
+
const docRef = (0, import_firestore41.doc)(this.collectionRef, templateId);
|
|
11188
13628
|
console.log("Update payload", updatePayload);
|
|
11189
|
-
await (0,
|
|
13629
|
+
await (0, import_firestore41.updateDoc)(docRef, updatePayload);
|
|
11190
13630
|
return { ...template, ...updatePayload };
|
|
11191
13631
|
}
|
|
11192
13632
|
/**
|
|
@@ -11196,11 +13636,11 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11196
13636
|
* @returns The template version or null if not found
|
|
11197
13637
|
*/
|
|
11198
13638
|
async getTemplateVersion(templateId, versionNumber) {
|
|
11199
|
-
const versionDocRef = (0,
|
|
13639
|
+
const versionDocRef = (0, import_firestore41.doc)(
|
|
11200
13640
|
this.db,
|
|
11201
13641
|
`${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions/${versionNumber}`
|
|
11202
13642
|
);
|
|
11203
|
-
const versionDocSnap = await (0,
|
|
13643
|
+
const versionDocSnap = await (0, import_firestore41.getDoc)(versionDocRef);
|
|
11204
13644
|
if (!versionDocSnap.exists()) {
|
|
11205
13645
|
return null;
|
|
11206
13646
|
}
|
|
@@ -11212,15 +13652,15 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11212
13652
|
* @returns Array of template versions
|
|
11213
13653
|
*/
|
|
11214
13654
|
async getTemplateOldVersions(templateId) {
|
|
11215
|
-
const versionsCollectionRef = (0,
|
|
13655
|
+
const versionsCollectionRef = (0, import_firestore41.collection)(
|
|
11216
13656
|
this.db,
|
|
11217
13657
|
`${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions`
|
|
11218
13658
|
);
|
|
11219
|
-
const q = (0,
|
|
11220
|
-
const querySnapshot = await (0,
|
|
13659
|
+
const q = (0, import_firestore41.query)(versionsCollectionRef, (0, import_firestore41.orderBy)("version", "desc"));
|
|
13660
|
+
const querySnapshot = await (0, import_firestore41.getDocs)(q);
|
|
11221
13661
|
const versions = [];
|
|
11222
|
-
querySnapshot.forEach((
|
|
11223
|
-
versions.push(
|
|
13662
|
+
querySnapshot.forEach((doc32) => {
|
|
13663
|
+
versions.push(doc32.data());
|
|
11224
13664
|
});
|
|
11225
13665
|
return versions;
|
|
11226
13666
|
}
|
|
@@ -11229,8 +13669,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11229
13669
|
* @param templateId - ID of the template to delete
|
|
11230
13670
|
*/
|
|
11231
13671
|
async deleteTemplate(templateId) {
|
|
11232
|
-
const docRef = (0,
|
|
11233
|
-
await (0,
|
|
13672
|
+
const docRef = (0, import_firestore41.doc)(this.collectionRef, templateId);
|
|
13673
|
+
await (0, import_firestore41.deleteDoc)(docRef);
|
|
11234
13674
|
}
|
|
11235
13675
|
/**
|
|
11236
13676
|
* Get all active templates
|
|
@@ -11239,21 +13679,21 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11239
13679
|
* @returns Array of templates and the last document for pagination
|
|
11240
13680
|
*/
|
|
11241
13681
|
async getActiveTemplates(pageSize = 20, lastDoc) {
|
|
11242
|
-
let q = (0,
|
|
13682
|
+
let q = (0, import_firestore41.query)(
|
|
11243
13683
|
this.collectionRef,
|
|
11244
|
-
(0,
|
|
11245
|
-
(0,
|
|
11246
|
-
(0,
|
|
13684
|
+
(0, import_firestore41.where)("isActive", "==", true),
|
|
13685
|
+
(0, import_firestore41.orderBy)("updatedAt", "desc"),
|
|
13686
|
+
(0, import_firestore41.limit)(pageSize)
|
|
11247
13687
|
);
|
|
11248
13688
|
if (lastDoc) {
|
|
11249
|
-
q = (0,
|
|
13689
|
+
q = (0, import_firestore41.query)(q, (0, import_firestore41.startAfter)(lastDoc));
|
|
11250
13690
|
}
|
|
11251
|
-
const querySnapshot = await (0,
|
|
13691
|
+
const querySnapshot = await (0, import_firestore41.getDocs)(q);
|
|
11252
13692
|
const templates = [];
|
|
11253
13693
|
let lastVisible = null;
|
|
11254
|
-
querySnapshot.forEach((
|
|
11255
|
-
templates.push(
|
|
11256
|
-
lastVisible =
|
|
13694
|
+
querySnapshot.forEach((doc32) => {
|
|
13695
|
+
templates.push(doc32.data());
|
|
13696
|
+
lastVisible = doc32;
|
|
11257
13697
|
});
|
|
11258
13698
|
return {
|
|
11259
13699
|
templates,
|
|
@@ -11268,22 +13708,22 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11268
13708
|
* @returns Array of templates and the last document for pagination
|
|
11269
13709
|
*/
|
|
11270
13710
|
async getTemplatesByTags(tags, pageSize = 20, lastDoc) {
|
|
11271
|
-
let q = (0,
|
|
13711
|
+
let q = (0, import_firestore41.query)(
|
|
11272
13712
|
this.collectionRef,
|
|
11273
|
-
(0,
|
|
11274
|
-
(0,
|
|
11275
|
-
(0,
|
|
11276
|
-
(0,
|
|
13713
|
+
(0, import_firestore41.where)("isActive", "==", true),
|
|
13714
|
+
(0, import_firestore41.where)("tags", "array-contains-any", tags),
|
|
13715
|
+
(0, import_firestore41.orderBy)("updatedAt", "desc"),
|
|
13716
|
+
(0, import_firestore41.limit)(pageSize)
|
|
11277
13717
|
);
|
|
11278
13718
|
if (lastDoc) {
|
|
11279
|
-
q = (0,
|
|
13719
|
+
q = (0, import_firestore41.query)(q, (0, import_firestore41.startAfter)(lastDoc));
|
|
11280
13720
|
}
|
|
11281
|
-
const querySnapshot = await (0,
|
|
13721
|
+
const querySnapshot = await (0, import_firestore41.getDocs)(q);
|
|
11282
13722
|
const templates = [];
|
|
11283
13723
|
let lastVisible = null;
|
|
11284
|
-
querySnapshot.forEach((
|
|
11285
|
-
templates.push(
|
|
11286
|
-
lastVisible =
|
|
13724
|
+
querySnapshot.forEach((doc32) => {
|
|
13725
|
+
templates.push(doc32.data());
|
|
13726
|
+
lastVisible = doc32;
|
|
11287
13727
|
});
|
|
11288
13728
|
return {
|
|
11289
13729
|
templates,
|
|
@@ -11298,21 +13738,21 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11298
13738
|
* @returns Array of templates and the last document for pagination
|
|
11299
13739
|
*/
|
|
11300
13740
|
async getTemplatesByCreator(userId, pageSize = 20, lastDoc) {
|
|
11301
|
-
let q = (0,
|
|
13741
|
+
let q = (0, import_firestore41.query)(
|
|
11302
13742
|
this.collectionRef,
|
|
11303
|
-
(0,
|
|
11304
|
-
(0,
|
|
11305
|
-
(0,
|
|
13743
|
+
(0, import_firestore41.where)("createdBy", "==", userId),
|
|
13744
|
+
(0, import_firestore41.orderBy)("updatedAt", "desc"),
|
|
13745
|
+
(0, import_firestore41.limit)(pageSize)
|
|
11306
13746
|
);
|
|
11307
13747
|
if (lastDoc) {
|
|
11308
|
-
q = (0,
|
|
13748
|
+
q = (0, import_firestore41.query)(q, (0, import_firestore41.startAfter)(lastDoc));
|
|
11309
13749
|
}
|
|
11310
|
-
const querySnapshot = await (0,
|
|
13750
|
+
const querySnapshot = await (0, import_firestore41.getDocs)(q);
|
|
11311
13751
|
const templates = [];
|
|
11312
13752
|
let lastVisible = null;
|
|
11313
|
-
querySnapshot.forEach((
|
|
11314
|
-
templates.push(
|
|
11315
|
-
lastVisible =
|
|
13753
|
+
querySnapshot.forEach((doc32) => {
|
|
13754
|
+
templates.push(doc32.data());
|
|
13755
|
+
lastVisible = doc32;
|
|
11316
13756
|
});
|
|
11317
13757
|
return {
|
|
11318
13758
|
templates,
|
|
@@ -11325,28 +13765,28 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
11325
13765
|
* @returns Array of templates
|
|
11326
13766
|
*/
|
|
11327
13767
|
async getAllTemplatesForSelection(options) {
|
|
11328
|
-
let q = (0,
|
|
13768
|
+
let q = (0, import_firestore41.query)(
|
|
11329
13769
|
this.collectionRef,
|
|
11330
|
-
(0,
|
|
11331
|
-
(0,
|
|
13770
|
+
(0, import_firestore41.where)("isActive", "==", true),
|
|
13771
|
+
(0, import_firestore41.orderBy)("updatedAt", "desc")
|
|
11332
13772
|
);
|
|
11333
13773
|
if ((options == null ? void 0 : options.isUserForm) !== void 0) {
|
|
11334
|
-
q = (0,
|
|
13774
|
+
q = (0, import_firestore41.query)(q, (0, import_firestore41.where)("isUserForm", "==", options.isUserForm));
|
|
11335
13775
|
}
|
|
11336
13776
|
if ((options == null ? void 0 : options.isRequired) !== void 0) {
|
|
11337
|
-
q = (0,
|
|
13777
|
+
q = (0, import_firestore41.query)(q, (0, import_firestore41.where)("isRequired", "==", options.isRequired));
|
|
11338
13778
|
}
|
|
11339
|
-
const querySnapshot = await (0,
|
|
13779
|
+
const querySnapshot = await (0, import_firestore41.getDocs)(q);
|
|
11340
13780
|
const templates = [];
|
|
11341
|
-
querySnapshot.forEach((
|
|
11342
|
-
templates.push(
|
|
13781
|
+
querySnapshot.forEach((doc32) => {
|
|
13782
|
+
templates.push(doc32.data());
|
|
11343
13783
|
});
|
|
11344
13784
|
return templates;
|
|
11345
13785
|
}
|
|
11346
13786
|
};
|
|
11347
13787
|
|
|
11348
13788
|
// src/services/documentation-templates/filled-document.service.ts
|
|
11349
|
-
var
|
|
13789
|
+
var import_firestore42 = require("firebase/firestore");
|
|
11350
13790
|
var FilledDocumentService = class extends BaseService {
|
|
11351
13791
|
constructor(...args) {
|
|
11352
13792
|
super(...args);
|
|
@@ -11401,7 +13841,7 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11401
13841
|
values: initialValues,
|
|
11402
13842
|
status: initialStatus
|
|
11403
13843
|
};
|
|
11404
|
-
const docRef = (0,
|
|
13844
|
+
const docRef = (0, import_firestore42.doc)(
|
|
11405
13845
|
this.db,
|
|
11406
13846
|
APPOINTMENTS_COLLECTION,
|
|
11407
13847
|
// Replaced "appointments"
|
|
@@ -11409,7 +13849,7 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11409
13849
|
formSubcollection,
|
|
11410
13850
|
documentId3
|
|
11411
13851
|
);
|
|
11412
|
-
await (0,
|
|
13852
|
+
await (0, import_firestore42.setDoc)(docRef, filledDocument);
|
|
11413
13853
|
return filledDocument;
|
|
11414
13854
|
}
|
|
11415
13855
|
/**
|
|
@@ -11421,7 +13861,7 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11421
13861
|
*/
|
|
11422
13862
|
async getFilledDocumentFromAppointmentById(appointmentId, formId, isUserForm) {
|
|
11423
13863
|
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
11424
|
-
const docRef = (0,
|
|
13864
|
+
const docRef = (0, import_firestore42.doc)(
|
|
11425
13865
|
this.db,
|
|
11426
13866
|
APPOINTMENTS_COLLECTION,
|
|
11427
13867
|
// Replaced "appointments"
|
|
@@ -11429,7 +13869,7 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11429
13869
|
formSubcollection,
|
|
11430
13870
|
formId
|
|
11431
13871
|
);
|
|
11432
|
-
const docSnap = await (0,
|
|
13872
|
+
const docSnap = await (0, import_firestore42.getDoc)(docRef);
|
|
11433
13873
|
if (!docSnap.exists()) {
|
|
11434
13874
|
return null;
|
|
11435
13875
|
}
|
|
@@ -11446,7 +13886,7 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11446
13886
|
*/
|
|
11447
13887
|
async updateFilledDocumentInAppointment(appointmentId, formId, isUserForm, values, status) {
|
|
11448
13888
|
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
11449
|
-
const docRef = (0,
|
|
13889
|
+
const docRef = (0, import_firestore42.doc)(
|
|
11450
13890
|
this.db,
|
|
11451
13891
|
APPOINTMENTS_COLLECTION,
|
|
11452
13892
|
// Replaced "appointments"
|
|
@@ -11478,7 +13918,7 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11478
13918
|
}
|
|
11479
13919
|
if (Object.keys(updatePayload).length === 1 && "updatedAt" in updatePayload) {
|
|
11480
13920
|
}
|
|
11481
|
-
await (0,
|
|
13921
|
+
await (0, import_firestore42.updateDoc)(docRef, updatePayload);
|
|
11482
13922
|
return { ...existingDoc, ...updatePayload };
|
|
11483
13923
|
}
|
|
11484
13924
|
/**
|
|
@@ -11488,20 +13928,20 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11488
13928
|
* @param lastDoc Last document from previous page for pagination.
|
|
11489
13929
|
*/
|
|
11490
13930
|
async getFilledUserFormsForAppointment(appointmentId, pageSize = 20, lastDoc) {
|
|
11491
|
-
const subcollectionRef = (0,
|
|
13931
|
+
const subcollectionRef = (0, import_firestore42.collection)(
|
|
11492
13932
|
this.db,
|
|
11493
13933
|
APPOINTMENTS_COLLECTION,
|
|
11494
13934
|
// Replaced "appointments"
|
|
11495
13935
|
appointmentId,
|
|
11496
13936
|
USER_FORMS_SUBCOLLECTION
|
|
11497
13937
|
);
|
|
11498
|
-
let q = (0,
|
|
13938
|
+
let q = (0, import_firestore42.query)(
|
|
11499
13939
|
subcollectionRef,
|
|
11500
|
-
(0,
|
|
11501
|
-
(0,
|
|
13940
|
+
(0, import_firestore42.orderBy)("updatedAt", "desc"),
|
|
13941
|
+
(0, import_firestore42.limit)(pageSize)
|
|
11502
13942
|
);
|
|
11503
13943
|
if (lastDoc) {
|
|
11504
|
-
q = (0,
|
|
13944
|
+
q = (0, import_firestore42.query)(q, (0, import_firestore42.startAfter)(lastDoc));
|
|
11505
13945
|
}
|
|
11506
13946
|
return this.executeQuery(q);
|
|
11507
13947
|
}
|
|
@@ -11512,31 +13952,31 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11512
13952
|
* @param lastDoc Last document from previous page for pagination.
|
|
11513
13953
|
*/
|
|
11514
13954
|
async getFilledDoctorFormsForAppointment(appointmentId, pageSize = 20, lastDoc) {
|
|
11515
|
-
const subcollectionRef = (0,
|
|
13955
|
+
const subcollectionRef = (0, import_firestore42.collection)(
|
|
11516
13956
|
this.db,
|
|
11517
13957
|
APPOINTMENTS_COLLECTION,
|
|
11518
13958
|
// Replaced "appointments"
|
|
11519
13959
|
appointmentId,
|
|
11520
13960
|
DOCTOR_FORMS_SUBCOLLECTION
|
|
11521
13961
|
);
|
|
11522
|
-
let q = (0,
|
|
13962
|
+
let q = (0, import_firestore42.query)(
|
|
11523
13963
|
subcollectionRef,
|
|
11524
|
-
(0,
|
|
11525
|
-
(0,
|
|
13964
|
+
(0, import_firestore42.orderBy)("updatedAt", "desc"),
|
|
13965
|
+
(0, import_firestore42.limit)(pageSize)
|
|
11526
13966
|
);
|
|
11527
13967
|
if (lastDoc) {
|
|
11528
|
-
q = (0,
|
|
13968
|
+
q = (0, import_firestore42.query)(q, (0, import_firestore42.startAfter)(lastDoc));
|
|
11529
13969
|
}
|
|
11530
13970
|
return this.executeQuery(q);
|
|
11531
13971
|
}
|
|
11532
13972
|
// Helper to execute query and return documents + lastDoc
|
|
11533
13973
|
async executeQuery(q) {
|
|
11534
|
-
const querySnapshot = await (0,
|
|
13974
|
+
const querySnapshot = await (0, import_firestore42.getDocs)(q);
|
|
11535
13975
|
const documents = [];
|
|
11536
13976
|
let lastVisible = null;
|
|
11537
|
-
querySnapshot.forEach((
|
|
11538
|
-
documents.push(
|
|
11539
|
-
lastVisible =
|
|
13977
|
+
querySnapshot.forEach((doc32) => {
|
|
13978
|
+
documents.push(doc32.data());
|
|
13979
|
+
lastVisible = doc32;
|
|
11540
13980
|
});
|
|
11541
13981
|
return {
|
|
11542
13982
|
documents,
|
|
@@ -11695,14 +14135,14 @@ var FilledDocumentService = class extends BaseService {
|
|
|
11695
14135
|
};
|
|
11696
14136
|
|
|
11697
14137
|
// src/services/notifications/notification.service.ts
|
|
11698
|
-
var
|
|
14138
|
+
var import_firestore43 = require("firebase/firestore");
|
|
11699
14139
|
var NotificationService = class extends BaseService {
|
|
11700
14140
|
/**
|
|
11701
14141
|
* Kreira novu notifikaciju
|
|
11702
14142
|
*/
|
|
11703
14143
|
async createNotification(notification) {
|
|
11704
|
-
const notificationsRef = (0,
|
|
11705
|
-
const now =
|
|
14144
|
+
const notificationsRef = (0, import_firestore43.collection)(this.db, NOTIFICATIONS_COLLECTION);
|
|
14145
|
+
const now = import_firestore43.Timestamp.now();
|
|
11706
14146
|
const notificationData = {
|
|
11707
14147
|
...notification,
|
|
11708
14148
|
createdAt: now,
|
|
@@ -11711,7 +14151,7 @@ var NotificationService = class extends BaseService {
|
|
|
11711
14151
|
isRead: false,
|
|
11712
14152
|
userRole: notification.userRole || "patient" /* PATIENT */
|
|
11713
14153
|
};
|
|
11714
|
-
const docRef = await (0,
|
|
14154
|
+
const docRef = await (0, import_firestore43.addDoc)(notificationsRef, notificationData);
|
|
11715
14155
|
return {
|
|
11716
14156
|
...notificationData,
|
|
11717
14157
|
id: docRef.id
|
|
@@ -11721,12 +14161,12 @@ var NotificationService = class extends BaseService {
|
|
|
11721
14161
|
* Dohvata notifikaciju po ID-u
|
|
11722
14162
|
*/
|
|
11723
14163
|
async getNotification(notificationId) {
|
|
11724
|
-
const notificationRef = (0,
|
|
14164
|
+
const notificationRef = (0, import_firestore43.doc)(
|
|
11725
14165
|
this.db,
|
|
11726
14166
|
NOTIFICATIONS_COLLECTION,
|
|
11727
14167
|
notificationId
|
|
11728
14168
|
);
|
|
11729
|
-
const notificationDoc = await (0,
|
|
14169
|
+
const notificationDoc = await (0, import_firestore43.getDoc)(notificationRef);
|
|
11730
14170
|
if (!notificationDoc.exists()) {
|
|
11731
14171
|
return null;
|
|
11732
14172
|
}
|
|
@@ -11739,45 +14179,45 @@ var NotificationService = class extends BaseService {
|
|
|
11739
14179
|
* Dohvata sve notifikacije za korisnika
|
|
11740
14180
|
*/
|
|
11741
14181
|
async getUserNotifications(userId) {
|
|
11742
|
-
const q = (0,
|
|
11743
|
-
(0,
|
|
11744
|
-
(0,
|
|
11745
|
-
(0,
|
|
11746
|
-
);
|
|
11747
|
-
const querySnapshot = await (0,
|
|
11748
|
-
return querySnapshot.docs.map((
|
|
11749
|
-
id:
|
|
11750
|
-
...
|
|
14182
|
+
const q = (0, import_firestore43.query)(
|
|
14183
|
+
(0, import_firestore43.collection)(this.db, NOTIFICATIONS_COLLECTION),
|
|
14184
|
+
(0, import_firestore43.where)("userId", "==", userId),
|
|
14185
|
+
(0, import_firestore43.orderBy)("notificationTime", "desc")
|
|
14186
|
+
);
|
|
14187
|
+
const querySnapshot = await (0, import_firestore43.getDocs)(q);
|
|
14188
|
+
return querySnapshot.docs.map((doc32) => ({
|
|
14189
|
+
id: doc32.id,
|
|
14190
|
+
...doc32.data()
|
|
11751
14191
|
}));
|
|
11752
14192
|
}
|
|
11753
14193
|
/**
|
|
11754
14194
|
* Dohvata nepročitane notifikacije za korisnika
|
|
11755
14195
|
*/
|
|
11756
14196
|
async getUnreadNotifications(userId) {
|
|
11757
|
-
const q = (0,
|
|
11758
|
-
(0,
|
|
11759
|
-
(0,
|
|
11760
|
-
(0,
|
|
11761
|
-
(0,
|
|
11762
|
-
);
|
|
11763
|
-
const querySnapshot = await (0,
|
|
11764
|
-
return querySnapshot.docs.map((
|
|
11765
|
-
id:
|
|
11766
|
-
...
|
|
14197
|
+
const q = (0, import_firestore43.query)(
|
|
14198
|
+
(0, import_firestore43.collection)(this.db, NOTIFICATIONS_COLLECTION),
|
|
14199
|
+
(0, import_firestore43.where)("userId", "==", userId),
|
|
14200
|
+
(0, import_firestore43.where)("isRead", "==", false),
|
|
14201
|
+
(0, import_firestore43.orderBy)("notificationTime", "desc")
|
|
14202
|
+
);
|
|
14203
|
+
const querySnapshot = await (0, import_firestore43.getDocs)(q);
|
|
14204
|
+
return querySnapshot.docs.map((doc32) => ({
|
|
14205
|
+
id: doc32.id,
|
|
14206
|
+
...doc32.data()
|
|
11767
14207
|
}));
|
|
11768
14208
|
}
|
|
11769
14209
|
/**
|
|
11770
14210
|
* Označava notifikaciju kao pročitanu
|
|
11771
14211
|
*/
|
|
11772
14212
|
async markAsRead(notificationId) {
|
|
11773
|
-
const notificationRef = (0,
|
|
14213
|
+
const notificationRef = (0, import_firestore43.doc)(
|
|
11774
14214
|
this.db,
|
|
11775
14215
|
NOTIFICATIONS_COLLECTION,
|
|
11776
14216
|
notificationId
|
|
11777
14217
|
);
|
|
11778
|
-
await (0,
|
|
14218
|
+
await (0, import_firestore43.updateDoc)(notificationRef, {
|
|
11779
14219
|
isRead: true,
|
|
11780
|
-
updatedAt:
|
|
14220
|
+
updatedAt: import_firestore43.Timestamp.now()
|
|
11781
14221
|
});
|
|
11782
14222
|
}
|
|
11783
14223
|
/**
|
|
@@ -11785,16 +14225,16 @@ var NotificationService = class extends BaseService {
|
|
|
11785
14225
|
*/
|
|
11786
14226
|
async markAllAsRead(userId) {
|
|
11787
14227
|
const notifications = await this.getUnreadNotifications(userId);
|
|
11788
|
-
const batch = (0,
|
|
14228
|
+
const batch = (0, import_firestore43.writeBatch)(this.db);
|
|
11789
14229
|
notifications.forEach((notification) => {
|
|
11790
|
-
const notificationRef = (0,
|
|
14230
|
+
const notificationRef = (0, import_firestore43.doc)(
|
|
11791
14231
|
this.db,
|
|
11792
14232
|
NOTIFICATIONS_COLLECTION,
|
|
11793
14233
|
notification.id
|
|
11794
14234
|
);
|
|
11795
14235
|
batch.update(notificationRef, {
|
|
11796
14236
|
isRead: true,
|
|
11797
|
-
updatedAt:
|
|
14237
|
+
updatedAt: import_firestore43.Timestamp.now()
|
|
11798
14238
|
});
|
|
11799
14239
|
});
|
|
11800
14240
|
await batch.commit();
|
|
@@ -11803,74 +14243,74 @@ var NotificationService = class extends BaseService {
|
|
|
11803
14243
|
* Ažurira status notifikacije
|
|
11804
14244
|
*/
|
|
11805
14245
|
async updateNotificationStatus(notificationId, status) {
|
|
11806
|
-
const notificationRef = (0,
|
|
14246
|
+
const notificationRef = (0, import_firestore43.doc)(
|
|
11807
14247
|
this.db,
|
|
11808
14248
|
NOTIFICATIONS_COLLECTION,
|
|
11809
14249
|
notificationId
|
|
11810
14250
|
);
|
|
11811
|
-
await (0,
|
|
14251
|
+
await (0, import_firestore43.updateDoc)(notificationRef, {
|
|
11812
14252
|
status,
|
|
11813
|
-
updatedAt:
|
|
14253
|
+
updatedAt: import_firestore43.Timestamp.now()
|
|
11814
14254
|
});
|
|
11815
14255
|
}
|
|
11816
14256
|
/**
|
|
11817
14257
|
* Briše notifikaciju
|
|
11818
14258
|
*/
|
|
11819
14259
|
async deleteNotification(notificationId) {
|
|
11820
|
-
const notificationRef = (0,
|
|
14260
|
+
const notificationRef = (0, import_firestore43.doc)(
|
|
11821
14261
|
this.db,
|
|
11822
14262
|
NOTIFICATIONS_COLLECTION,
|
|
11823
14263
|
notificationId
|
|
11824
14264
|
);
|
|
11825
|
-
await (0,
|
|
14265
|
+
await (0, import_firestore43.deleteDoc)(notificationRef);
|
|
11826
14266
|
}
|
|
11827
14267
|
/**
|
|
11828
14268
|
* Dohvata notifikacije po tipu
|
|
11829
14269
|
*/
|
|
11830
14270
|
async getNotificationsByType(userId, type) {
|
|
11831
|
-
const q = (0,
|
|
11832
|
-
(0,
|
|
11833
|
-
(0,
|
|
11834
|
-
(0,
|
|
11835
|
-
(0,
|
|
11836
|
-
);
|
|
11837
|
-
const querySnapshot = await (0,
|
|
11838
|
-
return querySnapshot.docs.map((
|
|
11839
|
-
id:
|
|
11840
|
-
...
|
|
14271
|
+
const q = (0, import_firestore43.query)(
|
|
14272
|
+
(0, import_firestore43.collection)(this.db, NOTIFICATIONS_COLLECTION),
|
|
14273
|
+
(0, import_firestore43.where)("userId", "==", userId),
|
|
14274
|
+
(0, import_firestore43.where)("notificationType", "==", type),
|
|
14275
|
+
(0, import_firestore43.orderBy)("notificationTime", "desc")
|
|
14276
|
+
);
|
|
14277
|
+
const querySnapshot = await (0, import_firestore43.getDocs)(q);
|
|
14278
|
+
return querySnapshot.docs.map((doc32) => ({
|
|
14279
|
+
id: doc32.id,
|
|
14280
|
+
...doc32.data()
|
|
11841
14281
|
}));
|
|
11842
14282
|
}
|
|
11843
14283
|
/**
|
|
11844
14284
|
* Dohvata notifikacije za određeni termin
|
|
11845
14285
|
*/
|
|
11846
14286
|
async getAppointmentNotifications(appointmentId) {
|
|
11847
|
-
const q = (0,
|
|
11848
|
-
(0,
|
|
11849
|
-
(0,
|
|
11850
|
-
(0,
|
|
11851
|
-
);
|
|
11852
|
-
const querySnapshot = await (0,
|
|
11853
|
-
return querySnapshot.docs.map((
|
|
11854
|
-
id:
|
|
11855
|
-
...
|
|
14287
|
+
const q = (0, import_firestore43.query)(
|
|
14288
|
+
(0, import_firestore43.collection)(this.db, NOTIFICATIONS_COLLECTION),
|
|
14289
|
+
(0, import_firestore43.where)("appointmentId", "==", appointmentId),
|
|
14290
|
+
(0, import_firestore43.orderBy)("notificationTime", "desc")
|
|
14291
|
+
);
|
|
14292
|
+
const querySnapshot = await (0, import_firestore43.getDocs)(q);
|
|
14293
|
+
return querySnapshot.docs.map((doc32) => ({
|
|
14294
|
+
id: doc32.id,
|
|
14295
|
+
...doc32.data()
|
|
11856
14296
|
}));
|
|
11857
14297
|
}
|
|
11858
14298
|
};
|
|
11859
14299
|
|
|
11860
14300
|
// src/services/patient/patientRequirements.service.ts
|
|
11861
|
-
var
|
|
14301
|
+
var import_firestore44 = require("firebase/firestore");
|
|
11862
14302
|
var PatientRequirementsService = class extends BaseService {
|
|
11863
14303
|
constructor(db, auth, app) {
|
|
11864
14304
|
super(db, auth, app);
|
|
11865
14305
|
}
|
|
11866
14306
|
getPatientRequirementsCollectionRef(patientId) {
|
|
11867
|
-
return (0,
|
|
14307
|
+
return (0, import_firestore44.collection)(
|
|
11868
14308
|
this.db,
|
|
11869
14309
|
`patients/${patientId}/${PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME}`
|
|
11870
14310
|
);
|
|
11871
14311
|
}
|
|
11872
14312
|
getPatientRequirementDocRef(patientId, instanceId) {
|
|
11873
|
-
return (0,
|
|
14313
|
+
return (0, import_firestore44.doc)(
|
|
11874
14314
|
this.getPatientRequirementsCollectionRef(patientId),
|
|
11875
14315
|
instanceId
|
|
11876
14316
|
);
|
|
@@ -11883,7 +14323,7 @@ var PatientRequirementsService = class extends BaseService {
|
|
|
11883
14323
|
*/
|
|
11884
14324
|
async getPatientRequirementInstance(patientId, instanceId) {
|
|
11885
14325
|
const docRef = this.getPatientRequirementDocRef(patientId, instanceId);
|
|
11886
|
-
const docSnap = await (0,
|
|
14326
|
+
const docSnap = await (0, import_firestore44.getDoc)(docRef);
|
|
11887
14327
|
if (!docSnap.exists()) {
|
|
11888
14328
|
return null;
|
|
11889
14329
|
}
|
|
@@ -11902,22 +14342,22 @@ var PatientRequirementsService = class extends BaseService {
|
|
|
11902
14342
|
*/
|
|
11903
14343
|
async getAllPatientRequirementInstances(patientId, filters, pageLimit = 20, lastVisible) {
|
|
11904
14344
|
const collRef = this.getPatientRequirementsCollectionRef(patientId);
|
|
11905
|
-
let q = (0,
|
|
14345
|
+
let q = (0, import_firestore44.query)(collRef, (0, import_firestore44.orderBy)("createdAt", "desc"));
|
|
11906
14346
|
const queryConstraints = [];
|
|
11907
14347
|
if ((filters == null ? void 0 : filters.appointmentId) && filters.appointmentId !== "all") {
|
|
11908
14348
|
queryConstraints.push(
|
|
11909
|
-
(0,
|
|
14349
|
+
(0, import_firestore44.where)("appointmentId", "==", filters.appointmentId)
|
|
11910
14350
|
);
|
|
11911
14351
|
}
|
|
11912
14352
|
if ((filters == null ? void 0 : filters.statuses) && filters.statuses.length > 0) {
|
|
11913
|
-
queryConstraints.push((0,
|
|
14353
|
+
queryConstraints.push((0, import_firestore44.where)("overallStatus", "in", filters.statuses));
|
|
11914
14354
|
}
|
|
11915
14355
|
if (lastVisible) {
|
|
11916
|
-
queryConstraints.push((0,
|
|
14356
|
+
queryConstraints.push((0, import_firestore44.startAfter)(lastVisible));
|
|
11917
14357
|
}
|
|
11918
|
-
queryConstraints.push((0,
|
|
11919
|
-
q = (0,
|
|
11920
|
-
const snapshot = await (0,
|
|
14358
|
+
queryConstraints.push((0, import_firestore44.limit)(pageLimit));
|
|
14359
|
+
q = (0, import_firestore44.query)(collRef, ...queryConstraints);
|
|
14360
|
+
const snapshot = await (0, import_firestore44.getDocs)(q);
|
|
11921
14361
|
let requirements = snapshot.docs.map((docSnap) => {
|
|
11922
14362
|
const data = docSnap.data();
|
|
11923
14363
|
return { id: docSnap.id, ...data };
|
|
@@ -11960,7 +14400,7 @@ var PatientRequirementsService = class extends BaseService {
|
|
|
11960
14400
|
*/
|
|
11961
14401
|
async completeInstruction(patientId, instanceId, instructionId) {
|
|
11962
14402
|
const instanceRef = this.getPatientRequirementDocRef(patientId, instanceId);
|
|
11963
|
-
const instanceSnap = await (0,
|
|
14403
|
+
const instanceSnap = await (0, import_firestore44.getDoc)(instanceRef);
|
|
11964
14404
|
if (!instanceSnap.exists()) {
|
|
11965
14405
|
throw new Error(
|
|
11966
14406
|
`PatientRequirementInstance ${instanceId} not found for patient ${patientId}.`
|
|
@@ -11988,7 +14428,7 @@ var PatientRequirementsService = class extends BaseService {
|
|
|
11988
14428
|
`Instruction ${instructionId} is in status ${instructionToUpdate.status} and cannot be marked as completed.`
|
|
11989
14429
|
);
|
|
11990
14430
|
}
|
|
11991
|
-
const now =
|
|
14431
|
+
const now = import_firestore44.Timestamp.now();
|
|
11992
14432
|
const updatedInstructions = [...instance.instructions];
|
|
11993
14433
|
updatedInstructions[instructionIndex] = {
|
|
11994
14434
|
...instructionToUpdate,
|
|
@@ -12015,7 +14455,7 @@ var PatientRequirementsService = class extends BaseService {
|
|
|
12015
14455
|
if (newOverallStatus !== instance.overallStatus) {
|
|
12016
14456
|
updatePayload.overallStatus = newOverallStatus;
|
|
12017
14457
|
}
|
|
12018
|
-
await (0,
|
|
14458
|
+
await (0, import_firestore44.updateDoc)(instanceRef, updatePayload);
|
|
12019
14459
|
return {
|
|
12020
14460
|
...instance,
|
|
12021
14461
|
instructions: updatedInstructions,
|
|
@@ -12028,7 +14468,7 @@ var PatientRequirementsService = class extends BaseService {
|
|
|
12028
14468
|
};
|
|
12029
14469
|
|
|
12030
14470
|
// src/services/procedure/procedure.service.ts
|
|
12031
|
-
var
|
|
14471
|
+
var import_firestore45 = require("firebase/firestore");
|
|
12032
14472
|
|
|
12033
14473
|
// src/validations/procedure.schema.ts
|
|
12034
14474
|
var import_zod24 = require("zod");
|
|
@@ -12183,24 +14623,24 @@ var ProcedureService = class extends BaseService {
|
|
|
12183
14623
|
if (!category || !subcategory || !technology || !product) {
|
|
12184
14624
|
throw new Error("One or more required base entities not found");
|
|
12185
14625
|
}
|
|
12186
|
-
const clinicRef = (0,
|
|
14626
|
+
const clinicRef = (0, import_firestore45.doc)(
|
|
12187
14627
|
this.db,
|
|
12188
14628
|
CLINICS_COLLECTION,
|
|
12189
14629
|
validatedData.clinicBranchId
|
|
12190
14630
|
);
|
|
12191
|
-
const clinicSnapshot = await (0,
|
|
14631
|
+
const clinicSnapshot = await (0, import_firestore45.getDoc)(clinicRef);
|
|
12192
14632
|
if (!clinicSnapshot.exists()) {
|
|
12193
14633
|
throw new Error(
|
|
12194
14634
|
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
12195
14635
|
);
|
|
12196
14636
|
}
|
|
12197
14637
|
const clinic = clinicSnapshot.data();
|
|
12198
|
-
const practitionerRef = (0,
|
|
14638
|
+
const practitionerRef = (0, import_firestore45.doc)(
|
|
12199
14639
|
this.db,
|
|
12200
14640
|
PRACTITIONERS_COLLECTION,
|
|
12201
14641
|
validatedData.practitionerId
|
|
12202
14642
|
);
|
|
12203
|
-
const practitionerSnapshot = await (0,
|
|
14643
|
+
const practitionerSnapshot = await (0, import_firestore45.getDoc)(practitionerRef);
|
|
12204
14644
|
if (!practitionerSnapshot.exists()) {
|
|
12205
14645
|
throw new Error(
|
|
12206
14646
|
`Practitioner with ID ${validatedData.practitionerId} not found`
|
|
@@ -12266,13 +14706,13 @@ var ProcedureService = class extends BaseService {
|
|
|
12266
14706
|
isActive: true
|
|
12267
14707
|
// Default to active
|
|
12268
14708
|
};
|
|
12269
|
-
const procedureRef = (0,
|
|
12270
|
-
await (0,
|
|
14709
|
+
const procedureRef = (0, import_firestore45.doc)(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
14710
|
+
await (0, import_firestore45.setDoc)(procedureRef, {
|
|
12271
14711
|
...newProcedure,
|
|
12272
|
-
createdAt: (0,
|
|
12273
|
-
updatedAt: (0,
|
|
14712
|
+
createdAt: (0, import_firestore45.serverTimestamp)(),
|
|
14713
|
+
updatedAt: (0, import_firestore45.serverTimestamp)()
|
|
12274
14714
|
});
|
|
12275
|
-
const savedDoc = await (0,
|
|
14715
|
+
const savedDoc = await (0, import_firestore45.getDoc)(procedureRef);
|
|
12276
14716
|
return savedDoc.data();
|
|
12277
14717
|
}
|
|
12278
14718
|
/**
|
|
@@ -12301,7 +14741,7 @@ var ProcedureService = class extends BaseService {
|
|
|
12301
14741
|
validatedData.technologyId,
|
|
12302
14742
|
validatedData.productId
|
|
12303
14743
|
),
|
|
12304
|
-
(0,
|
|
14744
|
+
(0, import_firestore45.getDoc)((0, import_firestore45.doc)(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
|
|
12305
14745
|
]);
|
|
12306
14746
|
if (!category || !subcategory || !technology || !product) {
|
|
12307
14747
|
throw new Error("One or more required base entities not found");
|
|
@@ -12324,13 +14764,13 @@ var ProcedureService = class extends BaseService {
|
|
|
12324
14764
|
const practitionersMap = /* @__PURE__ */ new Map();
|
|
12325
14765
|
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
12326
14766
|
const chunk = practitionerIds.slice(i, i + 30);
|
|
12327
|
-
const practitionersQuery = (0,
|
|
12328
|
-
(0,
|
|
12329
|
-
(0,
|
|
14767
|
+
const practitionersQuery = (0, import_firestore45.query)(
|
|
14768
|
+
(0, import_firestore45.collection)(this.db, PRACTITIONERS_COLLECTION),
|
|
14769
|
+
(0, import_firestore45.where)((0, import_firestore45.documentId)(), "in", chunk)
|
|
12330
14770
|
);
|
|
12331
|
-
const practitionersSnapshot = await (0,
|
|
12332
|
-
practitionersSnapshot.docs.forEach((
|
|
12333
|
-
practitionersMap.set(
|
|
14771
|
+
const practitionersSnapshot = await (0, import_firestore45.getDocs)(practitionersQuery);
|
|
14772
|
+
practitionersSnapshot.docs.forEach((doc32) => {
|
|
14773
|
+
practitionersMap.set(doc32.id, doc32.data());
|
|
12334
14774
|
});
|
|
12335
14775
|
}
|
|
12336
14776
|
if (practitionersMap.size !== practitionerIds.length) {
|
|
@@ -12342,7 +14782,7 @@ var ProcedureService = class extends BaseService {
|
|
|
12342
14782
|
`The following practitioners were not found: ${notFoundIds.join(", ")}`
|
|
12343
14783
|
);
|
|
12344
14784
|
}
|
|
12345
|
-
const batch = (0,
|
|
14785
|
+
const batch = (0, import_firestore45.writeBatch)(this.db);
|
|
12346
14786
|
const createdProcedureIds = [];
|
|
12347
14787
|
const clinicInfo = {
|
|
12348
14788
|
id: clinicSnapshot.id,
|
|
@@ -12364,7 +14804,7 @@ var ProcedureService = class extends BaseService {
|
|
|
12364
14804
|
};
|
|
12365
14805
|
const procedureId = this.generateId();
|
|
12366
14806
|
createdProcedureIds.push(procedureId);
|
|
12367
|
-
const procedureRef = (0,
|
|
14807
|
+
const procedureRef = (0, import_firestore45.doc)(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
12368
14808
|
const newProcedure = {
|
|
12369
14809
|
id: procedureId,
|
|
12370
14810
|
...validatedData,
|
|
@@ -12399,21 +14839,21 @@ var ProcedureService = class extends BaseService {
|
|
|
12399
14839
|
};
|
|
12400
14840
|
batch.set(procedureRef, {
|
|
12401
14841
|
...newProcedure,
|
|
12402
|
-
createdAt: (0,
|
|
12403
|
-
updatedAt: (0,
|
|
14842
|
+
createdAt: (0, import_firestore45.serverTimestamp)(),
|
|
14843
|
+
updatedAt: (0, import_firestore45.serverTimestamp)()
|
|
12404
14844
|
});
|
|
12405
14845
|
}
|
|
12406
14846
|
await batch.commit();
|
|
12407
14847
|
const fetchedProcedures = [];
|
|
12408
14848
|
for (let i = 0; i < createdProcedureIds.length; i += 30) {
|
|
12409
14849
|
const chunk = createdProcedureIds.slice(i, i + 30);
|
|
12410
|
-
const q = (0,
|
|
12411
|
-
(0,
|
|
12412
|
-
(0,
|
|
14850
|
+
const q = (0, import_firestore45.query)(
|
|
14851
|
+
(0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
|
|
14852
|
+
(0, import_firestore45.where)((0, import_firestore45.documentId)(), "in", chunk)
|
|
12413
14853
|
);
|
|
12414
|
-
const snapshot = await (0,
|
|
12415
|
-
snapshot.forEach((
|
|
12416
|
-
fetchedProcedures.push(
|
|
14854
|
+
const snapshot = await (0, import_firestore45.getDocs)(q);
|
|
14855
|
+
snapshot.forEach((doc32) => {
|
|
14856
|
+
fetchedProcedures.push(doc32.data());
|
|
12417
14857
|
});
|
|
12418
14858
|
}
|
|
12419
14859
|
return fetchedProcedures;
|
|
@@ -12424,8 +14864,8 @@ var ProcedureService = class extends BaseService {
|
|
|
12424
14864
|
* @returns The procedure if found, null otherwise
|
|
12425
14865
|
*/
|
|
12426
14866
|
async getProcedure(id) {
|
|
12427
|
-
const docRef = (0,
|
|
12428
|
-
const docSnap = await (0,
|
|
14867
|
+
const docRef = (0, import_firestore45.doc)(this.db, PROCEDURES_COLLECTION, id);
|
|
14868
|
+
const docSnap = await (0, import_firestore45.getDoc)(docRef);
|
|
12429
14869
|
if (!docSnap.exists()) {
|
|
12430
14870
|
return null;
|
|
12431
14871
|
}
|
|
@@ -12437,13 +14877,13 @@ var ProcedureService = class extends BaseService {
|
|
|
12437
14877
|
* @returns List of procedures
|
|
12438
14878
|
*/
|
|
12439
14879
|
async getProceduresByClinicBranch(clinicBranchId) {
|
|
12440
|
-
const q = (0,
|
|
12441
|
-
(0,
|
|
12442
|
-
(0,
|
|
12443
|
-
(0,
|
|
14880
|
+
const q = (0, import_firestore45.query)(
|
|
14881
|
+
(0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
|
|
14882
|
+
(0, import_firestore45.where)("clinicBranchId", "==", clinicBranchId),
|
|
14883
|
+
(0, import_firestore45.where)("isActive", "==", true)
|
|
12444
14884
|
);
|
|
12445
|
-
const snapshot = await (0,
|
|
12446
|
-
return snapshot.docs.map((
|
|
14885
|
+
const snapshot = await (0, import_firestore45.getDocs)(q);
|
|
14886
|
+
return snapshot.docs.map((doc32) => doc32.data());
|
|
12447
14887
|
}
|
|
12448
14888
|
/**
|
|
12449
14889
|
* Gets all procedures for a practitioner
|
|
@@ -12451,13 +14891,13 @@ var ProcedureService = class extends BaseService {
|
|
|
12451
14891
|
* @returns List of procedures
|
|
12452
14892
|
*/
|
|
12453
14893
|
async getProceduresByPractitioner(practitionerId) {
|
|
12454
|
-
const q = (0,
|
|
12455
|
-
(0,
|
|
12456
|
-
(0,
|
|
12457
|
-
(0,
|
|
14894
|
+
const q = (0, import_firestore45.query)(
|
|
14895
|
+
(0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
|
|
14896
|
+
(0, import_firestore45.where)("practitionerId", "==", practitionerId),
|
|
14897
|
+
(0, import_firestore45.where)("isActive", "==", true)
|
|
12458
14898
|
);
|
|
12459
|
-
const snapshot = await (0,
|
|
12460
|
-
return snapshot.docs.map((
|
|
14899
|
+
const snapshot = await (0, import_firestore45.getDocs)(q);
|
|
14900
|
+
return snapshot.docs.map((doc32) => doc32.data());
|
|
12461
14901
|
}
|
|
12462
14902
|
/**
|
|
12463
14903
|
* Gets all inactive procedures for a practitioner
|
|
@@ -12465,13 +14905,13 @@ var ProcedureService = class extends BaseService {
|
|
|
12465
14905
|
* @returns List of inactive procedures
|
|
12466
14906
|
*/
|
|
12467
14907
|
async getInactiveProceduresByPractitioner(practitionerId) {
|
|
12468
|
-
const q = (0,
|
|
12469
|
-
(0,
|
|
12470
|
-
(0,
|
|
12471
|
-
(0,
|
|
14908
|
+
const q = (0, import_firestore45.query)(
|
|
14909
|
+
(0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
|
|
14910
|
+
(0, import_firestore45.where)("practitionerId", "==", practitionerId),
|
|
14911
|
+
(0, import_firestore45.where)("isActive", "==", false)
|
|
12472
14912
|
);
|
|
12473
|
-
const snapshot = await (0,
|
|
12474
|
-
return snapshot.docs.map((
|
|
14913
|
+
const snapshot = await (0, import_firestore45.getDocs)(q);
|
|
14914
|
+
return snapshot.docs.map((doc32) => doc32.data());
|
|
12475
14915
|
}
|
|
12476
14916
|
/**
|
|
12477
14917
|
* Updates a procedure
|
|
@@ -12482,8 +14922,8 @@ var ProcedureService = class extends BaseService {
|
|
|
12482
14922
|
async updateProcedure(id, data) {
|
|
12483
14923
|
var _a;
|
|
12484
14924
|
const validatedData = updateProcedureSchema.parse(data);
|
|
12485
|
-
const procedureRef = (0,
|
|
12486
|
-
const procedureSnapshot = await (0,
|
|
14925
|
+
const procedureRef = (0, import_firestore45.doc)(this.db, PROCEDURES_COLLECTION, id);
|
|
14926
|
+
const procedureSnapshot = await (0, import_firestore45.getDoc)(procedureRef);
|
|
12487
14927
|
if (!procedureSnapshot.exists()) {
|
|
12488
14928
|
throw new Error(`Procedure with ID ${id} not found`);
|
|
12489
14929
|
}
|
|
@@ -12504,12 +14944,12 @@ var ProcedureService = class extends BaseService {
|
|
|
12504
14944
|
}
|
|
12505
14945
|
if (validatedData.practitionerId && validatedData.practitionerId !== oldPractitionerId) {
|
|
12506
14946
|
practitionerChanged = true;
|
|
12507
|
-
const newPractitionerRef = (0,
|
|
14947
|
+
const newPractitionerRef = (0, import_firestore45.doc)(
|
|
12508
14948
|
this.db,
|
|
12509
14949
|
PRACTITIONERS_COLLECTION,
|
|
12510
14950
|
validatedData.practitionerId
|
|
12511
14951
|
);
|
|
12512
|
-
const newPractitionerSnap = await (0,
|
|
14952
|
+
const newPractitionerSnap = await (0, import_firestore45.getDoc)(newPractitionerRef);
|
|
12513
14953
|
if (!newPractitionerSnap.exists())
|
|
12514
14954
|
throw new Error(
|
|
12515
14955
|
`New Practitioner ${validatedData.practitionerId} not found`
|
|
@@ -12527,12 +14967,12 @@ var ProcedureService = class extends BaseService {
|
|
|
12527
14967
|
}
|
|
12528
14968
|
if (validatedData.clinicBranchId && validatedData.clinicBranchId !== oldClinicId) {
|
|
12529
14969
|
clinicChanged = true;
|
|
12530
|
-
const newClinicRef = (0,
|
|
14970
|
+
const newClinicRef = (0, import_firestore45.doc)(
|
|
12531
14971
|
this.db,
|
|
12532
14972
|
CLINICS_COLLECTION,
|
|
12533
14973
|
validatedData.clinicBranchId
|
|
12534
14974
|
);
|
|
12535
|
-
const newClinicSnap = await (0,
|
|
14975
|
+
const newClinicSnap = await (0, import_firestore45.getDoc)(newClinicRef);
|
|
12536
14976
|
if (!newClinicSnap.exists())
|
|
12537
14977
|
throw new Error(`New Clinic ${validatedData.clinicBranchId} not found`);
|
|
12538
14978
|
newClinic = newClinicSnap.data();
|
|
@@ -12599,11 +15039,11 @@ var ProcedureService = class extends BaseService {
|
|
|
12599
15039
|
} else if (validatedData.productId) {
|
|
12600
15040
|
console.warn("Attempted to update product without a valid technologyId");
|
|
12601
15041
|
}
|
|
12602
|
-
await (0,
|
|
15042
|
+
await (0, import_firestore45.updateDoc)(procedureRef, {
|
|
12603
15043
|
...updatedProcedureData,
|
|
12604
|
-
updatedAt: (0,
|
|
15044
|
+
updatedAt: (0, import_firestore45.serverTimestamp)()
|
|
12605
15045
|
});
|
|
12606
|
-
const updatedSnapshot = await (0,
|
|
15046
|
+
const updatedSnapshot = await (0, import_firestore45.getDoc)(procedureRef);
|
|
12607
15047
|
return updatedSnapshot.data();
|
|
12608
15048
|
}
|
|
12609
15049
|
/**
|
|
@@ -12611,15 +15051,15 @@ var ProcedureService = class extends BaseService {
|
|
|
12611
15051
|
* @param id - The ID of the procedure to deactivate
|
|
12612
15052
|
*/
|
|
12613
15053
|
async deactivateProcedure(id) {
|
|
12614
|
-
const procedureRef = (0,
|
|
12615
|
-
const procedureSnap = await (0,
|
|
15054
|
+
const procedureRef = (0, import_firestore45.doc)(this.db, PROCEDURES_COLLECTION, id);
|
|
15055
|
+
const procedureSnap = await (0, import_firestore45.getDoc)(procedureRef);
|
|
12616
15056
|
if (!procedureSnap.exists()) {
|
|
12617
15057
|
console.warn(`Procedure ${id} not found for deactivation.`);
|
|
12618
15058
|
return;
|
|
12619
15059
|
}
|
|
12620
|
-
await (0,
|
|
15060
|
+
await (0, import_firestore45.updateDoc)(procedureRef, {
|
|
12621
15061
|
isActive: false,
|
|
12622
|
-
updatedAt: (0,
|
|
15062
|
+
updatedAt: (0, import_firestore45.serverTimestamp)()
|
|
12623
15063
|
});
|
|
12624
15064
|
}
|
|
12625
15065
|
/**
|
|
@@ -12628,12 +15068,12 @@ var ProcedureService = class extends BaseService {
|
|
|
12628
15068
|
* @returns A boolean indicating if the deletion was successful
|
|
12629
15069
|
*/
|
|
12630
15070
|
async deleteProcedure(id) {
|
|
12631
|
-
const procedureRef = (0,
|
|
12632
|
-
const procedureSnapshot = await (0,
|
|
15071
|
+
const procedureRef = (0, import_firestore45.doc)(this.db, PROCEDURES_COLLECTION, id);
|
|
15072
|
+
const procedureSnapshot = await (0, import_firestore45.getDoc)(procedureRef);
|
|
12633
15073
|
if (!procedureSnapshot.exists()) {
|
|
12634
15074
|
return false;
|
|
12635
15075
|
}
|
|
12636
|
-
await (0,
|
|
15076
|
+
await (0, import_firestore45.deleteDoc)(procedureRef);
|
|
12637
15077
|
return true;
|
|
12638
15078
|
}
|
|
12639
15079
|
/**
|
|
@@ -12659,35 +15099,35 @@ var ProcedureService = class extends BaseService {
|
|
|
12659
15099
|
*/
|
|
12660
15100
|
async getAllProcedures(pagination, lastDoc) {
|
|
12661
15101
|
try {
|
|
12662
|
-
const proceduresCollection = (0,
|
|
12663
|
-
let proceduresQuery = (0,
|
|
15102
|
+
const proceduresCollection = (0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION);
|
|
15103
|
+
let proceduresQuery = (0, import_firestore45.query)(proceduresCollection);
|
|
12664
15104
|
if (pagination && pagination > 0) {
|
|
12665
15105
|
const { limit: limit16, startAfter: startAfter14 } = await import("firebase/firestore");
|
|
12666
15106
|
if (lastDoc) {
|
|
12667
|
-
proceduresQuery = (0,
|
|
15107
|
+
proceduresQuery = (0, import_firestore45.query)(
|
|
12668
15108
|
proceduresCollection,
|
|
12669
|
-
(0,
|
|
15109
|
+
(0, import_firestore45.orderBy)("name"),
|
|
12670
15110
|
// Use imported orderBy
|
|
12671
15111
|
startAfter14(lastDoc),
|
|
12672
15112
|
limit16(pagination)
|
|
12673
15113
|
);
|
|
12674
15114
|
} else {
|
|
12675
|
-
proceduresQuery = (0,
|
|
15115
|
+
proceduresQuery = (0, import_firestore45.query)(
|
|
12676
15116
|
proceduresCollection,
|
|
12677
|
-
(0,
|
|
15117
|
+
(0, import_firestore45.orderBy)("name"),
|
|
12678
15118
|
limit16(pagination)
|
|
12679
15119
|
);
|
|
12680
15120
|
}
|
|
12681
15121
|
} else {
|
|
12682
|
-
proceduresQuery = (0,
|
|
15122
|
+
proceduresQuery = (0, import_firestore45.query)(proceduresCollection, (0, import_firestore45.orderBy)("name"));
|
|
12683
15123
|
}
|
|
12684
|
-
const proceduresSnapshot = await (0,
|
|
15124
|
+
const proceduresSnapshot = await (0, import_firestore45.getDocs)(proceduresQuery);
|
|
12685
15125
|
const lastVisible = proceduresSnapshot.docs[proceduresSnapshot.docs.length - 1];
|
|
12686
|
-
const procedures = proceduresSnapshot.docs.map((
|
|
12687
|
-
const data =
|
|
15126
|
+
const procedures = proceduresSnapshot.docs.map((doc32) => {
|
|
15127
|
+
const data = doc32.data();
|
|
12688
15128
|
return {
|
|
12689
15129
|
...data,
|
|
12690
|
-
id:
|
|
15130
|
+
id: doc32.id
|
|
12691
15131
|
// Ensure ID is present
|
|
12692
15132
|
};
|
|
12693
15133
|
});
|
|
@@ -12730,19 +15170,19 @@ var ProcedureService = class extends BaseService {
|
|
|
12730
15170
|
const isGeoQuery = filters.location && filters.radiusInKm && filters.radiusInKm > 0;
|
|
12731
15171
|
const constraints = [];
|
|
12732
15172
|
if (filters.isActive !== void 0) {
|
|
12733
|
-
constraints.push((0,
|
|
15173
|
+
constraints.push((0, import_firestore45.where)("isActive", "==", filters.isActive));
|
|
12734
15174
|
} else {
|
|
12735
|
-
constraints.push((0,
|
|
15175
|
+
constraints.push((0, import_firestore45.where)("isActive", "==", true));
|
|
12736
15176
|
}
|
|
12737
15177
|
if (filters.procedureFamily) {
|
|
12738
|
-
constraints.push((0,
|
|
15178
|
+
constraints.push((0, import_firestore45.where)("family", "==", filters.procedureFamily));
|
|
12739
15179
|
}
|
|
12740
|
-
constraints.push((0,
|
|
15180
|
+
constraints.push((0, import_firestore45.orderBy)("clinicInfo.location.geohash"));
|
|
12741
15181
|
if (filters.pagination && filters.pagination > 0 && filters.lastDoc) {
|
|
12742
|
-
constraints.push((0,
|
|
12743
|
-
constraints.push((0,
|
|
15182
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
15183
|
+
constraints.push((0, import_firestore45.limit)(filters.pagination));
|
|
12744
15184
|
} else if (filters.pagination && filters.pagination > 0) {
|
|
12745
|
-
constraints.push((0,
|
|
15185
|
+
constraints.push((0, import_firestore45.limit)(filters.pagination));
|
|
12746
15186
|
}
|
|
12747
15187
|
let proceduresResult = [];
|
|
12748
15188
|
let lastVisibleDoc = null;
|
|
@@ -12758,19 +15198,19 @@ var ProcedureService = class extends BaseService {
|
|
|
12758
15198
|
for (const bound of bounds) {
|
|
12759
15199
|
const geoConstraints = [
|
|
12760
15200
|
...constraints,
|
|
12761
|
-
(0,
|
|
12762
|
-
(0,
|
|
15201
|
+
(0, import_firestore45.where)("clinicInfo.location.geohash", ">=", bound[0]),
|
|
15202
|
+
(0, import_firestore45.where)("clinicInfo.location.geohash", "<=", bound[1])
|
|
12763
15203
|
];
|
|
12764
|
-
const q = (0,
|
|
12765
|
-
(0,
|
|
15204
|
+
const q = (0, import_firestore45.query)(
|
|
15205
|
+
(0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
|
|
12766
15206
|
...geoConstraints
|
|
12767
15207
|
);
|
|
12768
|
-
const querySnapshot = await (0,
|
|
15208
|
+
const querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
12769
15209
|
console.log(
|
|
12770
15210
|
`[PROCEDURE_SERVICE] Found ${querySnapshot.docs.length} procedures in geo bound`
|
|
12771
15211
|
);
|
|
12772
|
-
for (const
|
|
12773
|
-
const procedure = { ...
|
|
15212
|
+
for (const doc32 of querySnapshot.docs) {
|
|
15213
|
+
const procedure = { ...doc32.data(), id: doc32.id };
|
|
12774
15214
|
const distance = (0, import_geofire_common8.distanceBetween)(
|
|
12775
15215
|
[center.latitude, center.longitude],
|
|
12776
15216
|
[
|
|
@@ -12813,16 +15253,16 @@ var ProcedureService = class extends BaseService {
|
|
|
12813
15253
|
proceduresResult = filteredProcedures;
|
|
12814
15254
|
}
|
|
12815
15255
|
} else {
|
|
12816
|
-
const q = (0,
|
|
12817
|
-
(0,
|
|
15256
|
+
const q = (0, import_firestore45.query)(
|
|
15257
|
+
(0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
|
|
12818
15258
|
...constraints
|
|
12819
15259
|
);
|
|
12820
|
-
const querySnapshot = await (0,
|
|
15260
|
+
const querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
12821
15261
|
console.log(
|
|
12822
15262
|
`[PROCEDURE_SERVICE] Found ${querySnapshot.docs.length} procedures with regular query`
|
|
12823
15263
|
);
|
|
12824
|
-
const procedures = querySnapshot.docs.map((
|
|
12825
|
-
return { ...
|
|
15264
|
+
const procedures = querySnapshot.docs.map((doc32) => {
|
|
15265
|
+
return { ...doc32.data(), id: doc32.id };
|
|
12826
15266
|
});
|
|
12827
15267
|
if (filters.location) {
|
|
12828
15268
|
const center = filters.location;
|
|
@@ -12943,18 +15383,18 @@ var ProcedureService = class extends BaseService {
|
|
|
12943
15383
|
if (!category || !subcategory || !technology) {
|
|
12944
15384
|
throw new Error("One or more required base entities not found");
|
|
12945
15385
|
}
|
|
12946
|
-
const clinicRef = (0,
|
|
12947
|
-
const clinicSnapshot = await (0,
|
|
15386
|
+
const clinicRef = (0, import_firestore45.doc)(this.db, CLINICS_COLLECTION, data.clinicBranchId);
|
|
15387
|
+
const clinicSnapshot = await (0, import_firestore45.getDoc)(clinicRef);
|
|
12948
15388
|
if (!clinicSnapshot.exists()) {
|
|
12949
15389
|
throw new Error(`Clinic with ID ${data.clinicBranchId} not found`);
|
|
12950
15390
|
}
|
|
12951
15391
|
const clinic = clinicSnapshot.data();
|
|
12952
|
-
const practitionerRef = (0,
|
|
15392
|
+
const practitionerRef = (0, import_firestore45.doc)(
|
|
12953
15393
|
this.db,
|
|
12954
15394
|
PRACTITIONERS_COLLECTION,
|
|
12955
15395
|
data.practitionerId
|
|
12956
15396
|
);
|
|
12957
|
-
const practitionerSnapshot = await (0,
|
|
15397
|
+
const practitionerSnapshot = await (0, import_firestore45.getDoc)(practitionerRef);
|
|
12958
15398
|
if (!practitionerSnapshot.exists()) {
|
|
12959
15399
|
throw new Error(`Practitioner with ID ${data.practitionerId} not found`);
|
|
12960
15400
|
}
|
|
@@ -13025,19 +15465,19 @@ var ProcedureService = class extends BaseService {
|
|
|
13025
15465
|
},
|
|
13026
15466
|
isActive: true
|
|
13027
15467
|
};
|
|
13028
|
-
const procedureRef = (0,
|
|
13029
|
-
await (0,
|
|
15468
|
+
const procedureRef = (0, import_firestore45.doc)(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
15469
|
+
await (0, import_firestore45.setDoc)(procedureRef, {
|
|
13030
15470
|
...newProcedure,
|
|
13031
|
-
createdAt: (0,
|
|
13032
|
-
updatedAt: (0,
|
|
15471
|
+
createdAt: (0, import_firestore45.serverTimestamp)(),
|
|
15472
|
+
updatedAt: (0, import_firestore45.serverTimestamp)()
|
|
13033
15473
|
});
|
|
13034
|
-
const savedDoc = await (0,
|
|
15474
|
+
const savedDoc = await (0, import_firestore45.getDoc)(procedureRef);
|
|
13035
15475
|
return savedDoc.data();
|
|
13036
15476
|
}
|
|
13037
15477
|
};
|
|
13038
15478
|
|
|
13039
15479
|
// src/services/reviews/reviews.service.ts
|
|
13040
|
-
var
|
|
15480
|
+
var import_firestore46 = require("firebase/firestore");
|
|
13041
15481
|
var import_zod25 = require("zod");
|
|
13042
15482
|
var ReviewService = class extends BaseService {
|
|
13043
15483
|
constructor(db, auth, app) {
|
|
@@ -13117,11 +15557,11 @@ var ReviewService = class extends BaseService {
|
|
|
13117
15557
|
updatedAt: now
|
|
13118
15558
|
};
|
|
13119
15559
|
reviewSchema.parse(review);
|
|
13120
|
-
const docRef = (0,
|
|
13121
|
-
await (0,
|
|
15560
|
+
const docRef = (0, import_firestore46.doc)(this.db, REVIEWS_COLLECTION, reviewId);
|
|
15561
|
+
await (0, import_firestore46.setDoc)(docRef, {
|
|
13122
15562
|
...review,
|
|
13123
|
-
createdAt: (0,
|
|
13124
|
-
updatedAt: (0,
|
|
15563
|
+
createdAt: (0, import_firestore46.serverTimestamp)(),
|
|
15564
|
+
updatedAt: (0, import_firestore46.serverTimestamp)()
|
|
13125
15565
|
});
|
|
13126
15566
|
return review;
|
|
13127
15567
|
} catch (error) {
|
|
@@ -13137,8 +15577,8 @@ var ReviewService = class extends BaseService {
|
|
|
13137
15577
|
* @returns The review if found, null otherwise
|
|
13138
15578
|
*/
|
|
13139
15579
|
async getReview(reviewId) {
|
|
13140
|
-
const docRef = (0,
|
|
13141
|
-
const docSnap = await (0,
|
|
15580
|
+
const docRef = (0, import_firestore46.doc)(this.db, REVIEWS_COLLECTION, reviewId);
|
|
15581
|
+
const docSnap = await (0, import_firestore46.getDoc)(docRef);
|
|
13142
15582
|
if (!docSnap.exists()) {
|
|
13143
15583
|
return null;
|
|
13144
15584
|
}
|
|
@@ -13150,12 +15590,12 @@ var ReviewService = class extends BaseService {
|
|
|
13150
15590
|
* @returns Array of reviews for the patient
|
|
13151
15591
|
*/
|
|
13152
15592
|
async getReviewsByPatient(patientId) {
|
|
13153
|
-
const q = (0,
|
|
13154
|
-
(0,
|
|
13155
|
-
(0,
|
|
15593
|
+
const q = (0, import_firestore46.query)(
|
|
15594
|
+
(0, import_firestore46.collection)(this.db, REVIEWS_COLLECTION),
|
|
15595
|
+
(0, import_firestore46.where)("patientId", "==", patientId)
|
|
13156
15596
|
);
|
|
13157
|
-
const snapshot = await (0,
|
|
13158
|
-
return snapshot.docs.map((
|
|
15597
|
+
const snapshot = await (0, import_firestore46.getDocs)(q);
|
|
15598
|
+
return snapshot.docs.map((doc32) => doc32.data());
|
|
13159
15599
|
}
|
|
13160
15600
|
/**
|
|
13161
15601
|
* Gets all reviews for a specific clinic
|
|
@@ -13163,12 +15603,12 @@ var ReviewService = class extends BaseService {
|
|
|
13163
15603
|
* @returns Array of reviews containing clinic reviews
|
|
13164
15604
|
*/
|
|
13165
15605
|
async getReviewsByClinic(clinicId) {
|
|
13166
|
-
const q = (0,
|
|
13167
|
-
(0,
|
|
13168
|
-
(0,
|
|
15606
|
+
const q = (0, import_firestore46.query)(
|
|
15607
|
+
(0, import_firestore46.collection)(this.db, REVIEWS_COLLECTION),
|
|
15608
|
+
(0, import_firestore46.where)("clinicReview.clinicId", "==", clinicId)
|
|
13169
15609
|
);
|
|
13170
|
-
const snapshot = await (0,
|
|
13171
|
-
return snapshot.docs.map((
|
|
15610
|
+
const snapshot = await (0, import_firestore46.getDocs)(q);
|
|
15611
|
+
return snapshot.docs.map((doc32) => doc32.data());
|
|
13172
15612
|
}
|
|
13173
15613
|
/**
|
|
13174
15614
|
* Gets all reviews for a specific practitioner
|
|
@@ -13176,12 +15616,12 @@ var ReviewService = class extends BaseService {
|
|
|
13176
15616
|
* @returns Array of reviews containing practitioner reviews
|
|
13177
15617
|
*/
|
|
13178
15618
|
async getReviewsByPractitioner(practitionerId) {
|
|
13179
|
-
const q = (0,
|
|
13180
|
-
(0,
|
|
13181
|
-
(0,
|
|
15619
|
+
const q = (0, import_firestore46.query)(
|
|
15620
|
+
(0, import_firestore46.collection)(this.db, REVIEWS_COLLECTION),
|
|
15621
|
+
(0, import_firestore46.where)("practitionerReview.practitionerId", "==", practitionerId)
|
|
13182
15622
|
);
|
|
13183
|
-
const snapshot = await (0,
|
|
13184
|
-
return snapshot.docs.map((
|
|
15623
|
+
const snapshot = await (0, import_firestore46.getDocs)(q);
|
|
15624
|
+
return snapshot.docs.map((doc32) => doc32.data());
|
|
13185
15625
|
}
|
|
13186
15626
|
/**
|
|
13187
15627
|
* Gets all reviews for a specific procedure
|
|
@@ -13189,12 +15629,12 @@ var ReviewService = class extends BaseService {
|
|
|
13189
15629
|
* @returns Array of reviews containing procedure reviews
|
|
13190
15630
|
*/
|
|
13191
15631
|
async getReviewsByProcedure(procedureId) {
|
|
13192
|
-
const q = (0,
|
|
13193
|
-
(0,
|
|
13194
|
-
(0,
|
|
15632
|
+
const q = (0, import_firestore46.query)(
|
|
15633
|
+
(0, import_firestore46.collection)(this.db, REVIEWS_COLLECTION),
|
|
15634
|
+
(0, import_firestore46.where)("procedureReview.procedureId", "==", procedureId)
|
|
13195
15635
|
);
|
|
13196
|
-
const snapshot = await (0,
|
|
13197
|
-
return snapshot.docs.map((
|
|
15636
|
+
const snapshot = await (0, import_firestore46.getDocs)(q);
|
|
15637
|
+
return snapshot.docs.map((doc32) => doc32.data());
|
|
13198
15638
|
}
|
|
13199
15639
|
/**
|
|
13200
15640
|
* Gets all reviews for a specific appointment
|
|
@@ -13202,11 +15642,11 @@ var ReviewService = class extends BaseService {
|
|
|
13202
15642
|
* @returns The review for the appointment if found, null otherwise
|
|
13203
15643
|
*/
|
|
13204
15644
|
async getReviewByAppointment(appointmentId) {
|
|
13205
|
-
const q = (0,
|
|
13206
|
-
(0,
|
|
13207
|
-
(0,
|
|
15645
|
+
const q = (0, import_firestore46.query)(
|
|
15646
|
+
(0, import_firestore46.collection)(this.db, REVIEWS_COLLECTION),
|
|
15647
|
+
(0, import_firestore46.where)("appointmentId", "==", appointmentId)
|
|
13208
15648
|
);
|
|
13209
|
-
const snapshot = await (0,
|
|
15649
|
+
const snapshot = await (0, import_firestore46.getDocs)(q);
|
|
13210
15650
|
if (snapshot.empty) {
|
|
13211
15651
|
return null;
|
|
13212
15652
|
}
|
|
@@ -13221,7 +15661,7 @@ var ReviewService = class extends BaseService {
|
|
|
13221
15661
|
if (!review) {
|
|
13222
15662
|
throw new Error(`Review with ID ${reviewId} not found`);
|
|
13223
15663
|
}
|
|
13224
|
-
await (0,
|
|
15664
|
+
await (0, import_firestore46.deleteDoc)((0, import_firestore46.doc)(this.db, REVIEWS_COLLECTION, reviewId));
|
|
13225
15665
|
}
|
|
13226
15666
|
/**
|
|
13227
15667
|
* Calculates the average of an array of numbers
|
|
@@ -13240,11 +15680,11 @@ var ReviewService = class extends BaseService {
|
|
|
13240
15680
|
|
|
13241
15681
|
// src/validations/calendar.schema.ts
|
|
13242
15682
|
var import_zod27 = require("zod");
|
|
13243
|
-
var
|
|
15683
|
+
var import_firestore48 = require("firebase/firestore");
|
|
13244
15684
|
|
|
13245
15685
|
// src/validations/profile-info.schema.ts
|
|
13246
15686
|
var import_zod26 = require("zod");
|
|
13247
|
-
var
|
|
15687
|
+
var import_firestore47 = require("firebase/firestore");
|
|
13248
15688
|
var clinicBranchInfoSchema = import_zod26.z.object({
|
|
13249
15689
|
id: import_zod26.z.string(),
|
|
13250
15690
|
featuredPhoto: import_zod26.z.string(),
|
|
@@ -13266,19 +15706,19 @@ var patientProfileInfoSchema = import_zod26.z.object({
|
|
|
13266
15706
|
fullName: import_zod26.z.string(),
|
|
13267
15707
|
email: import_zod26.z.string().email(),
|
|
13268
15708
|
phone: import_zod26.z.string().nullable(),
|
|
13269
|
-
dateOfBirth: import_zod26.z.instanceof(
|
|
15709
|
+
dateOfBirth: import_zod26.z.instanceof(import_firestore47.Timestamp),
|
|
13270
15710
|
gender: import_zod26.z.nativeEnum(Gender)
|
|
13271
15711
|
});
|
|
13272
15712
|
|
|
13273
15713
|
// src/validations/calendar.schema.ts
|
|
13274
|
-
var
|
|
15714
|
+
var MIN_APPOINTMENT_DURATION2 = 15;
|
|
13275
15715
|
var calendarEventTimeSchema = import_zod27.z.object({
|
|
13276
|
-
start: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(
|
|
13277
|
-
end: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(
|
|
15716
|
+
start: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(import_firestore48.Timestamp)),
|
|
15717
|
+
end: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(import_firestore48.Timestamp))
|
|
13278
15718
|
}).refine(
|
|
13279
15719
|
(data) => {
|
|
13280
|
-
const startDate = data.start instanceof
|
|
13281
|
-
const endDate = data.end instanceof
|
|
15720
|
+
const startDate = data.start instanceof import_firestore48.Timestamp ? data.start.toDate() : data.start;
|
|
15721
|
+
const endDate = data.end instanceof import_firestore48.Timestamp ? data.end.toDate() : data.end;
|
|
13282
15722
|
return startDate < endDate;
|
|
13283
15723
|
},
|
|
13284
15724
|
{
|
|
@@ -13287,7 +15727,7 @@ var calendarEventTimeSchema = import_zod27.z.object({
|
|
|
13287
15727
|
}
|
|
13288
15728
|
).refine(
|
|
13289
15729
|
(data) => {
|
|
13290
|
-
const startDate = data.start instanceof
|
|
15730
|
+
const startDate = data.start instanceof import_firestore48.Timestamp ? data.start.toDate() : data.start;
|
|
13291
15731
|
return startDate > /* @__PURE__ */ new Date();
|
|
13292
15732
|
},
|
|
13293
15733
|
{
|
|
@@ -13306,12 +15746,12 @@ var timeSlotSchema2 = import_zod27.z.object({
|
|
|
13306
15746
|
var syncedCalendarEventSchema = import_zod27.z.object({
|
|
13307
15747
|
eventId: import_zod27.z.string(),
|
|
13308
15748
|
syncedCalendarProvider: import_zod27.z.nativeEnum(SyncedCalendarProvider),
|
|
13309
|
-
syncedAt: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(
|
|
15749
|
+
syncedAt: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(import_firestore48.Timestamp))
|
|
13310
15750
|
});
|
|
13311
15751
|
var procedureInfoSchema = import_zod27.z.object({
|
|
13312
15752
|
name: import_zod27.z.string(),
|
|
13313
15753
|
description: import_zod27.z.string(),
|
|
13314
|
-
duration: import_zod27.z.number().min(
|
|
15754
|
+
duration: import_zod27.z.number().min(MIN_APPOINTMENT_DURATION2),
|
|
13315
15755
|
price: import_zod27.z.number().min(0),
|
|
13316
15756
|
currency: import_zod27.z.nativeEnum(Currency)
|
|
13317
15757
|
});
|
|
@@ -13385,8 +15825,8 @@ var calendarEventSchema = import_zod27.z.object({
|
|
|
13385
15825
|
status: import_zod27.z.nativeEnum(CalendarEventStatus),
|
|
13386
15826
|
syncStatus: import_zod27.z.nativeEnum(CalendarSyncStatus),
|
|
13387
15827
|
eventType: import_zod27.z.nativeEnum(CalendarEventType),
|
|
13388
|
-
createdAt: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(
|
|
13389
|
-
updatedAt: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(
|
|
15828
|
+
createdAt: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(import_firestore48.Timestamp)),
|
|
15829
|
+
updatedAt: import_zod27.z.instanceof(Date).or(import_zod27.z.instanceof(import_firestore48.Timestamp))
|
|
13390
15830
|
});
|
|
13391
15831
|
var createBlockingEventSchema = import_zod27.z.object({
|
|
13392
15832
|
entityType: import_zod27.z.enum(["practitioner", "clinic"]),
|
|
@@ -13629,7 +16069,7 @@ var updatePatientInstructionStatusSchema = import_zod30.z.object({
|
|
|
13629
16069
|
|
|
13630
16070
|
// src/utils/TimestampUtils.ts
|
|
13631
16071
|
var admin = __toESM(require("firebase-admin"));
|
|
13632
|
-
var
|
|
16072
|
+
var import_firestore49 = require("firebase/firestore");
|
|
13633
16073
|
var IS_SERVER_ENV = process.env.NODE_ENV === "production" || process.env.FUNCTIONS_EMULATOR === "true" || process.env.FIREBASE_CONFIG !== void 0;
|
|
13634
16074
|
var TimestampUtils = class {
|
|
13635
16075
|
/**
|
|
@@ -13659,7 +16099,7 @@ var TimestampUtils = class {
|
|
|
13659
16099
|
if (this.serverMode) {
|
|
13660
16100
|
return adminTimestamp;
|
|
13661
16101
|
}
|
|
13662
|
-
return new
|
|
16102
|
+
return new import_firestore49.Timestamp(
|
|
13663
16103
|
adminTimestamp.seconds,
|
|
13664
16104
|
adminTimestamp.nanoseconds
|
|
13665
16105
|
);
|
|
@@ -13704,14 +16144,14 @@ var TimestampUtils = class {
|
|
|
13704
16144
|
if (this.serverMode) {
|
|
13705
16145
|
return admin.firestore.Timestamp.fromDate(date);
|
|
13706
16146
|
}
|
|
13707
|
-
return
|
|
16147
|
+
return import_firestore49.Timestamp.fromDate(date);
|
|
13708
16148
|
}
|
|
13709
16149
|
/**
|
|
13710
16150
|
* @deprecated Use dateToTimestamp() instead for better cross-environment compatibility
|
|
13711
16151
|
*/
|
|
13712
16152
|
static dateToClientTimestamp(date) {
|
|
13713
16153
|
if (!date) return null;
|
|
13714
|
-
return
|
|
16154
|
+
return import_firestore49.Timestamp.fromDate(date);
|
|
13715
16155
|
}
|
|
13716
16156
|
/**
|
|
13717
16157
|
* @deprecated Use dateToTimestamp() instead for better cross-environment compatibility
|
|
@@ -13741,7 +16181,7 @@ var TimestampUtils = class {
|
|
|
13741
16181
|
if (obj instanceof admin.firestore.Timestamp) {
|
|
13742
16182
|
return this.serverMode ? obj : this.adminToClient(obj);
|
|
13743
16183
|
}
|
|
13744
|
-
if (obj instanceof
|
|
16184
|
+
if (obj instanceof import_firestore49.Timestamp && this.serverMode) {
|
|
13745
16185
|
return this.clientToAdmin(obj);
|
|
13746
16186
|
}
|
|
13747
16187
|
if (Array.isArray(obj)) {
|
|
@@ -13785,7 +16225,7 @@ var TimestampUtils = class {
|
|
|
13785
16225
|
if (!obj || typeof obj !== "object") {
|
|
13786
16226
|
return obj;
|
|
13787
16227
|
}
|
|
13788
|
-
if (obj instanceof
|
|
16228
|
+
if (obj instanceof import_firestore49.Timestamp) {
|
|
13789
16229
|
return this.clientToAdmin(obj);
|
|
13790
16230
|
}
|
|
13791
16231
|
if (Array.isArray(obj)) {
|
|
@@ -13810,7 +16250,7 @@ TimestampUtils.serverMode = IS_SERVER_ENV;
|
|
|
13810
16250
|
|
|
13811
16251
|
// src/config/firebase.ts
|
|
13812
16252
|
var import_app = require("firebase/app");
|
|
13813
|
-
var
|
|
16253
|
+
var import_firestore50 = require("firebase/firestore");
|
|
13814
16254
|
var import_auth9 = require("firebase/auth");
|
|
13815
16255
|
var import_analytics = require("firebase/analytics");
|
|
13816
16256
|
var import_react_native = require("react-native");
|
|
@@ -13820,7 +16260,7 @@ var firebaseInstance = null;
|
|
|
13820
16260
|
var initializeFirebase = (config) => {
|
|
13821
16261
|
if (!firebaseInstance) {
|
|
13822
16262
|
const app = (0, import_app.initializeApp)(config);
|
|
13823
|
-
const db = (0,
|
|
16263
|
+
const db = (0, import_firestore50.getFirestore)(app);
|
|
13824
16264
|
const auth = (0, import_auth9.getAuth)(app);
|
|
13825
16265
|
const storage = (0, import_storage4.getStorage)(app);
|
|
13826
16266
|
const functions = (0, import_functions2.getFunctions)(app);
|
|
@@ -13875,6 +16315,7 @@ var getFirebaseFunctions = async () => {
|
|
|
13875
16315
|
CLINIC_GROUPS_COLLECTION,
|
|
13876
16316
|
CalendarEventStatus,
|
|
13877
16317
|
CalendarEventType,
|
|
16318
|
+
CalendarServiceV2,
|
|
13878
16319
|
CalendarServiceV3,
|
|
13879
16320
|
CalendarSyncStatus,
|
|
13880
16321
|
ClinicAdminService,
|
|
@@ -13943,6 +16384,7 @@ var getFirebaseFunctions = async () => {
|
|
|
13943
16384
|
USERS_COLLECTION,
|
|
13944
16385
|
USER_FORMS_SUBCOLLECTION,
|
|
13945
16386
|
UserRole,
|
|
16387
|
+
UserService,
|
|
13946
16388
|
addAllergySchema,
|
|
13947
16389
|
addBlockingConditionSchema,
|
|
13948
16390
|
addContraindicationSchema,
|