@blackcode_sa/metaestetics-api 1.7.19 → 1.7.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -877,16 +877,16 @@ var BaseService = class {
877
877
 
878
878
  // src/services/user.service.ts
879
879
  import {
880
- collection as collection7,
881
- doc as doc9,
882
- getDoc as getDoc12,
883
- getDocs as getDocs7,
884
- query as query7,
885
- where as where7,
886
- updateDoc as updateDoc9,
887
- deleteDoc as deleteDoc3,
888
- Timestamp as Timestamp10,
889
- setDoc as setDoc8,
880
+ collection as collection8,
881
+ doc as doc10,
882
+ getDoc as getDoc13,
883
+ getDocs as getDocs8,
884
+ query as query8,
885
+ where as where8,
886
+ updateDoc as updateDoc10,
887
+ deleteDoc as deleteDoc4,
888
+ Timestamp as Timestamp11,
889
+ setDoc as setDoc9,
890
890
  serverTimestamp as serverTimestamp10
891
891
  } from "firebase/firestore";
892
892
 
@@ -987,432 +987,788 @@ import { z as z16 } from "zod";
987
987
 
988
988
  // src/services/patient/patient.service.ts
989
989
  import {
990
- doc as doc6,
991
- getDoc as getDoc9,
992
- writeBatch
993
- } from "firebase/firestore";
994
-
995
- // src/services/patient/utils/profile.utils.ts
996
- import {
997
- getDoc as getDoc5,
998
- setDoc as setDoc4,
999
- updateDoc as updateDoc3,
1000
- arrayUnion as arrayUnion2,
1001
- arrayRemove as arrayRemove2,
1002
- serverTimestamp as serverTimestamp4,
1003
- increment,
1004
- Timestamp as Timestamp4,
1005
- collection as collection3,
1006
- query as query3,
1007
- where as where3,
1008
- getDocs as getDocs3,
1009
- limit as limit2,
1010
- startAfter as startAfter2,
1011
- doc as doc4
990
+ doc as doc7,
991
+ getDoc as getDoc10,
992
+ writeBatch,
993
+ updateDoc as updateDoc7,
994
+ serverTimestamp as serverTimestamp7
1012
995
  } from "firebase/firestore";
1013
- import { ref, uploadBytes, getDownloadURL, deleteObject } from "firebase/storage";
1014
- import { z as z8 } from "zod";
1015
-
1016
- // src/types/patient/medical-info.types.ts
1017
- var PATIENT_MEDICAL_INFO_COLLECTION = "medical_info";
1018
- var DEFAULT_MEDICAL_INFO = {
1019
- vitalStats: {},
1020
- blockingConditions: [],
1021
- contraindications: [],
1022
- allergies: [],
1023
- currentMedications: []
1024
- };
1025
-
1026
- // src/types/patient/index.ts
1027
- var PATIENTS_COLLECTION = "patients";
1028
- var PATIENT_SENSITIVE_INFO_COLLECTION = "sensitive-info";
1029
- var PATIENT_MEDICAL_HISTORY_COLLECTION = "medical-history";
1030
- var PATIENT_APPOINTMENTS_COLLECTION = "appointments";
1031
- var PATIENT_LOCATION_INFO_COLLECTION = "location-info";
1032
- var Gender = /* @__PURE__ */ ((Gender2) => {
1033
- Gender2["MALE"] = "male";
1034
- Gender2["FEMALE"] = "female";
1035
- Gender2["TRANSGENDER_MALE"] = "transgender_male";
1036
- Gender2["TRANSGENDER_FEMALE"] = "transgender_female";
1037
- Gender2["PREFER_NOT_TO_SAY"] = "prefer_not_to_say";
1038
- Gender2["OTHER"] = "other";
1039
- return Gender2;
1040
- })(Gender || {});
1041
-
1042
- // src/validations/patient.schema.ts
1043
- import { z as z6 } from "zod";
1044
- import { Timestamp as Timestamp2 } from "firebase/firestore";
1045
-
1046
- // src/validations/patient/medical-info.schema.ts
1047
- import { z as z5 } from "zod";
1048
-
1049
- // src/types/patient/allergies.ts
1050
- var AllergyType = /* @__PURE__ */ ((AllergyType2) => {
1051
- AllergyType2["MEDICATION"] = "medication";
1052
- AllergyType2["FOOD"] = "food";
1053
- AllergyType2["ENVIRONMENTAL"] = "environmental";
1054
- AllergyType2["LATEX"] = "latex";
1055
- AllergyType2["COSMETIC"] = "cosmetic";
1056
- AllergyType2["OTHER"] = "other";
1057
- return AllergyType2;
1058
- })(AllergyType || {});
1059
- var MedicationAllergySubtype = /* @__PURE__ */ ((MedicationAllergySubtype2) => {
1060
- MedicationAllergySubtype2["ANTIBIOTICS"] = "antibiotics";
1061
- MedicationAllergySubtype2["NSAIDS"] = "nsaids";
1062
- MedicationAllergySubtype2["OPIOIDS"] = "opioids";
1063
- MedicationAllergySubtype2["ANESTHETICS"] = "anesthetics";
1064
- MedicationAllergySubtype2["VACCINES"] = "vaccines";
1065
- MedicationAllergySubtype2["OTHER"] = "other";
1066
- return MedicationAllergySubtype2;
1067
- })(MedicationAllergySubtype || {});
1068
- var FoodAllergySubtype = /* @__PURE__ */ ((FoodAllergySubtype2) => {
1069
- FoodAllergySubtype2["NUTS"] = "nuts";
1070
- FoodAllergySubtype2["SHELLFISH"] = "shellfish";
1071
- FoodAllergySubtype2["DAIRY"] = "dairy";
1072
- FoodAllergySubtype2["EGGS"] = "eggs";
1073
- FoodAllergySubtype2["WHEAT"] = "wheat";
1074
- FoodAllergySubtype2["SOY"] = "soy";
1075
- FoodAllergySubtype2["FISH"] = "fish";
1076
- FoodAllergySubtype2["FRUITS"] = "fruits";
1077
- FoodAllergySubtype2["OTHER"] = "other";
1078
- return FoodAllergySubtype2;
1079
- })(FoodAllergySubtype || {});
1080
- var EnvironmentalAllergySubtype = /* @__PURE__ */ ((EnvironmentalAllergySubtype2) => {
1081
- EnvironmentalAllergySubtype2["POLLEN"] = "pollen";
1082
- EnvironmentalAllergySubtype2["DUST"] = "dust";
1083
- EnvironmentalAllergySubtype2["MOLD"] = "mold";
1084
- EnvironmentalAllergySubtype2["PET_DANDER"] = "pet_dander";
1085
- EnvironmentalAllergySubtype2["INSECTS"] = "insects";
1086
- EnvironmentalAllergySubtype2["OTHER"] = "other";
1087
- return EnvironmentalAllergySubtype2;
1088
- })(EnvironmentalAllergySubtype || {});
1089
- var CosmeticAllergySubtype = /* @__PURE__ */ ((CosmeticAllergySubtype2) => {
1090
- CosmeticAllergySubtype2["FRAGRANCES"] = "fragrances";
1091
- CosmeticAllergySubtype2["PRESERVATIVES"] = "preservatives";
1092
- CosmeticAllergySubtype2["DYES"] = "dyes";
1093
- CosmeticAllergySubtype2["METALS"] = "metals";
1094
- CosmeticAllergySubtype2["OTHER"] = "other";
1095
- return CosmeticAllergySubtype2;
1096
- })(CosmeticAllergySubtype || {});
1097
-
1098
- // src/backoffice/types/static/blocking-condition.types.ts
1099
- var BlockingCondition = /* @__PURE__ */ ((BlockingCondition2) => {
1100
- BlockingCondition2["PREGNANCY"] = "pregnancy";
1101
- BlockingCondition2["BREASTFEEDING"] = "breastfeeding";
1102
- BlockingCondition2["ACTIVE_INFECTION"] = "active_infection";
1103
- BlockingCondition2["SKIN_CONDITION"] = "skin_condition";
1104
- BlockingCondition2["AUTOIMMUNE_DISEASE"] = "autoimmune_disease";
1105
- BlockingCondition2["BLOOD_THINNERS"] = "blood_thinners";
1106
- BlockingCondition2["RECENT_SURGERY"] = "recent_surgery";
1107
- BlockingCondition2["DIABETES"] = "diabetes";
1108
- BlockingCondition2["HEART_CONDITION"] = "heart_condition";
1109
- BlockingCondition2["HIGH_BLOOD_PRESSURE"] = "high_blood_pressure";
1110
- BlockingCondition2["KELOID_SCARRING"] = "keloid_scarring";
1111
- BlockingCondition2["METAL_IMPLANTS"] = "metal_implants";
1112
- BlockingCondition2["PACEMAKER"] = "pacemaker";
1113
- BlockingCondition2["CANCER"] = "cancer";
1114
- BlockingCondition2["EPILEPSY"] = "epilepsy";
1115
- return BlockingCondition2;
1116
- })(BlockingCondition || {});
1117
-
1118
- // src/backoffice/types/static/contraindication.types.ts
1119
- var Contraindication = /* @__PURE__ */ ((Contraindication2) => {
1120
- Contraindication2["SENSITIVE_SKIN"] = "sensitive_skin";
1121
- Contraindication2["RECENT_TANNING"] = "recent_tanning";
1122
- Contraindication2["RECENT_BOTOX"] = "recent_botox";
1123
- Contraindication2["RECENT_FILLERS"] = "recent_fillers";
1124
- Contraindication2["SKIN_ALLERGIES"] = "skin_allergies";
1125
- Contraindication2["MEDICATIONS"] = "medications";
1126
- Contraindication2["RECENT_CHEMICAL_PEEL"] = "recent_chemical_peel";
1127
- Contraindication2["RECENT_LASER"] = "recent_laser";
1128
- Contraindication2["SKIN_INFLAMMATION"] = "skin_inflammation";
1129
- Contraindication2["OPEN_WOUNDS"] = "open_wounds";
1130
- Contraindication2["HERPES_SIMPLEX"] = "herpes_simplex";
1131
- Contraindication2["COLD_SORES"] = "cold_sores";
1132
- return Contraindication2;
1133
- })(Contraindication || {});
1134
996
 
1135
- // src/validations/common.schema.ts
1136
- import { z as z4 } from "zod";
997
+ // src/services/media/media.service.ts
1137
998
  import { Timestamp } from "firebase/firestore";
1138
- var timestampSchema2 = z4.union([
1139
- z4.object({
1140
- seconds: z4.number(),
1141
- nanoseconds: z4.number()
1142
- }),
1143
- z4.instanceof(Timestamp)
1144
- ]).transform((data) => {
1145
- if (data instanceof Timestamp) {
1146
- return data;
1147
- }
1148
- return new Timestamp(data.seconds, data.nanoseconds);
1149
- });
1150
-
1151
- // src/validations/patient/medical-info.schema.ts
1152
- var allergySubtypeSchema = z5.union([
1153
- z5.nativeEnum(MedicationAllergySubtype),
1154
- z5.nativeEnum(FoodAllergySubtype),
1155
- z5.nativeEnum(EnvironmentalAllergySubtype),
1156
- z5.nativeEnum(CosmeticAllergySubtype),
1157
- z5.literal("other")
1158
- ]);
1159
- var allergySchema = z5.object({
1160
- type: z5.nativeEnum(AllergyType),
1161
- subtype: allergySubtypeSchema,
1162
- name: z5.string().optional().nullable(),
1163
- severity: z5.enum(["mild", "moderate", "severe"]).optional(),
1164
- reaction: z5.string().optional().nullable(),
1165
- diagnosed: timestampSchema2.optional().nullable(),
1166
- notes: z5.string().optional().nullable()
1167
- });
1168
- var vitalStatsSchema = z5.object({
1169
- height: z5.number().positive().optional(),
1170
- weight: z5.number().positive().optional(),
1171
- bloodType: z5.enum(["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"]).optional(),
1172
- bloodPressure: z5.object({
1173
- systolic: z5.number().min(70).max(200),
1174
- diastolic: z5.number().min(40).max(130),
1175
- lastMeasured: timestampSchema2
1176
- }).optional()
1177
- });
1178
- var blockingConditionSchema = z5.object({
1179
- condition: z5.nativeEnum(BlockingCondition),
1180
- diagnosedAt: timestampSchema2,
1181
- notes: z5.string().optional().nullable(),
1182
- isActive: z5.boolean()
1183
- });
1184
- var contraindicationSchema = z5.object({
1185
- condition: z5.nativeEnum(Contraindication),
1186
- lastOccurrence: timestampSchema2,
1187
- frequency: z5.enum(["rare", "occasional", "frequent"]),
1188
- notes: z5.string().optional().nullable(),
1189
- isActive: z5.boolean()
1190
- });
1191
- var medicationSchema = z5.object({
1192
- name: z5.string().min(1),
1193
- dosage: z5.string().min(1),
1194
- frequency: z5.string().min(1),
1195
- startDate: timestampSchema2.optional().nullable(),
1196
- endDate: timestampSchema2.optional().nullable(),
1197
- prescribedBy: z5.string().optional().nullable()
1198
- });
1199
- var patientMedicalInfoSchema = z5.object({
1200
- patientId: z5.string(),
1201
- vitalStats: vitalStatsSchema,
1202
- blockingConditions: z5.array(blockingConditionSchema),
1203
- contraindications: z5.array(contraindicationSchema),
1204
- allergies: z5.array(allergySchema),
1205
- currentMedications: z5.array(medicationSchema),
1206
- emergencyNotes: z5.string().optional(),
1207
- lastUpdated: timestampSchema2,
1208
- updatedBy: z5.string(),
1209
- verifiedBy: z5.string().optional(),
1210
- verifiedAt: timestampSchema2.optional()
1211
- });
1212
- var createPatientMedicalInfoSchema = patientMedicalInfoSchema.omit({
1213
- patientId: true,
1214
- lastUpdated: true,
1215
- updatedBy: true,
1216
- verifiedBy: true,
1217
- verifiedAt: true
1218
- });
1219
- var updatePatientMedicalInfoSchema = createPatientMedicalInfoSchema.partial();
1220
- var updateVitalStatsSchema = vitalStatsSchema;
1221
- var addAllergySchema = allergySchema;
1222
- var updateAllergySchema = allergySchema.partial().extend({
1223
- allergyIndex: z5.number().min(0)
1224
- });
1225
- var addBlockingConditionSchema = blockingConditionSchema;
1226
- var updateBlockingConditionSchema = blockingConditionSchema.partial().extend({
1227
- conditionIndex: z5.number().min(0)
1228
- });
1229
- var addContraindicationSchema = contraindicationSchema;
1230
- var updateContraindicationSchema = contraindicationSchema.partial().extend({
1231
- contraindicationIndex: z5.number().min(0)
1232
- });
1233
- var addMedicationSchema = medicationSchema;
1234
- var updateMedicationSchema = medicationSchema.partial().extend({
1235
- medicationIndex: z5.number().min(0)
1236
- });
1237
-
1238
- // src/validations/patient.schema.ts
1239
- var locationDataSchema = z6.object({
1240
- latitude: z6.number().min(-90).max(90),
1241
- longitude: z6.number().min(-180).max(180),
1242
- geohash: z6.string().optional()
1243
- });
1244
- var addressDataSchema = z6.object({
1245
- address: z6.string(),
1246
- city: z6.string(),
1247
- country: z6.string(),
1248
- postalCode: z6.string()
1249
- });
1250
- var emergencyContactSchema = z6.object({
1251
- name: z6.string(),
1252
- relationship: z6.string(),
1253
- phoneNumber: z6.string(),
1254
- isNotifiable: z6.boolean()
1255
- });
1256
- var gamificationSchema = z6.object({
1257
- level: z6.number(),
1258
- points: z6.number()
1259
- });
1260
- var patientLocationInfoSchema = z6.object({
1261
- patientId: z6.string(),
1262
- userRef: z6.string(),
1263
- locationData: locationDataSchema,
1264
- createdAt: z6.instanceof(Timestamp2),
1265
- updatedAt: z6.instanceof(Timestamp2)
1266
- });
1267
- var createPatientLocationInfoSchema = z6.object({
1268
- patientId: z6.string(),
1269
- userRef: z6.string(),
1270
- locationData: locationDataSchema
1271
- });
1272
- var patientSensitiveInfoSchema = z6.object({
1273
- patientId: z6.string(),
1274
- userRef: z6.string(),
1275
- photoUrl: z6.string().optional(),
1276
- firstName: z6.string().min(2),
1277
- lastName: z6.string().min(2),
1278
- dateOfBirth: z6.instanceof(Timestamp2).nullable(),
1279
- gender: z6.nativeEnum(Gender),
1280
- email: z6.string().email().optional(),
1281
- phoneNumber: z6.string().optional(),
1282
- alternativePhoneNumber: z6.string().optional(),
1283
- addressData: addressDataSchema.optional(),
1284
- emergencyContacts: z6.array(emergencyContactSchema).optional(),
1285
- createdAt: z6.instanceof(Timestamp2),
1286
- updatedAt: z6.instanceof(Timestamp2)
1287
- });
1288
- var patientDoctorSchema = z6.object({
1289
- userRef: z6.string(),
1290
- assignedAt: z6.instanceof(Timestamp2),
1291
- assignedBy: z6.string().optional(),
1292
- isActive: z6.boolean(),
1293
- notes: z6.string().optional()
1294
- });
1295
- var patientClinicSchema = z6.object({
1296
- clinicId: z6.string(),
1297
- assignedAt: z6.instanceof(Timestamp2),
1298
- assignedBy: z6.string().optional(),
1299
- isActive: z6.boolean(),
1300
- notes: z6.string().optional()
1301
- });
1302
- var patientProfileSchema = z6.object({
1303
- id: z6.string(),
1304
- userRef: z6.string(),
1305
- displayName: z6.string(),
1306
- profilePhoto: z6.string().url().nullable(),
1307
- gamification: gamificationSchema,
1308
- expoTokens: z6.array(z6.string()),
1309
- isActive: z6.boolean(),
1310
- isVerified: z6.boolean(),
1311
- doctors: z6.array(patientDoctorSchema),
1312
- clinics: z6.array(patientClinicSchema),
1313
- doctorIds: z6.array(z6.string()),
1314
- clinicIds: z6.array(z6.string()),
1315
- createdAt: z6.instanceof(Timestamp2),
1316
- updatedAt: z6.instanceof(Timestamp2)
1317
- });
1318
- var createPatientProfileSchema = z6.object({
1319
- userRef: z6.string(),
1320
- displayName: z6.string(),
1321
- profilePhoto: z6.string().url().nullable().optional(),
1322
- expoTokens: z6.array(z6.string()),
1323
- gamification: gamificationSchema.optional(),
1324
- isActive: z6.boolean(),
1325
- isVerified: z6.boolean(),
1326
- doctors: z6.array(patientDoctorSchema).optional(),
1327
- clinics: z6.array(patientClinicSchema).optional(),
1328
- doctorIds: z6.array(z6.string()).optional(),
1329
- clinicIds: z6.array(z6.string()).optional()
1330
- });
1331
- var createPatientSensitiveInfoSchema = z6.object({
1332
- patientId: z6.string(),
1333
- userRef: z6.string(),
1334
- photoUrl: z6.string().optional(),
1335
- firstName: z6.string().min(2),
1336
- lastName: z6.string().min(2),
1337
- dateOfBirth: z6.instanceof(Timestamp2).nullable(),
1338
- gender: z6.nativeEnum(Gender),
1339
- email: z6.string().email().optional(),
1340
- phoneNumber: z6.string().optional(),
1341
- alternativePhoneNumber: z6.string().optional(),
1342
- addressData: addressDataSchema.optional(),
1343
- emergencyContacts: z6.array(emergencyContactSchema).optional()
1344
- });
1345
- var searchPatientsSchema = z6.object({
1346
- clinicId: z6.string().optional(),
1347
- practitionerId: z6.string().optional()
1348
- }).refine((data) => data.clinicId || data.practitionerId, {
1349
- message: "At least one of clinicId or practitionerId must be provided",
1350
- path: []
1351
- // Optional: specify a path like ['clinicId'] or ['practitionerId']
1352
- });
1353
- var requesterInfoSchema = z6.object({
1354
- id: z6.string(),
1355
- role: z6.enum(["clinic_admin", "practitioner"]),
1356
- associatedClinicId: z6.string().optional(),
1357
- associatedPractitionerId: z6.string().optional()
1358
- }).refine(
1359
- (data) => {
1360
- if (data.role === "clinic_admin") {
1361
- return !!data.associatedClinicId;
1362
- } else if (data.role === "practitioner") {
1363
- return !!data.associatedPractitionerId;
1364
- }
1365
- return false;
1366
- },
1367
- {
1368
- message: "Associated ID (clinic or practitioner) is required based on role",
1369
- path: ["associatedClinicId", "associatedPractitionerId"]
1370
- }
1371
- );
1372
-
1373
- // src/services/patient/utils/docs.utils.ts
999
+ import {
1000
+ ref,
1001
+ uploadBytes,
1002
+ getDownloadURL,
1003
+ deleteObject,
1004
+ getBytes
1005
+ } from "firebase/storage";
1374
1006
  import {
1375
1007
  doc,
1008
+ getDoc,
1009
+ setDoc,
1010
+ updateDoc,
1376
1011
  collection,
1377
1012
  query,
1378
1013
  where,
1014
+ limit,
1379
1015
  getDocs,
1380
- getDoc as getDoc2
1016
+ deleteDoc,
1017
+ orderBy
1018
+ } from "firebase/firestore";
1019
+ var MediaAccessLevel = /* @__PURE__ */ ((MediaAccessLevel2) => {
1020
+ MediaAccessLevel2["PUBLIC"] = "public";
1021
+ MediaAccessLevel2["PRIVATE"] = "private";
1022
+ MediaAccessLevel2["CONFIDENTIAL"] = "confidential";
1023
+ return MediaAccessLevel2;
1024
+ })(MediaAccessLevel || {});
1025
+ var MEDIA_METADATA_COLLECTION = "media_metadata";
1026
+ var MediaService = class extends BaseService {
1027
+ constructor(db, auth, app) {
1028
+ super(db, auth, app);
1029
+ }
1030
+ /**
1031
+ * Upload a media file, store its metadata, and return the metadata including the URL.
1032
+ * @param file - The file to upload.
1033
+ * @param ownerId - ID of the owner (user, patient, clinic, etc.).
1034
+ * @param accessLevel - Access level (public, private, confidential).
1035
+ * @param collectionName - The logical collection name this media belongs to (e.g., 'patient_profile_pictures', 'clinic_logos').
1036
+ * @param originalFileName - Optional: the original name of the file, if not using file.name.
1037
+ * @returns Promise with the media metadata.
1038
+ */
1039
+ async uploadMedia(file, ownerId, accessLevel, collectionName, originalFileName) {
1040
+ const mediaId = this.generateId();
1041
+ const fileNameToUse = originalFileName || (file instanceof File ? file.name : file.toString());
1042
+ const uniqueFileName = `${mediaId}-${fileNameToUse}`;
1043
+ const filePath = `media/${accessLevel}/${ownerId}/${collectionName}/${uniqueFileName}`;
1044
+ console.log(`[MediaService] Uploading file to: ${filePath}`);
1045
+ const storageRef = ref(this.storage, filePath);
1046
+ try {
1047
+ const uploadResult = await uploadBytes(storageRef, file, {
1048
+ contentType: file.type
1049
+ });
1050
+ console.log("[MediaService] File uploaded successfully", uploadResult);
1051
+ const downloadURL = await getDownloadURL(uploadResult.ref);
1052
+ console.log("[MediaService] Got download URL:", downloadURL);
1053
+ const metadata = {
1054
+ id: mediaId,
1055
+ name: fileNameToUse,
1056
+ url: downloadURL,
1057
+ contentType: file.type,
1058
+ size: file.size,
1059
+ createdAt: Timestamp.now(),
1060
+ accessLevel,
1061
+ ownerId,
1062
+ collectionName,
1063
+ path: filePath
1064
+ };
1065
+ const metadataDocRef = doc(this.db, MEDIA_METADATA_COLLECTION, mediaId);
1066
+ await setDoc(metadataDocRef, metadata);
1067
+ console.log("[MediaService] Metadata stored in Firestore:", mediaId);
1068
+ return metadata;
1069
+ } catch (error) {
1070
+ console.error("[MediaService] Error during media upload:", error);
1071
+ throw error;
1072
+ }
1073
+ }
1074
+ /**
1075
+ * Get media metadata from Firestore by its ID.
1076
+ * @param mediaId - ID of the media.
1077
+ * @returns Promise with the media metadata or null if not found.
1078
+ */
1079
+ async getMediaMetadata(mediaId) {
1080
+ console.log(`[MediaService] Getting media metadata for ID: ${mediaId}`);
1081
+ const docRef = doc(this.db, MEDIA_METADATA_COLLECTION, mediaId);
1082
+ const docSnap = await getDoc(docRef);
1083
+ if (docSnap.exists()) {
1084
+ console.log("[MediaService] Metadata found:", docSnap.data());
1085
+ return docSnap.data();
1086
+ }
1087
+ console.log("[MediaService] No metadata found for ID:", mediaId);
1088
+ return null;
1089
+ }
1090
+ /**
1091
+ * Get media metadata from Firestore by its public URL.
1092
+ * @param url - The public URL of the media file.
1093
+ * @returns Promise with the media metadata or null if not found.
1094
+ */
1095
+ async getMediaMetadataByUrl(url) {
1096
+ console.log(`[MediaService] Getting media metadata by URL: ${url}`);
1097
+ const q = query(
1098
+ collection(this.db, MEDIA_METADATA_COLLECTION),
1099
+ where("url", "==", url),
1100
+ limit(1)
1101
+ );
1102
+ try {
1103
+ const querySnapshot = await getDocs(q);
1104
+ if (!querySnapshot.empty) {
1105
+ const metadata = querySnapshot.docs[0].data();
1106
+ console.log("[MediaService] Metadata found by URL:", metadata);
1107
+ return metadata;
1108
+ }
1109
+ console.log("[MediaService] No metadata found for URL:", url);
1110
+ return null;
1111
+ } catch (error) {
1112
+ console.error("[MediaService] Error fetching metadata by URL:", error);
1113
+ throw error;
1114
+ }
1115
+ }
1116
+ /**
1117
+ * Delete media from storage and remove metadata from Firestore.
1118
+ * @param mediaId - ID of the media to delete.
1119
+ */
1120
+ async deleteMedia(mediaId) {
1121
+ console.log(`[MediaService] Deleting media with ID: ${mediaId}`);
1122
+ const metadata = await this.getMediaMetadata(mediaId);
1123
+ if (!metadata) {
1124
+ console.warn(
1125
+ `[MediaService] Metadata not found for media ID ${mediaId}. Cannot delete.`
1126
+ );
1127
+ return;
1128
+ }
1129
+ const storageFileRef = ref(this.storage, metadata.path);
1130
+ try {
1131
+ await deleteObject(storageFileRef);
1132
+ console.log(`[MediaService] File deleted from Storage: ${metadata.path}`);
1133
+ const metadataDocRef = doc(this.db, MEDIA_METADATA_COLLECTION, mediaId);
1134
+ await deleteDoc(metadataDocRef);
1135
+ console.log(
1136
+ `[MediaService] Metadata deleted from Firestore for ID: ${mediaId}`
1137
+ );
1138
+ } catch (error) {
1139
+ console.error(`[MediaService] Error deleting media ${mediaId}:`, error);
1140
+ throw error;
1141
+ }
1142
+ }
1143
+ /**
1144
+ * Update media access level. This involves moving the file in Firebase Storage
1145
+ * to a new path reflecting the new access level, and updating its metadata.
1146
+ * @param mediaId - ID of the media to update.
1147
+ * @param newAccessLevel - New access level.
1148
+ * @returns Promise with the updated media metadata, or null if metadata not found.
1149
+ */
1150
+ async updateMediaAccessLevel(mediaId, newAccessLevel) {
1151
+ var _a;
1152
+ console.log(
1153
+ `[MediaService] Attempting to update access level for media ID: ${mediaId} to ${newAccessLevel}`
1154
+ );
1155
+ const metadata = await this.getMediaMetadata(mediaId);
1156
+ if (!metadata) {
1157
+ console.warn(
1158
+ `[MediaService] Metadata not found for media ID ${mediaId}. Cannot update access level.`
1159
+ );
1160
+ return null;
1161
+ }
1162
+ if (metadata.accessLevel === newAccessLevel) {
1163
+ console.log(
1164
+ `[MediaService] Media ID ${mediaId} already has access level ${newAccessLevel}. Updating timestamp only.`
1165
+ );
1166
+ const metadataDocRef = doc(this.db, MEDIA_METADATA_COLLECTION, mediaId);
1167
+ try {
1168
+ await updateDoc(metadataDocRef, { updatedAt: Timestamp.now() });
1169
+ return { ...metadata, updatedAt: Timestamp.now() };
1170
+ } catch (error) {
1171
+ console.error(
1172
+ `[MediaService] Error updating timestamp for media ID ${mediaId}:`,
1173
+ error
1174
+ );
1175
+ throw error;
1176
+ }
1177
+ }
1178
+ const oldStoragePath = metadata.path;
1179
+ const fileNamePart = `${metadata.id}-${metadata.name}`;
1180
+ const newStoragePath = `media/${newAccessLevel}/${metadata.ownerId}/${metadata.collectionName}/${fileNamePart}`;
1181
+ console.log(
1182
+ `[MediaService] Moving file for ${mediaId} from ${oldStoragePath} to ${newStoragePath}`
1183
+ );
1184
+ const oldStorageFileRef = ref(this.storage, oldStoragePath);
1185
+ const newStorageFileRef = ref(this.storage, newStoragePath);
1186
+ try {
1187
+ console.log(`[MediaService] Downloading bytes from ${oldStoragePath}`);
1188
+ const fileBytes = await getBytes(oldStorageFileRef);
1189
+ console.log(
1190
+ `[MediaService] Successfully downloaded ${fileBytes.byteLength} bytes from ${oldStoragePath}`
1191
+ );
1192
+ console.log(`[MediaService] Uploading bytes to ${newStoragePath}`);
1193
+ await uploadBytes(newStorageFileRef, fileBytes, {
1194
+ contentType: metadata.contentType
1195
+ });
1196
+ console.log(
1197
+ `[MediaService] Successfully uploaded bytes to ${newStoragePath}`
1198
+ );
1199
+ const newDownloadURL = await getDownloadURL(newStorageFileRef);
1200
+ console.log(
1201
+ `[MediaService] Got new download URL for ${newStoragePath}: ${newDownloadURL}`
1202
+ );
1203
+ const updateData = {
1204
+ accessLevel: newAccessLevel,
1205
+ path: newStoragePath,
1206
+ url: newDownloadURL,
1207
+ updatedAt: Timestamp.now()
1208
+ };
1209
+ const metadataDocRef = doc(this.db, MEDIA_METADATA_COLLECTION, mediaId);
1210
+ console.log(
1211
+ `[MediaService] Updating Firestore metadata for ${mediaId} with new data:`,
1212
+ updateData
1213
+ );
1214
+ await updateDoc(metadataDocRef, updateData);
1215
+ console.log(
1216
+ `[MediaService] Successfully updated Firestore metadata for ${mediaId}`
1217
+ );
1218
+ try {
1219
+ console.log(`[MediaService] Deleting old file from ${oldStoragePath}`);
1220
+ await deleteObject(oldStorageFileRef);
1221
+ console.log(
1222
+ `[MediaService] Successfully deleted old file from ${oldStoragePath}`
1223
+ );
1224
+ } catch (deleteError) {
1225
+ console.error(
1226
+ `[MediaService] Failed to delete old file from ${oldStoragePath} for media ID ${mediaId}. This file is now orphaned. Error:`,
1227
+ deleteError
1228
+ );
1229
+ }
1230
+ return { ...metadata, ...updateData };
1231
+ } catch (error) {
1232
+ console.error(
1233
+ `[MediaService] Error updating media access level and moving file for ${mediaId}:`,
1234
+ error
1235
+ );
1236
+ if (newStorageFileRef && error.code !== "storage/object-not-found" && ((_a = error.message) == null ? void 0 : _a.includes("uploadBytes"))) {
1237
+ console.warn(
1238
+ `[MediaService] Attempting to delete partially uploaded file at ${newStoragePath} due to error.`
1239
+ );
1240
+ try {
1241
+ await deleteObject(newStorageFileRef);
1242
+ console.warn(
1243
+ `[MediaService] Cleaned up partially uploaded file at ${newStoragePath}.`
1244
+ );
1245
+ } catch (cleanupError) {
1246
+ console.error(
1247
+ `[MediaService] Failed to cleanup partially uploaded file at ${newStoragePath}:`,
1248
+ cleanupError
1249
+ );
1250
+ }
1251
+ }
1252
+ throw error;
1253
+ }
1254
+ }
1255
+ /**
1256
+ * List all media for an owner, optionally filtered by collection and access level.
1257
+ * @param ownerId - ID of the owner.
1258
+ * @param collectionName - Optional: Filter by collection name.
1259
+ * @param accessLevel - Optional: Filter by access level.
1260
+ * @param count - Optional: Number of items to fetch.
1261
+ * @param startAfterId - Optional: ID of the document to start after (for pagination).
1262
+ */
1263
+ async listMedia(ownerId, collectionName, accessLevel, count, startAfterId) {
1264
+ console.log(`[MediaService] Listing media for owner: ${ownerId}`);
1265
+ let qConstraints = [where("ownerId", "==", ownerId)];
1266
+ if (collectionName) {
1267
+ qConstraints.push(where("collectionName", "==", collectionName));
1268
+ }
1269
+ if (accessLevel) {
1270
+ qConstraints.push(where("accessLevel", "==", accessLevel));
1271
+ }
1272
+ qConstraints.push(orderBy("createdAt", "desc"));
1273
+ if (count) {
1274
+ qConstraints.push(limit(count));
1275
+ }
1276
+ if (startAfterId) {
1277
+ const startAfterDoc = await this.getMediaMetadata(startAfterId);
1278
+ if (startAfterDoc) {
1279
+ }
1280
+ }
1281
+ const finalQuery = query(
1282
+ collection(this.db, MEDIA_METADATA_COLLECTION),
1283
+ ...qConstraints
1284
+ );
1285
+ try {
1286
+ const querySnapshot = await getDocs(finalQuery);
1287
+ const mediaList = querySnapshot.docs.map(
1288
+ (doc34) => doc34.data()
1289
+ );
1290
+ console.log(`[MediaService] Found ${mediaList.length} media items.`);
1291
+ return mediaList;
1292
+ } catch (error) {
1293
+ console.error("[MediaService] Error listing media:", error);
1294
+ throw error;
1295
+ }
1296
+ }
1297
+ /**
1298
+ * Get download URL for media. (Convenience, as URL is in metadata)
1299
+ * @param mediaId - ID of the media.
1300
+ */
1301
+ async getMediaDownloadUrl(mediaId) {
1302
+ console.log(`[MediaService] Getting download URL for media ID: ${mediaId}`);
1303
+ const metadata = await this.getMediaMetadata(mediaId);
1304
+ if (metadata && metadata.url) {
1305
+ console.log(`[MediaService] URL found: ${metadata.url}`);
1306
+ return metadata.url;
1307
+ }
1308
+ console.log(`[MediaService] URL not found for media ID: ${mediaId}`);
1309
+ return null;
1310
+ }
1311
+ };
1312
+
1313
+ // src/services/patient/utils/profile.utils.ts
1314
+ import {
1315
+ getDoc as getDoc6,
1316
+ setDoc as setDoc5,
1317
+ updateDoc as updateDoc4,
1318
+ arrayUnion as arrayUnion2,
1319
+ arrayRemove as arrayRemove2,
1320
+ serverTimestamp as serverTimestamp4,
1321
+ increment,
1322
+ Timestamp as Timestamp5,
1323
+ collection as collection4,
1324
+ query as query4,
1325
+ where as where4,
1326
+ getDocs as getDocs4,
1327
+ limit as limit3,
1328
+ startAfter as startAfter2,
1329
+ doc as doc5
1330
+ } from "firebase/firestore";
1331
+ import { z as z9 } from "zod";
1332
+
1333
+ // src/types/patient/medical-info.types.ts
1334
+ var PATIENT_MEDICAL_INFO_COLLECTION = "medical_info";
1335
+ var DEFAULT_MEDICAL_INFO = {
1336
+ vitalStats: {},
1337
+ blockingConditions: [],
1338
+ contraindications: [],
1339
+ allergies: [],
1340
+ currentMedications: []
1341
+ };
1342
+
1343
+ // src/types/patient/index.ts
1344
+ var PATIENTS_COLLECTION = "patients";
1345
+ var PATIENT_SENSITIVE_INFO_COLLECTION = "sensitive-info";
1346
+ var PATIENT_MEDICAL_HISTORY_COLLECTION = "medical-history";
1347
+ var PATIENT_APPOINTMENTS_COLLECTION = "appointments";
1348
+ var PATIENT_LOCATION_INFO_COLLECTION = "location-info";
1349
+ var Gender = /* @__PURE__ */ ((Gender2) => {
1350
+ Gender2["MALE"] = "male";
1351
+ Gender2["FEMALE"] = "female";
1352
+ Gender2["TRANSGENDER_MALE"] = "transgender_male";
1353
+ Gender2["TRANSGENDER_FEMALE"] = "transgender_female";
1354
+ Gender2["PREFER_NOT_TO_SAY"] = "prefer_not_to_say";
1355
+ Gender2["OTHER"] = "other";
1356
+ return Gender2;
1357
+ })(Gender || {});
1358
+
1359
+ // src/validations/patient.schema.ts
1360
+ import { z as z7 } from "zod";
1361
+ import { Timestamp as Timestamp3 } from "firebase/firestore";
1362
+
1363
+ // src/validations/media.schema.ts
1364
+ import { z as z4 } from "zod";
1365
+ var mediaResourceSchema = z4.union([
1366
+ z4.string().url(),
1367
+ z4.instanceof(File),
1368
+ z4.instanceof(Blob)
1369
+ ]);
1370
+
1371
+ // src/validations/patient/medical-info.schema.ts
1372
+ import { z as z6 } from "zod";
1373
+
1374
+ // src/types/patient/allergies.ts
1375
+ var AllergyType = /* @__PURE__ */ ((AllergyType2) => {
1376
+ AllergyType2["MEDICATION"] = "medication";
1377
+ AllergyType2["FOOD"] = "food";
1378
+ AllergyType2["ENVIRONMENTAL"] = "environmental";
1379
+ AllergyType2["LATEX"] = "latex";
1380
+ AllergyType2["COSMETIC"] = "cosmetic";
1381
+ AllergyType2["OTHER"] = "other";
1382
+ return AllergyType2;
1383
+ })(AllergyType || {});
1384
+ var MedicationAllergySubtype = /* @__PURE__ */ ((MedicationAllergySubtype2) => {
1385
+ MedicationAllergySubtype2["ANTIBIOTICS"] = "antibiotics";
1386
+ MedicationAllergySubtype2["NSAIDS"] = "nsaids";
1387
+ MedicationAllergySubtype2["OPIOIDS"] = "opioids";
1388
+ MedicationAllergySubtype2["ANESTHETICS"] = "anesthetics";
1389
+ MedicationAllergySubtype2["VACCINES"] = "vaccines";
1390
+ MedicationAllergySubtype2["OTHER"] = "other";
1391
+ return MedicationAllergySubtype2;
1392
+ })(MedicationAllergySubtype || {});
1393
+ var FoodAllergySubtype = /* @__PURE__ */ ((FoodAllergySubtype2) => {
1394
+ FoodAllergySubtype2["NUTS"] = "nuts";
1395
+ FoodAllergySubtype2["SHELLFISH"] = "shellfish";
1396
+ FoodAllergySubtype2["DAIRY"] = "dairy";
1397
+ FoodAllergySubtype2["EGGS"] = "eggs";
1398
+ FoodAllergySubtype2["WHEAT"] = "wheat";
1399
+ FoodAllergySubtype2["SOY"] = "soy";
1400
+ FoodAllergySubtype2["FISH"] = "fish";
1401
+ FoodAllergySubtype2["FRUITS"] = "fruits";
1402
+ FoodAllergySubtype2["OTHER"] = "other";
1403
+ return FoodAllergySubtype2;
1404
+ })(FoodAllergySubtype || {});
1405
+ var EnvironmentalAllergySubtype = /* @__PURE__ */ ((EnvironmentalAllergySubtype2) => {
1406
+ EnvironmentalAllergySubtype2["POLLEN"] = "pollen";
1407
+ EnvironmentalAllergySubtype2["DUST"] = "dust";
1408
+ EnvironmentalAllergySubtype2["MOLD"] = "mold";
1409
+ EnvironmentalAllergySubtype2["PET_DANDER"] = "pet_dander";
1410
+ EnvironmentalAllergySubtype2["INSECTS"] = "insects";
1411
+ EnvironmentalAllergySubtype2["OTHER"] = "other";
1412
+ return EnvironmentalAllergySubtype2;
1413
+ })(EnvironmentalAllergySubtype || {});
1414
+ var CosmeticAllergySubtype = /* @__PURE__ */ ((CosmeticAllergySubtype2) => {
1415
+ CosmeticAllergySubtype2["FRAGRANCES"] = "fragrances";
1416
+ CosmeticAllergySubtype2["PRESERVATIVES"] = "preservatives";
1417
+ CosmeticAllergySubtype2["DYES"] = "dyes";
1418
+ CosmeticAllergySubtype2["METALS"] = "metals";
1419
+ CosmeticAllergySubtype2["OTHER"] = "other";
1420
+ return CosmeticAllergySubtype2;
1421
+ })(CosmeticAllergySubtype || {});
1422
+
1423
+ // src/backoffice/types/static/blocking-condition.types.ts
1424
+ var BlockingCondition = /* @__PURE__ */ ((BlockingCondition2) => {
1425
+ BlockingCondition2["PREGNANCY"] = "pregnancy";
1426
+ BlockingCondition2["BREASTFEEDING"] = "breastfeeding";
1427
+ BlockingCondition2["ACTIVE_INFECTION"] = "active_infection";
1428
+ BlockingCondition2["SKIN_CONDITION"] = "skin_condition";
1429
+ BlockingCondition2["AUTOIMMUNE_DISEASE"] = "autoimmune_disease";
1430
+ BlockingCondition2["BLOOD_THINNERS"] = "blood_thinners";
1431
+ BlockingCondition2["RECENT_SURGERY"] = "recent_surgery";
1432
+ BlockingCondition2["DIABETES"] = "diabetes";
1433
+ BlockingCondition2["HEART_CONDITION"] = "heart_condition";
1434
+ BlockingCondition2["HIGH_BLOOD_PRESSURE"] = "high_blood_pressure";
1435
+ BlockingCondition2["KELOID_SCARRING"] = "keloid_scarring";
1436
+ BlockingCondition2["METAL_IMPLANTS"] = "metal_implants";
1437
+ BlockingCondition2["PACEMAKER"] = "pacemaker";
1438
+ BlockingCondition2["CANCER"] = "cancer";
1439
+ BlockingCondition2["EPILEPSY"] = "epilepsy";
1440
+ return BlockingCondition2;
1441
+ })(BlockingCondition || {});
1442
+
1443
+ // src/backoffice/types/static/contraindication.types.ts
1444
+ var Contraindication = /* @__PURE__ */ ((Contraindication2) => {
1445
+ Contraindication2["SENSITIVE_SKIN"] = "sensitive_skin";
1446
+ Contraindication2["RECENT_TANNING"] = "recent_tanning";
1447
+ Contraindication2["RECENT_BOTOX"] = "recent_botox";
1448
+ Contraindication2["RECENT_FILLERS"] = "recent_fillers";
1449
+ Contraindication2["SKIN_ALLERGIES"] = "skin_allergies";
1450
+ Contraindication2["MEDICATIONS"] = "medications";
1451
+ Contraindication2["RECENT_CHEMICAL_PEEL"] = "recent_chemical_peel";
1452
+ Contraindication2["RECENT_LASER"] = "recent_laser";
1453
+ Contraindication2["SKIN_INFLAMMATION"] = "skin_inflammation";
1454
+ Contraindication2["OPEN_WOUNDS"] = "open_wounds";
1455
+ Contraindication2["HERPES_SIMPLEX"] = "herpes_simplex";
1456
+ Contraindication2["COLD_SORES"] = "cold_sores";
1457
+ return Contraindication2;
1458
+ })(Contraindication || {});
1459
+
1460
+ // src/validations/common.schema.ts
1461
+ import { z as z5 } from "zod";
1462
+ import { Timestamp as Timestamp2 } from "firebase/firestore";
1463
+ var timestampSchema2 = z5.union([
1464
+ z5.object({
1465
+ seconds: z5.number(),
1466
+ nanoseconds: z5.number()
1467
+ }),
1468
+ z5.instanceof(Timestamp2)
1469
+ ]).transform((data) => {
1470
+ if (data instanceof Timestamp2) {
1471
+ return data;
1472
+ }
1473
+ return new Timestamp2(data.seconds, data.nanoseconds);
1474
+ });
1475
+
1476
+ // src/validations/patient/medical-info.schema.ts
1477
+ var allergySubtypeSchema = z6.union([
1478
+ z6.nativeEnum(MedicationAllergySubtype),
1479
+ z6.nativeEnum(FoodAllergySubtype),
1480
+ z6.nativeEnum(EnvironmentalAllergySubtype),
1481
+ z6.nativeEnum(CosmeticAllergySubtype),
1482
+ z6.literal("other")
1483
+ ]);
1484
+ var allergySchema = z6.object({
1485
+ type: z6.nativeEnum(AllergyType),
1486
+ subtype: allergySubtypeSchema,
1487
+ name: z6.string().optional().nullable(),
1488
+ severity: z6.enum(["mild", "moderate", "severe"]).optional(),
1489
+ reaction: z6.string().optional().nullable(),
1490
+ diagnosed: timestampSchema2.optional().nullable(),
1491
+ notes: z6.string().optional().nullable()
1492
+ });
1493
+ var vitalStatsSchema = z6.object({
1494
+ height: z6.number().positive().optional(),
1495
+ weight: z6.number().positive().optional(),
1496
+ bloodType: z6.enum(["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"]).optional(),
1497
+ bloodPressure: z6.object({
1498
+ systolic: z6.number().min(70).max(200),
1499
+ diastolic: z6.number().min(40).max(130),
1500
+ lastMeasured: timestampSchema2
1501
+ }).optional()
1502
+ });
1503
+ var blockingConditionSchema = z6.object({
1504
+ condition: z6.nativeEnum(BlockingCondition),
1505
+ diagnosedAt: timestampSchema2,
1506
+ notes: z6.string().optional().nullable(),
1507
+ isActive: z6.boolean()
1508
+ });
1509
+ var contraindicationSchema = z6.object({
1510
+ condition: z6.nativeEnum(Contraindication),
1511
+ lastOccurrence: timestampSchema2,
1512
+ frequency: z6.enum(["rare", "occasional", "frequent"]),
1513
+ notes: z6.string().optional().nullable(),
1514
+ isActive: z6.boolean()
1515
+ });
1516
+ var medicationSchema = z6.object({
1517
+ name: z6.string().min(1),
1518
+ dosage: z6.string().min(1),
1519
+ frequency: z6.string().min(1),
1520
+ startDate: timestampSchema2.optional().nullable(),
1521
+ endDate: timestampSchema2.optional().nullable(),
1522
+ prescribedBy: z6.string().optional().nullable()
1523
+ });
1524
+ var patientMedicalInfoSchema = z6.object({
1525
+ patientId: z6.string(),
1526
+ vitalStats: vitalStatsSchema,
1527
+ blockingConditions: z6.array(blockingConditionSchema),
1528
+ contraindications: z6.array(contraindicationSchema),
1529
+ allergies: z6.array(allergySchema),
1530
+ currentMedications: z6.array(medicationSchema),
1531
+ emergencyNotes: z6.string().optional(),
1532
+ lastUpdated: timestampSchema2,
1533
+ updatedBy: z6.string(),
1534
+ verifiedBy: z6.string().optional(),
1535
+ verifiedAt: timestampSchema2.optional()
1536
+ });
1537
+ var createPatientMedicalInfoSchema = patientMedicalInfoSchema.omit({
1538
+ patientId: true,
1539
+ lastUpdated: true,
1540
+ updatedBy: true,
1541
+ verifiedBy: true,
1542
+ verifiedAt: true
1543
+ });
1544
+ var updatePatientMedicalInfoSchema = createPatientMedicalInfoSchema.partial();
1545
+ var updateVitalStatsSchema = vitalStatsSchema;
1546
+ var addAllergySchema = allergySchema;
1547
+ var updateAllergySchema = allergySchema.partial().extend({
1548
+ allergyIndex: z6.number().min(0)
1549
+ });
1550
+ var addBlockingConditionSchema = blockingConditionSchema;
1551
+ var updateBlockingConditionSchema = blockingConditionSchema.partial().extend({
1552
+ conditionIndex: z6.number().min(0)
1553
+ });
1554
+ var addContraindicationSchema = contraindicationSchema;
1555
+ var updateContraindicationSchema = contraindicationSchema.partial().extend({
1556
+ contraindicationIndex: z6.number().min(0)
1557
+ });
1558
+ var addMedicationSchema = medicationSchema;
1559
+ var updateMedicationSchema = medicationSchema.partial().extend({
1560
+ medicationIndex: z6.number().min(0)
1561
+ });
1562
+
1563
+ // src/validations/patient.schema.ts
1564
+ var locationDataSchema = z7.object({
1565
+ latitude: z7.number().min(-90).max(90),
1566
+ longitude: z7.number().min(-180).max(180),
1567
+ geohash: z7.string().optional()
1568
+ });
1569
+ var addressDataSchema = z7.object({
1570
+ address: z7.string(),
1571
+ city: z7.string(),
1572
+ country: z7.string(),
1573
+ postalCode: z7.string()
1574
+ });
1575
+ var emergencyContactSchema = z7.object({
1576
+ name: z7.string(),
1577
+ relationship: z7.string(),
1578
+ phoneNumber: z7.string(),
1579
+ isNotifiable: z7.boolean()
1580
+ });
1581
+ var gamificationSchema = z7.object({
1582
+ level: z7.number(),
1583
+ points: z7.number()
1584
+ });
1585
+ var patientLocationInfoSchema = z7.object({
1586
+ patientId: z7.string(),
1587
+ userRef: z7.string(),
1588
+ locationData: locationDataSchema,
1589
+ createdAt: z7.instanceof(Timestamp3),
1590
+ updatedAt: z7.instanceof(Timestamp3)
1591
+ });
1592
+ var createPatientLocationInfoSchema = z7.object({
1593
+ patientId: z7.string(),
1594
+ userRef: z7.string(),
1595
+ locationData: locationDataSchema
1596
+ });
1597
+ var patientSensitiveInfoSchema = z7.object({
1598
+ patientId: z7.string(),
1599
+ userRef: z7.string(),
1600
+ firstName: z7.string().min(2),
1601
+ lastName: z7.string().min(2),
1602
+ dateOfBirth: z7.instanceof(Timestamp3).nullable(),
1603
+ gender: z7.nativeEnum(Gender),
1604
+ email: z7.string().email().optional(),
1605
+ phoneNumber: z7.string().optional(),
1606
+ alternativePhoneNumber: z7.string().optional(),
1607
+ addressData: addressDataSchema.optional(),
1608
+ emergencyContacts: z7.array(emergencyContactSchema).optional(),
1609
+ createdAt: z7.instanceof(Timestamp3),
1610
+ updatedAt: z7.instanceof(Timestamp3)
1611
+ });
1612
+ var patientDoctorSchema = z7.object({
1613
+ userRef: z7.string(),
1614
+ assignedAt: z7.instanceof(Timestamp3),
1615
+ assignedBy: z7.string().optional(),
1616
+ isActive: z7.boolean(),
1617
+ notes: z7.string().optional()
1618
+ });
1619
+ var patientClinicSchema = z7.object({
1620
+ clinicId: z7.string(),
1621
+ assignedAt: z7.instanceof(Timestamp3),
1622
+ assignedBy: z7.string().optional(),
1623
+ isActive: z7.boolean(),
1624
+ notes: z7.string().optional()
1625
+ });
1626
+ var patientProfileSchema = z7.object({
1627
+ id: z7.string(),
1628
+ userRef: z7.string(),
1629
+ displayName: z7.string(),
1630
+ gamification: gamificationSchema,
1631
+ expoTokens: z7.array(z7.string()),
1632
+ isActive: z7.boolean(),
1633
+ isVerified: z7.boolean(),
1634
+ phoneNumber: z7.string().nullable().optional(),
1635
+ dateOfBirth: z7.instanceof(Timestamp3).nullable().optional(),
1636
+ doctors: z7.array(patientDoctorSchema),
1637
+ clinics: z7.array(patientClinicSchema),
1638
+ doctorIds: z7.array(z7.string()),
1639
+ clinicIds: z7.array(z7.string()),
1640
+ createdAt: z7.instanceof(Timestamp3),
1641
+ updatedAt: z7.instanceof(Timestamp3)
1642
+ });
1643
+ var createPatientProfileSchema = z7.object({
1644
+ userRef: z7.string(),
1645
+ displayName: z7.string(),
1646
+ expoTokens: z7.array(z7.string()),
1647
+ gamification: gamificationSchema.optional(),
1648
+ isActive: z7.boolean(),
1649
+ isVerified: z7.boolean(),
1650
+ doctors: z7.array(patientDoctorSchema).optional(),
1651
+ clinics: z7.array(patientClinicSchema).optional(),
1652
+ doctorIds: z7.array(z7.string()).optional(),
1653
+ clinicIds: z7.array(z7.string()).optional()
1654
+ });
1655
+ var createPatientSensitiveInfoSchema = z7.object({
1656
+ patientId: z7.string(),
1657
+ userRef: z7.string(),
1658
+ photoUrl: mediaResourceSchema.nullable().optional(),
1659
+ firstName: z7.string().min(2),
1660
+ lastName: z7.string().min(2),
1661
+ dateOfBirth: z7.instanceof(Timestamp3).nullable(),
1662
+ gender: z7.nativeEnum(Gender),
1663
+ email: z7.string().email().optional(),
1664
+ phoneNumber: z7.string().optional(),
1665
+ alternativePhoneNumber: z7.string().optional(),
1666
+ addressData: addressDataSchema.optional(),
1667
+ emergencyContacts: z7.array(emergencyContactSchema).optional()
1668
+ });
1669
+ var searchPatientsSchema = z7.object({
1670
+ clinicId: z7.string().optional(),
1671
+ practitionerId: z7.string().optional()
1672
+ }).refine((data) => data.clinicId || data.practitionerId, {
1673
+ message: "At least one of clinicId or practitionerId must be provided",
1674
+ path: []
1675
+ // Optional: specify a path like ['clinicId'] or ['practitionerId']
1676
+ });
1677
+ var requesterInfoSchema = z7.object({
1678
+ id: z7.string(),
1679
+ role: z7.enum(["clinic_admin", "practitioner"]),
1680
+ associatedClinicId: z7.string().optional(),
1681
+ associatedPractitionerId: z7.string().optional()
1682
+ }).refine(
1683
+ (data) => {
1684
+ if (data.role === "clinic_admin") {
1685
+ return !!data.associatedClinicId;
1686
+ } else if (data.role === "practitioner") {
1687
+ return !!data.associatedPractitionerId;
1688
+ }
1689
+ return false;
1690
+ },
1691
+ {
1692
+ message: "Associated ID (clinic or practitioner) is required based on role",
1693
+ path: ["associatedClinicId", "associatedPractitionerId"]
1694
+ }
1695
+ );
1696
+
1697
+ // src/services/patient/utils/docs.utils.ts
1698
+ import {
1699
+ doc as doc2,
1700
+ collection as collection2,
1701
+ query as query2,
1702
+ where as where2,
1703
+ getDocs as getDocs2,
1704
+ getDoc as getDoc3
1381
1705
  } from "firebase/firestore";
1382
1706
 
1383
1707
  // src/services/patient/utils/sensitive.utils.ts
1384
1708
  import {
1385
- getDoc,
1386
- updateDoc,
1387
- setDoc,
1709
+ getDoc as getDoc2,
1710
+ updateDoc as updateDoc2,
1711
+ setDoc as setDoc2,
1388
1712
  serverTimestamp
1389
1713
  } from "firebase/firestore";
1390
- import { z as z7 } from "zod";
1391
- var createSensitiveInfoUtil = async (db, data, requesterUserId) => {
1714
+ import { z as z8 } from "zod";
1715
+ var handlePhotoUrlUpload = async (photoUrl, patientId, mediaService) => {
1716
+ if (!photoUrl) {
1717
+ return null;
1718
+ }
1719
+ if (typeof photoUrl === "string") {
1720
+ return photoUrl;
1721
+ }
1722
+ if (photoUrl instanceof File || photoUrl instanceof Blob) {
1723
+ const mediaMetadata = await mediaService.uploadMedia(
1724
+ photoUrl,
1725
+ patientId,
1726
+ // Using patientId as ownerId
1727
+ "private" /* PRIVATE */,
1728
+ // Sensitive info should be private
1729
+ "patient_sensitive_photos",
1730
+ photoUrl instanceof File ? photoUrl.name : `sensitive_photo_${patientId}`
1731
+ );
1732
+ return mediaMetadata.url;
1733
+ }
1734
+ return null;
1735
+ };
1736
+ var createSensitiveInfoUtil = async (db, data, requesterUserId, mediaService) => {
1392
1737
  try {
1393
1738
  if (data.userRef !== requesterUserId) {
1394
1739
  throw new Error("Only patient can create their sensitive information");
1395
1740
  }
1396
1741
  const validatedData = createPatientSensitiveInfoSchema.parse(data);
1397
- const sensitiveDoc = await getDoc(
1742
+ const sensitiveDoc = await getDoc2(
1398
1743
  getSensitiveInfoDocRef(db, data.patientId)
1399
1744
  );
1400
1745
  if (sensitiveDoc.exists()) {
1401
1746
  throw new Error("Sensitive information already exists for this patient");
1402
1747
  }
1748
+ let processedPhotoUrl = null;
1749
+ if (validatedData.photoUrl && mediaService) {
1750
+ processedPhotoUrl = await handlePhotoUrlUpload(
1751
+ validatedData.photoUrl,
1752
+ data.patientId,
1753
+ mediaService
1754
+ );
1755
+ } else if (typeof validatedData.photoUrl === "string") {
1756
+ processedPhotoUrl = validatedData.photoUrl;
1757
+ }
1403
1758
  const sensitiveInfoData = {
1404
1759
  ...validatedData,
1760
+ photoUrl: processedPhotoUrl,
1405
1761
  createdAt: serverTimestamp(),
1406
1762
  updatedAt: serverTimestamp()
1407
1763
  };
1408
- await setDoc(getSensitiveInfoDocRef(db, data.patientId), sensitiveInfoData);
1409
- const createdDoc = await getDoc(getSensitiveInfoDocRef(db, data.patientId));
1764
+ await setDoc2(getSensitiveInfoDocRef(db, data.patientId), sensitiveInfoData);
1765
+ const createdDoc = await getDoc2(getSensitiveInfoDocRef(db, data.patientId));
1410
1766
  if (!createdDoc.exists()) {
1411
1767
  throw new Error("Failed to create sensitive information");
1412
1768
  }
1413
1769
  return createdDoc.data();
1414
1770
  } catch (error) {
1415
- if (error instanceof z7.ZodError) {
1771
+ if (error instanceof z8.ZodError) {
1416
1772
  throw new Error("Invalid sensitive info data: " + error.message);
1417
1773
  }
1418
1774
  throw error;
@@ -1420,17 +1776,32 @@ var createSensitiveInfoUtil = async (db, data, requesterUserId) => {
1420
1776
  };
1421
1777
  var getSensitiveInfoUtil = async (db, patientId, requesterUserId) => {
1422
1778
  await initSensitiveInfoDocIfNotExists(db, patientId, requesterUserId);
1423
- const sensitiveDoc = await getDoc(getSensitiveInfoDocRef(db, patientId));
1779
+ const sensitiveDoc = await getDoc2(getSensitiveInfoDocRef(db, patientId));
1424
1780
  return sensitiveDoc.exists() ? sensitiveDoc.data() : null;
1425
1781
  };
1426
- var updateSensitiveInfoUtil = async (db, patientId, data, requesterUserId) => {
1782
+ var updateSensitiveInfoUtil = async (db, patientId, data, requesterUserId, mediaService) => {
1427
1783
  await initSensitiveInfoDocIfNotExists(db, patientId, requesterUserId);
1784
+ let processedPhotoUrl = void 0;
1785
+ if (data.photoUrl !== void 0) {
1786
+ if (mediaService) {
1787
+ processedPhotoUrl = await handlePhotoUrlUpload(
1788
+ data.photoUrl,
1789
+ patientId,
1790
+ mediaService
1791
+ );
1792
+ } else if (typeof data.photoUrl === "string" || data.photoUrl === null) {
1793
+ processedPhotoUrl = data.photoUrl;
1794
+ } else {
1795
+ throw new Error("MediaService required to process photo upload");
1796
+ }
1797
+ }
1428
1798
  const updateData = {
1429
1799
  ...data,
1800
+ photoUrl: processedPhotoUrl,
1430
1801
  updatedAt: serverTimestamp()
1431
1802
  };
1432
- await updateDoc(getSensitiveInfoDocRef(db, patientId), updateData);
1433
- const updatedDoc = await getDoc(getSensitiveInfoDocRef(db, patientId));
1803
+ await updateDoc2(getSensitiveInfoDocRef(db, patientId), updateData);
1804
+ const updatedDoc = await getDoc2(getSensitiveInfoDocRef(db, patientId));
1434
1805
  if (!updatedDoc.exists()) {
1435
1806
  throw new Error("Failed to retrieve updated sensitive information");
1436
1807
  }
@@ -1439,21 +1810,21 @@ var updateSensitiveInfoUtil = async (db, patientId, data, requesterUserId) => {
1439
1810
 
1440
1811
  // src/services/patient/utils/docs.utils.ts
1441
1812
  var getPatientDocRef = (db, patientId) => {
1442
- return doc(db, PATIENTS_COLLECTION, patientId);
1813
+ return doc2(db, PATIENTS_COLLECTION, patientId);
1443
1814
  };
1444
1815
  var getPatientDocRefByUserRef = async (db, userRef) => {
1445
- const patientsRef = collection(db, PATIENTS_COLLECTION);
1446
- const q = query(patientsRef, where("userRef", "==", userRef));
1447
- const querySnapshot = await getDocs(q);
1816
+ const patientsRef = collection2(db, PATIENTS_COLLECTION);
1817
+ const q = query2(patientsRef, where2("userRef", "==", userRef));
1818
+ const querySnapshot = await getDocs2(q);
1448
1819
  if (querySnapshot.empty) {
1449
1820
  throw new Error("Patient profile not found");
1450
1821
  }
1451
- return doc(db, PATIENTS_COLLECTION, querySnapshot.docs[0].id);
1822
+ return doc2(db, PATIENTS_COLLECTION, querySnapshot.docs[0].id);
1452
1823
  };
1453
1824
  var getSensitiveInfoDocRef = (db, patientId) => {
1454
1825
  const path = `${PATIENTS_COLLECTION}/${patientId}/${PATIENT_SENSITIVE_INFO_COLLECTION}/${patientId}`;
1455
1826
  console.log(`[getSensitiveInfoDocRef] Creating reference with path: ${path}`);
1456
- return doc(
1827
+ return doc2(
1457
1828
  db,
1458
1829
  PATIENTS_COLLECTION,
1459
1830
  patientId,
@@ -1464,7 +1835,7 @@ var getSensitiveInfoDocRef = (db, patientId) => {
1464
1835
  var getLocationInfoDocRef = (db, patientId) => {
1465
1836
  const path = `${PATIENTS_COLLECTION}/${patientId}/${PATIENT_LOCATION_INFO_COLLECTION}/${patientId}`;
1466
1837
  console.log(`[getLocationInfoDocRef] Creating reference with path: ${path}`);
1467
- return doc(
1838
+ return doc2(
1468
1839
  db,
1469
1840
  PATIENTS_COLLECTION,
1470
1841
  patientId,
@@ -1475,7 +1846,7 @@ var getLocationInfoDocRef = (db, patientId) => {
1475
1846
  var getMedicalInfoDocRef = (db, patientId) => {
1476
1847
  const path = `${PATIENTS_COLLECTION}/${patientId}/${PATIENT_MEDICAL_INFO_COLLECTION}/${patientId}`;
1477
1848
  console.log(`[getMedicalInfoDocRef] Creating reference with path: ${path}`);
1478
- return doc(
1849
+ return doc2(
1479
1850
  db,
1480
1851
  PATIENTS_COLLECTION,
1481
1852
  patientId,
@@ -1492,7 +1863,7 @@ var initSensitiveInfoDocIfNotExists = async (db, patientId, userRef) => {
1492
1863
  console.log(
1493
1864
  `[initSensitiveInfoDocIfNotExists] Got document reference: ${sensitiveInfoRef.path}`
1494
1865
  );
1495
- const sensitiveDoc = await getDoc2(sensitiveInfoRef);
1866
+ const sensitiveDoc = await getDoc3(sensitiveInfoRef);
1496
1867
  console.log(
1497
1868
  `[initSensitiveInfoDocIfNotExists] Document exists: ${sensitiveDoc.exists()}`
1498
1869
  );
@@ -1516,7 +1887,7 @@ var initSensitiveInfoDocIfNotExists = async (db, patientId, userRef) => {
1516
1887
  )
1517
1888
  );
1518
1889
  await createSensitiveInfoUtil(db, defaultSensitiveInfo, userRef);
1519
- const verifyDoc = await getDoc2(sensitiveInfoRef);
1890
+ const verifyDoc = await getDoc3(sensitiveInfoRef);
1520
1891
  console.log(
1521
1892
  `[initSensitiveInfoDocIfNotExists] Verification - document exists: ${verifyDoc.exists()}`
1522
1893
  );
@@ -1534,24 +1905,24 @@ var initSensitiveInfoDocIfNotExists = async (db, patientId, userRef) => {
1534
1905
 
1535
1906
  // src/services/patient/utils/medical.utils.ts
1536
1907
  import {
1537
- getDoc as getDoc4,
1538
- updateDoc as updateDoc2,
1539
- setDoc as setDoc3,
1908
+ getDoc as getDoc5,
1909
+ updateDoc as updateDoc3,
1910
+ setDoc as setDoc4,
1540
1911
  serverTimestamp as serverTimestamp3,
1541
1912
  arrayUnion,
1542
- Timestamp as Timestamp3
1913
+ Timestamp as Timestamp4
1543
1914
  } from "firebase/firestore";
1544
1915
 
1545
1916
  // src/services/patient/utils/practitioner.utils.ts
1546
1917
  import {
1547
- collection as collection2,
1548
- query as query2,
1549
- where as where2,
1550
- getDocs as getDocs2,
1551
- limit,
1918
+ collection as collection3,
1919
+ query as query3,
1920
+ where as where3,
1921
+ getDocs as getDocs3,
1922
+ limit as limit2,
1552
1923
  startAfter,
1553
- doc as doc2,
1554
- getDoc as getDoc3
1924
+ doc as doc3,
1925
+ getDoc as getDoc4
1555
1926
  } from "firebase/firestore";
1556
1927
 
1557
1928
  // src/types/practitioner/index.ts
@@ -1577,23 +1948,23 @@ var getPatientsByPractitionerUtil = async (db, practitionerId, options) => {
1577
1948
  `[getPatientsByPractitionerUtil] Fetching patients for practitioner ID: ${practitionerId} with options:`,
1578
1949
  options
1579
1950
  );
1580
- const patientsCollection = collection2(db, PATIENTS_COLLECTION);
1951
+ const patientsCollection = collection3(db, PATIENTS_COLLECTION);
1581
1952
  const constraints = [
1582
- where2("doctorIds", "array-contains", practitionerId)
1953
+ where3("doctorIds", "array-contains", practitionerId)
1583
1954
  ];
1584
- let q = query2(patientsCollection, ...constraints);
1955
+ let q = query3(patientsCollection, ...constraints);
1585
1956
  if (options == null ? void 0 : options.limit) {
1586
- q = query2(q, limit(options.limit));
1957
+ q = query3(q, limit2(options.limit));
1587
1958
  }
1588
1959
  if (options == null ? void 0 : options.startAfter) {
1589
- const startAfterDoc = await getDoc3(
1590
- doc2(db, PATIENTS_COLLECTION, options.startAfter)
1960
+ const startAfterDoc = await getDoc4(
1961
+ doc3(db, PATIENTS_COLLECTION, options.startAfter)
1591
1962
  );
1592
1963
  if (startAfterDoc.exists()) {
1593
- q = query2(q, startAfter(startAfterDoc));
1964
+ q = query3(q, startAfter(startAfterDoc));
1594
1965
  }
1595
1966
  }
1596
- const patientsSnapshot = await getDocs2(q);
1967
+ const patientsSnapshot = await getDocs3(q);
1597
1968
  const patients = [];
1598
1969
  patientsSnapshot.forEach((doc34) => {
1599
1970
  patients.push(doc34.data());
@@ -1626,7 +1997,7 @@ var getPatientsByPractitionerWithDetailsUtil = async (db, practitionerId, option
1626
1997
  const patientProfilesWithDetails = await Promise.all(
1627
1998
  patientProfiles.map(async (profile) => {
1628
1999
  try {
1629
- const sensitiveInfoDoc = await getDoc3(
2000
+ const sensitiveInfoDoc = await getDoc4(
1630
2001
  getSensitiveInfoDocRef(db, profile.id)
1631
2002
  );
1632
2003
  const sensitiveInfo = sensitiveInfoDoc.exists() ? sensitiveInfoDoc.data() : void 0;
@@ -1662,13 +2033,13 @@ var getPractitionerProfileByUserRef = async (db, userRef) => {
1662
2033
  console.log(
1663
2034
  `[getPractitionerProfileByUserRef] Fetching practitioner with userRef: ${userRef}`
1664
2035
  );
1665
- const practitionersCollection = collection2(db, PRACTITIONERS_COLLECTION);
1666
- const q = query2(
2036
+ const practitionersCollection = collection3(db, PRACTITIONERS_COLLECTION);
2037
+ const q = query3(
1667
2038
  practitionersCollection,
1668
- where2("userRef", "==", userRef),
1669
- limit(1)
2039
+ where3("userRef", "==", userRef),
2040
+ limit2(1)
1670
2041
  );
1671
- const querySnapshot = await getDocs2(q);
2042
+ const querySnapshot = await getDocs3(q);
1672
2043
  if (querySnapshot.empty) {
1673
2044
  console.log(
1674
2045
  `[getPractitionerProfileByUserRef] No practitioner found with userRef: ${userRef}`
@@ -1702,7 +2073,7 @@ var ensureMedicalInfoExists = async (db, patientId, userRef) => {
1702
2073
  console.log(
1703
2074
  `[ensureMedicalInfoExists] Got document reference: ${medicalInfoRef.path}`
1704
2075
  );
1705
- const medicalInfoDoc = await getDoc4(medicalInfoRef);
2076
+ const medicalInfoDoc = await getDoc5(medicalInfoRef);
1706
2077
  console.log(
1707
2078
  `[ensureMedicalInfoExists] Document exists: ${medicalInfoDoc.exists()}`
1708
2079
  );
@@ -1720,9 +2091,9 @@ var ensureMedicalInfoExists = async (db, patientId, userRef) => {
1720
2091
  (key, value) => value && typeof value === "object" && value.constructor && value.constructor.name === "Object" ? "[serverTimestamp]" : value
1721
2092
  )
1722
2093
  );
1723
- await setDoc3(medicalInfoRef, defaultData);
2094
+ await setDoc4(medicalInfoRef, defaultData);
1724
2095
  console.log(`[ensureMedicalInfoExists] Document created successfully`);
1725
- const verifyDoc = await getDoc4(medicalInfoRef);
2096
+ const verifyDoc = await getDoc5(medicalInfoRef);
1726
2097
  console.log(
1727
2098
  `[ensureMedicalInfoExists] Verification - document exists: ${verifyDoc.exists()}`
1728
2099
  );
@@ -1738,7 +2109,7 @@ var ensureMedicalInfoExists = async (db, patientId, userRef) => {
1738
2109
  };
1739
2110
  var checkMedicalAccessUtil = async (db, patientId, userRef, userRoles) => {
1740
2111
  var _a;
1741
- const patientDoc = await getDoc4(getPatientDocRef(db, patientId));
2112
+ const patientDoc = await getDoc5(getPatientDocRef(db, patientId));
1742
2113
  if (!patientDoc.exists()) {
1743
2114
  throw new Error("Patient profile not found");
1744
2115
  }
@@ -1771,17 +2142,17 @@ var checkMedicalAccessUtil = async (db, patientId, userRef, userRoles) => {
1771
2142
  var createMedicalInfoUtil = async (db, patientId, data, userRef, userRoles) => {
1772
2143
  await checkMedicalAccessUtil(db, patientId, userRef, userRoles);
1773
2144
  const validatedData = createPatientMedicalInfoSchema.parse(data);
1774
- await setDoc3(getMedicalInfoDocRef(db, patientId), {
2145
+ await setDoc4(getMedicalInfoDocRef(db, patientId), {
1775
2146
  ...validatedData,
1776
2147
  patientId,
1777
- lastUpdated: Timestamp3.now(),
2148
+ lastUpdated: Timestamp4.now(),
1778
2149
  updatedBy: userRef
1779
2150
  });
1780
2151
  };
1781
2152
  var getMedicalInfoUtil = async (db, patientId, userRef, userRoles) => {
1782
2153
  await checkMedicalAccessUtil(db, patientId, userRef, userRoles);
1783
2154
  const docRef = getMedicalInfoDocRef(db, patientId);
1784
- const snapshot = await getDoc4(docRef);
2155
+ const snapshot = await getDoc5(docRef);
1785
2156
  if (!snapshot.exists()) {
1786
2157
  throw new Error("Medicinske informacije nisu prona\u0111ene");
1787
2158
  }
@@ -1790,7 +2161,7 @@ var getMedicalInfoUtil = async (db, patientId, userRef, userRoles) => {
1790
2161
  var updateVitalStatsUtil = async (db, patientId, data, userRef) => {
1791
2162
  await ensureMedicalInfoExists(db, patientId, userRef);
1792
2163
  const validatedData = updateVitalStatsSchema.parse(data);
1793
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2164
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1794
2165
  vitalStats: validatedData,
1795
2166
  lastUpdated: serverTimestamp3(),
1796
2167
  updatedBy: userRef
@@ -1799,7 +2170,7 @@ var updateVitalStatsUtil = async (db, patientId, data, userRef) => {
1799
2170
  var addAllergyUtil = async (db, patientId, data, userRef) => {
1800
2171
  await ensureMedicalInfoExists(db, patientId, userRef);
1801
2172
  const validatedData = addAllergySchema.parse(data);
1802
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2173
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1803
2174
  allergies: arrayUnion(validatedData),
1804
2175
  lastUpdated: serverTimestamp3(),
1805
2176
  updatedBy: userRef
@@ -1808,7 +2179,7 @@ var addAllergyUtil = async (db, patientId, data, userRef) => {
1808
2179
  var updateAllergyUtil = async (db, patientId, data, userRef) => {
1809
2180
  const validatedData = updateAllergySchema.parse(data);
1810
2181
  const { allergyIndex, ...updateData } = validatedData;
1811
- const docSnapshot = await getDoc4(getMedicalInfoDocRef(db, patientId));
2182
+ const docSnapshot = await getDoc5(getMedicalInfoDocRef(db, patientId));
1812
2183
  if (!docSnapshot.exists()) throw new Error("Medical info not found");
1813
2184
  const medicalInfo = patientMedicalInfoSchema.parse(docSnapshot.data());
1814
2185
  if (allergyIndex >= medicalInfo.allergies.length) {
@@ -1819,14 +2190,14 @@ var updateAllergyUtil = async (db, patientId, data, userRef) => {
1819
2190
  ...updatedAllergies[allergyIndex],
1820
2191
  ...updateData
1821
2192
  };
1822
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2193
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1823
2194
  allergies: updatedAllergies,
1824
2195
  lastUpdated: serverTimestamp3(),
1825
2196
  updatedBy: userRef
1826
2197
  });
1827
2198
  };
1828
2199
  var removeAllergyUtil = async (db, patientId, allergyIndex, userRef) => {
1829
- const doc34 = await getDoc4(getMedicalInfoDocRef(db, patientId));
2200
+ const doc34 = await getDoc5(getMedicalInfoDocRef(db, patientId));
1830
2201
  if (!doc34.exists()) throw new Error("Medical info not found");
1831
2202
  const medicalInfo = doc34.data();
1832
2203
  if (allergyIndex >= medicalInfo.allergies.length) {
@@ -1835,7 +2206,7 @@ var removeAllergyUtil = async (db, patientId, allergyIndex, userRef) => {
1835
2206
  const updatedAllergies = medicalInfo.allergies.filter(
1836
2207
  (_, index) => index !== allergyIndex
1837
2208
  );
1838
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2209
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1839
2210
  allergies: updatedAllergies,
1840
2211
  lastUpdated: serverTimestamp3(),
1841
2212
  updatedBy: userRef
@@ -1844,7 +2215,7 @@ var removeAllergyUtil = async (db, patientId, allergyIndex, userRef) => {
1844
2215
  var addBlockingConditionUtil = async (db, patientId, data, userRef) => {
1845
2216
  await ensureMedicalInfoExists(db, patientId, userRef);
1846
2217
  const validatedData = addBlockingConditionSchema.parse(data);
1847
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2218
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1848
2219
  blockingConditions: arrayUnion(validatedData),
1849
2220
  lastUpdated: serverTimestamp3(),
1850
2221
  updatedBy: userRef
@@ -1853,7 +2224,7 @@ var addBlockingConditionUtil = async (db, patientId, data, userRef) => {
1853
2224
  var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
1854
2225
  const validatedData = updateBlockingConditionSchema.parse(data);
1855
2226
  const { conditionIndex, ...updateData } = validatedData;
1856
- const doc34 = await getDoc4(getMedicalInfoDocRef(db, patientId));
2227
+ const doc34 = await getDoc5(getMedicalInfoDocRef(db, patientId));
1857
2228
  if (!doc34.exists()) throw new Error("Medical info not found");
1858
2229
  const medicalInfo = doc34.data();
1859
2230
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
@@ -1864,14 +2235,14 @@ var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
1864
2235
  ...updatedConditions[conditionIndex],
1865
2236
  ...updateData
1866
2237
  };
1867
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2238
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1868
2239
  blockingConditions: updatedConditions,
1869
2240
  lastUpdated: serverTimestamp3(),
1870
2241
  updatedBy: userRef
1871
2242
  });
1872
2243
  };
1873
2244
  var removeBlockingConditionUtil = async (db, patientId, conditionIndex, userRef) => {
1874
- const doc34 = await getDoc4(getMedicalInfoDocRef(db, patientId));
2245
+ const doc34 = await getDoc5(getMedicalInfoDocRef(db, patientId));
1875
2246
  if (!doc34.exists()) throw new Error("Medical info not found");
1876
2247
  const medicalInfo = doc34.data();
1877
2248
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
@@ -1880,7 +2251,7 @@ var removeBlockingConditionUtil = async (db, patientId, conditionIndex, userRef)
1880
2251
  const updatedConditions = medicalInfo.blockingConditions.filter(
1881
2252
  (_, index) => index !== conditionIndex
1882
2253
  );
1883
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2254
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1884
2255
  blockingConditions: updatedConditions,
1885
2256
  lastUpdated: serverTimestamp3(),
1886
2257
  updatedBy: userRef
@@ -1889,7 +2260,7 @@ var removeBlockingConditionUtil = async (db, patientId, conditionIndex, userRef)
1889
2260
  var addContraindicationUtil = async (db, patientId, data, userRef) => {
1890
2261
  await ensureMedicalInfoExists(db, patientId, userRef);
1891
2262
  const validatedData = addContraindicationSchema.parse(data);
1892
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2263
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1893
2264
  contraindications: arrayUnion(validatedData),
1894
2265
  lastUpdated: serverTimestamp3(),
1895
2266
  updatedBy: userRef
@@ -1898,7 +2269,7 @@ var addContraindicationUtil = async (db, patientId, data, userRef) => {
1898
2269
  var updateContraindicationUtil = async (db, patientId, data, userRef) => {
1899
2270
  const validatedData = updateContraindicationSchema.parse(data);
1900
2271
  const { contraindicationIndex, ...updateData } = validatedData;
1901
- const doc34 = await getDoc4(getMedicalInfoDocRef(db, patientId));
2272
+ const doc34 = await getDoc5(getMedicalInfoDocRef(db, patientId));
1902
2273
  if (!doc34.exists()) throw new Error("Medical info not found");
1903
2274
  const medicalInfo = doc34.data();
1904
2275
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
@@ -1909,14 +2280,14 @@ var updateContraindicationUtil = async (db, patientId, data, userRef) => {
1909
2280
  ...updatedContraindications[contraindicationIndex],
1910
2281
  ...updateData
1911
2282
  };
1912
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2283
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1913
2284
  contraindications: updatedContraindications,
1914
2285
  lastUpdated: serverTimestamp3(),
1915
2286
  updatedBy: userRef
1916
2287
  });
1917
2288
  };
1918
2289
  var removeContraindicationUtil = async (db, patientId, contraindicationIndex, userRef) => {
1919
- const doc34 = await getDoc4(getMedicalInfoDocRef(db, patientId));
2290
+ const doc34 = await getDoc5(getMedicalInfoDocRef(db, patientId));
1920
2291
  if (!doc34.exists()) throw new Error("Medical info not found");
1921
2292
  const medicalInfo = doc34.data();
1922
2293
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
@@ -1925,7 +2296,7 @@ var removeContraindicationUtil = async (db, patientId, contraindicationIndex, us
1925
2296
  const updatedContraindications = medicalInfo.contraindications.filter(
1926
2297
  (_, index) => index !== contraindicationIndex
1927
2298
  );
1928
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2299
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1929
2300
  contraindications: updatedContraindications,
1930
2301
  lastUpdated: serverTimestamp3(),
1931
2302
  updatedBy: userRef
@@ -1934,7 +2305,7 @@ var removeContraindicationUtil = async (db, patientId, contraindicationIndex, us
1934
2305
  var addMedicationUtil = async (db, patientId, data, userRef) => {
1935
2306
  await ensureMedicalInfoExists(db, patientId, userRef);
1936
2307
  const validatedData = addMedicationSchema.parse(data);
1937
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2308
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1938
2309
  currentMedications: arrayUnion(validatedData),
1939
2310
  lastUpdated: serverTimestamp3(),
1940
2311
  updatedBy: userRef
@@ -1943,7 +2314,7 @@ var addMedicationUtil = async (db, patientId, data, userRef) => {
1943
2314
  var updateMedicationUtil = async (db, patientId, data, userRef) => {
1944
2315
  const validatedData = updateMedicationSchema.parse(data);
1945
2316
  const { medicationIndex, ...updateData } = validatedData;
1946
- const doc34 = await getDoc4(getMedicalInfoDocRef(db, patientId));
2317
+ const doc34 = await getDoc5(getMedicalInfoDocRef(db, patientId));
1947
2318
  if (!doc34.exists()) throw new Error("Medical info not found");
1948
2319
  const medicalInfo = doc34.data();
1949
2320
  if (medicationIndex >= medicalInfo.currentMedications.length) {
@@ -1954,14 +2325,14 @@ var updateMedicationUtil = async (db, patientId, data, userRef) => {
1954
2325
  ...updatedMedications[medicationIndex],
1955
2326
  ...updateData
1956
2327
  };
1957
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2328
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1958
2329
  currentMedications: updatedMedications,
1959
2330
  lastUpdated: serverTimestamp3(),
1960
2331
  updatedBy: userRef
1961
2332
  });
1962
2333
  };
1963
2334
  var removeMedicationUtil = async (db, patientId, medicationIndex, userRef) => {
1964
- const doc34 = await getDoc4(getMedicalInfoDocRef(db, patientId));
2335
+ const doc34 = await getDoc5(getMedicalInfoDocRef(db, patientId));
1965
2336
  if (!doc34.exists()) throw new Error("Medical info not found");
1966
2337
  const medicalInfo = doc34.data();
1967
2338
  if (medicationIndex >= medicalInfo.currentMedications.length) {
@@ -1970,7 +2341,7 @@ var removeMedicationUtil = async (db, patientId, medicationIndex, userRef) => {
1970
2341
  const updatedMedications = medicalInfo.currentMedications.filter(
1971
2342
  (_, index) => index !== medicationIndex
1972
2343
  );
1973
- await updateDoc2(getMedicalInfoDocRef(db, patientId), {
2344
+ await updateDoc3(getMedicalInfoDocRef(db, patientId), {
1974
2345
  currentMedications: updatedMedications,
1975
2346
  lastUpdated: serverTimestamp3(),
1976
2347
  updatedBy: userRef
@@ -1989,7 +2360,6 @@ var createPatientProfileUtil = async (db, data, generateId2) => {
1989
2360
  id: patientId,
1990
2361
  userRef: validatedData.userRef,
1991
2362
  displayName: validatedData.displayName,
1992
- profilePhoto: validatedData.profilePhoto || null,
1993
2363
  expoTokens: validatedData.expoTokens,
1994
2364
  gamification: validatedData.gamification || {
1995
2365
  level: 1,
@@ -2006,10 +2376,10 @@ var createPatientProfileUtil = async (db, data, generateId2) => {
2006
2376
  };
2007
2377
  patientProfileSchema.parse({
2008
2378
  ...patientData,
2009
- createdAt: Timestamp4.now(),
2010
- updatedAt: Timestamp4.now()
2379
+ createdAt: Timestamp5.now(),
2380
+ updatedAt: Timestamp5.now()
2011
2381
  });
2012
- await setDoc4(getPatientDocRef(db, patientId), patientData);
2382
+ await setDoc5(getPatientDocRef(db, patientId), patientData);
2013
2383
  console.log(`[createPatientProfileUtil] Creating sensitive info document`);
2014
2384
  let sensitiveInfoSuccess = false;
2015
2385
  try {
@@ -2023,69 +2393,91 @@ var createPatientProfileUtil = async (db, data, generateId2) => {
2023
2393
  );
2024
2394
  sensitiveInfoSuccess = true;
2025
2395
  } catch (sensitiveError) {
2026
- console.error(`[createPatientProfileUtil] Error creating sensitive info:`, sensitiveError);
2396
+ console.error(
2397
+ `[createPatientProfileUtil] Error creating sensitive info:`,
2398
+ sensitiveError
2399
+ );
2027
2400
  }
2028
2401
  console.log(`[createPatientProfileUtil] Creating medical info document`);
2029
2402
  let medicalInfoSuccess = false;
2030
2403
  try {
2031
2404
  await ensureMedicalInfoExists(db, patientId, validatedData.userRef);
2032
- console.log(`[createPatientProfileUtil] Medical info document created successfully`);
2405
+ console.log(
2406
+ `[createPatientProfileUtil] Medical info document created successfully`
2407
+ );
2033
2408
  medicalInfoSuccess = true;
2034
2409
  } catch (medicalError) {
2035
- console.error(`[createPatientProfileUtil] Error creating medical info:`, medicalError);
2410
+ console.error(
2411
+ `[createPatientProfileUtil] Error creating medical info:`,
2412
+ medicalError
2413
+ );
2036
2414
  }
2037
2415
  if (!sensitiveInfoSuccess || !medicalInfoSuccess) {
2038
- console.log(`[createPatientProfileUtil] Using fallback method to create documents`);
2416
+ console.log(
2417
+ `[createPatientProfileUtil] Using fallback method to create documents`
2418
+ );
2039
2419
  try {
2040
2420
  await testCreateSubDocuments(db, patientId, validatedData.userRef);
2041
- console.log(`[createPatientProfileUtil] Fallback method completed successfully`);
2421
+ console.log(
2422
+ `[createPatientProfileUtil] Fallback method completed successfully`
2423
+ );
2042
2424
  } catch (fallbackError) {
2043
- console.error(`[createPatientProfileUtil] Fallback method failed:`, fallbackError);
2425
+ console.error(
2426
+ `[createPatientProfileUtil] Fallback method failed:`,
2427
+ fallbackError
2428
+ );
2044
2429
  }
2045
2430
  }
2046
2431
  console.log(`[createPatientProfileUtil] Verifying patient document exists`);
2047
- const patientDoc = await getDoc5(getPatientDocRef(db, patientId));
2432
+ const patientDoc = await getDoc6(getPatientDocRef(db, patientId));
2048
2433
  if (!patientDoc.exists()) {
2049
- console.error(`[createPatientProfileUtil] Patient document not found after creation`);
2434
+ console.error(
2435
+ `[createPatientProfileUtil] Patient document not found after creation`
2436
+ );
2050
2437
  throw new Error("Failed to create patient profile");
2051
2438
  }
2052
- console.log(`[createPatientProfileUtil] Patient profile creation completed successfully`);
2439
+ console.log(
2440
+ `[createPatientProfileUtil] Patient profile creation completed successfully`
2441
+ );
2053
2442
  return patientDoc.data();
2054
2443
  } catch (error) {
2055
- console.error(`[createPatientProfileUtil] Error in patient profile creation:`, error);
2056
- if (error instanceof z8.ZodError) {
2444
+ console.error(
2445
+ `[createPatientProfileUtil] Error in patient profile creation:`,
2446
+ error
2447
+ );
2448
+ if (error instanceof z9.ZodError) {
2057
2449
  throw new Error("Invalid patient data: " + error.message);
2058
2450
  }
2059
2451
  throw error;
2060
2452
  }
2061
2453
  };
2062
2454
  var getPatientProfileUtil = async (db, patientId) => {
2063
- const patientDoc = await getDoc5(getPatientDocRef(db, patientId));
2455
+ const patientDoc = await getDoc6(getPatientDocRef(db, patientId));
2064
2456
  return patientDoc.exists() ? patientDoc.data() : null;
2065
2457
  };
2066
2458
  var getPatientProfileByUserRefUtil = async (db, userRef) => {
2067
2459
  try {
2068
2460
  const docRef = await getPatientDocRefByUserRef(db, userRef);
2069
- const patientDoc = await getDoc5(docRef);
2461
+ const patientDoc = await getDoc6(docRef);
2070
2462
  return patientDoc.exists() ? patientDoc.data() : null;
2071
2463
  } catch (error) {
2072
2464
  return null;
2073
2465
  }
2074
2466
  };
2075
2467
  var addExpoTokenUtil = async (db, patientId, token) => {
2076
- await updateDoc3(getPatientDocRef(db, patientId), {
2468
+ await updateDoc4(getPatientDocRef(db, patientId), {
2077
2469
  expoTokens: arrayUnion2(token),
2078
2470
  updatedAt: serverTimestamp4()
2079
2471
  });
2080
2472
  };
2081
2473
  var removeExpoTokenUtil = async (db, patientId, token) => {
2082
- await updateDoc3(getPatientDocRef(db, patientId), {
2474
+ await updateDoc4(getPatientDocRef(db, patientId), {
2083
2475
  expoTokens: arrayRemove2(token),
2084
2476
  updatedAt: serverTimestamp4()
2085
2477
  });
2086
2478
  };
2087
2479
  var addPointsUtil = async (db, patientId, points) => {
2088
- await updateDoc3(getPatientDocRef(db, patientId), {
2480
+ await updateDoc4(getPatientDocRef(db, patientId), {
2089
2481
  "gamification.points": increment(points),
2090
2482
  updatedAt: serverTimestamp4()
2091
2483
  });
@@ -2096,8 +2488,8 @@ var updatePatientProfileUtil = async (db, patientId, data) => {
2096
2488
  ...data,
2097
2489
  updatedAt: serverTimestamp4()
2098
2490
  };
2099
- await updateDoc3(getPatientDocRef(db, patientId), updateData);
2100
- const updatedDoc = await getDoc5(getPatientDocRef(db, patientId));
2491
+ await updateDoc4(getPatientDocRef(db, patientId), updateData);
2492
+ const updatedDoc = await getDoc6(getPatientDocRef(db, patientId));
2101
2493
  if (!updatedDoc.exists()) {
2102
2494
  throw new Error("Patient profile not found after update");
2103
2495
  }
@@ -2110,7 +2502,7 @@ var updatePatientProfileUtil = async (db, patientId, data) => {
2110
2502
  var updatePatientProfileByUserRefUtil = async (db, userRef, data) => {
2111
2503
  try {
2112
2504
  const docRef = await getPatientDocRefByUserRef(db, userRef);
2113
- const patientDoc = await getDoc5(docRef);
2505
+ const patientDoc = await getDoc6(docRef);
2114
2506
  if (!patientDoc.exists()) {
2115
2507
  throw new Error("Patient profile not found");
2116
2508
  }
@@ -2118,49 +2510,10 @@ var updatePatientProfileByUserRefUtil = async (db, userRef, data) => {
2118
2510
  return updatePatientProfileUtil(db, patientData.id, data);
2119
2511
  } catch (error) {
2120
2512
  const errorMessage = error instanceof Error ? error.message : String(error);
2121
- throw new Error(`Failed to update patient profile by user ref: ${errorMessage}`);
2122
- }
2123
- };
2124
- var uploadProfilePhotoUtil = async (storage, patientId, file) => {
2125
- const photoRef = ref(storage, `patient-photos/${patientId}/profile-photo`);
2126
- await uploadBytes(photoRef, file);
2127
- return getDownloadURL(photoRef);
2128
- };
2129
- var updateProfilePhotoUtil = async (storage, db, patientId, file) => {
2130
- const patientDoc = await getDoc5(getPatientDocRef(db, patientId));
2131
- if (!patientDoc.exists()) throw new Error("Patient profile not found");
2132
- const patientData = patientDoc.data();
2133
- if (patientData.profilePhoto) {
2134
- try {
2135
- const oldPhotoRef = ref(storage, patientData.profilePhoto);
2136
- await deleteObject(oldPhotoRef);
2137
- } catch (error) {
2138
- console.warn("Failed to delete old profile photo:", error);
2139
- }
2140
- }
2141
- const newPhotoUrl = await uploadProfilePhotoUtil(storage, patientId, file);
2142
- await updateDoc3(getPatientDocRef(db, patientId), {
2143
- profilePhoto: newPhotoUrl,
2144
- updatedAt: serverTimestamp4()
2145
- });
2146
- return newPhotoUrl;
2147
- };
2148
- var deleteProfilePhotoUtil = async (storage, db, patientId) => {
2149
- const patientDoc = await getDoc5(getPatientDocRef(db, patientId));
2150
- if (!patientDoc.exists()) throw new Error("Patient profile not found");
2151
- const patientData = patientDoc.data();
2152
- if (patientData.profilePhoto) {
2153
- try {
2154
- const photoRef = ref(storage, patientData.profilePhoto);
2155
- await deleteObject(photoRef);
2156
- } catch (error) {
2157
- console.warn("Failed to delete profile photo:", error);
2158
- }
2513
+ throw new Error(
2514
+ `Failed to update patient profile by user ref: ${errorMessage}`
2515
+ );
2159
2516
  }
2160
- await updateDoc3(getPatientDocRef(db, patientId), {
2161
- profilePhoto: null,
2162
- updatedAt: serverTimestamp4()
2163
- });
2164
2517
  };
2165
2518
  var testCreateSubDocuments = async (db, patientId, userRef) => {
2166
2519
  console.log(
@@ -2169,33 +2522,41 @@ var testCreateSubDocuments = async (db, patientId, userRef) => {
2169
2522
  try {
2170
2523
  console.log(`[testCreateSubDocuments] Testing sensitive info creation`);
2171
2524
  const sensitiveInfoRef = getSensitiveInfoDocRef(db, patientId);
2172
- console.log(`[testCreateSubDocuments] Sensitive info path: ${sensitiveInfoRef.path}`);
2525
+ console.log(
2526
+ `[testCreateSubDocuments] Sensitive info path: ${sensitiveInfoRef.path}`
2527
+ );
2173
2528
  const defaultSensitiveInfo = {
2174
2529
  patientId,
2175
2530
  userRef,
2176
2531
  photoUrl: "",
2177
2532
  firstName: "Name",
2178
2533
  lastName: "Surname",
2179
- dateOfBirth: Timestamp4.now(),
2534
+ dateOfBirth: Timestamp5.now(),
2180
2535
  gender: "prefer_not_to_say" /* PREFER_NOT_TO_SAY */,
2181
2536
  email: "test@example.com",
2182
2537
  phoneNumber: "",
2183
- createdAt: Timestamp4.now(),
2184
- updatedAt: Timestamp4.now()
2538
+ createdAt: Timestamp5.now(),
2539
+ updatedAt: Timestamp5.now()
2185
2540
  };
2186
- await setDoc4(sensitiveInfoRef, defaultSensitiveInfo);
2187
- console.log(`[testCreateSubDocuments] Sensitive info document created directly`);
2541
+ await setDoc5(sensitiveInfoRef, defaultSensitiveInfo);
2542
+ console.log(
2543
+ `[testCreateSubDocuments] Sensitive info document created directly`
2544
+ );
2188
2545
  console.log(`[testCreateSubDocuments] Testing medical info creation`);
2189
2546
  const medicalInfoRef = getMedicalInfoDocRef(db, patientId);
2190
- console.log(`[testCreateSubDocuments] Medical info path: ${medicalInfoRef.path}`);
2547
+ console.log(
2548
+ `[testCreateSubDocuments] Medical info path: ${medicalInfoRef.path}`
2549
+ );
2191
2550
  const defaultMedicalInfo = {
2192
2551
  ...DEFAULT_MEDICAL_INFO,
2193
2552
  patientId,
2194
- lastUpdated: Timestamp4.now(),
2553
+ lastUpdated: Timestamp5.now(),
2195
2554
  updatedBy: userRef
2196
2555
  };
2197
- await setDoc4(medicalInfoRef, defaultMedicalInfo);
2198
- console.log(`[testCreateSubDocuments] Medical info document created directly`);
2556
+ await setDoc5(medicalInfoRef, defaultMedicalInfo);
2557
+ console.log(
2558
+ `[testCreateSubDocuments] Medical info document created directly`
2559
+ );
2199
2560
  console.log(`[testCreateSubDocuments] Test completed successfully`);
2200
2561
  } catch (error) {
2201
2562
  console.error(`[testCreateSubDocuments] Error:`, error);
@@ -2206,10 +2567,12 @@ var searchPatientsUtil = async (db, params, requester) => {
2206
2567
  searchPatientsSchema.parse(params);
2207
2568
  requesterInfoSchema.parse(requester);
2208
2569
  const constraints = [];
2209
- const patientsCollectionRef = collection3(db, PATIENTS_COLLECTION);
2570
+ const patientsCollectionRef = collection4(db, PATIENTS_COLLECTION);
2210
2571
  if (requester.role === "clinic_admin") {
2211
2572
  if (!requester.associatedClinicId) {
2212
- throw new Error("Associated clinic ID is required for clinic admin search.");
2573
+ throw new Error(
2574
+ "Associated clinic ID is required for clinic admin search."
2575
+ );
2213
2576
  }
2214
2577
  if (params.clinicId && params.clinicId !== requester.associatedClinicId) {
2215
2578
  console.warn(
@@ -2217,13 +2580,19 @@ var searchPatientsUtil = async (db, params, requester) => {
2217
2580
  );
2218
2581
  return [];
2219
2582
  }
2220
- constraints.push(where3("clinicIds", "array-contains", requester.associatedClinicId));
2583
+ constraints.push(
2584
+ where4("clinicIds", "array-contains", requester.associatedClinicId)
2585
+ );
2221
2586
  if (params.practitionerId) {
2222
- constraints.push(where3("doctorIds", "array-contains", params.practitionerId));
2587
+ constraints.push(
2588
+ where4("doctorIds", "array-contains", params.practitionerId)
2589
+ );
2223
2590
  }
2224
2591
  } else if (requester.role === "practitioner") {
2225
2592
  if (!requester.associatedPractitionerId) {
2226
- throw new Error("Associated practitioner ID is required for practitioner search.");
2593
+ throw new Error(
2594
+ "Associated practitioner ID is required for practitioner search."
2595
+ );
2227
2596
  }
2228
2597
  if (params.practitionerId && params.practitionerId !== requester.associatedPractitionerId) {
2229
2598
  console.warn(
@@ -2231,18 +2600,24 @@ var searchPatientsUtil = async (db, params, requester) => {
2231
2600
  );
2232
2601
  return [];
2233
2602
  }
2234
- constraints.push(where3("doctorIds", "array-contains", requester.associatedPractitionerId));
2603
+ constraints.push(
2604
+ where4("doctorIds", "array-contains", requester.associatedPractitionerId)
2605
+ );
2235
2606
  if (params.clinicId) {
2236
- constraints.push(where3("clinicIds", "array-contains", params.clinicId));
2607
+ constraints.push(where4("clinicIds", "array-contains", params.clinicId));
2237
2608
  }
2238
2609
  } else {
2239
2610
  throw new Error("Invalid requester role.");
2240
2611
  }
2241
2612
  try {
2242
- const finalQuery = query3(patientsCollectionRef, ...constraints);
2243
- const querySnapshot = await getDocs3(finalQuery);
2244
- const patients = querySnapshot.docs.map((doc34) => doc34.data());
2245
- console.log(`[searchPatientsUtil] Found ${patients.length} patients matching criteria.`);
2613
+ const finalQuery = query4(patientsCollectionRef, ...constraints);
2614
+ const querySnapshot = await getDocs4(finalQuery);
2615
+ const patients = querySnapshot.docs.map(
2616
+ (doc34) => doc34.data()
2617
+ );
2618
+ console.log(
2619
+ `[searchPatientsUtil] Found ${patients.length} patients matching criteria.`
2620
+ );
2246
2621
  return patients;
2247
2622
  } catch (error) {
2248
2623
  console.error("[searchPatientsUtil] Error searching patients:", error);
@@ -2251,19 +2626,24 @@ var searchPatientsUtil = async (db, params, requester) => {
2251
2626
  };
2252
2627
  var getAllPatientsUtil = async (db, options) => {
2253
2628
  try {
2254
- console.log(`[getAllPatientsUtil] Fetching patients with options:`, options);
2255
- const patientsCollection = collection3(db, PATIENTS_COLLECTION);
2256
- let q = query3(patientsCollection);
2629
+ console.log(
2630
+ `[getAllPatientsUtil] Fetching patients with options:`,
2631
+ options
2632
+ );
2633
+ const patientsCollection = collection4(db, PATIENTS_COLLECTION);
2634
+ let q = query4(patientsCollection);
2257
2635
  if (options == null ? void 0 : options.limit) {
2258
- q = query3(q, limit2(options.limit));
2636
+ q = query4(q, limit3(options.limit));
2259
2637
  }
2260
2638
  if (options == null ? void 0 : options.startAfter) {
2261
- const startAfterDoc = await getDoc5(doc4(db, PATIENTS_COLLECTION, options.startAfter));
2639
+ const startAfterDoc = await getDoc6(
2640
+ doc5(db, PATIENTS_COLLECTION, options.startAfter)
2641
+ );
2262
2642
  if (startAfterDoc.exists()) {
2263
- q = query3(q, startAfter2(startAfterDoc));
2643
+ q = query4(q, startAfter2(startAfterDoc));
2264
2644
  }
2265
2645
  }
2266
- const patientsSnapshot = await getDocs3(q);
2646
+ const patientsSnapshot = await getDocs4(q);
2267
2647
  const patients = [];
2268
2648
  patientsSnapshot.forEach((doc34) => {
2269
2649
  patients.push(doc34.data());
@@ -2280,12 +2660,12 @@ var getAllPatientsUtil = async (db, options) => {
2280
2660
 
2281
2661
  // src/services/patient/utils/location.utils.ts
2282
2662
  import {
2283
- getDoc as getDoc6,
2284
- updateDoc as updateDoc4,
2285
- setDoc as setDoc5,
2663
+ getDoc as getDoc7,
2664
+ updateDoc as updateDoc5,
2665
+ setDoc as setDoc6,
2286
2666
  serverTimestamp as serverTimestamp5
2287
2667
  } from "firebase/firestore";
2288
- import { z as z9 } from "zod";
2668
+ import { z as z10 } from "zod";
2289
2669
  import { geohashForLocation } from "geofire-common";
2290
2670
  var updatePatientLocationUtil = async (db, patientId, latitude, longitude) => {
2291
2671
  const locationData = {
@@ -2297,7 +2677,7 @@ var updatePatientLocationUtil = async (db, patientId, latitude, longitude) => {
2297
2677
  locationData,
2298
2678
  updatedAt: serverTimestamp5()
2299
2679
  };
2300
- await updateDoc4(getLocationInfoDocRef(db, patientId), updateData);
2680
+ await updateDoc5(getLocationInfoDocRef(db, patientId), updateData);
2301
2681
  };
2302
2682
  var createLocationInfoUtil = async (db, data, requesterId) => {
2303
2683
  try {
@@ -2317,14 +2697,14 @@ var createLocationInfoUtil = async (db, data, requesterId) => {
2317
2697
  createdAt: serverTimestamp5(),
2318
2698
  updatedAt: serverTimestamp5()
2319
2699
  };
2320
- await setDoc5(getLocationInfoDocRef(db, data.patientId), locationData);
2321
- const locationDoc = await getDoc6(getLocationInfoDocRef(db, data.patientId));
2700
+ await setDoc6(getLocationInfoDocRef(db, data.patientId), locationData);
2701
+ const locationDoc = await getDoc7(getLocationInfoDocRef(db, data.patientId));
2322
2702
  if (!locationDoc.exists()) {
2323
2703
  throw new Error("Failed to create location information");
2324
2704
  }
2325
2705
  return locationDoc.data();
2326
2706
  } catch (error) {
2327
- if (error instanceof z9.ZodError) {
2707
+ if (error instanceof z10.ZodError) {
2328
2708
  throw new Error("Invalid location data: " + error.message);
2329
2709
  }
2330
2710
  throw error;
@@ -2334,7 +2714,7 @@ var getLocationInfoUtil = async (db, patientId, requesterId) => {
2334
2714
  if (patientId !== requesterId) {
2335
2715
  throw new Error("Unauthorized access to location information");
2336
2716
  }
2337
- const locationDoc = await getDoc6(getLocationInfoDocRef(db, patientId));
2717
+ const locationDoc = await getDoc7(getLocationInfoDocRef(db, patientId));
2338
2718
  return locationDoc.exists() ? locationDoc.data() : null;
2339
2719
  };
2340
2720
  var updateLocationInfoUtil = async (db, patientId, data, requesterId) => {
@@ -2352,7 +2732,7 @@ var updateLocationInfoUtil = async (db, patientId, data, requesterId) => {
2352
2732
  };
2353
2733
  }
2354
2734
  updateData.updatedAt = serverTimestamp5();
2355
- await updateDoc4(getLocationInfoDocRef(db, patientId), updateData);
2735
+ await updateDoc5(getLocationInfoDocRef(db, patientId), updateData);
2356
2736
  const updatedInfo = await getLocationInfoUtil(db, patientId, requesterId);
2357
2737
  if (!updatedInfo) {
2358
2738
  throw new Error("Failed to retrieve updated location information");
@@ -2362,22 +2742,22 @@ var updateLocationInfoUtil = async (db, patientId, data, requesterId) => {
2362
2742
 
2363
2743
  // src/services/patient/utils/medical-stuff.utils.ts
2364
2744
  import {
2365
- getDoc as getDoc7,
2366
- updateDoc as updateDoc5,
2745
+ getDoc as getDoc8,
2746
+ updateDoc as updateDoc6,
2367
2747
  arrayUnion as arrayUnion3,
2368
2748
  arrayRemove as arrayRemove3,
2369
2749
  serverTimestamp as serverTimestamp6,
2370
- Timestamp as Timestamp5
2750
+ Timestamp as Timestamp6
2371
2751
  } from "firebase/firestore";
2372
2752
  var addDoctorUtil = async (db, patientId, doctorRef, assignedBy) => {
2373
2753
  var _a;
2374
2754
  const newDoctor = {
2375
2755
  userRef: doctorRef,
2376
- assignedAt: Timestamp5.now(),
2756
+ assignedAt: Timestamp6.now(),
2377
2757
  assignedBy,
2378
2758
  isActive: true
2379
2759
  };
2380
- const patientDoc = await getDoc7(getPatientDocRef(db, patientId));
2760
+ const patientDoc = await getDoc8(getPatientDocRef(db, patientId));
2381
2761
  if (!patientDoc.exists()) throw new Error("Patient profile not found");
2382
2762
  const patientData = patientDoc.data();
2383
2763
  const existingDoctorIndex = (_a = patientData.doctors) == null ? void 0 : _a.findIndex(
@@ -2392,23 +2772,23 @@ var addDoctorUtil = async (db, patientId, doctorRef, assignedBy) => {
2392
2772
  updatedDoctors[existingDoctorIndex] = {
2393
2773
  ...updatedDoctors[existingDoctorIndex],
2394
2774
  isActive: true,
2395
- assignedAt: Timestamp5.now(),
2775
+ assignedAt: Timestamp6.now(),
2396
2776
  assignedBy
2397
2777
  };
2398
2778
  updates.doctors = updatedDoctors;
2399
2779
  } else {
2400
2780
  updates.doctors = arrayUnion3(newDoctor);
2401
2781
  }
2402
- await updateDoc5(getPatientDocRef(db, patientId), updates);
2782
+ await updateDoc6(getPatientDocRef(db, patientId), updates);
2403
2783
  };
2404
2784
  var removeDoctorUtil = async (db, patientId, doctorRef) => {
2405
2785
  var _a;
2406
2786
  const patientDocRef = getPatientDocRef(db, patientId);
2407
- const patientDoc = await getDoc7(patientDocRef);
2787
+ const patientDoc = await getDoc8(patientDocRef);
2408
2788
  if (!patientDoc.exists()) throw new Error("Patient profile not found");
2409
2789
  const patientData = patientDoc.data();
2410
2790
  const updatedDoctors = ((_a = patientData.doctors) == null ? void 0 : _a.filter((doctor) => doctor.userRef !== doctorRef)) || [];
2411
- await updateDoc5(patientDocRef, {
2791
+ await updateDoc6(patientDocRef, {
2412
2792
  doctors: updatedDoctors,
2413
2793
  // Set the filtered array
2414
2794
  doctorIds: arrayRemove3(doctorRef),
@@ -2420,11 +2800,11 @@ var addClinicUtil = async (db, patientId, clinicId, assignedBy) => {
2420
2800
  var _a;
2421
2801
  const newClinic = {
2422
2802
  clinicId,
2423
- assignedAt: Timestamp5.now(),
2803
+ assignedAt: Timestamp6.now(),
2424
2804
  assignedBy,
2425
2805
  isActive: true
2426
2806
  };
2427
- const patientDoc = await getDoc7(getPatientDocRef(db, patientId));
2807
+ const patientDoc = await getDoc8(getPatientDocRef(db, patientId));
2428
2808
  if (!patientDoc.exists()) throw new Error("Patient profile not found");
2429
2809
  const patientData = patientDoc.data();
2430
2810
  const existingClinicIndex = (_a = patientData.clinics) == null ? void 0 : _a.findIndex(
@@ -2439,23 +2819,23 @@ var addClinicUtil = async (db, patientId, clinicId, assignedBy) => {
2439
2819
  updatedClinics[existingClinicIndex] = {
2440
2820
  ...updatedClinics[existingClinicIndex],
2441
2821
  isActive: true,
2442
- assignedAt: Timestamp5.now(),
2822
+ assignedAt: Timestamp6.now(),
2443
2823
  assignedBy
2444
2824
  };
2445
2825
  updates.clinics = updatedClinics;
2446
2826
  } else {
2447
2827
  updates.clinics = arrayUnion3(newClinic);
2448
2828
  }
2449
- await updateDoc5(getPatientDocRef(db, patientId), updates);
2829
+ await updateDoc6(getPatientDocRef(db, patientId), updates);
2450
2830
  };
2451
2831
  var removeClinicUtil = async (db, patientId, clinicId) => {
2452
2832
  var _a;
2453
2833
  const patientDocRef = getPatientDocRef(db, patientId);
2454
- const patientDoc = await getDoc7(patientDocRef);
2834
+ const patientDoc = await getDoc8(patientDocRef);
2455
2835
  if (!patientDoc.exists()) throw new Error("Patient profile not found");
2456
2836
  const patientData = patientDoc.data();
2457
2837
  const updatedClinics = ((_a = patientData.clinics) == null ? void 0 : _a.filter((clinic) => clinic.clinicId !== clinicId)) || [];
2458
- await updateDoc5(patientDocRef, {
2838
+ await updateDoc6(patientDocRef, {
2459
2839
  clinics: updatedClinics,
2460
2840
  // Set the filtered array
2461
2841
  clinicIds: arrayRemove3(clinicId),
@@ -2466,14 +2846,14 @@ var removeClinicUtil = async (db, patientId, clinicId) => {
2466
2846
 
2467
2847
  // src/services/patient/utils/clinic.utils.ts
2468
2848
  import {
2469
- collection as collection4,
2470
- query as query4,
2471
- where as where4,
2472
- getDocs as getDocs4,
2473
- limit as limit3,
2849
+ collection as collection5,
2850
+ query as query5,
2851
+ where as where5,
2852
+ getDocs as getDocs5,
2853
+ limit as limit4,
2474
2854
  startAfter as startAfter3,
2475
- doc as doc5,
2476
- getDoc as getDoc8
2855
+ doc as doc6,
2856
+ getDoc as getDoc9
2477
2857
  } from "firebase/firestore";
2478
2858
  var getPatientsByClinicUtil = async (db, clinicId, options) => {
2479
2859
  try {
@@ -2481,23 +2861,23 @@ var getPatientsByClinicUtil = async (db, clinicId, options) => {
2481
2861
  `[getPatientsByClinicUtil] Fetching patients for clinic ID: ${clinicId} with options:`,
2482
2862
  options
2483
2863
  );
2484
- const patientsCollection = collection4(db, PATIENTS_COLLECTION);
2864
+ const patientsCollection = collection5(db, PATIENTS_COLLECTION);
2485
2865
  const constraints = [
2486
- where4("clinicIds", "array-contains", clinicId)
2866
+ where5("clinicIds", "array-contains", clinicId)
2487
2867
  ];
2488
- let q = query4(patientsCollection, ...constraints);
2868
+ let q = query5(patientsCollection, ...constraints);
2489
2869
  if (options == null ? void 0 : options.limit) {
2490
- q = query4(q, limit3(options.limit));
2870
+ q = query5(q, limit4(options.limit));
2491
2871
  }
2492
2872
  if (options == null ? void 0 : options.startAfter) {
2493
- const startAfterDoc = await getDoc8(
2494
- doc5(db, PATIENTS_COLLECTION, options.startAfter)
2873
+ const startAfterDoc = await getDoc9(
2874
+ doc6(db, PATIENTS_COLLECTION, options.startAfter)
2495
2875
  );
2496
2876
  if (startAfterDoc.exists()) {
2497
- q = query4(q, startAfter3(startAfterDoc));
2877
+ q = query5(q, startAfter3(startAfterDoc));
2498
2878
  }
2499
2879
  }
2500
- const patientsSnapshot = await getDocs4(q);
2880
+ const patientsSnapshot = await getDocs5(q);
2501
2881
  const patients = [];
2502
2882
  patientsSnapshot.forEach((doc34) => {
2503
2883
  patients.push(doc34.data());
@@ -2521,6 +2901,7 @@ var getPatientsByClinicUtil = async (db, clinicId, options) => {
2521
2901
  var PatientService = class extends BaseService {
2522
2902
  constructor(db, auth, app) {
2523
2903
  super(db, auth, app);
2904
+ this.mediaService = new MediaService(db, auth, app);
2524
2905
  }
2525
2906
  // Metode za rad sa profilom pacijenta
2526
2907
  async createPatientProfile(data) {
@@ -2557,7 +2938,12 @@ var PatientService = class extends BaseService {
2557
2938
  }
2558
2939
  // Metode za rad sa osetljivim informacijama
2559
2940
  async createSensitiveInfo(data, requesterUserId) {
2560
- return createSensitiveInfoUtil(this.db, data, requesterUserId);
2941
+ return createSensitiveInfoUtil(
2942
+ this.db,
2943
+ data,
2944
+ requesterUserId,
2945
+ this.mediaService
2946
+ );
2561
2947
  }
2562
2948
  async getSensitiveInfo(patientId, requesterUserId) {
2563
2949
  return getSensitiveInfoUtil(this.db, patientId, requesterUserId);
@@ -2568,7 +2954,13 @@ var PatientService = class extends BaseService {
2568
2954
  return this.getSensitiveInfo(profile.id, requesterUserId);
2569
2955
  }
2570
2956
  async updateSensitiveInfo(patientId, data, requesterUserId) {
2571
- return updateSensitiveInfoUtil(this.db, patientId, data, requesterUserId);
2957
+ return updateSensitiveInfoUtil(
2958
+ this.db,
2959
+ patientId,
2960
+ data,
2961
+ requesterUserId,
2962
+ this.mediaService
2963
+ );
2572
2964
  }
2573
2965
  // Metode za rad sa medicinskim informacijama
2574
2966
  async createMedicalInfo(patientId, data) {
@@ -2701,8 +3093,8 @@ var PatientService = class extends BaseService {
2701
3093
  if (!this.auth.currentUser) {
2702
3094
  throw new Error("No authenticated user");
2703
3095
  }
2704
- const userDoc = await getDoc9(
2705
- doc6(this.db, "users", this.auth.currentUser.uid)
3096
+ const userDoc = await getDoc10(
3097
+ doc7(this.db, "users", this.auth.currentUser.uid)
2706
3098
  );
2707
3099
  if (!userDoc.exists()) {
2708
3100
  throw new Error("User not found");
@@ -2737,14 +3129,113 @@ var PatientService = class extends BaseService {
2737
3129
  await removeClinicUtil(this.db, patientId, clinicId);
2738
3130
  }
2739
3131
  // Metode za rad sa profilnom slikom
3132
+ /**
3133
+ * Uploads a profile photo for a patient
3134
+ * @param patientId - ID of the patient
3135
+ * @param file - File or Blob to upload
3136
+ * @returns URL of the uploaded photo
3137
+ */
2740
3138
  async uploadProfilePhoto(patientId, file) {
2741
- return uploadProfilePhotoUtil(this.storage, patientId, file);
3139
+ console.log(
3140
+ `[PatientService] Uploading profile photo for patient ${patientId}`
3141
+ );
3142
+ const mediaMetadata = await this.mediaService.uploadMedia(
3143
+ file,
3144
+ patientId,
3145
+ // Using patientId as ownerId
3146
+ "private" /* PRIVATE */,
3147
+ // Profile photos should be private
3148
+ "patient_profile_photos",
3149
+ file instanceof File ? file.name : `profile_photo_${patientId}`
3150
+ );
3151
+ await updateDoc7(getSensitiveInfoDocRef(this.db, patientId), {
3152
+ photoUrl: mediaMetadata.url,
3153
+ updatedAt: serverTimestamp7()
3154
+ });
3155
+ return mediaMetadata.url;
2742
3156
  }
3157
+ /**
3158
+ * Updates a patient's profile photo (replaces existing one)
3159
+ * @param patientId - ID of the patient
3160
+ * @param file - New file or Blob to upload
3161
+ * @returns URL of the new uploaded photo
3162
+ */
2743
3163
  async updateProfilePhoto(patientId, file) {
2744
- return updateProfilePhotoUtil(this.storage, this.db, patientId, file);
3164
+ console.log(
3165
+ `[PatientService] Updating profile photo for patient ${patientId}`
3166
+ );
3167
+ const currentUser = await this.getCurrentUser();
3168
+ const currentSensitiveInfo = await this.getSensitiveInfo(
3169
+ patientId,
3170
+ currentUser.uid
3171
+ );
3172
+ if ((currentSensitiveInfo == null ? void 0 : currentSensitiveInfo.photoUrl) && typeof currentSensitiveInfo.photoUrl === "string") {
3173
+ try {
3174
+ const existingMediaMetadata = await this.mediaService.getMediaMetadataByUrl(
3175
+ currentSensitiveInfo.photoUrl
3176
+ );
3177
+ if (existingMediaMetadata) {
3178
+ await this.mediaService.deleteMedia(existingMediaMetadata.id);
3179
+ }
3180
+ } catch (error) {
3181
+ console.warn(
3182
+ `[PatientService] Could not delete old profile photo for patient ${patientId}:`,
3183
+ error
3184
+ );
3185
+ }
3186
+ }
3187
+ return this.uploadProfilePhoto(patientId, file);
2745
3188
  }
3189
+ /**
3190
+ * Deletes a patient's profile photo
3191
+ * @param patientId - ID of the patient
3192
+ */
2746
3193
  async deleteProfilePhoto(patientId) {
2747
- await deleteProfilePhotoUtil(this.storage, this.db, patientId);
3194
+ console.log(
3195
+ `[PatientService] Deleting profile photo for patient ${patientId}`
3196
+ );
3197
+ const currentUser = await this.getCurrentUser();
3198
+ const currentSensitiveInfo = await this.getSensitiveInfo(
3199
+ patientId,
3200
+ currentUser.uid
3201
+ );
3202
+ if ((currentSensitiveInfo == null ? void 0 : currentSensitiveInfo.photoUrl) && typeof currentSensitiveInfo.photoUrl === "string") {
3203
+ try {
3204
+ const existingMediaMetadata = await this.mediaService.getMediaMetadataByUrl(
3205
+ currentSensitiveInfo.photoUrl
3206
+ );
3207
+ if (existingMediaMetadata) {
3208
+ await this.mediaService.deleteMedia(existingMediaMetadata.id);
3209
+ }
3210
+ } catch (error) {
3211
+ console.warn(
3212
+ `[PatientService] Could not delete profile photo for patient ${patientId}:`,
3213
+ error
3214
+ );
3215
+ }
3216
+ await updateDoc7(getSensitiveInfoDocRef(this.db, patientId), {
3217
+ photoUrl: null,
3218
+ updatedAt: serverTimestamp7()
3219
+ });
3220
+ }
3221
+ }
3222
+ /**
3223
+ * Handles profile photo upload for patients (supports MediaResource)
3224
+ * @param photoUrl - MediaResource (File, Blob, or URL string) from CreatePatientSensitiveInfoData
3225
+ * @param patientId - ID of the patient
3226
+ * @returns URL string of the uploaded or existing photo
3227
+ */
3228
+ async handleProfilePhotoUpload(photoUrl, patientId) {
3229
+ if (!photoUrl) {
3230
+ return void 0;
3231
+ }
3232
+ if (typeof photoUrl === "string") {
3233
+ return photoUrl;
3234
+ }
3235
+ if (photoUrl instanceof File || photoUrl instanceof Blob) {
3236
+ return this.uploadProfilePhoto(patientId, photoUrl);
3237
+ }
3238
+ return void 0;
2748
3239
  }
2749
3240
  // Metode za ažuriranje profila
2750
3241
  async updatePatientProfile(patientId, data) {
@@ -2838,16 +3329,16 @@ var PatientService = class extends BaseService {
2838
3329
 
2839
3330
  // src/services/clinic/utils/admin.utils.ts
2840
3331
  import {
2841
- collection as collection5,
2842
- doc as doc7,
2843
- getDoc as getDoc10,
2844
- getDocs as getDocs5,
2845
- query as query5,
2846
- where as where5,
2847
- updateDoc as updateDoc7,
2848
- setDoc as setDoc6,
2849
- deleteDoc,
2850
- Timestamp as Timestamp7,
3332
+ collection as collection6,
3333
+ doc as doc8,
3334
+ getDoc as getDoc11,
3335
+ getDocs as getDocs6,
3336
+ query as query6,
3337
+ where as where6,
3338
+ updateDoc as updateDoc8,
3339
+ setDoc as setDoc7,
3340
+ deleteDoc as deleteDoc2,
3341
+ Timestamp as Timestamp8,
2851
3342
  serverTimestamp as serverTimestamp8
2852
3343
  } from "firebase/firestore";
2853
3344
 
@@ -2976,131 +3467,131 @@ var SubscriptionModel = /* @__PURE__ */ ((SubscriptionModel2) => {
2976
3467
 
2977
3468
  // src/validations/clinic.schema.ts
2978
3469
  import { z as z13 } from "zod";
2979
- import { Timestamp as Timestamp6 } from "firebase/firestore";
3470
+ import { Timestamp as Timestamp7 } from "firebase/firestore";
2980
3471
 
2981
3472
  // src/validations/reviews.schema.ts
2982
- import { z as z10 } from "zod";
2983
- var baseReviewSchema = z10.object({
2984
- id: z10.string().min(1),
2985
- patientId: z10.string().min(1),
2986
- fullReviewId: z10.string().min(1),
2987
- createdAt: z10.date(),
2988
- updatedAt: z10.date(),
2989
- comment: z10.string().min(1).max(2e3),
2990
- isVerified: z10.boolean(),
2991
- isPublished: z10.boolean()
3473
+ import { z as z11 } from "zod";
3474
+ var baseReviewSchema = z11.object({
3475
+ id: z11.string().min(1),
3476
+ patientId: z11.string().min(1),
3477
+ fullReviewId: z11.string().min(1),
3478
+ createdAt: z11.date(),
3479
+ updatedAt: z11.date(),
3480
+ comment: z11.string().min(1).max(2e3),
3481
+ isVerified: z11.boolean(),
3482
+ isPublished: z11.boolean()
2992
3483
  });
2993
- var baseReviewCreateSchema = z10.object({
2994
- patientId: z10.string().min(1),
2995
- comment: z10.string().min(1).max(2e3),
2996
- isVerified: z10.boolean().default(false),
2997
- isPublished: z10.boolean().default(true)
3484
+ var baseReviewCreateSchema = z11.object({
3485
+ patientId: z11.string().min(1),
3486
+ comment: z11.string().min(1).max(2e3),
3487
+ isVerified: z11.boolean().default(false),
3488
+ isPublished: z11.boolean().default(true)
2998
3489
  });
2999
3490
  var clinicReviewSchema = baseReviewSchema.extend({
3000
- clinicId: z10.string().min(1),
3001
- cleanliness: z10.number().min(1).max(5),
3002
- facilities: z10.number().min(1).max(5),
3003
- staffFriendliness: z10.number().min(1).max(5),
3004
- waitingTime: z10.number().min(1).max(5),
3005
- accessibility: z10.number().min(1).max(5),
3006
- overallRating: z10.number().min(1).max(5),
3007
- wouldRecommend: z10.boolean()
3491
+ clinicId: z11.string().min(1),
3492
+ cleanliness: z11.number().min(1).max(5),
3493
+ facilities: z11.number().min(1).max(5),
3494
+ staffFriendliness: z11.number().min(1).max(5),
3495
+ waitingTime: z11.number().min(1).max(5),
3496
+ accessibility: z11.number().min(1).max(5),
3497
+ overallRating: z11.number().min(1).max(5),
3498
+ wouldRecommend: z11.boolean()
3008
3499
  });
3009
3500
  var createClinicReviewSchema = baseReviewCreateSchema.extend({
3010
- clinicId: z10.string().min(1),
3011
- cleanliness: z10.number().min(1).max(5),
3012
- facilities: z10.number().min(1).max(5),
3013
- staffFriendliness: z10.number().min(1).max(5),
3014
- waitingTime: z10.number().min(1).max(5),
3015
- accessibility: z10.number().min(1).max(5),
3016
- wouldRecommend: z10.boolean()
3501
+ clinicId: z11.string().min(1),
3502
+ cleanliness: z11.number().min(1).max(5),
3503
+ facilities: z11.number().min(1).max(5),
3504
+ staffFriendliness: z11.number().min(1).max(5),
3505
+ waitingTime: z11.number().min(1).max(5),
3506
+ accessibility: z11.number().min(1).max(5),
3507
+ wouldRecommend: z11.boolean()
3017
3508
  });
3018
3509
  var practitionerReviewSchema = baseReviewSchema.extend({
3019
- practitionerId: z10.string().min(1),
3020
- knowledgeAndExpertise: z10.number().min(1).max(5),
3021
- communicationSkills: z10.number().min(1).max(5),
3022
- bedSideManner: z10.number().min(1).max(5),
3023
- thoroughness: z10.number().min(1).max(5),
3024
- trustworthiness: z10.number().min(1).max(5),
3025
- overallRating: z10.number().min(1).max(5),
3026
- wouldRecommend: z10.boolean()
3510
+ practitionerId: z11.string().min(1),
3511
+ knowledgeAndExpertise: z11.number().min(1).max(5),
3512
+ communicationSkills: z11.number().min(1).max(5),
3513
+ bedSideManner: z11.number().min(1).max(5),
3514
+ thoroughness: z11.number().min(1).max(5),
3515
+ trustworthiness: z11.number().min(1).max(5),
3516
+ overallRating: z11.number().min(1).max(5),
3517
+ wouldRecommend: z11.boolean()
3027
3518
  });
3028
3519
  var createPractitionerReviewSchema = baseReviewCreateSchema.extend({
3029
- practitionerId: z10.string().min(1),
3030
- knowledgeAndExpertise: z10.number().min(1).max(5),
3031
- communicationSkills: z10.number().min(1).max(5),
3032
- bedSideManner: z10.number().min(1).max(5),
3033
- thoroughness: z10.number().min(1).max(5),
3034
- trustworthiness: z10.number().min(1).max(5),
3035
- wouldRecommend: z10.boolean()
3520
+ practitionerId: z11.string().min(1),
3521
+ knowledgeAndExpertise: z11.number().min(1).max(5),
3522
+ communicationSkills: z11.number().min(1).max(5),
3523
+ bedSideManner: z11.number().min(1).max(5),
3524
+ thoroughness: z11.number().min(1).max(5),
3525
+ trustworthiness: z11.number().min(1).max(5),
3526
+ wouldRecommend: z11.boolean()
3036
3527
  });
3037
3528
  var procedureReviewSchema = baseReviewSchema.extend({
3038
- procedureId: z10.string().min(1),
3039
- effectivenessOfTreatment: z10.number().min(1).max(5),
3040
- outcomeExplanation: z10.number().min(1).max(5),
3041
- painManagement: z10.number().min(1).max(5),
3042
- followUpCare: z10.number().min(1).max(5),
3043
- valueForMoney: z10.number().min(1).max(5),
3044
- overallRating: z10.number().min(1).max(5),
3045
- wouldRecommend: z10.boolean()
3529
+ procedureId: z11.string().min(1),
3530
+ effectivenessOfTreatment: z11.number().min(1).max(5),
3531
+ outcomeExplanation: z11.number().min(1).max(5),
3532
+ painManagement: z11.number().min(1).max(5),
3533
+ followUpCare: z11.number().min(1).max(5),
3534
+ valueForMoney: z11.number().min(1).max(5),
3535
+ overallRating: z11.number().min(1).max(5),
3536
+ wouldRecommend: z11.boolean()
3046
3537
  });
3047
3538
  var createProcedureReviewSchema = baseReviewCreateSchema.extend({
3048
- procedureId: z10.string().min(1),
3049
- effectivenessOfTreatment: z10.number().min(1).max(5),
3050
- outcomeExplanation: z10.number().min(1).max(5),
3051
- painManagement: z10.number().min(1).max(5),
3052
- followUpCare: z10.number().min(1).max(5),
3053
- valueForMoney: z10.number().min(1).max(5),
3054
- wouldRecommend: z10.boolean()
3539
+ procedureId: z11.string().min(1),
3540
+ effectivenessOfTreatment: z11.number().min(1).max(5),
3541
+ outcomeExplanation: z11.number().min(1).max(5),
3542
+ painManagement: z11.number().min(1).max(5),
3543
+ followUpCare: z11.number().min(1).max(5),
3544
+ valueForMoney: z11.number().min(1).max(5),
3545
+ wouldRecommend: z11.boolean()
3055
3546
  });
3056
- var clinicReviewInfoSchema = z10.object({
3057
- totalReviews: z10.number().min(0),
3058
- averageRating: z10.number().min(0).max(5),
3059
- cleanliness: z10.number().min(0).max(5),
3060
- facilities: z10.number().min(0).max(5),
3061
- staffFriendliness: z10.number().min(0).max(5),
3062
- waitingTime: z10.number().min(0).max(5),
3063
- accessibility: z10.number().min(0).max(5),
3064
- recommendationPercentage: z10.number().min(0).max(100)
3547
+ var clinicReviewInfoSchema = z11.object({
3548
+ totalReviews: z11.number().min(0),
3549
+ averageRating: z11.number().min(0).max(5),
3550
+ cleanliness: z11.number().min(0).max(5),
3551
+ facilities: z11.number().min(0).max(5),
3552
+ staffFriendliness: z11.number().min(0).max(5),
3553
+ waitingTime: z11.number().min(0).max(5),
3554
+ accessibility: z11.number().min(0).max(5),
3555
+ recommendationPercentage: z11.number().min(0).max(100)
3065
3556
  });
3066
- var practitionerReviewInfoSchema = z10.object({
3067
- totalReviews: z10.number().min(0),
3068
- averageRating: z10.number().min(0).max(5),
3069
- knowledgeAndExpertise: z10.number().min(0).max(5),
3070
- communicationSkills: z10.number().min(0).max(5),
3071
- bedSideManner: z10.number().min(0).max(5),
3072
- thoroughness: z10.number().min(0).max(5),
3073
- trustworthiness: z10.number().min(0).max(5),
3074
- recommendationPercentage: z10.number().min(0).max(100)
3557
+ var practitionerReviewInfoSchema = z11.object({
3558
+ totalReviews: z11.number().min(0),
3559
+ averageRating: z11.number().min(0).max(5),
3560
+ knowledgeAndExpertise: z11.number().min(0).max(5),
3561
+ communicationSkills: z11.number().min(0).max(5),
3562
+ bedSideManner: z11.number().min(0).max(5),
3563
+ thoroughness: z11.number().min(0).max(5),
3564
+ trustworthiness: z11.number().min(0).max(5),
3565
+ recommendationPercentage: z11.number().min(0).max(100)
3075
3566
  });
3076
- var procedureReviewInfoSchema = z10.object({
3077
- totalReviews: z10.number().min(0),
3078
- averageRating: z10.number().min(0).max(5),
3079
- effectivenessOfTreatment: z10.number().min(0).max(5),
3080
- outcomeExplanation: z10.number().min(0).max(5),
3081
- painManagement: z10.number().min(0).max(5),
3082
- followUpCare: z10.number().min(0).max(5),
3083
- valueForMoney: z10.number().min(0).max(5),
3084
- recommendationPercentage: z10.number().min(0).max(100)
3567
+ var procedureReviewInfoSchema = z11.object({
3568
+ totalReviews: z11.number().min(0),
3569
+ averageRating: z11.number().min(0).max(5),
3570
+ effectivenessOfTreatment: z11.number().min(0).max(5),
3571
+ outcomeExplanation: z11.number().min(0).max(5),
3572
+ painManagement: z11.number().min(0).max(5),
3573
+ followUpCare: z11.number().min(0).max(5),
3574
+ valueForMoney: z11.number().min(0).max(5),
3575
+ recommendationPercentage: z11.number().min(0).max(100)
3085
3576
  });
3086
- var reviewSchema = z10.object({
3087
- id: z10.string().min(1),
3088
- appointmentId: z10.string().min(1),
3089
- patientId: z10.string().min(1),
3090
- createdAt: z10.date(),
3091
- updatedAt: z10.date(),
3577
+ var reviewSchema = z11.object({
3578
+ id: z11.string().min(1),
3579
+ appointmentId: z11.string().min(1),
3580
+ patientId: z11.string().min(1),
3581
+ createdAt: z11.date(),
3582
+ updatedAt: z11.date(),
3092
3583
  clinicReview: clinicReviewSchema.optional(),
3093
3584
  practitionerReview: practitionerReviewSchema.optional(),
3094
3585
  procedureReview: procedureReviewSchema.optional(),
3095
- overallComment: z10.string().min(1).max(2e3),
3096
- overallRating: z10.number().min(1).max(5)
3586
+ overallComment: z11.string().min(1).max(2e3),
3587
+ overallRating: z11.number().min(1).max(5)
3097
3588
  });
3098
- var createReviewSchema = z10.object({
3099
- patientId: z10.string().min(1),
3589
+ var createReviewSchema = z11.object({
3590
+ patientId: z11.string().min(1),
3100
3591
  clinicReview: createClinicReviewSchema.optional(),
3101
3592
  practitionerReview: createPractitionerReviewSchema.optional(),
3102
3593
  procedureReview: createProcedureReviewSchema.optional(),
3103
- overallComment: z10.string().min(1).max(2e3)
3594
+ overallComment: z11.string().min(1).max(2e3)
3104
3595
  }).refine(
3105
3596
  (data) => {
3106
3597
  return data.clinicReview || data.practitionerReview || data.procedureReview;
@@ -3112,7 +3603,7 @@ var createReviewSchema = z10.object({
3112
3603
  );
3113
3604
 
3114
3605
  // src/validations/shared.schema.ts
3115
- import { z as z11 } from "zod";
3606
+ import { z as z12 } from "zod";
3116
3607
 
3117
3608
  // src/backoffice/types/static/procedure-family.types.ts
3118
3609
  var ProcedureFamily = /* @__PURE__ */ ((ProcedureFamily2) => {
@@ -3141,65 +3632,57 @@ var Currency = /* @__PURE__ */ ((Currency2) => {
3141
3632
  })(Currency || {});
3142
3633
 
3143
3634
  // src/validations/shared.schema.ts
3144
- var sharedClinicContactInfoSchema = z11.object({
3145
- email: z11.string().email(),
3146
- phoneNumber: z11.string(),
3147
- alternativePhoneNumber: z11.string().nullable().optional(),
3148
- website: z11.string().nullable().optional()
3635
+ var sharedClinicContactInfoSchema = z12.object({
3636
+ email: z12.string().email(),
3637
+ phoneNumber: z12.string(),
3638
+ alternativePhoneNumber: z12.string().nullable().optional(),
3639
+ website: z12.string().nullable().optional()
3149
3640
  });
3150
- var sharedClinicLocationSchema = z11.object({
3151
- address: z11.string(),
3152
- city: z11.string(),
3153
- country: z11.string(),
3154
- postalCode: z11.string(),
3155
- latitude: z11.number().min(-90).max(90),
3156
- longitude: z11.number().min(-180).max(180),
3157
- geohash: z11.string().nullable().optional()
3641
+ var sharedClinicLocationSchema = z12.object({
3642
+ address: z12.string(),
3643
+ city: z12.string(),
3644
+ country: z12.string(),
3645
+ postalCode: z12.string(),
3646
+ latitude: z12.number().min(-90).max(90),
3647
+ longitude: z12.number().min(-180).max(180),
3648
+ geohash: z12.string().nullable().optional()
3158
3649
  });
3159
- var procedureSummaryInfoSchema = z11.object({
3160
- id: z11.string().min(1),
3161
- name: z11.string().min(1),
3162
- description: z11.string().optional(),
3163
- photo: z11.string().optional(),
3164
- family: z11.nativeEnum(ProcedureFamily),
3165
- categoryName: z11.string(),
3166
- subcategoryName: z11.string(),
3167
- technologyName: z11.string(),
3168
- price: z11.number().nonnegative(),
3169
- pricingMeasure: z11.nativeEnum(PricingMeasure),
3170
- currency: z11.nativeEnum(Currency),
3171
- duration: z11.number().int().positive(),
3172
- clinicId: z11.string().min(1),
3173
- clinicName: z11.string().min(1),
3174
- practitionerId: z11.string().min(1),
3175
- practitionerName: z11.string().min(1)
3650
+ var procedureSummaryInfoSchema = z12.object({
3651
+ id: z12.string().min(1),
3652
+ name: z12.string().min(1),
3653
+ description: z12.string().optional(),
3654
+ photo: z12.string().optional(),
3655
+ family: z12.nativeEnum(ProcedureFamily),
3656
+ categoryName: z12.string(),
3657
+ subcategoryName: z12.string(),
3658
+ technologyName: z12.string(),
3659
+ price: z12.number().nonnegative(),
3660
+ pricingMeasure: z12.nativeEnum(PricingMeasure),
3661
+ currency: z12.nativeEnum(Currency),
3662
+ duration: z12.number().int().positive(),
3663
+ clinicId: z12.string().min(1),
3664
+ clinicName: z12.string().min(1),
3665
+ practitionerId: z12.string().min(1),
3666
+ practitionerName: z12.string().min(1)
3176
3667
  });
3177
- var clinicInfoSchema = z11.object({
3178
- id: z11.string(),
3179
- featuredPhoto: z11.string(),
3180
- name: z11.string(),
3181
- description: z11.string().nullable().optional(),
3668
+ var clinicInfoSchema = z12.object({
3669
+ id: z12.string(),
3670
+ featuredPhoto: z12.string(),
3671
+ name: z12.string(),
3672
+ description: z12.string().nullable().optional(),
3182
3673
  location: sharedClinicLocationSchema,
3183
3674
  contactInfo: sharedClinicContactInfoSchema
3184
3675
  });
3185
- var doctorInfoSchema = z11.object({
3186
- id: z11.string(),
3187
- name: z11.string(),
3188
- description: z11.string().nullable().optional(),
3189
- photo: z11.string(),
3190
- rating: z11.number().min(0).max(5),
3191
- services: z11.array(z11.string())
3676
+ var doctorInfoSchema = z12.object({
3677
+ id: z12.string(),
3678
+ name: z12.string(),
3679
+ description: z12.string().nullable().optional(),
3680
+ photo: z12.string(),
3681
+ rating: z12.number().min(0).max(5),
3682
+ services: z12.array(z12.string())
3192
3683
  // List of procedure IDs practitioner offers
3193
3684
  });
3194
3685
 
3195
- // src/validations/media.schema.ts
3196
- import { z as z12 } from "zod";
3197
- var mediaResourceSchema = z12.union([
3198
- z12.string(),
3199
- z12.instanceof(File),
3200
- z12.instanceof(Blob)
3201
- ]);
3202
-
3203
3686
  // src/validations/clinic.schema.ts
3204
3687
  var clinicContactInfoSchema = z13.object({
3205
3688
  email: z13.string().email(),
@@ -3259,8 +3742,8 @@ var clinicAdminSchema = z13.object({
3259
3742
  clinicsManagedInfo: z13.array(clinicInfoSchema),
3260
3743
  contactInfo: contactPersonSchema,
3261
3744
  roleTitle: z13.string(),
3262
- createdAt: z13.instanceof(Date).or(z13.instanceof(Timestamp6)),
3263
- updatedAt: z13.instanceof(Date).or(z13.instanceof(Timestamp6)),
3745
+ createdAt: z13.instanceof(Date).or(z13.instanceof(Timestamp7)),
3746
+ updatedAt: z13.instanceof(Date).or(z13.instanceof(Timestamp7)),
3264
3747
  isActive: z13.boolean()
3265
3748
  });
3266
3749
  var adminTokenSchema = z13.object({
@@ -3269,9 +3752,9 @@ var adminTokenSchema = z13.object({
3269
3752
  email: z13.string().email().optional().nullable(),
3270
3753
  status: z13.nativeEnum(AdminTokenStatus),
3271
3754
  usedByUserRef: z13.string().optional(),
3272
- createdAt: z13.instanceof(Date).or(z13.instanceof(Timestamp6)),
3755
+ createdAt: z13.instanceof(Date).or(z13.instanceof(Timestamp7)),
3273
3756
  // Timestamp
3274
- expiresAt: z13.instanceof(Date).or(z13.instanceof(Timestamp6))
3757
+ expiresAt: z13.instanceof(Date).or(z13.instanceof(Timestamp7))
3275
3758
  // Timestamp
3276
3759
  });
3277
3760
  var createAdminTokenSchema = z13.object({
@@ -3291,9 +3774,9 @@ var clinicGroupSchema = z13.object({
3291
3774
  adminsInfo: z13.array(adminInfoSchema),
3292
3775
  adminTokens: z13.array(adminTokenSchema),
3293
3776
  ownerId: z13.string().nullable(),
3294
- createdAt: z13.instanceof(Date).or(z13.instanceof(Timestamp6)),
3777
+ createdAt: z13.instanceof(Date).or(z13.instanceof(Timestamp7)),
3295
3778
  // Timestamp
3296
- updatedAt: z13.instanceof(Date).or(z13.instanceof(Timestamp6)),
3779
+ updatedAt: z13.instanceof(Date).or(z13.instanceof(Timestamp7)),
3297
3780
  // Timestamp
3298
3781
  isActive: z13.boolean(),
3299
3782
  logo: mediaResourceSchema.optional().nullable(),
@@ -3335,9 +3818,9 @@ var clinicSchema = z13.object({
3335
3818
  // Use the correct schema for aggregated procedure info
3336
3819
  reviewInfo: clinicReviewInfoSchema,
3337
3820
  admins: z13.array(z13.string()),
3338
- createdAt: z13.instanceof(Date).or(z13.instanceof(Timestamp6)),
3821
+ createdAt: z13.instanceof(Date).or(z13.instanceof(Timestamp7)),
3339
3822
  // Timestamp
3340
- updatedAt: z13.instanceof(Date).or(z13.instanceof(Timestamp6)),
3823
+ updatedAt: z13.instanceof(Date).or(z13.instanceof(Timestamp7)),
3341
3824
  // Timestamp
3342
3825
  isActive: z13.boolean(),
3343
3826
  isVerified: z13.boolean(),
@@ -3559,7 +4042,7 @@ async function createClinicAdmin(db, data, clinicGroupService) {
3559
4042
  }
3560
4043
  console.log("[CLINIC_ADMIN] Preparing admin data object");
3561
4044
  const adminData = {
3562
- id: doc7(collection5(db, CLINIC_ADMINS_COLLECTION)).id,
4045
+ id: doc8(collection6(db, CLINIC_ADMINS_COLLECTION)).id,
3563
4046
  // Generate a new ID for the admin document
3564
4047
  userRef: validatedData.userRef,
3565
4048
  clinicGroupId: clinicGroupId || "",
@@ -3579,8 +4062,8 @@ async function createClinicAdmin(db, data, clinicGroupService) {
3579
4062
  try {
3580
4063
  clinicAdminSchema.parse({
3581
4064
  ...adminData,
3582
- createdAt: Timestamp7.now(),
3583
- updatedAt: Timestamp7.now()
4065
+ createdAt: Timestamp8.now(),
4066
+ updatedAt: Timestamp8.now()
3584
4067
  });
3585
4068
  console.log("[CLINIC_ADMIN] Admin object validation passed");
3586
4069
  } catch (schemaError) {
@@ -3594,7 +4077,7 @@ async function createClinicAdmin(db, data, clinicGroupService) {
3594
4077
  adminId: adminData.id
3595
4078
  });
3596
4079
  try {
3597
- await setDoc6(doc7(db, CLINIC_ADMINS_COLLECTION, adminData.id), adminData);
4080
+ await setDoc7(doc8(db, CLINIC_ADMINS_COLLECTION, adminData.id), adminData);
3598
4081
  console.log("[CLINIC_ADMIN] Admin saved successfully");
3599
4082
  } catch (firestoreError) {
3600
4083
  console.error(
@@ -3643,30 +4126,30 @@ async function checkClinicGroupExists(db, groupId, clinicGroupService) {
3643
4126
  return !!group;
3644
4127
  }
3645
4128
  async function getClinicAdmin(db, adminId) {
3646
- const docRef = doc7(db, CLINIC_ADMINS_COLLECTION, adminId);
3647
- const docSnap = await getDoc10(docRef);
4129
+ const docRef = doc8(db, CLINIC_ADMINS_COLLECTION, adminId);
4130
+ const docSnap = await getDoc11(docRef);
3648
4131
  if (docSnap.exists()) {
3649
4132
  return docSnap.data();
3650
4133
  }
3651
4134
  return null;
3652
4135
  }
3653
4136
  async function getClinicAdminByUserRef(db, userRef) {
3654
- const q = query5(
3655
- collection5(db, CLINIC_ADMINS_COLLECTION),
3656
- where5("userRef", "==", userRef)
4137
+ const q = query6(
4138
+ collection6(db, CLINIC_ADMINS_COLLECTION),
4139
+ where6("userRef", "==", userRef)
3657
4140
  );
3658
- const querySnapshot = await getDocs5(q);
4141
+ const querySnapshot = await getDocs6(q);
3659
4142
  if (querySnapshot.empty) {
3660
4143
  return null;
3661
4144
  }
3662
4145
  return querySnapshot.docs[0].data();
3663
4146
  }
3664
4147
  async function getClinicAdminsByGroup(db, clinicGroupId) {
3665
- const q = query5(
3666
- collection5(db, CLINIC_ADMINS_COLLECTION),
3667
- where5("clinicGroupId", "==", clinicGroupId)
4148
+ const q = query6(
4149
+ collection6(db, CLINIC_ADMINS_COLLECTION),
4150
+ where6("clinicGroupId", "==", clinicGroupId)
3668
4151
  );
3669
- const querySnapshot = await getDocs5(q);
4152
+ const querySnapshot = await getDocs6(q);
3670
4153
  return querySnapshot.docs.map((doc34) => doc34.data());
3671
4154
  }
3672
4155
  async function updateClinicAdmin(db, adminId, data) {
@@ -3678,7 +4161,7 @@ async function updateClinicAdmin(db, adminId, data) {
3678
4161
  ...data,
3679
4162
  updatedAt: serverTimestamp8()
3680
4163
  };
3681
- await updateDoc7(doc7(db, CLINIC_ADMINS_COLLECTION, adminId), updatedData);
4164
+ await updateDoc8(doc8(db, CLINIC_ADMINS_COLLECTION, adminId), updatedData);
3682
4165
  const updatedAdmin = await getClinicAdmin(db, adminId);
3683
4166
  if (!updatedAdmin) {
3684
4167
  throw new Error("Failed to retrieve updated admin");
@@ -3690,7 +4173,7 @@ async function deleteClinicAdmin(db, adminId) {
3690
4173
  if (!admin) {
3691
4174
  throw new Error("Clinic admin not found");
3692
4175
  }
3693
- await deleteDoc(doc7(db, CLINIC_ADMINS_COLLECTION, adminId));
4176
+ await deleteDoc2(doc8(db, CLINIC_ADMINS_COLLECTION, adminId));
3694
4177
  }
3695
4178
  async function addClinicToManaged(db, adminId, clinicId, requesterId, clinicService) {
3696
4179
  const admin = await getClinicAdmin(db, adminId);
@@ -3934,27 +4417,27 @@ var ClinicAdminService = class extends BaseService {
3934
4417
 
3935
4418
  // src/services/practitioner/practitioner.service.ts
3936
4419
  import {
3937
- collection as collection6,
3938
- doc as doc8,
3939
- getDoc as getDoc11,
3940
- getDocs as getDocs6,
3941
- query as query6,
3942
- where as where6,
3943
- updateDoc as updateDoc8,
3944
- setDoc as setDoc7,
3945
- deleteDoc as deleteDoc2,
3946
- Timestamp as Timestamp9,
4420
+ collection as collection7,
4421
+ doc as doc9,
4422
+ getDoc as getDoc12,
4423
+ getDocs as getDocs7,
4424
+ query as query7,
4425
+ where as where7,
4426
+ updateDoc as updateDoc9,
4427
+ setDoc as setDoc8,
4428
+ deleteDoc as deleteDoc3,
4429
+ Timestamp as Timestamp10,
3947
4430
  serverTimestamp as serverTimestamp9,
3948
- limit as limit4,
4431
+ limit as limit5,
3949
4432
  startAfter as startAfter4,
3950
- orderBy,
4433
+ orderBy as orderBy2,
3951
4434
  arrayUnion as arrayUnion5,
3952
4435
  arrayRemove as arrayRemove4
3953
4436
  } from "firebase/firestore";
3954
4437
 
3955
4438
  // src/validations/practitioner.schema.ts
3956
4439
  import { z as z14 } from "zod";
3957
- import { Timestamp as Timestamp8 } from "firebase/firestore";
4440
+ import { Timestamp as Timestamp9 } from "firebase/firestore";
3958
4441
 
3959
4442
  // src/backoffice/types/static/certification.types.ts
3960
4443
  var CertificationLevel = /* @__PURE__ */ ((CertificationLevel2) => {
@@ -3987,9 +4470,9 @@ var practitionerBasicInfoSchema = z14.object({
3987
4470
  title: z14.string().min(2).max(100),
3988
4471
  email: z14.string().email(),
3989
4472
  phoneNumber: z14.string().regex(/^\+?[1-9]\d{1,14}$/, "Invalid phone number"),
3990
- dateOfBirth: z14.instanceof(Timestamp8).or(z14.date()),
4473
+ dateOfBirth: z14.instanceof(Timestamp9).or(z14.date()),
3991
4474
  gender: z14.enum(["male", "female", "other"]),
3992
- profileImageUrl: z14.string().url().optional(),
4475
+ profileImageUrl: mediaResourceSchema.optional(),
3993
4476
  bio: z14.string().max(1e3).optional(),
3994
4477
  languages: z14.array(z14.string()).min(1)
3995
4478
  });
@@ -3998,8 +4481,8 @@ var practitionerCertificationSchema = z14.object({
3998
4481
  specialties: z14.array(z14.nativeEnum(CertificationSpecialty)),
3999
4482
  licenseNumber: z14.string().min(3).max(50),
4000
4483
  issuingAuthority: z14.string().min(2).max(100),
4001
- issueDate: z14.instanceof(Timestamp8).or(z14.date()),
4002
- expiryDate: z14.instanceof(Timestamp8).or(z14.date()).optional(),
4484
+ issueDate: z14.instanceof(Timestamp9).or(z14.date()),
4485
+ expiryDate: z14.instanceof(Timestamp9).or(z14.date()).optional(),
4003
4486
  verificationStatus: z14.enum(["pending", "verified", "rejected"])
4004
4487
  });
4005
4488
  var timeSlotSchema = z14.object({
@@ -4016,8 +4499,8 @@ var practitionerWorkingHoursSchema = z14.object({
4016
4499
  friday: timeSlotSchema,
4017
4500
  saturday: timeSlotSchema,
4018
4501
  sunday: timeSlotSchema,
4019
- createdAt: z14.instanceof(Timestamp8).or(z14.date()),
4020
- updatedAt: z14.instanceof(Timestamp8).or(z14.date())
4502
+ createdAt: z14.instanceof(Timestamp9).or(z14.date()),
4503
+ updatedAt: z14.instanceof(Timestamp9).or(z14.date())
4021
4504
  });
4022
4505
  var practitionerClinicWorkingHoursSchema = z14.object({
4023
4506
  clinicId: z14.string().min(1),
@@ -4031,8 +4514,8 @@ var practitionerClinicWorkingHoursSchema = z14.object({
4031
4514
  sunday: timeSlotSchema
4032
4515
  }),
4033
4516
  isActive: z14.boolean(),
4034
- createdAt: z14.instanceof(Timestamp8).or(z14.date()),
4035
- updatedAt: z14.instanceof(Timestamp8).or(z14.date())
4517
+ createdAt: z14.instanceof(Timestamp9).or(z14.date()),
4518
+ updatedAt: z14.instanceof(Timestamp9).or(z14.date())
4036
4519
  });
4037
4520
  var practitionerSchema = z14.object({
4038
4521
  id: z14.string().min(1),
@@ -4048,8 +4531,8 @@ var practitionerSchema = z14.object({
4048
4531
  isActive: z14.boolean(),
4049
4532
  isVerified: z14.boolean(),
4050
4533
  status: z14.nativeEnum(PractitionerStatus),
4051
- createdAt: z14.instanceof(Timestamp8).or(z14.date()),
4052
- updatedAt: z14.instanceof(Timestamp8).or(z14.date())
4534
+ createdAt: z14.instanceof(Timestamp9).or(z14.date()),
4535
+ updatedAt: z14.instanceof(Timestamp9).or(z14.date())
4053
4536
  });
4054
4537
  var createPractitionerSchema = z14.object({
4055
4538
  userRef: z14.string().min(1),
@@ -4081,10 +4564,10 @@ var practitionerTokenSchema = z14.object({
4081
4564
  clinicId: z14.string().min(1),
4082
4565
  status: z14.nativeEnum(PractitionerTokenStatus),
4083
4566
  createdBy: z14.string().min(1),
4084
- createdAt: z14.instanceof(Timestamp8).or(z14.date()),
4085
- expiresAt: z14.instanceof(Timestamp8).or(z14.date()),
4567
+ createdAt: z14.instanceof(Timestamp9).or(z14.date()),
4568
+ expiresAt: z14.instanceof(Timestamp9).or(z14.date()),
4086
4569
  usedBy: z14.string().optional(),
4087
- usedAt: z14.instanceof(Timestamp8).or(z14.date()).optional()
4570
+ usedAt: z14.instanceof(Timestamp9).or(z14.date()).optional()
4088
4571
  });
4089
4572
  var createPractitionerTokenSchema = z14.object({
4090
4573
  practitionerId: z14.string().min(1),
@@ -4101,7 +4584,7 @@ var practitionerSignupSchema = z14.object({
4101
4584
  profileData: z14.object({
4102
4585
  basicInfo: z14.object({
4103
4586
  phoneNumber: z14.string().optional(),
4104
- profileImageUrl: z14.string().optional(),
4587
+ profileImageUrl: mediaResourceSchema.optional(),
4105
4588
  gender: z14.enum(["male", "female", "other"]).optional(),
4106
4589
  bio: z14.string().optional()
4107
4590
  }).optional(),
@@ -4116,6 +4599,7 @@ var PractitionerService = class extends BaseService {
4116
4599
  constructor(db, auth, app, clinicService) {
4117
4600
  super(db, auth, app);
4118
4601
  this.clinicService = clinicService;
4602
+ this.mediaService = new MediaService(db, auth, app);
4119
4603
  }
4120
4604
  getClinicService() {
4121
4605
  if (!this.clinicService) {
@@ -4126,6 +4610,53 @@ var PractitionerService = class extends BaseService {
4126
4610
  setClinicService(clinicService) {
4127
4611
  this.clinicService = clinicService;
4128
4612
  }
4613
+ /**
4614
+ * Handles profile photo upload for practitioners
4615
+ * @param profilePhoto - MediaResource (File, Blob, or URL string)
4616
+ * @param practitionerId - ID of the practitioner
4617
+ * @returns URL string of the uploaded or existing photo
4618
+ */
4619
+ async handleProfilePhotoUpload(profilePhoto, practitionerId) {
4620
+ if (!profilePhoto) {
4621
+ return void 0;
4622
+ }
4623
+ if (typeof profilePhoto === "string") {
4624
+ return profilePhoto;
4625
+ }
4626
+ if (profilePhoto instanceof File || profilePhoto instanceof Blob) {
4627
+ console.log(
4628
+ `[PractitionerService] Uploading profile photo for practitioner ${practitionerId}`
4629
+ );
4630
+ const mediaMetadata = await this.mediaService.uploadMedia(
4631
+ profilePhoto,
4632
+ practitionerId,
4633
+ // Using practitionerId as ownerId
4634
+ "public" /* PUBLIC */,
4635
+ // Profile photos should be public
4636
+ "practitioner_profile_photos",
4637
+ profilePhoto instanceof File ? profilePhoto.name : `profile_photo_${practitionerId}`
4638
+ );
4639
+ return mediaMetadata.url;
4640
+ }
4641
+ return void 0;
4642
+ }
4643
+ /**
4644
+ * Processes BasicPractitionerInfo to handle profile photo uploads
4645
+ * @param basicInfo - The basic info containing potential MediaResource profile photo
4646
+ * @param practitionerId - ID of the practitioner
4647
+ * @returns Processed basic info with URL string for profileImageUrl
4648
+ */
4649
+ async processBasicInfo(basicInfo, practitionerId) {
4650
+ const processedBasicInfo = { ...basicInfo };
4651
+ if (basicInfo.profileImageUrl) {
4652
+ const uploadedUrl = await this.handleProfilePhotoUpload(
4653
+ basicInfo.profileImageUrl,
4654
+ practitionerId
4655
+ );
4656
+ processedBasicInfo.profileImageUrl = uploadedUrl;
4657
+ }
4658
+ return processedBasicInfo;
4659
+ }
4129
4660
  /**
4130
4661
  * Creates a new practitioner
4131
4662
  */
@@ -4146,7 +4677,10 @@ var PractitionerService = class extends BaseService {
4146
4677
  const practitioner = {
4147
4678
  id: practitionerId,
4148
4679
  userRef: validData.userRef,
4149
- basicInfo: validData.basicInfo,
4680
+ basicInfo: await this.processBasicInfo(
4681
+ validData.basicInfo,
4682
+ practitionerId
4683
+ ),
4150
4684
  certification: validData.certification,
4151
4685
  clinics: validData.clinics || [],
4152
4686
  clinicWorkingHours: validData.clinicWorkingHours || [],
@@ -4162,15 +4696,15 @@ var PractitionerService = class extends BaseService {
4162
4696
  };
4163
4697
  practitionerSchema.parse({
4164
4698
  ...practitioner,
4165
- createdAt: Timestamp9.now(),
4166
- updatedAt: Timestamp9.now()
4699
+ createdAt: Timestamp10.now(),
4700
+ updatedAt: Timestamp10.now()
4167
4701
  });
4168
- const practitionerRef = doc8(
4702
+ const practitionerRef = doc9(
4169
4703
  this.db,
4170
4704
  PRACTITIONERS_COLLECTION,
4171
4705
  practitionerId
4172
4706
  );
4173
- await setDoc7(practitionerRef, practitioner);
4707
+ await setDoc8(practitionerRef, practitioner);
4174
4708
  const createdPractitioner = await this.getPractitioner(practitionerId);
4175
4709
  if (!createdPractitioner) {
4176
4710
  throw new Error(
@@ -4246,7 +4780,10 @@ var PractitionerService = class extends BaseService {
4246
4780
  id: practitionerId,
4247
4781
  userRef: "",
4248
4782
  // Prazno - biće popunjeno kada korisnik kreira nalog
4249
- basicInfo: validatedData.basicInfo,
4783
+ basicInfo: await this.processBasicInfo(
4784
+ validatedData.basicInfo,
4785
+ practitionerId
4786
+ ),
4250
4787
  certification: validatedData.certification,
4251
4788
  clinics,
4252
4789
  clinicWorkingHours: validatedData.clinicWorkingHours || [],
@@ -4263,11 +4800,11 @@ var PractitionerService = class extends BaseService {
4263
4800
  practitionerSchema.parse({
4264
4801
  ...practitionerData,
4265
4802
  userRef: "temp-for-validation",
4266
- createdAt: Timestamp9.now(),
4267
- updatedAt: Timestamp9.now()
4803
+ createdAt: Timestamp10.now(),
4804
+ updatedAt: Timestamp10.now()
4268
4805
  });
4269
- await setDoc7(
4270
- doc8(this.db, PRACTITIONERS_COLLECTION, practitionerData.id),
4806
+ await setDoc8(
4807
+ doc9(this.db, PRACTITIONERS_COLLECTION, practitionerData.id),
4271
4808
  practitionerData
4272
4809
  );
4273
4810
  const savedPractitioner = await this.getPractitioner(practitionerData.id);
@@ -4284,12 +4821,12 @@ var PractitionerService = class extends BaseService {
4284
4821
  clinicId,
4285
4822
  status: "active" /* ACTIVE */,
4286
4823
  createdBy,
4287
- createdAt: Timestamp9.now(),
4288
- expiresAt: Timestamp9.fromDate(expiration)
4824
+ createdAt: Timestamp10.now(),
4825
+ expiresAt: Timestamp10.fromDate(expiration)
4289
4826
  };
4290
4827
  practitionerTokenSchema.parse(token);
4291
4828
  const tokenPath = `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${token.id}`;
4292
- await setDoc7(doc8(this.db, tokenPath), token);
4829
+ await setDoc8(doc9(this.db, tokenPath), token);
4293
4830
  return { practitioner: savedPractitioner, token };
4294
4831
  } catch (error) {
4295
4832
  if (error instanceof z15.ZodError) {
@@ -4337,12 +4874,12 @@ var PractitionerService = class extends BaseService {
4337
4874
  clinicId: validatedData.clinicId,
4338
4875
  status: "active" /* ACTIVE */,
4339
4876
  createdBy,
4340
- createdAt: Timestamp9.now(),
4341
- expiresAt: Timestamp9.fromDate(expiration)
4877
+ createdAt: Timestamp10.now(),
4878
+ expiresAt: Timestamp10.fromDate(expiration)
4342
4879
  };
4343
4880
  practitionerTokenSchema.parse(token);
4344
4881
  const tokenPath = `${PRACTITIONERS_COLLECTION}/${validatedData.practitionerId}/${REGISTER_TOKENS_COLLECTION}/${token.id}`;
4345
- await setDoc7(doc8(this.db, tokenPath), token);
4882
+ await setDoc8(doc9(this.db, tokenPath), token);
4346
4883
  return token;
4347
4884
  } catch (error) {
4348
4885
  if (error instanceof z15.ZodError) {
@@ -4357,16 +4894,16 @@ var PractitionerService = class extends BaseService {
4357
4894
  * @returns Array of active tokens
4358
4895
  */
4359
4896
  async getPractitionerActiveTokens(practitionerId) {
4360
- const tokensRef = collection6(
4897
+ const tokensRef = collection7(
4361
4898
  this.db,
4362
4899
  `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
4363
4900
  );
4364
- const q = query6(
4901
+ const q = query7(
4365
4902
  tokensRef,
4366
- where6("status", "==", "active" /* ACTIVE */),
4367
- where6("expiresAt", ">", Timestamp9.now())
4903
+ where7("status", "==", "active" /* ACTIVE */),
4904
+ where7("expiresAt", ">", Timestamp10.now())
4368
4905
  );
4369
- const querySnapshot = await getDocs6(q);
4906
+ const querySnapshot = await getDocs7(q);
4370
4907
  return querySnapshot.docs.map((doc34) => doc34.data());
4371
4908
  }
4372
4909
  /**
@@ -4375,11 +4912,11 @@ var PractitionerService = class extends BaseService {
4375
4912
  * @returns The token if found and valid, null otherwise
4376
4913
  */
4377
4914
  async validateToken(tokenString) {
4378
- const practitionersRef = collection6(this.db, PRACTITIONERS_COLLECTION);
4379
- const practitionersSnapshot = await getDocs6(practitionersRef);
4915
+ const practitionersRef = collection7(this.db, PRACTITIONERS_COLLECTION);
4916
+ const practitionersSnapshot = await getDocs7(practitionersRef);
4380
4917
  for (const practitionerDoc of practitionersSnapshot.docs) {
4381
4918
  const practitionerId = practitionerDoc.id;
4382
- const tokensRef = collection6(
4919
+ const tokensRef = collection7(
4383
4920
  this.db,
4384
4921
  `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
4385
4922
  );
@@ -4387,17 +4924,17 @@ var PractitionerService = class extends BaseService {
4387
4924
  `[PRACTITIONER] Validating token for practitioner ${practitionerId}`,
4388
4925
  {
4389
4926
  tokenString,
4390
- timestamp: Timestamp9.now().toDate()
4927
+ timestamp: Timestamp10.now().toDate()
4391
4928
  }
4392
4929
  );
4393
- const q = query6(
4930
+ const q = query7(
4394
4931
  tokensRef,
4395
- where6("token", "==", tokenString),
4396
- where6("status", "==", "active" /* ACTIVE */),
4397
- where6("expiresAt", ">", Timestamp9.now())
4932
+ where7("token", "==", tokenString),
4933
+ where7("status", "==", "active" /* ACTIVE */),
4934
+ where7("expiresAt", ">", Timestamp10.now())
4398
4935
  );
4399
4936
  try {
4400
- const tokenSnapshot = await getDocs6(q);
4937
+ const tokenSnapshot = await getDocs7(q);
4401
4938
  console.log(
4402
4939
  `[PRACTITIONER] Token query results for practitioner ${practitionerId}`,
4403
4940
  {
@@ -4430,22 +4967,22 @@ var PractitionerService = class extends BaseService {
4430
4967
  * @param userId ID of the user using the token
4431
4968
  */
4432
4969
  async markTokenAsUsed(tokenId, practitionerId, userId) {
4433
- const tokenRef = doc8(
4970
+ const tokenRef = doc9(
4434
4971
  this.db,
4435
4972
  `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${tokenId}`
4436
4973
  );
4437
- await updateDoc8(tokenRef, {
4974
+ await updateDoc9(tokenRef, {
4438
4975
  status: "used" /* USED */,
4439
4976
  usedBy: userId,
4440
- usedAt: Timestamp9.now()
4977
+ usedAt: Timestamp10.now()
4441
4978
  });
4442
4979
  }
4443
4980
  /**
4444
4981
  * Dohvata zdravstvenog radnika po ID-u
4445
4982
  */
4446
4983
  async getPractitioner(practitionerId) {
4447
- const practitionerDoc = await getDoc11(
4448
- doc8(this.db, PRACTITIONERS_COLLECTION, practitionerId)
4984
+ const practitionerDoc = await getDoc12(
4985
+ doc9(this.db, PRACTITIONERS_COLLECTION, practitionerId)
4449
4986
  );
4450
4987
  if (!practitionerDoc.exists()) {
4451
4988
  return null;
@@ -4456,11 +4993,11 @@ var PractitionerService = class extends BaseService {
4456
4993
  * Dohvata zdravstvenog radnika po User ID-u
4457
4994
  */
4458
4995
  async getPractitionerByUserRef(userRef) {
4459
- const q = query6(
4460
- collection6(this.db, PRACTITIONERS_COLLECTION),
4461
- where6("userRef", "==", userRef)
4996
+ const q = query7(
4997
+ collection7(this.db, PRACTITIONERS_COLLECTION),
4998
+ where7("userRef", "==", userRef)
4462
4999
  );
4463
- const querySnapshot = await getDocs6(q);
5000
+ const querySnapshot = await getDocs7(q);
4464
5001
  if (querySnapshot.empty) {
4465
5002
  return null;
4466
5003
  }
@@ -4470,37 +5007,37 @@ var PractitionerService = class extends BaseService {
4470
5007
  * Dohvata sve zdravstvene radnike za određenu kliniku sa statusom ACTIVE
4471
5008
  */
4472
5009
  async getPractitionersByClinic(clinicId) {
4473
- const q = query6(
4474
- collection6(this.db, PRACTITIONERS_COLLECTION),
4475
- where6("clinics", "array-contains", clinicId),
4476
- where6("isActive", "==", true),
4477
- where6("status", "==", "active" /* ACTIVE */)
5010
+ const q = query7(
5011
+ collection7(this.db, PRACTITIONERS_COLLECTION),
5012
+ where7("clinics", "array-contains", clinicId),
5013
+ where7("isActive", "==", true),
5014
+ where7("status", "==", "active" /* ACTIVE */)
4478
5015
  );
4479
- const querySnapshot = await getDocs6(q);
5016
+ const querySnapshot = await getDocs7(q);
4480
5017
  return querySnapshot.docs.map((doc34) => doc34.data());
4481
5018
  }
4482
5019
  /**
4483
5020
  * Dohvata sve zdravstvene radnike za određenu kliniku
4484
5021
  */
4485
5022
  async getAllPractitionersByClinic(clinicId) {
4486
- const q = query6(
4487
- collection6(this.db, PRACTITIONERS_COLLECTION),
4488
- where6("clinics", "array-contains", clinicId),
4489
- where6("isActive", "==", true)
5023
+ const q = query7(
5024
+ collection7(this.db, PRACTITIONERS_COLLECTION),
5025
+ where7("clinics", "array-contains", clinicId),
5026
+ where7("isActive", "==", true)
4490
5027
  );
4491
- const querySnapshot = await getDocs6(q);
5028
+ const querySnapshot = await getDocs7(q);
4492
5029
  return querySnapshot.docs.map((doc34) => doc34.data());
4493
5030
  }
4494
5031
  /**
4495
5032
  * Dohvata sve draft zdravstvene radnike za određenu kliniku sa statusom DRAFT
4496
5033
  */
4497
5034
  async getDraftPractitionersByClinic(clinicId) {
4498
- const q = query6(
4499
- collection6(this.db, PRACTITIONERS_COLLECTION),
4500
- where6("clinics", "array-contains", clinicId),
4501
- where6("status", "==", "draft" /* DRAFT */)
5035
+ const q = query7(
5036
+ collection7(this.db, PRACTITIONERS_COLLECTION),
5037
+ where7("clinics", "array-contains", clinicId),
5038
+ where7("status", "==", "draft" /* DRAFT */)
4502
5039
  );
4503
- const querySnapshot = await getDocs6(q);
5040
+ const querySnapshot = await getDocs7(q);
4504
5041
  return querySnapshot.docs.map((doc34) => doc34.data());
4505
5042
  }
4506
5043
  /**
@@ -4509,21 +5046,28 @@ var PractitionerService = class extends BaseService {
4509
5046
  async updatePractitioner(practitionerId, data) {
4510
5047
  try {
4511
5048
  const validData = data;
4512
- const practitionerRef = doc8(
5049
+ const practitionerRef = doc9(
4513
5050
  this.db,
4514
5051
  PRACTITIONERS_COLLECTION,
4515
5052
  practitionerId
4516
5053
  );
4517
- const practitionerDoc = await getDoc11(practitionerRef);
5054
+ const practitionerDoc = await getDoc12(practitionerRef);
4518
5055
  if (!practitionerDoc.exists()) {
4519
5056
  throw new Error(`Practitioner ${practitionerId} not found`);
4520
5057
  }
4521
5058
  const currentPractitioner = practitionerDoc.data();
5059
+ let processedData = { ...validData };
5060
+ if (validData.basicInfo) {
5061
+ processedData.basicInfo = await this.processBasicInfo(
5062
+ validData.basicInfo,
5063
+ practitionerId
5064
+ );
5065
+ }
4522
5066
  const updateData = {
4523
- ...validData,
5067
+ ...processedData,
4524
5068
  updatedAt: serverTimestamp9()
4525
5069
  };
4526
- await updateDoc8(practitionerRef, updateData);
5070
+ await updateDoc9(practitionerRef, updateData);
4527
5071
  const updatedPractitioner = await this.getPractitioner(practitionerId);
4528
5072
  if (!updatedPractitioner) {
4529
5073
  throw new Error(
@@ -4545,12 +5089,12 @@ var PractitionerService = class extends BaseService {
4545
5089
  async addClinic(practitionerId, clinicId) {
4546
5090
  var _a;
4547
5091
  try {
4548
- const practitionerRef = doc8(
5092
+ const practitionerRef = doc9(
4549
5093
  this.db,
4550
5094
  PRACTITIONERS_COLLECTION,
4551
5095
  practitionerId
4552
5096
  );
4553
- const practitionerDoc = await getDoc11(practitionerRef);
5097
+ const practitionerDoc = await getDoc12(practitionerRef);
4554
5098
  if (!practitionerDoc.exists()) {
4555
5099
  throw new Error(`Practitioner ${practitionerId} not found`);
4556
5100
  }
@@ -4561,7 +5105,7 @@ var PractitionerService = class extends BaseService {
4561
5105
  );
4562
5106
  return;
4563
5107
  }
4564
- await updateDoc8(practitionerRef, {
5108
+ await updateDoc9(practitionerRef, {
4565
5109
  clinics: arrayUnion5(clinicId),
4566
5110
  updatedAt: serverTimestamp9()
4567
5111
  });
@@ -4578,16 +5122,16 @@ var PractitionerService = class extends BaseService {
4578
5122
  */
4579
5123
  async removeClinic(practitionerId, clinicId) {
4580
5124
  try {
4581
- const practitionerRef = doc8(
5125
+ const practitionerRef = doc9(
4582
5126
  this.db,
4583
5127
  PRACTITIONERS_COLLECTION,
4584
5128
  practitionerId
4585
5129
  );
4586
- const practitionerDoc = await getDoc11(practitionerRef);
5130
+ const practitionerDoc = await getDoc12(practitionerRef);
4587
5131
  if (!practitionerDoc.exists()) {
4588
5132
  throw new Error(`Practitioner ${practitionerId} not found`);
4589
5133
  }
4590
- await updateDoc8(practitionerRef, {
5134
+ await updateDoc9(practitionerRef, {
4591
5135
  clinics: arrayRemove4(clinicId),
4592
5136
  updatedAt: serverTimestamp9()
4593
5137
  });
@@ -4623,7 +5167,7 @@ var PractitionerService = class extends BaseService {
4623
5167
  if (!practitioner) {
4624
5168
  throw new Error("Practitioner not found");
4625
5169
  }
4626
- await deleteDoc2(doc8(this.db, PRACTITIONERS_COLLECTION, practitionerId));
5170
+ await deleteDoc3(doc9(this.db, PRACTITIONERS_COLLECTION, practitionerId));
4627
5171
  }
4628
5172
  /**
4629
5173
  * Validates a registration token and claims the associated draft practitioner profile
@@ -4692,21 +5236,21 @@ var PractitionerService = class extends BaseService {
4692
5236
  try {
4693
5237
  const constraints = [];
4694
5238
  if (!(options == null ? void 0 : options.includeDraftPractitioners)) {
4695
- constraints.push(where6("status", "==", "active" /* ACTIVE */));
5239
+ constraints.push(where7("status", "==", "active" /* ACTIVE */));
4696
5240
  }
4697
- constraints.push(orderBy("basicInfo.lastName", "asc"));
4698
- constraints.push(orderBy("basicInfo.firstName", "asc"));
5241
+ constraints.push(orderBy2("basicInfo.lastName", "asc"));
5242
+ constraints.push(orderBy2("basicInfo.firstName", "asc"));
4699
5243
  if ((options == null ? void 0 : options.pagination) && options.pagination > 0) {
4700
5244
  if (options.lastDoc) {
4701
5245
  constraints.push(startAfter4(options.lastDoc));
4702
5246
  }
4703
- constraints.push(limit4(options.pagination));
5247
+ constraints.push(limit5(options.pagination));
4704
5248
  }
4705
- const q = query6(
4706
- collection6(this.db, PRACTITIONERS_COLLECTION),
5249
+ const q = query7(
5250
+ collection7(this.db, PRACTITIONERS_COLLECTION),
4707
5251
  ...constraints
4708
5252
  );
4709
- const querySnapshot = await getDocs6(q);
5253
+ const querySnapshot = await getDocs7(q);
4710
5254
  const practitioners = querySnapshot.docs.map(
4711
5255
  (doc34) => doc34.data()
4712
5256
  );
@@ -4751,31 +5295,31 @@ var PractitionerService = class extends BaseService {
4751
5295
  );
4752
5296
  const constraints = [];
4753
5297
  if (!filters.includeDraftPractitioners) {
4754
- constraints.push(where6("status", "==", "active" /* ACTIVE */));
5298
+ constraints.push(where7("status", "==", "active" /* ACTIVE */));
4755
5299
  }
4756
- constraints.push(where6("isActive", "==", true));
5300
+ constraints.push(where7("isActive", "==", true));
4757
5301
  if (filters.certifications && filters.certifications.length > 0) {
4758
5302
  constraints.push(
4759
- where6(
5303
+ where7(
4760
5304
  "certification.certifications",
4761
5305
  "array-contains-any",
4762
5306
  filters.certifications
4763
5307
  )
4764
5308
  );
4765
5309
  }
4766
- constraints.push(orderBy("basicInfo.lastName", "asc"));
4767
- constraints.push(orderBy("basicInfo.firstName", "asc"));
5310
+ constraints.push(orderBy2("basicInfo.lastName", "asc"));
5311
+ constraints.push(orderBy2("basicInfo.firstName", "asc"));
4768
5312
  if (filters.pagination && filters.pagination > 0) {
4769
5313
  if (filters.lastDoc) {
4770
5314
  constraints.push(startAfter4(filters.lastDoc));
4771
5315
  }
4772
- constraints.push(limit4(filters.pagination));
5316
+ constraints.push(limit5(filters.pagination));
4773
5317
  }
4774
- const q = query6(
4775
- collection6(this.db, PRACTITIONERS_COLLECTION),
5318
+ const q = query7(
5319
+ collection7(this.db, PRACTITIONERS_COLLECTION),
4776
5320
  ...constraints
4777
5321
  );
4778
- const querySnapshot = await getDocs6(q);
5322
+ const querySnapshot = await getDocs7(q);
4779
5323
  console.log(
4780
5324
  `[PRACTITIONER_SERVICE] Found ${querySnapshot.docs.length} practitioners with base query`
4781
5325
  );
@@ -4901,7 +5445,7 @@ var UserService = class extends BaseService {
4901
5445
  updatedAt: serverTimestamp10(),
4902
5446
  lastLoginAt: serverTimestamp10()
4903
5447
  };
4904
- await setDoc8(doc9(this.db, USERS_COLLECTION, userData.uid), userData);
5448
+ await setDoc9(doc10(this.db, USERS_COLLECTION, userData.uid), userData);
4905
5449
  if (options == null ? void 0 : options.skipProfileCreation) {
4906
5450
  return this.getUserById(userData.uid);
4907
5451
  }
@@ -4910,7 +5454,7 @@ var UserService = class extends BaseService {
4910
5454
  roles,
4911
5455
  options
4912
5456
  );
4913
- await updateDoc9(doc9(this.db, USERS_COLLECTION, userData.uid), profiles);
5457
+ await updateDoc10(doc10(this.db, USERS_COLLECTION, userData.uid), profiles);
4914
5458
  return this.getUserById(userData.uid);
4915
5459
  }
4916
5460
  /**
@@ -4988,7 +5532,7 @@ var UserService = class extends BaseService {
4988
5532
  email: "",
4989
5533
  phoneNumber: "",
4990
5534
  title: "",
4991
- dateOfBirth: Timestamp10.now(),
5535
+ dateOfBirth: Timestamp11.now(),
4992
5536
  gender: "other",
4993
5537
  languages: ["Serbian"]
4994
5538
  },
@@ -4997,7 +5541,7 @@ var UserService = class extends BaseService {
4997
5541
  specialties: [],
4998
5542
  licenseNumber: "",
4999
5543
  issuingAuthority: "",
5000
- issueDate: Timestamp10.now(),
5544
+ issueDate: Timestamp11.now(),
5001
5545
  verificationStatus: "pending"
5002
5546
  },
5003
5547
  isActive: true,
@@ -5013,7 +5557,7 @@ var UserService = class extends BaseService {
5013
5557
  * Dohvata korisnika po ID-u
5014
5558
  */
5015
5559
  async getUserById(uid) {
5016
- const userDoc = await getDoc12(doc9(this.db, USERS_COLLECTION, uid));
5560
+ const userDoc = await getDoc13(doc10(this.db, USERS_COLLECTION, uid));
5017
5561
  if (!userDoc.exists()) {
5018
5562
  throw USER_ERRORS.NOT_FOUND;
5019
5563
  }
@@ -5024,19 +5568,19 @@ var UserService = class extends BaseService {
5024
5568
  * Dohvata korisnika po email-u
5025
5569
  */
5026
5570
  async getUserByEmail(email) {
5027
- const usersRef = collection7(this.db, USERS_COLLECTION);
5028
- const q = query7(usersRef, where7("email", "==", email));
5029
- const querySnapshot = await getDocs7(q);
5571
+ const usersRef = collection8(this.db, USERS_COLLECTION);
5572
+ const q = query8(usersRef, where8("email", "==", email));
5573
+ const querySnapshot = await getDocs8(q);
5030
5574
  if (querySnapshot.empty) return null;
5031
5575
  const userData = querySnapshot.docs[0].data();
5032
5576
  return userSchema.parse(userData);
5033
5577
  }
5034
5578
  async getUsersByRole(role) {
5035
5579
  const constraints = [
5036
- where7("roles", "array-contains", role)
5580
+ where8("roles", "array-contains", role)
5037
5581
  ];
5038
- const q = query7(collection7(this.db, USERS_COLLECTION), ...constraints);
5039
- const querySnapshot = await getDocs7(q);
5582
+ const q = query8(collection8(this.db, USERS_COLLECTION), ...constraints);
5583
+ const querySnapshot = await getDocs8(q);
5040
5584
  const users = querySnapshot.docs.map((doc34) => doc34.data());
5041
5585
  return Promise.all(users.map((userData) => userSchema.parse(userData)));
5042
5586
  }
@@ -5044,24 +5588,24 @@ var UserService = class extends BaseService {
5044
5588
  * Ažurira timestamp poslednjeg logovanja
5045
5589
  */
5046
5590
  async updateUserLoginTimestamp(uid) {
5047
- const userRef = doc9(this.db, USERS_COLLECTION, uid);
5048
- const userDoc = await getDoc12(userRef);
5591
+ const userRef = doc10(this.db, USERS_COLLECTION, uid);
5592
+ const userDoc = await getDoc13(userRef);
5049
5593
  if (!userDoc.exists()) {
5050
5594
  throw AUTH_ERRORS.USER_NOT_FOUND;
5051
5595
  }
5052
- await updateDoc9(userRef, {
5596
+ await updateDoc10(userRef, {
5053
5597
  lastLoginAt: serverTimestamp10(),
5054
5598
  updatedAt: serverTimestamp10()
5055
5599
  });
5056
5600
  return this.getUserById(uid);
5057
5601
  }
5058
5602
  async upgradeAnonymousUser(uid, email) {
5059
- const userRef = doc9(this.db, USERS_COLLECTION, uid);
5060
- const userDoc = await getDoc12(userRef);
5603
+ const userRef = doc10(this.db, USERS_COLLECTION, uid);
5604
+ const userDoc = await getDoc13(userRef);
5061
5605
  if (!userDoc.exists()) {
5062
5606
  throw USER_ERRORS.NOT_FOUND;
5063
5607
  }
5064
- await updateDoc9(userRef, {
5608
+ await updateDoc10(userRef, {
5065
5609
  email,
5066
5610
  isAnonymous: false,
5067
5611
  updatedAt: serverTimestamp10()
@@ -5069,8 +5613,8 @@ var UserService = class extends BaseService {
5069
5613
  return this.getUserById(uid);
5070
5614
  }
5071
5615
  async updateUser(uid, updates) {
5072
- const userRef = doc9(this.db, USERS_COLLECTION, uid);
5073
- const userDoc = await getDoc12(userRef);
5616
+ const userRef = doc10(this.db, USERS_COLLECTION, uid);
5617
+ const userDoc = await getDoc13(userRef);
5074
5618
  if (!userDoc.exists()) {
5075
5619
  throw USER_ERRORS.NOT_FOUND;
5076
5620
  }
@@ -5082,7 +5626,7 @@ var UserService = class extends BaseService {
5082
5626
  updatedAt: serverTimestamp10()
5083
5627
  };
5084
5628
  userSchema.parse(updatedUser);
5085
- await updateDoc9(userRef, {
5629
+ await updateDoc10(userRef, {
5086
5630
  ...updates,
5087
5631
  updatedAt: serverTimestamp10()
5088
5632
  });
@@ -5101,7 +5645,7 @@ var UserService = class extends BaseService {
5101
5645
  const user = await this.getUserById(uid);
5102
5646
  if (user.roles.includes(role)) return;
5103
5647
  const profiles = await this.createProfilesForRoles(uid, [role], options);
5104
- await updateDoc9(doc9(this.db, USERS_COLLECTION, uid), {
5648
+ await updateDoc10(doc10(this.db, USERS_COLLECTION, uid), {
5105
5649
  roles: [...user.roles, role],
5106
5650
  ...profiles,
5107
5651
  updatedAt: serverTimestamp10()
@@ -5136,15 +5680,15 @@ var UserService = class extends BaseService {
5136
5680
  }
5137
5681
  break;
5138
5682
  }
5139
- await updateDoc9(doc9(this.db, USERS_COLLECTION, uid), {
5683
+ await updateDoc10(doc10(this.db, USERS_COLLECTION, uid), {
5140
5684
  roles: user.roles.filter((r) => r !== role),
5141
5685
  updatedAt: serverTimestamp10()
5142
5686
  });
5143
5687
  }
5144
5688
  // Delete operations
5145
5689
  async deleteUser(uid) {
5146
- const userRef = doc9(this.db, USERS_COLLECTION, uid);
5147
- const userDoc = await getDoc12(userRef);
5690
+ const userRef = doc10(this.db, USERS_COLLECTION, uid);
5691
+ const userDoc = await getDoc13(userRef);
5148
5692
  if (!userDoc.exists()) {
5149
5693
  throw USER_ERRORS.NOT_FOUND;
5150
5694
  }
@@ -5165,7 +5709,7 @@ var UserService = class extends BaseService {
5165
5709
  userData.adminProfile
5166
5710
  );
5167
5711
  }
5168
- await deleteDoc3(userRef);
5712
+ await deleteDoc4(userRef);
5169
5713
  } catch (error) {
5170
5714
  throw error;
5171
5715
  }
@@ -5174,15 +5718,15 @@ var UserService = class extends BaseService {
5174
5718
 
5175
5719
  // src/services/clinic/utils/clinic-group.utils.ts
5176
5720
  import {
5177
- collection as collection8,
5178
- doc as doc10,
5179
- getDoc as getDoc13,
5180
- getDocs as getDocs8,
5181
- query as query8,
5182
- where as where8,
5183
- updateDoc as updateDoc10,
5184
- setDoc as setDoc9,
5185
- Timestamp as Timestamp11
5721
+ collection as collection9,
5722
+ doc as doc11,
5723
+ getDoc as getDoc14,
5724
+ getDocs as getDocs9,
5725
+ query as query9,
5726
+ where as where9,
5727
+ updateDoc as updateDoc11,
5728
+ setDoc as setDoc10,
5729
+ Timestamp as Timestamp12
5186
5730
  } from "firebase/firestore";
5187
5731
  import { geohashForLocation as geohashForLocation2 } from "geofire-common";
5188
5732
  import { z as z17 } from "zod";
@@ -5306,9 +5850,9 @@ async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdm
5306
5850
  throw geohashError;
5307
5851
  }
5308
5852
  }
5309
- const now = Timestamp11.now();
5853
+ const now = Timestamp12.now();
5310
5854
  console.log("[CLINIC_GROUP] Preparing clinic group data object");
5311
- const groupId = doc10(collection8(db, CLINIC_GROUPS_COLLECTION)).id;
5855
+ const groupId = doc11(collection9(db, CLINIC_GROUPS_COLLECTION)).id;
5312
5856
  console.log("[CLINIC_GROUP] Logo value:", {
5313
5857
  logoValue: validatedData.logo,
5314
5858
  logoType: validatedData.logo === null ? "null" : typeof validatedData.logo
@@ -5358,7 +5902,7 @@ async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdm
5358
5902
  groupId: groupData.id
5359
5903
  });
5360
5904
  try {
5361
- await setDoc9(doc10(db, CLINIC_GROUPS_COLLECTION, groupData.id), groupData);
5905
+ await setDoc10(doc11(db, CLINIC_GROUPS_COLLECTION, groupData.id), groupData);
5362
5906
  console.log("[CLINIC_GROUP] Clinic group saved successfully");
5363
5907
  } catch (firestoreError) {
5364
5908
  console.error(
@@ -5404,19 +5948,19 @@ async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdm
5404
5948
  }
5405
5949
  }
5406
5950
  async function getClinicGroup(db, groupId) {
5407
- const docRef = doc10(db, CLINIC_GROUPS_COLLECTION, groupId);
5408
- const docSnap = await getDoc13(docRef);
5951
+ const docRef = doc11(db, CLINIC_GROUPS_COLLECTION, groupId);
5952
+ const docSnap = await getDoc14(docRef);
5409
5953
  if (docSnap.exists()) {
5410
5954
  return docSnap.data();
5411
5955
  }
5412
5956
  return null;
5413
5957
  }
5414
5958
  async function getAllActiveGroups(db) {
5415
- const q = query8(
5416
- collection8(db, CLINIC_GROUPS_COLLECTION),
5417
- where8("isActive", "==", true)
5959
+ const q = query9(
5960
+ collection9(db, CLINIC_GROUPS_COLLECTION),
5961
+ where9("isActive", "==", true)
5418
5962
  );
5419
- const querySnapshot = await getDocs8(q);
5963
+ const querySnapshot = await getDocs9(q);
5420
5964
  return querySnapshot.docs.map((doc34) => doc34.data());
5421
5965
  }
5422
5966
  async function updateClinicGroup(db, groupId, data, app) {
@@ -5445,10 +5989,10 @@ async function updateClinicGroup(db, groupId, data, app) {
5445
5989
  }
5446
5990
  updatedData = {
5447
5991
  ...updatedData,
5448
- updatedAt: Timestamp11.now()
5992
+ updatedAt: Timestamp12.now()
5449
5993
  };
5450
5994
  console.log("[CLINIC_GROUP] Updating clinic group in Firestore");
5451
- await updateDoc10(doc10(db, CLINIC_GROUPS_COLLECTION, groupId), updatedData);
5995
+ await updateDoc11(doc11(db, CLINIC_GROUPS_COLLECTION, groupId), updatedData);
5452
5996
  console.log("[CLINIC_GROUP] Clinic group updated successfully");
5453
5997
  const updatedGroup = await getClinicGroup(db, groupId);
5454
5998
  if (!updatedGroup) {
@@ -5529,10 +6073,10 @@ async function createAdminToken(db, groupId, creatorAdminId, app, data) {
5529
6073
  if (!group.admins.includes(creatorAdminId)) {
5530
6074
  throw new Error("Admin does not belong to this clinic group");
5531
6075
  }
5532
- const now = Timestamp11.now();
6076
+ const now = Timestamp12.now();
5533
6077
  const expiresInDays = (data == null ? void 0 : data.expiresInDays) || 7;
5534
6078
  const email = (data == null ? void 0 : data.email) || null;
5535
- const expiresAt = new Timestamp11(
6079
+ const expiresAt = new Timestamp12(
5536
6080
  now.seconds + expiresInDays * 24 * 60 * 60,
5537
6081
  now.nanoseconds
5538
6082
  );
@@ -5566,7 +6110,7 @@ async function verifyAndUseAdminToken(db, groupId, token, userRef, app) {
5566
6110
  if (adminToken.status !== "active" /* ACTIVE */) {
5567
6111
  throw new Error("Admin token is not active");
5568
6112
  }
5569
- const now = Timestamp11.now();
6113
+ const now = Timestamp12.now();
5570
6114
  if (adminToken.expiresAt.seconds < now.seconds) {
5571
6115
  const updatedTokens2 = group.adminTokens.map(
5572
6116
  (t) => t.id === adminToken.id ? { ...t, status: "expired" /* EXPIRED */ } : t
@@ -5849,16 +6393,16 @@ import { z as z19 } from "zod";
5849
6393
 
5850
6394
  // src/services/clinic/utils/clinic.utils.ts
5851
6395
  import {
5852
- collection as collection9,
5853
- doc as doc11,
5854
- getDoc as getDoc14,
5855
- getDocs as getDocs9,
5856
- query as query9,
5857
- where as where9,
5858
- updateDoc as updateDoc11,
5859
- setDoc as setDoc10,
5860
- Timestamp as Timestamp12,
5861
- limit as limit5,
6396
+ collection as collection10,
6397
+ doc as doc12,
6398
+ getDoc as getDoc15,
6399
+ getDocs as getDocs10,
6400
+ query as query10,
6401
+ where as where10,
6402
+ updateDoc as updateDoc12,
6403
+ setDoc as setDoc11,
6404
+ Timestamp as Timestamp13,
6405
+ limit as limit6,
5862
6406
  startAfter as startAfter5
5863
6407
  } from "firebase/firestore";
5864
6408
  import {
@@ -5868,20 +6412,20 @@ import {
5868
6412
  } from "geofire-common";
5869
6413
  import { z as z18 } from "zod";
5870
6414
  async function getClinic(db, clinicId) {
5871
- const docRef = doc11(db, CLINICS_COLLECTION, clinicId);
5872
- const docSnap = await getDoc14(docRef);
6415
+ const docRef = doc12(db, CLINICS_COLLECTION, clinicId);
6416
+ const docSnap = await getDoc15(docRef);
5873
6417
  if (docSnap.exists()) {
5874
6418
  return docSnap.data();
5875
6419
  }
5876
6420
  return null;
5877
6421
  }
5878
6422
  async function getClinicsByGroup(db, groupId) {
5879
- const q = query9(
5880
- collection9(db, CLINICS_COLLECTION),
5881
- where9("clinicGroupId", "==", groupId),
5882
- where9("isActive", "==", true)
6423
+ const q = query10(
6424
+ collection10(db, CLINICS_COLLECTION),
6425
+ where10("clinicGroupId", "==", groupId),
6426
+ where10("isActive", "==", true)
5883
6427
  );
5884
- const querySnapshot = await getDocs9(q);
6428
+ const querySnapshot = await getDocs10(q);
5885
6429
  return querySnapshot.docs.map((doc34) => doc34.data());
5886
6430
  }
5887
6431
  async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app) {
@@ -6037,11 +6581,11 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app
6037
6581
  }
6038
6582
  updatedData = {
6039
6583
  ...updatedData,
6040
- updatedAt: Timestamp12.now()
6584
+ updatedAt: Timestamp13.now()
6041
6585
  };
6042
6586
  console.log("[CLINIC] Updating clinic in Firestore");
6043
6587
  try {
6044
- await updateDoc11(doc11(db, CLINICS_COLLECTION, clinicId), updatedData);
6588
+ await updateDoc12(doc12(db, CLINICS_COLLECTION, clinicId), updatedData);
6045
6589
  console.log("[CLINIC] Clinic updated successfully");
6046
6590
  } catch (updateError) {
6047
6591
  console.error("[CLINIC] Error updating clinic in Firestore:", updateError);
@@ -6070,12 +6614,12 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
6070
6614
  if (clinicIds.length === 0) {
6071
6615
  return [];
6072
6616
  }
6073
- const constraints = [where9("id", "in", clinicIds)];
6617
+ const constraints = [where10("id", "in", clinicIds)];
6074
6618
  if (options.isActive !== void 0) {
6075
- constraints.push(where9("isActive", "==", options.isActive));
6619
+ constraints.push(where10("isActive", "==", options.isActive));
6076
6620
  }
6077
- const q = query9(collection9(db, CLINICS_COLLECTION), ...constraints);
6078
- const querySnapshot = await getDocs9(q);
6621
+ const q = query10(collection10(db, CLINICS_COLLECTION), ...constraints);
6622
+ const querySnapshot = await getDocs10(q);
6079
6623
  return querySnapshot.docs.map((doc34) => doc34.data());
6080
6624
  }
6081
6625
  async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
@@ -6089,8 +6633,8 @@ async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGr
6089
6633
  }
6090
6634
  async function getClinicById(db, clinicId) {
6091
6635
  try {
6092
- const clinicRef = doc11(db, CLINICS_COLLECTION, clinicId);
6093
- const clinicSnapshot = await getDoc14(clinicRef);
6636
+ const clinicRef = doc12(db, CLINICS_COLLECTION, clinicId);
6637
+ const clinicSnapshot = await getDoc15(clinicRef);
6094
6638
  if (!clinicSnapshot.exists()) {
6095
6639
  return null;
6096
6640
  }
@@ -6106,20 +6650,20 @@ async function getClinicById(db, clinicId) {
6106
6650
  }
6107
6651
  async function getAllClinics(db, pagination, lastDoc) {
6108
6652
  try {
6109
- const clinicsCollection = collection9(db, CLINICS_COLLECTION);
6110
- let clinicsQuery = query9(clinicsCollection);
6653
+ const clinicsCollection = collection10(db, CLINICS_COLLECTION);
6654
+ let clinicsQuery = query10(clinicsCollection);
6111
6655
  if (pagination && pagination > 0) {
6112
6656
  if (lastDoc) {
6113
- clinicsQuery = query9(
6657
+ clinicsQuery = query10(
6114
6658
  clinicsCollection,
6115
6659
  startAfter5(lastDoc),
6116
- limit5(pagination)
6660
+ limit6(pagination)
6117
6661
  );
6118
6662
  } else {
6119
- clinicsQuery = query9(clinicsCollection, limit5(pagination));
6663
+ clinicsQuery = query10(clinicsCollection, limit6(pagination));
6120
6664
  }
6121
6665
  }
6122
- const clinicsSnapshot = await getDocs9(clinicsQuery);
6666
+ const clinicsSnapshot = await getDocs10(clinicsQuery);
6123
6667
  const lastVisible = clinicsSnapshot.docs[clinicsSnapshot.docs.length - 1];
6124
6668
  const clinics = clinicsSnapshot.docs.map((doc34) => {
6125
6669
  const data = doc34.data();
@@ -6146,12 +6690,12 @@ async function getAllClinicsInRange(db, center, rangeInKm, pagination, lastDoc)
6146
6690
  let lastDocSnapshot = null;
6147
6691
  for (const b of bounds) {
6148
6692
  const constraints = [
6149
- where9("location.geohash", ">=", b[0]),
6150
- where9("location.geohash", "<=", b[1]),
6151
- where9("isActive", "==", true)
6693
+ where10("location.geohash", ">=", b[0]),
6694
+ where10("location.geohash", "<=", b[1]),
6695
+ where10("isActive", "==", true)
6152
6696
  ];
6153
- const q = query9(collection9(db, CLINICS_COLLECTION), ...constraints);
6154
- const querySnapshot = await getDocs9(q);
6697
+ const q = query10(collection10(db, CLINICS_COLLECTION), ...constraints);
6698
+ const querySnapshot = await getDocs10(q);
6155
6699
  for (const doc34 of querySnapshot.docs) {
6156
6700
  const clinic = doc34.data();
6157
6701
  const distance = distanceBetween2(
@@ -6247,10 +6791,10 @@ async function removeTags(db, clinicId, adminId, tagsToRemove, clinicAdminServic
6247
6791
 
6248
6792
  // src/services/clinic/utils/search.utils.ts
6249
6793
  import {
6250
- collection as collection10,
6251
- query as query10,
6252
- where as where10,
6253
- getDocs as getDocs10
6794
+ collection as collection11,
6795
+ query as query11,
6796
+ where as where11,
6797
+ getDocs as getDocs11
6254
6798
  } from "firebase/firestore";
6255
6799
  import { geohashQueryBounds as geohashQueryBounds2, distanceBetween as distanceBetween3 } from "geofire-common";
6256
6800
  async function findClinicsInRadius(db, center, radiusInKm, filters) {
@@ -6261,20 +6805,20 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
6261
6805
  const matchingDocs = [];
6262
6806
  for (const b of bounds) {
6263
6807
  const constraints = [
6264
- where10("location.geohash", ">=", b[0]),
6265
- where10("location.geohash", "<=", b[1]),
6266
- where10("isActive", "==", true)
6808
+ where11("location.geohash", ">=", b[0]),
6809
+ where11("location.geohash", "<=", b[1]),
6810
+ where11("isActive", "==", true)
6267
6811
  ];
6268
6812
  if (filters == null ? void 0 : filters.services) {
6269
6813
  constraints.push(
6270
- where10("services", "array-contains-any", filters.services)
6814
+ where11("services", "array-contains-any", filters.services)
6271
6815
  );
6272
6816
  }
6273
6817
  if ((filters == null ? void 0 : filters.tags) && filters.tags.length > 0) {
6274
- constraints.push(where10("tags", "array-contains-any", filters.tags));
6818
+ constraints.push(where11("tags", "array-contains-any", filters.tags));
6275
6819
  }
6276
- const q = query10(collection10(db, CLINICS_COLLECTION), ...constraints);
6277
- const querySnapshot = await getDocs10(q);
6820
+ const q = query11(collection11(db, CLINICS_COLLECTION), ...constraints);
6821
+ const querySnapshot = await getDocs11(q);
6278
6822
  for (const doc34 of querySnapshot.docs) {
6279
6823
  const clinic = doc34.data();
6280
6824
  const distance = distanceBetween3(
@@ -6302,13 +6846,13 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
6302
6846
 
6303
6847
  // src/services/clinic/utils/filter.utils.ts
6304
6848
  import {
6305
- collection as collection11,
6306
- query as query11,
6307
- where as where11,
6308
- getDocs as getDocs11,
6849
+ collection as collection12,
6850
+ query as query12,
6851
+ where as where12,
6852
+ getDocs as getDocs12,
6309
6853
  startAfter as startAfter6,
6310
- limit as limit6,
6311
- orderBy as orderBy2
6854
+ limit as limit7,
6855
+ orderBy as orderBy3
6312
6856
  } from "firebase/firestore";
6313
6857
  import { geohashQueryBounds as geohashQueryBounds3, distanceBetween as distanceBetween4 } from "geofire-common";
6314
6858
  async function getClinicsByFilters(db, filters) {
@@ -6319,37 +6863,37 @@ async function getClinicsByFilters(db, filters) {
6319
6863
  const isGeoQuery = filters.center && filters.radiusInKm && filters.radiusInKm > 0;
6320
6864
  const constraints = [];
6321
6865
  if (filters.isActive !== void 0) {
6322
- constraints.push(where11("isActive", "==", filters.isActive));
6866
+ constraints.push(where12("isActive", "==", filters.isActive));
6323
6867
  } else {
6324
- constraints.push(where11("isActive", "==", true));
6868
+ constraints.push(where12("isActive", "==", true));
6325
6869
  }
6326
6870
  if (filters.tags && filters.tags.length > 0) {
6327
- constraints.push(where11("tags", "array-contains", filters.tags[0]));
6871
+ constraints.push(where12("tags", "array-contains", filters.tags[0]));
6328
6872
  }
6329
6873
  if (filters.procedureTechnology) {
6330
6874
  constraints.push(
6331
- where11("servicesInfo.technology", "==", filters.procedureTechnology)
6875
+ where12("servicesInfo.technology", "==", filters.procedureTechnology)
6332
6876
  );
6333
6877
  } else if (filters.procedureSubcategory) {
6334
6878
  constraints.push(
6335
- where11("servicesInfo.subCategory", "==", filters.procedureSubcategory)
6879
+ where12("servicesInfo.subCategory", "==", filters.procedureSubcategory)
6336
6880
  );
6337
6881
  } else if (filters.procedureCategory) {
6338
6882
  constraints.push(
6339
- where11("servicesInfo.category", "==", filters.procedureCategory)
6883
+ where12("servicesInfo.category", "==", filters.procedureCategory)
6340
6884
  );
6341
6885
  } else if (filters.procedureFamily) {
6342
6886
  constraints.push(
6343
- where11("servicesInfo.procedureFamily", "==", filters.procedureFamily)
6887
+ where12("servicesInfo.procedureFamily", "==", filters.procedureFamily)
6344
6888
  );
6345
6889
  }
6346
6890
  if (filters.pagination && filters.pagination > 0 && filters.lastDoc) {
6347
6891
  constraints.push(startAfter6(filters.lastDoc));
6348
- constraints.push(limit6(filters.pagination));
6892
+ constraints.push(limit7(filters.pagination));
6349
6893
  } else if (filters.pagination && filters.pagination > 0) {
6350
- constraints.push(limit6(filters.pagination));
6894
+ constraints.push(limit7(filters.pagination));
6351
6895
  }
6352
- constraints.push(orderBy2("location.geohash"));
6896
+ constraints.push(orderBy3("location.geohash"));
6353
6897
  let clinicsResult = [];
6354
6898
  let lastVisibleDoc = null;
6355
6899
  if (isGeoQuery) {
@@ -6357,440 +6901,124 @@ async function getClinicsByFilters(db, filters) {
6357
6901
  const radiusInKm = filters.radiusInKm;
6358
6902
  const bounds = geohashQueryBounds3(
6359
6903
  [center.latitude, center.longitude],
6360
- radiusInKm * 1e3
6361
- // Convert to meters
6362
- );
6363
- const matchingClinics = [];
6364
- for (const bound of bounds) {
6365
- const geoConstraints = [
6366
- ...constraints,
6367
- where11("location.geohash", ">=", bound[0]),
6368
- where11("location.geohash", "<=", bound[1])
6369
- ];
6370
- const q = query11(collection11(db, CLINICS_COLLECTION), ...geoConstraints);
6371
- const querySnapshot = await getDocs11(q);
6372
- console.log(
6373
- `[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics in geo bound`
6374
- );
6375
- for (const doc34 of querySnapshot.docs) {
6376
- const clinic = { ...doc34.data(), id: doc34.id };
6377
- const distance = distanceBetween4(
6378
- [center.latitude, center.longitude],
6379
- [clinic.location.latitude, clinic.location.longitude]
6380
- );
6381
- const distanceInKm = distance / 1e3;
6382
- if (distanceInKm <= radiusInKm) {
6383
- matchingClinics.push({
6384
- ...clinic,
6385
- distance: distanceInKm
6386
- });
6387
- }
6388
- }
6389
- }
6390
- let filteredClinics = matchingClinics;
6391
- if (filters.tags && filters.tags.length > 1) {
6392
- filteredClinics = filteredClinics.filter((clinic) => {
6393
- return filters.tags.every((tag) => clinic.tags.includes(tag));
6394
- });
6395
- }
6396
- if (filters.minRating !== void 0) {
6397
- filteredClinics = filteredClinics.filter(
6398
- (clinic) => clinic.reviewInfo.averageRating >= filters.minRating
6399
- );
6400
- }
6401
- if (filters.maxRating !== void 0) {
6402
- filteredClinics = filteredClinics.filter(
6403
- (clinic) => clinic.reviewInfo.averageRating <= filters.maxRating
6404
- );
6405
- }
6406
- filteredClinics.sort((a, b) => a.distance - b.distance);
6407
- if (filters.pagination && filters.pagination > 0) {
6408
- let startIndex = 0;
6409
- if (filters.lastDoc) {
6410
- const lastDocIndex = filteredClinics.findIndex(
6411
- (clinic) => clinic.id === filters.lastDoc.id
6412
- );
6413
- if (lastDocIndex !== -1) {
6414
- startIndex = lastDocIndex + 1;
6415
- }
6416
- }
6417
- const paginatedClinics = filteredClinics.slice(
6418
- startIndex,
6419
- startIndex + filters.pagination
6420
- );
6421
- lastVisibleDoc = paginatedClinics.length > 0 ? paginatedClinics[paginatedClinics.length - 1] : null;
6422
- clinicsResult = paginatedClinics;
6423
- } else {
6424
- clinicsResult = filteredClinics;
6425
- }
6426
- } else {
6427
- const q = query11(collection11(db, CLINICS_COLLECTION), ...constraints);
6428
- const querySnapshot = await getDocs11(q);
6429
- console.log(
6430
- `[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics with regular query`
6431
- );
6432
- const clinics = querySnapshot.docs.map((doc34) => {
6433
- return { ...doc34.data(), id: doc34.id };
6434
- });
6435
- let filteredClinics = clinics;
6436
- if (filters.center) {
6437
- const center = filters.center;
6438
- const clinicsWithDistance = [];
6439
- filteredClinics.forEach((clinic) => {
6440
- const distance = distanceBetween4(
6441
- [center.latitude, center.longitude],
6442
- [clinic.location.latitude, clinic.location.longitude]
6443
- );
6444
- clinicsWithDistance.push({
6445
- ...clinic,
6446
- distance: distance / 1e3
6447
- // Convert to kilometers
6448
- });
6449
- });
6450
- filteredClinics = clinicsWithDistance;
6451
- filteredClinics.sort(
6452
- (a, b) => a.distance - b.distance
6453
- );
6454
- }
6455
- if (filters.tags && filters.tags.length > 1) {
6456
- filteredClinics = filteredClinics.filter((clinic) => {
6457
- return filters.tags.every((tag) => clinic.tags.includes(tag));
6458
- });
6459
- }
6460
- if (filters.minRating !== void 0) {
6461
- filteredClinics = filteredClinics.filter(
6462
- (clinic) => clinic.reviewInfo.averageRating >= filters.minRating
6463
- );
6464
- }
6465
- if (filters.maxRating !== void 0) {
6466
- filteredClinics = filteredClinics.filter(
6467
- (clinic) => clinic.reviewInfo.averageRating <= filters.maxRating
6468
- );
6469
- }
6470
- lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
6471
- clinicsResult = filteredClinics;
6472
- }
6473
- return {
6474
- clinics: clinicsResult,
6475
- lastDoc: lastVisibleDoc
6476
- };
6477
- }
6478
-
6479
- // src/services/media/media.service.ts
6480
- import { Timestamp as Timestamp13 } from "firebase/firestore";
6481
- import {
6482
- ref as ref3,
6483
- uploadBytes as uploadBytes3,
6484
- getDownloadURL as getDownloadURL3,
6485
- deleteObject as deleteObject3,
6486
- getBytes
6487
- } from "firebase/storage";
6488
- import {
6489
- doc as doc12,
6490
- getDoc as getDoc15,
6491
- setDoc as setDoc11,
6492
- updateDoc as updateDoc12,
6493
- collection as collection12,
6494
- query as query12,
6495
- where as where12,
6496
- limit as limit7,
6497
- getDocs as getDocs12,
6498
- deleteDoc as deleteDoc6,
6499
- orderBy as orderBy3
6500
- } from "firebase/firestore";
6501
- var MediaAccessLevel = /* @__PURE__ */ ((MediaAccessLevel2) => {
6502
- MediaAccessLevel2["PUBLIC"] = "public";
6503
- MediaAccessLevel2["PRIVATE"] = "private";
6504
- MediaAccessLevel2["CONFIDENTIAL"] = "confidential";
6505
- return MediaAccessLevel2;
6506
- })(MediaAccessLevel || {});
6507
- var MEDIA_METADATA_COLLECTION = "media_metadata";
6508
- var MediaService = class extends BaseService {
6509
- constructor(db, auth, app) {
6510
- super(db, auth, app);
6511
- }
6512
- /**
6513
- * Upload a media file, store its metadata, and return the metadata including the URL.
6514
- * @param file - The file to upload.
6515
- * @param ownerId - ID of the owner (user, patient, clinic, etc.).
6516
- * @param accessLevel - Access level (public, private, confidential).
6517
- * @param collectionName - The logical collection name this media belongs to (e.g., 'patient_profile_pictures', 'clinic_logos').
6518
- * @param originalFileName - Optional: the original name of the file, if not using file.name.
6519
- * @returns Promise with the media metadata.
6520
- */
6521
- async uploadMedia(file, ownerId, accessLevel, collectionName, originalFileName) {
6522
- const mediaId = this.generateId();
6523
- const fileNameToUse = originalFileName || (file instanceof File ? file.name : file.toString());
6524
- const uniqueFileName = `${mediaId}-${fileNameToUse}`;
6525
- const filePath = `media/${accessLevel}/${ownerId}/${collectionName}/${uniqueFileName}`;
6526
- console.log(`[MediaService] Uploading file to: ${filePath}`);
6527
- const storageRef = ref3(this.storage, filePath);
6528
- try {
6529
- const uploadResult = await uploadBytes3(storageRef, file, {
6530
- contentType: file.type
6531
- });
6532
- console.log("[MediaService] File uploaded successfully", uploadResult);
6533
- const downloadURL = await getDownloadURL3(uploadResult.ref);
6534
- console.log("[MediaService] Got download URL:", downloadURL);
6535
- const metadata = {
6536
- id: mediaId,
6537
- name: fileNameToUse,
6538
- url: downloadURL,
6539
- contentType: file.type,
6540
- size: file.size,
6541
- createdAt: Timestamp13.now(),
6542
- accessLevel,
6543
- ownerId,
6544
- collectionName,
6545
- path: filePath
6546
- };
6547
- const metadataDocRef = doc12(this.db, MEDIA_METADATA_COLLECTION, mediaId);
6548
- await setDoc11(metadataDocRef, metadata);
6549
- console.log("[MediaService] Metadata stored in Firestore:", mediaId);
6550
- return metadata;
6551
- } catch (error) {
6552
- console.error("[MediaService] Error during media upload:", error);
6553
- throw error;
6554
- }
6555
- }
6556
- /**
6557
- * Get media metadata from Firestore by its ID.
6558
- * @param mediaId - ID of the media.
6559
- * @returns Promise with the media metadata or null if not found.
6560
- */
6561
- async getMediaMetadata(mediaId) {
6562
- console.log(`[MediaService] Getting media metadata for ID: ${mediaId}`);
6563
- const docRef = doc12(this.db, MEDIA_METADATA_COLLECTION, mediaId);
6564
- const docSnap = await getDoc15(docRef);
6565
- if (docSnap.exists()) {
6566
- console.log("[MediaService] Metadata found:", docSnap.data());
6567
- return docSnap.data();
6568
- }
6569
- console.log("[MediaService] No metadata found for ID:", mediaId);
6570
- return null;
6571
- }
6572
- /**
6573
- * Get media metadata from Firestore by its public URL.
6574
- * @param url - The public URL of the media file.
6575
- * @returns Promise with the media metadata or null if not found.
6576
- */
6577
- async getMediaMetadataByUrl(url) {
6578
- console.log(`[MediaService] Getting media metadata by URL: ${url}`);
6579
- const q = query12(
6580
- collection12(this.db, MEDIA_METADATA_COLLECTION),
6581
- where12("url", "==", url),
6582
- limit7(1)
6904
+ radiusInKm * 1e3
6905
+ // Convert to meters
6583
6906
  );
6584
- try {
6907
+ const matchingClinics = [];
6908
+ for (const bound of bounds) {
6909
+ const geoConstraints = [
6910
+ ...constraints,
6911
+ where12("location.geohash", ">=", bound[0]),
6912
+ where12("location.geohash", "<=", bound[1])
6913
+ ];
6914
+ const q = query12(collection12(db, CLINICS_COLLECTION), ...geoConstraints);
6585
6915
  const querySnapshot = await getDocs12(q);
6586
- if (!querySnapshot.empty) {
6587
- const metadata = querySnapshot.docs[0].data();
6588
- console.log("[MediaService] Metadata found by URL:", metadata);
6589
- return metadata;
6916
+ console.log(
6917
+ `[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics in geo bound`
6918
+ );
6919
+ for (const doc34 of querySnapshot.docs) {
6920
+ const clinic = { ...doc34.data(), id: doc34.id };
6921
+ const distance = distanceBetween4(
6922
+ [center.latitude, center.longitude],
6923
+ [clinic.location.latitude, clinic.location.longitude]
6924
+ );
6925
+ const distanceInKm = distance / 1e3;
6926
+ if (distanceInKm <= radiusInKm) {
6927
+ matchingClinics.push({
6928
+ ...clinic,
6929
+ distance: distanceInKm
6930
+ });
6931
+ }
6590
6932
  }
6591
- console.log("[MediaService] No metadata found for URL:", url);
6592
- return null;
6593
- } catch (error) {
6594
- console.error("[MediaService] Error fetching metadata by URL:", error);
6595
- throw error;
6596
6933
  }
6597
- }
6598
- /**
6599
- * Delete media from storage and remove metadata from Firestore.
6600
- * @param mediaId - ID of the media to delete.
6601
- */
6602
- async deleteMedia(mediaId) {
6603
- console.log(`[MediaService] Deleting media with ID: ${mediaId}`);
6604
- const metadata = await this.getMediaMetadata(mediaId);
6605
- if (!metadata) {
6606
- console.warn(
6607
- `[MediaService] Metadata not found for media ID ${mediaId}. Cannot delete.`
6608
- );
6609
- return;
6934
+ let filteredClinics = matchingClinics;
6935
+ if (filters.tags && filters.tags.length > 1) {
6936
+ filteredClinics = filteredClinics.filter((clinic) => {
6937
+ return filters.tags.every((tag) => clinic.tags.includes(tag));
6938
+ });
6610
6939
  }
6611
- const storageFileRef = ref3(this.storage, metadata.path);
6612
- try {
6613
- await deleteObject3(storageFileRef);
6614
- console.log(`[MediaService] File deleted from Storage: ${metadata.path}`);
6615
- const metadataDocRef = doc12(this.db, MEDIA_METADATA_COLLECTION, mediaId);
6616
- await deleteDoc6(metadataDocRef);
6617
- console.log(
6618
- `[MediaService] Metadata deleted from Firestore for ID: ${mediaId}`
6940
+ if (filters.minRating !== void 0) {
6941
+ filteredClinics = filteredClinics.filter(
6942
+ (clinic) => clinic.reviewInfo.averageRating >= filters.minRating
6619
6943
  );
6620
- } catch (error) {
6621
- console.error(`[MediaService] Error deleting media ${mediaId}:`, error);
6622
- throw error;
6623
6944
  }
6624
- }
6625
- /**
6626
- * Update media access level. This involves moving the file in Firebase Storage
6627
- * to a new path reflecting the new access level, and updating its metadata.
6628
- * @param mediaId - ID of the media to update.
6629
- * @param newAccessLevel - New access level.
6630
- * @returns Promise with the updated media metadata, or null if metadata not found.
6631
- */
6632
- async updateMediaAccessLevel(mediaId, newAccessLevel) {
6633
- var _a;
6634
- console.log(
6635
- `[MediaService] Attempting to update access level for media ID: ${mediaId} to ${newAccessLevel}`
6636
- );
6637
- const metadata = await this.getMediaMetadata(mediaId);
6638
- if (!metadata) {
6639
- console.warn(
6640
- `[MediaService] Metadata not found for media ID ${mediaId}. Cannot update access level.`
6945
+ if (filters.maxRating !== void 0) {
6946
+ filteredClinics = filteredClinics.filter(
6947
+ (clinic) => clinic.reviewInfo.averageRating <= filters.maxRating
6641
6948
  );
6642
- return null;
6643
6949
  }
6644
- if (metadata.accessLevel === newAccessLevel) {
6645
- console.log(
6646
- `[MediaService] Media ID ${mediaId} already has access level ${newAccessLevel}. Updating timestamp only.`
6647
- );
6648
- const metadataDocRef = doc12(this.db, MEDIA_METADATA_COLLECTION, mediaId);
6649
- try {
6650
- await updateDoc12(metadataDocRef, { updatedAt: Timestamp13.now() });
6651
- return { ...metadata, updatedAt: Timestamp13.now() };
6652
- } catch (error) {
6653
- console.error(
6654
- `[MediaService] Error updating timestamp for media ID ${mediaId}:`,
6655
- error
6950
+ filteredClinics.sort((a, b) => a.distance - b.distance);
6951
+ if (filters.pagination && filters.pagination > 0) {
6952
+ let startIndex = 0;
6953
+ if (filters.lastDoc) {
6954
+ const lastDocIndex = filteredClinics.findIndex(
6955
+ (clinic) => clinic.id === filters.lastDoc.id
6656
6956
  );
6657
- throw error;
6957
+ if (lastDocIndex !== -1) {
6958
+ startIndex = lastDocIndex + 1;
6959
+ }
6658
6960
  }
6961
+ const paginatedClinics = filteredClinics.slice(
6962
+ startIndex,
6963
+ startIndex + filters.pagination
6964
+ );
6965
+ lastVisibleDoc = paginatedClinics.length > 0 ? paginatedClinics[paginatedClinics.length - 1] : null;
6966
+ clinicsResult = paginatedClinics;
6967
+ } else {
6968
+ clinicsResult = filteredClinics;
6659
6969
  }
6660
- const oldStoragePath = metadata.path;
6661
- const fileNamePart = `${metadata.id}-${metadata.name}`;
6662
- const newStoragePath = `media/${newAccessLevel}/${metadata.ownerId}/${metadata.collectionName}/${fileNamePart}`;
6970
+ } else {
6971
+ const q = query12(collection12(db, CLINICS_COLLECTION), ...constraints);
6972
+ const querySnapshot = await getDocs12(q);
6663
6973
  console.log(
6664
- `[MediaService] Moving file for ${mediaId} from ${oldStoragePath} to ${newStoragePath}`
6974
+ `[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics with regular query`
6665
6975
  );
6666
- const oldStorageFileRef = ref3(this.storage, oldStoragePath);
6667
- const newStorageFileRef = ref3(this.storage, newStoragePath);
6668
- try {
6669
- console.log(`[MediaService] Downloading bytes from ${oldStoragePath}`);
6670
- const fileBytes = await getBytes(oldStorageFileRef);
6671
- console.log(
6672
- `[MediaService] Successfully downloaded ${fileBytes.byteLength} bytes from ${oldStoragePath}`
6673
- );
6674
- console.log(`[MediaService] Uploading bytes to ${newStoragePath}`);
6675
- await uploadBytes3(newStorageFileRef, fileBytes, {
6676
- contentType: metadata.contentType
6677
- });
6678
- console.log(
6679
- `[MediaService] Successfully uploaded bytes to ${newStoragePath}`
6680
- );
6681
- const newDownloadURL = await getDownloadURL3(newStorageFileRef);
6682
- console.log(
6683
- `[MediaService] Got new download URL for ${newStoragePath}: ${newDownloadURL}`
6684
- );
6685
- const updateData = {
6686
- accessLevel: newAccessLevel,
6687
- path: newStoragePath,
6688
- url: newDownloadURL,
6689
- updatedAt: Timestamp13.now()
6690
- };
6691
- const metadataDocRef = doc12(this.db, MEDIA_METADATA_COLLECTION, mediaId);
6692
- console.log(
6693
- `[MediaService] Updating Firestore metadata for ${mediaId} with new data:`,
6694
- updateData
6695
- );
6696
- await updateDoc12(metadataDocRef, updateData);
6697
- console.log(
6698
- `[MediaService] Successfully updated Firestore metadata for ${mediaId}`
6699
- );
6700
- try {
6701
- console.log(`[MediaService] Deleting old file from ${oldStoragePath}`);
6702
- await deleteObject3(oldStorageFileRef);
6703
- console.log(
6704
- `[MediaService] Successfully deleted old file from ${oldStoragePath}`
6705
- );
6706
- } catch (deleteError) {
6707
- console.error(
6708
- `[MediaService] Failed to delete old file from ${oldStoragePath} for media ID ${mediaId}. This file is now orphaned. Error:`,
6709
- deleteError
6976
+ const clinics = querySnapshot.docs.map((doc34) => {
6977
+ return { ...doc34.data(), id: doc34.id };
6978
+ });
6979
+ let filteredClinics = clinics;
6980
+ if (filters.center) {
6981
+ const center = filters.center;
6982
+ const clinicsWithDistance = [];
6983
+ filteredClinics.forEach((clinic) => {
6984
+ const distance = distanceBetween4(
6985
+ [center.latitude, center.longitude],
6986
+ [clinic.location.latitude, clinic.location.longitude]
6710
6987
  );
6711
- }
6712
- return { ...metadata, ...updateData };
6713
- } catch (error) {
6714
- console.error(
6715
- `[MediaService] Error updating media access level and moving file for ${mediaId}:`,
6716
- error
6988
+ clinicsWithDistance.push({
6989
+ ...clinic,
6990
+ distance: distance / 1e3
6991
+ // Convert to kilometers
6992
+ });
6993
+ });
6994
+ filteredClinics = clinicsWithDistance;
6995
+ filteredClinics.sort(
6996
+ (a, b) => a.distance - b.distance
6717
6997
  );
6718
- if (newStorageFileRef && error.code !== "storage/object-not-found" && ((_a = error.message) == null ? void 0 : _a.includes("uploadBytes"))) {
6719
- console.warn(
6720
- `[MediaService] Attempting to delete partially uploaded file at ${newStoragePath} due to error.`
6721
- );
6722
- try {
6723
- await deleteObject3(newStorageFileRef);
6724
- console.warn(
6725
- `[MediaService] Cleaned up partially uploaded file at ${newStoragePath}.`
6726
- );
6727
- } catch (cleanupError) {
6728
- console.error(
6729
- `[MediaService] Failed to cleanup partially uploaded file at ${newStoragePath}:`,
6730
- cleanupError
6731
- );
6732
- }
6733
- }
6734
- throw error;
6735
- }
6736
- }
6737
- /**
6738
- * List all media for an owner, optionally filtered by collection and access level.
6739
- * @param ownerId - ID of the owner.
6740
- * @param collectionName - Optional: Filter by collection name.
6741
- * @param accessLevel - Optional: Filter by access level.
6742
- * @param count - Optional: Number of items to fetch.
6743
- * @param startAfterId - Optional: ID of the document to start after (for pagination).
6744
- */
6745
- async listMedia(ownerId, collectionName, accessLevel, count, startAfterId) {
6746
- console.log(`[MediaService] Listing media for owner: ${ownerId}`);
6747
- let qConstraints = [where12("ownerId", "==", ownerId)];
6748
- if (collectionName) {
6749
- qConstraints.push(where12("collectionName", "==", collectionName));
6750
- }
6751
- if (accessLevel) {
6752
- qConstraints.push(where12("accessLevel", "==", accessLevel));
6753
6998
  }
6754
- qConstraints.push(orderBy3("createdAt", "desc"));
6755
- if (count) {
6756
- qConstraints.push(limit7(count));
6757
- }
6758
- if (startAfterId) {
6759
- const startAfterDoc = await this.getMediaMetadata(startAfterId);
6760
- if (startAfterDoc) {
6761
- }
6999
+ if (filters.tags && filters.tags.length > 1) {
7000
+ filteredClinics = filteredClinics.filter((clinic) => {
7001
+ return filters.tags.every((tag) => clinic.tags.includes(tag));
7002
+ });
6762
7003
  }
6763
- const finalQuery = query12(
6764
- collection12(this.db, MEDIA_METADATA_COLLECTION),
6765
- ...qConstraints
6766
- );
6767
- try {
6768
- const querySnapshot = await getDocs12(finalQuery);
6769
- const mediaList = querySnapshot.docs.map(
6770
- (doc34) => doc34.data()
7004
+ if (filters.minRating !== void 0) {
7005
+ filteredClinics = filteredClinics.filter(
7006
+ (clinic) => clinic.reviewInfo.averageRating >= filters.minRating
6771
7007
  );
6772
- console.log(`[MediaService] Found ${mediaList.length} media items.`);
6773
- return mediaList;
6774
- } catch (error) {
6775
- console.error("[MediaService] Error listing media:", error);
6776
- throw error;
6777
7008
  }
6778
- }
6779
- /**
6780
- * Get download URL for media. (Convenience, as URL is in metadata)
6781
- * @param mediaId - ID of the media.
6782
- */
6783
- async getMediaDownloadUrl(mediaId) {
6784
- console.log(`[MediaService] Getting download URL for media ID: ${mediaId}`);
6785
- const metadata = await this.getMediaMetadata(mediaId);
6786
- if (metadata && metadata.url) {
6787
- console.log(`[MediaService] URL found: ${metadata.url}`);
6788
- return metadata.url;
7009
+ if (filters.maxRating !== void 0) {
7010
+ filteredClinics = filteredClinics.filter(
7011
+ (clinic) => clinic.reviewInfo.averageRating <= filters.maxRating
7012
+ );
6789
7013
  }
6790
- console.log(`[MediaService] URL not found for media ID: ${mediaId}`);
6791
- return null;
7014
+ lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
7015
+ clinicsResult = filteredClinics;
6792
7016
  }
6793
- };
7017
+ return {
7018
+ clinics: clinicsResult,
7019
+ lastDoc: lastVisibleDoc
7020
+ };
7021
+ }
6794
7022
 
6795
7023
  // src/services/clinic/clinic.service.ts
6796
7024
  var ClinicService = class extends BaseService {
@@ -8522,7 +8750,8 @@ var ProcedureService = class extends BaseService {
8522
8750
  id: practitionerSnapshot.id,
8523
8751
  name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
8524
8752
  description: practitioner.basicInfo.bio || "",
8525
- photo: practitioner.basicInfo.profileImageUrl || "",
8753
+ photo: typeof practitioner.basicInfo.profileImageUrl === "string" ? practitioner.basicInfo.profileImageUrl : "",
8754
+ // Default to empty string if not a processed URL
8526
8755
  rating: ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
8527
8756
  services: practitioner.procedures || []
8528
8757
  };
@@ -8656,7 +8885,8 @@ var ProcedureService = class extends BaseService {
8656
8885
  id: newPractitioner.id,
8657
8886
  name: `${newPractitioner.basicInfo.firstName} ${newPractitioner.basicInfo.lastName}`,
8658
8887
  description: newPractitioner.basicInfo.bio || "",
8659
- photo: newPractitioner.basicInfo.profileImageUrl || "",
8888
+ photo: typeof newPractitioner.basicInfo.profileImageUrl === "string" ? newPractitioner.basicInfo.profileImageUrl : "",
8889
+ // Default to empty string if not a processed URL
8660
8890
  rating: ((_a = newPractitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
8661
8891
  services: newPractitioner.procedures || []
8662
8892
  };