@blackcode_sa/metaestetics-api 1.8.15 → 1.8.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -6935,8 +6935,9 @@ var PractitionerService = class extends BaseService {
6935
6935
  }
6936
6936
  constraints.push(where10("isActive", "==", true));
6937
6937
  if (filters.certifications && filters.certifications.length > 0) {
6938
+ const certificationsToMatch = filters.certifications;
6938
6939
  constraints.push(
6939
- where10("certification.specialties", "array-contains-any", filters.certifications)
6940
+ where10("certification.specialties", "array-contains-any", certificationsToMatch)
6940
6941
  );
6941
6942
  }
6942
6943
  if (filters.minRating !== void 0) {
@@ -6979,18 +6980,7 @@ var PractitionerService = class extends BaseService {
6979
6980
  });
6980
6981
  practitioners = practitioners.slice(0, filters.pagination || 10);
6981
6982
  }
6982
- if (filters.nameSearch && filters.nameSearch.trim()) {
6983
- const searchTerm = filters.nameSearch.trim().toLowerCase();
6984
- practitioners = practitioners.filter((practitioner) => {
6985
- var _a, _b;
6986
- const firstName = (((_a = practitioner.basicInfo) == null ? void 0 : _a.firstName) || "").toLowerCase();
6987
- const lastName = (((_b = practitioner.basicInfo) == null ? void 0 : _b.lastName) || "").toLowerCase();
6988
- const fullName = `${firstName} ${lastName}`.trim();
6989
- const fullNameLower = practitioner.fullNameLower || "";
6990
- return firstName.includes(searchTerm) || lastName.includes(searchTerm) || fullName.includes(searchTerm) || fullNameLower.includes(searchTerm);
6991
- });
6992
- console.log(`[PRACTITIONER_SERVICE] Applied name filter, results: ${practitioners.length}`);
6993
- }
6983
+ practitioners = this.applyInMemoryFilters(practitioners, filters);
6994
6984
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
6995
6985
  console.log(`[PRACTITIONER_SERVICE] Strategy 2 success: ${practitioners.length} practitioners`);
6996
6986
  if (practitioners.length < (filters.pagination || 10)) {
@@ -7010,18 +7000,7 @@ var PractitionerService = class extends BaseService {
7010
7000
  const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
7011
7001
  const querySnapshot = await getDocs10(q);
7012
7002
  let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
7013
- if (filters.nameSearch && filters.nameSearch.trim()) {
7014
- const searchTerm = filters.nameSearch.trim().toLowerCase();
7015
- practitioners = practitioners.filter((practitioner) => {
7016
- var _a, _b;
7017
- const firstName = (((_a = practitioner.basicInfo) == null ? void 0 : _a.firstName) || "").toLowerCase();
7018
- const lastName = (((_b = practitioner.basicInfo) == null ? void 0 : _b.lastName) || "").toLowerCase();
7019
- const fullName = `${firstName} ${lastName}`.trim();
7020
- const fullNameLower = practitioner.fullNameLower || "";
7021
- return firstName.includes(searchTerm) || lastName.includes(searchTerm) || fullName.includes(searchTerm) || fullNameLower.includes(searchTerm);
7022
- });
7023
- console.log(`[PRACTITIONER_SERVICE] Applied name filter, results: ${practitioners.length}`);
7024
- }
7003
+ practitioners = this.applyInMemoryFilters(practitioners, filters);
7025
7004
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
7026
7005
  console.log(`[PRACTITIONER_SERVICE] Strategy 3 success: ${practitioners.length} practitioners`);
7027
7006
  if (practitioners.length < (filters.pagination || 10)) {
@@ -7038,6 +7017,98 @@ var PractitionerService = class extends BaseService {
7038
7017
  return { practitioners: [], lastDoc: null };
7039
7018
  }
7040
7019
  }
7020
+ /**
7021
+ * Applies in-memory filters to practitioners array
7022
+ * Used when Firestore queries fail or for complex filtering
7023
+ */
7024
+ applyInMemoryFilters(practitioners, filters) {
7025
+ let filteredPractitioners = [...practitioners];
7026
+ if (filters.nameSearch && filters.nameSearch.trim()) {
7027
+ const searchTerm = filters.nameSearch.trim().toLowerCase();
7028
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7029
+ var _a, _b;
7030
+ const firstName = (((_a = practitioner.basicInfo) == null ? void 0 : _a.firstName) || "").toLowerCase();
7031
+ const lastName = (((_b = practitioner.basicInfo) == null ? void 0 : _b.lastName) || "").toLowerCase();
7032
+ const fullName = `${firstName} ${lastName}`.trim();
7033
+ const fullNameLower = practitioner.fullNameLower || "";
7034
+ return firstName.includes(searchTerm) || lastName.includes(searchTerm) || fullName.includes(searchTerm) || fullNameLower.includes(searchTerm);
7035
+ });
7036
+ console.log(`[PRACTITIONER_SERVICE] Applied name filter, results: ${filteredPractitioners.length}`);
7037
+ }
7038
+ if (filters.certifications && filters.certifications.length > 0) {
7039
+ const certificationsToMatch = filters.certifications;
7040
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7041
+ var _a;
7042
+ const practitionerCerts = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
7043
+ return certificationsToMatch.some((cert) => practitionerCerts.includes(cert));
7044
+ });
7045
+ console.log(`[PRACTITIONER_SERVICE] Applied certifications filter, results: ${filteredPractitioners.length}`);
7046
+ }
7047
+ if (filters.specialties && filters.specialties.length > 0) {
7048
+ const specialtiesToMatch = filters.specialties;
7049
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7050
+ var _a;
7051
+ const practitionerSpecs = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
7052
+ return specialtiesToMatch.some((spec) => practitionerSpecs.includes(spec));
7053
+ });
7054
+ console.log(`[PRACTITIONER_SERVICE] Applied specialties filter, results: ${filteredPractitioners.length}`);
7055
+ }
7056
+ if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
7057
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7058
+ var _a;
7059
+ const rating = ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
7060
+ if (filters.minRating !== void 0 && rating < filters.minRating) return false;
7061
+ if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
7062
+ return true;
7063
+ });
7064
+ console.log(`[PRACTITIONER_SERVICE] Applied rating filter, results: ${filteredPractitioners.length}`);
7065
+ }
7066
+ if (filters.procedureFamily) {
7067
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7068
+ const proceduresInfo = practitioner.proceduresInfo || [];
7069
+ return proceduresInfo.some((proc) => proc.family === filters.procedureFamily);
7070
+ });
7071
+ console.log(`[PRACTITIONER_SERVICE] Applied procedure family filter, results: ${filteredPractitioners.length}`);
7072
+ }
7073
+ if (filters.procedureCategory) {
7074
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7075
+ const proceduresInfo = practitioner.proceduresInfo || [];
7076
+ return proceduresInfo.some((proc) => proc.categoryName === filters.procedureCategory);
7077
+ });
7078
+ console.log(`[PRACTITIONER_SERVICE] Applied procedure category filter, results: ${filteredPractitioners.length}`);
7079
+ }
7080
+ if (filters.procedureSubcategory) {
7081
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7082
+ const proceduresInfo = practitioner.proceduresInfo || [];
7083
+ return proceduresInfo.some((proc) => proc.subcategoryName === filters.procedureSubcategory);
7084
+ });
7085
+ console.log(`[PRACTITIONER_SERVICE] Applied procedure subcategory filter, results: ${filteredPractitioners.length}`);
7086
+ }
7087
+ if (filters.procedureTechnology) {
7088
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7089
+ const proceduresInfo = practitioner.proceduresInfo || [];
7090
+ return proceduresInfo.some((proc) => proc.technologyName === filters.procedureTechnology);
7091
+ });
7092
+ console.log(`[PRACTITIONER_SERVICE] Applied procedure technology filter, results: ${filteredPractitioners.length}`);
7093
+ }
7094
+ if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
7095
+ const location = filters.location;
7096
+ const radiusInKm = filters.radiusInKm;
7097
+ filteredPractitioners = filteredPractitioners.filter((practitioner) => {
7098
+ const clinics = practitioner.clinicsInfo || [];
7099
+ return clinics.some((clinic) => {
7100
+ const distance = distanceBetween(
7101
+ [location.latitude, location.longitude],
7102
+ [clinic.location.latitude, clinic.location.longitude]
7103
+ );
7104
+ const distanceInKm = distance / 1e3;
7105
+ return distanceInKm <= radiusInKm;
7106
+ });
7107
+ });
7108
+ console.log(`[PRACTITIONER_SERVICE] Applied geo filter, results: ${filteredPractitioners.length}`);
7109
+ }
7110
+ return filteredPractitioners;
7111
+ }
7041
7112
  /**
7042
7113
  * Enables free consultation for a practitioner in a specific clinic
7043
7114
  * Creates a free consultation procedure with hardcoded parameters
@@ -8741,15 +8812,6 @@ async function getClinicsByFilters(db, filters) {
8741
8812
  if (filters.tags && filters.tags.length > 0) {
8742
8813
  constraints.push(where15("tags", "array-contains", filters.tags[0]));
8743
8814
  }
8744
- if (filters.procedureTechnology) {
8745
- constraints.push(where15("servicesInfo.technology", "==", filters.procedureTechnology));
8746
- } else if (filters.procedureSubcategory) {
8747
- constraints.push(where15("servicesInfo.subCategory", "==", filters.procedureSubcategory));
8748
- } else if (filters.procedureCategory) {
8749
- constraints.push(where15("servicesInfo.category", "==", filters.procedureCategory));
8750
- } else if (filters.procedureFamily) {
8751
- constraints.push(where15("servicesInfo.procedureFamily", "==", filters.procedureFamily));
8752
- }
8753
8815
  if (filters.minRating !== void 0) {
8754
8816
  constraints.push(where15("reviewInfo.averageRating", ">=", filters.minRating));
8755
8817
  }
@@ -8839,15 +8901,6 @@ async function getClinicsByFilters(db, filters) {
8839
8901
  const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8840
8902
  const querySnapshot = await getDocs15(q);
8841
8903
  let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
8842
- if (filters.nameSearch && filters.nameSearch.trim()) {
8843
- const searchTerm = filters.nameSearch.trim().toLowerCase();
8844
- clinics = clinics.filter((clinic) => {
8845
- const name = (clinic.name || "").toLowerCase();
8846
- const nameLower = clinic.nameLower || "";
8847
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
8848
- });
8849
- console.log(`[CLINIC_SERVICE] Applied name filter, results: ${clinics.length}`);
8850
- }
8851
8904
  clinics = applyInMemoryFilters(clinics, filters);
8852
8905
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8853
8906
  console.log(`[CLINIC_SERVICE] Strategy 3 success: ${clinics.length} clinics`);
@@ -8868,15 +8921,6 @@ async function getClinicsByFilters(db, filters) {
8868
8921
  const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8869
8922
  const querySnapshot = await getDocs15(q);
8870
8923
  let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
8871
- if (filters.nameSearch && filters.nameSearch.trim()) {
8872
- const searchTerm = filters.nameSearch.trim().toLowerCase();
8873
- clinics = clinics.filter((clinic) => {
8874
- const name = (clinic.name || "").toLowerCase();
8875
- const nameLower = clinic.nameLower || "";
8876
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
8877
- });
8878
- console.log(`[CLINIC_SERVICE] Applied name filter, results: ${clinics.length}`);
8879
- }
8880
8924
  clinics = applyInMemoryFilters(clinics, filters);
8881
8925
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8882
8926
  console.log(`[CLINIC_SERVICE] Strategy 4 success: ${clinics.length} clinics`);
@@ -8895,21 +8939,96 @@ async function getClinicsByFilters(db, filters) {
8895
8939
  }
8896
8940
  }
8897
8941
  function applyInMemoryFilters(clinics, filters) {
8942
+ let filteredClinics = [...clinics];
8943
+ console.log(`[CLINIC_SERVICE] Applying in-memory filters - input: ${filteredClinics.length} clinics`);
8898
8944
  if (filters.tags && filters.tags.length > 1) {
8899
- clinics = clinics.filter((clinic) => filters.tags.every((tag) => clinic.tags.includes(tag)));
8945
+ const initialCount = filteredClinics.length;
8946
+ filteredClinics = filteredClinics.filter(
8947
+ (clinic) => filters.tags.every((tag) => clinic.tags.includes(tag))
8948
+ );
8949
+ console.log(`[CLINIC_SERVICE] Applied multi-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8950
+ }
8951
+ if (filters.tags && filters.tags.length === 1) {
8952
+ const initialCount = filteredClinics.length;
8953
+ filteredClinics = filteredClinics.filter(
8954
+ (clinic) => clinic.tags.includes(filters.tags[0])
8955
+ );
8956
+ console.log(`[CLINIC_SERVICE] Applied single-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8957
+ }
8958
+ if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
8959
+ const initialCount = filteredClinics.length;
8960
+ filteredClinics = filteredClinics.filter((clinic) => {
8961
+ var _a;
8962
+ const rating = ((_a = clinic.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
8963
+ if (filters.minRating !== void 0 && rating < filters.minRating) return false;
8964
+ if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
8965
+ return true;
8966
+ });
8967
+ console.log(`[CLINIC_SERVICE] Applied rating filter (${filters.minRating}-${filters.maxRating}): ${initialCount} \u2192 ${filteredClinics.length}`);
8968
+ }
8969
+ if (filters.nameSearch && filters.nameSearch.trim()) {
8970
+ const initialCount = filteredClinics.length;
8971
+ const searchTerm = filters.nameSearch.trim().toLowerCase();
8972
+ filteredClinics = filteredClinics.filter((clinic) => {
8973
+ const name = (clinic.name || "").toLowerCase();
8974
+ const nameLower = clinic.nameLower || "";
8975
+ return name.includes(searchTerm) || nameLower.includes(searchTerm);
8976
+ });
8977
+ console.log(`[CLINIC_SERVICE] Applied name search filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8978
+ }
8979
+ if (filters.procedureFamily) {
8980
+ const initialCount = filteredClinics.length;
8981
+ filteredClinics = filteredClinics.filter((clinic) => {
8982
+ const proceduresInfo = clinic.proceduresInfo || [];
8983
+ return proceduresInfo.some((proc) => proc.family === filters.procedureFamily);
8984
+ });
8985
+ console.log(`[CLINIC_SERVICE] Applied procedure family filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8986
+ }
8987
+ if (filters.procedureCategory) {
8988
+ const initialCount = filteredClinics.length;
8989
+ filteredClinics = filteredClinics.filter((clinic) => {
8990
+ const proceduresInfo = clinic.proceduresInfo || [];
8991
+ return proceduresInfo.some((proc) => proc.categoryName === filters.procedureCategory);
8992
+ });
8993
+ console.log(`[CLINIC_SERVICE] Applied procedure category filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8994
+ }
8995
+ if (filters.procedureSubcategory) {
8996
+ const initialCount = filteredClinics.length;
8997
+ filteredClinics = filteredClinics.filter((clinic) => {
8998
+ const proceduresInfo = clinic.proceduresInfo || [];
8999
+ return proceduresInfo.some((proc) => proc.subcategoryName === filters.procedureSubcategory);
9000
+ });
9001
+ console.log(`[CLINIC_SERVICE] Applied procedure subcategory filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9002
+ }
9003
+ if (filters.procedureTechnology) {
9004
+ const initialCount = filteredClinics.length;
9005
+ filteredClinics = filteredClinics.filter((clinic) => {
9006
+ const proceduresInfo = clinic.proceduresInfo || [];
9007
+ return proceduresInfo.some((proc) => proc.technologyName === filters.procedureTechnology);
9008
+ });
9009
+ console.log(`[CLINIC_SERVICE] Applied procedure technology filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8900
9010
  }
8901
9011
  if (filters.center && filters.radiusInKm) {
8902
- clinics = clinics.filter((clinic) => {
9012
+ const initialCount = filteredClinics.length;
9013
+ filteredClinics = filteredClinics.filter((clinic) => {
9014
+ var _a, _b;
9015
+ if (!((_a = clinic.location) == null ? void 0 : _a.latitude) || !((_b = clinic.location) == null ? void 0 : _b.longitude)) {
9016
+ console.log(`[CLINIC_SERVICE] Clinic ${clinic.id} missing location data`);
9017
+ return false;
9018
+ }
8903
9019
  const distance = distanceBetween4(
8904
9020
  [filters.center.latitude, filters.center.longitude],
8905
9021
  [clinic.location.latitude, clinic.location.longitude]
8906
9022
  ) / 1e3;
9023
+ console.log(`[CLINIC_SERVICE] Clinic ${clinic.name}: distance ${distance.toFixed(2)}km (limit: ${filters.radiusInKm}km)`);
8907
9024
  clinic.distance = distance;
8908
9025
  return distance <= filters.radiusInKm;
8909
9026
  });
8910
- clinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
9027
+ console.log(`[CLINIC_SERVICE] Applied geo filter (${filters.radiusInKm}km): ${initialCount} \u2192 ${filteredClinics.length}`);
9028
+ filteredClinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
8911
9029
  }
8912
- return clinics;
9030
+ console.log(`[CLINIC_SERVICE] Final filtered result: ${filteredClinics.length} clinics`);
9031
+ return filteredClinics;
8913
9032
  }
8914
9033
 
8915
9034
  // src/services/clinic/clinic.service.ts
@@ -15424,7 +15543,8 @@ var ProcedureService = class extends BaseService {
15424
15543
  constraints.push(where29("reviewInfo.averageRating", "<=", filters.maxRating));
15425
15544
  }
15426
15545
  if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
15427
- constraints.push(where29("treatmentBenefits", "array-contains-any", filters.treatmentBenefits));
15546
+ const benefitsToMatch = filters.treatmentBenefits;
15547
+ constraints.push(where29("treatmentBenefits", "array-contains-any", benefitsToMatch));
15428
15548
  }
15429
15549
  return constraints;
15430
15550
  };
@@ -15507,15 +15627,7 @@ var ProcedureService = class extends BaseService {
15507
15627
  const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
15508
15628
  const querySnapshot = await getDocs29(q);
15509
15629
  let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15510
- if (filters.nameSearch && filters.nameSearch.trim()) {
15511
- const searchTerm = filters.nameSearch.trim().toLowerCase();
15512
- procedures = procedures.filter((procedure) => {
15513
- const name = (procedure.name || "").toLowerCase();
15514
- const nameLower = procedure.nameLower || "";
15515
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
15516
- });
15517
- console.log(`[PROCEDURE_SERVICE] Applied name filter, results: ${procedures.length}`);
15518
- }
15630
+ procedures = this.applyInMemoryFilters(procedures, filters);
15519
15631
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15520
15632
  console.log(`[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`);
15521
15633
  if (procedures.length < (filters.pagination || 10)) {
@@ -15535,15 +15647,7 @@ var ProcedureService = class extends BaseService {
15535
15647
  const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
15536
15648
  const querySnapshot = await getDocs29(q);
15537
15649
  let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15538
- if (filters.nameSearch && filters.nameSearch.trim()) {
15539
- const searchTerm = filters.nameSearch.trim().toLowerCase();
15540
- procedures = procedures.filter((procedure) => {
15541
- const name = (procedure.name || "").toLowerCase();
15542
- const nameLower = procedure.nameLower || "";
15543
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
15544
- });
15545
- console.log(`[PROCEDURE_SERVICE] Applied name filter, results: ${procedures.length}`);
15546
- }
15650
+ procedures = this.applyInMemoryFilters(procedures, filters);
15547
15651
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15548
15652
  console.log(`[PROCEDURE_SERVICE] Strategy 4 success: ${procedures.length} procedures`);
15549
15653
  if (procedures.length < (filters.pagination || 10)) {
@@ -15560,6 +15664,94 @@ var ProcedureService = class extends BaseService {
15560
15664
  return { procedures: [], lastDoc: null };
15561
15665
  }
15562
15666
  }
15667
+ /**
15668
+ * Applies in-memory filters to procedures array
15669
+ * Used when Firestore queries fail or for complex filtering
15670
+ */
15671
+ applyInMemoryFilters(procedures, filters) {
15672
+ let filteredProcedures = [...procedures];
15673
+ if (filters.nameSearch && filters.nameSearch.trim()) {
15674
+ const searchTerm = filters.nameSearch.trim().toLowerCase();
15675
+ filteredProcedures = filteredProcedures.filter((procedure) => {
15676
+ const name = (procedure.name || "").toLowerCase();
15677
+ const nameLower = procedure.nameLower || "";
15678
+ return name.includes(searchTerm) || nameLower.includes(searchTerm);
15679
+ });
15680
+ console.log(`[PROCEDURE_SERVICE] Applied name filter, results: ${filteredProcedures.length}`);
15681
+ }
15682
+ if (filters.minPrice !== void 0 || filters.maxPrice !== void 0) {
15683
+ filteredProcedures = filteredProcedures.filter((procedure) => {
15684
+ const price = procedure.price || 0;
15685
+ if (filters.minPrice !== void 0 && price < filters.minPrice) return false;
15686
+ if (filters.maxPrice !== void 0 && price > filters.maxPrice) return false;
15687
+ return true;
15688
+ });
15689
+ console.log(`[PROCEDURE_SERVICE] Applied price filter (${filters.minPrice}-${filters.maxPrice}), results: ${filteredProcedures.length}`);
15690
+ }
15691
+ if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
15692
+ filteredProcedures = filteredProcedures.filter((procedure) => {
15693
+ var _a;
15694
+ const rating = ((_a = procedure.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
15695
+ if (filters.minRating !== void 0 && rating < filters.minRating) return false;
15696
+ if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
15697
+ return true;
15698
+ });
15699
+ console.log(`[PROCEDURE_SERVICE] Applied rating filter, results: ${filteredProcedures.length}`);
15700
+ }
15701
+ if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
15702
+ const benefitsToMatch = filters.treatmentBenefits;
15703
+ filteredProcedures = filteredProcedures.filter((procedure) => {
15704
+ const procedureBenefits = procedure.treatmentBenefits || [];
15705
+ return benefitsToMatch.some((benefit) => procedureBenefits.includes(benefit));
15706
+ });
15707
+ console.log(`[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`);
15708
+ }
15709
+ if (filters.procedureFamily) {
15710
+ filteredProcedures = filteredProcedures.filter((procedure) => procedure.family === filters.procedureFamily);
15711
+ console.log(`[PROCEDURE_SERVICE] Applied family filter, results: ${filteredProcedures.length}`);
15712
+ }
15713
+ if (filters.procedureCategory) {
15714
+ filteredProcedures = filteredProcedures.filter((procedure) => {
15715
+ var _a;
15716
+ return ((_a = procedure.category) == null ? void 0 : _a.id) === filters.procedureCategory;
15717
+ });
15718
+ console.log(`[PROCEDURE_SERVICE] Applied category filter, results: ${filteredProcedures.length}`);
15719
+ }
15720
+ if (filters.procedureSubcategory) {
15721
+ filteredProcedures = filteredProcedures.filter((procedure) => {
15722
+ var _a;
15723
+ return ((_a = procedure.subcategory) == null ? void 0 : _a.id) === filters.procedureSubcategory;
15724
+ });
15725
+ console.log(`[PROCEDURE_SERVICE] Applied subcategory filter, results: ${filteredProcedures.length}`);
15726
+ }
15727
+ if (filters.procedureTechnology) {
15728
+ filteredProcedures = filteredProcedures.filter((procedure) => {
15729
+ var _a;
15730
+ return ((_a = procedure.technology) == null ? void 0 : _a.id) === filters.procedureTechnology;
15731
+ });
15732
+ console.log(`[PROCEDURE_SERVICE] Applied technology filter, results: ${filteredProcedures.length}`);
15733
+ }
15734
+ if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
15735
+ const location = filters.location;
15736
+ const radiusInKm = filters.radiusInKm;
15737
+ filteredProcedures = filteredProcedures.filter((procedure) => {
15738
+ var _a;
15739
+ const clinicLocation = (_a = procedure.clinicInfo) == null ? void 0 : _a.location;
15740
+ if (!(clinicLocation == null ? void 0 : clinicLocation.latitude) || !(clinicLocation == null ? void 0 : clinicLocation.longitude)) {
15741
+ return false;
15742
+ }
15743
+ const distance = distanceBetween6(
15744
+ [location.latitude, location.longitude],
15745
+ [clinicLocation.latitude, clinicLocation.longitude]
15746
+ ) / 1e3;
15747
+ procedure.distance = distance;
15748
+ return distance <= radiusInKm;
15749
+ });
15750
+ console.log(`[PROCEDURE_SERVICE] Applied geo filter, results: ${filteredProcedures.length}`);
15751
+ filteredProcedures.sort((a, b) => (a.distance || 0) - (b.distance || 0));
15752
+ }
15753
+ return filteredProcedures;
15754
+ }
15563
15755
  handleGeoQuery(filters) {
15564
15756
  console.log("[PROCEDURE_SERVICE] Executing geo query with enhanced debugging");
15565
15757
  try {
@@ -15580,21 +15772,7 @@ var ProcedureService = class extends BaseService {
15580
15772
  const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
15581
15773
  return getDocs29(q).then((querySnapshot) => {
15582
15774
  let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15583
- procedures = procedures.filter((procedure) => {
15584
- var _a;
15585
- const clinicLocation = (_a = procedure.clinicInfo) == null ? void 0 : _a.location;
15586
- if (!(clinicLocation == null ? void 0 : clinicLocation.latitude) || !(clinicLocation == null ? void 0 : clinicLocation.longitude)) {
15587
- return false;
15588
- }
15589
- const distance = distanceBetween6(
15590
- [location.latitude, location.longitude],
15591
- [clinicLocation.latitude, clinicLocation.longitude]
15592
- ) / 1e3;
15593
- procedure.distance = distance;
15594
- return distance <= radiusInKm;
15595
- });
15596
- procedures.sort((a, b) => (a.distance || 0) - (b.distance || 0));
15597
- procedures = procedures.slice(0, filters.pagination || 10);
15775
+ procedures = this.applyInMemoryFilters(procedures, filters);
15598
15776
  console.log(`[PROCEDURE_SERVICE] Geo query success: ${procedures.length} procedures within ${radiusInKm}km`);
15599
15777
  const lastDoc = procedures.length < (filters.pagination || 10) ? null : querySnapshot.docs[querySnapshot.docs.length - 1];
15600
15778
  return { procedures, lastDoc };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.8.15",
4
+ "version": "1.8.17",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -70,17 +70,6 @@ export async function getClinicsByFilters(
70
70
  constraints.push(where("tags", "array-contains", filters.tags[0]));
71
71
  }
72
72
 
73
- // Procedure filters (most specific first)
74
- if (filters.procedureTechnology) {
75
- constraints.push(where("servicesInfo.technology", "==", filters.procedureTechnology));
76
- } else if (filters.procedureSubcategory) {
77
- constraints.push(where("servicesInfo.subCategory", "==", filters.procedureSubcategory));
78
- } else if (filters.procedureCategory) {
79
- constraints.push(where("servicesInfo.category", "==", filters.procedureCategory));
80
- } else if (filters.procedureFamily) {
81
- constraints.push(where("servicesInfo.procedureFamily", "==", filters.procedureFamily));
82
- }
83
-
84
73
  // Rating filters
85
74
  if (filters.minRating !== undefined) {
86
75
  constraints.push(where("reviewInfo.averageRating", ">=", filters.minRating));
@@ -197,18 +186,7 @@ export async function getClinicsByFilters(
197
186
  const querySnapshot = await getDocs(q);
198
187
  let clinics = querySnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id } as Clinic));
199
188
 
200
- // Client-side name filtering
201
- if (filters.nameSearch && filters.nameSearch.trim()) {
202
- const searchTerm = filters.nameSearch.trim().toLowerCase();
203
- clinics = clinics.filter(clinic => {
204
- const name = (clinic.name || '').toLowerCase();
205
- const nameLower = clinic.nameLower || '';
206
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
207
- });
208
- console.log(`[CLINIC_SERVICE] Applied name filter, results: ${clinics.length}`);
209
- }
210
-
211
- // Apply in-memory filters
189
+ // Apply in-memory filters (includes name search)
212
190
  clinics = applyInMemoryFilters(clinics, filters);
213
191
 
214
192
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
@@ -237,18 +215,7 @@ export async function getClinicsByFilters(
237
215
  const querySnapshot = await getDocs(q);
238
216
  let clinics = querySnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id } as Clinic));
239
217
 
240
- // Client-side name filtering
241
- if (filters.nameSearch && filters.nameSearch.trim()) {
242
- const searchTerm = filters.nameSearch.trim().toLowerCase();
243
- clinics = clinics.filter(clinic => {
244
- const name = (clinic.name || '').toLowerCase();
245
- const nameLower = clinic.nameLower || '';
246
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
247
- });
248
- console.log(`[CLINIC_SERVICE] Applied name filter, results: ${clinics.length}`);
249
- }
250
-
251
- // Apply in-memory filters
218
+ // Apply in-memory filters (includes name search)
252
219
  clinics = applyInMemoryFilters(clinics, filters);
253
220
 
254
221
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
@@ -278,27 +245,118 @@ export async function getClinicsByFilters(
278
245
  * Helper function to apply in-memory filters that Firestore doesn't support well
279
246
  */
280
247
  function applyInMemoryFilters(clinics: Clinic[], filters: any): (Clinic & { distance?: number })[] {
281
- // Multi-tag filter (Firestore only supports single array-contains)
248
+ let filteredClinics = [...clinics]; // Kreiraj kopiju
249
+
250
+ console.log(`[CLINIC_SERVICE] Applying in-memory filters - input: ${filteredClinics.length} clinics`);
251
+
252
+ // ✅ Multi-tag filter (postojeći kod)
282
253
  if (filters.tags && filters.tags.length > 1) {
283
- clinics = clinics.filter(clinic => filters.tags!.every((tag: ClinicTag) => clinic.tags.includes(tag)));
254
+ const initialCount = filteredClinics.length;
255
+ filteredClinics = filteredClinics.filter(clinic =>
256
+ filters.tags.every((tag: ClinicTag) => clinic.tags.includes(tag))
257
+ );
258
+ console.log(`[CLINIC_SERVICE] Applied multi-tag filter: ${initialCount} → ${filteredClinics.length}`);
284
259
  }
285
-
286
- // Geo-radius filter
260
+
261
+ // 🆕 DODAJTE: Single tag filter (za Strategy 4 fallback)
262
+ if (filters.tags && filters.tags.length === 1) {
263
+ const initialCount = filteredClinics.length;
264
+ filteredClinics = filteredClinics.filter(clinic =>
265
+ clinic.tags.includes(filters.tags[0])
266
+ );
267
+ console.log(`[CLINIC_SERVICE] Applied single-tag filter: ${initialCount} → ${filteredClinics.length}`);
268
+ }
269
+
270
+ // 🆕 DODAJTE: Rating filter
271
+ if (filters.minRating !== undefined || filters.maxRating !== undefined) {
272
+ const initialCount = filteredClinics.length;
273
+ filteredClinics = filteredClinics.filter(clinic => {
274
+ const rating = clinic.reviewInfo?.averageRating || 0;
275
+ if (filters.minRating !== undefined && rating < filters.minRating) return false;
276
+ if (filters.maxRating !== undefined && rating > filters.maxRating) return false;
277
+ return true;
278
+ });
279
+ console.log(`[CLINIC_SERVICE] Applied rating filter (${filters.minRating}-${filters.maxRating}): ${initialCount} → ${filteredClinics.length}`);
280
+ }
281
+
282
+ // 🆕 DODAJTE: Name search filter
283
+ if (filters.nameSearch && filters.nameSearch.trim()) {
284
+ const initialCount = filteredClinics.length;
285
+ const searchTerm = filters.nameSearch.trim().toLowerCase();
286
+ filteredClinics = filteredClinics.filter(clinic => {
287
+ const name = (clinic.name || '').toLowerCase();
288
+ const nameLower = clinic.nameLower || '';
289
+ return name.includes(searchTerm) || nameLower.includes(searchTerm);
290
+ });
291
+ console.log(`[CLINIC_SERVICE] Applied name search filter: ${initialCount} → ${filteredClinics.length}`);
292
+ }
293
+
294
+ // 🆕 DODAJTE: Procedure family filtering
295
+ if (filters.procedureFamily) {
296
+ const initialCount = filteredClinics.length;
297
+ filteredClinics = filteredClinics.filter(clinic => {
298
+ const proceduresInfo = clinic.proceduresInfo || [];
299
+ return proceduresInfo.some(proc => proc.family === filters.procedureFamily);
300
+ });
301
+ console.log(`[CLINIC_SERVICE] Applied procedure family filter: ${initialCount} → ${filteredClinics.length}`);
302
+ }
303
+
304
+ // 🆕 DODAJTE: Procedure category filtering
305
+ if (filters.procedureCategory) {
306
+ const initialCount = filteredClinics.length;
307
+ filteredClinics = filteredClinics.filter(clinic => {
308
+ const proceduresInfo = clinic.proceduresInfo || [];
309
+ return proceduresInfo.some(proc => proc.categoryName === filters.procedureCategory);
310
+ });
311
+ console.log(`[CLINIC_SERVICE] Applied procedure category filter: ${initialCount} → ${filteredClinics.length}`);
312
+ }
313
+
314
+ // 🆕 DODAJTE: Procedure subcategory filtering
315
+ if (filters.procedureSubcategory) {
316
+ const initialCount = filteredClinics.length;
317
+ filteredClinics = filteredClinics.filter(clinic => {
318
+ const proceduresInfo = clinic.proceduresInfo || [];
319
+ return proceduresInfo.some(proc => proc.subcategoryName === filters.procedureSubcategory);
320
+ });
321
+ console.log(`[CLINIC_SERVICE] Applied procedure subcategory filter: ${initialCount} → ${filteredClinics.length}`);
322
+ }
323
+
324
+ // 🆕 DODAJTE: Procedure technology filtering
325
+ if (filters.procedureTechnology) {
326
+ const initialCount = filteredClinics.length;
327
+ filteredClinics = filteredClinics.filter(clinic => {
328
+ const proceduresInfo = clinic.proceduresInfo || [];
329
+ return proceduresInfo.some(proc => proc.technologyName === filters.procedureTechnology);
330
+ });
331
+ console.log(`[CLINIC_SERVICE] Applied procedure technology filter: ${initialCount} → ${filteredClinics.length}`);
332
+ }
333
+
334
+ // ✅ Geo-radius filter (postojeći kod)
287
335
  if (filters.center && filters.radiusInKm) {
288
- clinics = clinics.filter(clinic => {
336
+ const initialCount = filteredClinics.length;
337
+ filteredClinics = filteredClinics.filter(clinic => {
338
+ if (!clinic.location?.latitude || !clinic.location?.longitude) {
339
+ console.log(`[CLINIC_SERVICE] Clinic ${clinic.id} missing location data`);
340
+ return false;
341
+ }
342
+
289
343
  const distance = distanceBetween(
290
- [filters.center!.latitude, filters.center!.longitude],
344
+ [filters.center.latitude, filters.center.longitude],
291
345
  [clinic.location.latitude, clinic.location.longitude]
292
346
  ) / 1000; // Convert to km
293
347
 
348
+ console.log(`[CLINIC_SERVICE] Clinic ${clinic.name}: distance ${distance.toFixed(2)}km (limit: ${filters.radiusInKm}km)`);
349
+
294
350
  // Attach distance for frontend sorting/display
295
351
  (clinic as any).distance = distance;
296
- return distance <= filters.radiusInKm!;
352
+ return distance <= filters.radiusInKm;
297
353
  });
354
+ console.log(`[CLINIC_SERVICE] Applied geo filter (${filters.radiusInKm}km): ${initialCount} → ${filteredClinics.length}`);
298
355
 
299
356
  // Sort by distance when geo filtering is applied
300
- clinics.sort((a, b) => ((a as any).distance || 0) - ((b as any).distance || 0));
357
+ filteredClinics.sort((a, b) => ((a as any).distance || 0) - ((b as any).distance || 0));
301
358
  }
302
-
303
- return clinics as (Clinic & { distance?: number })[];
359
+
360
+ console.log(`[CLINIC_SERVICE] Final filtered result: ${filteredClinics.length} clinics`);
361
+ return filteredClinics as (Clinic & { distance?: number })[];
304
362
  }