@blackcode_sa/metaestetics-api 1.8.11 → 1.8.13
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/admin/index.d.mts +4 -0
- package/dist/admin/index.d.ts +4 -0
- package/dist/backoffice/index.d.mts +1 -0
- package/dist/backoffice/index.d.ts +1 -0
- package/dist/index.d.mts +36 -9
- package/dist/index.d.ts +36 -9
- package/dist/index.js +218 -375
- package/dist/index.mjs +221 -376
- package/package.json +1 -1
- package/src/admin/scripts/migrateProcedures.js +23 -0
- package/src/admin/scripts/serviceAccountKey.json +13 -0
- package/src/services/clinic/clinic.service.ts +33 -0
- package/src/services/clinic/utils/filter.utils.ts +54 -225
- package/src/services/practitioner/practitioner.service.ts +48 -115
- package/src/services/procedure/procedure.service.ts +123 -249
- package/src/types/clinic/index.ts +1 -0
- package/src/types/practitioner/index.ts +1 -0
- package/src/types/procedure/index.ts +6 -0
- package/src/validations/procedure.schema.ts +3 -0
package/dist/index.js
CHANGED
|
@@ -6211,6 +6211,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6211
6211
|
trustworthiness: 0,
|
|
6212
6212
|
recommendationPercentage: 0
|
|
6213
6213
|
};
|
|
6214
|
+
const fullNameLower = `${validData.basicInfo.firstName} ${validData.basicInfo.lastName}`.toLowerCase();
|
|
6214
6215
|
const practitioner = {
|
|
6215
6216
|
id: practitionerId,
|
|
6216
6217
|
userRef: validData.userRef,
|
|
@@ -6218,6 +6219,8 @@ var PractitionerService = class extends BaseService {
|
|
|
6218
6219
|
validData.basicInfo,
|
|
6219
6220
|
practitionerId
|
|
6220
6221
|
),
|
|
6222
|
+
fullNameLower,
|
|
6223
|
+
// Ensure this is present
|
|
6221
6224
|
certification: validData.certification,
|
|
6222
6225
|
clinics: validData.clinics || [],
|
|
6223
6226
|
clinicWorkingHours: validData.clinicWorkingHours || [],
|
|
@@ -6313,6 +6316,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6313
6316
|
}
|
|
6314
6317
|
const finalClinicsInfo = validatedData.clinicsInfo && validatedData.clinicsInfo.length > 0 ? validatedData.clinicsInfo : clinicsInfo;
|
|
6315
6318
|
const proceduresInfo = [];
|
|
6319
|
+
const fullNameLowerDraft = `${validatedData.basicInfo.firstName} ${validatedData.basicInfo.lastName}`.toLowerCase();
|
|
6316
6320
|
const practitionerData = {
|
|
6317
6321
|
id: practitionerId,
|
|
6318
6322
|
userRef: "",
|
|
@@ -6321,6 +6325,8 @@ var PractitionerService = class extends BaseService {
|
|
|
6321
6325
|
validatedData.basicInfo,
|
|
6322
6326
|
practitionerId
|
|
6323
6327
|
),
|
|
6328
|
+
fullNameLower: fullNameLowerDraft,
|
|
6329
|
+
// Ensure this is present
|
|
6324
6330
|
certification: validatedData.certification,
|
|
6325
6331
|
clinics,
|
|
6326
6332
|
clinicWorkingHours: validatedData.clinicWorkingHours || [],
|
|
@@ -6826,10 +6832,6 @@ var PractitionerService = class extends BaseService {
|
|
|
6826
6832
|
*/
|
|
6827
6833
|
async getPractitionersByFilters(filters) {
|
|
6828
6834
|
try {
|
|
6829
|
-
console.log(
|
|
6830
|
-
"[PRACTITIONER_SERVICE] Starting practitioner filtering with criteria:",
|
|
6831
|
-
filters
|
|
6832
|
-
);
|
|
6833
6835
|
const constraints = [];
|
|
6834
6836
|
if (!filters.includeDraftPractitioners) {
|
|
6835
6837
|
constraints.push((0, import_firestore21.where)("status", "==", "active" /* ACTIVE */));
|
|
@@ -6844,60 +6846,40 @@ var PractitionerService = class extends BaseService {
|
|
|
6844
6846
|
)
|
|
6845
6847
|
);
|
|
6846
6848
|
}
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
|
|
6852
|
-
}
|
|
6853
|
-
constraints.push((0, import_firestore21.limit)(filters.pagination));
|
|
6849
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
6850
|
+
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
6851
|
+
constraints.push((0, import_firestore21.where)("fullNameLower", ">=", searchTerm));
|
|
6852
|
+
constraints.push((0, import_firestore21.where)("fullNameLower", "<=", searchTerm + "\uF8FF"));
|
|
6854
6853
|
}
|
|
6855
|
-
|
|
6856
|
-
(0, import_firestore21.
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
let practitioners = querySnapshot.docs.map((doc37) => {
|
|
6864
|
-
return { ...doc37.data(), id: doc37.id };
|
|
6865
|
-
});
|
|
6866
|
-
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6867
|
-
if (filters.nameSearch && filters.nameSearch.trim() !== "") {
|
|
6868
|
-
const searchTerm = filters.nameSearch.toLowerCase().trim();
|
|
6869
|
-
practitioners = practitioners.filter((practitioner) => {
|
|
6870
|
-
const fullName = `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`.toLowerCase();
|
|
6871
|
-
return fullName.includes(searchTerm);
|
|
6872
|
-
});
|
|
6854
|
+
if (filters.procedureTechnology) {
|
|
6855
|
+
constraints.push((0, import_firestore21.where)("proceduresInfo.technologyName", "==", filters.procedureTechnology));
|
|
6856
|
+
} else if (filters.procedureSubcategory) {
|
|
6857
|
+
constraints.push((0, import_firestore21.where)("proceduresInfo.subcategoryName", "==", filters.procedureSubcategory));
|
|
6858
|
+
} else if (filters.procedureCategory) {
|
|
6859
|
+
constraints.push((0, import_firestore21.where)("proceduresInfo.categoryName", "==", filters.procedureCategory));
|
|
6860
|
+
} else if (filters.procedureFamily) {
|
|
6861
|
+
constraints.push((0, import_firestore21.where)("proceduresInfo.family", "==", filters.procedureFamily));
|
|
6873
6862
|
}
|
|
6874
|
-
if (filters.
|
|
6875
|
-
|
|
6876
|
-
return filters.specialties.every(
|
|
6877
|
-
(specialty) => practitioner.certification.specialties.includes(specialty)
|
|
6878
|
-
);
|
|
6879
|
-
});
|
|
6863
|
+
if (filters.minRating !== void 0) {
|
|
6864
|
+
constraints.push((0, import_firestore21.where)("reviewInfo.averageRating", ">=", filters.minRating));
|
|
6880
6865
|
}
|
|
6881
|
-
if (filters.
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
}
|
|
6894
|
-
if (filters.procedureFamily) {
|
|
6895
|
-
return procedure.family === filters.procedureFamily;
|
|
6896
|
-
}
|
|
6897
|
-
return false;
|
|
6898
|
-
});
|
|
6899
|
-
});
|
|
6866
|
+
if (filters.maxRating !== void 0) {
|
|
6867
|
+
constraints.push((0, import_firestore21.where)("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
6868
|
+
}
|
|
6869
|
+
constraints.push((0, import_firestore21.orderBy)("fullNameLower"));
|
|
6870
|
+
if (filters.lastDoc) {
|
|
6871
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
6872
|
+
constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
|
|
6873
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
6874
|
+
constraints.push((0, import_firestore21.startAfter)(...filters.lastDoc));
|
|
6875
|
+
} else {
|
|
6876
|
+
constraints.push((0, import_firestore21.startAfter)(filters.lastDoc));
|
|
6877
|
+
}
|
|
6900
6878
|
}
|
|
6879
|
+
constraints.push((0, import_firestore21.limit)(filters.pagination || 5));
|
|
6880
|
+
const q = (0, import_firestore21.query)((0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION), ...constraints);
|
|
6881
|
+
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6882
|
+
let practitioners = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
6901
6883
|
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
6902
6884
|
const location = filters.location;
|
|
6903
6885
|
const radiusInKm = filters.radiusInKm;
|
|
@@ -6913,22 +6895,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6913
6895
|
});
|
|
6914
6896
|
});
|
|
6915
6897
|
}
|
|
6916
|
-
|
|
6917
|
-
practitioners = practitioners.filter(
|
|
6918
|
-
(p) => p.reviewInfo.averageRating >= filters.minRating
|
|
6919
|
-
);
|
|
6920
|
-
}
|
|
6921
|
-
if (filters.maxRating !== void 0) {
|
|
6922
|
-
practitioners = practitioners.filter(
|
|
6923
|
-
(p) => p.reviewInfo.averageRating <= filters.maxRating
|
|
6924
|
-
);
|
|
6925
|
-
}
|
|
6926
|
-
console.log(
|
|
6927
|
-
`[PRACTITIONER_SERVICE] Filtered to ${practitioners.length} practitioners`
|
|
6928
|
-
);
|
|
6929
|
-
if (filters.pagination && filters.pagination > 0) {
|
|
6930
|
-
practitioners = practitioners.slice(0, filters.pagination);
|
|
6931
|
-
}
|
|
6898
|
+
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6932
6899
|
return {
|
|
6933
6900
|
practitioners,
|
|
6934
6901
|
lastDoc
|
|
@@ -6993,6 +6960,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6993
6960
|
}
|
|
6994
6961
|
const consultationData = {
|
|
6995
6962
|
name: "Free Consultation",
|
|
6963
|
+
nameLower: "free consultation",
|
|
6996
6964
|
description: "Free initial consultation to discuss treatment options and assess patient needs.",
|
|
6997
6965
|
family: "aesthetics" /* AESTHETICS */,
|
|
6998
6966
|
categoryId: "consultation",
|
|
@@ -8566,168 +8534,64 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
|
|
|
8566
8534
|
var import_firestore26 = require("firebase/firestore");
|
|
8567
8535
|
var import_geofire_common6 = require("geofire-common");
|
|
8568
8536
|
async function getClinicsByFilters(db, filters) {
|
|
8569
|
-
|
|
8570
|
-
"[FILTER_UTILS] Starting clinic filtering with criteria:",
|
|
8571
|
-
filters
|
|
8572
|
-
);
|
|
8573
|
-
const isGeoQuery = filters.center && filters.radiusInKm && filters.radiusInKm > 0;
|
|
8537
|
+
var _a;
|
|
8574
8538
|
const constraints = [];
|
|
8575
|
-
|
|
8576
|
-
constraints.push((0, import_firestore26.where)("isActive", "==", filters.isActive));
|
|
8577
|
-
} else {
|
|
8578
|
-
constraints.push((0, import_firestore26.where)("isActive", "==", true));
|
|
8579
|
-
}
|
|
8539
|
+
constraints.push((0, import_firestore26.where)("isActive", "==", (_a = filters.isActive) != null ? _a : true));
|
|
8580
8540
|
if (filters.tags && filters.tags.length > 0) {
|
|
8581
8541
|
constraints.push((0, import_firestore26.where)("tags", "array-contains", filters.tags[0]));
|
|
8582
8542
|
}
|
|
8583
8543
|
if (filters.procedureTechnology) {
|
|
8584
|
-
constraints.push(
|
|
8585
|
-
(0, import_firestore26.where)("servicesInfo.technology", "==", filters.procedureTechnology)
|
|
8586
|
-
);
|
|
8544
|
+
constraints.push((0, import_firestore26.where)("servicesInfo.technology", "==", filters.procedureTechnology));
|
|
8587
8545
|
} else if (filters.procedureSubcategory) {
|
|
8588
|
-
constraints.push(
|
|
8589
|
-
(0, import_firestore26.where)("servicesInfo.subCategory", "==", filters.procedureSubcategory)
|
|
8590
|
-
);
|
|
8546
|
+
constraints.push((0, import_firestore26.where)("servicesInfo.subCategory", "==", filters.procedureSubcategory));
|
|
8591
8547
|
} else if (filters.procedureCategory) {
|
|
8592
|
-
constraints.push(
|
|
8593
|
-
(0, import_firestore26.where)("servicesInfo.category", "==", filters.procedureCategory)
|
|
8594
|
-
);
|
|
8548
|
+
constraints.push((0, import_firestore26.where)("servicesInfo.category", "==", filters.procedureCategory));
|
|
8595
8549
|
} else if (filters.procedureFamily) {
|
|
8596
|
-
constraints.push(
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
if (filters.
|
|
8601
|
-
|
|
8602
|
-
constraints.push((0, import_firestore26.
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
}
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
if (
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
)
|
|
8617
|
-
|
|
8618
|
-
for (const bound of bounds) {
|
|
8619
|
-
const geoConstraints = [
|
|
8620
|
-
...constraints,
|
|
8621
|
-
(0, import_firestore26.where)("location.geohash", ">=", bound[0]),
|
|
8622
|
-
(0, import_firestore26.where)("location.geohash", "<=", bound[1])
|
|
8623
|
-
];
|
|
8624
|
-
const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...geoConstraints);
|
|
8625
|
-
const querySnapshot = await (0, import_firestore26.getDocs)(q);
|
|
8626
|
-
console.log(
|
|
8627
|
-
`[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics in geo bound`
|
|
8628
|
-
);
|
|
8629
|
-
for (const doc37 of querySnapshot.docs) {
|
|
8630
|
-
const clinic = { ...doc37.data(), id: doc37.id };
|
|
8631
|
-
const distance = (0, import_geofire_common6.distanceBetween)(
|
|
8632
|
-
[center.latitude, center.longitude],
|
|
8633
|
-
[clinic.location.latitude, clinic.location.longitude]
|
|
8634
|
-
);
|
|
8635
|
-
const distanceInKm = distance / 1e3;
|
|
8636
|
-
if (distanceInKm <= radiusInKm) {
|
|
8637
|
-
matchingClinics.push({
|
|
8638
|
-
...clinic,
|
|
8639
|
-
distance: distanceInKm
|
|
8640
|
-
});
|
|
8641
|
-
}
|
|
8642
|
-
}
|
|
8643
|
-
}
|
|
8644
|
-
let filteredClinics = matchingClinics;
|
|
8645
|
-
if (filters.tags && filters.tags.length > 1) {
|
|
8646
|
-
filteredClinics = filteredClinics.filter((clinic) => {
|
|
8647
|
-
return filters.tags.every((tag) => clinic.tags.includes(tag));
|
|
8648
|
-
});
|
|
8649
|
-
}
|
|
8650
|
-
if (filters.minRating !== void 0) {
|
|
8651
|
-
filteredClinics = filteredClinics.filter(
|
|
8652
|
-
(clinic) => clinic.reviewInfo.averageRating >= filters.minRating
|
|
8653
|
-
);
|
|
8654
|
-
}
|
|
8655
|
-
if (filters.maxRating !== void 0) {
|
|
8656
|
-
filteredClinics = filteredClinics.filter(
|
|
8657
|
-
(clinic) => clinic.reviewInfo.averageRating <= filters.maxRating
|
|
8658
|
-
);
|
|
8659
|
-
}
|
|
8660
|
-
filteredClinics.sort((a, b) => a.distance - b.distance);
|
|
8661
|
-
if (filters.pagination && filters.pagination > 0) {
|
|
8662
|
-
let startIndex = 0;
|
|
8663
|
-
if (filters.lastDoc) {
|
|
8664
|
-
const lastDocIndex = filteredClinics.findIndex(
|
|
8665
|
-
(clinic) => clinic.id === filters.lastDoc.id
|
|
8666
|
-
);
|
|
8667
|
-
if (lastDocIndex !== -1) {
|
|
8668
|
-
startIndex = lastDocIndex + 1;
|
|
8669
|
-
}
|
|
8670
|
-
}
|
|
8671
|
-
const paginatedClinics = filteredClinics.slice(
|
|
8672
|
-
startIndex,
|
|
8673
|
-
startIndex + filters.pagination
|
|
8674
|
-
);
|
|
8675
|
-
lastVisibleDoc = paginatedClinics.length > 0 ? paginatedClinics[paginatedClinics.length - 1] : null;
|
|
8676
|
-
clinicsResult = paginatedClinics;
|
|
8550
|
+
constraints.push((0, import_firestore26.where)("servicesInfo.procedureFamily", "==", filters.procedureFamily));
|
|
8551
|
+
}
|
|
8552
|
+
let useNameLower = false;
|
|
8553
|
+
let searchTerm = "";
|
|
8554
|
+
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
8555
|
+
searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
8556
|
+
constraints.push((0, import_firestore26.where)("nameLower", ">=", searchTerm));
|
|
8557
|
+
constraints.push((0, import_firestore26.where)("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
8558
|
+
useNameLower = true;
|
|
8559
|
+
}
|
|
8560
|
+
if (filters.minRating !== void 0) {
|
|
8561
|
+
constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", ">=", filters.minRating));
|
|
8562
|
+
}
|
|
8563
|
+
if (filters.maxRating !== void 0) {
|
|
8564
|
+
constraints.push((0, import_firestore26.where)("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
8565
|
+
}
|
|
8566
|
+
constraints.push((0, import_firestore26.orderBy)("nameLower"));
|
|
8567
|
+
if (filters.lastDoc) {
|
|
8568
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
8569
|
+
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8570
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
8571
|
+
constraints.push((0, import_firestore26.startAfter)(...filters.lastDoc));
|
|
8677
8572
|
} else {
|
|
8678
|
-
|
|
8573
|
+
constraints.push((0, import_firestore26.startAfter)(filters.lastDoc));
|
|
8679
8574
|
}
|
|
8680
|
-
}
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8575
|
+
}
|
|
8576
|
+
constraints.push((0, import_firestore26.limit)(filters.pagination || 5));
|
|
8577
|
+
const q = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
|
|
8578
|
+
const querySnapshot = await (0, import_firestore26.getDocs)(q);
|
|
8579
|
+
let clinics = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
8580
|
+
if (filters.tags && filters.tags.length > 1) {
|
|
8581
|
+
clinics = clinics.filter((clinic) => filters.tags.every((tag) => clinic.tags.includes(tag)));
|
|
8582
|
+
}
|
|
8583
|
+
if (filters.center && filters.radiusInKm) {
|
|
8584
|
+
clinics = clinics.filter((clinic) => {
|
|
8585
|
+
const distance = (0, import_geofire_common6.distanceBetween)(
|
|
8586
|
+
[filters.center.latitude, filters.center.longitude],
|
|
8587
|
+
[clinic.location.latitude, clinic.location.longitude]
|
|
8588
|
+
) / 1e3;
|
|
8589
|
+
clinic.distance = distance;
|
|
8590
|
+
return distance <= filters.radiusInKm;
|
|
8688
8591
|
});
|
|
8689
|
-
let filteredClinics = clinics;
|
|
8690
|
-
if (filters.center) {
|
|
8691
|
-
const center = filters.center;
|
|
8692
|
-
const clinicsWithDistance = [];
|
|
8693
|
-
filteredClinics.forEach((clinic) => {
|
|
8694
|
-
const distance = (0, import_geofire_common6.distanceBetween)(
|
|
8695
|
-
[center.latitude, center.longitude],
|
|
8696
|
-
[clinic.location.latitude, clinic.location.longitude]
|
|
8697
|
-
);
|
|
8698
|
-
clinicsWithDistance.push({
|
|
8699
|
-
...clinic,
|
|
8700
|
-
distance: distance / 1e3
|
|
8701
|
-
// Convert to kilometers
|
|
8702
|
-
});
|
|
8703
|
-
});
|
|
8704
|
-
filteredClinics = clinicsWithDistance;
|
|
8705
|
-
filteredClinics.sort(
|
|
8706
|
-
(a, b) => a.distance - b.distance
|
|
8707
|
-
);
|
|
8708
|
-
}
|
|
8709
|
-
if (filters.tags && filters.tags.length > 1) {
|
|
8710
|
-
filteredClinics = filteredClinics.filter((clinic) => {
|
|
8711
|
-
return filters.tags.every((tag) => clinic.tags.includes(tag));
|
|
8712
|
-
});
|
|
8713
|
-
}
|
|
8714
|
-
if (filters.minRating !== void 0) {
|
|
8715
|
-
filteredClinics = filteredClinics.filter(
|
|
8716
|
-
(clinic) => clinic.reviewInfo.averageRating >= filters.minRating
|
|
8717
|
-
);
|
|
8718
|
-
}
|
|
8719
|
-
if (filters.maxRating !== void 0) {
|
|
8720
|
-
filteredClinics = filteredClinics.filter(
|
|
8721
|
-
(clinic) => clinic.reviewInfo.averageRating <= filters.maxRating
|
|
8722
|
-
);
|
|
8723
|
-
}
|
|
8724
|
-
lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8725
|
-
clinicsResult = filteredClinics;
|
|
8726
8592
|
}
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
lastDoc: lastVisibleDoc
|
|
8730
|
-
};
|
|
8593
|
+
const lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8594
|
+
return { clinics, lastDoc: lastVisibleDoc };
|
|
8731
8595
|
}
|
|
8732
8596
|
|
|
8733
8597
|
// src/services/clinic/clinic.service.ts
|
|
@@ -8863,6 +8727,8 @@ var ClinicService = class extends BaseService {
|
|
|
8863
8727
|
id: clinicId,
|
|
8864
8728
|
clinicGroupId: validatedData.clinicGroupId,
|
|
8865
8729
|
name: validatedData.name,
|
|
8730
|
+
nameLower: validatedData.name.toLowerCase(),
|
|
8731
|
+
// Add this line
|
|
8866
8732
|
description: validatedData.description,
|
|
8867
8733
|
location: { ...location, geohash: hash },
|
|
8868
8734
|
contactInfo: validatedData.contactInfo,
|
|
@@ -8963,6 +8829,9 @@ var ClinicService = class extends BaseService {
|
|
|
8963
8829
|
updatePayload[field] = validatedData[field];
|
|
8964
8830
|
}
|
|
8965
8831
|
}
|
|
8832
|
+
if (validatedData.name) {
|
|
8833
|
+
updatePayload.nameLower = validatedData.name.toLowerCase();
|
|
8834
|
+
}
|
|
8966
8835
|
if (validatedData.location) {
|
|
8967
8836
|
const loc = validatedData.location;
|
|
8968
8837
|
updatePayload.location = {
|
|
@@ -9138,6 +9007,27 @@ var ClinicService = class extends BaseService {
|
|
|
9138
9007
|
async getClinicsByFilters(filters) {
|
|
9139
9008
|
return getClinicsByFilters(this.db, filters);
|
|
9140
9009
|
}
|
|
9010
|
+
/**
|
|
9011
|
+
* Gets all clinics with minimal info for map display (id, name, address, latitude, longitude)
|
|
9012
|
+
* This is optimized for mobile map usage to reduce payload size.
|
|
9013
|
+
* @returns Array of minimal clinic info for map
|
|
9014
|
+
*/
|
|
9015
|
+
async getClinicsForMap() {
|
|
9016
|
+
const clinicsRef = (0, import_firestore27.collection)(this.db, CLINICS_COLLECTION);
|
|
9017
|
+
const snapshot = await (0, import_firestore27.getDocs)(clinicsRef);
|
|
9018
|
+
const clinicsForMap = snapshot.docs.map((doc37) => {
|
|
9019
|
+
var _a, _b, _c;
|
|
9020
|
+
const data = doc37.data();
|
|
9021
|
+
return {
|
|
9022
|
+
id: doc37.id,
|
|
9023
|
+
name: data.name,
|
|
9024
|
+
address: ((_a = data.location) == null ? void 0 : _a.address) || "",
|
|
9025
|
+
latitude: (_b = data.location) == null ? void 0 : _b.latitude,
|
|
9026
|
+
longitude: (_c = data.location) == null ? void 0 : _c.longitude
|
|
9027
|
+
};
|
|
9028
|
+
});
|
|
9029
|
+
return clinicsForMap;
|
|
9030
|
+
}
|
|
9141
9031
|
};
|
|
9142
9032
|
|
|
9143
9033
|
// src/services/auth/utils/firebase.utils.ts
|
|
@@ -14319,6 +14209,7 @@ var import_firestore45 = require("firebase/firestore");
|
|
|
14319
14209
|
var import_zod24 = require("zod");
|
|
14320
14210
|
var createProcedureSchema = import_zod24.z.object({
|
|
14321
14211
|
name: import_zod24.z.string().min(1).max(200),
|
|
14212
|
+
nameLower: import_zod24.z.string().min(1).max(200),
|
|
14322
14213
|
description: import_zod24.z.string().min(1).max(2e3),
|
|
14323
14214
|
family: import_zod24.z.nativeEnum(ProcedureFamily),
|
|
14324
14215
|
categoryId: import_zod24.z.string().min(1),
|
|
@@ -14336,6 +14227,7 @@ var createProcedureSchema = import_zod24.z.object({
|
|
|
14336
14227
|
});
|
|
14337
14228
|
var updateProcedureSchema = import_zod24.z.object({
|
|
14338
14229
|
name: import_zod24.z.string().min(3).max(100).optional(),
|
|
14230
|
+
nameLower: import_zod24.z.string().min(1).max(200).optional(),
|
|
14339
14231
|
description: import_zod24.z.string().min(3).max(1e3).optional(),
|
|
14340
14232
|
price: import_zod24.z.number().min(0).optional(),
|
|
14341
14233
|
currency: import_zod24.z.nativeEnum(Currency).optional(),
|
|
@@ -14352,6 +14244,7 @@ var updateProcedureSchema = import_zod24.z.object({
|
|
|
14352
14244
|
});
|
|
14353
14245
|
var procedureSchema = createProcedureSchema.extend({
|
|
14354
14246
|
id: import_zod24.z.string().min(1),
|
|
14247
|
+
nameLower: import_zod24.z.string().min(1).max(200),
|
|
14355
14248
|
category: import_zod24.z.any(),
|
|
14356
14249
|
// We'll validate the full category object separately
|
|
14357
14250
|
subcategory: import_zod24.z.any(),
|
|
@@ -14520,6 +14413,7 @@ var ProcedureService = class extends BaseService {
|
|
|
14520
14413
|
const newProcedure = {
|
|
14521
14414
|
id: procedureId,
|
|
14522
14415
|
...validatedData,
|
|
14416
|
+
nameLower: validatedData.nameLower || validatedData.name.toLowerCase(),
|
|
14523
14417
|
photos: processedPhotos,
|
|
14524
14418
|
category,
|
|
14525
14419
|
// Embed full objects
|
|
@@ -14653,6 +14547,7 @@ var ProcedureService = class extends BaseService {
|
|
|
14653
14547
|
const newProcedure = {
|
|
14654
14548
|
id: procedureId,
|
|
14655
14549
|
...validatedData,
|
|
14550
|
+
nameLower: validatedData.nameLower || validatedData.name.toLowerCase(),
|
|
14656
14551
|
practitionerId,
|
|
14657
14552
|
// Override practitionerId with the correct one
|
|
14658
14553
|
photos: processedPhotos,
|
|
@@ -14831,6 +14726,9 @@ var ProcedureService = class extends BaseService {
|
|
|
14831
14726
|
};
|
|
14832
14727
|
}
|
|
14833
14728
|
let finalCategoryId = existingProcedure.category.id;
|
|
14729
|
+
if (validatedData.name) {
|
|
14730
|
+
updatedProcedureData.nameLower = validatedData.name.toLowerCase();
|
|
14731
|
+
}
|
|
14834
14732
|
if (validatedData.categoryId) {
|
|
14835
14733
|
const category = await this.categoryService.getById(
|
|
14836
14734
|
validatedData.categoryId
|
|
@@ -14988,6 +14886,8 @@ var ProcedureService = class extends BaseService {
|
|
|
14988
14886
|
/**
|
|
14989
14887
|
* Searches and filters procedures based on multiple criteria
|
|
14990
14888
|
*
|
|
14889
|
+
* @note Frontend MORA da šalje ceo snapshot (ili barem sva polja po kojima sortiraš, npr. nameLower) kao lastDoc za paginaciju, a ne samo id!
|
|
14890
|
+
*
|
|
14991
14891
|
* @param filters - Various filters to apply
|
|
14992
14892
|
* @param filters.nameSearch - Optional search text for procedure name
|
|
14993
14893
|
* @param filters.treatmentBenefits - Optional array of treatment benefits to filter by
|
|
@@ -15008,10 +14908,6 @@ var ProcedureService = class extends BaseService {
|
|
|
15008
14908
|
*/
|
|
15009
14909
|
async getProceduresByFilters(filters) {
|
|
15010
14910
|
try {
|
|
15011
|
-
console.log(
|
|
15012
|
-
"[PROCEDURE_SERVICE] Starting procedure filtering with criteria:",
|
|
15013
|
-
filters
|
|
15014
|
-
);
|
|
15015
14911
|
const isGeoQuery = filters.location && filters.radiusInKm && filters.radiusInKm > 0;
|
|
15016
14912
|
const constraints = [];
|
|
15017
14913
|
if (filters.isActive !== void 0) {
|
|
@@ -15022,15 +14918,53 @@ var ProcedureService = class extends BaseService {
|
|
|
15022
14918
|
if (filters.procedureFamily) {
|
|
15023
14919
|
constraints.push((0, import_firestore45.where)("family", "==", filters.procedureFamily));
|
|
15024
14920
|
}
|
|
15025
|
-
|
|
15026
|
-
|
|
15027
|
-
|
|
15028
|
-
|
|
15029
|
-
|
|
14921
|
+
if (filters.procedureCategory) {
|
|
14922
|
+
constraints.push((0, import_firestore45.where)("category.id", "==", filters.procedureCategory));
|
|
14923
|
+
}
|
|
14924
|
+
if (filters.procedureSubcategory) {
|
|
14925
|
+
constraints.push((0, import_firestore45.where)("subcategory.id", "==", filters.procedureSubcategory));
|
|
14926
|
+
}
|
|
14927
|
+
if (filters.procedureTechnology) {
|
|
14928
|
+
constraints.push((0, import_firestore45.where)("technology.id", "==", filters.procedureTechnology));
|
|
14929
|
+
}
|
|
14930
|
+
if (filters.minPrice !== void 0) {
|
|
14931
|
+
constraints.push((0, import_firestore45.where)("price", ">=", filters.minPrice));
|
|
14932
|
+
}
|
|
14933
|
+
if (filters.maxPrice !== void 0) {
|
|
14934
|
+
constraints.push((0, import_firestore45.where)("price", "<=", filters.maxPrice));
|
|
14935
|
+
}
|
|
14936
|
+
if (filters.minRating !== void 0) {
|
|
14937
|
+
constraints.push((0, import_firestore45.where)("reviewInfo.averageRating", ">=", filters.minRating));
|
|
14938
|
+
}
|
|
14939
|
+
if (filters.maxRating !== void 0) {
|
|
14940
|
+
constraints.push((0, import_firestore45.where)("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
14941
|
+
}
|
|
14942
|
+
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
14943
|
+
constraints.push((0, import_firestore45.where)("treatmentBenefits", "array-contains-any", filters.treatmentBenefits));
|
|
14944
|
+
}
|
|
14945
|
+
let useNameLower = false;
|
|
14946
|
+
let searchTerm = "";
|
|
14947
|
+
if (filters.nameSearch && filters.nameSearch.trim() !== "") {
|
|
14948
|
+
searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
14949
|
+
useNameLower = true;
|
|
14950
|
+
constraints.push((0, import_firestore45.where)("nameLower", ">=", searchTerm));
|
|
14951
|
+
constraints.push((0, import_firestore45.where)("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
14952
|
+
constraints.push((0, import_firestore45.orderBy)("nameLower"));
|
|
14953
|
+
} else {
|
|
14954
|
+
constraints.push((0, import_firestore45.orderBy)("nameLower"));
|
|
14955
|
+
}
|
|
14956
|
+
if (filters.lastDoc) {
|
|
14957
|
+
if (typeof filters.lastDoc.data === "function") {
|
|
14958
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
14959
|
+
} else if (Array.isArray(filters.lastDoc)) {
|
|
14960
|
+
constraints.push((0, import_firestore45.startAfter)(...filters.lastDoc));
|
|
14961
|
+
} else {
|
|
14962
|
+
constraints.push((0, import_firestore45.startAfter)(filters.lastDoc));
|
|
14963
|
+
}
|
|
14964
|
+
}
|
|
14965
|
+
if (filters.pagination && filters.pagination > 0) {
|
|
15030
14966
|
constraints.push((0, import_firestore45.limit)(filters.pagination));
|
|
15031
14967
|
}
|
|
15032
|
-
let proceduresResult = [];
|
|
15033
|
-
let lastVisibleDoc = null;
|
|
15034
14968
|
if (isGeoQuery) {
|
|
15035
14969
|
const center = filters.location;
|
|
15036
14970
|
const radiusInKm = filters.radiusInKm;
|
|
@@ -15039,178 +14973,63 @@ var ProcedureService = class extends BaseService {
|
|
|
15039
14973
|
radiusInKm * 1e3
|
|
15040
14974
|
// Convert to meters
|
|
15041
14975
|
);
|
|
15042
|
-
|
|
14976
|
+
let allDocs = [];
|
|
15043
14977
|
for (const bound of bounds) {
|
|
15044
14978
|
const geoConstraints = [
|
|
15045
|
-
...constraints,
|
|
14979
|
+
...constraints.filter((c) => !c.fieldPath || c.fieldPath !== "name"),
|
|
14980
|
+
// Remove name orderBy for geo
|
|
15046
14981
|
(0, import_firestore45.where)("clinicInfo.location.geohash", ">=", bound[0]),
|
|
15047
|
-
(0, import_firestore45.where)("clinicInfo.location.geohash", "<=", bound[1])
|
|
14982
|
+
(0, import_firestore45.where)("clinicInfo.location.geohash", "<=", bound[1]),
|
|
14983
|
+
(0, import_firestore45.orderBy)("clinicInfo.location.geohash")
|
|
15048
14984
|
];
|
|
15049
|
-
const q = (0, import_firestore45.query)(
|
|
15050
|
-
(0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
|
|
15051
|
-
...geoConstraints
|
|
15052
|
-
);
|
|
14985
|
+
const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...geoConstraints);
|
|
15053
14986
|
const querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
15054
|
-
console.log(
|
|
15055
|
-
`[PROCEDURE_SERVICE] Found ${querySnapshot.docs.length} procedures in geo bound`
|
|
15056
|
-
);
|
|
15057
14987
|
for (const doc37 of querySnapshot.docs) {
|
|
15058
14988
|
const procedure = { ...doc37.data(), id: doc37.id };
|
|
15059
14989
|
const distance = (0, import_geofire_common8.distanceBetween)(
|
|
15060
14990
|
[center.latitude, center.longitude],
|
|
15061
|
-
[
|
|
15062
|
-
procedure.clinicInfo.location.latitude,
|
|
15063
|
-
procedure.clinicInfo.location.longitude
|
|
15064
|
-
]
|
|
14991
|
+
[procedure.clinicInfo.location.latitude, procedure.clinicInfo.location.longitude]
|
|
15065
14992
|
);
|
|
15066
14993
|
const distanceInKm = distance / 1e3;
|
|
15067
14994
|
if (distanceInKm <= radiusInKm) {
|
|
15068
|
-
|
|
15069
|
-
...procedure,
|
|
15070
|
-
distance: distanceInKm
|
|
15071
|
-
});
|
|
14995
|
+
allDocs.push({ ...procedure, distance: distanceInKm });
|
|
15072
14996
|
}
|
|
15073
14997
|
}
|
|
15074
14998
|
}
|
|
15075
|
-
|
|
15076
|
-
|
|
15077
|
-
filteredProcedures,
|
|
15078
|
-
filters
|
|
15079
|
-
);
|
|
15080
|
-
filteredProcedures.sort((a, b) => a.distance - b.distance);
|
|
14999
|
+
allDocs.sort((a, b) => a.distance - b.distance);
|
|
15000
|
+
let paginated = allDocs;
|
|
15081
15001
|
if (filters.pagination && filters.pagination > 0) {
|
|
15082
15002
|
let startIndex = 0;
|
|
15083
15003
|
if (filters.lastDoc) {
|
|
15084
|
-
const lastDocIndex =
|
|
15085
|
-
|
|
15086
|
-
);
|
|
15087
|
-
if (lastDocIndex !== -1) {
|
|
15088
|
-
startIndex = lastDocIndex + 1;
|
|
15089
|
-
}
|
|
15004
|
+
const lastDocIndex = allDocs.findIndex((p) => p.id === filters.lastDoc.id);
|
|
15005
|
+
if (lastDocIndex !== -1) startIndex = lastDocIndex + 1;
|
|
15090
15006
|
}
|
|
15091
|
-
|
|
15092
|
-
startIndex,
|
|
15093
|
-
startIndex + filters.pagination
|
|
15094
|
-
);
|
|
15095
|
-
lastVisibleDoc = paginatedProcedures.length > 0 ? paginatedProcedures[paginatedProcedures.length - 1] : null;
|
|
15096
|
-
proceduresResult = paginatedProcedures;
|
|
15097
|
-
} else {
|
|
15098
|
-
proceduresResult = filteredProcedures;
|
|
15007
|
+
paginated = allDocs.slice(startIndex, startIndex + filters.pagination);
|
|
15099
15008
|
}
|
|
15009
|
+
const lastVisibleDoc = paginated.length > 0 ? paginated[paginated.length - 1] : null;
|
|
15010
|
+
return { procedures: paginated, lastDoc: lastVisibleDoc };
|
|
15100
15011
|
} else {
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
if (filters.location) {
|
|
15113
|
-
const center = filters.location;
|
|
15114
|
-
const proceduresWithDistance = [];
|
|
15115
|
-
procedures.forEach((procedure) => {
|
|
15116
|
-
const distance = (0, import_geofire_common8.distanceBetween)(
|
|
15117
|
-
[center.latitude, center.longitude],
|
|
15118
|
-
[
|
|
15119
|
-
procedure.clinicInfo.location.latitude,
|
|
15120
|
-
procedure.clinicInfo.location.longitude
|
|
15121
|
-
]
|
|
15122
|
-
);
|
|
15123
|
-
proceduresWithDistance.push({
|
|
15124
|
-
...procedure,
|
|
15125
|
-
distance: distance / 1e3
|
|
15126
|
-
// Convert to kilometers
|
|
15127
|
-
});
|
|
15128
|
-
});
|
|
15129
|
-
let filteredProcedures = proceduresWithDistance;
|
|
15130
|
-
filteredProcedures = this.applyInMemoryFilters(
|
|
15131
|
-
filteredProcedures,
|
|
15132
|
-
filters
|
|
15133
|
-
);
|
|
15134
|
-
filteredProcedures.sort((a, b) => a.distance - b.distance);
|
|
15135
|
-
proceduresResult = filteredProcedures;
|
|
15136
|
-
} else {
|
|
15137
|
-
let filteredProcedures = this.applyInMemoryFilters(
|
|
15138
|
-
procedures,
|
|
15139
|
-
filters
|
|
15140
|
-
);
|
|
15141
|
-
proceduresResult = filteredProcedures;
|
|
15012
|
+
let q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15013
|
+
let querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
15014
|
+
if (useNameLower && querySnapshot.empty && searchTerm) {
|
|
15015
|
+
constraints.pop();
|
|
15016
|
+
constraints.pop();
|
|
15017
|
+
constraints.pop();
|
|
15018
|
+
constraints.push((0, import_firestore45.where)("name", ">=", searchTerm));
|
|
15019
|
+
constraints.push((0, import_firestore45.where)("name", "<=", searchTerm + "\uF8FF"));
|
|
15020
|
+
constraints.push((0, import_firestore45.orderBy)("name"));
|
|
15021
|
+
q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
15022
|
+
querySnapshot = await (0, import_firestore45.getDocs)(q);
|
|
15142
15023
|
}
|
|
15143
|
-
|
|
15024
|
+
const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
|
|
15025
|
+
const lastVisibleDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15026
|
+
return { procedures, lastDoc: lastVisibleDoc };
|
|
15144
15027
|
}
|
|
15145
|
-
return {
|
|
15146
|
-
procedures: proceduresResult,
|
|
15147
|
-
lastDoc: lastVisibleDoc
|
|
15148
|
-
};
|
|
15149
15028
|
} catch (error) {
|
|
15150
15029
|
console.error("[PROCEDURE_SERVICE] Error filtering procedures:", error);
|
|
15151
15030
|
throw error;
|
|
15152
15031
|
}
|
|
15153
15032
|
}
|
|
15154
|
-
/**
|
|
15155
|
-
* Helper method to apply in-memory filters to procedures
|
|
15156
|
-
* Used by getProceduresByFilters to apply filters that can't be done in Firestore queries
|
|
15157
|
-
*
|
|
15158
|
-
* @param procedures - The procedures to filter
|
|
15159
|
-
* @param filters - The filters to apply
|
|
15160
|
-
* @returns Filtered procedures
|
|
15161
|
-
*/
|
|
15162
|
-
applyInMemoryFilters(procedures, filters) {
|
|
15163
|
-
let filteredProcedures = procedures;
|
|
15164
|
-
if (filters.nameSearch && filters.nameSearch.trim() !== "") {
|
|
15165
|
-
const searchTerm = filters.nameSearch.toLowerCase().trim();
|
|
15166
|
-
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15167
|
-
return procedure.name.toLowerCase().includes(searchTerm);
|
|
15168
|
-
});
|
|
15169
|
-
}
|
|
15170
|
-
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
15171
|
-
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15172
|
-
return filters.treatmentBenefits.every(
|
|
15173
|
-
(benefit) => procedure.treatmentBenefits.includes(benefit)
|
|
15174
|
-
);
|
|
15175
|
-
});
|
|
15176
|
-
}
|
|
15177
|
-
if (filters.procedureCategory) {
|
|
15178
|
-
filteredProcedures = filteredProcedures.filter(
|
|
15179
|
-
(procedure) => procedure.category.id === filters.procedureCategory
|
|
15180
|
-
);
|
|
15181
|
-
}
|
|
15182
|
-
if (filters.procedureSubcategory) {
|
|
15183
|
-
filteredProcedures = filteredProcedures.filter(
|
|
15184
|
-
(procedure) => procedure.subcategory.id === filters.procedureSubcategory
|
|
15185
|
-
);
|
|
15186
|
-
}
|
|
15187
|
-
if (filters.procedureTechnology) {
|
|
15188
|
-
filteredProcedures = filteredProcedures.filter(
|
|
15189
|
-
(procedure) => procedure.technology.id === filters.procedureTechnology
|
|
15190
|
-
);
|
|
15191
|
-
}
|
|
15192
|
-
if (filters.minPrice !== void 0) {
|
|
15193
|
-
filteredProcedures = filteredProcedures.filter(
|
|
15194
|
-
(procedure) => procedure.price >= filters.minPrice
|
|
15195
|
-
);
|
|
15196
|
-
}
|
|
15197
|
-
if (filters.maxPrice !== void 0) {
|
|
15198
|
-
filteredProcedures = filteredProcedures.filter(
|
|
15199
|
-
(procedure) => procedure.price <= filters.maxPrice
|
|
15200
|
-
);
|
|
15201
|
-
}
|
|
15202
|
-
if (filters.minRating !== void 0) {
|
|
15203
|
-
filteredProcedures = filteredProcedures.filter(
|
|
15204
|
-
(procedure) => procedure.reviewInfo.averageRating >= filters.minRating
|
|
15205
|
-
);
|
|
15206
|
-
}
|
|
15207
|
-
if (filters.maxRating !== void 0) {
|
|
15208
|
-
filteredProcedures = filteredProcedures.filter(
|
|
15209
|
-
(procedure) => procedure.reviewInfo.averageRating <= filters.maxRating
|
|
15210
|
-
);
|
|
15211
|
-
}
|
|
15212
|
-
return filteredProcedures;
|
|
15213
|
-
}
|
|
15214
15033
|
/**
|
|
15215
15034
|
* Creates a consultation procedure without requiring a product
|
|
15216
15035
|
* This is a special method for consultation procedures that don't use products
|
|
@@ -15283,6 +15102,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15283
15102
|
const newProcedure = {
|
|
15284
15103
|
id: procedureId,
|
|
15285
15104
|
...data,
|
|
15105
|
+
nameLower: data.nameLower || data.name.toLowerCase(),
|
|
15286
15106
|
photos: processedPhotos,
|
|
15287
15107
|
category,
|
|
15288
15108
|
subcategory,
|
|
@@ -15319,6 +15139,29 @@ var ProcedureService = class extends BaseService {
|
|
|
15319
15139
|
const savedDoc = await (0, import_firestore45.getDoc)(procedureRef);
|
|
15320
15140
|
return savedDoc.data();
|
|
15321
15141
|
}
|
|
15142
|
+
/**
|
|
15143
|
+
* Gets all procedures with minimal info for map display (id, name, clinicId, clinicName, address, latitude, longitude)
|
|
15144
|
+
* This is optimized for mobile map usage to reduce payload size.
|
|
15145
|
+
* @returns Array of minimal procedure info for map
|
|
15146
|
+
*/
|
|
15147
|
+
async getProceduresForMap() {
|
|
15148
|
+
const proceduresRef = (0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION);
|
|
15149
|
+
const snapshot = await (0, import_firestore45.getDocs)(proceduresRef);
|
|
15150
|
+
const proceduresForMap = snapshot.docs.map((doc37) => {
|
|
15151
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
15152
|
+
const data = doc37.data();
|
|
15153
|
+
return {
|
|
15154
|
+
id: doc37.id,
|
|
15155
|
+
name: data.name,
|
|
15156
|
+
clinicId: (_a = data.clinicInfo) == null ? void 0 : _a.id,
|
|
15157
|
+
clinicName: (_b = data.clinicInfo) == null ? void 0 : _b.name,
|
|
15158
|
+
address: ((_d = (_c = data.clinicInfo) == null ? void 0 : _c.location) == null ? void 0 : _d.address) || "",
|
|
15159
|
+
latitude: (_f = (_e = data.clinicInfo) == null ? void 0 : _e.location) == null ? void 0 : _f.latitude,
|
|
15160
|
+
longitude: (_h = (_g = data.clinicInfo) == null ? void 0 : _g.location) == null ? void 0 : _h.longitude
|
|
15161
|
+
};
|
|
15162
|
+
});
|
|
15163
|
+
return proceduresForMap;
|
|
15164
|
+
}
|
|
15322
15165
|
};
|
|
15323
15166
|
|
|
15324
15167
|
// src/services/reviews/reviews.service.ts
|