@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.js
CHANGED
|
@@ -6833,81 +6833,235 @@ var PractitionerService = class extends BaseService {
|
|
|
6833
6833
|
*/
|
|
6834
6834
|
async getPractitionersByFilters(filters) {
|
|
6835
6835
|
try {
|
|
6836
|
-
|
|
6837
|
-
if (
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
);
|
|
6836
|
+
console.log("[PRACTITIONER_SERVICE] Starting practitioner filtering with fallback strategies");
|
|
6837
|
+
if (filters.location && filters.radiusInKm) {
|
|
6838
|
+
console.log("[PRACTITIONER_SERVICE] Executing geo query:", {
|
|
6839
|
+
location: filters.location,
|
|
6840
|
+
radius: filters.radiusInKm,
|
|
6841
|
+
serviceName: "PractitionerService"
|
|
6842
|
+
});
|
|
6843
|
+
if (!filters.location.latitude || !filters.location.longitude) {
|
|
6844
|
+
console.warn("[PRACTITIONER_SERVICE] Invalid location data:", filters.location);
|
|
6845
|
+
filters.location = void 0;
|
|
6846
|
+
filters.radiusInKm = void 0;
|
|
6847
|
+
}
|
|
6849
6848
|
}
|
|
6850
6849
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6850
|
+
try {
|
|
6851
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 1: Trying fullNameLower search");
|
|
6852
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
6853
|
+
const constraints = [];
|
|
6854
|
+
if (!filters.includeDraftPractitioners) {
|
|
6855
|
+
constraints.push((0, import_firestore21.where)("status", "==", "active" /* ACTIVE */));
|
|
6856
|
+
}
|
|
6857
|
+
constraints.push((0, import_firestore21.where)("isActive", "==", true));
|
|
6858
|
+
constraints.push((0, import_firestore21.where)("fullNameLower", ">=", searchTerm));
|
|
6859
|
+
constraints.push((0, import_firestore21.where)("fullNameLower", "<=", searchTerm + "\uF8FF"));
|
|
6860
|
+
constraints.push((0, import_firestore21.orderBy)("fullNameLower"));
|
|
6861
|
+
if (filters.lastDoc) {
|
|
6862
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
6863
|
+
constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
|
|
6864
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
6865
|
+
constraints.push((0, import_firestore21.startAfter)(...filters.lastDoc));
|
|
6866
|
+
} else {
|
|
6867
|
+
constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
constraints.push((0, import_firestore21.limit)(filters.pagination || 10));
|
|
6871
|
+
const q = (0, import_firestore21.query)((0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION), ...constraints);
|
|
6872
|
+
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6873
|
+
const practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
6874
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6875
|
+
console.log(`[PRACTITIONER_SERVICE] Strategy 1 success: ${practitioners.length} practitioners`);
|
|
6876
|
+
if (practitioners.length < (filters.pagination || 10)) {
|
|
6877
|
+
return { practitioners, lastDoc: null };
|
|
6878
|
+
}
|
|
6879
|
+
return { practitioners, lastDoc };
|
|
6880
|
+
} catch (error) {
|
|
6881
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 1 failed:", error);
|
|
6882
|
+
}
|
|
6869
6883
|
}
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6884
|
+
try {
|
|
6885
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 2: Basic query with createdAt ordering");
|
|
6886
|
+
const constraints = [];
|
|
6887
|
+
if (!filters.includeDraftPractitioners) {
|
|
6888
|
+
constraints.push((0, import_firestore21.where)("status", "==", "active" /* ACTIVE */));
|
|
6889
|
+
}
|
|
6890
|
+
constraints.push((0, import_firestore21.where)("isActive", "==", true));
|
|
6891
|
+
if (filters.certifications && filters.certifications.length > 0) {
|
|
6892
|
+
const certificationsToMatch = filters.certifications;
|
|
6893
|
+
constraints.push(
|
|
6894
|
+
(0, import_firestore21.where)("certification.specialties", "array-contains-any", certificationsToMatch)
|
|
6895
|
+
);
|
|
6896
|
+
}
|
|
6897
|
+
if (filters.minRating !== void 0) {
|
|
6898
|
+
constraints.push((0, import_firestore21.where)("reviewInfo.averageRating", ">=", filters.minRating));
|
|
6899
|
+
}
|
|
6900
|
+
if (filters.maxRating !== void 0) {
|
|
6901
|
+
constraints.push((0, import_firestore21.where)("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
6902
|
+
}
|
|
6903
|
+
constraints.push((0, import_firestore21.orderBy)("createdAt", "desc"));
|
|
6904
|
+
if (filters.location && filters.radiusInKm) {
|
|
6905
|
+
constraints.push((0, import_firestore21.limit)((filters.pagination || 10) * 2));
|
|
6876
6906
|
} else {
|
|
6877
|
-
|
|
6907
|
+
if (filters.lastDoc) {
|
|
6908
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
6909
|
+
constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
|
|
6910
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
6911
|
+
constraints.push((0, import_firestore21.startAfter)(...filters.lastDoc));
|
|
6912
|
+
} else {
|
|
6913
|
+
constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
|
|
6914
|
+
}
|
|
6915
|
+
}
|
|
6916
|
+
constraints.push((0, import_firestore21.limit)(filters.pagination || 10));
|
|
6878
6917
|
}
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
return distanceInKm <= radiusInKm;
|
|
6918
|
+
const q = (0, import_firestore21.query)((0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION), ...constraints);
|
|
6919
|
+
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6920
|
+
let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
6921
|
+
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
6922
|
+
const location = filters.location;
|
|
6923
|
+
const radiusInKm = filters.radiusInKm;
|
|
6924
|
+
practitioners = practitioners.filter((practitioner) => {
|
|
6925
|
+
const clinics = practitioner.clinicsInfo || [];
|
|
6926
|
+
return clinics.some((clinic) => {
|
|
6927
|
+
const distance = (0, import_geofire_common2.distanceBetween)(
|
|
6928
|
+
[location.latitude, location.longitude],
|
|
6929
|
+
[clinic.location.latitude, clinic.location.longitude]
|
|
6930
|
+
);
|
|
6931
|
+
const distanceInKm = distance / 1e3;
|
|
6932
|
+
return distanceInKm <= radiusInKm;
|
|
6933
|
+
});
|
|
6896
6934
|
});
|
|
6897
|
-
|
|
6935
|
+
practitioners = practitioners.slice(0, filters.pagination || 10);
|
|
6936
|
+
}
|
|
6937
|
+
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
6938
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6939
|
+
console.log(`[PRACTITIONER_SERVICE] Strategy 2 success: ${practitioners.length} practitioners`);
|
|
6940
|
+
if (practitioners.length < (filters.pagination || 10)) {
|
|
6941
|
+
return { practitioners, lastDoc: null };
|
|
6942
|
+
}
|
|
6943
|
+
return { practitioners, lastDoc };
|
|
6944
|
+
} catch (error) {
|
|
6945
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 2 failed:", error);
|
|
6898
6946
|
}
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6947
|
+
try {
|
|
6948
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 3: Minimal query fallback");
|
|
6949
|
+
const constraints = [
|
|
6950
|
+
(0, import_firestore21.where)("isActive", "==", true),
|
|
6951
|
+
(0, import_firestore21.orderBy)("createdAt", "desc"),
|
|
6952
|
+
(0, import_firestore21.limit)(filters.pagination || 10)
|
|
6953
|
+
];
|
|
6954
|
+
const q = (0, import_firestore21.query)((0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION), ...constraints);
|
|
6955
|
+
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6956
|
+
let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
6957
|
+
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
6958
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6959
|
+
console.log(`[PRACTITIONER_SERVICE] Strategy 3 success: ${practitioners.length} practitioners`);
|
|
6960
|
+
if (practitioners.length < (filters.pagination || 10)) {
|
|
6961
|
+
return { practitioners, lastDoc: null };
|
|
6962
|
+
}
|
|
6963
|
+
return { practitioners, lastDoc };
|
|
6964
|
+
} catch (error) {
|
|
6965
|
+
console.log("[PRACTITIONER_SERVICE] Strategy 3 failed:", error);
|
|
6966
|
+
}
|
|
6967
|
+
console.log("[PRACTITIONER_SERVICE] All strategies failed, returning empty result");
|
|
6968
|
+
return { practitioners: [], lastDoc: null };
|
|
6904
6969
|
} catch (error) {
|
|
6905
|
-
console.error(
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6970
|
+
console.error("[PRACTITIONER_SERVICE] Error filtering practitioners:", error);
|
|
6971
|
+
return { practitioners: [], lastDoc: null };
|
|
6972
|
+
}
|
|
6973
|
+
}
|
|
6974
|
+
/**
|
|
6975
|
+
* Applies in-memory filters to practitioners array
|
|
6976
|
+
* Used when Firestore queries fail or for complex filtering
|
|
6977
|
+
*/
|
|
6978
|
+
applyInMemoryFilters(practitioners, filters) {
|
|
6979
|
+
let filteredPractitioners = [...practitioners];
|
|
6980
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
6981
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
6982
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
6983
|
+
var _a, _b;
|
|
6984
|
+
const firstName = (((_a = practitioner.basicInfo) == null ? void 0 : _a.firstName) || "").toLowerCase();
|
|
6985
|
+
const lastName = (((_b = practitioner.basicInfo) == null ? void 0 : _b.lastName) || "").toLowerCase();
|
|
6986
|
+
const fullName = `${firstName} ${lastName}`.trim();
|
|
6987
|
+
const fullNameLower = practitioner.fullNameLower || "";
|
|
6988
|
+
return firstName.includes(searchTerm) || lastName.includes(searchTerm) || fullName.includes(searchTerm) || fullNameLower.includes(searchTerm);
|
|
6989
|
+
});
|
|
6990
|
+
console.log(`[PRACTITIONER_SERVICE] Applied name filter, results: ${filteredPractitioners.length}`);
|
|
6991
|
+
}
|
|
6992
|
+
if (filters.certifications && filters.certifications.length > 0) {
|
|
6993
|
+
const certificationsToMatch = filters.certifications;
|
|
6994
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
6995
|
+
var _a;
|
|
6996
|
+
const practitionerCerts = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
|
|
6997
|
+
return certificationsToMatch.some((cert) => practitionerCerts.includes(cert));
|
|
6998
|
+
});
|
|
6999
|
+
console.log(`[PRACTITIONER_SERVICE] Applied certifications filter, results: ${filteredPractitioners.length}`);
|
|
7000
|
+
}
|
|
7001
|
+
if (filters.specialties && filters.specialties.length > 0) {
|
|
7002
|
+
const specialtiesToMatch = filters.specialties;
|
|
7003
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7004
|
+
var _a;
|
|
7005
|
+
const practitionerSpecs = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
|
|
7006
|
+
return specialtiesToMatch.some((spec) => practitionerSpecs.includes(spec));
|
|
7007
|
+
});
|
|
7008
|
+
console.log(`[PRACTITIONER_SERVICE] Applied specialties filter, results: ${filteredPractitioners.length}`);
|
|
7009
|
+
}
|
|
7010
|
+
if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
|
|
7011
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7012
|
+
var _a;
|
|
7013
|
+
const rating = ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
|
|
7014
|
+
if (filters.minRating !== void 0 && rating < filters.minRating) return false;
|
|
7015
|
+
if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
|
|
7016
|
+
return true;
|
|
7017
|
+
});
|
|
7018
|
+
console.log(`[PRACTITIONER_SERVICE] Applied rating filter, results: ${filteredPractitioners.length}`);
|
|
7019
|
+
}
|
|
7020
|
+
if (filters.procedureFamily) {
|
|
7021
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7022
|
+
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7023
|
+
return proceduresInfo.some((proc) => proc.family === filters.procedureFamily);
|
|
7024
|
+
});
|
|
7025
|
+
console.log(`[PRACTITIONER_SERVICE] Applied procedure family filter, results: ${filteredPractitioners.length}`);
|
|
7026
|
+
}
|
|
7027
|
+
if (filters.procedureCategory) {
|
|
7028
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7029
|
+
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7030
|
+
return proceduresInfo.some((proc) => proc.categoryName === filters.procedureCategory);
|
|
7031
|
+
});
|
|
7032
|
+
console.log(`[PRACTITIONER_SERVICE] Applied procedure category filter, results: ${filteredPractitioners.length}`);
|
|
7033
|
+
}
|
|
7034
|
+
if (filters.procedureSubcategory) {
|
|
7035
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7036
|
+
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7037
|
+
return proceduresInfo.some((proc) => proc.subcategoryName === filters.procedureSubcategory);
|
|
7038
|
+
});
|
|
7039
|
+
console.log(`[PRACTITIONER_SERVICE] Applied procedure subcategory filter, results: ${filteredPractitioners.length}`);
|
|
6910
7040
|
}
|
|
7041
|
+
if (filters.procedureTechnology) {
|
|
7042
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7043
|
+
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7044
|
+
return proceduresInfo.some((proc) => proc.technologyName === filters.procedureTechnology);
|
|
7045
|
+
});
|
|
7046
|
+
console.log(`[PRACTITIONER_SERVICE] Applied procedure technology filter, results: ${filteredPractitioners.length}`);
|
|
7047
|
+
}
|
|
7048
|
+
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
7049
|
+
const location = filters.location;
|
|
7050
|
+
const radiusInKm = filters.radiusInKm;
|
|
7051
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7052
|
+
const clinics = practitioner.clinicsInfo || [];
|
|
7053
|
+
return clinics.some((clinic) => {
|
|
7054
|
+
const distance = (0, import_geofire_common2.distanceBetween)(
|
|
7055
|
+
[location.latitude, location.longitude],
|
|
7056
|
+
[clinic.location.latitude, clinic.location.longitude]
|
|
7057
|
+
);
|
|
7058
|
+
const distanceInKm = distance / 1e3;
|
|
7059
|
+
return distanceInKm <= radiusInKm;
|
|
7060
|
+
});
|
|
7061
|
+
});
|
|
7062
|
+
console.log(`[PRACTITIONER_SERVICE] Applied geo filter, results: ${filteredPractitioners.length}`);
|
|
7063
|
+
}
|
|
7064
|
+
return filteredPractitioners;
|
|
6911
7065
|
}
|
|
6912
7066
|
/**
|
|
6913
7067
|
* Enables free consultation for a practitioner in a specific clinic
|
|
@@ -8535,49 +8689,181 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
|
|
|
8535
8689
|
var import_firestore26 = require("firebase/firestore");
|
|
8536
8690
|
var import_geofire_common6 = require("geofire-common");
|
|
8537
8691
|
async function getClinicsByFilters(db, filters) {
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
constraints.push((0, import_firestore26.where)("servicesInfo.procedureFamily", "==", filters.procedureFamily));
|
|
8552
|
-
}
|
|
8553
|
-
let useNameLower = false;
|
|
8554
|
-
let searchTerm = "";
|
|
8555
|
-
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
8556
|
-
searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
8557
|
-
constraints.push((0, import_firestore26.where)("nameLower", ">=", searchTerm));
|
|
8558
|
-
constraints.push((0, import_firestore26.where)("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
8559
|
-
useNameLower = true;
|
|
8560
|
-
}
|
|
8561
|
-
if (filters.minRating !== void 0) {
|
|
8562
|
-
constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", ">=", filters.minRating));
|
|
8563
|
-
}
|
|
8564
|
-
if (filters.maxRating !== void 0) {
|
|
8565
|
-
constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
8566
|
-
}
|
|
8567
|
-
constraints.push((0, import_firestore26.orderBy)("nameLower"));
|
|
8568
|
-
if (filters.lastDoc) {
|
|
8569
|
-
if (typeof filters.lastDoc.data === "function") {
|
|
8570
|
-
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8571
|
-
} else if (Array.isArray(filters.lastDoc)) {
|
|
8572
|
-
constraints.push((0, import_firestore26.startAfter)(...filters.lastDoc));
|
|
8573
|
-
} else {
|
|
8574
|
-
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8692
|
+
try {
|
|
8693
|
+
console.log("[CLINIC_SERVICE] Starting clinic filtering with multiple strategies");
|
|
8694
|
+
if (filters.center && filters.radiusInKm) {
|
|
8695
|
+
console.log("[CLINIC_SERVICE] Executing geo query:", {
|
|
8696
|
+
center: filters.center,
|
|
8697
|
+
radius: filters.radiusInKm,
|
|
8698
|
+
serviceName: "ClinicService"
|
|
8699
|
+
});
|
|
8700
|
+
if (!filters.center.latitude || !filters.center.longitude) {
|
|
8701
|
+
console.warn("[CLINIC_SERVICE] Invalid location data:", filters.center);
|
|
8702
|
+
filters.center = void 0;
|
|
8703
|
+
filters.radiusInKm = void 0;
|
|
8704
|
+
}
|
|
8575
8705
|
}
|
|
8706
|
+
const getBaseConstraints = () => {
|
|
8707
|
+
var _a;
|
|
8708
|
+
const constraints = [];
|
|
8709
|
+
constraints.push((0, import_firestore26.where)("isActive", "==", (_a = filters.isActive) != null ? _a : true));
|
|
8710
|
+
if (filters.tags && filters.tags.length > 0) {
|
|
8711
|
+
constraints.push((0, import_firestore26.where)("tags", "array-contains", filters.tags[0]));
|
|
8712
|
+
}
|
|
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
|
+
if (filters.minRating !== void 0) {
|
|
8723
|
+
constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", ">=", filters.minRating));
|
|
8724
|
+
}
|
|
8725
|
+
if (filters.maxRating !== void 0) {
|
|
8726
|
+
constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
8727
|
+
}
|
|
8728
|
+
return constraints;
|
|
8729
|
+
};
|
|
8730
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
8731
|
+
try {
|
|
8732
|
+
console.log("[CLINIC_SERVICE] Strategy 1: Trying nameLower search");
|
|
8733
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
8734
|
+
const constraints = getBaseConstraints();
|
|
8735
|
+
constraints.push((0, import_firestore26.where)("nameLower", ">=", searchTerm));
|
|
8736
|
+
constraints.push((0, import_firestore26.where)("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
8737
|
+
constraints.push((0, import_firestore26.orderBy)("nameLower"));
|
|
8738
|
+
if (filters.lastDoc) {
|
|
8739
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
8740
|
+
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8741
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
8742
|
+
constraints.push((0, import_firestore26.startAfter)(...filters.lastDoc));
|
|
8743
|
+
} else {
|
|
8744
|
+
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8747
|
+
constraints.push((0, import_firestore26.limit)(filters.pagination || 5));
|
|
8748
|
+
const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
|
|
8749
|
+
const querySnapshot = await (0, import_firestore26.getDocs)(q);
|
|
8750
|
+
let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
8751
|
+
clinics = applyInMemoryFilters(clinics, filters);
|
|
8752
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8753
|
+
console.log(`[CLINIC_SERVICE] Strategy 1 success: ${clinics.length} clinics`);
|
|
8754
|
+
if (clinics.length < (filters.pagination || 5)) {
|
|
8755
|
+
return { clinics, lastDoc: null };
|
|
8756
|
+
}
|
|
8757
|
+
return { clinics, lastDoc };
|
|
8758
|
+
} catch (error) {
|
|
8759
|
+
console.log("[CLINIC_SERVICE] Strategy 1 failed:", error);
|
|
8760
|
+
}
|
|
8761
|
+
}
|
|
8762
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
8763
|
+
try {
|
|
8764
|
+
console.log("[CLINIC_SERVICE] Strategy 2: Trying name field search");
|
|
8765
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
8766
|
+
const constraints = getBaseConstraints();
|
|
8767
|
+
constraints.push((0, import_firestore26.where)("name", ">=", searchTerm));
|
|
8768
|
+
constraints.push((0, import_firestore26.where)("name", "<=", searchTerm + "\uF8FF"));
|
|
8769
|
+
constraints.push((0, import_firestore26.orderBy)("name"));
|
|
8770
|
+
if (filters.lastDoc) {
|
|
8771
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
8772
|
+
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8773
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
8774
|
+
constraints.push((0, import_firestore26.startAfter)(...filters.lastDoc));
|
|
8775
|
+
} else {
|
|
8776
|
+
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8777
|
+
}
|
|
8778
|
+
}
|
|
8779
|
+
constraints.push((0, import_firestore26.limit)(filters.pagination || 5));
|
|
8780
|
+
const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
|
|
8781
|
+
const querySnapshot = await (0, import_firestore26.getDocs)(q);
|
|
8782
|
+
let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
8783
|
+
clinics = applyInMemoryFilters(clinics, filters);
|
|
8784
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8785
|
+
console.log(`[CLINIC_SERVICE] Strategy 2 success: ${clinics.length} clinics`);
|
|
8786
|
+
if (clinics.length < (filters.pagination || 5)) {
|
|
8787
|
+
return { clinics, lastDoc: null };
|
|
8788
|
+
}
|
|
8789
|
+
return { clinics, lastDoc };
|
|
8790
|
+
} catch (error) {
|
|
8791
|
+
console.log("[CLINIC_SERVICE] Strategy 2 failed:", error);
|
|
8792
|
+
}
|
|
8793
|
+
}
|
|
8794
|
+
try {
|
|
8795
|
+
console.log("[CLINIC_SERVICE] Strategy 3: Using createdAt ordering with client-side filtering");
|
|
8796
|
+
const constraints = getBaseConstraints();
|
|
8797
|
+
constraints.push((0, import_firestore26.orderBy)("createdAt", "desc"));
|
|
8798
|
+
if (filters.lastDoc) {
|
|
8799
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
8800
|
+
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8801
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
8802
|
+
constraints.push((0, import_firestore26.startAfter)(...filters.lastDoc));
|
|
8803
|
+
} else {
|
|
8804
|
+
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8805
|
+
}
|
|
8806
|
+
}
|
|
8807
|
+
constraints.push((0, import_firestore26.limit)(filters.pagination || 5));
|
|
8808
|
+
const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
|
|
8809
|
+
const querySnapshot = await (0, import_firestore26.getDocs)(q);
|
|
8810
|
+
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
|
+
clinics = applyInMemoryFilters(clinics, filters);
|
|
8821
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8822
|
+
console.log(`[CLINIC_SERVICE] Strategy 3 success: ${clinics.length} clinics`);
|
|
8823
|
+
if (clinics.length < (filters.pagination || 5)) {
|
|
8824
|
+
return { clinics, lastDoc: null };
|
|
8825
|
+
}
|
|
8826
|
+
return { clinics, lastDoc };
|
|
8827
|
+
} catch (error) {
|
|
8828
|
+
console.log("[CLINIC_SERVICE] Strategy 3 failed:", error);
|
|
8829
|
+
}
|
|
8830
|
+
try {
|
|
8831
|
+
console.log("[CLINIC_SERVICE] Strategy 4: Minimal fallback");
|
|
8832
|
+
const constraints = [
|
|
8833
|
+
(0, import_firestore26.where)("isActive", "==", true),
|
|
8834
|
+
(0, import_firestore26.orderBy)("createdAt", "desc"),
|
|
8835
|
+
(0, import_firestore26.limit)(filters.pagination || 5)
|
|
8836
|
+
];
|
|
8837
|
+
const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
|
|
8838
|
+
const querySnapshot = await (0, import_firestore26.getDocs)(q);
|
|
8839
|
+
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
|
+
clinics = applyInMemoryFilters(clinics, filters);
|
|
8850
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8851
|
+
console.log(`[CLINIC_SERVICE] Strategy 4 success: ${clinics.length} clinics`);
|
|
8852
|
+
if (clinics.length < (filters.pagination || 5)) {
|
|
8853
|
+
return { clinics, lastDoc: null };
|
|
8854
|
+
}
|
|
8855
|
+
return { clinics, lastDoc };
|
|
8856
|
+
} catch (error) {
|
|
8857
|
+
console.log("[CLINIC_SERVICE] Strategy 4 failed:", error);
|
|
8858
|
+
}
|
|
8859
|
+
console.log("[CLINIC_SERVICE] All strategies failed, returning empty result");
|
|
8860
|
+
return { clinics: [], lastDoc: null };
|
|
8861
|
+
} catch (error) {
|
|
8862
|
+
console.error("[CLINIC_SERVICE] Error filtering clinics:", error);
|
|
8863
|
+
return { clinics: [], lastDoc: null };
|
|
8576
8864
|
}
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
const querySnapshot = await (0, import_firestore26.getDocs)(q);
|
|
8580
|
-
let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
8865
|
+
}
|
|
8866
|
+
function applyInMemoryFilters(clinics, filters) {
|
|
8581
8867
|
if (filters.tags && filters.tags.length > 1) {
|
|
8582
8868
|
clinics = clinics.filter((clinic) => filters.tags.every((tag) => clinic.tags.includes(tag)));
|
|
8583
8869
|
}
|
|
@@ -8590,9 +8876,9 @@ async function getClinicsByFilters(db, filters) {
|
|
|
8590
8876
|
clinic.distance = distance;
|
|
8591
8877
|
return distance <= filters.radiusInKm;
|
|
8592
8878
|
});
|
|
8879
|
+
clinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
|
|
8593
8880
|
}
|
|
8594
|
-
|
|
8595
|
-
return { clinics, lastDoc: lastVisibleDoc };
|
|
8881
|
+
return clinics;
|
|
8596
8882
|
}
|
|
8597
8883
|
|
|
8598
8884
|
// src/services/clinic/clinic.service.ts
|
|
@@ -14909,126 +15195,292 @@ var ProcedureService = class extends BaseService {
|
|
|
14909
15195
|
*/
|
|
14910
15196
|
async getProceduresByFilters(filters) {
|
|
14911
15197
|
try {
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
}
|
|
14925
|
-
if (filters.procedureSubcategory) {
|
|
14926
|
-
constraints.push((0, import_firestore45.where)("subcategory.id", "==", filters.procedureSubcategory));
|
|
14927
|
-
}
|
|
14928
|
-
if (filters.procedureTechnology) {
|
|
14929
|
-
constraints.push((0, import_firestore45.where)("technology.id", "==", filters.procedureTechnology));
|
|
14930
|
-
}
|
|
14931
|
-
if (filters.minPrice !== void 0) {
|
|
14932
|
-
constraints.push((0, import_firestore45.where)("price", ">=", filters.minPrice));
|
|
14933
|
-
}
|
|
14934
|
-
if (filters.maxPrice !== void 0) {
|
|
14935
|
-
constraints.push((0, import_firestore45.where)("price", "<=", filters.maxPrice));
|
|
14936
|
-
}
|
|
14937
|
-
if (filters.minRating !== void 0) {
|
|
14938
|
-
constraints.push((0, import_firestore45.where)("reviewInfo.averageRating", ">=", filters.minRating));
|
|
15198
|
+
console.log("[PROCEDURE_SERVICE] Starting procedure filtering with multiple strategies");
|
|
15199
|
+
if (filters.location && filters.radiusInKm) {
|
|
15200
|
+
console.log("[PROCEDURE_SERVICE] Executing geo query:", {
|
|
15201
|
+
location: filters.location,
|
|
15202
|
+
radius: filters.radiusInKm,
|
|
15203
|
+
serviceName: "ProcedureService"
|
|
15204
|
+
});
|
|
15205
|
+
if (!filters.location.latitude || !filters.location.longitude) {
|
|
15206
|
+
console.warn("[PROCEDURE_SERVICE] Invalid location data:", filters.location);
|
|
15207
|
+
filters.location = void 0;
|
|
15208
|
+
filters.radiusInKm = void 0;
|
|
15209
|
+
}
|
|
14939
15210
|
}
|
|
14940
|
-
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
14944
|
-
constraints.push((0, import_firestore45.where)("treatmentBenefits", "array-contains-any", filters.treatmentBenefits));
|
|
14945
|
-
}
|
|
14946
|
-
let useNameLower = false;
|
|
14947
|
-
let searchTerm = "";
|
|
14948
|
-
if (filters.nameSearch && filters.nameSearch.trim() !== "") {
|
|
14949
|
-
searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
14950
|
-
useNameLower = true;
|
|
14951
|
-
constraints.push((0, import_firestore45.where)("nameLower", ">=", searchTerm));
|
|
14952
|
-
constraints.push((0, import_firestore45.where)("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
14953
|
-
constraints.push((0, import_firestore45.orderBy)("nameLower"));
|
|
14954
|
-
} else {
|
|
14955
|
-
constraints.push((0, import_firestore45.orderBy)("nameLower"));
|
|
15211
|
+
const isGeoQuery = filters.location && filters.radiusInKm && filters.radiusInKm > 0;
|
|
15212
|
+
if (isGeoQuery) {
|
|
15213
|
+
return this.handleGeoQuery(filters);
|
|
14956
15214
|
}
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
14961
|
-
constraints.push((0, import_firestore45.startAfter)(...filters.lastDoc));
|
|
15215
|
+
const getBaseConstraints = () => {
|
|
15216
|
+
const constraints = [];
|
|
15217
|
+
if (filters.isActive !== void 0) {
|
|
15218
|
+
constraints.push((0, import_firestore45.where)("isActive", "==", filters.isActive));
|
|
14962
15219
|
} else {
|
|
14963
|
-
constraints.push((0, import_firestore45.
|
|
15220
|
+
constraints.push((0, import_firestore45.where)("isActive", "==", true));
|
|
14964
15221
|
}
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
14971
|
-
|
|
14972
|
-
|
|
14973
|
-
|
|
14974
|
-
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
|
|
14978
|
-
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
(0, import_firestore45.where)("clinicInfo.location.geohash", ">=", bound[0]),
|
|
14983
|
-
(0, import_firestore45.where)("clinicInfo.location.geohash", "<=", bound[1]),
|
|
14984
|
-
(0, import_firestore45.orderBy)("clinicInfo.location.geohash")
|
|
14985
|
-
];
|
|
14986
|
-
const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...geoConstraints);
|
|
14987
|
-
const querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
14988
|
-
for (const doc37 of querySnapshot.docs) {
|
|
14989
|
-
const procedure = { ...doc37.data(), id: doc37.id };
|
|
14990
|
-
const distance = (0, import_geofire_common8.distanceBetween)(
|
|
14991
|
-
[center.latitude, center.longitude],
|
|
14992
|
-
[procedure.clinicInfo.location.latitude, procedure.clinicInfo.location.longitude]
|
|
14993
|
-
);
|
|
14994
|
-
const distanceInKm = distance / 1e3;
|
|
14995
|
-
if (distanceInKm <= radiusInKm) {
|
|
14996
|
-
allDocs.push({ ...procedure, distance: distanceInKm });
|
|
14997
|
-
}
|
|
14998
|
-
}
|
|
15222
|
+
if (filters.procedureFamily) {
|
|
15223
|
+
constraints.push((0, import_firestore45.where)("family", "==", filters.procedureFamily));
|
|
15224
|
+
}
|
|
15225
|
+
if (filters.procedureCategory) {
|
|
15226
|
+
constraints.push((0, import_firestore45.where)("category.id", "==", filters.procedureCategory));
|
|
15227
|
+
}
|
|
15228
|
+
if (filters.procedureSubcategory) {
|
|
15229
|
+
constraints.push((0, import_firestore45.where)("subcategory.id", "==", filters.procedureSubcategory));
|
|
15230
|
+
}
|
|
15231
|
+
if (filters.procedureTechnology) {
|
|
15232
|
+
constraints.push((0, import_firestore45.where)("technology.id", "==", filters.procedureTechnology));
|
|
15233
|
+
}
|
|
15234
|
+
if (filters.minPrice !== void 0) {
|
|
15235
|
+
constraints.push((0, import_firestore45.where)("price", ">=", filters.minPrice));
|
|
15236
|
+
}
|
|
15237
|
+
if (filters.maxPrice !== void 0) {
|
|
15238
|
+
constraints.push((0, import_firestore45.where)("price", "<=", filters.maxPrice));
|
|
14999
15239
|
}
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15240
|
+
if (filters.minRating !== void 0) {
|
|
15241
|
+
constraints.push((0, import_firestore45.where)("reviewInfo.averageRating", ">=", filters.minRating));
|
|
15242
|
+
}
|
|
15243
|
+
if (filters.maxRating !== void 0) {
|
|
15244
|
+
constraints.push((0, import_firestore45.where)("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
15245
|
+
}
|
|
15246
|
+
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
15247
|
+
const benefitsToMatch = filters.treatmentBenefits;
|
|
15248
|
+
constraints.push((0, import_firestore45.where)("treatmentBenefits", "array-contains-any", benefitsToMatch));
|
|
15249
|
+
}
|
|
15250
|
+
return constraints;
|
|
15251
|
+
};
|
|
15252
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
15253
|
+
try {
|
|
15254
|
+
console.log("[PROCEDURE_SERVICE] Strategy 1: Trying nameLower search");
|
|
15255
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15256
|
+
const constraints = getBaseConstraints();
|
|
15257
|
+
constraints.push((0, import_firestore45.where)("nameLower", ">=", searchTerm));
|
|
15258
|
+
constraints.push((0, import_firestore45.where)("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
15259
|
+
constraints.push((0, import_firestore45.orderBy)("nameLower"));
|
|
15004
15260
|
if (filters.lastDoc) {
|
|
15005
|
-
|
|
15006
|
-
|
|
15261
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
15262
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
15263
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
15264
|
+
constraints.push((0, import_firestore45.startAfter)(...filters.lastDoc));
|
|
15265
|
+
} else {
|
|
15266
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
15267
|
+
}
|
|
15007
15268
|
}
|
|
15008
|
-
|
|
15269
|
+
constraints.push((0, import_firestore45.limit)(filters.pagination || 10));
|
|
15270
|
+
const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15271
|
+
const querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
15272
|
+
const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15273
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15274
|
+
console.log(`[PROCEDURE_SERVICE] Strategy 1 success: ${procedures.length} procedures`);
|
|
15275
|
+
if (procedures.length < (filters.pagination || 10)) {
|
|
15276
|
+
return { procedures, lastDoc: null };
|
|
15277
|
+
}
|
|
15278
|
+
return { procedures, lastDoc };
|
|
15279
|
+
} catch (error) {
|
|
15280
|
+
console.log("[PROCEDURE_SERVICE] Strategy 1 failed:", error);
|
|
15009
15281
|
}
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
constraints.pop();
|
|
15017
|
-
constraints.pop();
|
|
15018
|
-
constraints.pop();
|
|
15282
|
+
}
|
|
15283
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
15284
|
+
try {
|
|
15285
|
+
console.log("[PROCEDURE_SERVICE] Strategy 2: Trying name field search");
|
|
15286
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15287
|
+
const constraints = getBaseConstraints();
|
|
15019
15288
|
constraints.push((0, import_firestore45.where)("name", ">=", searchTerm));
|
|
15020
15289
|
constraints.push((0, import_firestore45.where)("name", "<=", searchTerm + "\uF8FF"));
|
|
15021
15290
|
constraints.push((0, import_firestore45.orderBy)("name"));
|
|
15022
|
-
|
|
15023
|
-
|
|
15291
|
+
if (filters.lastDoc) {
|
|
15292
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
15293
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
15294
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
15295
|
+
constraints.push((0, import_firestore45.startAfter)(...filters.lastDoc));
|
|
15296
|
+
} else {
|
|
15297
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
15298
|
+
}
|
|
15299
|
+
}
|
|
15300
|
+
constraints.push((0, import_firestore45.limit)(filters.pagination || 10));
|
|
15301
|
+
const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15302
|
+
const querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
15303
|
+
const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15304
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15305
|
+
console.log(`[PROCEDURE_SERVICE] Strategy 2 success: ${procedures.length} procedures`);
|
|
15306
|
+
if (procedures.length < (filters.pagination || 10)) {
|
|
15307
|
+
return { procedures, lastDoc: null };
|
|
15308
|
+
}
|
|
15309
|
+
return { procedures, lastDoc };
|
|
15310
|
+
} catch (error) {
|
|
15311
|
+
console.log("[PROCEDURE_SERVICE] Strategy 2 failed:", error);
|
|
15024
15312
|
}
|
|
15025
|
-
const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15026
|
-
const lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15027
|
-
return { procedures, lastDoc: lastVisibleDoc };
|
|
15028
15313
|
}
|
|
15314
|
+
try {
|
|
15315
|
+
console.log("[PROCEDURE_SERVICE] Strategy 3: Using createdAt orderBy with client-side filtering");
|
|
15316
|
+
const constraints = getBaseConstraints();
|
|
15317
|
+
constraints.push((0, import_firestore45.orderBy)("createdAt", "desc"));
|
|
15318
|
+
if (filters.lastDoc) {
|
|
15319
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
15320
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
15321
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
15322
|
+
constraints.push((0, import_firestore45.startAfter)(...filters.lastDoc));
|
|
15323
|
+
} else {
|
|
15324
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
15325
|
+
}
|
|
15326
|
+
}
|
|
15327
|
+
constraints.push((0, import_firestore45.limit)(filters.pagination || 10));
|
|
15328
|
+
const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15329
|
+
const querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
15330
|
+
let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15331
|
+
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
15332
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15333
|
+
console.log(`[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`);
|
|
15334
|
+
if (procedures.length < (filters.pagination || 10)) {
|
|
15335
|
+
return { procedures, lastDoc: null };
|
|
15336
|
+
}
|
|
15337
|
+
return { procedures, lastDoc };
|
|
15338
|
+
} catch (error) {
|
|
15339
|
+
console.log("[PROCEDURE_SERVICE] Strategy 3 failed:", error);
|
|
15340
|
+
}
|
|
15341
|
+
try {
|
|
15342
|
+
console.log("[PROCEDURE_SERVICE] Strategy 4: Minimal query fallback");
|
|
15343
|
+
const constraints = [
|
|
15344
|
+
(0, import_firestore45.where)("isActive", "==", true),
|
|
15345
|
+
(0, import_firestore45.orderBy)("createdAt", "desc"),
|
|
15346
|
+
(0, import_firestore45.limit)(filters.pagination || 10)
|
|
15347
|
+
];
|
|
15348
|
+
const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15349
|
+
const querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
15350
|
+
let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15351
|
+
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
15352
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15353
|
+
console.log(`[PROCEDURE_SERVICE] Strategy 4 success: ${procedures.length} procedures`);
|
|
15354
|
+
if (procedures.length < (filters.pagination || 10)) {
|
|
15355
|
+
return { procedures, lastDoc: null };
|
|
15356
|
+
}
|
|
15357
|
+
return { procedures, lastDoc };
|
|
15358
|
+
} catch (error) {
|
|
15359
|
+
console.log("[PROCEDURE_SERVICE] Strategy 4 failed:", error);
|
|
15360
|
+
}
|
|
15361
|
+
console.log("[PROCEDURE_SERVICE] All strategies failed, returning empty result");
|
|
15362
|
+
return { procedures: [], lastDoc: null };
|
|
15029
15363
|
} catch (error) {
|
|
15030
15364
|
console.error("[PROCEDURE_SERVICE] Error filtering procedures:", error);
|
|
15031
|
-
|
|
15365
|
+
return { procedures: [], lastDoc: null };
|
|
15366
|
+
}
|
|
15367
|
+
}
|
|
15368
|
+
/**
|
|
15369
|
+
* Applies in-memory filters to procedures array
|
|
15370
|
+
* Used when Firestore queries fail or for complex filtering
|
|
15371
|
+
*/
|
|
15372
|
+
applyInMemoryFilters(procedures, filters) {
|
|
15373
|
+
let filteredProcedures = [...procedures];
|
|
15374
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
15375
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15376
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15377
|
+
const name = (procedure.name || "").toLowerCase();
|
|
15378
|
+
const nameLower = procedure.nameLower || "";
|
|
15379
|
+
return name.includes(searchTerm) || nameLower.includes(searchTerm);
|
|
15380
|
+
});
|
|
15381
|
+
console.log(`[PROCEDURE_SERVICE] Applied name filter, results: ${filteredProcedures.length}`);
|
|
15382
|
+
}
|
|
15383
|
+
if (filters.minPrice !== void 0 || filters.maxPrice !== void 0) {
|
|
15384
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15385
|
+
const price = procedure.price || 0;
|
|
15386
|
+
if (filters.minPrice !== void 0 && price < filters.minPrice) return false;
|
|
15387
|
+
if (filters.maxPrice !== void 0 && price > filters.maxPrice) return false;
|
|
15388
|
+
return true;
|
|
15389
|
+
});
|
|
15390
|
+
console.log(`[PROCEDURE_SERVICE] Applied price filter (${filters.minPrice}-${filters.maxPrice}), results: ${filteredProcedures.length}`);
|
|
15391
|
+
}
|
|
15392
|
+
if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
|
|
15393
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15394
|
+
var _a;
|
|
15395
|
+
const rating = ((_a = procedure.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
|
|
15396
|
+
if (filters.minRating !== void 0 && rating < filters.minRating) return false;
|
|
15397
|
+
if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
|
|
15398
|
+
return true;
|
|
15399
|
+
});
|
|
15400
|
+
console.log(`[PROCEDURE_SERVICE] Applied rating filter, results: ${filteredProcedures.length}`);
|
|
15401
|
+
}
|
|
15402
|
+
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
15403
|
+
const benefitsToMatch = filters.treatmentBenefits;
|
|
15404
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15405
|
+
const procedureBenefits = procedure.treatmentBenefits || [];
|
|
15406
|
+
return benefitsToMatch.some((benefit) => procedureBenefits.includes(benefit));
|
|
15407
|
+
});
|
|
15408
|
+
console.log(`[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`);
|
|
15409
|
+
}
|
|
15410
|
+
if (filters.procedureFamily) {
|
|
15411
|
+
filteredProcedures = filteredProcedures.filter((procedure) => procedure.family === filters.procedureFamily);
|
|
15412
|
+
console.log(`[PROCEDURE_SERVICE] Applied family filter, results: ${filteredProcedures.length}`);
|
|
15413
|
+
}
|
|
15414
|
+
if (filters.procedureCategory) {
|
|
15415
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15416
|
+
var _a;
|
|
15417
|
+
return ((_a = procedure.category) == null ? void 0 : _a.id) === filters.procedureCategory;
|
|
15418
|
+
});
|
|
15419
|
+
console.log(`[PROCEDURE_SERVICE] Applied category filter, results: ${filteredProcedures.length}`);
|
|
15420
|
+
}
|
|
15421
|
+
if (filters.procedureSubcategory) {
|
|
15422
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15423
|
+
var _a;
|
|
15424
|
+
return ((_a = procedure.subcategory) == null ? void 0 : _a.id) === filters.procedureSubcategory;
|
|
15425
|
+
});
|
|
15426
|
+
console.log(`[PROCEDURE_SERVICE] Applied subcategory filter, results: ${filteredProcedures.length}`);
|
|
15427
|
+
}
|
|
15428
|
+
if (filters.procedureTechnology) {
|
|
15429
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15430
|
+
var _a;
|
|
15431
|
+
return ((_a = procedure.technology) == null ? void 0 : _a.id) === filters.procedureTechnology;
|
|
15432
|
+
});
|
|
15433
|
+
console.log(`[PROCEDURE_SERVICE] Applied technology filter, results: ${filteredProcedures.length}`);
|
|
15434
|
+
}
|
|
15435
|
+
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
15436
|
+
const location = filters.location;
|
|
15437
|
+
const radiusInKm = filters.radiusInKm;
|
|
15438
|
+
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15439
|
+
var _a;
|
|
15440
|
+
const clinicLocation = (_a = procedure.clinicInfo) == null ? void 0 : _a.location;
|
|
15441
|
+
if (!(clinicLocation == null ? void 0 : clinicLocation.latitude) || !(clinicLocation == null ? void 0 : clinicLocation.longitude)) {
|
|
15442
|
+
return false;
|
|
15443
|
+
}
|
|
15444
|
+
const distance = (0, import_geofire_common8.distanceBetween)(
|
|
15445
|
+
[location.latitude, location.longitude],
|
|
15446
|
+
[clinicLocation.latitude, clinicLocation.longitude]
|
|
15447
|
+
) / 1e3;
|
|
15448
|
+
procedure.distance = distance;
|
|
15449
|
+
return distance <= radiusInKm;
|
|
15450
|
+
});
|
|
15451
|
+
console.log(`[PROCEDURE_SERVICE] Applied geo filter, results: ${filteredProcedures.length}`);
|
|
15452
|
+
filteredProcedures.sort((a, b) => (a.distance || 0) - (b.distance || 0));
|
|
15453
|
+
}
|
|
15454
|
+
return filteredProcedures;
|
|
15455
|
+
}
|
|
15456
|
+
handleGeoQuery(filters) {
|
|
15457
|
+
console.log("[PROCEDURE_SERVICE] Executing geo query with enhanced debugging");
|
|
15458
|
+
try {
|
|
15459
|
+
const location = filters.location;
|
|
15460
|
+
const radiusInKm = filters.radiusInKm;
|
|
15461
|
+
console.log("[PROCEDURE_SERVICE] Geo query parameters:", {
|
|
15462
|
+
latitude: location.latitude,
|
|
15463
|
+
longitude: location.longitude,
|
|
15464
|
+
radiusInKm,
|
|
15465
|
+
pagination: filters.pagination || 10
|
|
15466
|
+
});
|
|
15467
|
+
const constraints = [
|
|
15468
|
+
(0, import_firestore45.where)("isActive", "==", true),
|
|
15469
|
+
(0, import_firestore45.orderBy)("createdAt", "desc"),
|
|
15470
|
+
(0, import_firestore45.limit)((filters.pagination || 10) * 3)
|
|
15471
|
+
// Get more results for geo filtering
|
|
15472
|
+
];
|
|
15473
|
+
const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15474
|
+
return (0, import_firestore45.getDocs)(q).then((querySnapshot) => {
|
|
15475
|
+
let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15476
|
+
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
15477
|
+
console.log(`[PROCEDURE_SERVICE] Geo query success: ${procedures.length} procedures within ${radiusInKm}km`);
|
|
15478
|
+
const lastDoc = procedures.length < (filters.pagination || 10) ? null : querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
15479
|
+
return { procedures, lastDoc };
|
|
15480
|
+
});
|
|
15481
|
+
} catch (error) {
|
|
15482
|
+
console.error("[PROCEDURE_SERVICE] Geo query failed:", error);
|
|
15483
|
+
return Promise.resolve({ procedures: [], lastDoc: null });
|
|
15032
15484
|
}
|
|
15033
15485
|
}
|
|
15034
15486
|
/**
|