@blackcode_sa/metaestetics-api 1.8.11 → 1.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6211,6 +6211,7 @@ var PractitionerService = class extends BaseService {
6211
6211
  trustworthiness: 0,
6212
6212
  recommendationPercentage: 0
6213
6213
  };
6214
+ const fullNameLower = `${validData.basicInfo.firstName} ${validData.basicInfo.lastName}`.toLowerCase();
6214
6215
  const practitioner = {
6215
6216
  id: practitionerId,
6216
6217
  userRef: validData.userRef,
@@ -6218,6 +6219,8 @@ var PractitionerService = class extends BaseService {
6218
6219
  validData.basicInfo,
6219
6220
  practitionerId
6220
6221
  ),
6222
+ fullNameLower,
6223
+ // Ensure this is present
6221
6224
  certification: validData.certification,
6222
6225
  clinics: validData.clinics || [],
6223
6226
  clinicWorkingHours: validData.clinicWorkingHours || [],
@@ -6313,6 +6316,7 @@ var PractitionerService = class extends BaseService {
6313
6316
  }
6314
6317
  const finalClinicsInfo = validatedData.clinicsInfo && validatedData.clinicsInfo.length > 0 ? validatedData.clinicsInfo : clinicsInfo;
6315
6318
  const proceduresInfo = [];
6319
+ const fullNameLowerDraft = `${validatedData.basicInfo.firstName} ${validatedData.basicInfo.lastName}`.toLowerCase();
6316
6320
  const practitionerData = {
6317
6321
  id: practitionerId,
6318
6322
  userRef: "",
@@ -6321,6 +6325,8 @@ var PractitionerService = class extends BaseService {
6321
6325
  validatedData.basicInfo,
6322
6326
  practitionerId
6323
6327
  ),
6328
+ fullNameLower: fullNameLowerDraft,
6329
+ // Ensure this is present
6324
6330
  certification: validatedData.certification,
6325
6331
  clinics,
6326
6332
  clinicWorkingHours: validatedData.clinicWorkingHours || [],
@@ -6826,10 +6832,6 @@ var PractitionerService = class extends BaseService {
6826
6832
  */
6827
6833
  async getPractitionersByFilters(filters) {
6828
6834
  try {
6829
- console.log(
6830
- "[PRACTITIONER_SERVICE] Starting practitioner filtering with criteria:",
6831
- filters
6832
- );
6833
6835
  const constraints = [];
6834
6836
  if (!filters.includeDraftPractitioners) {
6835
6837
  constraints.push((0, import_firestore21.where)("status", "==", "active" /* ACTIVE */));
@@ -6844,60 +6846,40 @@ var PractitionerService = class extends BaseService {
6844
6846
  )
6845
6847
  );
6846
6848
  }
6847
- constraints.push((0, import_firestore21.orderBy)("basicInfo.lastName", "asc"));
6848
- constraints.push((0, import_firestore21.orderBy)("basicInfo.firstName", "asc"));
6849
- if (filters.pagination && filters.pagination > 0) {
6850
- if (filters.lastDoc) {
6851
- constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
6852
- }
6853
- constraints.push((0, import_firestore21.limit)(filters.pagination));
6849
+ if (filters.nameSearch && filters.nameSearch.trim()) {
6850
+ const searchTerm = filters.nameSearch.trim().toLowerCase();
6851
+ constraints.push((0, import_firestore21.where)("fullNameLower", ">=", searchTerm));
6852
+ constraints.push((0, import_firestore21.where)("fullNameLower", "<=", searchTerm + "\uF8FF"));
6854
6853
  }
6855
- const q = (0, import_firestore21.query)(
6856
- (0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION),
6857
- ...constraints
6858
- );
6859
- const querySnapshot = await (0, import_firestore21.getDocs)(q);
6860
- console.log(
6861
- `[PRACTITIONER_SERVICE] Found ${querySnapshot.docs.length} practitioners with base query`
6862
- );
6863
- let practitioners = querySnapshot.docs.map((doc37) => {
6864
- return { ...doc37.data(), id: doc37.id };
6865
- });
6866
- const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
6867
- if (filters.nameSearch && filters.nameSearch.trim() !== "") {
6868
- const searchTerm = filters.nameSearch.toLowerCase().trim();
6869
- practitioners = practitioners.filter((practitioner) => {
6870
- const fullName = `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`.toLowerCase();
6871
- return fullName.includes(searchTerm);
6872
- });
6854
+ if (filters.procedureTechnology) {
6855
+ constraints.push((0, import_firestore21.where)("proceduresInfo.technologyName", "==", filters.procedureTechnology));
6856
+ } else if (filters.procedureSubcategory) {
6857
+ constraints.push((0, import_firestore21.where)("proceduresInfo.subcategoryName", "==", filters.procedureSubcategory));
6858
+ } else if (filters.procedureCategory) {
6859
+ constraints.push((0, import_firestore21.where)("proceduresInfo.categoryName", "==", filters.procedureCategory));
6860
+ } else if (filters.procedureFamily) {
6861
+ constraints.push((0, import_firestore21.where)("proceduresInfo.family", "==", filters.procedureFamily));
6873
6862
  }
6874
- if (filters.specialties && filters.specialties.length > 0) {
6875
- practitioners = practitioners.filter((practitioner) => {
6876
- return filters.specialties.every(
6877
- (specialty) => practitioner.certification.specialties.includes(specialty)
6878
- );
6879
- });
6863
+ if (filters.minRating !== void 0) {
6864
+ constraints.push((0, import_firestore21.where)("reviewInfo.averageRating", ">=", filters.minRating));
6880
6865
  }
6881
- if (filters.procedureTechnology || filters.procedureSubcategory || filters.procedureCategory || filters.procedureFamily) {
6882
- practitioners = practitioners.filter((practitioner) => {
6883
- const procedures = practitioner.proceduresInfo || [];
6884
- return procedures.some((procedure) => {
6885
- if (filters.procedureTechnology) {
6886
- return procedure.technologyName === filters.procedureTechnology;
6887
- }
6888
- if (filters.procedureSubcategory) {
6889
- return procedure.subcategoryName === filters.procedureSubcategory;
6890
- }
6891
- if (filters.procedureCategory) {
6892
- return procedure.categoryName === filters.procedureCategory;
6893
- }
6894
- if (filters.procedureFamily) {
6895
- return procedure.family === filters.procedureFamily;
6896
- }
6897
- return false;
6898
- });
6899
- });
6866
+ if (filters.maxRating !== void 0) {
6867
+ constraints.push((0, import_firestore21.where)("reviewInfo.averageRating", "<=", filters.maxRating));
6868
+ }
6869
+ constraints.push((0, import_firestore21.orderBy)("fullNameLower"));
6870
+ if (filters.lastDoc) {
6871
+ if (typeof filters.lastDoc.data === "function") {
6872
+ constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
6873
+ } else if (Array.isArray(filters.lastDoc)) {
6874
+ constraints.push((0, import_firestore21.startAfter)(...filters.lastDoc));
6875
+ } else {
6876
+ constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
6877
+ }
6900
6878
  }
6879
+ constraints.push((0, import_firestore21.limit)(filters.pagination || 5));
6880
+ const q = (0, import_firestore21.query)((0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION), ...constraints);
6881
+ const querySnapshot = await (0, import_firestore21.getDocs)(q);
6882
+ let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
6901
6883
  if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
6902
6884
  const location = filters.location;
6903
6885
  const radiusInKm = filters.radiusInKm;
@@ -6913,22 +6895,7 @@ var PractitionerService = class extends BaseService {
6913
6895
  });
6914
6896
  });
6915
6897
  }
6916
- if (filters.minRating !== void 0) {
6917
- practitioners = practitioners.filter(
6918
- (p) => p.reviewInfo.averageRating >= filters.minRating
6919
- );
6920
- }
6921
- if (filters.maxRating !== void 0) {
6922
- practitioners = practitioners.filter(
6923
- (p) => p.reviewInfo.averageRating <= filters.maxRating
6924
- );
6925
- }
6926
- console.log(
6927
- `[PRACTITIONER_SERVICE] Filtered to ${practitioners.length} practitioners`
6928
- );
6929
- if (filters.pagination && filters.pagination > 0) {
6930
- practitioners = practitioners.slice(0, filters.pagination);
6931
- }
6898
+ const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
6932
6899
  return {
6933
6900
  practitioners,
6934
6901
  lastDoc
@@ -6993,6 +6960,7 @@ var PractitionerService = class extends BaseService {
6993
6960
  }
6994
6961
  const consultationData = {
6995
6962
  name: "Free Consultation",
6963
+ nameLower: "free consultation",
6996
6964
  description: "Free initial consultation to discuss treatment options and assess patient needs.",
6997
6965
  family: "aesthetics" /* AESTHETICS */,
6998
6966
  categoryId: "consultation",
@@ -8566,168 +8534,64 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
8566
8534
  var import_firestore26 = require("firebase/firestore");
8567
8535
  var import_geofire_common6 = require("geofire-common");
8568
8536
  async function getClinicsByFilters(db, filters) {
8569
- console.log(
8570
- "[FILTER_UTILS] Starting clinic filtering with criteria:",
8571
- filters
8572
- );
8573
- const isGeoQuery = filters.center && filters.radiusInKm && filters.radiusInKm > 0;
8537
+ var _a;
8574
8538
  const constraints = [];
8575
- if (filters.isActive !== void 0) {
8576
- constraints.push((0, import_firestore26.where)("isActive", "==", filters.isActive));
8577
- } else {
8578
- constraints.push((0, import_firestore26.where)("isActive", "==", true));
8579
- }
8539
+ constraints.push((0, import_firestore26.where)("isActive", "==", (_a = filters.isActive) != null ? _a : true));
8580
8540
  if (filters.tags && filters.tags.length > 0) {
8581
8541
  constraints.push((0, import_firestore26.where)("tags", "array-contains", filters.tags[0]));
8582
8542
  }
8583
8543
  if (filters.procedureTechnology) {
8584
- constraints.push(
8585
- (0, import_firestore26.where)("servicesInfo.technology", "==", filters.procedureTechnology)
8586
- );
8544
+ constraints.push((0, import_firestore26.where)("servicesInfo.technology", "==", filters.procedureTechnology));
8587
8545
  } else if (filters.procedureSubcategory) {
8588
- constraints.push(
8589
- (0, import_firestore26.where)("servicesInfo.subCategory", "==", filters.procedureSubcategory)
8590
- );
8546
+ constraints.push((0, import_firestore26.where)("servicesInfo.subCategory", "==", filters.procedureSubcategory));
8591
8547
  } else if (filters.procedureCategory) {
8592
- constraints.push(
8593
- (0, import_firestore26.where)("servicesInfo.category", "==", filters.procedureCategory)
8594
- );
8548
+ constraints.push((0, import_firestore26.where)("servicesInfo.category", "==", filters.procedureCategory));
8595
8549
  } else if (filters.procedureFamily) {
8596
- constraints.push(
8597
- (0, import_firestore26.where)("servicesInfo.procedureFamily", "==", filters.procedureFamily)
8598
- );
8599
- }
8600
- if (filters.pagination && filters.pagination > 0 && filters.lastDoc) {
8601
- constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
8602
- constraints.push((0, import_firestore26.limit)(filters.pagination));
8603
- } else if (filters.pagination && filters.pagination > 0) {
8604
- constraints.push((0, import_firestore26.limit)(filters.pagination));
8605
- }
8606
- constraints.push((0, import_firestore26.orderBy)("location.geohash"));
8607
- let clinicsResult = [];
8608
- let lastVisibleDoc = null;
8609
- if (isGeoQuery) {
8610
- const center = filters.center;
8611
- const radiusInKm = filters.radiusInKm;
8612
- const bounds = (0, import_geofire_common6.geohashQueryBounds)(
8613
- [center.latitude, center.longitude],
8614
- radiusInKm * 1e3
8615
- // Convert to meters
8616
- );
8617
- const matchingClinics = [];
8618
- for (const bound of bounds) {
8619
- const geoConstraints = [
8620
- ...constraints,
8621
- (0, import_firestore26.where)("location.geohash", ">=", bound[0]),
8622
- (0, import_firestore26.where)("location.geohash", "<=", bound[1])
8623
- ];
8624
- const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...geoConstraints);
8625
- const querySnapshot = await (0, import_firestore26.getDocs)(q);
8626
- console.log(
8627
- `[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics in geo bound`
8628
- );
8629
- for (const doc37 of querySnapshot.docs) {
8630
- const clinic = { ...doc37.data(), id: doc37.id };
8631
- const distance = (0, import_geofire_common6.distanceBetween)(
8632
- [center.latitude, center.longitude],
8633
- [clinic.location.latitude, clinic.location.longitude]
8634
- );
8635
- const distanceInKm = distance / 1e3;
8636
- if (distanceInKm <= radiusInKm) {
8637
- matchingClinics.push({
8638
- ...clinic,
8639
- distance: distanceInKm
8640
- });
8641
- }
8642
- }
8643
- }
8644
- let filteredClinics = matchingClinics;
8645
- if (filters.tags && filters.tags.length > 1) {
8646
- filteredClinics = filteredClinics.filter((clinic) => {
8647
- return filters.tags.every((tag) => clinic.tags.includes(tag));
8648
- });
8649
- }
8650
- if (filters.minRating !== void 0) {
8651
- filteredClinics = filteredClinics.filter(
8652
- (clinic) => clinic.reviewInfo.averageRating >= filters.minRating
8653
- );
8654
- }
8655
- if (filters.maxRating !== void 0) {
8656
- filteredClinics = filteredClinics.filter(
8657
- (clinic) => clinic.reviewInfo.averageRating <= filters.maxRating
8658
- );
8659
- }
8660
- filteredClinics.sort((a, b) => a.distance - b.distance);
8661
- if (filters.pagination && filters.pagination > 0) {
8662
- let startIndex = 0;
8663
- if (filters.lastDoc) {
8664
- const lastDocIndex = filteredClinics.findIndex(
8665
- (clinic) => clinic.id === filters.lastDoc.id
8666
- );
8667
- if (lastDocIndex !== -1) {
8668
- startIndex = lastDocIndex + 1;
8669
- }
8670
- }
8671
- const paginatedClinics = filteredClinics.slice(
8672
- startIndex,
8673
- startIndex + filters.pagination
8674
- );
8675
- lastVisibleDoc = paginatedClinics.length > 0 ? paginatedClinics[paginatedClinics.length - 1] : null;
8676
- clinicsResult = paginatedClinics;
8550
+ constraints.push((0, import_firestore26.where)("servicesInfo.procedureFamily", "==", filters.procedureFamily));
8551
+ }
8552
+ let useNameLower = false;
8553
+ let searchTerm = "";
8554
+ if (filters.nameSearch && filters.nameSearch.trim()) {
8555
+ searchTerm = filters.nameSearch.trim().toLowerCase();
8556
+ constraints.push((0, import_firestore26.where)("nameLower", ">=", searchTerm));
8557
+ constraints.push((0, import_firestore26.where)("nameLower", "<=", searchTerm + "\uF8FF"));
8558
+ useNameLower = true;
8559
+ }
8560
+ if (filters.minRating !== void 0) {
8561
+ constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", ">=", filters.minRating));
8562
+ }
8563
+ if (filters.maxRating !== void 0) {
8564
+ constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", "<=", filters.maxRating));
8565
+ }
8566
+ constraints.push((0, import_firestore26.orderBy)("nameLower"));
8567
+ if (filters.lastDoc) {
8568
+ if (typeof filters.lastDoc.data === "function") {
8569
+ constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
8570
+ } else if (Array.isArray(filters.lastDoc)) {
8571
+ constraints.push((0, import_firestore26.startAfter)(...filters.lastDoc));
8677
8572
  } else {
8678
- clinicsResult = filteredClinics;
8573
+ constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
8679
8574
  }
8680
- } else {
8681
- const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
8682
- const querySnapshot = await (0, import_firestore26.getDocs)(q);
8683
- console.log(
8684
- `[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics with regular query`
8685
- );
8686
- const clinics = querySnapshot.docs.map((doc37) => {
8687
- return { ...doc37.data(), id: doc37.id };
8575
+ }
8576
+ constraints.push((0, import_firestore26.limit)(filters.pagination || 5));
8577
+ const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
8578
+ const querySnapshot = await (0, import_firestore26.getDocs)(q);
8579
+ let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
8580
+ if (filters.tags && filters.tags.length > 1) {
8581
+ clinics = clinics.filter((clinic) => filters.tags.every((tag) => clinic.tags.includes(tag)));
8582
+ }
8583
+ if (filters.center && filters.radiusInKm) {
8584
+ clinics = clinics.filter((clinic) => {
8585
+ const distance = (0, import_geofire_common6.distanceBetween)(
8586
+ [filters.center.latitude, filters.center.longitude],
8587
+ [clinic.location.latitude, clinic.location.longitude]
8588
+ ) / 1e3;
8589
+ clinic.distance = distance;
8590
+ return distance <= filters.radiusInKm;
8688
8591
  });
8689
- let filteredClinics = clinics;
8690
- if (filters.center) {
8691
- const center = filters.center;
8692
- const clinicsWithDistance = [];
8693
- filteredClinics.forEach((clinic) => {
8694
- const distance = (0, import_geofire_common6.distanceBetween)(
8695
- [center.latitude, center.longitude],
8696
- [clinic.location.latitude, clinic.location.longitude]
8697
- );
8698
- clinicsWithDistance.push({
8699
- ...clinic,
8700
- distance: distance / 1e3
8701
- // Convert to kilometers
8702
- });
8703
- });
8704
- filteredClinics = clinicsWithDistance;
8705
- filteredClinics.sort(
8706
- (a, b) => a.distance - b.distance
8707
- );
8708
- }
8709
- if (filters.tags && filters.tags.length > 1) {
8710
- filteredClinics = filteredClinics.filter((clinic) => {
8711
- return filters.tags.every((tag) => clinic.tags.includes(tag));
8712
- });
8713
- }
8714
- if (filters.minRating !== void 0) {
8715
- filteredClinics = filteredClinics.filter(
8716
- (clinic) => clinic.reviewInfo.averageRating >= filters.minRating
8717
- );
8718
- }
8719
- if (filters.maxRating !== void 0) {
8720
- filteredClinics = filteredClinics.filter(
8721
- (clinic) => clinic.reviewInfo.averageRating <= filters.maxRating
8722
- );
8723
- }
8724
- lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8725
- clinicsResult = filteredClinics;
8726
8592
  }
8727
- return {
8728
- clinics: clinicsResult,
8729
- lastDoc: lastVisibleDoc
8730
- };
8593
+ const lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8594
+ return { clinics, lastDoc: lastVisibleDoc };
8731
8595
  }
8732
8596
 
8733
8597
  // src/services/clinic/clinic.service.ts
@@ -8863,6 +8727,8 @@ var ClinicService = class extends BaseService {
8863
8727
  id: clinicId,
8864
8728
  clinicGroupId: validatedData.clinicGroupId,
8865
8729
  name: validatedData.name,
8730
+ nameLower: validatedData.name.toLowerCase(),
8731
+ // Add this line
8866
8732
  description: validatedData.description,
8867
8733
  location: { ...location, geohash: hash },
8868
8734
  contactInfo: validatedData.contactInfo,
@@ -8963,6 +8829,9 @@ var ClinicService = class extends BaseService {
8963
8829
  updatePayload[field] = validatedData[field];
8964
8830
  }
8965
8831
  }
8832
+ if (validatedData.name) {
8833
+ updatePayload.nameLower = validatedData.name.toLowerCase();
8834
+ }
8966
8835
  if (validatedData.location) {
8967
8836
  const loc = validatedData.location;
8968
8837
  updatePayload.location = {
@@ -9138,6 +9007,27 @@ var ClinicService = class extends BaseService {
9138
9007
  async getClinicsByFilters(filters) {
9139
9008
  return getClinicsByFilters(this.db, filters);
9140
9009
  }
9010
+ /**
9011
+ * Gets all clinics with minimal info for map display (id, name, address, latitude, longitude)
9012
+ * This is optimized for mobile map usage to reduce payload size.
9013
+ * @returns Array of minimal clinic info for map
9014
+ */
9015
+ async getClinicsForMap() {
9016
+ const clinicsRef = (0, import_firestore27.collection)(this.db, CLINICS_COLLECTION);
9017
+ const snapshot = await (0, import_firestore27.getDocs)(clinicsRef);
9018
+ const clinicsForMap = snapshot.docs.map((doc37) => {
9019
+ var _a, _b, _c;
9020
+ const data = doc37.data();
9021
+ return {
9022
+ id: doc37.id,
9023
+ name: data.name,
9024
+ address: ((_a = data.location) == null ? void 0 : _a.address) || "",
9025
+ latitude: (_b = data.location) == null ? void 0 : _b.latitude,
9026
+ longitude: (_c = data.location) == null ? void 0 : _c.longitude
9027
+ };
9028
+ });
9029
+ return clinicsForMap;
9030
+ }
9141
9031
  };
9142
9032
 
9143
9033
  // src/services/auth/utils/firebase.utils.ts
@@ -14319,6 +14209,7 @@ var import_firestore45 = require("firebase/firestore");
14319
14209
  var import_zod24 = require("zod");
14320
14210
  var createProcedureSchema = import_zod24.z.object({
14321
14211
  name: import_zod24.z.string().min(1).max(200),
14212
+ nameLower: import_zod24.z.string().min(1).max(200),
14322
14213
  description: import_zod24.z.string().min(1).max(2e3),
14323
14214
  family: import_zod24.z.nativeEnum(ProcedureFamily),
14324
14215
  categoryId: import_zod24.z.string().min(1),
@@ -14336,6 +14227,7 @@ var createProcedureSchema = import_zod24.z.object({
14336
14227
  });
14337
14228
  var updateProcedureSchema = import_zod24.z.object({
14338
14229
  name: import_zod24.z.string().min(3).max(100).optional(),
14230
+ nameLower: import_zod24.z.string().min(1).max(200).optional(),
14339
14231
  description: import_zod24.z.string().min(3).max(1e3).optional(),
14340
14232
  price: import_zod24.z.number().min(0).optional(),
14341
14233
  currency: import_zod24.z.nativeEnum(Currency).optional(),
@@ -14352,6 +14244,7 @@ var updateProcedureSchema = import_zod24.z.object({
14352
14244
  });
14353
14245
  var procedureSchema = createProcedureSchema.extend({
14354
14246
  id: import_zod24.z.string().min(1),
14247
+ nameLower: import_zod24.z.string().min(1).max(200),
14355
14248
  category: import_zod24.z.any(),
14356
14249
  // We'll validate the full category object separately
14357
14250
  subcategory: import_zod24.z.any(),
@@ -14520,6 +14413,7 @@ var ProcedureService = class extends BaseService {
14520
14413
  const newProcedure = {
14521
14414
  id: procedureId,
14522
14415
  ...validatedData,
14416
+ nameLower: validatedData.nameLower || validatedData.name.toLowerCase(),
14523
14417
  photos: processedPhotos,
14524
14418
  category,
14525
14419
  // Embed full objects
@@ -14653,6 +14547,7 @@ var ProcedureService = class extends BaseService {
14653
14547
  const newProcedure = {
14654
14548
  id: procedureId,
14655
14549
  ...validatedData,
14550
+ nameLower: validatedData.nameLower || validatedData.name.toLowerCase(),
14656
14551
  practitionerId,
14657
14552
  // Override practitionerId with the correct one
14658
14553
  photos: processedPhotos,
@@ -14831,6 +14726,9 @@ var ProcedureService = class extends BaseService {
14831
14726
  };
14832
14727
  }
14833
14728
  let finalCategoryId = existingProcedure.category.id;
14729
+ if (validatedData.name) {
14730
+ updatedProcedureData.nameLower = validatedData.name.toLowerCase();
14731
+ }
14834
14732
  if (validatedData.categoryId) {
14835
14733
  const category = await this.categoryService.getById(
14836
14734
  validatedData.categoryId
@@ -14988,6 +14886,8 @@ var ProcedureService = class extends BaseService {
14988
14886
  /**
14989
14887
  * Searches and filters procedures based on multiple criteria
14990
14888
  *
14889
+ * @note Frontend MORA da šalje ceo snapshot (ili barem sva polja po kojima sortiraš, npr. nameLower) kao lastDoc za paginaciju, a ne samo id!
14890
+ *
14991
14891
  * @param filters - Various filters to apply
14992
14892
  * @param filters.nameSearch - Optional search text for procedure name
14993
14893
  * @param filters.treatmentBenefits - Optional array of treatment benefits to filter by
@@ -15008,10 +14908,6 @@ var ProcedureService = class extends BaseService {
15008
14908
  */
15009
14909
  async getProceduresByFilters(filters) {
15010
14910
  try {
15011
- console.log(
15012
- "[PROCEDURE_SERVICE] Starting procedure filtering with criteria:",
15013
- filters
15014
- );
15015
14911
  const isGeoQuery = filters.location && filters.radiusInKm && filters.radiusInKm > 0;
15016
14912
  const constraints = [];
15017
14913
  if (filters.isActive !== void 0) {
@@ -15022,15 +14918,53 @@ var ProcedureService = class extends BaseService {
15022
14918
  if (filters.procedureFamily) {
15023
14919
  constraints.push((0, import_firestore45.where)("family", "==", filters.procedureFamily));
15024
14920
  }
15025
- constraints.push((0, import_firestore45.orderBy)("clinicInfo.location.geohash"));
15026
- if (filters.pagination && filters.pagination > 0 && filters.lastDoc) {
15027
- constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
15028
- constraints.push((0, import_firestore45.limit)(filters.pagination));
15029
- } else if (filters.pagination && filters.pagination > 0) {
14921
+ if (filters.procedureCategory) {
14922
+ constraints.push((0, import_firestore45.where)("category.id", "==", filters.procedureCategory));
14923
+ }
14924
+ if (filters.procedureSubcategory) {
14925
+ constraints.push((0, import_firestore45.where)("subcategory.id", "==", filters.procedureSubcategory));
14926
+ }
14927
+ if (filters.procedureTechnology) {
14928
+ constraints.push((0, import_firestore45.where)("technology.id", "==", filters.procedureTechnology));
14929
+ }
14930
+ if (filters.minPrice !== void 0) {
14931
+ constraints.push((0, import_firestore45.where)("price", ">=", filters.minPrice));
14932
+ }
14933
+ if (filters.maxPrice !== void 0) {
14934
+ constraints.push((0, import_firestore45.where)("price", "<=", filters.maxPrice));
14935
+ }
14936
+ if (filters.minRating !== void 0) {
14937
+ constraints.push((0, import_firestore45.where)("reviewInfo.averageRating", ">=", filters.minRating));
14938
+ }
14939
+ if (filters.maxRating !== void 0) {
14940
+ constraints.push((0, import_firestore45.where)("reviewInfo.averageRating", "<=", filters.maxRating));
14941
+ }
14942
+ if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
14943
+ constraints.push((0, import_firestore45.where)("treatmentBenefits", "array-contains-any", filters.treatmentBenefits));
14944
+ }
14945
+ let useNameLower = false;
14946
+ let searchTerm = "";
14947
+ if (filters.nameSearch && filters.nameSearch.trim() !== "") {
14948
+ searchTerm = filters.nameSearch.trim().toLowerCase();
14949
+ useNameLower = true;
14950
+ constraints.push((0, import_firestore45.where)("nameLower", ">=", searchTerm));
14951
+ constraints.push((0, import_firestore45.where)("nameLower", "<=", searchTerm + "\uF8FF"));
14952
+ constraints.push((0, import_firestore45.orderBy)("nameLower"));
14953
+ } else {
14954
+ constraints.push((0, import_firestore45.orderBy)("nameLower"));
14955
+ }
14956
+ if (filters.lastDoc) {
14957
+ if (typeof filters.lastDoc.data === "function") {
14958
+ constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
14959
+ } else if (Array.isArray(filters.lastDoc)) {
14960
+ constraints.push((0, import_firestore45.startAfter)(...filters.lastDoc));
14961
+ } else {
14962
+ constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
14963
+ }
14964
+ }
14965
+ if (filters.pagination && filters.pagination > 0) {
15030
14966
  constraints.push((0, import_firestore45.limit)(filters.pagination));
15031
14967
  }
15032
- let proceduresResult = [];
15033
- let lastVisibleDoc = null;
15034
14968
  if (isGeoQuery) {
15035
14969
  const center = filters.location;
15036
14970
  const radiusInKm = filters.radiusInKm;
@@ -15039,178 +14973,63 @@ var ProcedureService = class extends BaseService {
15039
14973
  radiusInKm * 1e3
15040
14974
  // Convert to meters
15041
14975
  );
15042
- const matchingProcedures = [];
14976
+ let allDocs = [];
15043
14977
  for (const bound of bounds) {
15044
14978
  const geoConstraints = [
15045
- ...constraints,
14979
+ ...constraints.filter((c) => !c.fieldPath || c.fieldPath !== "name"),
14980
+ // Remove name orderBy for geo
15046
14981
  (0, import_firestore45.where)("clinicInfo.location.geohash", ">=", bound[0]),
15047
- (0, import_firestore45.where)("clinicInfo.location.geohash", "<=", bound[1])
14982
+ (0, import_firestore45.where)("clinicInfo.location.geohash", "<=", bound[1]),
14983
+ (0, import_firestore45.orderBy)("clinicInfo.location.geohash")
15048
14984
  ];
15049
- const q = (0, import_firestore45.query)(
15050
- (0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
15051
- ...geoConstraints
15052
- );
14985
+ const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...geoConstraints);
15053
14986
  const querySnapshot = await (0, import_firestore45.getDocs)(q);
15054
- console.log(
15055
- `[PROCEDURE_SERVICE] Found ${querySnapshot.docs.length} procedures in geo bound`
15056
- );
15057
14987
  for (const doc37 of querySnapshot.docs) {
15058
14988
  const procedure = { ...doc37.data(), id: doc37.id };
15059
14989
  const distance = (0, import_geofire_common8.distanceBetween)(
15060
14990
  [center.latitude, center.longitude],
15061
- [
15062
- procedure.clinicInfo.location.latitude,
15063
- procedure.clinicInfo.location.longitude
15064
- ]
14991
+ [procedure.clinicInfo.location.latitude, procedure.clinicInfo.location.longitude]
15065
14992
  );
15066
14993
  const distanceInKm = distance / 1e3;
15067
14994
  if (distanceInKm <= radiusInKm) {
15068
- matchingProcedures.push({
15069
- ...procedure,
15070
- distance: distanceInKm
15071
- });
14995
+ allDocs.push({ ...procedure, distance: distanceInKm });
15072
14996
  }
15073
14997
  }
15074
14998
  }
15075
- let filteredProcedures = matchingProcedures;
15076
- filteredProcedures = this.applyInMemoryFilters(
15077
- filteredProcedures,
15078
- filters
15079
- );
15080
- filteredProcedures.sort((a, b) => a.distance - b.distance);
14999
+ allDocs.sort((a, b) => a.distance - b.distance);
15000
+ let paginated = allDocs;
15081
15001
  if (filters.pagination && filters.pagination > 0) {
15082
15002
  let startIndex = 0;
15083
15003
  if (filters.lastDoc) {
15084
- const lastDocIndex = filteredProcedures.findIndex(
15085
- (procedure) => procedure.id === filters.lastDoc.id
15086
- );
15087
- if (lastDocIndex !== -1) {
15088
- startIndex = lastDocIndex + 1;
15089
- }
15004
+ const lastDocIndex = allDocs.findIndex((p) => p.id === filters.lastDoc.id);
15005
+ if (lastDocIndex !== -1) startIndex = lastDocIndex + 1;
15090
15006
  }
15091
- const paginatedProcedures = filteredProcedures.slice(
15092
- startIndex,
15093
- startIndex + filters.pagination
15094
- );
15095
- lastVisibleDoc = paginatedProcedures.length > 0 ? paginatedProcedures[paginatedProcedures.length - 1] : null;
15096
- proceduresResult = paginatedProcedures;
15097
- } else {
15098
- proceduresResult = filteredProcedures;
15007
+ paginated = allDocs.slice(startIndex, startIndex + filters.pagination);
15099
15008
  }
15009
+ const lastVisibleDoc = paginated.length > 0 ? paginated[paginated.length - 1] : null;
15010
+ return { procedures: paginated, lastDoc: lastVisibleDoc };
15100
15011
  } else {
15101
- const q = (0, import_firestore45.query)(
15102
- (0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
15103
- ...constraints
15104
- );
15105
- const querySnapshot = await (0, import_firestore45.getDocs)(q);
15106
- console.log(
15107
- `[PROCEDURE_SERVICE] Found ${querySnapshot.docs.length} procedures with regular query`
15108
- );
15109
- const procedures = querySnapshot.docs.map((doc37) => {
15110
- return { ...doc37.data(), id: doc37.id };
15111
- });
15112
- if (filters.location) {
15113
- const center = filters.location;
15114
- const proceduresWithDistance = [];
15115
- procedures.forEach((procedure) => {
15116
- const distance = (0, import_geofire_common8.distanceBetween)(
15117
- [center.latitude, center.longitude],
15118
- [
15119
- procedure.clinicInfo.location.latitude,
15120
- procedure.clinicInfo.location.longitude
15121
- ]
15122
- );
15123
- proceduresWithDistance.push({
15124
- ...procedure,
15125
- distance: distance / 1e3
15126
- // Convert to kilometers
15127
- });
15128
- });
15129
- let filteredProcedures = proceduresWithDistance;
15130
- filteredProcedures = this.applyInMemoryFilters(
15131
- filteredProcedures,
15132
- filters
15133
- );
15134
- filteredProcedures.sort((a, b) => a.distance - b.distance);
15135
- proceduresResult = filteredProcedures;
15136
- } else {
15137
- let filteredProcedures = this.applyInMemoryFilters(
15138
- procedures,
15139
- filters
15140
- );
15141
- proceduresResult = filteredProcedures;
15012
+ let q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
15013
+ let querySnapshot = await (0, import_firestore45.getDocs)(q);
15014
+ if (useNameLower && querySnapshot.empty && searchTerm) {
15015
+ constraints.pop();
15016
+ constraints.pop();
15017
+ constraints.pop();
15018
+ constraints.push((0, import_firestore45.where)("name", ">=", searchTerm));
15019
+ constraints.push((0, import_firestore45.where)("name", "<=", searchTerm + "\uF8FF"));
15020
+ constraints.push((0, import_firestore45.orderBy)("name"));
15021
+ q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
15022
+ querySnapshot = await (0, import_firestore45.getDocs)(q);
15142
15023
  }
15143
- lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15024
+ const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15025
+ const lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15026
+ return { procedures, lastDoc: lastVisibleDoc };
15144
15027
  }
15145
- return {
15146
- procedures: proceduresResult,
15147
- lastDoc: lastVisibleDoc
15148
- };
15149
15028
  } catch (error) {
15150
15029
  console.error("[PROCEDURE_SERVICE] Error filtering procedures:", error);
15151
15030
  throw error;
15152
15031
  }
15153
15032
  }
15154
- /**
15155
- * Helper method to apply in-memory filters to procedures
15156
- * Used by getProceduresByFilters to apply filters that can't be done in Firestore queries
15157
- *
15158
- * @param procedures - The procedures to filter
15159
- * @param filters - The filters to apply
15160
- * @returns Filtered procedures
15161
- */
15162
- applyInMemoryFilters(procedures, filters) {
15163
- let filteredProcedures = procedures;
15164
- if (filters.nameSearch && filters.nameSearch.trim() !== "") {
15165
- const searchTerm = filters.nameSearch.toLowerCase().trim();
15166
- filteredProcedures = filteredProcedures.filter((procedure) => {
15167
- return procedure.name.toLowerCase().includes(searchTerm);
15168
- });
15169
- }
15170
- if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
15171
- filteredProcedures = filteredProcedures.filter((procedure) => {
15172
- return filters.treatmentBenefits.every(
15173
- (benefit) => procedure.treatmentBenefits.includes(benefit)
15174
- );
15175
- });
15176
- }
15177
- if (filters.procedureCategory) {
15178
- filteredProcedures = filteredProcedures.filter(
15179
- (procedure) => procedure.category.id === filters.procedureCategory
15180
- );
15181
- }
15182
- if (filters.procedureSubcategory) {
15183
- filteredProcedures = filteredProcedures.filter(
15184
- (procedure) => procedure.subcategory.id === filters.procedureSubcategory
15185
- );
15186
- }
15187
- if (filters.procedureTechnology) {
15188
- filteredProcedures = filteredProcedures.filter(
15189
- (procedure) => procedure.technology.id === filters.procedureTechnology
15190
- );
15191
- }
15192
- if (filters.minPrice !== void 0) {
15193
- filteredProcedures = filteredProcedures.filter(
15194
- (procedure) => procedure.price >= filters.minPrice
15195
- );
15196
- }
15197
- if (filters.maxPrice !== void 0) {
15198
- filteredProcedures = filteredProcedures.filter(
15199
- (procedure) => procedure.price <= filters.maxPrice
15200
- );
15201
- }
15202
- if (filters.minRating !== void 0) {
15203
- filteredProcedures = filteredProcedures.filter(
15204
- (procedure) => procedure.reviewInfo.averageRating >= filters.minRating
15205
- );
15206
- }
15207
- if (filters.maxRating !== void 0) {
15208
- filteredProcedures = filteredProcedures.filter(
15209
- (procedure) => procedure.reviewInfo.averageRating <= filters.maxRating
15210
- );
15211
- }
15212
- return filteredProcedures;
15213
- }
15214
15033
  /**
15215
15034
  * Creates a consultation procedure without requiring a product
15216
15035
  * This is a special method for consultation procedures that don't use products
@@ -15283,6 +15102,7 @@ var ProcedureService = class extends BaseService {
15283
15102
  const newProcedure = {
15284
15103
  id: procedureId,
15285
15104
  ...data,
15105
+ nameLower: data.nameLower || data.name.toLowerCase(),
15286
15106
  photos: processedPhotos,
15287
15107
  category,
15288
15108
  subcategory,
@@ -15319,6 +15139,29 @@ var ProcedureService = class extends BaseService {
15319
15139
  const savedDoc = await (0, import_firestore45.getDoc)(procedureRef);
15320
15140
  return savedDoc.data();
15321
15141
  }
15142
+ /**
15143
+ * Gets all procedures with minimal info for map display (id, name, clinicId, clinicName, address, latitude, longitude)
15144
+ * This is optimized for mobile map usage to reduce payload size.
15145
+ * @returns Array of minimal procedure info for map
15146
+ */
15147
+ async getProceduresForMap() {
15148
+ const proceduresRef = (0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION);
15149
+ const snapshot = await (0, import_firestore45.getDocs)(proceduresRef);
15150
+ const proceduresForMap = snapshot.docs.map((doc37) => {
15151
+ var _a, _b, _c, _d, _e, _f, _g, _h;
15152
+ const data = doc37.data();
15153
+ return {
15154
+ id: doc37.id,
15155
+ name: data.name,
15156
+ clinicId: (_a = data.clinicInfo) == null ? void 0 : _a.id,
15157
+ clinicName: (_b = data.clinicInfo) == null ? void 0 : _b.name,
15158
+ address: ((_d = (_c = data.clinicInfo) == null ? void 0 : _c.location) == null ? void 0 : _d.address) || "",
15159
+ latitude: (_f = (_e = data.clinicInfo) == null ? void 0 : _e.location) == null ? void 0 : _f.latitude,
15160
+ longitude: (_h = (_g = data.clinicInfo) == null ? void 0 : _g.location) == null ? void 0 : _h.longitude
15161
+ };
15162
+ });
15163
+ return proceduresForMap;
15164
+ }
15322
15165
  };
15323
15166
 
15324
15167
  // src/services/reviews/reviews.service.ts