@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 +100 -31
- package/dist/index.mjs +100 -31
- package/package.json +1 -1
- package/src/services/clinic/utils/filter.utils.ts +103 -45
- package/src/services/practitioner/practitioner.service.ts +31 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
357
|
+
filteredClinics.sort((a, b) => ((a as any).distance || 0) - ((b as any).distance || 0));
|
|
301
358
|
}
|
|
302
|
-
|
|
303
|
-
|
|
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 };
|