@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.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +268 -90
- package/dist/index.mjs +268 -90
- package/package.json +1 -1
- package/src/services/clinic/utils/filter.utils.ts +103 -45
- package/src/services/practitioner/practitioner.service.ts +118 -33
- package/src/services/procedure/procedure.service.ts +111 -44
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",
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
@@ -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
|
}
|