@blackcode_sa/metaestetics-api 1.8.16 β†’ 1.8.18

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
@@ -6964,6 +6964,27 @@ var PractitionerService = class extends BaseService {
6964
6964
  } catch (error) {
6965
6965
  console.log("[PRACTITIONER_SERVICE] Strategy 3 failed:", error);
6966
6966
  }
6967
+ try {
6968
+ console.log("[PRACTITIONER_SERVICE] Strategy 4: Client-side filtering fallback");
6969
+ const constraints = [
6970
+ (0, import_firestore21.where)("isActive", "==", true),
6971
+ (0, import_firestore21.where)("status", "==", "active" /* ACTIVE */),
6972
+ (0, import_firestore21.orderBy)("createdAt", "desc"),
6973
+ (0, import_firestore21.limit)(filters.pagination || 10)
6974
+ ];
6975
+ const q = (0, import_firestore21.query)((0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION), ...constraints);
6976
+ const querySnapshot = await (0, import_firestore21.getDocs)(q);
6977
+ let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
6978
+ practitioners = this.applyInMemoryFilters(practitioners, filters);
6979
+ const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
6980
+ console.log(`[PRACTITIONER_SERVICE] Strategy 4 success: ${practitioners.length} practitioners`);
6981
+ if (practitioners.length < (filters.pagination || 10)) {
6982
+ return { practitioners, lastDoc: null };
6983
+ }
6984
+ return { practitioners, lastDoc };
6985
+ } catch (error) {
6986
+ console.log("[PRACTITIONER_SERVICE] Strategy 4 failed:", error);
6987
+ }
6967
6988
  console.log("[PRACTITIONER_SERVICE] All strategies failed, returning empty result");
6968
6989
  return { practitioners: [], lastDoc: null };
6969
6990
  } catch (error) {
@@ -8710,15 +8731,6 @@ async function getClinicsByFilters(db, filters) {
8710
8731
  if (filters.tags && filters.tags.length > 0) {
8711
8732
  constraints.push((0, import_firestore26.where)("tags", "array-contains", filters.tags[0]));
8712
8733
  }
8713
- if (filters.procedureTechnology) {
8714
- constraints.push((0, import_firestore26.where)("servicesInfo.technology", "==", filters.procedureTechnology));
8715
- } else if (filters.procedureSubcategory) {
8716
- constraints.push((0, import_firestore26.where)("servicesInfo.subCategory", "==", filters.procedureSubcategory));
8717
- } else if (filters.procedureCategory) {
8718
- constraints.push((0, import_firestore26.where)("servicesInfo.category", "==", filters.procedureCategory));
8719
- } else if (filters.procedureFamily) {
8720
- constraints.push((0, import_firestore26.where)("servicesInfo.procedureFamily", "==", filters.procedureFamily));
8721
- }
8722
8734
  if (filters.minRating !== void 0) {
8723
8735
  constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", ">=", filters.minRating));
8724
8736
  }
@@ -8808,15 +8820,6 @@ async function getClinicsByFilters(db, filters) {
8808
8820
  const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
8809
8821
  const querySnapshot = await (0, import_firestore26.getDocs)(q);
8810
8822
  let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
8811
- if (filters.nameSearch && filters.nameSearch.trim()) {
8812
- const searchTerm = filters.nameSearch.trim().toLowerCase();
8813
- clinics = clinics.filter((clinic) => {
8814
- const name = (clinic.name || "").toLowerCase();
8815
- const nameLower = clinic.nameLower || "";
8816
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
8817
- });
8818
- console.log(`[CLINIC_SERVICE] Applied name filter, results: ${clinics.length}`);
8819
- }
8820
8823
  clinics = applyInMemoryFilters(clinics, filters);
8821
8824
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8822
8825
  console.log(`[CLINIC_SERVICE] Strategy 3 success: ${clinics.length} clinics`);
@@ -8837,15 +8840,6 @@ async function getClinicsByFilters(db, filters) {
8837
8840
  const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
8838
8841
  const querySnapshot = await (0, import_firestore26.getDocs)(q);
8839
8842
  let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
8840
- if (filters.nameSearch && filters.nameSearch.trim()) {
8841
- const searchTerm = filters.nameSearch.trim().toLowerCase();
8842
- clinics = clinics.filter((clinic) => {
8843
- const name = (clinic.name || "").toLowerCase();
8844
- const nameLower = clinic.nameLower || "";
8845
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
8846
- });
8847
- console.log(`[CLINIC_SERVICE] Applied name filter, results: ${clinics.length}`);
8848
- }
8849
8843
  clinics = applyInMemoryFilters(clinics, filters);
8850
8844
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8851
8845
  console.log(`[CLINIC_SERVICE] Strategy 4 success: ${clinics.length} clinics`);
@@ -8864,21 +8858,96 @@ async function getClinicsByFilters(db, filters) {
8864
8858
  }
8865
8859
  }
8866
8860
  function applyInMemoryFilters(clinics, filters) {
8861
+ let filteredClinics = [...clinics];
8862
+ console.log(`[CLINIC_SERVICE] Applying in-memory filters - input: ${filteredClinics.length} clinics`);
8867
8863
  if (filters.tags && filters.tags.length > 1) {
8868
- clinics = clinics.filter((clinic) => filters.tags.every((tag) => clinic.tags.includes(tag)));
8864
+ const initialCount = filteredClinics.length;
8865
+ filteredClinics = filteredClinics.filter(
8866
+ (clinic) => filters.tags.every((tag) => clinic.tags.includes(tag))
8867
+ );
8868
+ console.log(`[CLINIC_SERVICE] Applied multi-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8869
+ }
8870
+ if (filters.tags && filters.tags.length === 1) {
8871
+ const initialCount = filteredClinics.length;
8872
+ filteredClinics = filteredClinics.filter(
8873
+ (clinic) => clinic.tags.includes(filters.tags[0])
8874
+ );
8875
+ console.log(`[CLINIC_SERVICE] Applied single-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8876
+ }
8877
+ if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
8878
+ const initialCount = filteredClinics.length;
8879
+ filteredClinics = filteredClinics.filter((clinic) => {
8880
+ var _a;
8881
+ const rating = ((_a = clinic.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
8882
+ if (filters.minRating !== void 0 && rating < filters.minRating) return false;
8883
+ if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
8884
+ return true;
8885
+ });
8886
+ console.log(`[CLINIC_SERVICE] Applied rating filter (${filters.minRating}-${filters.maxRating}): ${initialCount} \u2192 ${filteredClinics.length}`);
8887
+ }
8888
+ if (filters.nameSearch && filters.nameSearch.trim()) {
8889
+ const initialCount = filteredClinics.length;
8890
+ const searchTerm = filters.nameSearch.trim().toLowerCase();
8891
+ filteredClinics = filteredClinics.filter((clinic) => {
8892
+ const name = (clinic.name || "").toLowerCase();
8893
+ const nameLower = clinic.nameLower || "";
8894
+ return name.includes(searchTerm) || nameLower.includes(searchTerm);
8895
+ });
8896
+ console.log(`[CLINIC_SERVICE] Applied name search filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8897
+ }
8898
+ if (filters.procedureFamily) {
8899
+ const initialCount = filteredClinics.length;
8900
+ filteredClinics = filteredClinics.filter((clinic) => {
8901
+ const proceduresInfo = clinic.proceduresInfo || [];
8902
+ return proceduresInfo.some((proc) => proc.family === filters.procedureFamily);
8903
+ });
8904
+ console.log(`[CLINIC_SERVICE] Applied procedure family filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8905
+ }
8906
+ if (filters.procedureCategory) {
8907
+ const initialCount = filteredClinics.length;
8908
+ filteredClinics = filteredClinics.filter((clinic) => {
8909
+ const proceduresInfo = clinic.proceduresInfo || [];
8910
+ return proceduresInfo.some((proc) => proc.categoryName === filters.procedureCategory);
8911
+ });
8912
+ console.log(`[CLINIC_SERVICE] Applied procedure category filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8913
+ }
8914
+ if (filters.procedureSubcategory) {
8915
+ const initialCount = filteredClinics.length;
8916
+ filteredClinics = filteredClinics.filter((clinic) => {
8917
+ const proceduresInfo = clinic.proceduresInfo || [];
8918
+ return proceduresInfo.some((proc) => proc.subcategoryName === filters.procedureSubcategory);
8919
+ });
8920
+ console.log(`[CLINIC_SERVICE] Applied procedure subcategory filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8921
+ }
8922
+ if (filters.procedureTechnology) {
8923
+ const initialCount = filteredClinics.length;
8924
+ filteredClinics = filteredClinics.filter((clinic) => {
8925
+ const proceduresInfo = clinic.proceduresInfo || [];
8926
+ return proceduresInfo.some((proc) => proc.technologyName === filters.procedureTechnology);
8927
+ });
8928
+ console.log(`[CLINIC_SERVICE] Applied procedure technology filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8869
8929
  }
8870
8930
  if (filters.center && filters.radiusInKm) {
8871
- clinics = clinics.filter((clinic) => {
8931
+ const initialCount = filteredClinics.length;
8932
+ filteredClinics = filteredClinics.filter((clinic) => {
8933
+ var _a, _b;
8934
+ if (!((_a = clinic.location) == null ? void 0 : _a.latitude) || !((_b = clinic.location) == null ? void 0 : _b.longitude)) {
8935
+ console.log(`[CLINIC_SERVICE] Clinic ${clinic.id} missing location data`);
8936
+ return false;
8937
+ }
8872
8938
  const distance = (0, import_geofire_common6.distanceBetween)(
8873
8939
  [filters.center.latitude, filters.center.longitude],
8874
8940
  [clinic.location.latitude, clinic.location.longitude]
8875
8941
  ) / 1e3;
8942
+ console.log(`[CLINIC_SERVICE] Clinic ${clinic.name}: distance ${distance.toFixed(2)}km (limit: ${filters.radiusInKm}km)`);
8876
8943
  clinic.distance = distance;
8877
8944
  return distance <= filters.radiusInKm;
8878
8945
  });
8879
- clinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
8946
+ console.log(`[CLINIC_SERVICE] Applied geo filter (${filters.radiusInKm}km): ${initialCount} \u2192 ${filteredClinics.length}`);
8947
+ filteredClinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
8880
8948
  }
8881
- return clinics;
8949
+ console.log(`[CLINIC_SERVICE] Final filtered result: ${filteredClinics.length} clinics`);
8950
+ return filteredClinics;
8882
8951
  }
8883
8952
 
8884
8953
  // src/services/clinic/clinic.service.ts
package/dist/index.mjs CHANGED
@@ -7010,6 +7010,27 @@ var PractitionerService = class extends BaseService {
7010
7010
  } catch (error) {
7011
7011
  console.log("[PRACTITIONER_SERVICE] Strategy 3 failed:", error);
7012
7012
  }
7013
+ try {
7014
+ console.log("[PRACTITIONER_SERVICE] Strategy 4: Client-side filtering fallback");
7015
+ const constraints = [
7016
+ where10("isActive", "==", true),
7017
+ where10("status", "==", "active" /* ACTIVE */),
7018
+ orderBy4("createdAt", "desc"),
7019
+ limit7(filters.pagination || 10)
7020
+ ];
7021
+ const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
7022
+ const querySnapshot = await getDocs10(q);
7023
+ let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
7024
+ practitioners = this.applyInMemoryFilters(practitioners, filters);
7025
+ const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
7026
+ console.log(`[PRACTITIONER_SERVICE] Strategy 4 success: ${practitioners.length} practitioners`);
7027
+ if (practitioners.length < (filters.pagination || 10)) {
7028
+ return { practitioners, lastDoc: null };
7029
+ }
7030
+ return { practitioners, lastDoc };
7031
+ } catch (error) {
7032
+ console.log("[PRACTITIONER_SERVICE] Strategy 4 failed:", error);
7033
+ }
7013
7034
  console.log("[PRACTITIONER_SERVICE] All strategies failed, returning empty result");
7014
7035
  return { practitioners: [], lastDoc: null };
7015
7036
  } catch (error) {
@@ -8812,15 +8833,6 @@ async function getClinicsByFilters(db, filters) {
8812
8833
  if (filters.tags && filters.tags.length > 0) {
8813
8834
  constraints.push(where15("tags", "array-contains", filters.tags[0]));
8814
8835
  }
8815
- if (filters.procedureTechnology) {
8816
- constraints.push(where15("servicesInfo.technology", "==", filters.procedureTechnology));
8817
- } else if (filters.procedureSubcategory) {
8818
- constraints.push(where15("servicesInfo.subCategory", "==", filters.procedureSubcategory));
8819
- } else if (filters.procedureCategory) {
8820
- constraints.push(where15("servicesInfo.category", "==", filters.procedureCategory));
8821
- } else if (filters.procedureFamily) {
8822
- constraints.push(where15("servicesInfo.procedureFamily", "==", filters.procedureFamily));
8823
- }
8824
8836
  if (filters.minRating !== void 0) {
8825
8837
  constraints.push(where15("reviewInfo.averageRating", ">=", filters.minRating));
8826
8838
  }
@@ -8910,15 +8922,6 @@ async function getClinicsByFilters(db, filters) {
8910
8922
  const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8911
8923
  const querySnapshot = await getDocs15(q);
8912
8924
  let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
8913
- if (filters.nameSearch && filters.nameSearch.trim()) {
8914
- const searchTerm = filters.nameSearch.trim().toLowerCase();
8915
- clinics = clinics.filter((clinic) => {
8916
- const name = (clinic.name || "").toLowerCase();
8917
- const nameLower = clinic.nameLower || "";
8918
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
8919
- });
8920
- console.log(`[CLINIC_SERVICE] Applied name filter, results: ${clinics.length}`);
8921
- }
8922
8925
  clinics = applyInMemoryFilters(clinics, filters);
8923
8926
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8924
8927
  console.log(`[CLINIC_SERVICE] Strategy 3 success: ${clinics.length} clinics`);
@@ -8939,15 +8942,6 @@ async function getClinicsByFilters(db, filters) {
8939
8942
  const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8940
8943
  const querySnapshot = await getDocs15(q);
8941
8944
  let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
8942
- if (filters.nameSearch && filters.nameSearch.trim()) {
8943
- const searchTerm = filters.nameSearch.trim().toLowerCase();
8944
- clinics = clinics.filter((clinic) => {
8945
- const name = (clinic.name || "").toLowerCase();
8946
- const nameLower = clinic.nameLower || "";
8947
- return name.includes(searchTerm) || nameLower.includes(searchTerm);
8948
- });
8949
- console.log(`[CLINIC_SERVICE] Applied name filter, results: ${clinics.length}`);
8950
- }
8951
8945
  clinics = applyInMemoryFilters(clinics, filters);
8952
8946
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
8953
8947
  console.log(`[CLINIC_SERVICE] Strategy 4 success: ${clinics.length} clinics`);
@@ -8966,21 +8960,96 @@ async function getClinicsByFilters(db, filters) {
8966
8960
  }
8967
8961
  }
8968
8962
  function applyInMemoryFilters(clinics, filters) {
8963
+ let filteredClinics = [...clinics];
8964
+ console.log(`[CLINIC_SERVICE] Applying in-memory filters - input: ${filteredClinics.length} clinics`);
8969
8965
  if (filters.tags && filters.tags.length > 1) {
8970
- clinics = clinics.filter((clinic) => filters.tags.every((tag) => clinic.tags.includes(tag)));
8966
+ const initialCount = filteredClinics.length;
8967
+ filteredClinics = filteredClinics.filter(
8968
+ (clinic) => filters.tags.every((tag) => clinic.tags.includes(tag))
8969
+ );
8970
+ console.log(`[CLINIC_SERVICE] Applied multi-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8971
+ }
8972
+ if (filters.tags && filters.tags.length === 1) {
8973
+ const initialCount = filteredClinics.length;
8974
+ filteredClinics = filteredClinics.filter(
8975
+ (clinic) => clinic.tags.includes(filters.tags[0])
8976
+ );
8977
+ console.log(`[CLINIC_SERVICE] Applied single-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8978
+ }
8979
+ if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
8980
+ const initialCount = filteredClinics.length;
8981
+ filteredClinics = filteredClinics.filter((clinic) => {
8982
+ var _a;
8983
+ const rating = ((_a = clinic.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
8984
+ if (filters.minRating !== void 0 && rating < filters.minRating) return false;
8985
+ if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
8986
+ return true;
8987
+ });
8988
+ console.log(`[CLINIC_SERVICE] Applied rating filter (${filters.minRating}-${filters.maxRating}): ${initialCount} \u2192 ${filteredClinics.length}`);
8989
+ }
8990
+ if (filters.nameSearch && filters.nameSearch.trim()) {
8991
+ const initialCount = filteredClinics.length;
8992
+ const searchTerm = filters.nameSearch.trim().toLowerCase();
8993
+ filteredClinics = filteredClinics.filter((clinic) => {
8994
+ const name = (clinic.name || "").toLowerCase();
8995
+ const nameLower = clinic.nameLower || "";
8996
+ return name.includes(searchTerm) || nameLower.includes(searchTerm);
8997
+ });
8998
+ console.log(`[CLINIC_SERVICE] Applied name search filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8999
+ }
9000
+ if (filters.procedureFamily) {
9001
+ const initialCount = filteredClinics.length;
9002
+ filteredClinics = filteredClinics.filter((clinic) => {
9003
+ const proceduresInfo = clinic.proceduresInfo || [];
9004
+ return proceduresInfo.some((proc) => proc.family === filters.procedureFamily);
9005
+ });
9006
+ console.log(`[CLINIC_SERVICE] Applied procedure family filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9007
+ }
9008
+ if (filters.procedureCategory) {
9009
+ const initialCount = filteredClinics.length;
9010
+ filteredClinics = filteredClinics.filter((clinic) => {
9011
+ const proceduresInfo = clinic.proceduresInfo || [];
9012
+ return proceduresInfo.some((proc) => proc.categoryName === filters.procedureCategory);
9013
+ });
9014
+ console.log(`[CLINIC_SERVICE] Applied procedure category filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9015
+ }
9016
+ if (filters.procedureSubcategory) {
9017
+ const initialCount = filteredClinics.length;
9018
+ filteredClinics = filteredClinics.filter((clinic) => {
9019
+ const proceduresInfo = clinic.proceduresInfo || [];
9020
+ return proceduresInfo.some((proc) => proc.subcategoryName === filters.procedureSubcategory);
9021
+ });
9022
+ console.log(`[CLINIC_SERVICE] Applied procedure subcategory filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9023
+ }
9024
+ if (filters.procedureTechnology) {
9025
+ const initialCount = filteredClinics.length;
9026
+ filteredClinics = filteredClinics.filter((clinic) => {
9027
+ const proceduresInfo = clinic.proceduresInfo || [];
9028
+ return proceduresInfo.some((proc) => proc.technologyName === filters.procedureTechnology);
9029
+ });
9030
+ console.log(`[CLINIC_SERVICE] Applied procedure technology filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8971
9031
  }
8972
9032
  if (filters.center && filters.radiusInKm) {
8973
- clinics = clinics.filter((clinic) => {
9033
+ const initialCount = filteredClinics.length;
9034
+ filteredClinics = filteredClinics.filter((clinic) => {
9035
+ var _a, _b;
9036
+ if (!((_a = clinic.location) == null ? void 0 : _a.latitude) || !((_b = clinic.location) == null ? void 0 : _b.longitude)) {
9037
+ console.log(`[CLINIC_SERVICE] Clinic ${clinic.id} missing location data`);
9038
+ return false;
9039
+ }
8974
9040
  const distance = distanceBetween4(
8975
9041
  [filters.center.latitude, filters.center.longitude],
8976
9042
  [clinic.location.latitude, clinic.location.longitude]
8977
9043
  ) / 1e3;
9044
+ console.log(`[CLINIC_SERVICE] Clinic ${clinic.name}: distance ${distance.toFixed(2)}km (limit: ${filters.radiusInKm}km)`);
8978
9045
  clinic.distance = distance;
8979
9046
  return distance <= filters.radiusInKm;
8980
9047
  });
8981
- clinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
9048
+ console.log(`[CLINIC_SERVICE] Applied geo filter (${filters.radiusInKm}km): ${initialCount} \u2192 ${filteredClinics.length}`);
9049
+ filteredClinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
8982
9050
  }
8983
- return clinics;
9051
+ console.log(`[CLINIC_SERVICE] Final filtered result: ${filteredClinics.length} clinics`);
9052
+ return filteredClinics;
8984
9053
  }
8985
9054
 
8986
9055
  // src/services/clinic/clinic.service.ts
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.16",
4
+ "version": "1.8.18",
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
  }
@@ -1215,6 +1215,37 @@ export class PractitionerService extends BaseService {
1215
1215
  console.log("[PRACTITIONER_SERVICE] Strategy 3 failed:", error);
1216
1216
  }
1217
1217
 
1218
+ // Strategy 4: Client-side filtering fallback (kao u procedure/clinic services)
1219
+ try {
1220
+ console.log("[PRACTITIONER_SERVICE] Strategy 4: Client-side filtering fallback");
1221
+
1222
+ const constraints: any[] = [
1223
+ where("isActive", "==", true),
1224
+ where("status", "==", PractitionerStatus.ACTIVE),
1225
+ orderBy("createdAt", "desc"),
1226
+ limit(filters.pagination || 10)
1227
+ ];
1228
+
1229
+ const q = query(collection(this.db, PRACTITIONERS_COLLECTION), ...constraints);
1230
+ const querySnapshot = await getDocs(q);
1231
+ let practitioners = querySnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id } as Practitioner));
1232
+
1233
+ // Apply all client-side filters using centralized function
1234
+ practitioners = this.applyInMemoryFilters(practitioners, filters);
1235
+
1236
+ const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
1237
+ console.log(`[PRACTITIONER_SERVICE] Strategy 4 success: ${practitioners.length} practitioners`);
1238
+
1239
+ // Fix Load More - ako je broj rezultata manji od pagination, nema viΕ‘e
1240
+ if (practitioners.length < (filters.pagination || 10)) {
1241
+ return { practitioners, lastDoc: null };
1242
+ }
1243
+ return { practitioners, lastDoc };
1244
+
1245
+ } catch (error) {
1246
+ console.log("[PRACTITIONER_SERVICE] Strategy 4 failed:", error);
1247
+ }
1248
+
1218
1249
  // All strategies failed
1219
1250
  console.log("[PRACTITIONER_SERVICE] All strategies failed, returning empty result");
1220
1251
  return { practitioners: [], lastDoc: null };