@blackcode_sa/metaestetics-api 1.11.3 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/admin/index.d.mts +378 -334
  2. package/dist/admin/index.d.ts +378 -334
  3. package/dist/backoffice/index.d.mts +1198 -430
  4. package/dist/backoffice/index.d.ts +1198 -430
  5. package/dist/backoffice/index.js +1128 -245
  6. package/dist/backoffice/index.mjs +1119 -209
  7. package/dist/index.d.mts +4478 -4031
  8. package/dist/index.d.ts +4478 -4031
  9. package/dist/index.js +1974 -757
  10. package/dist/index.mjs +1735 -490
  11. package/package.json +1 -1
  12. package/src/backoffice/expo-safe/index.ts +4 -0
  13. package/src/backoffice/services/README.md +40 -0
  14. package/src/backoffice/services/brand.service.ts +85 -6
  15. package/src/backoffice/services/category.service.ts +92 -10
  16. package/src/backoffice/services/constants.service.ts +308 -0
  17. package/src/backoffice/services/documentation-template.service.ts +56 -2
  18. package/src/backoffice/services/index.ts +1 -0
  19. package/src/backoffice/services/product.service.ts +126 -5
  20. package/src/backoffice/services/requirement.service.ts +13 -0
  21. package/src/backoffice/services/subcategory.service.ts +184 -13
  22. package/src/backoffice/services/technology.service.ts +344 -129
  23. package/src/backoffice/types/admin-constants.types.ts +69 -0
  24. package/src/backoffice/types/brand.types.ts +1 -0
  25. package/src/backoffice/types/index.ts +2 -0
  26. package/src/backoffice/types/procedure-product.types.ts +38 -0
  27. package/src/backoffice/types/product.types.ts +31 -4
  28. package/src/backoffice/types/static/contraindication.types.ts +1 -0
  29. package/src/backoffice/types/static/treatment-benefit.types.ts +1 -0
  30. package/src/backoffice/types/technology.types.ts +113 -4
  31. package/src/backoffice/validations/schemas.ts +35 -9
  32. package/src/services/appointment/appointment.service.ts +0 -5
  33. package/src/services/appointment/utils/appointment.utils.ts +124 -113
  34. package/src/services/base.service.ts +10 -3
  35. package/src/services/documentation-templates/documentation-template.service.ts +116 -0
  36. package/src/services/media/media.service.ts +2 -2
  37. package/src/services/practitioner/practitioner.service.ts +201 -83
  38. package/src/services/procedure/README.md +76 -1
  39. package/src/services/procedure/procedure.service.ts +538 -235
  40. package/src/types/appointment/index.ts +2 -3
  41. package/src/types/clinic/index.ts +1 -6
  42. package/src/types/patient/medical-info.types.ts +3 -3
  43. package/src/types/procedure/index.ts +39 -20
  44. package/src/validations/clinic.schema.ts +1 -6
  45. package/src/validations/patient/medical-info.schema.ts +7 -2
  46. package/src/validations/procedure-product.schema.ts +41 -0
  47. package/src/validations/procedure.schema.ts +59 -8
  48. package/src/backoffice/services/__tests__/brand.service.test.ts +0 -196
  49. package/src/backoffice/services/__tests__/category.service.test.ts +0 -201
  50. package/src/backoffice/services/__tests__/product.service.test.ts +0 -358
  51. package/src/backoffice/services/__tests__/requirement.service.test.ts +0 -226
  52. package/src/backoffice/services/__tests__/subcategory.service.test.ts +0 -181
  53. package/src/backoffice/services/__tests__/technology.service.test.ts +0 -1097
package/dist/index.mjs CHANGED
@@ -17,11 +17,13 @@ import { getFunctions } from "firebase/functions";
17
17
  // src/services/base.service.ts
18
18
  import { getStorage } from "firebase/storage";
19
19
  var BaseService = class {
20
- constructor(db, auth, app) {
20
+ constructor(db, auth, app, storage) {
21
21
  this.db = db;
22
22
  this.auth = auth;
23
23
  this.app = app;
24
- this.storage = getStorage(app);
24
+ if (app) {
25
+ this.storage = storage || getStorage(app);
26
+ }
25
27
  }
26
28
  /**
27
29
  * Generiše jedinstveni ID za dokumente
@@ -53,13 +55,13 @@ var AppointmentStatus = /* @__PURE__ */ ((AppointmentStatus2) => {
53
55
  AppointmentStatus2["RESCHEDULED_BY_CLINIC"] = "rescheduled_by_clinic";
54
56
  return AppointmentStatus2;
55
57
  })(AppointmentStatus || {});
56
- var PaymentStatus = /* @__PURE__ */ ((PaymentStatus3) => {
57
- PaymentStatus3["UNPAID"] = "unpaid";
58
- PaymentStatus3["PAID"] = "paid";
59
- PaymentStatus3["PARTIALLY_PAID"] = "partially_paid";
60
- PaymentStatus3["REFUNDED"] = "refunded";
61
- PaymentStatus3["NOT_APPLICABLE"] = "not_applicable";
62
- return PaymentStatus3;
58
+ var PaymentStatus = /* @__PURE__ */ ((PaymentStatus4) => {
59
+ PaymentStatus4["UNPAID"] = "unpaid";
60
+ PaymentStatus4["PAID"] = "paid";
61
+ PaymentStatus4["PARTIALLY_PAID"] = "partially_paid";
62
+ PaymentStatus4["REFUNDED"] = "refunded";
63
+ PaymentStatus4["NOT_APPLICABLE"] = "not_applicable";
64
+ return PaymentStatus4;
63
65
  })(PaymentStatus || {});
64
66
  var MediaType = /* @__PURE__ */ ((MediaType2) => {
65
67
  MediaType2["BEFORE_PHOTO"] = "before_photo";
@@ -582,7 +584,6 @@ import {
582
584
  getDocs,
583
585
  query,
584
586
  where,
585
- setDoc,
586
587
  updateDoc,
587
588
  serverTimestamp,
588
589
  Timestamp,
@@ -819,44 +820,48 @@ async function updateAppointmentUtil(db, appointmentId, data) {
819
820
  const validPreReqIds = currentAppointment.preProcedureRequirements.map(
820
821
  (req) => req.id
821
822
  );
822
- const invalidPreReqIds = data.completedPreRequirements.filter(
823
- (id) => !validPreReqIds.includes(id)
824
- );
825
- if (invalidPreReqIds.length > 0) {
826
- throw new Error(
827
- `Invalid pre-requirement IDs: ${invalidPreReqIds.join(", ")}`
823
+ if (Array.isArray(data.completedPreRequirements)) {
824
+ const invalidPreReqIds = data.completedPreRequirements.filter(
825
+ (id) => !validPreReqIds.includes(id)
828
826
  );
827
+ if (invalidPreReqIds.length > 0) {
828
+ throw new Error(
829
+ `Invalid pre-requirement IDs: ${invalidPreReqIds.join(", ")}`
830
+ );
831
+ }
832
+ completedPreRequirements = [
833
+ .../* @__PURE__ */ new Set([
834
+ ...completedPreRequirements,
835
+ ...data.completedPreRequirements
836
+ ])
837
+ ];
829
838
  }
830
- completedPreRequirements = [
831
- .../* @__PURE__ */ new Set([
832
- ...completedPreRequirements,
833
- ...data.completedPreRequirements
834
- ])
835
- ];
836
839
  }
837
840
  if (data.completedPostRequirements) {
838
841
  const validPostReqIds = currentAppointment.postProcedureRequirements.map(
839
842
  (req) => req.id
840
843
  );
841
- const invalidPostReqIds = data.completedPostRequirements.filter(
842
- (id) => !validPostReqIds.includes(id)
843
- );
844
- if (invalidPostReqIds.length > 0) {
845
- throw new Error(
846
- `Invalid post-requirement IDs: ${invalidPostReqIds.join(", ")}`
844
+ if (Array.isArray(data.completedPostRequirements)) {
845
+ const invalidPostReqIds = data.completedPostRequirements.filter(
846
+ (id) => !validPostReqIds.includes(id)
847
847
  );
848
+ if (invalidPostReqIds.length > 0) {
849
+ throw new Error(
850
+ `Invalid post-requirement IDs: ${invalidPostReqIds.join(", ")}`
851
+ );
852
+ }
853
+ completedPostRequirements = [
854
+ .../* @__PURE__ */ new Set([
855
+ ...completedPostRequirements,
856
+ ...data.completedPostRequirements
857
+ ])
858
+ ];
848
859
  }
849
- completedPostRequirements = [
850
- .../* @__PURE__ */ new Set([
851
- ...completedPostRequirements,
852
- ...data.completedPostRequirements
853
- ])
854
- ];
855
860
  }
856
861
  const updateData = {
857
862
  ...data,
858
- completedPreRequirements,
859
- completedPostRequirements,
863
+ completedPreRequirements: Array.isArray(data.completedPreRequirements) ? completedPreRequirements : data.completedPreRequirements,
864
+ completedPostRequirements: Array.isArray(data.completedPostRequirements) ? completedPostRequirements : data.completedPostRequirements,
860
865
  updatedAt: serverTimestamp()
861
866
  };
862
867
  Object.keys(updateData).forEach((key) => {
@@ -906,7 +911,7 @@ async function updateCalendarEventStatus(db, calendarEventId, appointmentStatus)
906
911
  case "canceled_clinic" /* CANCELED_CLINIC */:
907
912
  calendarStatus = "canceled";
908
913
  break;
909
- case AppointmentStatus.RESCHEDULED:
914
+ case "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */:
910
915
  calendarStatus = "rescheduled";
911
916
  break;
912
917
  case "completed" /* COMPLETED */:
@@ -980,7 +985,7 @@ async function searchAppointmentsUtil(db, params) {
980
985
  const q = query(collection(db, APPOINTMENTS_COLLECTION), ...constraints);
981
986
  const querySnapshot = await getDocs(q);
982
987
  const appointments = querySnapshot.docs.map(
983
- (doc37) => doc37.data()
988
+ (doc38) => doc38.data()
984
989
  );
985
990
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
986
991
  return { appointments, lastDoc };
@@ -1787,7 +1792,7 @@ var AppointmentService = class extends BaseService {
1787
1792
  );
1788
1793
  const querySnapshot = await getDocs2(q);
1789
1794
  const appointments = querySnapshot.docs.map(
1790
- (doc37) => doc37.data()
1795
+ (doc38) => doc38.data()
1791
1796
  );
1792
1797
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
1793
1798
  console.log(
@@ -1860,7 +1865,7 @@ var AppointmentService = class extends BaseService {
1860
1865
  );
1861
1866
  const querySnapshot = await getDocs2(q);
1862
1867
  const appointments = querySnapshot.docs.map(
1863
- (doc37) => doc37.data()
1868
+ (doc38) => doc38.data()
1864
1869
  );
1865
1870
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
1866
1871
  console.log(
@@ -2412,8 +2417,8 @@ var MediaAccessLevel = /* @__PURE__ */ ((MediaAccessLevel2) => {
2412
2417
  })(MediaAccessLevel || {});
2413
2418
  var MEDIA_METADATA_COLLECTION = "media_metadata";
2414
2419
  var MediaService = class extends BaseService {
2415
- constructor(db, auth, app) {
2416
- super(db, auth, app);
2420
+ constructor(...args) {
2421
+ super(...args);
2417
2422
  }
2418
2423
  /**
2419
2424
  * Upload a media file, store its metadata, and return the metadata including the URL.
@@ -2673,7 +2678,7 @@ var MediaService = class extends BaseService {
2673
2678
  try {
2674
2679
  const querySnapshot = await getDocs3(finalQuery);
2675
2680
  const mediaList = querySnapshot.docs.map(
2676
- (doc37) => doc37.data()
2681
+ (doc38) => doc38.data()
2677
2682
  );
2678
2683
  console.log(`[MediaService] Found ${mediaList.length} media items.`);
2679
2684
  return mediaList;
@@ -2736,8 +2741,8 @@ var getPatientsByClinicUtil = async (db, clinicId, options) => {
2736
2741
  }
2737
2742
  const patientsSnapshot = await getDocs4(q);
2738
2743
  const patients = [];
2739
- patientsSnapshot.forEach((doc37) => {
2740
- patients.push(doc37.data());
2744
+ patientsSnapshot.forEach((doc38) => {
2745
+ patients.push(doc38.data());
2741
2746
  });
2742
2747
  console.log(
2743
2748
  `[getPatientsByClinicUtil] Found ${patients.length} patients for clinic ID: ${clinicId}`
@@ -2799,23 +2804,6 @@ var BlockingCondition = /* @__PURE__ */ ((BlockingCondition2) => {
2799
2804
  return BlockingCondition2;
2800
2805
  })(BlockingCondition || {});
2801
2806
 
2802
- // src/backoffice/types/static/contraindication.types.ts
2803
- var Contraindication = /* @__PURE__ */ ((Contraindication2) => {
2804
- Contraindication2["SENSITIVE_SKIN"] = "sensitive_skin";
2805
- Contraindication2["RECENT_TANNING"] = "recent_tanning";
2806
- Contraindication2["RECENT_BOTOX"] = "recent_botox";
2807
- Contraindication2["RECENT_FILLERS"] = "recent_fillers";
2808
- Contraindication2["SKIN_ALLERGIES"] = "skin_allergies";
2809
- Contraindication2["MEDICATIONS"] = "medications";
2810
- Contraindication2["RECENT_CHEMICAL_PEEL"] = "recent_chemical_peel";
2811
- Contraindication2["RECENT_LASER"] = "recent_laser";
2812
- Contraindication2["SKIN_INFLAMMATION"] = "skin_inflammation";
2813
- Contraindication2["OPEN_WOUNDS"] = "open_wounds";
2814
- Contraindication2["HERPES_SIMPLEX"] = "herpes_simplex";
2815
- Contraindication2["COLD_SORES"] = "cold_sores";
2816
- return Contraindication2;
2817
- })(Contraindication || {});
2818
-
2819
2807
  // src/validations/common.schema.ts
2820
2808
  import { z as z5 } from "zod";
2821
2809
  import { Timestamp as Timestamp4 } from "firebase/firestore";
@@ -2870,8 +2858,13 @@ var blockingConditionSchema = z6.object({
2870
2858
  notes: z6.string().optional().nullable(),
2871
2859
  isActive: z6.boolean()
2872
2860
  });
2861
+ var contraindicationDynamicSchema = z6.object({
2862
+ id: z6.string(),
2863
+ name: z6.string(),
2864
+ description: z6.string().optional()
2865
+ });
2873
2866
  var contraindicationSchema = z6.object({
2874
- condition: z6.nativeEnum(Contraindication),
2867
+ condition: contraindicationDynamicSchema,
2875
2868
  lastOccurrence: timestampSchema,
2876
2869
  frequency: z6.enum(["rare", "occasional", "frequent"]),
2877
2870
  notes: z6.string().optional().nullable(),
@@ -3111,8 +3104,8 @@ var getPatientsByPractitionerUtil = async (db, practitionerId, options) => {
3111
3104
  }
3112
3105
  const patientsSnapshot = await getDocs5(q);
3113
3106
  const patients = [];
3114
- patientsSnapshot.forEach((doc37) => {
3115
- patients.push(doc37.data());
3107
+ patientsSnapshot.forEach((doc38) => {
3108
+ patients.push(doc38.data());
3116
3109
  });
3117
3110
  console.log(
3118
3111
  `[getPatientsByPractitionerUtil] Found ${patients.length} patients for practitioner ID: ${practitionerId}`
@@ -3891,7 +3884,7 @@ async function getClinicAdminsByGroup(db, clinicGroupId) {
3891
3884
  where6("clinicGroupId", "==", clinicGroupId)
3892
3885
  );
3893
3886
  const querySnapshot = await getDocs6(q);
3894
- return querySnapshot.docs.map((doc37) => doc37.data());
3887
+ return querySnapshot.docs.map((doc38) => doc38.data());
3895
3888
  }
3896
3889
  async function updateClinicAdmin(db, adminId, data) {
3897
3890
  const admin = await getClinicAdmin(db, adminId);
@@ -4572,9 +4565,9 @@ var updateAllergyUtil = async (db, patientId, data, requesterId, requesterRoles)
4572
4565
  };
4573
4566
  var removeAllergyUtil = async (db, patientId, allergyIndex, requesterId, requesterRoles) => {
4574
4567
  await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
4575
- const doc37 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4576
- if (!doc37.exists()) throw new Error("Medical info not found");
4577
- const medicalInfo = doc37.data();
4568
+ const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4569
+ if (!doc38.exists()) throw new Error("Medical info not found");
4570
+ const medicalInfo = doc38.data();
4578
4571
  if (allergyIndex >= medicalInfo.allergies.length) {
4579
4572
  throw new Error("Invalid allergy index");
4580
4573
  }
@@ -4601,9 +4594,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, requesterId, reque
4601
4594
  await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
4602
4595
  const validatedData = updateBlockingConditionSchema.parse(data);
4603
4596
  const { conditionIndex, ...updateData } = validatedData;
4604
- const doc37 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4605
- if (!doc37.exists()) throw new Error("Medical info not found");
4606
- const medicalInfo = doc37.data();
4597
+ const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4598
+ if (!doc38.exists()) throw new Error("Medical info not found");
4599
+ const medicalInfo = doc38.data();
4607
4600
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
4608
4601
  throw new Error("Invalid blocking condition index");
4609
4602
  }
@@ -4620,9 +4613,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, requesterId, reque
4620
4613
  };
4621
4614
  var removeBlockingConditionUtil = async (db, patientId, conditionIndex, requesterId, requesterRoles) => {
4622
4615
  await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
4623
- const doc37 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4624
- if (!doc37.exists()) throw new Error("Medical info not found");
4625
- const medicalInfo = doc37.data();
4616
+ const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4617
+ if (!doc38.exists()) throw new Error("Medical info not found");
4618
+ const medicalInfo = doc38.data();
4626
4619
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
4627
4620
  throw new Error("Invalid blocking condition index");
4628
4621
  }
@@ -4649,9 +4642,9 @@ var updateContraindicationUtil = async (db, patientId, data, requesterId, reques
4649
4642
  await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
4650
4643
  const validatedData = updateContraindicationSchema.parse(data);
4651
4644
  const { contraindicationIndex, ...updateData } = validatedData;
4652
- const doc37 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4653
- if (!doc37.exists()) throw new Error("Medical info not found");
4654
- const medicalInfo = doc37.data();
4645
+ const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4646
+ if (!doc38.exists()) throw new Error("Medical info not found");
4647
+ const medicalInfo = doc38.data();
4655
4648
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
4656
4649
  throw new Error("Invalid contraindication index");
4657
4650
  }
@@ -4668,9 +4661,9 @@ var updateContraindicationUtil = async (db, patientId, data, requesterId, reques
4668
4661
  };
4669
4662
  var removeContraindicationUtil = async (db, patientId, contraindicationIndex, requesterId, requesterRoles) => {
4670
4663
  await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
4671
- const doc37 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4672
- if (!doc37.exists()) throw new Error("Medical info not found");
4673
- const medicalInfo = doc37.data();
4664
+ const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4665
+ if (!doc38.exists()) throw new Error("Medical info not found");
4666
+ const medicalInfo = doc38.data();
4674
4667
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
4675
4668
  throw new Error("Invalid contraindication index");
4676
4669
  }
@@ -4697,9 +4690,9 @@ var updateMedicationUtil = async (db, patientId, data, requesterId, requesterRol
4697
4690
  await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
4698
4691
  const validatedData = updateMedicationSchema.parse(data);
4699
4692
  const { medicationIndex, ...updateData } = validatedData;
4700
- const doc37 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4701
- if (!doc37.exists()) throw new Error("Medical info not found");
4702
- const medicalInfo = doc37.data();
4693
+ const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4694
+ if (!doc38.exists()) throw new Error("Medical info not found");
4695
+ const medicalInfo = doc38.data();
4703
4696
  if (medicationIndex >= medicalInfo.currentMedications.length) {
4704
4697
  throw new Error("Invalid medication index");
4705
4698
  }
@@ -4716,9 +4709,9 @@ var updateMedicationUtil = async (db, patientId, data, requesterId, requesterRol
4716
4709
  };
4717
4710
  var removeMedicationUtil = async (db, patientId, medicationIndex, requesterId, requesterRoles) => {
4718
4711
  await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
4719
- const doc37 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4720
- if (!doc37.exists()) throw new Error("Medical info not found");
4721
- const medicalInfo = doc37.data();
4712
+ const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
4713
+ if (!doc38.exists()) throw new Error("Medical info not found");
4714
+ const medicalInfo = doc38.data();
4722
4715
  if (medicationIndex >= medicalInfo.currentMedications.length) {
4723
4716
  throw new Error("Invalid medication index");
4724
4717
  }
@@ -5021,7 +5014,7 @@ var searchPatientsUtil = async (db, params, requester) => {
5021
5014
  const finalQuery = query8(patientsCollectionRef, ...constraints);
5022
5015
  const querySnapshot = await getDocs8(finalQuery);
5023
5016
  const patients = querySnapshot.docs.map(
5024
- (doc37) => doc37.data()
5017
+ (doc38) => doc38.data()
5025
5018
  );
5026
5019
  console.log(
5027
5020
  `[searchPatientsUtil] Found ${patients.length} patients matching criteria.`
@@ -5053,8 +5046,8 @@ var getAllPatientsUtil = async (db, options) => {
5053
5046
  }
5054
5047
  const patientsSnapshot = await getDocs8(q);
5055
5048
  const patients = [];
5056
- patientsSnapshot.forEach((doc37) => {
5057
- patients.push(doc37.data());
5049
+ patientsSnapshot.forEach((doc38) => {
5050
+ patients.push(doc38.data());
5058
5051
  });
5059
5052
  console.log(`[getAllPatientsUtil] Found ${patients.length} patients`);
5060
5053
  return patients;
@@ -5187,7 +5180,7 @@ var getActiveInviteTokensByClinicUtil = async (db, clinicId) => {
5187
5180
  if (querySnapshot.empty) {
5188
5181
  return [];
5189
5182
  }
5190
- return querySnapshot.docs.map((doc37) => doc37.data());
5183
+ return querySnapshot.docs.map((doc38) => doc38.data());
5191
5184
  };
5192
5185
  var getActiveInviteTokensByPatientUtil = async (db, patientId) => {
5193
5186
  const tokensRef = collection9(
@@ -5205,7 +5198,7 @@ var getActiveInviteTokensByPatientUtil = async (db, patientId) => {
5205
5198
  if (querySnapshot.empty) {
5206
5199
  return [];
5207
5200
  }
5208
- return querySnapshot.docs.map((doc37) => doc37.data());
5201
+ return querySnapshot.docs.map((doc38) => doc38.data());
5209
5202
  };
5210
5203
 
5211
5204
  // src/services/patient/patient.service.ts
@@ -6494,7 +6487,7 @@ var PractitionerService = class extends BaseService {
6494
6487
  where10("expiresAt", ">", Timestamp14.now())
6495
6488
  );
6496
6489
  const querySnapshot = await getDocs10(q);
6497
- return querySnapshot.docs.map((doc37) => doc37.data());
6490
+ return querySnapshot.docs.map((doc38) => doc38.data());
6498
6491
  }
6499
6492
  /**
6500
6493
  * Gets a token by its string value and validates it
@@ -6604,7 +6597,7 @@ var PractitionerService = class extends BaseService {
6604
6597
  where10("status", "==", "active" /* ACTIVE */)
6605
6598
  );
6606
6599
  const querySnapshot = await getDocs10(q);
6607
- return querySnapshot.docs.map((doc37) => doc37.data());
6600
+ return querySnapshot.docs.map((doc38) => doc38.data());
6608
6601
  }
6609
6602
  /**
6610
6603
  * Dohvata sve zdravstvene radnike za određenu kliniku
@@ -6616,7 +6609,7 @@ var PractitionerService = class extends BaseService {
6616
6609
  where10("isActive", "==", true)
6617
6610
  );
6618
6611
  const querySnapshot = await getDocs10(q);
6619
- return querySnapshot.docs.map((doc37) => doc37.data());
6612
+ return querySnapshot.docs.map((doc38) => doc38.data());
6620
6613
  }
6621
6614
  /**
6622
6615
  * Dohvata sve draft zdravstvene radnike za određenu kliniku sa statusom DRAFT
@@ -6628,7 +6621,7 @@ var PractitionerService = class extends BaseService {
6628
6621
  where10("status", "==", "draft" /* DRAFT */)
6629
6622
  );
6630
6623
  const querySnapshot = await getDocs10(q);
6631
- return querySnapshot.docs.map((doc37) => doc37.data());
6624
+ return querySnapshot.docs.map((doc38) => doc38.data());
6632
6625
  }
6633
6626
  /**
6634
6627
  * Updates a practitioner
@@ -6646,7 +6639,9 @@ var PractitionerService = class extends BaseService {
6646
6639
  throw new Error(`Practitioner ${practitionerId} not found`);
6647
6640
  }
6648
6641
  const currentPractitioner = practitionerDoc.data();
6649
- let processedData = { ...validData };
6642
+ let processedData = {
6643
+ ...validData
6644
+ };
6650
6645
  if (validData.basicInfo) {
6651
6646
  processedData.basicInfo = await this.processBasicInfo(
6652
6647
  validData.basicInfo,
@@ -6843,7 +6838,7 @@ var PractitionerService = class extends BaseService {
6843
6838
  );
6844
6839
  const querySnapshot = await getDocs10(q);
6845
6840
  const practitioners = querySnapshot.docs.map(
6846
- (doc37) => doc37.data()
6841
+ (doc38) => doc38.data()
6847
6842
  );
6848
6843
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
6849
6844
  return {
@@ -6880,7 +6875,9 @@ var PractitionerService = class extends BaseService {
6880
6875
  */
6881
6876
  async getPractitionersByFilters(filters) {
6882
6877
  try {
6883
- console.log("[PRACTITIONER_SERVICE] Starting practitioner filtering with fallback strategies");
6878
+ console.log(
6879
+ "[PRACTITIONER_SERVICE] Starting practitioner filtering with fallback strategies"
6880
+ );
6884
6881
  if (filters.location && filters.radiusInKm) {
6885
6882
  console.log("[PRACTITIONER_SERVICE] Executing geo query:", {
6886
6883
  location: filters.location,
@@ -6888,14 +6885,19 @@ var PractitionerService = class extends BaseService {
6888
6885
  serviceName: "PractitionerService"
6889
6886
  });
6890
6887
  if (!filters.location.latitude || !filters.location.longitude) {
6891
- console.warn("[PRACTITIONER_SERVICE] Invalid location data:", filters.location);
6888
+ console.warn(
6889
+ "[PRACTITIONER_SERVICE] Invalid location data:",
6890
+ filters.location
6891
+ );
6892
6892
  filters.location = void 0;
6893
6893
  filters.radiusInKm = void 0;
6894
6894
  }
6895
6895
  }
6896
6896
  if (filters.nameSearch && filters.nameSearch.trim()) {
6897
6897
  try {
6898
- console.log("[PRACTITIONER_SERVICE] Strategy 1: Trying fullNameLower search");
6898
+ console.log(
6899
+ "[PRACTITIONER_SERVICE] Strategy 1: Trying fullNameLower search"
6900
+ );
6899
6901
  const searchTerm = filters.nameSearch.trim().toLowerCase();
6900
6902
  const constraints = [];
6901
6903
  if (!filters.includeDraftPractitioners) {
@@ -6915,11 +6917,18 @@ var PractitionerService = class extends BaseService {
6915
6917
  }
6916
6918
  }
6917
6919
  constraints.push(limit7(filters.pagination || 10));
6918
- const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
6920
+ const q = query10(
6921
+ collection10(this.db, PRACTITIONERS_COLLECTION),
6922
+ ...constraints
6923
+ );
6919
6924
  const querySnapshot = await getDocs10(q);
6920
- const practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
6925
+ const practitioners = querySnapshot.docs.map(
6926
+ (doc38) => ({ ...doc38.data(), id: doc38.id })
6927
+ );
6921
6928
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
6922
- console.log(`[PRACTITIONER_SERVICE] Strategy 1 success: ${practitioners.length} practitioners`);
6929
+ console.log(
6930
+ `[PRACTITIONER_SERVICE] Strategy 1 success: ${practitioners.length} practitioners`
6931
+ );
6923
6932
  if (practitioners.length < (filters.pagination || 10)) {
6924
6933
  return { practitioners, lastDoc: null };
6925
6934
  }
@@ -6929,7 +6938,9 @@ var PractitionerService = class extends BaseService {
6929
6938
  }
6930
6939
  }
6931
6940
  try {
6932
- console.log("[PRACTITIONER_SERVICE] Strategy 2: Basic query with createdAt ordering");
6941
+ console.log(
6942
+ "[PRACTITIONER_SERVICE] Strategy 2: Basic query with createdAt ordering"
6943
+ );
6933
6944
  const constraints = [];
6934
6945
  if (!filters.includeDraftPractitioners) {
6935
6946
  constraints.push(where10("status", "==", "active" /* ACTIVE */));
@@ -6938,14 +6949,22 @@ var PractitionerService = class extends BaseService {
6938
6949
  if (filters.certifications && filters.certifications.length > 0) {
6939
6950
  const certificationsToMatch = filters.certifications;
6940
6951
  constraints.push(
6941
- where10("certification.specialties", "array-contains-any", certificationsToMatch)
6952
+ where10(
6953
+ "certification.specialties",
6954
+ "array-contains-any",
6955
+ certificationsToMatch
6956
+ )
6942
6957
  );
6943
6958
  }
6944
6959
  if (filters.minRating !== void 0) {
6945
- constraints.push(where10("reviewInfo.averageRating", ">=", filters.minRating));
6960
+ constraints.push(
6961
+ where10("reviewInfo.averageRating", ">=", filters.minRating)
6962
+ );
6946
6963
  }
6947
6964
  if (filters.maxRating !== void 0) {
6948
- constraints.push(where10("reviewInfo.averageRating", "<=", filters.maxRating));
6965
+ constraints.push(
6966
+ where10("reviewInfo.averageRating", "<=", filters.maxRating)
6967
+ );
6949
6968
  }
6950
6969
  constraints.push(orderBy4("createdAt", "desc"));
6951
6970
  if (filters.location && filters.radiusInKm) {
@@ -6962,9 +6981,14 @@ var PractitionerService = class extends BaseService {
6962
6981
  }
6963
6982
  constraints.push(limit7(filters.pagination || 10));
6964
6983
  }
6965
- const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
6984
+ const q = query10(
6985
+ collection10(this.db, PRACTITIONERS_COLLECTION),
6986
+ ...constraints
6987
+ );
6966
6988
  const querySnapshot = await getDocs10(q);
6967
- let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
6989
+ let practitioners = querySnapshot.docs.map(
6990
+ (doc38) => ({ ...doc38.data(), id: doc38.id })
6991
+ );
6968
6992
  if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
6969
6993
  const location = filters.location;
6970
6994
  const radiusInKm = filters.radiusInKm;
@@ -6983,7 +7007,9 @@ var PractitionerService = class extends BaseService {
6983
7007
  }
6984
7008
  practitioners = this.applyInMemoryFilters(practitioners, filters);
6985
7009
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
6986
- console.log(`[PRACTITIONER_SERVICE] Strategy 2 success: ${practitioners.length} practitioners`);
7010
+ console.log(
7011
+ `[PRACTITIONER_SERVICE] Strategy 2 success: ${practitioners.length} practitioners`
7012
+ );
6987
7013
  if (practitioners.length < (filters.pagination || 10)) {
6988
7014
  return { practitioners, lastDoc: null };
6989
7015
  }
@@ -6992,18 +7018,27 @@ var PractitionerService = class extends BaseService {
6992
7018
  console.log("[PRACTITIONER_SERVICE] Strategy 2 failed:", error);
6993
7019
  }
6994
7020
  try {
6995
- console.log("[PRACTITIONER_SERVICE] Strategy 3: Minimal query fallback");
7021
+ console.log(
7022
+ "[PRACTITIONER_SERVICE] Strategy 3: Minimal query fallback"
7023
+ );
6996
7024
  const constraints = [
6997
7025
  where10("isActive", "==", true),
6998
7026
  orderBy4("createdAt", "desc"),
6999
7027
  limit7(filters.pagination || 10)
7000
7028
  ];
7001
- const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
7029
+ const q = query10(
7030
+ collection10(this.db, PRACTITIONERS_COLLECTION),
7031
+ ...constraints
7032
+ );
7002
7033
  const querySnapshot = await getDocs10(q);
7003
- let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
7034
+ let practitioners = querySnapshot.docs.map(
7035
+ (doc38) => ({ ...doc38.data(), id: doc38.id })
7036
+ );
7004
7037
  practitioners = this.applyInMemoryFilters(practitioners, filters);
7005
7038
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
7006
- console.log(`[PRACTITIONER_SERVICE] Strategy 3 success: ${practitioners.length} practitioners`);
7039
+ console.log(
7040
+ `[PRACTITIONER_SERVICE] Strategy 3 success: ${practitioners.length} practitioners`
7041
+ );
7007
7042
  if (practitioners.length < (filters.pagination || 10)) {
7008
7043
  return { practitioners, lastDoc: null };
7009
7044
  }
@@ -7012,19 +7047,28 @@ var PractitionerService = class extends BaseService {
7012
7047
  console.log("[PRACTITIONER_SERVICE] Strategy 3 failed:", error);
7013
7048
  }
7014
7049
  try {
7015
- console.log("[PRACTITIONER_SERVICE] Strategy 4: Client-side filtering fallback");
7050
+ console.log(
7051
+ "[PRACTITIONER_SERVICE] Strategy 4: Client-side filtering fallback"
7052
+ );
7016
7053
  const constraints = [
7017
7054
  where10("isActive", "==", true),
7018
7055
  where10("status", "==", "active" /* ACTIVE */),
7019
7056
  orderBy4("createdAt", "desc"),
7020
7057
  limit7(filters.pagination || 10)
7021
7058
  ];
7022
- const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
7059
+ const q = query10(
7060
+ collection10(this.db, PRACTITIONERS_COLLECTION),
7061
+ ...constraints
7062
+ );
7023
7063
  const querySnapshot = await getDocs10(q);
7024
- let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
7064
+ let practitioners = querySnapshot.docs.map(
7065
+ (doc38) => ({ ...doc38.data(), id: doc38.id })
7066
+ );
7025
7067
  practitioners = this.applyInMemoryFilters(practitioners, filters);
7026
7068
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
7027
- console.log(`[PRACTITIONER_SERVICE] Strategy 4 success: ${practitioners.length} practitioners`);
7069
+ console.log(
7070
+ `[PRACTITIONER_SERVICE] Strategy 4 success: ${practitioners.length} practitioners`
7071
+ );
7028
7072
  if (practitioners.length < (filters.pagination || 10)) {
7029
7073
  return { practitioners, lastDoc: null };
7030
7074
  }
@@ -7032,10 +7076,15 @@ var PractitionerService = class extends BaseService {
7032
7076
  } catch (error) {
7033
7077
  console.log("[PRACTITIONER_SERVICE] Strategy 4 failed:", error);
7034
7078
  }
7035
- console.log("[PRACTITIONER_SERVICE] All strategies failed, returning empty result");
7079
+ console.log(
7080
+ "[PRACTITIONER_SERVICE] All strategies failed, returning empty result"
7081
+ );
7036
7082
  return { practitioners: [], lastDoc: null };
7037
7083
  } catch (error) {
7038
- console.error("[PRACTITIONER_SERVICE] Error filtering practitioners:", error);
7084
+ console.error(
7085
+ "[PRACTITIONER_SERVICE] Error filtering practitioners:",
7086
+ error
7087
+ );
7039
7088
  return { practitioners: [], lastDoc: null };
7040
7089
  }
7041
7090
  }
@@ -7055,63 +7104,93 @@ var PractitionerService = class extends BaseService {
7055
7104
  const fullNameLower = practitioner.fullNameLower || "";
7056
7105
  return firstName.includes(searchTerm) || lastName.includes(searchTerm) || fullName.includes(searchTerm) || fullNameLower.includes(searchTerm);
7057
7106
  });
7058
- console.log(`[PRACTITIONER_SERVICE] Applied name filter, results: ${filteredPractitioners.length}`);
7107
+ console.log(
7108
+ `[PRACTITIONER_SERVICE] Applied name filter, results: ${filteredPractitioners.length}`
7109
+ );
7059
7110
  }
7060
7111
  if (filters.certifications && filters.certifications.length > 0) {
7061
7112
  const certificationsToMatch = filters.certifications;
7062
7113
  filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7063
7114
  var _a;
7064
7115
  const practitionerCerts = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
7065
- return certificationsToMatch.some((cert) => practitionerCerts.includes(cert));
7116
+ return certificationsToMatch.some(
7117
+ (cert) => practitionerCerts.includes(cert)
7118
+ );
7066
7119
  });
7067
- console.log(`[PRACTITIONER_SERVICE] Applied certifications filter, results: ${filteredPractitioners.length}`);
7120
+ console.log(
7121
+ `[PRACTITIONER_SERVICE] Applied certifications filter, results: ${filteredPractitioners.length}`
7122
+ );
7068
7123
  }
7069
7124
  if (filters.specialties && filters.specialties.length > 0) {
7070
7125
  const specialtiesToMatch = filters.specialties;
7071
7126
  filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7072
7127
  var _a;
7073
7128
  const practitionerSpecs = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
7074
- return specialtiesToMatch.some((spec) => practitionerSpecs.includes(spec));
7129
+ return specialtiesToMatch.some(
7130
+ (spec) => practitionerSpecs.includes(spec)
7131
+ );
7075
7132
  });
7076
- console.log(`[PRACTITIONER_SERVICE] Applied specialties filter, results: ${filteredPractitioners.length}`);
7133
+ console.log(
7134
+ `[PRACTITIONER_SERVICE] Applied specialties filter, results: ${filteredPractitioners.length}`
7135
+ );
7077
7136
  }
7078
7137
  if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
7079
7138
  filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7080
7139
  var _a;
7081
7140
  const rating = ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
7082
- if (filters.minRating !== void 0 && rating < filters.minRating) return false;
7083
- if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
7141
+ if (filters.minRating !== void 0 && rating < filters.minRating)
7142
+ return false;
7143
+ if (filters.maxRating !== void 0 && rating > filters.maxRating)
7144
+ return false;
7084
7145
  return true;
7085
7146
  });
7086
- console.log(`[PRACTITIONER_SERVICE] Applied rating filter, results: ${filteredPractitioners.length}`);
7147
+ console.log(
7148
+ `[PRACTITIONER_SERVICE] Applied rating filter, results: ${filteredPractitioners.length}`
7149
+ );
7087
7150
  }
7088
7151
  if (filters.procedureFamily) {
7089
7152
  filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7090
7153
  const proceduresInfo = practitioner.proceduresInfo || [];
7091
- return proceduresInfo.some((proc) => proc.family === filters.procedureFamily);
7154
+ return proceduresInfo.some(
7155
+ (proc) => proc.family === filters.procedureFamily
7156
+ );
7092
7157
  });
7093
- console.log(`[PRACTITIONER_SERVICE] Applied procedure family filter, results: ${filteredPractitioners.length}`);
7158
+ console.log(
7159
+ `[PRACTITIONER_SERVICE] Applied procedure family filter, results: ${filteredPractitioners.length}`
7160
+ );
7094
7161
  }
7095
7162
  if (filters.procedureCategory) {
7096
7163
  filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7097
7164
  const proceduresInfo = practitioner.proceduresInfo || [];
7098
- return proceduresInfo.some((proc) => proc.categoryName === filters.procedureCategory);
7165
+ return proceduresInfo.some(
7166
+ (proc) => proc.categoryName === filters.procedureCategory
7167
+ );
7099
7168
  });
7100
- console.log(`[PRACTITIONER_SERVICE] Applied procedure category filter, results: ${filteredPractitioners.length}`);
7169
+ console.log(
7170
+ `[PRACTITIONER_SERVICE] Applied procedure category filter, results: ${filteredPractitioners.length}`
7171
+ );
7101
7172
  }
7102
7173
  if (filters.procedureSubcategory) {
7103
7174
  filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7104
7175
  const proceduresInfo = practitioner.proceduresInfo || [];
7105
- return proceduresInfo.some((proc) => proc.subcategoryName === filters.procedureSubcategory);
7176
+ return proceduresInfo.some(
7177
+ (proc) => proc.subcategoryName === filters.procedureSubcategory
7178
+ );
7106
7179
  });
7107
- console.log(`[PRACTITIONER_SERVICE] Applied procedure subcategory filter, results: ${filteredPractitioners.length}`);
7180
+ console.log(
7181
+ `[PRACTITIONER_SERVICE] Applied procedure subcategory filter, results: ${filteredPractitioners.length}`
7182
+ );
7108
7183
  }
7109
7184
  if (filters.procedureTechnology) {
7110
7185
  filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7111
7186
  const proceduresInfo = practitioner.proceduresInfo || [];
7112
- return proceduresInfo.some((proc) => proc.technologyName === filters.procedureTechnology);
7187
+ return proceduresInfo.some(
7188
+ (proc) => proc.technologyName === filters.procedureTechnology
7189
+ );
7113
7190
  });
7114
- console.log(`[PRACTITIONER_SERVICE] Applied procedure technology filter, results: ${filteredPractitioners.length}`);
7191
+ console.log(
7192
+ `[PRACTITIONER_SERVICE] Applied procedure technology filter, results: ${filteredPractitioners.length}`
7193
+ );
7115
7194
  }
7116
7195
  if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
7117
7196
  const location = filters.location;
@@ -7127,7 +7206,9 @@ var PractitionerService = class extends BaseService {
7127
7206
  return distanceInKm <= radiusInKm;
7128
7207
  });
7129
7208
  });
7130
- console.log(`[PRACTITIONER_SERVICE] Applied geo filter, results: ${filteredPractitioners.length}`);
7209
+ console.log(
7210
+ `[PRACTITIONER_SERVICE] Applied geo filter, results: ${filteredPractitioners.length}`
7211
+ );
7131
7212
  }
7132
7213
  return filteredPractitioners;
7133
7214
  }
@@ -7192,6 +7273,15 @@ var PractitionerService = class extends BaseService {
7192
7273
  price: 0,
7193
7274
  currency: "EUR" /* EUR */,
7194
7275
  pricingMeasure: "per_session" /* PER_SESSION */,
7276
+ productsMetadata: [
7277
+ {
7278
+ productId: "free-consultation-product",
7279
+ price: 0,
7280
+ currency: "EUR" /* EUR */,
7281
+ pricingMeasure: "per_session" /* PER_SESSION */,
7282
+ isDefault: true
7283
+ }
7284
+ ],
7195
7285
  duration: 30,
7196
7286
  // 30 minutes consultation
7197
7287
  practitionerId,
@@ -7536,7 +7626,7 @@ var UserService = class extends BaseService {
7536
7626
  ];
7537
7627
  const q = query11(collection11(this.db, USERS_COLLECTION), ...constraints);
7538
7628
  const querySnapshot = await getDocs11(q);
7539
- const users = querySnapshot.docs.map((doc37) => doc37.data());
7629
+ const users = querySnapshot.docs.map((doc38) => doc38.data());
7540
7630
  return users.map((userData) => userSchema.parse(userData));
7541
7631
  }
7542
7632
  /**
@@ -7916,7 +8006,7 @@ async function getAllActiveGroups(db) {
7916
8006
  where12("isActive", "==", true)
7917
8007
  );
7918
8008
  const querySnapshot = await getDocs12(q);
7919
- return querySnapshot.docs.map((doc37) => doc37.data());
8009
+ return querySnapshot.docs.map((doc38) => doc38.data());
7920
8010
  }
7921
8011
  async function updateClinicGroup(db, groupId, data, app) {
7922
8012
  console.log("[CLINIC_GROUP] Updating clinic group", { groupId });
@@ -8384,7 +8474,7 @@ async function getClinicsByGroup(db, groupId) {
8384
8474
  where13("isActive", "==", true)
8385
8475
  );
8386
8476
  const querySnapshot = await getDocs13(q);
8387
- return querySnapshot.docs.map((doc37) => doc37.data());
8477
+ return querySnapshot.docs.map((doc38) => doc38.data());
8388
8478
  }
8389
8479
  async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app) {
8390
8480
  console.log("[CLINIC] Starting clinic update", { clinicId, adminId });
@@ -8578,7 +8668,7 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
8578
8668
  }
8579
8669
  const q = query13(collection13(db, CLINICS_COLLECTION), ...constraints);
8580
8670
  const querySnapshot = await getDocs13(q);
8581
- return querySnapshot.docs.map((doc37) => doc37.data());
8671
+ return querySnapshot.docs.map((doc38) => doc38.data());
8582
8672
  }
8583
8673
  async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
8584
8674
  return getClinicsByAdmin(
@@ -8623,11 +8713,11 @@ async function getAllClinics(db, pagination, lastDoc) {
8623
8713
  }
8624
8714
  const clinicsSnapshot = await getDocs13(clinicsQuery);
8625
8715
  const lastVisible = clinicsSnapshot.docs[clinicsSnapshot.docs.length - 1];
8626
- const clinics = clinicsSnapshot.docs.map((doc37) => {
8627
- const data = doc37.data();
8716
+ const clinics = clinicsSnapshot.docs.map((doc38) => {
8717
+ const data = doc38.data();
8628
8718
  return {
8629
8719
  ...data,
8630
- id: doc37.id
8720
+ id: doc38.id
8631
8721
  };
8632
8722
  });
8633
8723
  return {
@@ -8654,8 +8744,8 @@ async function getAllClinicsInRange(db, center, rangeInKm, pagination, lastDoc)
8654
8744
  ];
8655
8745
  const q = query13(collection13(db, CLINICS_COLLECTION), ...constraints);
8656
8746
  const querySnapshot = await getDocs13(q);
8657
- for (const doc37 of querySnapshot.docs) {
8658
- const clinic = doc37.data();
8747
+ for (const doc38 of querySnapshot.docs) {
8748
+ const clinic = doc38.data();
8659
8749
  const distance = distanceBetween2(
8660
8750
  [center.latitude, center.longitude],
8661
8751
  [clinic.location.latitude, clinic.location.longitude]
@@ -8777,8 +8867,8 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
8777
8867
  }
8778
8868
  const q = query14(collection14(db, CLINICS_COLLECTION), ...constraints);
8779
8869
  const querySnapshot = await getDocs14(q);
8780
- for (const doc37 of querySnapshot.docs) {
8781
- const clinic = doc37.data();
8870
+ for (const doc38 of querySnapshot.docs) {
8871
+ const clinic = doc38.data();
8782
8872
  const distance = distanceBetween3(
8783
8873
  [center.latitude, center.longitude],
8784
8874
  [clinic.location.latitude, clinic.location.longitude]
@@ -8907,7 +8997,7 @@ async function getClinicsByFilters(db, filters) {
8907
8997
  constraints.push(limit9(filters.pagination || 5));
8908
8998
  const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8909
8999
  const querySnapshot = await getDocs15(q);
8910
- let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
9000
+ let clinics = querySnapshot.docs.map((doc38) => ({ ...doc38.data(), id: doc38.id }));
8911
9001
  clinics = applyInMemoryFilters(clinics, filters);
8912
9002
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8913
9003
  console.log(`[CLINIC_SERVICE] Strategy 1 success: ${clinics.length} clinics`);
@@ -8939,7 +9029,7 @@ async function getClinicsByFilters(db, filters) {
8939
9029
  constraints.push(limit9(filters.pagination || 5));
8940
9030
  const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8941
9031
  const querySnapshot = await getDocs15(q);
8942
- let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
9032
+ let clinics = querySnapshot.docs.map((doc38) => ({ ...doc38.data(), id: doc38.id }));
8943
9033
  clinics = applyInMemoryFilters(clinics, filters);
8944
9034
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8945
9035
  console.log(`[CLINIC_SERVICE] Strategy 2 success: ${clinics.length} clinics`);
@@ -8969,7 +9059,7 @@ async function getClinicsByFilters(db, filters) {
8969
9059
  constraints.push(limit9(filters.pagination || 5));
8970
9060
  const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8971
9061
  const querySnapshot = await getDocs15(q);
8972
- let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
9062
+ let clinics = querySnapshot.docs.map((doc38) => ({ ...doc38.data(), id: doc38.id }));
8973
9063
  clinics = applyInMemoryFilters(clinics, filters);
8974
9064
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8975
9065
  console.log(`[CLINIC_SERVICE] Strategy 3 success: ${clinics.length} clinics`);
@@ -8989,7 +9079,7 @@ async function getClinicsByFilters(db, filters) {
8989
9079
  ];
8990
9080
  const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8991
9081
  const querySnapshot = await getDocs15(q);
8992
- let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
9082
+ let clinics = querySnapshot.docs.map((doc38) => ({ ...doc38.data(), id: doc38.id }));
8993
9083
  clinics = applyInMemoryFilters(clinics, filters);
8994
9084
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8995
9085
  console.log(`[CLINIC_SERVICE] Strategy 4 success: ${clinics.length} clinics`);
@@ -9565,11 +9655,11 @@ var ClinicService = class extends BaseService {
9565
9655
  async getClinicsForMap() {
9566
9656
  const clinicsRef = collection16(this.db, CLINICS_COLLECTION);
9567
9657
  const snapshot = await getDocs16(clinicsRef);
9568
- const clinicsForMap = snapshot.docs.map((doc37) => {
9658
+ const clinicsForMap = snapshot.docs.map((doc38) => {
9569
9659
  var _a, _b, _c;
9570
- const data = doc37.data();
9660
+ const data = doc38.data();
9571
9661
  return {
9572
- id: doc37.id,
9662
+ id: doc38.id,
9573
9663
  name: data.name,
9574
9664
  address: ((_a = data.location) == null ? void 0 : _a.address) || "",
9575
9665
  latitude: (_b = data.location) == null ? void 0 : _b.latitude,
@@ -10776,7 +10866,7 @@ async function updatePractitionerCalendarEventUtil(db, practitionerId, eventId,
10776
10866
  }
10777
10867
 
10778
10868
  // src/services/calendar/utils/appointment.utils.ts
10779
- async function createAppointmentUtil2(db, clinicId, practitionerId, patientId, eventData, generateId2) {
10869
+ async function createAppointmentUtil(db, clinicId, practitionerId, patientId, eventData, generateId2) {
10780
10870
  const eventId = generateId2();
10781
10871
  const autoConfirm = await checkAutoConfirmAppointmentsUtil(db, clinicId);
10782
10872
  const initialStatus = autoConfirm ? "confirmed" /* CONFIRMED */ : "pending" /* PENDING */;
@@ -10923,7 +11013,7 @@ async function searchCalendarEventsUtil(db, params) {
10923
11013
  const finalQuery = query21(collectionRef, ...constraints);
10924
11014
  const querySnapshot = await getDocs21(finalQuery);
10925
11015
  const events = querySnapshot.docs.map(
10926
- (doc37) => ({ id: doc37.id, ...doc37.data() })
11016
+ (doc38) => ({ id: doc38.id, ...doc38.data() })
10927
11017
  );
10928
11018
  return events;
10929
11019
  } catch (error) {
@@ -11016,7 +11106,7 @@ async function getPractitionerSyncedCalendarsUtil(db, practitionerId) {
11016
11106
  );
11017
11107
  const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
11018
11108
  const querySnapshot = await getDocs22(q);
11019
- return querySnapshot.docs.map((doc37) => doc37.data());
11109
+ return querySnapshot.docs.map((doc38) => doc38.data());
11020
11110
  }
11021
11111
  async function getPatientSyncedCalendarUtil(db, patientId, calendarId) {
11022
11112
  const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
@@ -11033,7 +11123,7 @@ async function getPatientSyncedCalendarsUtil(db, patientId) {
11033
11123
  );
11034
11124
  const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
11035
11125
  const querySnapshot = await getDocs22(q);
11036
- return querySnapshot.docs.map((doc37) => doc37.data());
11126
+ return querySnapshot.docs.map((doc38) => doc38.data());
11037
11127
  }
11038
11128
  async function getClinicSyncedCalendarUtil(db, clinicId, calendarId) {
11039
11129
  const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
@@ -11050,7 +11140,7 @@ async function getClinicSyncedCalendarsUtil(db, clinicId) {
11050
11140
  );
11051
11141
  const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
11052
11142
  const querySnapshot = await getDocs22(q);
11053
- return querySnapshot.docs.map((doc37) => doc37.data());
11143
+ return querySnapshot.docs.map((doc38) => doc38.data());
11054
11144
  }
11055
11145
  async function updatePractitionerSyncedCalendarUtil(db, practitionerId, calendarId, updateData) {
11056
11146
  const calendarRef = getPractitionerSyncedCalendarDocRef(
@@ -12118,7 +12208,7 @@ var CalendarServiceV2 = class extends BaseService {
12118
12208
  syncStatus: "internal" /* INTERNAL */,
12119
12209
  eventType: "appointment" /* APPOINTMENT */
12120
12210
  };
12121
- const appointment = await createAppointmentUtil2(
12211
+ const appointment = await createAppointmentUtil(
12122
12212
  this.db,
12123
12213
  params.clinicId,
12124
12214
  params.doctorId,
@@ -12405,9 +12495,9 @@ var CalendarServiceV2 = class extends BaseService {
12405
12495
  where23("eventTime.start", "<=", Timestamp26.fromDate(endDate))
12406
12496
  );
12407
12497
  const eventsSnapshot = await getDocs23(q);
12408
- const events = eventsSnapshot.docs.map((doc37) => ({
12409
- id: doc37.id,
12410
- ...doc37.data()
12498
+ const events = eventsSnapshot.docs.map((doc38) => ({
12499
+ id: doc38.id,
12500
+ ...doc38.data()
12411
12501
  }));
12412
12502
  const calendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
12413
12503
  doctorId
@@ -13041,7 +13131,7 @@ var CalendarServiceV2 = class extends BaseService {
13041
13131
  ])
13042
13132
  );
13043
13133
  const querySnapshot = await getDocs23(q);
13044
- return querySnapshot.docs.map((doc37) => doc37.data());
13134
+ return querySnapshot.docs.map((doc38) => doc38.data());
13045
13135
  }
13046
13136
  /**
13047
13137
  * Calculates available time slots based on working hours, schedule and existing appointments
@@ -13586,7 +13676,7 @@ var PractitionerInviteService = class extends BaseService {
13586
13676
  ...constraints
13587
13677
  );
13588
13678
  const querySnapshot = await getDocs24(q);
13589
- return querySnapshot.docs.map((doc37) => doc37.data());
13679
+ return querySnapshot.docs.map((doc38) => doc38.data());
13590
13680
  } catch (error) {
13591
13681
  console.error(
13592
13682
  "[PractitionerInviteService] Error getting doctor invites:",
@@ -13615,7 +13705,7 @@ var PractitionerInviteService = class extends BaseService {
13615
13705
  ...constraints
13616
13706
  );
13617
13707
  const querySnapshot = await getDocs24(q);
13618
- return querySnapshot.docs.map((doc37) => doc37.data());
13708
+ return querySnapshot.docs.map((doc38) => doc38.data());
13619
13709
  } catch (error) {
13620
13710
  console.error(
13621
13711
  "[PractitionerInviteService] Error getting clinic invites:",
@@ -13771,7 +13861,7 @@ var PractitionerInviteService = class extends BaseService {
13771
13861
  );
13772
13862
  const querySnapshot = await getDocs24(q);
13773
13863
  let invites = querySnapshot.docs.map(
13774
- (doc37) => doc37.data()
13864
+ (doc38) => doc38.data()
13775
13865
  );
13776
13866
  if (filters.fromDate) {
13777
13867
  invites = invites.filter(
@@ -13887,9 +13977,10 @@ import {
13887
13977
  limit as limit12,
13888
13978
  startAfter as startAfter10
13889
13979
  } from "firebase/firestore";
13980
+ import { getCountFromServer } from "firebase/firestore";
13890
13981
  var DocumentationTemplateService = class extends BaseService {
13891
- constructor() {
13892
- super(...arguments);
13982
+ constructor(...args) {
13983
+ super(...args);
13893
13984
  this.collectionRef = collection25(
13894
13985
  this.db,
13895
13986
  DOCUMENTATION_TEMPLATES_COLLECTION
@@ -14041,8 +14132,8 @@ var DocumentationTemplateService = class extends BaseService {
14041
14132
  const q = query25(versionsCollectionRef, orderBy13("version", "desc"));
14042
14133
  const querySnapshot = await getDocs25(q);
14043
14134
  const versions = [];
14044
- querySnapshot.forEach((doc37) => {
14045
- versions.push(doc37.data());
14135
+ querySnapshot.forEach((doc38) => {
14136
+ versions.push(doc38.data());
14046
14137
  });
14047
14138
  return versions;
14048
14139
  }
@@ -14073,15 +14164,97 @@ var DocumentationTemplateService = class extends BaseService {
14073
14164
  const querySnapshot = await getDocs25(q);
14074
14165
  const templates = [];
14075
14166
  let lastVisible = null;
14076
- querySnapshot.forEach((doc37) => {
14077
- templates.push(doc37.data());
14078
- lastVisible = doc37;
14167
+ querySnapshot.forEach((doc38) => {
14168
+ templates.push(doc38.data());
14169
+ lastVisible = doc38;
14170
+ });
14171
+ return {
14172
+ templates,
14173
+ lastDoc: lastVisible
14174
+ };
14175
+ }
14176
+ /**
14177
+ * Get all active templates with optional filters and pagination.
14178
+ * @param options - Options for filtering and pagination.
14179
+ * @returns A promise that resolves to the templates and the last visible document.
14180
+ */
14181
+ async getTemplates(options) {
14182
+ const {
14183
+ pageSize = 20,
14184
+ lastDoc,
14185
+ isUserForm,
14186
+ isRequired,
14187
+ sortingOrder
14188
+ } = options;
14189
+ const constraints = [
14190
+ where25("isActive", "==", true),
14191
+ orderBy13("sortingOrder", "asc"),
14192
+ orderBy13("title", "asc"),
14193
+ limit12(pageSize)
14194
+ ];
14195
+ if (isUserForm !== void 0) {
14196
+ constraints.push(where25("isUserForm", "==", isUserForm));
14197
+ }
14198
+ if (isRequired !== void 0) {
14199
+ constraints.push(where25("isRequired", "==", isRequired));
14200
+ }
14201
+ if (sortingOrder !== void 0) {
14202
+ constraints.push(where25("sortingOrder", "==", sortingOrder));
14203
+ }
14204
+ if (lastDoc) {
14205
+ constraints.push(startAfter10(lastDoc));
14206
+ }
14207
+ const q = query25(this.collectionRef, ...constraints.filter((c) => c));
14208
+ const querySnapshot = await getDocs25(q);
14209
+ const templates = [];
14210
+ let lastVisible = null;
14211
+ querySnapshot.forEach((doc38) => {
14212
+ templates.push(doc38.data());
14213
+ lastVisible = doc38;
14079
14214
  });
14080
14215
  return {
14081
14216
  templates,
14082
14217
  lastDoc: lastVisible
14083
14218
  };
14084
14219
  }
14220
+ /**
14221
+ * Get the total count of active templates with optional filters.
14222
+ * @param options - Options for filtering.
14223
+ * @returns A promise that resolves to the total count of templates.
14224
+ */
14225
+ async getTemplatesCount(options) {
14226
+ const { isUserForm, isRequired, sortingOrder } = options;
14227
+ const constraints = [where25("isActive", "==", true)];
14228
+ if (isUserForm !== void 0) {
14229
+ constraints.push(where25("isUserForm", "==", isUserForm));
14230
+ }
14231
+ if (isRequired !== void 0) {
14232
+ constraints.push(where25("isRequired", "==", isRequired));
14233
+ }
14234
+ if (sortingOrder !== void 0) {
14235
+ constraints.push(where25("sortingOrder", "==", sortingOrder));
14236
+ }
14237
+ const q = query25(this.collectionRef, ...constraints.filter((c) => c));
14238
+ const snapshot = await getCountFromServer(q);
14239
+ return snapshot.data().count;
14240
+ }
14241
+ /**
14242
+ * Get all active templates without pagination for filtering purposes.
14243
+ * @returns A promise that resolves to an array of all active templates.
14244
+ */
14245
+ async getAllActiveTemplates() {
14246
+ const q = query25(
14247
+ this.collectionRef,
14248
+ where25("isActive", "==", true),
14249
+ orderBy13("title", "asc")
14250
+ );
14251
+ const querySnapshot = await getDocs25(q);
14252
+ const templates = [];
14253
+ querySnapshot.forEach((doc38) => {
14254
+ templates.push(doc38.data());
14255
+ });
14256
+ return templates;
14257
+ }
14085
14258
  /**
14086
14259
  * Get templates by tags
14087
14260
  * @param tags - Tags to filter by
@@ -14103,9 +14276,9 @@ var DocumentationTemplateService = class extends BaseService {
14103
14276
  const querySnapshot = await getDocs25(q);
14104
14277
  const templates = [];
14105
14278
  let lastVisible = null;
14106
- querySnapshot.forEach((doc37) => {
14107
- templates.push(doc37.data());
14108
- lastVisible = doc37;
14279
+ querySnapshot.forEach((doc38) => {
14280
+ templates.push(doc38.data());
14281
+ lastVisible = doc38;
14109
14282
  });
14110
14283
  return {
14111
14284
  templates,
@@ -14132,9 +14305,9 @@ var DocumentationTemplateService = class extends BaseService {
14132
14305
  const querySnapshot = await getDocs25(q);
14133
14306
  const templates = [];
14134
14307
  let lastVisible = null;
14135
- querySnapshot.forEach((doc37) => {
14136
- templates.push(doc37.data());
14137
- lastVisible = doc37;
14308
+ querySnapshot.forEach((doc38) => {
14309
+ templates.push(doc38.data());
14310
+ lastVisible = doc38;
14138
14311
  });
14139
14312
  return {
14140
14313
  templates,
@@ -14160,8 +14333,8 @@ var DocumentationTemplateService = class extends BaseService {
14160
14333
  }
14161
14334
  const querySnapshot = await getDocs25(q);
14162
14335
  const templates = [];
14163
- querySnapshot.forEach((doc37) => {
14164
- templates.push(doc37.data());
14336
+ querySnapshot.forEach((doc38) => {
14337
+ templates.push(doc38.data());
14165
14338
  });
14166
14339
  return templates;
14167
14340
  }
@@ -14367,9 +14540,9 @@ var FilledDocumentService = class extends BaseService {
14367
14540
  const querySnapshot = await getDocs26(q);
14368
14541
  const documents = [];
14369
14542
  let lastVisible = null;
14370
- querySnapshot.forEach((doc37) => {
14371
- documents.push(doc37.data());
14372
- lastVisible = doc37;
14543
+ querySnapshot.forEach((doc38) => {
14544
+ documents.push(doc38.data());
14545
+ lastVisible = doc38;
14373
14546
  });
14374
14547
  return {
14375
14548
  documents,
@@ -14591,9 +14764,9 @@ var NotificationService = class extends BaseService {
14591
14764
  orderBy15("notificationTime", "desc")
14592
14765
  );
14593
14766
  const querySnapshot = await getDocs27(q);
14594
- return querySnapshot.docs.map((doc37) => ({
14595
- id: doc37.id,
14596
- ...doc37.data()
14767
+ return querySnapshot.docs.map((doc38) => ({
14768
+ id: doc38.id,
14769
+ ...doc38.data()
14597
14770
  }));
14598
14771
  }
14599
14772
  /**
@@ -14607,9 +14780,9 @@ var NotificationService = class extends BaseService {
14607
14780
  orderBy15("notificationTime", "desc")
14608
14781
  );
14609
14782
  const querySnapshot = await getDocs27(q);
14610
- return querySnapshot.docs.map((doc37) => ({
14611
- id: doc37.id,
14612
- ...doc37.data()
14783
+ return querySnapshot.docs.map((doc38) => ({
14784
+ id: doc38.id,
14785
+ ...doc38.data()
14613
14786
  }));
14614
14787
  }
14615
14788
  /**
@@ -14681,9 +14854,9 @@ var NotificationService = class extends BaseService {
14681
14854
  orderBy15("notificationTime", "desc")
14682
14855
  );
14683
14856
  const querySnapshot = await getDocs27(q);
14684
- return querySnapshot.docs.map((doc37) => ({
14685
- id: doc37.id,
14686
- ...doc37.data()
14857
+ return querySnapshot.docs.map((doc38) => ({
14858
+ id: doc38.id,
14859
+ ...doc38.data()
14687
14860
  }));
14688
14861
  }
14689
14862
  /**
@@ -14696,9 +14869,9 @@ var NotificationService = class extends BaseService {
14696
14869
  orderBy15("notificationTime", "desc")
14697
14870
  );
14698
14871
  const querySnapshot = await getDocs27(q);
14699
- return querySnapshot.docs.map((doc37) => ({
14700
- id: doc37.id,
14701
- ...doc37.data()
14872
+ return querySnapshot.docs.map((doc38) => ({
14873
+ id: doc38.id,
14874
+ ...doc38.data()
14702
14875
  }));
14703
14876
  }
14704
14877
  };
@@ -14905,67 +15078,141 @@ import {
14905
15078
  } from "firebase/firestore";
14906
15079
 
14907
15080
  // src/validations/procedure.schema.ts
15081
+ import { z as z25 } from "zod";
15082
+
15083
+ // src/validations/procedure-product.schema.ts
14908
15084
  import { z as z24 } from "zod";
14909
- var createProcedureSchema = z24.object({
14910
- name: z24.string().min(1).max(200),
14911
- // Optional: service will derive from name if not provided by client
14912
- nameLower: z24.string().min(1).max(200).optional(),
14913
- description: z24.string().min(1).max(2e3),
14914
- family: z24.nativeEnum(ProcedureFamily),
14915
- categoryId: z24.string().min(1),
14916
- subcategoryId: z24.string().min(1),
14917
- technologyId: z24.string().min(1),
14918
- productId: z24.string().min(1),
14919
- price: z24.number().min(0),
15085
+ var procedureProductDataSchema = z24.object({
15086
+ /**
15087
+ * The ID of the product. Must be a non-empty string.
15088
+ * @validation
15089
+ */
15090
+ productId: z24.string().min(1, "Product ID is required"),
15091
+ /**
15092
+ * The price of the product. Must be a non-negative number.
15093
+ * @validation
15094
+ */
15095
+ price: z24.number().min(0, "Price must be a non-negative number"),
15096
+ /**
15097
+ * The currency for the price. Must be one of the values from the Currency enum.
15098
+ * @validation
15099
+ */
14920
15100
  currency: z24.nativeEnum(Currency),
15101
+ /**
15102
+ * The pricing measure for the product. Must be one of the values from the PricingMeasure enum.
15103
+ * @validation
15104
+ */
14921
15105
  pricingMeasure: z24.nativeEnum(PricingMeasure),
14922
- duration: z24.number().min(1).max(480),
15106
+ /**
15107
+ * Whether this is the default product for the procedure.
15108
+ * @validation
15109
+ */
15110
+ isDefault: z24.boolean().optional()
15111
+ });
15112
+
15113
+ // src/validations/procedure.schema.ts
15114
+ var storedProcedureProductSchema = z25.object({
15115
+ /**
15116
+ * The full product object used in the procedure.
15117
+ */
15118
+ product: z25.any(),
15119
+ // We'll validate the full product object separately
15120
+ /**
15121
+ * The price of the procedure when using this specific product.
15122
+ */
15123
+ price: z25.number().min(0, "Price must be a non-negative number"),
15124
+ /**
15125
+ * The currency for the price of this product.
15126
+ */
15127
+ currency: z25.nativeEnum(Currency),
15128
+ /**
15129
+ * How the price is measured (e.g., per ml, per zone).
15130
+ */
15131
+ pricingMeasure: z25.nativeEnum(PricingMeasure),
15132
+ /**
15133
+ * Whether this is the default product for the procedure.
15134
+ */
15135
+ isDefault: z25.boolean().optional()
15136
+ });
15137
+ var createProcedureSchema = z25.object({
15138
+ name: z25.string().min(1).max(200),
15139
+ // Optional: service will derive from name if not provided by client
15140
+ nameLower: z25.string().min(1).max(200).optional(),
15141
+ description: z25.string().min(1).max(2e3),
15142
+ family: z25.nativeEnum(ProcedureFamily),
15143
+ categoryId: z25.string().min(1),
15144
+ subcategoryId: z25.string().min(1),
15145
+ technologyId: z25.string().min(1),
15146
+ productId: z25.string().min(1),
15147
+ price: z25.number().min(0),
15148
+ currency: z25.nativeEnum(Currency),
15149
+ pricingMeasure: z25.nativeEnum(PricingMeasure),
15150
+ productsMetadata: z25.array(procedureProductDataSchema).min(1),
15151
+ duration: z25.number().min(1).max(480),
14923
15152
  // Max 8 hours
14924
- practitionerId: z24.string().min(1),
14925
- clinicBranchId: z24.string().min(1),
14926
- photos: z24.array(mediaResourceSchema).optional()
15153
+ practitionerId: z25.string().min(1),
15154
+ clinicBranchId: z25.string().min(1),
15155
+ photos: z25.array(mediaResourceSchema).optional()
14927
15156
  });
14928
- var updateProcedureSchema = z24.object({
14929
- name: z24.string().min(3).max(100).optional(),
14930
- nameLower: z24.string().min(1).max(200).optional(),
14931
- description: z24.string().min(3).max(1e3).optional(),
14932
- price: z24.number().min(0).optional(),
14933
- currency: z24.nativeEnum(Currency).optional(),
14934
- pricingMeasure: z24.nativeEnum(PricingMeasure).optional(),
14935
- duration: z24.number().min(0).optional(),
14936
- isActive: z24.boolean().optional(),
14937
- practitionerId: z24.string().optional(),
14938
- categoryId: z24.string().optional(),
14939
- subcategoryId: z24.string().optional(),
14940
- technologyId: z24.string().optional(),
14941
- productId: z24.string().optional(),
14942
- clinicBranchId: z24.string().optional(),
14943
- photos: z24.array(mediaResourceSchema).optional()
15157
+ var updateProcedureSchema = z25.object({
15158
+ name: z25.string().min(3).max(100).optional(),
15159
+ nameLower: z25.string().min(1).max(200).optional(),
15160
+ description: z25.string().min(3).max(1e3).optional(),
15161
+ price: z25.number().min(0).optional(),
15162
+ currency: z25.nativeEnum(Currency).optional(),
15163
+ pricingMeasure: z25.nativeEnum(PricingMeasure).optional(),
15164
+ productsMetadata: z25.array(procedureProductDataSchema).min(1).optional(),
15165
+ duration: z25.number().min(0).optional(),
15166
+ isActive: z25.boolean().optional(),
15167
+ practitionerId: z25.string().optional(),
15168
+ categoryId: z25.string().optional(),
15169
+ subcategoryId: z25.string().optional(),
15170
+ technologyId: z25.string().optional(),
15171
+ productId: z25.string().optional(),
15172
+ clinicBranchId: z25.string().optional(),
15173
+ photos: z25.array(mediaResourceSchema).optional()
14944
15174
  });
14945
- var procedureSchema = createProcedureSchema.extend({
14946
- id: z24.string().min(1),
14947
- nameLower: z24.string().min(1).max(200),
14948
- category: z24.any(),
15175
+ var procedureSchema = z25.object({
15176
+ id: z25.string().min(1),
15177
+ name: z25.string().min(1).max(200),
15178
+ nameLower: z25.string().min(1).max(200),
15179
+ description: z25.string().min(1).max(2e3),
15180
+ family: z25.nativeEnum(ProcedureFamily),
15181
+ category: z25.any(),
14949
15182
  // We'll validate the full category object separately
14950
- subcategory: z24.any(),
15183
+ subcategory: z25.any(),
14951
15184
  // We'll validate the full subcategory object separately
14952
- technology: z24.any(),
15185
+ technology: z25.any(),
14953
15186
  // We'll validate the full technology object separately
14954
- product: z24.any(),
15187
+ product: z25.any(),
14955
15188
  // We'll validate the full product object separately
14956
- blockingConditions: z24.array(z24.any()),
15189
+ productsMetadata: z25.array(storedProcedureProductSchema).min(1),
15190
+ // Use stored format schema
15191
+ price: z25.number().min(0),
15192
+ currency: z25.nativeEnum(Currency),
15193
+ pricingMeasure: z25.nativeEnum(PricingMeasure),
15194
+ duration: z25.number().min(1).max(480),
15195
+ practitionerId: z25.string().min(1),
15196
+ clinicBranchId: z25.string().min(1),
15197
+ photos: z25.array(z25.string()).optional(),
15198
+ // Stored as URL strings
15199
+ blockingConditions: z25.array(z25.any()),
14957
15200
  // We'll validate blocking conditions separately
14958
- contraindications: z24.array(z24.any()),
15201
+ contraindications: z25.array(z25.any()),
14959
15202
  // We'll validate contraindications separately
14960
- treatmentBenefits: z24.array(z24.any()),
15203
+ contraindicationIds: z25.array(z25.string()),
15204
+ // Array of IDs for efficient querying
15205
+ treatmentBenefits: z25.array(z25.any()),
14961
15206
  // We'll validate treatment benefits separately
14962
- preRequirements: z24.array(z24.any()),
15207
+ treatmentBenefitIds: z25.array(z25.string()),
15208
+ // Array of IDs for efficient querying
15209
+ preRequirements: z25.array(z25.any()),
14963
15210
  // We'll validate requirements separately
14964
- postRequirements: z24.array(z24.any()),
15211
+ postRequirements: z25.array(z25.any()),
14965
15212
  // We'll validate requirements separately
14966
- certificationRequirement: z24.any(),
15213
+ certificationRequirement: z25.any(),
14967
15214
  // We'll validate certification requirement separately
14968
- documentationTemplates: z24.array(z24.any()),
15215
+ documentationTemplates: z25.array(z25.any()),
14969
15216
  // We'll validate documentation templates separately
14970
15217
  clinicInfo: clinicInfoSchema,
14971
15218
  // Clinic info validation
@@ -14973,9 +15220,9 @@ var procedureSchema = createProcedureSchema.extend({
14973
15220
  // Doctor info validation
14974
15221
  reviewInfo: procedureReviewInfoSchema,
14975
15222
  // Procedure review info validation
14976
- isActive: z24.boolean(),
14977
- createdAt: z24.date(),
14978
- updatedAt: z24.date()
15223
+ isActive: z25.boolean(),
15224
+ createdAt: z25.date(),
15225
+ updatedAt: z25.date()
14979
15226
  });
14980
15227
 
14981
15228
  // src/services/procedure/procedure.service.ts
@@ -15002,7 +15249,9 @@ var ProcedureService = class extends BaseService {
15002
15249
  return media;
15003
15250
  }
15004
15251
  if (media instanceof File || media instanceof Blob) {
15005
- console.log(`[ProcedureService] Uploading ${collectionName} media for ${ownerId}`);
15252
+ console.log(
15253
+ `[ProcedureService] Uploading ${collectionName} media for ${ownerId}`
15254
+ );
15006
15255
  const metadata = await this.mediaService.uploadMedia(
15007
15256
  media,
15008
15257
  ownerId,
@@ -15024,41 +15273,91 @@ var ProcedureService = class extends BaseService {
15024
15273
  if (!mediaArray || mediaArray.length === 0) return [];
15025
15274
  const result = [];
15026
15275
  for (const media of mediaArray) {
15027
- const processedUrl = await this.processMedia(media, ownerId, collectionName);
15276
+ const processedUrl = await this.processMedia(
15277
+ media,
15278
+ ownerId,
15279
+ collectionName
15280
+ );
15028
15281
  if (processedUrl) {
15029
15282
  result.push(processedUrl);
15030
15283
  }
15031
15284
  }
15032
15285
  return result;
15033
15286
  }
15287
+ /**
15288
+ * Transforms validated procedure product data (with productId) to ProcedureProduct objects (with full product)
15289
+ * @param productsMetadata Array of validated procedure product data
15290
+ * @param technologyId Technology ID to fetch products from
15291
+ * @returns Array of ProcedureProduct objects with full product information
15292
+ */
15293
+ async transformProductsMetadata(productsMetadata, technologyId) {
15294
+ const transformedProducts = [];
15295
+ for (const productData of productsMetadata) {
15296
+ const product = await this.productService.getById(
15297
+ technologyId,
15298
+ productData.productId
15299
+ );
15300
+ if (!product) {
15301
+ throw new Error(
15302
+ `Product with ID ${productData.productId} not found for technology ${technologyId}`
15303
+ );
15304
+ }
15305
+ transformedProducts.push({
15306
+ product,
15307
+ price: productData.price,
15308
+ currency: productData.currency,
15309
+ pricingMeasure: productData.pricingMeasure,
15310
+ isDefault: productData.isDefault
15311
+ });
15312
+ }
15313
+ return transformedProducts;
15314
+ }
15034
15315
  /**
15035
15316
  * Creates a new procedure
15036
15317
  * @param data - The data for creating a new procedure
15037
15318
  * @returns The created procedure
15038
15319
  */
15039
15320
  async createProcedure(data) {
15040
- var _a;
15321
+ var _a, _b, _c;
15041
15322
  const validatedData = createProcedureSchema.parse(data);
15042
15323
  const procedureId = this.generateId();
15043
15324
  const [category, subcategory, technology, product] = await Promise.all([
15044
15325
  this.categoryService.getById(validatedData.categoryId),
15045
- this.subcategoryService.getById(validatedData.categoryId, validatedData.subcategoryId),
15326
+ this.subcategoryService.getById(
15327
+ validatedData.categoryId,
15328
+ validatedData.subcategoryId
15329
+ ),
15046
15330
  this.technologyService.getById(validatedData.technologyId),
15047
- this.productService.getById(validatedData.technologyId, validatedData.productId)
15331
+ this.productService.getById(
15332
+ validatedData.technologyId,
15333
+ validatedData.productId
15334
+ )
15048
15335
  ]);
15049
15336
  if (!category || !subcategory || !technology || !product) {
15050
15337
  throw new Error("One or more required base entities not found");
15051
15338
  }
15052
- const clinicRef = doc30(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId);
15339
+ const clinicRef = doc30(
15340
+ this.db,
15341
+ CLINICS_COLLECTION,
15342
+ validatedData.clinicBranchId
15343
+ );
15053
15344
  const clinicSnapshot = await getDoc32(clinicRef);
15054
15345
  if (!clinicSnapshot.exists()) {
15055
- throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
15346
+ throw new Error(
15347
+ `Clinic with ID ${validatedData.clinicBranchId} not found`
15348
+ );
15056
15349
  }
15057
15350
  const clinic = clinicSnapshot.data();
15058
- const practitionerRef = doc30(this.db, PRACTITIONERS_COLLECTION, validatedData.practitionerId);
15351
+ const practitionerRef = doc30(
15352
+ this.db,
15353
+ PRACTITIONERS_COLLECTION,
15354
+ validatedData.practitionerId
15355
+ );
15059
15356
  const practitionerSnapshot = await getDoc32(practitionerRef);
15060
15357
  if (!practitionerSnapshot.exists()) {
15061
- throw new Error(`Practitioner with ID ${validatedData.practitionerId} not found`);
15358
+ throw new Error(
15359
+ `Practitioner with ID ${validatedData.practitionerId} not found`
15360
+ );
15062
15361
  }
15063
15362
  const practitioner = practitionerSnapshot.data();
15064
15363
  let processedPhotos = [];
@@ -15069,6 +15368,10 @@ var ProcedureService = class extends BaseService {
15069
15368
  "procedure-photos"
15070
15369
  );
15071
15370
  }
15371
+ const transformedProductsMetadata = await this.transformProductsMetadata(
15372
+ validatedData.productsMetadata,
15373
+ validatedData.technologyId
15374
+ );
15072
15375
  const clinicInfo = {
15073
15376
  id: clinicSnapshot.id,
15074
15377
  name: clinic.name,
@@ -15097,9 +15400,12 @@ var ProcedureService = class extends BaseService {
15097
15400
  subcategory,
15098
15401
  technology,
15099
15402
  product,
15403
+ productsMetadata: transformedProductsMetadata,
15100
15404
  blockingConditions: technology.blockingConditions,
15101
15405
  contraindications: technology.contraindications || [],
15406
+ contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
15102
15407
  treatmentBenefits: technology.benefits,
15408
+ treatmentBenefitIds: ((_c = technology.benefits) == null ? void 0 : _c.map((b) => b.id)) || [],
15103
15409
  preRequirements: technology.requirements.pre,
15104
15410
  postRequirements: technology.requirements.post,
15105
15411
  certificationRequirement: technology.certificationRequirement,
@@ -15140,7 +15446,7 @@ var ProcedureService = class extends BaseService {
15140
15446
  * @returns A promise that resolves to an array of the newly created procedures.
15141
15447
  */
15142
15448
  async bulkCreateProcedures(baseData, practitionerIds) {
15143
- var _a;
15449
+ var _a, _b, _c;
15144
15450
  if (!practitionerIds || practitionerIds.length === 0) {
15145
15451
  throw new Error("Practitioner IDs array cannot be empty.");
15146
15452
  }
@@ -15148,16 +15454,24 @@ var ProcedureService = class extends BaseService {
15148
15454
  const validatedData = createProcedureSchema.parse(validationData);
15149
15455
  const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
15150
15456
  this.categoryService.getById(validatedData.categoryId),
15151
- this.subcategoryService.getById(validatedData.categoryId, validatedData.subcategoryId),
15457
+ this.subcategoryService.getById(
15458
+ validatedData.categoryId,
15459
+ validatedData.subcategoryId
15460
+ ),
15152
15461
  this.technologyService.getById(validatedData.technologyId),
15153
- this.productService.getById(validatedData.technologyId, validatedData.productId),
15462
+ this.productService.getById(
15463
+ validatedData.technologyId,
15464
+ validatedData.productId
15465
+ ),
15154
15466
  getDoc32(doc30(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
15155
15467
  ]);
15156
15468
  if (!category || !subcategory || !technology || !product) {
15157
15469
  throw new Error("One or more required base entities not found");
15158
15470
  }
15159
15471
  if (!clinicSnapshot.exists()) {
15160
- throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
15472
+ throw new Error(
15473
+ `Clinic with ID ${validatedData.clinicBranchId} not found`
15474
+ );
15161
15475
  }
15162
15476
  const clinic = clinicSnapshot.data();
15163
15477
  let processedPhotos = [];
@@ -15169,6 +15483,10 @@ var ProcedureService = class extends BaseService {
15169
15483
  "procedure-photos-batch"
15170
15484
  );
15171
15485
  }
15486
+ const transformedProductsMetadata = await this.transformProductsMetadata(
15487
+ validatedData.productsMetadata,
15488
+ validatedData.technologyId
15489
+ );
15172
15490
  const practitionersMap = /* @__PURE__ */ new Map();
15173
15491
  for (let i = 0; i < practitionerIds.length; i += 30) {
15174
15492
  const chunk = practitionerIds.slice(i, i + 30);
@@ -15177,14 +15495,18 @@ var ProcedureService = class extends BaseService {
15177
15495
  where29(documentId2(), "in", chunk)
15178
15496
  );
15179
15497
  const practitionersSnapshot = await getDocs29(practitionersQuery);
15180
- practitionersSnapshot.docs.forEach((doc37) => {
15181
- practitionersMap.set(doc37.id, doc37.data());
15498
+ practitionersSnapshot.docs.forEach((doc38) => {
15499
+ practitionersMap.set(doc38.id, doc38.data());
15182
15500
  });
15183
15501
  }
15184
15502
  if (practitionersMap.size !== practitionerIds.length) {
15185
15503
  const foundIds = Array.from(practitionersMap.keys());
15186
- const notFoundIds = practitionerIds.filter((id) => !foundIds.includes(id));
15187
- throw new Error(`The following practitioners were not found: ${notFoundIds.join(", ")}`);
15504
+ const notFoundIds = practitionerIds.filter(
15505
+ (id) => !foundIds.includes(id)
15506
+ );
15507
+ throw new Error(
15508
+ `The following practitioners were not found: ${notFoundIds.join(", ")}`
15509
+ );
15188
15510
  }
15189
15511
  const batch = writeBatch6(this.db);
15190
15512
  const createdProcedureIds = [];
@@ -15220,9 +15542,12 @@ var ProcedureService = class extends BaseService {
15220
15542
  subcategory,
15221
15543
  technology,
15222
15544
  product,
15545
+ productsMetadata: transformedProductsMetadata,
15223
15546
  blockingConditions: technology.blockingConditions,
15224
15547
  contraindications: technology.contraindications || [],
15548
+ contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
15225
15549
  treatmentBenefits: technology.benefits,
15550
+ treatmentBenefitIds: ((_c = technology.benefits) == null ? void 0 : _c.map((b) => b.id)) || [],
15226
15551
  preRequirements: technology.requirements.pre,
15227
15552
  postRequirements: technology.requirements.post,
15228
15553
  certificationRequirement: technology.certificationRequirement,
@@ -15252,10 +15577,13 @@ var ProcedureService = class extends BaseService {
15252
15577
  const fetchedProcedures = [];
15253
15578
  for (let i = 0; i < createdProcedureIds.length; i += 30) {
15254
15579
  const chunk = createdProcedureIds.slice(i, i + 30);
15255
- const q = query29(collection29(this.db, PROCEDURES_COLLECTION), where29(documentId2(), "in", chunk));
15580
+ const q = query29(
15581
+ collection29(this.db, PROCEDURES_COLLECTION),
15582
+ where29(documentId2(), "in", chunk)
15583
+ );
15256
15584
  const snapshot = await getDocs29(q);
15257
- snapshot.forEach((doc37) => {
15258
- fetchedProcedures.push(doc37.data());
15585
+ snapshot.forEach((doc38) => {
15586
+ fetchedProcedures.push(doc38.data());
15259
15587
  });
15260
15588
  }
15261
15589
  return fetchedProcedures;
@@ -15285,7 +15613,7 @@ var ProcedureService = class extends BaseService {
15285
15613
  where29("isActive", "==", true)
15286
15614
  );
15287
15615
  const snapshot = await getDocs29(q);
15288
- return snapshot.docs.map((doc37) => doc37.data());
15616
+ return snapshot.docs.map((doc38) => doc38.data());
15289
15617
  }
15290
15618
  /**
15291
15619
  * Gets all procedures for a practitioner
@@ -15299,7 +15627,7 @@ var ProcedureService = class extends BaseService {
15299
15627
  where29("isActive", "==", true)
15300
15628
  );
15301
15629
  const snapshot = await getDocs29(q);
15302
- return snapshot.docs.map((doc37) => doc37.data());
15630
+ return snapshot.docs.map((doc38) => doc38.data());
15303
15631
  }
15304
15632
  /**
15305
15633
  * Gets all inactive procedures for a practitioner
@@ -15313,7 +15641,7 @@ var ProcedureService = class extends BaseService {
15313
15641
  where29("isActive", "==", false)
15314
15642
  );
15315
15643
  const snapshot = await getDocs29(q);
15316
- return snapshot.docs.map((doc37) => doc37.data());
15644
+ return snapshot.docs.map((doc38) => doc38.data());
15317
15645
  }
15318
15646
  /**
15319
15647
  * Updates a procedure
@@ -15322,7 +15650,7 @@ var ProcedureService = class extends BaseService {
15322
15650
  * @returns The updated procedure
15323
15651
  */
15324
15652
  async updateProcedure(id, data) {
15325
- var _a;
15653
+ var _a, _b, _c, _d;
15326
15654
  const validatedData = updateProcedureSchema.parse(data);
15327
15655
  const procedureRef = doc30(this.db, PROCEDURES_COLLECTION, id);
15328
15656
  const procedureSnapshot = await getDoc32(procedureRef);
@@ -15330,7 +15658,21 @@ var ProcedureService = class extends BaseService {
15330
15658
  throw new Error(`Procedure with ID ${id} not found`);
15331
15659
  }
15332
15660
  const existingProcedure = procedureSnapshot.data();
15333
- let updatedProcedureData = { ...validatedData };
15661
+ let updatedProcedureData = {};
15662
+ if (validatedData.name !== void 0)
15663
+ updatedProcedureData.name = validatedData.name;
15664
+ if (validatedData.description !== void 0)
15665
+ updatedProcedureData.description = validatedData.description;
15666
+ if (validatedData.price !== void 0)
15667
+ updatedProcedureData.price = validatedData.price;
15668
+ if (validatedData.currency !== void 0)
15669
+ updatedProcedureData.currency = validatedData.currency;
15670
+ if (validatedData.pricingMeasure !== void 0)
15671
+ updatedProcedureData.pricingMeasure = validatedData.pricingMeasure;
15672
+ if (validatedData.duration !== void 0)
15673
+ updatedProcedureData.duration = validatedData.duration;
15674
+ if (validatedData.isActive !== void 0)
15675
+ updatedProcedureData.isActive = validatedData.isActive;
15334
15676
  let practitionerChanged = false;
15335
15677
  let clinicChanged = false;
15336
15678
  const oldPractitionerId = existingProcedure.practitionerId;
@@ -15344,6 +15686,18 @@ var ProcedureService = class extends BaseService {
15344
15686
  "procedure-photos"
15345
15687
  );
15346
15688
  }
15689
+ if (validatedData.productsMetadata !== void 0) {
15690
+ const technologyId = (_a = validatedData.technologyId) != null ? _a : existingProcedure.technology.id;
15691
+ if (!technologyId) {
15692
+ throw new Error(
15693
+ "Technology ID is required for updating products metadata"
15694
+ );
15695
+ }
15696
+ updatedProcedureData.productsMetadata = await this.transformProductsMetadata(
15697
+ validatedData.productsMetadata,
15698
+ technologyId
15699
+ );
15700
+ }
15347
15701
  if (validatedData.practitionerId && validatedData.practitionerId !== oldPractitionerId) {
15348
15702
  practitionerChanged = true;
15349
15703
  const newPractitionerRef = doc30(
@@ -15353,7 +15707,9 @@ var ProcedureService = class extends BaseService {
15353
15707
  );
15354
15708
  const newPractitionerSnap = await getDoc32(newPractitionerRef);
15355
15709
  if (!newPractitionerSnap.exists())
15356
- throw new Error(`New Practitioner ${validatedData.practitionerId} not found`);
15710
+ throw new Error(
15711
+ `New Practitioner ${validatedData.practitionerId} not found`
15712
+ );
15357
15713
  newPractitioner = newPractitionerSnap.data();
15358
15714
  updatedProcedureData.doctorInfo = {
15359
15715
  id: newPractitioner.id,
@@ -15361,13 +15717,17 @@ var ProcedureService = class extends BaseService {
15361
15717
  description: newPractitioner.basicInfo.bio || "",
15362
15718
  photo: typeof newPractitioner.basicInfo.profileImageUrl === "string" ? newPractitioner.basicInfo.profileImageUrl : "",
15363
15719
  // Default to empty string if not a processed URL
15364
- rating: ((_a = newPractitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
15720
+ rating: ((_b = newPractitioner.reviewInfo) == null ? void 0 : _b.averageRating) || 0,
15365
15721
  services: newPractitioner.procedures || []
15366
15722
  };
15367
15723
  }
15368
15724
  if (validatedData.clinicBranchId && validatedData.clinicBranchId !== oldClinicId) {
15369
15725
  clinicChanged = true;
15370
- const newClinicRef = doc30(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId);
15726
+ const newClinicRef = doc30(
15727
+ this.db,
15728
+ CLINICS_COLLECTION,
15729
+ validatedData.clinicBranchId
15730
+ );
15371
15731
  const newClinicSnap = await getDoc32(newClinicRef);
15372
15732
  if (!newClinicSnap.exists())
15373
15733
  throw new Error(`New Clinic ${validatedData.clinicBranchId} not found`);
@@ -15386,8 +15746,11 @@ var ProcedureService = class extends BaseService {
15386
15746
  updatedProcedureData.nameLower = validatedData.name.toLowerCase();
15387
15747
  }
15388
15748
  if (validatedData.categoryId) {
15389
- const category = await this.categoryService.getById(validatedData.categoryId);
15390
- if (!category) throw new Error(`Category ${validatedData.categoryId} not found`);
15749
+ const category = await this.categoryService.getById(
15750
+ validatedData.categoryId
15751
+ );
15752
+ if (!category)
15753
+ throw new Error(`Category ${validatedData.categoryId} not found`);
15391
15754
  updatedProcedureData.category = category;
15392
15755
  finalCategoryId = category.id;
15393
15756
  }
@@ -15402,23 +15765,34 @@ var ProcedureService = class extends BaseService {
15402
15765
  );
15403
15766
  updatedProcedureData.subcategory = subcategory;
15404
15767
  } else if (validatedData.subcategoryId) {
15405
- console.warn("Attempted to update subcategory without a valid categoryId");
15768
+ console.warn(
15769
+ "Attempted to update subcategory without a valid categoryId"
15770
+ );
15406
15771
  }
15407
15772
  let finalTechnologyId = existingProcedure.technology.id;
15408
15773
  if (validatedData.technologyId) {
15409
- const technology = await this.technologyService.getById(validatedData.technologyId);
15410
- if (!technology) throw new Error(`Technology ${validatedData.technologyId} not found`);
15774
+ const technology = await this.technologyService.getById(
15775
+ validatedData.technologyId
15776
+ );
15777
+ if (!technology)
15778
+ throw new Error(`Technology ${validatedData.technologyId} not found`);
15411
15779
  updatedProcedureData.technology = technology;
15412
15780
  finalTechnologyId = technology.id;
15413
15781
  updatedProcedureData.blockingConditions = technology.blockingConditions;
15782
+ updatedProcedureData.contraindications = technology.contraindications || [];
15783
+ updatedProcedureData.contraindicationIds = ((_c = technology.contraindications) == null ? void 0 : _c.map((c) => c.id)) || [];
15414
15784
  updatedProcedureData.treatmentBenefits = technology.benefits;
15785
+ updatedProcedureData.treatmentBenefitIds = ((_d = technology.benefits) == null ? void 0 : _d.map((b) => b.id)) || [];
15415
15786
  updatedProcedureData.preRequirements = technology.requirements.pre;
15416
15787
  updatedProcedureData.postRequirements = technology.requirements.post;
15417
15788
  updatedProcedureData.certificationRequirement = technology.certificationRequirement;
15418
15789
  updatedProcedureData.documentationTemplates = technology.documentationTemplates || [];
15419
15790
  }
15420
15791
  if (validatedData.productId && finalTechnologyId) {
15421
- const product = await this.productService.getById(finalTechnologyId, validatedData.productId);
15792
+ const product = await this.productService.getById(
15793
+ finalTechnologyId,
15794
+ validatedData.productId
15795
+ );
15422
15796
  if (!product)
15423
15797
  throw new Error(
15424
15798
  `Product ${validatedData.productId} not found for technology ${finalTechnologyId}`
@@ -15490,28 +15864,32 @@ var ProcedureService = class extends BaseService {
15490
15864
  const proceduresCollection = collection29(this.db, PROCEDURES_COLLECTION);
15491
15865
  let proceduresQuery = query29(proceduresCollection);
15492
15866
  if (pagination && pagination > 0) {
15493
- const { limit: limit16, startAfter: startAfter14 } = await import("firebase/firestore");
15867
+ const { limit: limit21, startAfter: startAfter19 } = await import("firebase/firestore");
15494
15868
  if (lastDoc) {
15495
15869
  proceduresQuery = query29(
15496
15870
  proceduresCollection,
15497
15871
  orderBy17("name"),
15498
15872
  // Use imported orderBy
15499
- startAfter14(lastDoc),
15500
- limit16(pagination)
15873
+ startAfter19(lastDoc),
15874
+ limit21(pagination)
15501
15875
  );
15502
15876
  } else {
15503
- proceduresQuery = query29(proceduresCollection, orderBy17("name"), limit16(pagination));
15877
+ proceduresQuery = query29(
15878
+ proceduresCollection,
15879
+ orderBy17("name"),
15880
+ limit21(pagination)
15881
+ );
15504
15882
  }
15505
15883
  } else {
15506
15884
  proceduresQuery = query29(proceduresCollection, orderBy17("name"));
15507
15885
  }
15508
15886
  const proceduresSnapshot = await getDocs29(proceduresQuery);
15509
15887
  const lastVisible = proceduresSnapshot.docs[proceduresSnapshot.docs.length - 1];
15510
- const procedures = proceduresSnapshot.docs.map((doc37) => {
15511
- const data = doc37.data();
15888
+ const procedures = proceduresSnapshot.docs.map((doc38) => {
15889
+ const data = doc38.data();
15512
15890
  return {
15513
15891
  ...data,
15514
- id: doc37.id
15892
+ id: doc38.id
15515
15893
  // Ensure ID is present
15516
15894
  };
15517
15895
  });
@@ -15531,7 +15909,7 @@ var ProcedureService = class extends BaseService {
15531
15909
  *
15532
15910
  * @param filters - Various filters to apply
15533
15911
  * @param filters.nameSearch - Optional search text for procedure name
15534
- * @param filters.treatmentBenefits - Optional array of treatment benefits to filter by
15912
+ * @param filters.treatmentBenefitIds - Optional array of treatment benefits to filter by
15535
15913
  * @param filters.procedureFamily - Optional procedure family to filter by
15536
15914
  * @param filters.procedureCategory - Optional procedure category to filter by
15537
15915
  * @param filters.procedureSubcategory - Optional procedure subcategory to filter by
@@ -15549,7 +15927,9 @@ var ProcedureService = class extends BaseService {
15549
15927
  */
15550
15928
  async getProceduresByFilters(filters) {
15551
15929
  try {
15552
- console.log("[PROCEDURE_SERVICE] Starting procedure filtering with multiple strategies");
15930
+ console.log(
15931
+ "[PROCEDURE_SERVICE] Starting procedure filtering with multiple strategies"
15932
+ );
15553
15933
  if (filters.location && filters.radiusInKm) {
15554
15934
  console.log("[PROCEDURE_SERVICE] Executing geo query:", {
15555
15935
  location: filters.location,
@@ -15557,7 +15937,10 @@ var ProcedureService = class extends BaseService {
15557
15937
  serviceName: "ProcedureService"
15558
15938
  });
15559
15939
  if (!filters.location.latitude || !filters.location.longitude) {
15560
- console.warn("[PROCEDURE_SERVICE] Invalid location data:", filters.location);
15940
+ console.warn(
15941
+ "[PROCEDURE_SERVICE] Invalid location data:",
15942
+ filters.location
15943
+ );
15561
15944
  filters.location = void 0;
15562
15945
  filters.radiusInKm = void 0;
15563
15946
  }
@@ -15577,13 +15960,19 @@ var ProcedureService = class extends BaseService {
15577
15960
  constraints.push(where29("family", "==", filters.procedureFamily));
15578
15961
  }
15579
15962
  if (filters.procedureCategory) {
15580
- constraints.push(where29("category.id", "==", filters.procedureCategory));
15963
+ constraints.push(
15964
+ where29("category.id", "==", filters.procedureCategory)
15965
+ );
15581
15966
  }
15582
15967
  if (filters.procedureSubcategory) {
15583
- constraints.push(where29("subcategory.id", "==", filters.procedureSubcategory));
15968
+ constraints.push(
15969
+ where29("subcategory.id", "==", filters.procedureSubcategory)
15970
+ );
15584
15971
  }
15585
15972
  if (filters.procedureTechnology) {
15586
- constraints.push(where29("technology.id", "==", filters.procedureTechnology));
15973
+ constraints.push(
15974
+ where29("technology.id", "==", filters.procedureTechnology)
15975
+ );
15587
15976
  }
15588
15977
  if (filters.minPrice !== void 0) {
15589
15978
  constraints.push(where29("price", ">=", filters.minPrice));
@@ -15592,20 +15981,32 @@ var ProcedureService = class extends BaseService {
15592
15981
  constraints.push(where29("price", "<=", filters.maxPrice));
15593
15982
  }
15594
15983
  if (filters.minRating !== void 0) {
15595
- constraints.push(where29("reviewInfo.averageRating", ">=", filters.minRating));
15984
+ constraints.push(
15985
+ where29("reviewInfo.averageRating", ">=", filters.minRating)
15986
+ );
15596
15987
  }
15597
15988
  if (filters.maxRating !== void 0) {
15598
- constraints.push(where29("reviewInfo.averageRating", "<=", filters.maxRating));
15989
+ constraints.push(
15990
+ where29("reviewInfo.averageRating", "<=", filters.maxRating)
15991
+ );
15599
15992
  }
15600
15993
  if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
15601
- const benefitsToMatch = filters.treatmentBenefits;
15602
- constraints.push(where29("treatmentBenefits", "array-contains-any", benefitsToMatch));
15994
+ const benefitIdsToMatch = filters.treatmentBenefits;
15995
+ constraints.push(
15996
+ where29(
15997
+ "treatmentBenefitIds",
15998
+ "array-contains-any",
15999
+ benefitIdsToMatch
16000
+ )
16001
+ );
15603
16002
  }
15604
16003
  return constraints;
15605
16004
  };
15606
16005
  if (filters.nameSearch && filters.nameSearch.trim()) {
15607
16006
  try {
15608
- console.log("[PROCEDURE_SERVICE] Strategy 1: Trying nameLower search");
16007
+ console.log(
16008
+ "[PROCEDURE_SERVICE] Strategy 1: Trying nameLower search"
16009
+ );
15609
16010
  const searchTerm = filters.nameSearch.trim().toLowerCase();
15610
16011
  const constraints = getBaseConstraints();
15611
16012
  constraints.push(where29("nameLower", ">=", searchTerm));
@@ -15621,13 +16022,18 @@ var ProcedureService = class extends BaseService {
15621
16022
  }
15622
16023
  }
15623
16024
  constraints.push(limit15(filters.pagination || 10));
15624
- const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
16025
+ const q = query29(
16026
+ collection29(this.db, PROCEDURES_COLLECTION),
16027
+ ...constraints
16028
+ );
15625
16029
  const querySnapshot = await getDocs29(q);
15626
16030
  const procedures = querySnapshot.docs.map(
15627
- (doc37) => ({ ...doc37.data(), id: doc37.id })
16031
+ (doc38) => ({ ...doc38.data(), id: doc38.id })
15628
16032
  );
15629
16033
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15630
- console.log(`[PROCEDURE_SERVICE] Strategy 1 success: ${procedures.length} procedures`);
16034
+ console.log(
16035
+ `[PROCEDURE_SERVICE] Strategy 1 success: ${procedures.length} procedures`
16036
+ );
15631
16037
  if (procedures.length < (filters.pagination || 10)) {
15632
16038
  return { procedures, lastDoc: null };
15633
16039
  }
@@ -15638,7 +16044,9 @@ var ProcedureService = class extends BaseService {
15638
16044
  }
15639
16045
  if (filters.nameSearch && filters.nameSearch.trim()) {
15640
16046
  try {
15641
- console.log("[PROCEDURE_SERVICE] Strategy 2: Trying name field search");
16047
+ console.log(
16048
+ "[PROCEDURE_SERVICE] Strategy 2: Trying name field search"
16049
+ );
15642
16050
  const searchTerm = filters.nameSearch.trim().toLowerCase();
15643
16051
  const constraints = getBaseConstraints();
15644
16052
  constraints.push(where29("name", ">=", searchTerm));
@@ -15654,13 +16062,18 @@ var ProcedureService = class extends BaseService {
15654
16062
  }
15655
16063
  }
15656
16064
  constraints.push(limit15(filters.pagination || 10));
15657
- const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
16065
+ const q = query29(
16066
+ collection29(this.db, PROCEDURES_COLLECTION),
16067
+ ...constraints
16068
+ );
15658
16069
  const querySnapshot = await getDocs29(q);
15659
16070
  const procedures = querySnapshot.docs.map(
15660
- (doc37) => ({ ...doc37.data(), id: doc37.id })
16071
+ (doc38) => ({ ...doc38.data(), id: doc38.id })
15661
16072
  );
15662
16073
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15663
- console.log(`[PROCEDURE_SERVICE] Strategy 2 success: ${procedures.length} procedures`);
16074
+ console.log(
16075
+ `[PROCEDURE_SERVICE] Strategy 2 success: ${procedures.length} procedures`
16076
+ );
15664
16077
  if (procedures.length < (filters.pagination || 10)) {
15665
16078
  return { procedures, lastDoc: null };
15666
16079
  }
@@ -15685,14 +16098,19 @@ var ProcedureService = class extends BaseService {
15685
16098
  }
15686
16099
  }
15687
16100
  constraints.push(limit15(filters.pagination || 10));
15688
- const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
16101
+ const q = query29(
16102
+ collection29(this.db, PROCEDURES_COLLECTION),
16103
+ ...constraints
16104
+ );
15689
16105
  const querySnapshot = await getDocs29(q);
15690
16106
  let procedures = querySnapshot.docs.map(
15691
- (doc37) => ({ ...doc37.data(), id: doc37.id })
16107
+ (doc38) => ({ ...doc38.data(), id: doc38.id })
15692
16108
  );
15693
16109
  procedures = this.applyInMemoryFilters(procedures, filters);
15694
16110
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15695
- console.log(`[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`);
16111
+ console.log(
16112
+ `[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`
16113
+ );
15696
16114
  if (procedures.length < (filters.pagination || 10)) {
15697
16115
  return { procedures, lastDoc: null };
15698
16116
  }
@@ -15707,14 +16125,19 @@ var ProcedureService = class extends BaseService {
15707
16125
  orderBy17("createdAt", "desc"),
15708
16126
  limit15(filters.pagination || 10)
15709
16127
  ];
15710
- const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
16128
+ const q = query29(
16129
+ collection29(this.db, PROCEDURES_COLLECTION),
16130
+ ...constraints
16131
+ );
15711
16132
  const querySnapshot = await getDocs29(q);
15712
16133
  let procedures = querySnapshot.docs.map(
15713
- (doc37) => ({ ...doc37.data(), id: doc37.id })
16134
+ (doc38) => ({ ...doc38.data(), id: doc38.id })
15714
16135
  );
15715
16136
  procedures = this.applyInMemoryFilters(procedures, filters);
15716
16137
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15717
- console.log(`[PROCEDURE_SERVICE] Strategy 4 success: ${procedures.length} procedures`);
16138
+ console.log(
16139
+ `[PROCEDURE_SERVICE] Strategy 4 success: ${procedures.length} procedures`
16140
+ );
15718
16141
  if (procedures.length < (filters.pagination || 10)) {
15719
16142
  return { procedures, lastDoc: null };
15720
16143
  }
@@ -15722,7 +16145,9 @@ var ProcedureService = class extends BaseService {
15722
16145
  } catch (error) {
15723
16146
  console.log("[PROCEDURE_SERVICE] Strategy 4 failed:", error);
15724
16147
  }
15725
- console.log("[PROCEDURE_SERVICE] All strategies failed, returning empty result");
16148
+ console.log(
16149
+ "[PROCEDURE_SERVICE] All strategies failed, returning empty result"
16150
+ );
15726
16151
  return { procedures: [], lastDoc: null };
15727
16152
  } catch (error) {
15728
16153
  console.error("[PROCEDURE_SERVICE] Error filtering procedures:", error);
@@ -15742,13 +16167,17 @@ var ProcedureService = class extends BaseService {
15742
16167
  const nameLower = procedure.nameLower || "";
15743
16168
  return name.includes(searchTerm) || nameLower.includes(searchTerm);
15744
16169
  });
15745
- console.log(`[PROCEDURE_SERVICE] Applied name filter, results: ${filteredProcedures.length}`);
16170
+ console.log(
16171
+ `[PROCEDURE_SERVICE] Applied name filter, results: ${filteredProcedures.length}`
16172
+ );
15746
16173
  }
15747
16174
  if (filters.minPrice !== void 0 || filters.maxPrice !== void 0) {
15748
16175
  filteredProcedures = filteredProcedures.filter((procedure) => {
15749
16176
  const price = procedure.price || 0;
15750
- if (filters.minPrice !== void 0 && price < filters.minPrice) return false;
15751
- if (filters.maxPrice !== void 0 && price > filters.maxPrice) return false;
16177
+ if (filters.minPrice !== void 0 && price < filters.minPrice)
16178
+ return false;
16179
+ if (filters.maxPrice !== void 0 && price > filters.maxPrice)
16180
+ return false;
15752
16181
  return true;
15753
16182
  });
15754
16183
  console.log(
@@ -15759,8 +16188,10 @@ var ProcedureService = class extends BaseService {
15759
16188
  filteredProcedures = filteredProcedures.filter((procedure) => {
15760
16189
  var _a;
15761
16190
  const rating = ((_a = procedure.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
15762
- if (filters.minRating !== void 0 && rating < filters.minRating) return false;
15763
- if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
16191
+ if (filters.minRating !== void 0 && rating < filters.minRating)
16192
+ return false;
16193
+ if (filters.maxRating !== void 0 && rating > filters.maxRating)
16194
+ return false;
15764
16195
  return true;
15765
16196
  });
15766
16197
  console.log(
@@ -15768,10 +16199,12 @@ var ProcedureService = class extends BaseService {
15768
16199
  );
15769
16200
  }
15770
16201
  if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
15771
- const benefitsToMatch = filters.treatmentBenefits;
16202
+ const benefitIdsToMatch = filters.treatmentBenefits;
15772
16203
  filteredProcedures = filteredProcedures.filter((procedure) => {
15773
- const procedureBenefits = procedure.treatmentBenefits || [];
15774
- return benefitsToMatch.some((benefit) => procedureBenefits.includes(benefit));
16204
+ const procedureBenefitIds = procedure.treatmentBenefitIds || [];
16205
+ return benefitIdsToMatch.some(
16206
+ (benefitId) => procedureBenefitIds.includes(benefitId)
16207
+ );
15775
16208
  });
15776
16209
  console.log(
15777
16210
  `[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`
@@ -15834,8 +16267,12 @@ var ProcedureService = class extends BaseService {
15834
16267
  procedure.distance = distance;
15835
16268
  return distance <= radiusInKm;
15836
16269
  });
15837
- console.log(`[PROCEDURE_SERVICE] Applied geo filter, results: ${filteredProcedures.length}`);
15838
- filteredProcedures.sort((a, b) => (a.distance || 0) - (b.distance || 0));
16270
+ console.log(
16271
+ `[PROCEDURE_SERVICE] Applied geo filter, results: ${filteredProcedures.length}`
16272
+ );
16273
+ filteredProcedures.sort(
16274
+ (a, b) => (a.distance || 0) - (b.distance || 0)
16275
+ );
15839
16276
  }
15840
16277
  return filteredProcedures;
15841
16278
  }
@@ -15847,19 +16284,30 @@ var ProcedureService = class extends BaseService {
15847
16284
  if (!location || !radiusInKm) {
15848
16285
  return Promise.resolve({ procedures: [], lastDoc: null });
15849
16286
  }
15850
- const bounds = geohashQueryBounds5([location.latitude, location.longitude], radiusInKm * 1e3);
16287
+ const bounds = geohashQueryBounds5(
16288
+ [location.latitude, location.longitude],
16289
+ radiusInKm * 1e3
16290
+ );
15851
16291
  const fetches = bounds.map((b) => {
15852
16292
  const constraints = [
15853
16293
  where29("clinicInfo.location.geohash", ">=", b[0]),
15854
16294
  where29("clinicInfo.location.geohash", "<=", b[1]),
15855
- where29("isActive", "==", filters.isActive !== void 0 ? filters.isActive : true)
16295
+ where29(
16296
+ "isActive",
16297
+ "==",
16298
+ filters.isActive !== void 0 ? filters.isActive : true
16299
+ )
15856
16300
  ];
15857
- return getDocs29(query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints));
16301
+ return getDocs29(
16302
+ query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints)
16303
+ );
15858
16304
  });
15859
16305
  return Promise.all(fetches).then((snaps) => {
15860
16306
  const collected = [];
15861
16307
  snaps.forEach((snap) => {
15862
- snap.docs.forEach((d) => collected.push({ ...d.data(), id: d.id }));
16308
+ snap.docs.forEach(
16309
+ (d) => collected.push({ ...d.data(), id: d.id })
16310
+ );
15863
16311
  });
15864
16312
  const uniqueMap = /* @__PURE__ */ new Map();
15865
16313
  for (const p of collected) {
@@ -15870,7 +16318,9 @@ var ProcedureService = class extends BaseService {
15870
16318
  const pageSize = filters.pagination || 10;
15871
16319
  let startIndex = 0;
15872
16320
  if (filters.lastDoc && typeof filters.lastDoc === "object" && filters.lastDoc.id) {
15873
- const idx = procedures.findIndex((p) => p.id === filters.lastDoc.id);
16321
+ const idx = procedures.findIndex(
16322
+ (p) => p.id === filters.lastDoc.id
16323
+ );
15874
16324
  if (idx >= 0) startIndex = idx + 1;
15875
16325
  }
15876
16326
  const page = procedures.slice(startIndex, startIndex + pageSize);
@@ -15895,7 +16345,7 @@ var ProcedureService = class extends BaseService {
15895
16345
  * @returns The created procedure
15896
16346
  */
15897
16347
  async createConsultationProcedure(data) {
15898
- var _a;
16348
+ var _a, _b, _c;
15899
16349
  const procedureId = this.generateId();
15900
16350
  const [category, subcategory, technology] = await Promise.all([
15901
16351
  this.categoryService.getById(data.categoryId),
@@ -15911,7 +16361,11 @@ var ProcedureService = class extends BaseService {
15911
16361
  throw new Error(`Clinic with ID ${data.clinicBranchId} not found`);
15912
16362
  }
15913
16363
  const clinic = clinicSnapshot.data();
15914
- const practitionerRef = doc30(this.db, PRACTITIONERS_COLLECTION, data.practitionerId);
16364
+ const practitionerRef = doc30(
16365
+ this.db,
16366
+ PRACTITIONERS_COLLECTION,
16367
+ data.practitionerId
16368
+ );
15915
16369
  const practitionerSnapshot = await getDoc32(practitionerRef);
15916
16370
  if (!practitionerSnapshot.exists()) {
15917
16371
  throw new Error(`Practitioner with ID ${data.practitionerId} not found`);
@@ -15919,8 +16373,16 @@ var ProcedureService = class extends BaseService {
15919
16373
  const practitioner = practitionerSnapshot.data();
15920
16374
  let processedPhotos = [];
15921
16375
  if (data.photos && data.photos.length > 0) {
15922
- processedPhotos = await this.processMediaArray(data.photos, procedureId, "procedure-photos");
16376
+ processedPhotos = await this.processMediaArray(
16377
+ data.photos,
16378
+ procedureId,
16379
+ "procedure-photos"
16380
+ );
15923
16381
  }
16382
+ const transformedProductsMetadata = await this.transformProductsMetadata(
16383
+ data.productsMetadata,
16384
+ data.technologyId
16385
+ );
15924
16386
  const clinicInfo = {
15925
16387
  id: clinicSnapshot.id,
15926
16388
  name: clinic.name,
@@ -15945,6 +16407,8 @@ var ProcedureService = class extends BaseService {
15945
16407
  brandName: "Consultation",
15946
16408
  technologyId: data.technologyId,
15947
16409
  technologyName: technology.name,
16410
+ categoryId: technology.categoryId,
16411
+ subcategoryId: technology.subcategoryId,
15948
16412
  isActive: true,
15949
16413
  createdAt: /* @__PURE__ */ new Date(),
15950
16414
  updatedAt: /* @__PURE__ */ new Date()
@@ -15959,9 +16423,12 @@ var ProcedureService = class extends BaseService {
15959
16423
  technology,
15960
16424
  product: consultationProduct,
15961
16425
  // Use placeholder product
16426
+ productsMetadata: transformedProductsMetadata,
15962
16427
  blockingConditions: technology.blockingConditions,
15963
16428
  contraindications: technology.contraindications || [],
16429
+ contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
15964
16430
  treatmentBenefits: technology.benefits,
16431
+ treatmentBenefitIds: ((_c = technology.benefits) == null ? void 0 : _c.map((b) => b.id)) || [],
15965
16432
  preRequirements: technology.requirements.pre,
15966
16433
  postRequirements: technology.requirements.post,
15967
16434
  certificationRequirement: technology.certificationRequirement,
@@ -15997,11 +16464,11 @@ var ProcedureService = class extends BaseService {
15997
16464
  async getProceduresForMap() {
15998
16465
  const proceduresRef = collection29(this.db, PROCEDURES_COLLECTION);
15999
16466
  const snapshot = await getDocs29(proceduresRef);
16000
- const proceduresForMap = snapshot.docs.map((doc37) => {
16467
+ const proceduresForMap = snapshot.docs.map((doc38) => {
16001
16468
  var _a, _b, _c, _d, _e, _f, _g, _h;
16002
- const data = doc37.data();
16469
+ const data = doc38.data();
16003
16470
  return {
16004
- id: doc37.id,
16471
+ id: doc38.id,
16005
16472
  name: data.name,
16006
16473
  clinicId: (_a = data.clinicInfo) == null ? void 0 : _a.id,
16007
16474
  clinicName: (_b = data.clinicInfo) == null ? void 0 : _b.name,
@@ -16026,7 +16493,7 @@ import {
16026
16493
  deleteDoc as deleteDoc19,
16027
16494
  serverTimestamp as serverTimestamp26
16028
16495
  } from "firebase/firestore";
16029
- import { z as z25 } from "zod";
16496
+ import { z as z26 } from "zod";
16030
16497
  var ReviewService = class extends BaseService {
16031
16498
  constructor(db, auth, app) {
16032
16499
  super(db, auth, app);
@@ -16113,7 +16580,7 @@ var ReviewService = class extends BaseService {
16113
16580
  });
16114
16581
  return review;
16115
16582
  } catch (error) {
16116
- if (error instanceof z25.ZodError) {
16583
+ if (error instanceof z26.ZodError) {
16117
16584
  throw new Error(`Invalid review data: ${error.message}`);
16118
16585
  }
16119
16586
  throw error;
@@ -16143,7 +16610,7 @@ var ReviewService = class extends BaseService {
16143
16610
  where30("patientId", "==", patientId)
16144
16611
  );
16145
16612
  const snapshot = await getDocs30(q);
16146
- return snapshot.docs.map((doc37) => doc37.data());
16613
+ return snapshot.docs.map((doc38) => doc38.data());
16147
16614
  }
16148
16615
  /**
16149
16616
  * Gets all reviews for a specific clinic
@@ -16156,7 +16623,7 @@ var ReviewService = class extends BaseService {
16156
16623
  where30("clinicReview.clinicId", "==", clinicId)
16157
16624
  );
16158
16625
  const snapshot = await getDocs30(q);
16159
- return snapshot.docs.map((doc37) => doc37.data());
16626
+ return snapshot.docs.map((doc38) => doc38.data());
16160
16627
  }
16161
16628
  /**
16162
16629
  * Gets all reviews for a specific practitioner
@@ -16169,7 +16636,7 @@ var ReviewService = class extends BaseService {
16169
16636
  where30("practitionerReview.practitionerId", "==", practitionerId)
16170
16637
  );
16171
16638
  const snapshot = await getDocs30(q);
16172
- return snapshot.docs.map((doc37) => doc37.data());
16639
+ return snapshot.docs.map((doc38) => doc38.data());
16173
16640
  }
16174
16641
  /**
16175
16642
  * Gets all reviews for a specific procedure
@@ -16182,7 +16649,7 @@ var ReviewService = class extends BaseService {
16182
16649
  where30("procedureReview.procedureId", "==", procedureId)
16183
16650
  );
16184
16651
  const snapshot = await getDocs30(q);
16185
- return snapshot.docs.map((doc37) => doc37.data());
16652
+ return snapshot.docs.map((doc38) => doc38.data());
16186
16653
  }
16187
16654
  /**
16188
16655
  * Gets all reviews for a specific appointment
@@ -16288,7 +16755,11 @@ import {
16288
16755
  getDocs as getDocs31,
16289
16756
  query as query31,
16290
16757
  updateDoc as updateDoc30,
16291
- where as where31
16758
+ where as where31,
16759
+ limit as limit16,
16760
+ orderBy as orderBy18,
16761
+ startAfter as startAfter14,
16762
+ getCountFromServer as getCountFromServer2
16292
16763
  } from "firebase/firestore";
16293
16764
 
16294
16765
  // src/backoffice/types/brand.types.ts
@@ -16309,6 +16780,7 @@ var BrandService = class extends BaseService {
16309
16780
  const now = /* @__PURE__ */ new Date();
16310
16781
  const newBrand = {
16311
16782
  ...brand,
16783
+ name_lowercase: brand.name.toLowerCase(),
16312
16784
  createdAt: now,
16313
16785
  updatedAt: now,
16314
16786
  isActive: true
@@ -16317,15 +16789,69 @@ var BrandService = class extends BaseService {
16317
16789
  return { id: docRef.id, ...newBrand };
16318
16790
  }
16319
16791
  /**
16320
- * Gets all active brands
16792
+ * Gets a paginated list of active brands, optionally filtered by name.
16793
+ * @param rowsPerPage - The number of brands to fetch.
16794
+ * @param searchTerm - An optional string to filter brand names by (starts-with search).
16795
+ * @param lastVisible - An optional document snapshot to use as a cursor for pagination.
16321
16796
  */
16322
- async getAll() {
16323
- const q = query31(this.getBrandsRef(), where31("isActive", "==", true));
16797
+ async getAll(rowsPerPage, searchTerm, lastVisible) {
16798
+ const constraints = [
16799
+ where31("isActive", "==", true),
16800
+ orderBy18("name_lowercase")
16801
+ ];
16802
+ if (searchTerm) {
16803
+ const lowercasedSearchTerm = searchTerm.toLowerCase();
16804
+ constraints.push(where31("name_lowercase", ">=", lowercasedSearchTerm));
16805
+ constraints.push(
16806
+ where31("name_lowercase", "<=", lowercasedSearchTerm + "\uF8FF")
16807
+ );
16808
+ }
16809
+ if (lastVisible) {
16810
+ constraints.push(startAfter14(lastVisible));
16811
+ }
16812
+ constraints.push(limit16(rowsPerPage));
16813
+ const q = query31(this.getBrandsRef(), ...constraints);
16814
+ const snapshot = await getDocs31(q);
16815
+ const brands = snapshot.docs.map(
16816
+ (doc38) => ({
16817
+ id: doc38.id,
16818
+ ...doc38.data()
16819
+ })
16820
+ );
16821
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
16822
+ return { brands, lastVisible: newLastVisible };
16823
+ }
16824
+ /**
16825
+ * Gets the total count of active brands, optionally filtered by name.
16826
+ * @param searchTerm - An optional string to filter brand names by (starts-with search).
16827
+ */
16828
+ async getBrandsCount(searchTerm) {
16829
+ const constraints = [where31("isActive", "==", true)];
16830
+ if (searchTerm) {
16831
+ const lowercasedSearchTerm = searchTerm.toLowerCase();
16832
+ constraints.push(where31("name_lowercase", ">=", lowercasedSearchTerm));
16833
+ constraints.push(
16834
+ where31("name_lowercase", "<=", lowercasedSearchTerm + "\uF8FF")
16835
+ );
16836
+ }
16837
+ const q = query31(this.getBrandsRef(), ...constraints);
16838
+ const snapshot = await getCountFromServer2(q);
16839
+ return snapshot.data().count;
16840
+ }
16841
+ /**
16842
+ * Gets all active brands for filter dropdowns (not paginated).
16843
+ */
16844
+ async getAllForFilter() {
16845
+ const q = query31(
16846
+ this.getBrandsRef(),
16847
+ where31("isActive", "==", true),
16848
+ orderBy18("name")
16849
+ );
16324
16850
  const snapshot = await getDocs31(q);
16325
16851
  return snapshot.docs.map(
16326
- (doc37) => ({
16327
- id: doc37.id,
16328
- ...doc37.data()
16852
+ (doc38) => ({
16853
+ id: doc38.id,
16854
+ ...doc38.data()
16329
16855
  })
16330
16856
  );
16331
16857
  }
@@ -16337,6 +16863,9 @@ var BrandService = class extends BaseService {
16337
16863
  ...brand,
16338
16864
  updatedAt: /* @__PURE__ */ new Date()
16339
16865
  };
16866
+ if (brand.name) {
16867
+ updateData.name_lowercase = brand.name.toLowerCase();
16868
+ }
16340
16869
  const docRef = doc32(this.getBrandsRef(), brandId);
16341
16870
  await updateDoc30(docRef, updateData);
16342
16871
  return this.getById(brandId);
@@ -16368,9 +16897,13 @@ import {
16368
16897
  addDoc as addDoc4,
16369
16898
  collection as collection32,
16370
16899
  doc as doc33,
16900
+ getCountFromServer as getCountFromServer3,
16371
16901
  getDoc as getDoc35,
16372
16902
  getDocs as getDocs32,
16903
+ limit as limit17,
16904
+ orderBy as orderBy19,
16373
16905
  query as query32,
16906
+ startAfter as startAfter15,
16374
16907
  updateDoc as updateDoc31,
16375
16908
  where as where32
16376
16909
  } from "firebase/firestore";
@@ -16403,37 +16936,87 @@ var CategoryService = class extends BaseService {
16403
16936
  return { id: docRef.id, ...newCategory };
16404
16937
  }
16405
16938
  /**
16406
- * Vraća sve aktivne kategorije
16407
- * @returns Lista aktivnih kategorija
16939
+ * Returns counts of categories for each family.
16940
+ * @param active - Whether to count active or inactive categories.
16941
+ * @returns A record mapping family to category count.
16408
16942
  */
16409
- async getAll() {
16943
+ async getCategoryCounts(active = true) {
16944
+ const counts = {};
16945
+ const families = Object.values(ProcedureFamily);
16946
+ for (const family of families) {
16947
+ const q = query32(
16948
+ this.categoriesRef,
16949
+ where32("family", "==", family),
16950
+ where32("isActive", "==", active)
16951
+ );
16952
+ const snapshot = await getCountFromServer3(q);
16953
+ counts[family] = snapshot.data().count;
16954
+ }
16955
+ return counts;
16956
+ }
16957
+ /**
16958
+ * Vraća sve kategorije za potrebe filtera (bez paginacije)
16959
+ * @returns Lista svih aktivnih kategorija
16960
+ */
16961
+ async getAllForFilter() {
16410
16962
  const q = query32(this.categoriesRef, where32("isActive", "==", true));
16411
16963
  const snapshot = await getDocs32(q);
16412
16964
  return snapshot.docs.map(
16413
- (doc37) => ({
16414
- id: doc37.id,
16415
- ...doc37.data()
16965
+ (doc38) => ({
16966
+ id: doc38.id,
16967
+ ...doc38.data()
16968
+ })
16969
+ );
16970
+ }
16971
+ /**
16972
+ * Vraća sve kategorije sa paginacijom
16973
+ * @param options - Pagination and filter options
16974
+ * @returns Lista kategorija i poslednji vidljiv dokument
16975
+ */
16976
+ async getAll(options = {}) {
16977
+ const { active = true, limit: queryLimit = 10, lastVisible } = options;
16978
+ const constraints = [
16979
+ where32("isActive", "==", active),
16980
+ orderBy19("name"),
16981
+ queryLimit ? limit17(queryLimit) : void 0,
16982
+ lastVisible ? startAfter15(lastVisible) : void 0
16983
+ ].filter((c) => !!c);
16984
+ const q = query32(this.categoriesRef, ...constraints);
16985
+ const snapshot = await getDocs32(q);
16986
+ const categories = snapshot.docs.map(
16987
+ (doc38) => ({
16988
+ id: doc38.id,
16989
+ ...doc38.data()
16416
16990
  })
16417
16991
  );
16992
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
16993
+ return { categories, lastVisible: newLastVisible };
16418
16994
  }
16419
16995
  /**
16420
- * Vraća sve aktivne kategorije za određenu familiju procedura
16996
+ * Vraća sve aktivne kategorije za određenu familiju procedura sa paginacijom
16421
16997
  * @param family - Familija procedura (aesthetics/surgery)
16998
+ * @param options - Pagination options
16422
16999
  * @returns Lista kategorija koje pripadaju traženoj familiji
16423
17000
  */
16424
- async getAllByFamily(family) {
16425
- const q = query32(
16426
- this.categoriesRef,
17001
+ async getAllByFamily(family, options = {}) {
17002
+ const { active = true, limit: queryLimit = 10, lastVisible } = options;
17003
+ const constraints = [
16427
17004
  where32("family", "==", family),
16428
- where32("isActive", "==", true)
16429
- );
17005
+ where32("isActive", "==", active),
17006
+ orderBy19("name"),
17007
+ queryLimit ? limit17(queryLimit) : void 0,
17008
+ lastVisible ? startAfter15(lastVisible) : void 0
17009
+ ].filter((c) => !!c);
17010
+ const q = query32(this.categoriesRef, ...constraints);
16430
17011
  const snapshot = await getDocs32(q);
16431
- return snapshot.docs.map(
16432
- (doc37) => ({
16433
- id: doc37.id,
16434
- ...doc37.data()
17012
+ const categories = snapshot.docs.map(
17013
+ (doc38) => ({
17014
+ id: doc38.id,
17015
+ ...doc38.data()
16435
17016
  })
16436
17017
  );
17018
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
17019
+ return { categories, lastVisible: newLastVisible };
16437
17020
  }
16438
17021
  /**
16439
17022
  * Ažurira postojeću kategoriju
@@ -16457,6 +17040,13 @@ var CategoryService = class extends BaseService {
16457
17040
  async delete(id) {
16458
17041
  await this.update(id, { isActive: false });
16459
17042
  }
17043
+ /**
17044
+ * Reactivates a category by setting its isActive flag to true.
17045
+ * @param id - The ID of the category to reactivate.
17046
+ */
17047
+ async reactivate(id) {
17048
+ await this.update(id, { isActive: true });
17049
+ }
16460
17050
  /**
16461
17051
  * Vraća kategoriju po ID-u
16462
17052
  * @param id - ID tražene kategorije
@@ -16477,10 +17067,17 @@ var CategoryService = class extends BaseService {
16477
17067
  import {
16478
17068
  addDoc as addDoc5,
16479
17069
  collection as collection33,
17070
+ collectionGroup as collectionGroup2,
17071
+ deleteDoc as deleteDoc20,
16480
17072
  doc as doc34,
17073
+ getCountFromServer as getCountFromServer4,
16481
17074
  getDoc as getDoc36,
16482
17075
  getDocs as getDocs33,
17076
+ limit as limit18,
17077
+ orderBy as orderBy20,
16483
17078
  query as query33,
17079
+ setDoc as setDoc28,
17080
+ startAfter as startAfter16,
16484
17081
  updateDoc as updateDoc32,
16485
17082
  where as where33
16486
17083
  } from "firebase/firestore";
@@ -16524,20 +17121,110 @@ var SubcategoryService = class extends BaseService {
16524
17121
  return { id: docRef.id, ...newSubcategory };
16525
17122
  }
16526
17123
  /**
16527
- * Vraća sve aktivne podkategorije za određenu kategoriju
17124
+ * Returns counts of subcategories for all categories.
17125
+ * @param active - Whether to count active or inactive subcategories.
17126
+ * @returns A record mapping category ID to subcategory count.
17127
+ */
17128
+ async getSubcategoryCounts(active = true) {
17129
+ const categoriesRef = collection33(this.db, CATEGORIES_COLLECTION);
17130
+ const categoriesSnapshot = await getDocs33(categoriesRef);
17131
+ const counts = {};
17132
+ for (const categoryDoc of categoriesSnapshot.docs) {
17133
+ const categoryId = categoryDoc.id;
17134
+ const subcategoriesRef = this.getSubcategoriesRef(categoryId);
17135
+ const q = query33(subcategoriesRef, where33("isActive", "==", active));
17136
+ const snapshot = await getCountFromServer4(q);
17137
+ counts[categoryId] = snapshot.data().count;
17138
+ }
17139
+ return counts;
17140
+ }
17141
+ /**
17142
+ * Vraća sve aktivne podkategorije za određenu kategoriju sa paginacijom
16528
17143
  * @param categoryId - ID kategorije čije podkategorije tražimo
16529
- * @returns Lista aktivnih podkategorija
17144
+ * @param options - Pagination options
17145
+ * @returns Lista aktivnih podkategorija i poslednji vidljiv dokument
16530
17146
  */
16531
- async getAllByCategoryId(categoryId) {
17147
+ async getAllByCategoryId(categoryId, options = {}) {
17148
+ const { active = true, limit: queryLimit = 10, lastVisible } = options;
17149
+ const constraints = [
17150
+ where33("isActive", "==", active),
17151
+ orderBy20("name"),
17152
+ queryLimit ? limit18(queryLimit) : void 0,
17153
+ lastVisible ? startAfter16(lastVisible) : void 0
17154
+ ].filter((c) => !!c);
17155
+ const q = query33(this.getSubcategoriesRef(categoryId), ...constraints);
17156
+ const querySnapshot = await getDocs33(q);
17157
+ const subcategories = querySnapshot.docs.map(
17158
+ (doc38) => ({
17159
+ id: doc38.id,
17160
+ ...doc38.data()
17161
+ })
17162
+ );
17163
+ const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
17164
+ return { subcategories, lastVisible: newLastVisible };
17165
+ }
17166
+ /**
17167
+ * Vraća sve podkategorije sa paginacijom koristeći collection group query.
17168
+ * NOTE: This query requires a composite index in Firestore on the 'subcategories' collection group.
17169
+ * The index should be on 'isActive' (ascending) and 'name' (ascending).
17170
+ * Firestore will provide a link to create this index in the console error if it's missing.
17171
+ * @param options - Pagination options
17172
+ * @returns Lista podkategorija i poslednji vidljiv dokument
17173
+ */
17174
+ async getAll(options = {}) {
17175
+ const { active = true, limit: queryLimit = 10, lastVisible } = options;
17176
+ const constraints = [
17177
+ where33("isActive", "==", active),
17178
+ orderBy20("name"),
17179
+ queryLimit ? limit18(queryLimit) : void 0,
17180
+ lastVisible ? startAfter16(lastVisible) : void 0
17181
+ ].filter((c) => !!c);
17182
+ const q = query33(
17183
+ collectionGroup2(this.db, SUBCATEGORIES_COLLECTION),
17184
+ ...constraints
17185
+ );
17186
+ const querySnapshot = await getDocs33(q);
17187
+ const subcategories = querySnapshot.docs.map(
17188
+ (doc38) => ({
17189
+ id: doc38.id,
17190
+ ...doc38.data()
17191
+ })
17192
+ );
17193
+ const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
17194
+ return { subcategories, lastVisible: newLastVisible };
17195
+ }
17196
+ /**
17197
+ * Vraća sve subkategorije za određenu kategoriju za potrebe filtera (bez paginacije)
17198
+ * @param categoryId - ID kategorije čije subkategorije tražimo
17199
+ * @returns Lista svih aktivnih subkategorija
17200
+ */
17201
+ async getAllForFilterByCategoryId(categoryId) {
16532
17202
  const q = query33(
16533
17203
  this.getSubcategoriesRef(categoryId),
16534
17204
  where33("isActive", "==", true)
16535
17205
  );
16536
- const snapshot = await getDocs33(q);
16537
- return snapshot.docs.map(
16538
- (doc37) => ({
16539
- id: doc37.id,
16540
- ...doc37.data()
17206
+ const querySnapshot = await getDocs33(q);
17207
+ return querySnapshot.docs.map(
17208
+ (doc38) => ({
17209
+ id: doc38.id,
17210
+ ...doc38.data()
17211
+ })
17212
+ );
17213
+ }
17214
+ /**
17215
+ * Vraća sve subkategorije za potrebe filtera (bez paginacije)
17216
+ * @returns Lista svih aktivnih subkategorija
17217
+ */
17218
+ async getAllForFilter() {
17219
+ const q = query33(
17220
+ collectionGroup2(this.db, SUBCATEGORIES_COLLECTION),
17221
+ where33("isActive", "==", true)
17222
+ );
17223
+ const querySnapshot = await getDocs33(q);
17224
+ return querySnapshot.docs.map(
17225
+ (doc38) => ({
17226
+ id: doc38.id,
17227
+ ...doc38.data()
16541
17228
  })
16542
17229
  );
16543
17230
  }
@@ -16549,13 +17236,42 @@ var SubcategoryService = class extends BaseService {
16549
17236
  * @returns Ažurirana podkategorija
16550
17237
  */
16551
17238
  async update(categoryId, subcategoryId, subcategory) {
16552
- const updateData = {
16553
- ...subcategory,
16554
- updatedAt: /* @__PURE__ */ new Date()
16555
- };
16556
- const docRef = doc34(this.getSubcategoriesRef(categoryId), subcategoryId);
16557
- await updateDoc32(docRef, updateData);
16558
- return this.getById(categoryId, subcategoryId);
17239
+ const newCategoryId = subcategory.categoryId;
17240
+ if (newCategoryId && newCategoryId !== categoryId) {
17241
+ const oldDocRef = doc34(
17242
+ this.getSubcategoriesRef(categoryId),
17243
+ subcategoryId
17244
+ );
17245
+ const docSnap = await getDoc36(oldDocRef);
17246
+ if (!docSnap.exists()) {
17247
+ throw new Error("Subcategory to update does not exist.");
17248
+ }
17249
+ const existingData = docSnap.data();
17250
+ const newData = {
17251
+ ...existingData,
17252
+ ...subcategory,
17253
+ categoryId: newCategoryId,
17254
+ // Ensure categoryId is updated
17255
+ createdAt: existingData.createdAt,
17256
+ // Preserve original creation date
17257
+ updatedAt: /* @__PURE__ */ new Date()
17258
+ };
17259
+ const newDocRef = doc34(
17260
+ this.getSubcategoriesRef(newCategoryId),
17261
+ subcategoryId
17262
+ );
17263
+ await setDoc28(newDocRef, newData);
17264
+ await deleteDoc20(oldDocRef);
17265
+ return { id: subcategoryId, ...newData };
17266
+ } else {
17267
+ const updateData = {
17268
+ ...subcategory,
17269
+ updatedAt: /* @__PURE__ */ new Date()
17270
+ };
17271
+ const docRef = doc34(this.getSubcategoriesRef(categoryId), subcategoryId);
17272
+ await updateDoc32(docRef, updateData);
17273
+ return this.getById(categoryId, subcategoryId);
17274
+ }
16559
17275
  }
16560
17276
  /**
16561
17277
  * Soft delete podkategorije (postavlja isActive na false)
@@ -16565,6 +17281,14 @@ var SubcategoryService = class extends BaseService {
16565
17281
  async delete(categoryId, subcategoryId) {
16566
17282
  await this.update(categoryId, subcategoryId, { isActive: false });
16567
17283
  }
17284
+ /**
17285
+ * Reactivates a subcategory by setting its isActive flag to true.
17286
+ * @param categoryId - The ID of the category to which the subcategory belongs.
17287
+ * @param subcategoryId - The ID of the subcategory to reactivate.
17288
+ */
17289
+ async reactivate(categoryId, subcategoryId) {
17290
+ await this.update(categoryId, subcategoryId, { isActive: true });
17291
+ }
16568
17292
  /**
16569
17293
  * Vraća podkategoriju po ID-u
16570
17294
  * @param categoryId - ID kategorije kojoj pripada podkategorija
@@ -16589,7 +17313,10 @@ import {
16589
17313
  doc as doc35,
16590
17314
  getDoc as getDoc37,
16591
17315
  getDocs as getDocs34,
17316
+ limit as limit19,
17317
+ orderBy as orderBy21,
16592
17318
  query as query34,
17319
+ startAfter as startAfter17,
16593
17320
  updateDoc as updateDoc33,
16594
17321
  where as where34,
16595
17322
  arrayUnion as arrayUnion9,
@@ -16601,137 +17328,185 @@ var DEFAULT_CERTIFICATION_REQUIREMENT = {
16601
17328
  };
16602
17329
  var TechnologyService = class extends BaseService {
16603
17330
  /**
16604
- * Vraća referencu na Firestore kolekciju tehnologija
17331
+ * Reference to the Firestore collection of technologies.
16605
17332
  */
16606
- getTechnologiesRef() {
17333
+ get technologiesRef() {
16607
17334
  return collection34(this.db, TECHNOLOGIES_COLLECTION);
16608
17335
  }
16609
17336
  /**
16610
- * Kreira novu tehnologiju
16611
- * @param technology - Podaci za novu tehnologiju
16612
- * @returns Kreirana tehnologija sa generisanim ID-em
17337
+ * Creates a new technology.
17338
+ * @param technology - Data for the new technology.
17339
+ * @returns The created technology with its generated ID.
16613
17340
  */
16614
17341
  async create(technology) {
16615
17342
  const now = /* @__PURE__ */ new Date();
16616
17343
  const newTechnology = {
16617
- ...technology,
16618
- createdAt: now,
16619
- updatedAt: now,
16620
- isActive: true,
16621
- requirements: technology.requirements || {
16622
- pre: [],
16623
- post: []
16624
- },
17344
+ name: technology.name,
17345
+ description: technology.description,
17346
+ family: technology.family,
17347
+ categoryId: technology.categoryId,
17348
+ subcategoryId: technology.subcategoryId,
17349
+ requirements: technology.requirements || { pre: [], post: [] },
16625
17350
  blockingConditions: technology.blockingConditions || [],
16626
17351
  contraindications: technology.contraindications || [],
16627
17352
  benefits: technology.benefits || [],
16628
- certificationRequirement: technology.certificationRequirement || DEFAULT_CERTIFICATION_REQUIREMENT
17353
+ certificationRequirement: technology.certificationRequirement || DEFAULT_CERTIFICATION_REQUIREMENT,
17354
+ documentationTemplates: technology.documentationTemplates || [],
17355
+ isActive: true,
17356
+ createdAt: now,
17357
+ updatedAt: now
16629
17358
  };
16630
- const docRef = await addDoc6(this.getTechnologiesRef(), newTechnology);
17359
+ if (technology.technicalDetails) {
17360
+ newTechnology.technicalDetails = technology.technicalDetails;
17361
+ }
17362
+ const docRef = await addDoc6(this.technologiesRef, newTechnology);
16631
17363
  return { id: docRef.id, ...newTechnology };
16632
17364
  }
16633
17365
  /**
16634
- * Vraća sve aktivne tehnologije
16635
- * @returns Lista aktivnih tehnologija
17366
+ * Returns counts of technologies for each subcategory.
17367
+ * @param active - Whether to count active or inactive technologies.
17368
+ * @returns A record mapping subcategory ID to technology count.
16636
17369
  */
16637
- async getAll() {
16638
- const q = query34(this.getTechnologiesRef(), where34("isActive", "==", true));
17370
+ async getTechnologyCounts(active = true) {
17371
+ const q = query34(this.technologiesRef, where34("isActive", "==", active));
16639
17372
  const snapshot = await getDocs34(q);
16640
- return snapshot.docs.map(
16641
- (doc37) => ({
16642
- id: doc37.id,
16643
- ...doc37.data()
16644
- })
16645
- );
17373
+ const counts = {};
17374
+ snapshot.docs.forEach((doc38) => {
17375
+ const tech = doc38.data();
17376
+ counts[tech.subcategoryId] = (counts[tech.subcategoryId] || 0) + 1;
17377
+ });
17378
+ return counts;
16646
17379
  }
16647
17380
  /**
16648
- * Vraća sve aktivne tehnologije za određenu familiju
16649
- * @param family - Familija procedura
16650
- * @returns Lista aktivnih tehnologija
17381
+ * Returns counts of technologies for each category.
17382
+ * @param active - Whether to count active or inactive technologies.
17383
+ * @returns A record mapping category ID to technology count.
16651
17384
  */
16652
- async getAllByFamily(family) {
16653
- const q = query34(
16654
- this.getTechnologiesRef(),
16655
- where34("isActive", "==", true),
16656
- where34("family", "==", family)
16657
- );
17385
+ async getTechnologyCountsByCategory(active = true) {
17386
+ const q = query34(this.technologiesRef, where34("isActive", "==", active));
16658
17387
  const snapshot = await getDocs34(q);
16659
- return snapshot.docs.map(
16660
- (doc37) => ({
16661
- id: doc37.id,
16662
- ...doc37.data()
17388
+ const counts = {};
17389
+ snapshot.docs.forEach((doc38) => {
17390
+ const tech = doc38.data();
17391
+ counts[tech.categoryId] = (counts[tech.categoryId] || 0) + 1;
17392
+ });
17393
+ return counts;
17394
+ }
17395
+ /**
17396
+ * Returns all technologies with pagination.
17397
+ * @param options - Pagination and filter options.
17398
+ * @returns A list of technologies and the last visible document.
17399
+ */
17400
+ async getAll(options = {}) {
17401
+ const { active = true, limit: queryLimit = 10, lastVisible } = options;
17402
+ const constraints = [
17403
+ where34("isActive", "==", active),
17404
+ orderBy21("name"),
17405
+ queryLimit ? limit19(queryLimit) : void 0,
17406
+ lastVisible ? startAfter17(lastVisible) : void 0
17407
+ ].filter((c) => !!c);
17408
+ const q = query34(this.technologiesRef, ...constraints);
17409
+ const snapshot = await getDocs34(q);
17410
+ const technologies = snapshot.docs.map(
17411
+ (doc38) => ({
17412
+ id: doc38.id,
17413
+ ...doc38.data()
16663
17414
  })
16664
17415
  );
17416
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
17417
+ return { technologies, lastVisible: newLastVisible };
16665
17418
  }
16666
17419
  /**
16667
- * Vraća sve aktivne tehnologije za određenu kategoriju
16668
- * @param categoryId - ID kategorije
16669
- * @returns Lista aktivnih tehnologija
17420
+ * Returns all technologies for a specific category with pagination.
17421
+ * @param categoryId - The ID of the category.
17422
+ * @param options - Pagination options.
17423
+ * @returns A list of technologies for the specified category.
16670
17424
  */
16671
- async getAllByCategoryId(categoryId) {
16672
- const q = query34(
16673
- this.getTechnologiesRef(),
16674
- where34("isActive", "==", true),
16675
- where34("categoryId", "==", categoryId)
16676
- );
17425
+ async getAllByCategoryId(categoryId, options = {}) {
17426
+ const { active = true, limit: queryLimit = 10, lastVisible } = options;
17427
+ const constraints = [
17428
+ where34("categoryId", "==", categoryId),
17429
+ where34("isActive", "==", active),
17430
+ orderBy21("name"),
17431
+ queryLimit ? limit19(queryLimit) : void 0,
17432
+ lastVisible ? startAfter17(lastVisible) : void 0
17433
+ ].filter((c) => !!c);
17434
+ const q = query34(this.technologiesRef, ...constraints);
16677
17435
  const snapshot = await getDocs34(q);
16678
- return snapshot.docs.map(
16679
- (doc37) => ({
16680
- id: doc37.id,
16681
- ...doc37.data()
17436
+ const technologies = snapshot.docs.map(
17437
+ (doc38) => ({
17438
+ id: doc38.id,
17439
+ ...doc38.data()
16682
17440
  })
16683
17441
  );
17442
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
17443
+ return { technologies, lastVisible: newLastVisible };
16684
17444
  }
16685
17445
  /**
16686
- * Vraća sve aktivne tehnologije za određenu podkategoriju
16687
- * @param subcategoryId - ID podkategorije
16688
- * @returns Lista aktivnih tehnologija
17446
+ * Returns all technologies for a specific subcategory with pagination.
17447
+ * @param subcategoryId - The ID of the subcategory.
17448
+ * @param options - Pagination options.
17449
+ * @returns A list of technologies for the specified subcategory.
16689
17450
  */
16690
- async getAllBySubcategoryId(subcategoryId) {
16691
- const q = query34(
16692
- this.getTechnologiesRef(),
16693
- where34("isActive", "==", true),
16694
- where34("subcategoryId", "==", subcategoryId)
16695
- );
17451
+ async getAllBySubcategoryId(subcategoryId, options = {}) {
17452
+ const { active = true, limit: queryLimit = 10, lastVisible } = options;
17453
+ const constraints = [
17454
+ where34("subcategoryId", "==", subcategoryId),
17455
+ where34("isActive", "==", active),
17456
+ orderBy21("name"),
17457
+ queryLimit ? limit19(queryLimit) : void 0,
17458
+ lastVisible ? startAfter17(lastVisible) : void 0
17459
+ ].filter((c) => !!c);
17460
+ const q = query34(this.technologiesRef, ...constraints);
16696
17461
  const snapshot = await getDocs34(q);
16697
- return snapshot.docs.map(
16698
- (doc37) => ({
16699
- id: doc37.id,
16700
- ...doc37.data()
17462
+ const technologies = snapshot.docs.map(
17463
+ (doc38) => ({
17464
+ id: doc38.id,
17465
+ ...doc38.data()
16701
17466
  })
16702
17467
  );
17468
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
17469
+ return { technologies, lastVisible: newLastVisible };
16703
17470
  }
16704
17471
  /**
16705
- * Ažurira postojeću tehnologiju
16706
- * @param technologyId - ID tehnologije
16707
- * @param technology - Novi podaci za tehnologiju
16708
- * @returns Ažurirana tehnologija
17472
+ * Updates an existing technology.
17473
+ * @param id - The ID of the technology to update.
17474
+ * @param technology - New data for the technology.
17475
+ * @returns The updated technology.
16709
17476
  */
16710
- async update(technologyId, technology) {
16711
- const updateData = {
16712
- ...technology,
16713
- updatedAt: /* @__PURE__ */ new Date()
16714
- };
16715
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17477
+ async update(id, technology) {
17478
+ const updateData = { ...technology };
17479
+ Object.keys(updateData).forEach((key) => {
17480
+ if (updateData[key] === void 0) {
17481
+ delete updateData[key];
17482
+ }
17483
+ });
17484
+ updateData.updatedAt = /* @__PURE__ */ new Date();
17485
+ const docRef = doc35(this.technologiesRef, id);
16716
17486
  await updateDoc33(docRef, updateData);
16717
- return this.getById(technologyId);
17487
+ return this.getById(id);
16718
17488
  }
16719
17489
  /**
16720
- * Soft delete tehnologije (postavlja isActive na false)
16721
- * @param technologyId - ID tehnologije koja se briše
17490
+ * Soft deletes a technology.
17491
+ * @param id - The ID of the technology to delete.
16722
17492
  */
16723
- async delete(technologyId) {
16724
- await this.update(technologyId, {
16725
- isActive: false
16726
- });
17493
+ async delete(id) {
17494
+ await this.update(id, { isActive: false });
17495
+ }
17496
+ /**
17497
+ * Reactivates a technology.
17498
+ * @param id - The ID of the technology to reactivate.
17499
+ */
17500
+ async reactivate(id) {
17501
+ await this.update(id, { isActive: true });
16727
17502
  }
16728
17503
  /**
16729
- * Vraća tehnologiju po ID-u
16730
- * @param technologyId - ID tražene tehnologije
16731
- * @returns Tehnologija ili null ako ne postoji
17504
+ * Returns a technology by its ID.
17505
+ * @param id - The ID of the requested technology.
17506
+ * @returns The technology or null if it doesn't exist.
16732
17507
  */
16733
- async getById(technologyId) {
16734
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17508
+ async getById(id) {
17509
+ const docRef = doc35(this.technologiesRef, id);
16735
17510
  const docSnap = await getDoc37(docRef);
16736
17511
  if (!docSnap.exists()) return null;
16737
17512
  return {
@@ -16746,7 +17521,7 @@ var TechnologyService = class extends BaseService {
16746
17521
  * @returns Ažurirana tehnologija sa novim zahtevom
16747
17522
  */
16748
17523
  async addRequirement(technologyId, requirement) {
16749
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17524
+ const docRef = doc35(this.technologiesRef, technologyId);
16750
17525
  const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
16751
17526
  await updateDoc33(docRef, {
16752
17527
  [requirementType]: arrayUnion9(requirement),
@@ -16761,7 +17536,7 @@ var TechnologyService = class extends BaseService {
16761
17536
  * @returns Ažurirana tehnologija bez uklonjenog zahteva
16762
17537
  */
16763
17538
  async removeRequirement(technologyId, requirement) {
16764
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17539
+ const docRef = doc35(this.technologiesRef, technologyId);
16765
17540
  const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
16766
17541
  await updateDoc33(docRef, {
16767
17542
  [requirementType]: arrayRemove8(requirement),
@@ -16801,7 +17576,7 @@ var TechnologyService = class extends BaseService {
16801
17576
  * @returns Ažurirana tehnologija
16802
17577
  */
16803
17578
  async addBlockingCondition(technologyId, condition) {
16804
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17579
+ const docRef = doc35(this.technologiesRef, technologyId);
16805
17580
  await updateDoc33(docRef, {
16806
17581
  blockingConditions: arrayUnion9(condition),
16807
17582
  updatedAt: /* @__PURE__ */ new Date()
@@ -16815,7 +17590,7 @@ var TechnologyService = class extends BaseService {
16815
17590
  * @returns Ažurirana tehnologija
16816
17591
  */
16817
17592
  async removeBlockingCondition(technologyId, condition) {
16818
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17593
+ const docRef = doc35(this.technologiesRef, technologyId);
16819
17594
  await updateDoc33(docRef, {
16820
17595
  blockingConditions: arrayRemove8(condition),
16821
17596
  updatedAt: /* @__PURE__ */ new Date()
@@ -16829,9 +17604,17 @@ var TechnologyService = class extends BaseService {
16829
17604
  * @returns Ažurirana tehnologija
16830
17605
  */
16831
17606
  async addContraindication(technologyId, contraindication) {
16832
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17607
+ const docRef = doc35(this.technologiesRef, technologyId);
17608
+ const technology = await this.getById(technologyId);
17609
+ if (!technology) {
17610
+ throw new Error(`Technology with id ${technologyId} not found`);
17611
+ }
17612
+ const existingContraindications = technology.contraindications || [];
17613
+ if (existingContraindications.some((c) => c.id === contraindication.id)) {
17614
+ return technology;
17615
+ }
16833
17616
  await updateDoc33(docRef, {
16834
- contraindications: arrayUnion9(contraindication),
17617
+ contraindications: [...existingContraindications, contraindication],
16835
17618
  updatedAt: /* @__PURE__ */ new Date()
16836
17619
  });
16837
17620
  return this.getById(technologyId);
@@ -16843,9 +17626,45 @@ var TechnologyService = class extends BaseService {
16843
17626
  * @returns Ažurirana tehnologija
16844
17627
  */
16845
17628
  async removeContraindication(technologyId, contraindication) {
16846
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17629
+ const docRef = doc35(this.technologiesRef, technologyId);
17630
+ const technology = await this.getById(technologyId);
17631
+ if (!technology) {
17632
+ throw new Error(`Technology with id ${technologyId} not found`);
17633
+ }
17634
+ const updatedContraindications = (technology.contraindications || []).filter((c) => c.id !== contraindication.id);
16847
17635
  await updateDoc33(docRef, {
16848
- contraindications: arrayRemove8(contraindication),
17636
+ contraindications: updatedContraindications,
17637
+ updatedAt: /* @__PURE__ */ new Date()
17638
+ });
17639
+ return this.getById(technologyId);
17640
+ }
17641
+ /**
17642
+ * Updates an existing contraindication in a technology's list.
17643
+ * If the contraindication does not exist, it will not be added.
17644
+ * @param technologyId - ID of the technology
17645
+ * @param contraindication - The updated contraindication object
17646
+ * @returns The updated technology
17647
+ */
17648
+ async updateContraindication(technologyId, contraindication) {
17649
+ const docRef = doc35(this.technologiesRef, technologyId);
17650
+ const technology = await this.getById(technologyId);
17651
+ if (!technology) {
17652
+ throw new Error(`Technology with id ${technologyId} not found`);
17653
+ }
17654
+ const contraindications = technology.contraindications || [];
17655
+ const index = contraindications.findIndex(
17656
+ (c) => c.id === contraindication.id
17657
+ );
17658
+ if (index === -1) {
17659
+ console.warn(
17660
+ `Contraindication with id ${contraindication.id} not found for technology ${technologyId}. No update performed.`
17661
+ );
17662
+ return technology;
17663
+ }
17664
+ const updatedContraindications = [...contraindications];
17665
+ updatedContraindications[index] = contraindication;
17666
+ await updateDoc33(docRef, {
17667
+ contraindications: updatedContraindications,
16849
17668
  updatedAt: /* @__PURE__ */ new Date()
16850
17669
  });
16851
17670
  return this.getById(technologyId);
@@ -16857,9 +17676,17 @@ var TechnologyService = class extends BaseService {
16857
17676
  * @returns Ažurirana tehnologija
16858
17677
  */
16859
17678
  async addBenefit(technologyId, benefit) {
16860
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17679
+ const docRef = doc35(this.technologiesRef, technologyId);
17680
+ const technology = await this.getById(technologyId);
17681
+ if (!technology) {
17682
+ throw new Error(`Technology with id ${technologyId} not found`);
17683
+ }
17684
+ const existingBenefits = technology.benefits || [];
17685
+ if (existingBenefits.some((b) => b.id === benefit.id)) {
17686
+ return technology;
17687
+ }
16861
17688
  await updateDoc33(docRef, {
16862
- benefits: arrayUnion9(benefit),
17689
+ benefits: [...existingBenefits, benefit],
16863
17690
  updatedAt: /* @__PURE__ */ new Date()
16864
17691
  });
16865
17692
  return this.getById(technologyId);
@@ -16871,9 +17698,45 @@ var TechnologyService = class extends BaseService {
16871
17698
  * @returns Ažurirana tehnologija
16872
17699
  */
16873
17700
  async removeBenefit(technologyId, benefit) {
16874
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17701
+ const docRef = doc35(this.technologiesRef, technologyId);
17702
+ const technology = await this.getById(technologyId);
17703
+ if (!technology) {
17704
+ throw new Error(`Technology with id ${technologyId} not found`);
17705
+ }
17706
+ const updatedBenefits = (technology.benefits || []).filter(
17707
+ (b) => b.id !== benefit.id
17708
+ );
17709
+ await updateDoc33(docRef, {
17710
+ benefits: updatedBenefits,
17711
+ updatedAt: /* @__PURE__ */ new Date()
17712
+ });
17713
+ return this.getById(technologyId);
17714
+ }
17715
+ /**
17716
+ * Updates an existing benefit in a technology's list.
17717
+ * If the benefit does not exist, it will not be added.
17718
+ * @param technologyId - ID of the technology
17719
+ * @param benefit - The updated benefit object
17720
+ * @returns The updated technology
17721
+ */
17722
+ async updateBenefit(technologyId, benefit) {
17723
+ const docRef = doc35(this.technologiesRef, technologyId);
17724
+ const technology = await this.getById(technologyId);
17725
+ if (!technology) {
17726
+ throw new Error(`Technology with id ${technologyId} not found`);
17727
+ }
17728
+ const benefits = technology.benefits || [];
17729
+ const index = benefits.findIndex((b) => b.id === benefit.id);
17730
+ if (index === -1) {
17731
+ console.warn(
17732
+ `Benefit with id ${benefit.id} not found for technology ${technologyId}. No update performed.`
17733
+ );
17734
+ return technology;
17735
+ }
17736
+ const updatedBenefits = [...benefits];
17737
+ updatedBenefits[index] = benefit;
16875
17738
  await updateDoc33(docRef, {
16876
- benefits: arrayRemove8(benefit),
17739
+ benefits: updatedBenefits,
16877
17740
  updatedAt: /* @__PURE__ */ new Date()
16878
17741
  });
16879
17742
  return this.getById(technologyId);
@@ -16912,7 +17775,7 @@ var TechnologyService = class extends BaseService {
16912
17775
  * @returns Ažurirana tehnologija
16913
17776
  */
16914
17777
  async updateCertificationRequirement(technologyId, certificationRequirement) {
16915
- const docRef = doc35(this.getTechnologiesRef(), technologyId);
17778
+ const docRef = doc35(this.technologiesRef, technologyId);
16916
17779
  await updateDoc33(docRef, {
16917
17780
  certificationRequirement,
16918
17781
  updatedAt: /* @__PURE__ */ new Date()
@@ -16990,7 +17853,7 @@ var TechnologyService = class extends BaseService {
16990
17853
  */
16991
17854
  async getAllowedTechnologies(practitioner) {
16992
17855
  const allTechnologies = await this.getAll();
16993
- const allowedTechnologies = allTechnologies.filter(
17856
+ const allowedTechnologies = allTechnologies.technologies.filter(
16994
17857
  (technology) => this.validateCertification(
16995
17858
  technology.certificationRequirement,
16996
17859
  practitioner.certification
@@ -17010,18 +17873,61 @@ var TechnologyService = class extends BaseService {
17010
17873
  subcategories
17011
17874
  };
17012
17875
  }
17876
+ /**
17877
+ * Gets all active technologies for a subcategory for filter dropdowns.
17878
+ * @param categoryId - The ID of the parent category.
17879
+ * @param subcategoryId - The ID of the subcategory.
17880
+ */
17881
+ async getAllForFilterBySubcategoryId(categoryId, subcategoryId) {
17882
+ const q = query34(
17883
+ collection34(this.db, TECHNOLOGIES_COLLECTION),
17884
+ where34("isActive", "==", true),
17885
+ where34("categoryId", "==", categoryId),
17886
+ where34("subcategoryId", "==", subcategoryId),
17887
+ orderBy21("name")
17888
+ );
17889
+ const snapshot = await getDocs34(q);
17890
+ return snapshot.docs.map(
17891
+ (doc38) => ({
17892
+ id: doc38.id,
17893
+ ...doc38.data()
17894
+ })
17895
+ );
17896
+ }
17897
+ /**
17898
+ * Gets all active technologies for filter dropdowns.
17899
+ */
17900
+ async getAllForFilter() {
17901
+ const q = query34(
17902
+ collection34(this.db, TECHNOLOGIES_COLLECTION),
17903
+ where34("isActive", "==", true),
17904
+ orderBy21("name")
17905
+ );
17906
+ const snapshot = await getDocs34(q);
17907
+ return snapshot.docs.map(
17908
+ (doc38) => ({
17909
+ id: doc38.id,
17910
+ ...doc38.data()
17911
+ })
17912
+ );
17913
+ }
17013
17914
  };
17014
17915
 
17015
17916
  // src/backoffice/services/product.service.ts
17016
17917
  import {
17017
17918
  addDoc as addDoc7,
17018
17919
  collection as collection35,
17920
+ collectionGroup as collectionGroup3,
17019
17921
  doc as doc36,
17020
17922
  getDoc as getDoc38,
17021
17923
  getDocs as getDocs35,
17022
17924
  query as query35,
17023
17925
  updateDoc as updateDoc34,
17024
- where as where35
17926
+ where as where35,
17927
+ limit as limit20,
17928
+ orderBy as orderBy22,
17929
+ startAfter as startAfter18,
17930
+ getCountFromServer as getCountFromServer6
17025
17931
  } from "firebase/firestore";
17026
17932
 
17027
17933
  // src/backoffice/types/product.types.ts
@@ -17062,20 +17968,102 @@ var ProductService = class extends BaseService {
17062
17968
  return { id: productRef.id, ...newProduct };
17063
17969
  }
17064
17970
  /**
17065
- * Gets all products for a technology
17971
+ * Gets a paginated list of all products, with optional filters.
17972
+ * This uses a collectionGroup query to search across all technologies.
17066
17973
  */
17067
- async getAllByTechnology(technologyId) {
17974
+ async getAll(options) {
17975
+ const {
17976
+ rowsPerPage,
17977
+ lastVisible,
17978
+ categoryId,
17979
+ subcategoryId,
17980
+ technologyId
17981
+ } = options;
17982
+ const constraints = [
17983
+ where35("isActive", "==", true),
17984
+ orderBy22("name")
17985
+ ];
17986
+ if (categoryId) {
17987
+ constraints.push(where35("categoryId", "==", categoryId));
17988
+ }
17989
+ if (subcategoryId) {
17990
+ constraints.push(where35("subcategoryId", "==", subcategoryId));
17991
+ }
17992
+ if (technologyId) {
17993
+ constraints.push(where35("technologyId", "==", technologyId));
17994
+ }
17995
+ if (lastVisible) {
17996
+ constraints.push(startAfter18(lastVisible));
17997
+ }
17998
+ constraints.push(limit20(rowsPerPage));
17068
17999
  const q = query35(
17069
- this.getProductsRef(technologyId),
17070
- where35("isActive", "==", true)
18000
+ collectionGroup3(this.db, PRODUCTS_COLLECTION),
18001
+ ...constraints
17071
18002
  );
17072
18003
  const snapshot = await getDocs35(q);
17073
- return snapshot.docs.map(
17074
- (doc37) => ({
17075
- id: doc37.id,
17076
- ...doc37.data()
18004
+ const products = snapshot.docs.map(
18005
+ (doc38) => ({
18006
+ id: doc38.id,
18007
+ ...doc38.data()
17077
18008
  })
17078
18009
  );
18010
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
18011
+ return { products, lastVisible: newLastVisible };
18012
+ }
18013
+ /**
18014
+ * Gets the total count of active products, with optional filters.
18015
+ */
18016
+ async getProductsCount(options) {
18017
+ const { categoryId, subcategoryId, technologyId } = options;
18018
+ const constraints = [where35("isActive", "==", true)];
18019
+ if (categoryId) {
18020
+ constraints.push(where35("categoryId", "==", categoryId));
18021
+ }
18022
+ if (subcategoryId) {
18023
+ constraints.push(where35("subcategoryId", "==", subcategoryId));
18024
+ }
18025
+ if (technologyId) {
18026
+ constraints.push(where35("technologyId", "==", technologyId));
18027
+ }
18028
+ const q = query35(
18029
+ collectionGroup3(this.db, PRODUCTS_COLLECTION),
18030
+ ...constraints
18031
+ );
18032
+ const snapshot = await getCountFromServer6(q);
18033
+ return snapshot.data().count;
18034
+ }
18035
+ /**
18036
+ * Gets counts of active products grouped by category, subcategory, and technology.
18037
+ * This uses a single collectionGroup query for efficiency.
18038
+ */
18039
+ async getProductCounts() {
18040
+ const q = query35(
18041
+ collectionGroup3(this.db, PRODUCTS_COLLECTION),
18042
+ where35("isActive", "==", true)
18043
+ );
18044
+ const snapshot = await getDocs35(q);
18045
+ const counts = {
18046
+ byCategory: {},
18047
+ bySubcategory: {},
18048
+ byTechnology: {}
18049
+ };
18050
+ if (snapshot.empty) {
18051
+ return counts;
18052
+ }
18053
+ snapshot.docs.forEach((doc38) => {
18054
+ const product = doc38.data();
18055
+ const { categoryId, subcategoryId, technologyId } = product;
18056
+ if (categoryId) {
18057
+ counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
18058
+ }
18059
+ if (subcategoryId) {
18060
+ counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
18061
+ }
18062
+ if (technologyId) {
18063
+ counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
18064
+ }
18065
+ });
18066
+ return counts;
17079
18067
  }
17080
18068
  /**
17081
18069
  * Gets all products for a brand by filtering through all technologies
@@ -17093,9 +18081,9 @@ var ProductService = class extends BaseService {
17093
18081
  const snapshot = await getDocs35(q);
17094
18082
  products.push(
17095
18083
  ...snapshot.docs.map(
17096
- (doc37) => ({
17097
- id: doc37.id,
17098
- ...doc37.data()
18084
+ (doc38) => ({
18085
+ id: doc38.id,
18086
+ ...doc38.data()
17099
18087
  })
17100
18088
  )
17101
18089
  );
@@ -17136,6 +18124,262 @@ var ProductService = class extends BaseService {
17136
18124
  }
17137
18125
  };
17138
18126
 
18127
+ // src/backoffice/services/constants.service.ts
18128
+ import {
18129
+ arrayRemove as arrayRemove9,
18130
+ arrayUnion as arrayUnion10,
18131
+ doc as doc37,
18132
+ getDoc as getDoc39,
18133
+ setDoc as setDoc29,
18134
+ updateDoc as updateDoc35
18135
+ } from "firebase/firestore";
18136
+ var ADMIN_CONSTANTS_COLLECTION = "admin-constants";
18137
+ var TREATMENT_BENEFITS_DOC = "treatment-benefits";
18138
+ var CONTRAINDICATIONS_DOC = "contraindications";
18139
+ var ConstantsService = class extends BaseService {
18140
+ /**
18141
+ * @description Gets the reference to the document holding treatment benefits.
18142
+ * @private
18143
+ * @type {DocumentReference}
18144
+ */
18145
+ get treatmentBenefitsDocRef() {
18146
+ return doc37(this.db, ADMIN_CONSTANTS_COLLECTION, TREATMENT_BENEFITS_DOC);
18147
+ }
18148
+ /**
18149
+ * @description Gets the reference to the document holding contraindications.
18150
+ * @private
18151
+ * @type {DocumentReference}
18152
+ */
18153
+ get contraindicationsDocRef() {
18154
+ return doc37(this.db, ADMIN_CONSTANTS_COLLECTION, CONTRAINDICATIONS_DOC);
18155
+ }
18156
+ // =================================================================
18157
+ // Treatment Benefits
18158
+ // =================================================================
18159
+ /**
18160
+ * @description Retrieves all treatment benefits without pagination.
18161
+ * @returns {Promise<TreatmentBenefitDynamic[]>} An array of all treatment benefits.
18162
+ */
18163
+ async getAllBenefitsForFilter() {
18164
+ const docSnap = await getDoc39(this.treatmentBenefitsDocRef);
18165
+ if (!docSnap.exists()) {
18166
+ return [];
18167
+ }
18168
+ return docSnap.data().benefits;
18169
+ }
18170
+ /**
18171
+ * @description Retrieves a paginated list of treatment benefits.
18172
+ * @param {{ page: number; limit: number }} options - Pagination options.
18173
+ * @returns {Promise<{ benefits: TreatmentBenefitDynamic[]; total: number }>} A paginated list of benefits and the total count.
18174
+ */
18175
+ async getAllBenefits(options) {
18176
+ const allBenefits = await this.getAllBenefitsForFilter();
18177
+ const { page, limit: limit21 } = options;
18178
+ const startIndex = page * limit21;
18179
+ const endIndex = startIndex + limit21;
18180
+ const paginatedBenefits = allBenefits.slice(startIndex, endIndex);
18181
+ return { benefits: paginatedBenefits, total: allBenefits.length };
18182
+ }
18183
+ /**
18184
+ * @description Adds a new treatment benefit.
18185
+ * @param {Omit<TreatmentBenefitDynamic, "id">} benefit - The treatment benefit to add, without an ID.
18186
+ * @returns {Promise<TreatmentBenefitDynamic>} The newly created treatment benefit with its generated ID.
18187
+ */
18188
+ async addTreatmentBenefit(benefit) {
18189
+ const newBenefit = {
18190
+ id: this.generateId(),
18191
+ ...benefit
18192
+ };
18193
+ const docSnap = await getDoc39(this.treatmentBenefitsDocRef);
18194
+ if (!docSnap.exists()) {
18195
+ await setDoc29(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
18196
+ } else {
18197
+ await updateDoc35(this.treatmentBenefitsDocRef, {
18198
+ benefits: arrayUnion10(newBenefit)
18199
+ });
18200
+ }
18201
+ return newBenefit;
18202
+ }
18203
+ /**
18204
+ * @description Retrieves a single treatment benefit by its ID.
18205
+ * @param {string} benefitId - The ID of the treatment benefit to retrieve.
18206
+ * @returns {Promise<TreatmentBenefitDynamic | undefined>} The found treatment benefit or undefined.
18207
+ */
18208
+ async getBenefitById(benefitId) {
18209
+ const benefits = await this.getAllBenefitsForFilter();
18210
+ return benefits.find((b) => b.id === benefitId);
18211
+ }
18212
+ /**
18213
+ * @description Searches for treatment benefits by name (case-insensitive).
18214
+ * @param {string} searchTerm - The term to search for in the benefit names.
18215
+ * @returns {Promise<TreatmentBenefitDynamic[]>} An array of matching treatment benefits.
18216
+ */
18217
+ async searchBenefitsByName(searchTerm) {
18218
+ const benefits = await this.getAllBenefitsForFilter();
18219
+ const normalizedSearchTerm = searchTerm.toLowerCase();
18220
+ return benefits.filter(
18221
+ (b) => b.name.toLowerCase().includes(normalizedSearchTerm)
18222
+ );
18223
+ }
18224
+ /**
18225
+ * @description Updates an existing treatment benefit.
18226
+ * @param {TreatmentBenefitDynamic} benefit - The treatment benefit with updated data. Its ID must match an existing benefit.
18227
+ * @returns {Promise<TreatmentBenefitDynamic>} The updated treatment benefit.
18228
+ * @throws {Error} If the treatment benefit is not found.
18229
+ */
18230
+ async updateTreatmentBenefit(benefit) {
18231
+ const benefits = await this.getAllBenefitsForFilter();
18232
+ const benefitIndex = benefits.findIndex((b) => b.id === benefit.id);
18233
+ if (benefitIndex === -1) {
18234
+ throw new Error("Treatment benefit not found.");
18235
+ }
18236
+ benefits[benefitIndex] = benefit;
18237
+ await updateDoc35(this.treatmentBenefitsDocRef, { benefits });
18238
+ return benefit;
18239
+ }
18240
+ /**
18241
+ * @description Deletes a treatment benefit by its ID.
18242
+ * @param {string} benefitId - The ID of the treatment benefit to delete.
18243
+ * @returns {Promise<void>}
18244
+ */
18245
+ async deleteTreatmentBenefit(benefitId) {
18246
+ const benefits = await this.getAllBenefitsForFilter();
18247
+ const benefitToRemove = benefits.find((b) => b.id === benefitId);
18248
+ if (!benefitToRemove) {
18249
+ return;
18250
+ }
18251
+ await updateDoc35(this.treatmentBenefitsDocRef, {
18252
+ benefits: arrayRemove9(benefitToRemove)
18253
+ });
18254
+ }
18255
+ // =================================================================
18256
+ // Contraindications
18257
+ // =================================================================
18258
+ /**
18259
+ * @description Retrieves all contraindications without pagination.
18260
+ * @returns {Promise<ContraindicationDynamic[]>} An array of all contraindications.
18261
+ */
18262
+ async getAllContraindicationsForFilter() {
18263
+ const docSnap = await getDoc39(this.contraindicationsDocRef);
18264
+ if (!docSnap.exists()) {
18265
+ return [];
18266
+ }
18267
+ return docSnap.data().contraindications;
18268
+ }
18269
+ /**
18270
+ * @description Retrieves a paginated list of contraindications.
18271
+ * @param {{ page: number; limit: number }} options - Pagination options.
18272
+ * @returns {Promise<{ contraindications: ContraindicationDynamic[]; total: number }>} A paginated list and the total count.
18273
+ */
18274
+ async getAllContraindications(options) {
18275
+ const allContraindications = await this.getAllContraindicationsForFilter();
18276
+ const { page, limit: limit21 } = options;
18277
+ const startIndex = page * limit21;
18278
+ const endIndex = startIndex + limit21;
18279
+ const paginatedContraindications = allContraindications.slice(
18280
+ startIndex,
18281
+ endIndex
18282
+ );
18283
+ return {
18284
+ contraindications: paginatedContraindications,
18285
+ total: allContraindications.length
18286
+ };
18287
+ }
18288
+ /**
18289
+ * @description Adds a new contraindication.
18290
+ * @param {Omit<ContraindicationDynamic, "id">} contraindication - The contraindication to add, without an ID.
18291
+ * @returns {Promise<ContraindicationDynamic>} The newly created contraindication with its generated ID.
18292
+ */
18293
+ async addContraindication(contraindication) {
18294
+ const newContraindication = {
18295
+ id: this.generateId(),
18296
+ ...contraindication
18297
+ };
18298
+ const docSnap = await getDoc39(this.contraindicationsDocRef);
18299
+ if (!docSnap.exists()) {
18300
+ await setDoc29(this.contraindicationsDocRef, {
18301
+ contraindications: [newContraindication]
18302
+ });
18303
+ } else {
18304
+ await updateDoc35(this.contraindicationsDocRef, {
18305
+ contraindications: arrayUnion10(newContraindication)
18306
+ });
18307
+ }
18308
+ return newContraindication;
18309
+ }
18310
+ /**
18311
+ * @description Retrieves a single contraindication by its ID.
18312
+ * @param {string} contraindicationId - The ID of the contraindication to retrieve.
18313
+ * @returns {Promise<ContraindicationDynamic | undefined>} The found contraindication or undefined.
18314
+ */
18315
+ async getContraindicationById(contraindicationId) {
18316
+ const contraindications = await this.getAllContraindicationsForFilter();
18317
+ return contraindications.find((c) => c.id === contraindicationId);
18318
+ }
18319
+ /**
18320
+ * @description Searches for contraindications by name (case-insensitive).
18321
+ * @param {string} searchTerm - The term to search for in the contraindication names.
18322
+ * @returns {Promise<ContraindicationDynamic[]>} An array of matching contraindications.
18323
+ */
18324
+ async searchContraindicationsByName(searchTerm) {
18325
+ const contraindications = await this.getAllContraindicationsForFilter();
18326
+ const normalizedSearchTerm = searchTerm.toLowerCase();
18327
+ return contraindications.filter(
18328
+ (c) => c.name.toLowerCase().includes(normalizedSearchTerm)
18329
+ );
18330
+ }
18331
+ /**
18332
+ * @description Updates an existing contraindication.
18333
+ * @param {ContraindicationDynamic} contraindication - The contraindication with updated data. Its ID must match an existing one.
18334
+ * @returns {Promise<ContraindicationDynamic>} The updated contraindication.
18335
+ * @throws {Error} If the contraindication is not found.
18336
+ */
18337
+ async updateContraindication(contraindication) {
18338
+ const contraindications = await this.getAllContraindicationsForFilter();
18339
+ const index = contraindications.findIndex(
18340
+ (c) => c.id === contraindication.id
18341
+ );
18342
+ if (index === -1) {
18343
+ throw new Error("Contraindication not found.");
18344
+ }
18345
+ contraindications[index] = contraindication;
18346
+ await updateDoc35(this.contraindicationsDocRef, { contraindications });
18347
+ return contraindication;
18348
+ }
18349
+ /**
18350
+ * @description Deletes a contraindication by its ID.
18351
+ * @param {string} contraindicationId - The ID of the contraindication to delete.
18352
+ * @returns {Promise<void>}
18353
+ */
18354
+ async deleteContraindication(contraindicationId) {
18355
+ const contraindications = await this.getAllContraindicationsForFilter();
18356
+ const toRemove = contraindications.find((c) => c.id === contraindicationId);
18357
+ if (!toRemove) {
18358
+ return;
18359
+ }
18360
+ await updateDoc35(this.contraindicationsDocRef, {
18361
+ contraindications: arrayRemove9(toRemove)
18362
+ });
18363
+ }
18364
+ };
18365
+
18366
+ // src/backoffice/types/static/contraindication.types.ts
18367
+ var Contraindication = /* @__PURE__ */ ((Contraindication2) => {
18368
+ Contraindication2["SENSITIVE_SKIN"] = "sensitive_skin";
18369
+ Contraindication2["RECENT_TANNING"] = "recent_tanning";
18370
+ Contraindication2["RECENT_BOTOX"] = "recent_botox";
18371
+ Contraindication2["RECENT_FILLERS"] = "recent_fillers";
18372
+ Contraindication2["SKIN_ALLERGIES"] = "skin_allergies";
18373
+ Contraindication2["MEDICATIONS"] = "medications";
18374
+ Contraindication2["RECENT_CHEMICAL_PEEL"] = "recent_chemical_peel";
18375
+ Contraindication2["RECENT_LASER"] = "recent_laser";
18376
+ Contraindication2["SKIN_INFLAMMATION"] = "skin_inflammation";
18377
+ Contraindication2["OPEN_WOUNDS"] = "open_wounds";
18378
+ Contraindication2["HERPES_SIMPLEX"] = "herpes_simplex";
18379
+ Contraindication2["COLD_SORES"] = "cold_sores";
18380
+ return Contraindication2;
18381
+ })(Contraindication || {});
18382
+
17139
18383
  // src/backoffice/types/static/treatment-benefit.types.ts
17140
18384
  var TreatmentBenefit = /* @__PURE__ */ ((TreatmentBenefit2) => {
17141
18385
  TreatmentBenefit2["WRINKLE_REDUCTION"] = "wrinkle_reduction";
@@ -17194,6 +18438,7 @@ export {
17194
18438
  ClinicPhotoTag,
17195
18439
  ClinicService,
17196
18440
  ClinicTag,
18441
+ ConstantsService,
17197
18442
  Contraindication,
17198
18443
  CosmeticAllergySubtype,
17199
18444
  Currency,