@blackcode_sa/metaestetics-api 1.8.14 → 1.8.16
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 +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +666 -214
- package/dist/index.mjs +667 -215
- package/package.json +1 -1
- package/src/services/clinic/utils/filter.utils.ts +242 -57
- package/src/services/practitioner/practitioner.service.ts +277 -81
- package/src/services/procedure/procedure.service.ts +346 -123
package/dist/index.mjs
CHANGED
|
@@ -6879,81 +6879,235 @@ var PractitionerService = class extends BaseService {
|
|
|
6879
6879
|
*/
|
|
6880
6880
|
async getPractitionersByFilters(filters) {
|
|
6881
6881
|
try {
|
|
6882
|
-
|
|
6883
|
-
if (
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
);
|
|
6882
|
+
console.log("[PRACTITIONER_SERVICE] Starting practitioner filtering with fallback strategies");
|
|
6883
|
+
if (filters.location && filters.radiusInKm) {
|
|
6884
|
+
console.log("[PRACTITIONER_SERVICE] Executing geo query:", {
|
|
6885
|
+
location: filters.location,
|
|
6886
|
+
radius: filters.radiusInKm,
|
|
6887
|
+
serviceName: "PractitionerService"
|
|
6888
|
+
});
|
|
6889
|
+
if (!filters.location.latitude || !filters.location.longitude) {
|
|
6890
|
+
console.warn("[PRACTITIONER_SERVICE] Invalid location data:", filters.location);
|
|
6891
|
+
filters.location = void 0;
|
|
6892
|
+
filters.radiusInKm = void 0;
|
|
6893
|
+
}
|
|
6895
6894
|
}
|
|
6896
6895
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6896
|
+
try {
|
|
6897
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 1: Trying fullNameLower search");
|
|
6898
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
6899
|
+
const constraints = [];
|
|
6900
|
+
if (!filters.includeDraftPractitioners) {
|
|
6901
|
+
constraints.push(where10("status", "==", "active" /* ACTIVE */));
|
|
6902
|
+
}
|
|
6903
|
+
constraints.push(where10("isActive", "==", true));
|
|
6904
|
+
constraints.push(where10("fullNameLower", ">=", searchTerm));
|
|
6905
|
+
constraints.push(where10("fullNameLower", "<=", searchTerm + "\uF8FF"));
|
|
6906
|
+
constraints.push(orderBy4("fullNameLower"));
|
|
6907
|
+
if (filters.lastDoc) {
|
|
6908
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
6909
|
+
constraints.push(startAfter6(filters.lastDoc));
|
|
6910
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
6911
|
+
constraints.push(startAfter6(...filters.lastDoc));
|
|
6912
|
+
} else {
|
|
6913
|
+
constraints.push(startAfter6(filters.lastDoc));
|
|
6914
|
+
}
|
|
6915
|
+
}
|
|
6916
|
+
constraints.push(limit7(filters.pagination || 10));
|
|
6917
|
+
const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
|
|
6918
|
+
const querySnapshot = await getDocs10(q);
|
|
6919
|
+
const practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
6920
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6921
|
+
console.log(`[PRACTITIONER_SERVICE] Strategy 1 success: ${practitioners.length} practitioners`);
|
|
6922
|
+
if (practitioners.length < (filters.pagination || 10)) {
|
|
6923
|
+
return { practitioners, lastDoc: null };
|
|
6924
|
+
}
|
|
6925
|
+
return { practitioners, lastDoc };
|
|
6926
|
+
} catch (error) {
|
|
6927
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 1 failed:", error);
|
|
6928
|
+
}
|
|
6915
6929
|
}
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6930
|
+
try {
|
|
6931
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 2: Basic query with createdAt ordering");
|
|
6932
|
+
const constraints = [];
|
|
6933
|
+
if (!filters.includeDraftPractitioners) {
|
|
6934
|
+
constraints.push(where10("status", "==", "active" /* ACTIVE */));
|
|
6935
|
+
}
|
|
6936
|
+
constraints.push(where10("isActive", "==", true));
|
|
6937
|
+
if (filters.certifications && filters.certifications.length > 0) {
|
|
6938
|
+
const certificationsToMatch = filters.certifications;
|
|
6939
|
+
constraints.push(
|
|
6940
|
+
where10("certification.specialties", "array-contains-any", certificationsToMatch)
|
|
6941
|
+
);
|
|
6942
|
+
}
|
|
6943
|
+
if (filters.minRating !== void 0) {
|
|
6944
|
+
constraints.push(where10("reviewInfo.averageRating", ">=", filters.minRating));
|
|
6945
|
+
}
|
|
6946
|
+
if (filters.maxRating !== void 0) {
|
|
6947
|
+
constraints.push(where10("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
6948
|
+
}
|
|
6949
|
+
constraints.push(orderBy4("createdAt", "desc"));
|
|
6950
|
+
if (filters.location && filters.radiusInKm) {
|
|
6951
|
+
constraints.push(limit7((filters.pagination || 10) * 2));
|
|
6922
6952
|
} else {
|
|
6923
|
-
|
|
6953
|
+
if (filters.lastDoc) {
|
|
6954
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
6955
|
+
constraints.push(startAfter6(filters.lastDoc));
|
|
6956
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
6957
|
+
constraints.push(startAfter6(...filters.lastDoc));
|
|
6958
|
+
} else {
|
|
6959
|
+
constraints.push(startAfter6(filters.lastDoc));
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
constraints.push(limit7(filters.pagination || 10));
|
|
6924
6963
|
}
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
return distanceInKm <= radiusInKm;
|
|
6964
|
+
const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
|
|
6965
|
+
const querySnapshot = await getDocs10(q);
|
|
6966
|
+
let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
6967
|
+
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
6968
|
+
const location = filters.location;
|
|
6969
|
+
const radiusInKm = filters.radiusInKm;
|
|
6970
|
+
practitioners = practitioners.filter((practitioner) => {
|
|
6971
|
+
const clinics = practitioner.clinicsInfo || [];
|
|
6972
|
+
return clinics.some((clinic) => {
|
|
6973
|
+
const distance = distanceBetween(
|
|
6974
|
+
[location.latitude, location.longitude],
|
|
6975
|
+
[clinic.location.latitude, clinic.location.longitude]
|
|
6976
|
+
);
|
|
6977
|
+
const distanceInKm = distance / 1e3;
|
|
6978
|
+
return distanceInKm <= radiusInKm;
|
|
6979
|
+
});
|
|
6942
6980
|
});
|
|
6943
|
-
|
|
6981
|
+
practitioners = practitioners.slice(0, filters.pagination || 10);
|
|
6982
|
+
}
|
|
6983
|
+
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
6984
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6985
|
+
console.log(`[PRACTITIONER_SERVICE] Strategy 2 success: ${practitioners.length} practitioners`);
|
|
6986
|
+
if (practitioners.length < (filters.pagination || 10)) {
|
|
6987
|
+
return { practitioners, lastDoc: null };
|
|
6988
|
+
}
|
|
6989
|
+
return { practitioners, lastDoc };
|
|
6990
|
+
} catch (error) {
|
|
6991
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 2 failed:", error);
|
|
6944
6992
|
}
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6993
|
+
try {
|
|
6994
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 3: Minimal query fallback");
|
|
6995
|
+
const constraints = [
|
|
6996
|
+
where10("isActive", "==", true),
|
|
6997
|
+
orderBy4("createdAt", "desc"),
|
|
6998
|
+
limit7(filters.pagination || 10)
|
|
6999
|
+
];
|
|
7000
|
+
const q = query10(collection10(this.db, PRACTITIONERS_COLLECTION), ...constraints);
|
|
7001
|
+
const querySnapshot = await getDocs10(q);
|
|
7002
|
+
let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
7003
|
+
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
7004
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
7005
|
+
console.log(`[PRACTITIONER_SERVICE] Strategy 3 success: ${practitioners.length} practitioners`);
|
|
7006
|
+
if (practitioners.length < (filters.pagination || 10)) {
|
|
7007
|
+
return { practitioners, lastDoc: null };
|
|
7008
|
+
}
|
|
7009
|
+
return { practitioners, lastDoc };
|
|
7010
|
+
} catch (error) {
|
|
7011
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 3 failed:", error);
|
|
7012
|
+
}
|
|
7013
|
+
console.log("[PRACTITIONER_SERVICE] All strategies failed, returning empty result");
|
|
7014
|
+
return { practitioners: [], lastDoc: null };
|
|
6950
7015
|
} catch (error) {
|
|
6951
|
-
console.error(
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
7016
|
+
console.error("[PRACTITIONER_SERVICE] Error filtering practitioners:", error);
|
|
7017
|
+
return { practitioners: [], lastDoc: null };
|
|
7018
|
+
}
|
|
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}`);
|
|
6956
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;
|
|
6957
7111
|
}
|
|
6958
7112
|
/**
|
|
6959
7113
|
* Enables free consultation for a practitioner in a specific clinic
|
|
@@ -8637,49 +8791,181 @@ import {
|
|
|
8637
8791
|
} from "firebase/firestore";
|
|
8638
8792
|
import { distanceBetween as distanceBetween4 } from "geofire-common";
|
|
8639
8793
|
async function getClinicsByFilters(db, filters) {
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
constraints
|
|
8794
|
+
try {
|
|
8795
|
+
console.log("[CLINIC_SERVICE] Starting clinic filtering with multiple strategies");
|
|
8796
|
+
if (filters.center && filters.radiusInKm) {
|
|
8797
|
+
console.log("[CLINIC_SERVICE] Executing geo query:", {
|
|
8798
|
+
center: filters.center,
|
|
8799
|
+
radius: filters.radiusInKm,
|
|
8800
|
+
serviceName: "ClinicService"
|
|
8801
|
+
});
|
|
8802
|
+
if (!filters.center.latitude || !filters.center.longitude) {
|
|
8803
|
+
console.warn("[CLINIC_SERVICE] Invalid location data:", filters.center);
|
|
8804
|
+
filters.center = void 0;
|
|
8805
|
+
filters.radiusInKm = void 0;
|
|
8806
|
+
}
|
|
8807
|
+
}
|
|
8808
|
+
const getBaseConstraints = () => {
|
|
8809
|
+
var _a;
|
|
8810
|
+
const constraints = [];
|
|
8811
|
+
constraints.push(where15("isActive", "==", (_a = filters.isActive) != null ? _a : true));
|
|
8812
|
+
if (filters.tags && filters.tags.length > 0) {
|
|
8813
|
+
constraints.push(where15("tags", "array-contains", filters.tags[0]));
|
|
8814
|
+
}
|
|
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
|
+
if (filters.minRating !== void 0) {
|
|
8825
|
+
constraints.push(where15("reviewInfo.averageRating", ">=", filters.minRating));
|
|
8826
|
+
}
|
|
8827
|
+
if (filters.maxRating !== void 0) {
|
|
8828
|
+
constraints.push(where15("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
8829
|
+
}
|
|
8830
|
+
return constraints;
|
|
8831
|
+
};
|
|
8832
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
8833
|
+
try {
|
|
8834
|
+
console.log("[CLINIC_SERVICE] Strategy 1: Trying nameLower search");
|
|
8835
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
8836
|
+
const constraints = getBaseConstraints();
|
|
8837
|
+
constraints.push(where15("nameLower", ">=", searchTerm));
|
|
8838
|
+
constraints.push(where15("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
8839
|
+
constraints.push(orderBy5("nameLower"));
|
|
8840
|
+
if (filters.lastDoc) {
|
|
8841
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
8842
|
+
constraints.push(startAfter8(filters.lastDoc));
|
|
8843
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
8844
|
+
constraints.push(startAfter8(...filters.lastDoc));
|
|
8845
|
+
} else {
|
|
8846
|
+
constraints.push(startAfter8(filters.lastDoc));
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
constraints.push(limit9(filters.pagination || 5));
|
|
8850
|
+
const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
|
|
8851
|
+
const querySnapshot = await getDocs15(q);
|
|
8852
|
+
let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
8853
|
+
clinics = applyInMemoryFilters(clinics, filters);
|
|
8854
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8855
|
+
console.log(`[CLINIC_SERVICE] Strategy 1 success: ${clinics.length} clinics`);
|
|
8856
|
+
if (clinics.length < (filters.pagination || 5)) {
|
|
8857
|
+
return { clinics, lastDoc: null };
|
|
8858
|
+
}
|
|
8859
|
+
return { clinics, lastDoc };
|
|
8860
|
+
} catch (error) {
|
|
8861
|
+
console.log("[CLINIC_SERVICE] Strategy 1 failed:", error);
|
|
8862
|
+
}
|
|
8677
8863
|
}
|
|
8864
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
8865
|
+
try {
|
|
8866
|
+
console.log("[CLINIC_SERVICE] Strategy 2: Trying name field search");
|
|
8867
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
8868
|
+
const constraints = getBaseConstraints();
|
|
8869
|
+
constraints.push(where15("name", ">=", searchTerm));
|
|
8870
|
+
constraints.push(where15("name", "<=", searchTerm + "\uF8FF"));
|
|
8871
|
+
constraints.push(orderBy5("name"));
|
|
8872
|
+
if (filters.lastDoc) {
|
|
8873
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
8874
|
+
constraints.push(startAfter8(filters.lastDoc));
|
|
8875
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
8876
|
+
constraints.push(startAfter8(...filters.lastDoc));
|
|
8877
|
+
} else {
|
|
8878
|
+
constraints.push(startAfter8(filters.lastDoc));
|
|
8879
|
+
}
|
|
8880
|
+
}
|
|
8881
|
+
constraints.push(limit9(filters.pagination || 5));
|
|
8882
|
+
const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
|
|
8883
|
+
const querySnapshot = await getDocs15(q);
|
|
8884
|
+
let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
8885
|
+
clinics = applyInMemoryFilters(clinics, filters);
|
|
8886
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8887
|
+
console.log(`[CLINIC_SERVICE] Strategy 2 success: ${clinics.length} clinics`);
|
|
8888
|
+
if (clinics.length < (filters.pagination || 5)) {
|
|
8889
|
+
return { clinics, lastDoc: null };
|
|
8890
|
+
}
|
|
8891
|
+
return { clinics, lastDoc };
|
|
8892
|
+
} catch (error) {
|
|
8893
|
+
console.log("[CLINIC_SERVICE] Strategy 2 failed:", error);
|
|
8894
|
+
}
|
|
8895
|
+
}
|
|
8896
|
+
try {
|
|
8897
|
+
console.log("[CLINIC_SERVICE] Strategy 3: Using createdAt ordering with client-side filtering");
|
|
8898
|
+
const constraints = getBaseConstraints();
|
|
8899
|
+
constraints.push(orderBy5("createdAt", "desc"));
|
|
8900
|
+
if (filters.lastDoc) {
|
|
8901
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
8902
|
+
constraints.push(startAfter8(filters.lastDoc));
|
|
8903
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
8904
|
+
constraints.push(startAfter8(...filters.lastDoc));
|
|
8905
|
+
} else {
|
|
8906
|
+
constraints.push(startAfter8(filters.lastDoc));
|
|
8907
|
+
}
|
|
8908
|
+
}
|
|
8909
|
+
constraints.push(limit9(filters.pagination || 5));
|
|
8910
|
+
const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
|
|
8911
|
+
const querySnapshot = await getDocs15(q);
|
|
8912
|
+
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
|
+
clinics = applyInMemoryFilters(clinics, filters);
|
|
8923
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8924
|
+
console.log(`[CLINIC_SERVICE] Strategy 3 success: ${clinics.length} clinics`);
|
|
8925
|
+
if (clinics.length < (filters.pagination || 5)) {
|
|
8926
|
+
return { clinics, lastDoc: null };
|
|
8927
|
+
}
|
|
8928
|
+
return { clinics, lastDoc };
|
|
8929
|
+
} catch (error) {
|
|
8930
|
+
console.log("[CLINIC_SERVICE] Strategy 3 failed:", error);
|
|
8931
|
+
}
|
|
8932
|
+
try {
|
|
8933
|
+
console.log("[CLINIC_SERVICE] Strategy 4: Minimal fallback");
|
|
8934
|
+
const constraints = [
|
|
8935
|
+
where15("isActive", "==", true),
|
|
8936
|
+
orderBy5("createdAt", "desc"),
|
|
8937
|
+
limit9(filters.pagination || 5)
|
|
8938
|
+
];
|
|
8939
|
+
const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
|
|
8940
|
+
const querySnapshot = await getDocs15(q);
|
|
8941
|
+
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
|
+
clinics = applyInMemoryFilters(clinics, filters);
|
|
8952
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8953
|
+
console.log(`[CLINIC_SERVICE] Strategy 4 success: ${clinics.length} clinics`);
|
|
8954
|
+
if (clinics.length < (filters.pagination || 5)) {
|
|
8955
|
+
return { clinics, lastDoc: null };
|
|
8956
|
+
}
|
|
8957
|
+
return { clinics, lastDoc };
|
|
8958
|
+
} catch (error) {
|
|
8959
|
+
console.log("[CLINIC_SERVICE] Strategy 4 failed:", error);
|
|
8960
|
+
}
|
|
8961
|
+
console.log("[CLINIC_SERVICE] All strategies failed, returning empty result");
|
|
8962
|
+
return { clinics: [], lastDoc: null };
|
|
8963
|
+
} catch (error) {
|
|
8964
|
+
console.error("[CLINIC_SERVICE] Error filtering clinics:", error);
|
|
8965
|
+
return { clinics: [], lastDoc: null };
|
|
8678
8966
|
}
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
const querySnapshot = await getDocs15(q);
|
|
8682
|
-
let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
8967
|
+
}
|
|
8968
|
+
function applyInMemoryFilters(clinics, filters) {
|
|
8683
8969
|
if (filters.tags && filters.tags.length > 1) {
|
|
8684
8970
|
clinics = clinics.filter((clinic) => filters.tags.every((tag) => clinic.tags.includes(tag)));
|
|
8685
8971
|
}
|
|
@@ -8692,9 +8978,9 @@ async function getClinicsByFilters(db, filters) {
|
|
|
8692
8978
|
clinic.distance = distance;
|
|
8693
8979
|
return distance <= filters.radiusInKm;
|
|
8694
8980
|
});
|
|
8981
|
+
clinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
|
|
8695
8982
|
}
|
|
8696
|
-
|
|
8697
|
-
return { clinics, lastDoc: lastVisibleDoc };
|
|
8983
|
+
return clinics;
|
|
8698
8984
|
}
|
|
8699
8985
|
|
|
8700
8986
|
// src/services/clinic/clinic.service.ts
|
|
@@ -14531,7 +14817,7 @@ var procedureSchema = createProcedureSchema.extend({
|
|
|
14531
14817
|
});
|
|
14532
14818
|
|
|
14533
14819
|
// src/services/procedure/procedure.service.ts
|
|
14534
|
-
import { distanceBetween as distanceBetween6
|
|
14820
|
+
import { distanceBetween as distanceBetween6 } from "geofire-common";
|
|
14535
14821
|
var ProcedureService = class extends BaseService {
|
|
14536
14822
|
constructor(db, auth, app, categoryService, subcategoryService, technologyService, productService, mediaService) {
|
|
14537
14823
|
super(db, auth, app);
|
|
@@ -15160,126 +15446,292 @@ var ProcedureService = class extends BaseService {
|
|
|
15160
15446
|
*/
|
|
15161
15447
|
async getProceduresByFilters(filters) {
|
|
15162
15448
|
try {
|
|
15163
|
-
|
|
15164
|
-
|
|
15165
|
-
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
}
|
|
15176
|
-
if (filters.procedureSubcategory) {
|
|
15177
|
-
constraints.push(where29("subcategory.id", "==", filters.procedureSubcategory));
|
|
15178
|
-
}
|
|
15179
|
-
if (filters.procedureTechnology) {
|
|
15180
|
-
constraints.push(where29("technology.id", "==", filters.procedureTechnology));
|
|
15181
|
-
}
|
|
15182
|
-
if (filters.minPrice !== void 0) {
|
|
15183
|
-
constraints.push(where29("price", ">=", filters.minPrice));
|
|
15184
|
-
}
|
|
15185
|
-
if (filters.maxPrice !== void 0) {
|
|
15186
|
-
constraints.push(where29("price", "<=", filters.maxPrice));
|
|
15187
|
-
}
|
|
15188
|
-
if (filters.minRating !== void 0) {
|
|
15189
|
-
constraints.push(where29("reviewInfo.averageRating", ">=", filters.minRating));
|
|
15190
|
-
}
|
|
15191
|
-
if (filters.maxRating !== void 0) {
|
|
15192
|
-
constraints.push(where29("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
15193
|
-
}
|
|
15194
|
-
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
15195
|
-
constraints.push(where29("treatmentBenefits", "array-contains-any", filters.treatmentBenefits));
|
|
15449
|
+
console.log("[PROCEDURE_SERVICE] Starting procedure filtering with multiple strategies");
|
|
15450
|
+
if (filters.location && filters.radiusInKm) {
|
|
15451
|
+
console.log("[PROCEDURE_SERVICE] Executing geo query:", {
|
|
15452
|
+
location: filters.location,
|
|
15453
|
+
radius: filters.radiusInKm,
|
|
15454
|
+
serviceName: "ProcedureService"
|
|
15455
|
+
});
|
|
15456
|
+
if (!filters.location.latitude || !filters.location.longitude) {
|
|
15457
|
+
console.warn("[PROCEDURE_SERVICE] Invalid location data:", filters.location);
|
|
15458
|
+
filters.location = void 0;
|
|
15459
|
+
filters.radiusInKm = void 0;
|
|
15460
|
+
}
|
|
15196
15461
|
}
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15201
|
-
useNameLower = true;
|
|
15202
|
-
constraints.push(where29("nameLower", ">=", searchTerm));
|
|
15203
|
-
constraints.push(where29("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
15204
|
-
constraints.push(orderBy17("nameLower"));
|
|
15205
|
-
} else {
|
|
15206
|
-
constraints.push(orderBy17("nameLower"));
|
|
15462
|
+
const isGeoQuery = filters.location && filters.radiusInKm && filters.radiusInKm > 0;
|
|
15463
|
+
if (isGeoQuery) {
|
|
15464
|
+
return this.handleGeoQuery(filters);
|
|
15207
15465
|
}
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
constraints.push(startAfter13(...filters.lastDoc));
|
|
15466
|
+
const getBaseConstraints = () => {
|
|
15467
|
+
const constraints = [];
|
|
15468
|
+
if (filters.isActive !== void 0) {
|
|
15469
|
+
constraints.push(where29("isActive", "==", filters.isActive));
|
|
15213
15470
|
} else {
|
|
15214
|
-
constraints.push(
|
|
15471
|
+
constraints.push(where29("isActive", "==", true));
|
|
15215
15472
|
}
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
|
|
15233
|
-
where29("clinicInfo.location.geohash", ">=", bound[0]),
|
|
15234
|
-
where29("clinicInfo.location.geohash", "<=", bound[1]),
|
|
15235
|
-
orderBy17("clinicInfo.location.geohash")
|
|
15236
|
-
];
|
|
15237
|
-
const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...geoConstraints);
|
|
15238
|
-
const querySnapshot = await getDocs29(q);
|
|
15239
|
-
for (const doc37 of querySnapshot.docs) {
|
|
15240
|
-
const procedure = { ...doc37.data(), id: doc37.id };
|
|
15241
|
-
const distance = distanceBetween6(
|
|
15242
|
-
[center.latitude, center.longitude],
|
|
15243
|
-
[procedure.clinicInfo.location.latitude, procedure.clinicInfo.location.longitude]
|
|
15244
|
-
);
|
|
15245
|
-
const distanceInKm = distance / 1e3;
|
|
15246
|
-
if (distanceInKm <= radiusInKm) {
|
|
15247
|
-
allDocs.push({ ...procedure, distance: distanceInKm });
|
|
15248
|
-
}
|
|
15249
|
-
}
|
|
15473
|
+
if (filters.procedureFamily) {
|
|
15474
|
+
constraints.push(where29("family", "==", filters.procedureFamily));
|
|
15475
|
+
}
|
|
15476
|
+
if (filters.procedureCategory) {
|
|
15477
|
+
constraints.push(where29("category.id", "==", filters.procedureCategory));
|
|
15478
|
+
}
|
|
15479
|
+
if (filters.procedureSubcategory) {
|
|
15480
|
+
constraints.push(where29("subcategory.id", "==", filters.procedureSubcategory));
|
|
15481
|
+
}
|
|
15482
|
+
if (filters.procedureTechnology) {
|
|
15483
|
+
constraints.push(where29("technology.id", "==", filters.procedureTechnology));
|
|
15484
|
+
}
|
|
15485
|
+
if (filters.minPrice !== void 0) {
|
|
15486
|
+
constraints.push(where29("price", ">=", filters.minPrice));
|
|
15487
|
+
}
|
|
15488
|
+
if (filters.maxPrice !== void 0) {
|
|
15489
|
+
constraints.push(where29("price", "<=", filters.maxPrice));
|
|
15250
15490
|
}
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15491
|
+
if (filters.minRating !== void 0) {
|
|
15492
|
+
constraints.push(where29("reviewInfo.averageRating", ">=", filters.minRating));
|
|
15493
|
+
}
|
|
15494
|
+
if (filters.maxRating !== void 0) {
|
|
15495
|
+
constraints.push(where29("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
15496
|
+
}
|
|
15497
|
+
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
15498
|
+
const benefitsToMatch = filters.treatmentBenefits;
|
|
15499
|
+
constraints.push(where29("treatmentBenefits", "array-contains-any", benefitsToMatch));
|
|
15500
|
+
}
|
|
15501
|
+
return constraints;
|
|
15502
|
+
};
|
|
15503
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
15504
|
+
try {
|
|
15505
|
+
console.log("[PROCEDURE_SERVICE] Strategy 1: Trying nameLower search");
|
|
15506
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15507
|
+
const constraints = getBaseConstraints();
|
|
15508
|
+
constraints.push(where29("nameLower", ">=", searchTerm));
|
|
15509
|
+
constraints.push(where29("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
15510
|
+
constraints.push(orderBy17("nameLower"));
|
|
15255
15511
|
if (filters.lastDoc) {
|
|
15256
|
-
|
|
15257
|
-
|
|
15512
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
15513
|
+
constraints.push(startAfter13(filters.lastDoc));
|
|
15514
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
15515
|
+
constraints.push(startAfter13(...filters.lastDoc));
|
|
15516
|
+
} else {
|
|
15517
|
+
constraints.push(startAfter13(filters.lastDoc));
|
|
15518
|
+
}
|
|
15258
15519
|
}
|
|
15259
|
-
|
|
15520
|
+
constraints.push(limit15(filters.pagination || 10));
|
|
15521
|
+
const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15522
|
+
const querySnapshot = await getDocs29(q);
|
|
15523
|
+
const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15524
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15525
|
+
console.log(`[PROCEDURE_SERVICE] Strategy 1 success: ${procedures.length} procedures`);
|
|
15526
|
+
if (procedures.length < (filters.pagination || 10)) {
|
|
15527
|
+
return { procedures, lastDoc: null };
|
|
15528
|
+
}
|
|
15529
|
+
return { procedures, lastDoc };
|
|
15530
|
+
} catch (error) {
|
|
15531
|
+
console.log("[PROCEDURE_SERVICE] Strategy 1 failed:", error);
|
|
15260
15532
|
}
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
constraints.pop();
|
|
15268
|
-
constraints.pop();
|
|
15269
|
-
constraints.pop();
|
|
15533
|
+
}
|
|
15534
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
15535
|
+
try {
|
|
15536
|
+
console.log("[PROCEDURE_SERVICE] Strategy 2: Trying name field search");
|
|
15537
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15538
|
+
const constraints = getBaseConstraints();
|
|
15270
15539
|
constraints.push(where29("name", ">=", searchTerm));
|
|
15271
15540
|
constraints.push(where29("name", "<=", searchTerm + "\uF8FF"));
|
|
15272
15541
|
constraints.push(orderBy17("name"));
|
|
15273
|
-
|
|
15274
|
-
|
|
15542
|
+
if (filters.lastDoc) {
|
|
15543
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
15544
|
+
constraints.push(startAfter13(filters.lastDoc));
|
|
15545
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
15546
|
+
constraints.push(startAfter13(...filters.lastDoc));
|
|
15547
|
+
} else {
|
|
15548
|
+
constraints.push(startAfter13(filters.lastDoc));
|
|
15549
|
+
}
|
|
15550
|
+
}
|
|
15551
|
+
constraints.push(limit15(filters.pagination || 10));
|
|
15552
|
+
const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15553
|
+
const querySnapshot = await getDocs29(q);
|
|
15554
|
+
const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15555
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15556
|
+
console.log(`[PROCEDURE_SERVICE] Strategy 2 success: ${procedures.length} procedures`);
|
|
15557
|
+
if (procedures.length < (filters.pagination || 10)) {
|
|
15558
|
+
return { procedures, lastDoc: null };
|
|
15559
|
+
}
|
|
15560
|
+
return { procedures, lastDoc };
|
|
15561
|
+
} catch (error) {
|
|
15562
|
+
console.log("[PROCEDURE_SERVICE] Strategy 2 failed:", error);
|
|
15275
15563
|
}
|
|
15276
|
-
const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15277
|
-
const lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15278
|
-
return { procedures, lastDoc: lastVisibleDoc };
|
|
15279
15564
|
}
|
|
15565
|
+
try {
|
|
15566
|
+
console.log("[PROCEDURE_SERVICE] Strategy 3: Using createdAt orderBy with client-side filtering");
|
|
15567
|
+
const constraints = getBaseConstraints();
|
|
15568
|
+
constraints.push(orderBy17("createdAt", "desc"));
|
|
15569
|
+
if (filters.lastDoc) {
|
|
15570
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
15571
|
+
constraints.push(startAfter13(filters.lastDoc));
|
|
15572
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
15573
|
+
constraints.push(startAfter13(...filters.lastDoc));
|
|
15574
|
+
} else {
|
|
15575
|
+
constraints.push(startAfter13(filters.lastDoc));
|
|
15576
|
+
}
|
|
15577
|
+
}
|
|
15578
|
+
constraints.push(limit15(filters.pagination || 10));
|
|
15579
|
+
const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15580
|
+
const querySnapshot = await getDocs29(q);
|
|
15581
|
+
let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15582
|
+
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
15583
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15584
|
+
console.log(`[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`);
|
|
15585
|
+
if (procedures.length < (filters.pagination || 10)) {
|
|
15586
|
+
return { procedures, lastDoc: null };
|
|
15587
|
+
}
|
|
15588
|
+
return { procedures, lastDoc };
|
|
15589
|
+
} catch (error) {
|
|
15590
|
+
console.log("[PROCEDURE_SERVICE] Strategy 3 failed:", error);
|
|
15591
|
+
}
|
|
15592
|
+
try {
|
|
15593
|
+
console.log("[PROCEDURE_SERVICE] Strategy 4: Minimal query fallback");
|
|
15594
|
+
const constraints = [
|
|
15595
|
+
where29("isActive", "==", true),
|
|
15596
|
+
orderBy17("createdAt", "desc"),
|
|
15597
|
+
limit15(filters.pagination || 10)
|
|
15598
|
+
];
|
|
15599
|
+
const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15600
|
+
const querySnapshot = await getDocs29(q);
|
|
15601
|
+
let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15602
|
+
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
15603
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15604
|
+
console.log(`[PROCEDURE_SERVICE] Strategy 4 success: ${procedures.length} procedures`);
|
|
15605
|
+
if (procedures.length < (filters.pagination || 10)) {
|
|
15606
|
+
return { procedures, lastDoc: null };
|
|
15607
|
+
}
|
|
15608
|
+
return { procedures, lastDoc };
|
|
15609
|
+
} catch (error) {
|
|
15610
|
+
console.log("[PROCEDURE_SERVICE] Strategy 4 failed:", error);
|
|
15611
|
+
}
|
|
15612
|
+
console.log("[PROCEDURE_SERVICE] All strategies failed, returning empty result");
|
|
15613
|
+
return { procedures: [], lastDoc: null };
|
|
15280
15614
|
} catch (error) {
|
|
15281
15615
|
console.error("[PROCEDURE_SERVICE] Error filtering procedures:", error);
|
|
15282
|
-
|
|
15616
|
+
return { procedures: [], lastDoc: null };
|
|
15617
|
+
}
|
|
15618
|
+
}
|
|
15619
|
+
/**
|
|
15620
|
+
* Applies in-memory filters to procedures array
|
|
15621
|
+
* Used when Firestore queries fail or for complex filtering
|
|
15622
|
+
*/
|
|
15623
|
+
applyInMemoryFilters(procedures, filters) {
|
|
15624
|
+
let filteredProcedures = [...procedures];
|
|
15625
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
15626
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15627
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15628
|
+
const name = (procedure.name || "").toLowerCase();
|
|
15629
|
+
const nameLower = procedure.nameLower || "";
|
|
15630
|
+
return name.includes(searchTerm) || nameLower.includes(searchTerm);
|
|
15631
|
+
});
|
|
15632
|
+
console.log(`[PROCEDURE_SERVICE] Applied name filter, results: ${filteredProcedures.length}`);
|
|
15633
|
+
}
|
|
15634
|
+
if (filters.minPrice !== void 0 || filters.maxPrice !== void 0) {
|
|
15635
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15636
|
+
const price = procedure.price || 0;
|
|
15637
|
+
if (filters.minPrice !== void 0 && price < filters.minPrice) return false;
|
|
15638
|
+
if (filters.maxPrice !== void 0 && price > filters.maxPrice) return false;
|
|
15639
|
+
return true;
|
|
15640
|
+
});
|
|
15641
|
+
console.log(`[PROCEDURE_SERVICE] Applied price filter (${filters.minPrice}-${filters.maxPrice}), results: ${filteredProcedures.length}`);
|
|
15642
|
+
}
|
|
15643
|
+
if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
|
|
15644
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15645
|
+
var _a;
|
|
15646
|
+
const rating = ((_a = procedure.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
|
|
15647
|
+
if (filters.minRating !== void 0 && rating < filters.minRating) return false;
|
|
15648
|
+
if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
|
|
15649
|
+
return true;
|
|
15650
|
+
});
|
|
15651
|
+
console.log(`[PROCEDURE_SERVICE] Applied rating filter, results: ${filteredProcedures.length}`);
|
|
15652
|
+
}
|
|
15653
|
+
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
15654
|
+
const benefitsToMatch = filters.treatmentBenefits;
|
|
15655
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15656
|
+
const procedureBenefits = procedure.treatmentBenefits || [];
|
|
15657
|
+
return benefitsToMatch.some((benefit) => procedureBenefits.includes(benefit));
|
|
15658
|
+
});
|
|
15659
|
+
console.log(`[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`);
|
|
15660
|
+
}
|
|
15661
|
+
if (filters.procedureFamily) {
|
|
15662
|
+
filteredProcedures = filteredProcedures.filter((procedure) => procedure.family === filters.procedureFamily);
|
|
15663
|
+
console.log(`[PROCEDURE_SERVICE] Applied family filter, results: ${filteredProcedures.length}`);
|
|
15664
|
+
}
|
|
15665
|
+
if (filters.procedureCategory) {
|
|
15666
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15667
|
+
var _a;
|
|
15668
|
+
return ((_a = procedure.category) == null ? void 0 : _a.id) === filters.procedureCategory;
|
|
15669
|
+
});
|
|
15670
|
+
console.log(`[PROCEDURE_SERVICE] Applied category filter, results: ${filteredProcedures.length}`);
|
|
15671
|
+
}
|
|
15672
|
+
if (filters.procedureSubcategory) {
|
|
15673
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15674
|
+
var _a;
|
|
15675
|
+
return ((_a = procedure.subcategory) == null ? void 0 : _a.id) === filters.procedureSubcategory;
|
|
15676
|
+
});
|
|
15677
|
+
console.log(`[PROCEDURE_SERVICE] Applied subcategory filter, results: ${filteredProcedures.length}`);
|
|
15678
|
+
}
|
|
15679
|
+
if (filters.procedureTechnology) {
|
|
15680
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15681
|
+
var _a;
|
|
15682
|
+
return ((_a = procedure.technology) == null ? void 0 : _a.id) === filters.procedureTechnology;
|
|
15683
|
+
});
|
|
15684
|
+
console.log(`[PROCEDURE_SERVICE] Applied technology filter, results: ${filteredProcedures.length}`);
|
|
15685
|
+
}
|
|
15686
|
+
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
15687
|
+
const location = filters.location;
|
|
15688
|
+
const radiusInKm = filters.radiusInKm;
|
|
15689
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15690
|
+
var _a;
|
|
15691
|
+
const clinicLocation = (_a = procedure.clinicInfo) == null ? void 0 : _a.location;
|
|
15692
|
+
if (!(clinicLocation == null ? void 0 : clinicLocation.latitude) || !(clinicLocation == null ? void 0 : clinicLocation.longitude)) {
|
|
15693
|
+
return false;
|
|
15694
|
+
}
|
|
15695
|
+
const distance = distanceBetween6(
|
|
15696
|
+
[location.latitude, location.longitude],
|
|
15697
|
+
[clinicLocation.latitude, clinicLocation.longitude]
|
|
15698
|
+
) / 1e3;
|
|
15699
|
+
procedure.distance = distance;
|
|
15700
|
+
return distance <= radiusInKm;
|
|
15701
|
+
});
|
|
15702
|
+
console.log(`[PROCEDURE_SERVICE] Applied geo filter, results: ${filteredProcedures.length}`);
|
|
15703
|
+
filteredProcedures.sort((a, b) => (a.distance || 0) - (b.distance || 0));
|
|
15704
|
+
}
|
|
15705
|
+
return filteredProcedures;
|
|
15706
|
+
}
|
|
15707
|
+
handleGeoQuery(filters) {
|
|
15708
|
+
console.log("[PROCEDURE_SERVICE] Executing geo query with enhanced debugging");
|
|
15709
|
+
try {
|
|
15710
|
+
const location = filters.location;
|
|
15711
|
+
const radiusInKm = filters.radiusInKm;
|
|
15712
|
+
console.log("[PROCEDURE_SERVICE] Geo query parameters:", {
|
|
15713
|
+
latitude: location.latitude,
|
|
15714
|
+
longitude: location.longitude,
|
|
15715
|
+
radiusInKm,
|
|
15716
|
+
pagination: filters.pagination || 10
|
|
15717
|
+
});
|
|
15718
|
+
const constraints = [
|
|
15719
|
+
where29("isActive", "==", true),
|
|
15720
|
+
orderBy17("createdAt", "desc"),
|
|
15721
|
+
limit15((filters.pagination || 10) * 3)
|
|
15722
|
+
// Get more results for geo filtering
|
|
15723
|
+
];
|
|
15724
|
+
const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15725
|
+
return getDocs29(q).then((querySnapshot) => {
|
|
15726
|
+
let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15727
|
+
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
15728
|
+
console.log(`[PROCEDURE_SERVICE] Geo query success: ${procedures.length} procedures within ${radiusInKm}km`);
|
|
15729
|
+
const lastDoc = procedures.length < (filters.pagination || 10) ? null : querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
15730
|
+
return { procedures, lastDoc };
|
|
15731
|
+
});
|
|
15732
|
+
} catch (error) {
|
|
15733
|
+
console.error("[PROCEDURE_SERVICE] Geo query failed:", error);
|
|
15734
|
+
return Promise.resolve({ procedures: [], lastDoc: null });
|
|
15283
15735
|
}
|
|
15284
15736
|
}
|
|
15285
15737
|
/**
|