@blackcode_sa/metaestetics-api 1.12.0 → 1.12.1
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 +36 -3
- package/dist/admin/index.d.ts +36 -3
- package/dist/backoffice/index.d.mts +33 -1
- package/dist/backoffice/index.d.ts +33 -1
- package/dist/index.d.mts +58 -4
- package/dist/index.d.ts +58 -4
- package/dist/index.js +336 -96
- package/dist/index.mjs +336 -96
- package/package.json +1 -1
- package/src/backoffice/expo-safe/index.ts +1 -0
- package/src/backoffice/types/index.ts +1 -0
- package/src/backoffice/types/procedure-product.types.ts +38 -0
- package/src/services/practitioner/practitioner.service.ts +201 -83
- package/src/services/procedure/README.md +76 -1
- package/src/services/procedure/procedure.service.ts +102 -1
- package/src/types/procedure/index.ts +19 -3
- package/src/validations/procedure-product.schema.ts +41 -0
- package/src/validations/procedure.schema.ts +59 -8
package/dist/index.js
CHANGED
|
@@ -6595,7 +6595,9 @@ var PractitionerService = class extends BaseService {
|
|
|
6595
6595
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
6596
6596
|
}
|
|
6597
6597
|
const currentPractitioner = practitionerDoc.data();
|
|
6598
|
-
let processedData = {
|
|
6598
|
+
let processedData = {
|
|
6599
|
+
...validData
|
|
6600
|
+
};
|
|
6599
6601
|
if (validData.basicInfo) {
|
|
6600
6602
|
processedData.basicInfo = await this.processBasicInfo(
|
|
6601
6603
|
validData.basicInfo,
|
|
@@ -6829,7 +6831,9 @@ var PractitionerService = class extends BaseService {
|
|
|
6829
6831
|
*/
|
|
6830
6832
|
async getPractitionersByFilters(filters) {
|
|
6831
6833
|
try {
|
|
6832
|
-
console.log(
|
|
6834
|
+
console.log(
|
|
6835
|
+
"[PRACTITIONER_SERVICE] Starting practitioner filtering with fallback strategies"
|
|
6836
|
+
);
|
|
6833
6837
|
if (filters.location && filters.radiusInKm) {
|
|
6834
6838
|
console.log("[PRACTITIONER_SERVICE] Executing geo query:", {
|
|
6835
6839
|
location: filters.location,
|
|
@@ -6837,14 +6841,19 @@ var PractitionerService = class extends BaseService {
|
|
|
6837
6841
|
serviceName: "PractitionerService"
|
|
6838
6842
|
});
|
|
6839
6843
|
if (!filters.location.latitude || !filters.location.longitude) {
|
|
6840
|
-
console.warn(
|
|
6844
|
+
console.warn(
|
|
6845
|
+
"[PRACTITIONER_SERVICE] Invalid location data:",
|
|
6846
|
+
filters.location
|
|
6847
|
+
);
|
|
6841
6848
|
filters.location = void 0;
|
|
6842
6849
|
filters.radiusInKm = void 0;
|
|
6843
6850
|
}
|
|
6844
6851
|
}
|
|
6845
6852
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
6846
6853
|
try {
|
|
6847
|
-
console.log(
|
|
6854
|
+
console.log(
|
|
6855
|
+
"[PRACTITIONER_SERVICE] Strategy 1: Trying fullNameLower search"
|
|
6856
|
+
);
|
|
6848
6857
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
6849
6858
|
const constraints = [];
|
|
6850
6859
|
if (!filters.includeDraftPractitioners) {
|
|
@@ -6864,11 +6873,18 @@ var PractitionerService = class extends BaseService {
|
|
|
6864
6873
|
}
|
|
6865
6874
|
}
|
|
6866
6875
|
constraints.push((0, import_firestore21.limit)(filters.pagination || 10));
|
|
6867
|
-
const q = (0, import_firestore21.query)(
|
|
6876
|
+
const q = (0, import_firestore21.query)(
|
|
6877
|
+
(0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION),
|
|
6878
|
+
...constraints
|
|
6879
|
+
);
|
|
6868
6880
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6869
|
-
const practitioners = querySnapshot.docs.map(
|
|
6881
|
+
const practitioners = querySnapshot.docs.map(
|
|
6882
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
6883
|
+
);
|
|
6870
6884
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6871
|
-
console.log(
|
|
6885
|
+
console.log(
|
|
6886
|
+
`[PRACTITIONER_SERVICE] Strategy 1 success: ${practitioners.length} practitioners`
|
|
6887
|
+
);
|
|
6872
6888
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
6873
6889
|
return { practitioners, lastDoc: null };
|
|
6874
6890
|
}
|
|
@@ -6878,7 +6894,9 @@ var PractitionerService = class extends BaseService {
|
|
|
6878
6894
|
}
|
|
6879
6895
|
}
|
|
6880
6896
|
try {
|
|
6881
|
-
console.log(
|
|
6897
|
+
console.log(
|
|
6898
|
+
"[PRACTITIONER_SERVICE] Strategy 2: Basic query with createdAt ordering"
|
|
6899
|
+
);
|
|
6882
6900
|
const constraints = [];
|
|
6883
6901
|
if (!filters.includeDraftPractitioners) {
|
|
6884
6902
|
constraints.push((0, import_firestore21.where)("status", "==", "active" /* ACTIVE */));
|
|
@@ -6887,14 +6905,22 @@ var PractitionerService = class extends BaseService {
|
|
|
6887
6905
|
if (filters.certifications && filters.certifications.length > 0) {
|
|
6888
6906
|
const certificationsToMatch = filters.certifications;
|
|
6889
6907
|
constraints.push(
|
|
6890
|
-
(0, import_firestore21.where)(
|
|
6908
|
+
(0, import_firestore21.where)(
|
|
6909
|
+
"certification.specialties",
|
|
6910
|
+
"array-contains-any",
|
|
6911
|
+
certificationsToMatch
|
|
6912
|
+
)
|
|
6891
6913
|
);
|
|
6892
6914
|
}
|
|
6893
6915
|
if (filters.minRating !== void 0) {
|
|
6894
|
-
constraints.push(
|
|
6916
|
+
constraints.push(
|
|
6917
|
+
(0, import_firestore21.where)("reviewInfo.averageRating", ">=", filters.minRating)
|
|
6918
|
+
);
|
|
6895
6919
|
}
|
|
6896
6920
|
if (filters.maxRating !== void 0) {
|
|
6897
|
-
constraints.push(
|
|
6921
|
+
constraints.push(
|
|
6922
|
+
(0, import_firestore21.where)("reviewInfo.averageRating", "<=", filters.maxRating)
|
|
6923
|
+
);
|
|
6898
6924
|
}
|
|
6899
6925
|
constraints.push((0, import_firestore21.orderBy)("createdAt", "desc"));
|
|
6900
6926
|
if (filters.location && filters.radiusInKm) {
|
|
@@ -6911,9 +6937,14 @@ var PractitionerService = class extends BaseService {
|
|
|
6911
6937
|
}
|
|
6912
6938
|
constraints.push((0, import_firestore21.limit)(filters.pagination || 10));
|
|
6913
6939
|
}
|
|
6914
|
-
const q = (0, import_firestore21.query)(
|
|
6940
|
+
const q = (0, import_firestore21.query)(
|
|
6941
|
+
(0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION),
|
|
6942
|
+
...constraints
|
|
6943
|
+
);
|
|
6915
6944
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6916
|
-
let practitioners = querySnapshot.docs.map(
|
|
6945
|
+
let practitioners = querySnapshot.docs.map(
|
|
6946
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
6947
|
+
);
|
|
6917
6948
|
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
6918
6949
|
const location = filters.location;
|
|
6919
6950
|
const radiusInKm = filters.radiusInKm;
|
|
@@ -6932,7 +6963,9 @@ var PractitionerService = class extends BaseService {
|
|
|
6932
6963
|
}
|
|
6933
6964
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
6934
6965
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6935
|
-
console.log(
|
|
6966
|
+
console.log(
|
|
6967
|
+
`[PRACTITIONER_SERVICE] Strategy 2 success: ${practitioners.length} practitioners`
|
|
6968
|
+
);
|
|
6936
6969
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
6937
6970
|
return { practitioners, lastDoc: null };
|
|
6938
6971
|
}
|
|
@@ -6941,18 +6974,27 @@ var PractitionerService = class extends BaseService {
|
|
|
6941
6974
|
console.log("[PRACTITIONER_SERVICE] Strategy 2 failed:", error);
|
|
6942
6975
|
}
|
|
6943
6976
|
try {
|
|
6944
|
-
console.log(
|
|
6977
|
+
console.log(
|
|
6978
|
+
"[PRACTITIONER_SERVICE] Strategy 3: Minimal query fallback"
|
|
6979
|
+
);
|
|
6945
6980
|
const constraints = [
|
|
6946
6981
|
(0, import_firestore21.where)("isActive", "==", true),
|
|
6947
6982
|
(0, import_firestore21.orderBy)("createdAt", "desc"),
|
|
6948
6983
|
(0, import_firestore21.limit)(filters.pagination || 10)
|
|
6949
6984
|
];
|
|
6950
|
-
const q = (0, import_firestore21.query)(
|
|
6985
|
+
const q = (0, import_firestore21.query)(
|
|
6986
|
+
(0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION),
|
|
6987
|
+
...constraints
|
|
6988
|
+
);
|
|
6951
6989
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6952
|
-
let practitioners = querySnapshot.docs.map(
|
|
6990
|
+
let practitioners = querySnapshot.docs.map(
|
|
6991
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
6992
|
+
);
|
|
6953
6993
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
6954
6994
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6955
|
-
console.log(
|
|
6995
|
+
console.log(
|
|
6996
|
+
`[PRACTITIONER_SERVICE] Strategy 3 success: ${practitioners.length} practitioners`
|
|
6997
|
+
);
|
|
6956
6998
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
6957
6999
|
return { practitioners, lastDoc: null };
|
|
6958
7000
|
}
|
|
@@ -6961,19 +7003,28 @@ var PractitionerService = class extends BaseService {
|
|
|
6961
7003
|
console.log("[PRACTITIONER_SERVICE] Strategy 3 failed:", error);
|
|
6962
7004
|
}
|
|
6963
7005
|
try {
|
|
6964
|
-
console.log(
|
|
7006
|
+
console.log(
|
|
7007
|
+
"[PRACTITIONER_SERVICE] Strategy 4: Client-side filtering fallback"
|
|
7008
|
+
);
|
|
6965
7009
|
const constraints = [
|
|
6966
7010
|
(0, import_firestore21.where)("isActive", "==", true),
|
|
6967
7011
|
(0, import_firestore21.where)("status", "==", "active" /* ACTIVE */),
|
|
6968
7012
|
(0, import_firestore21.orderBy)("createdAt", "desc"),
|
|
6969
7013
|
(0, import_firestore21.limit)(filters.pagination || 10)
|
|
6970
7014
|
];
|
|
6971
|
-
const q = (0, import_firestore21.query)(
|
|
7015
|
+
const q = (0, import_firestore21.query)(
|
|
7016
|
+
(0, import_firestore21.collection)(this.db, PRACTITIONERS_COLLECTION),
|
|
7017
|
+
...constraints
|
|
7018
|
+
);
|
|
6972
7019
|
const querySnapshot = await (0, import_firestore21.getDocs)(q);
|
|
6973
|
-
let practitioners = querySnapshot.docs.map(
|
|
7020
|
+
let practitioners = querySnapshot.docs.map(
|
|
7021
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
7022
|
+
);
|
|
6974
7023
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
6975
7024
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6976
|
-
console.log(
|
|
7025
|
+
console.log(
|
|
7026
|
+
`[PRACTITIONER_SERVICE] Strategy 4 success: ${practitioners.length} practitioners`
|
|
7027
|
+
);
|
|
6977
7028
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
6978
7029
|
return { practitioners, lastDoc: null };
|
|
6979
7030
|
}
|
|
@@ -6981,10 +7032,15 @@ var PractitionerService = class extends BaseService {
|
|
|
6981
7032
|
} catch (error) {
|
|
6982
7033
|
console.log("[PRACTITIONER_SERVICE] Strategy 4 failed:", error);
|
|
6983
7034
|
}
|
|
6984
|
-
console.log(
|
|
7035
|
+
console.log(
|
|
7036
|
+
"[PRACTITIONER_SERVICE] All strategies failed, returning empty result"
|
|
7037
|
+
);
|
|
6985
7038
|
return { practitioners: [], lastDoc: null };
|
|
6986
7039
|
} catch (error) {
|
|
6987
|
-
console.error(
|
|
7040
|
+
console.error(
|
|
7041
|
+
"[PRACTITIONER_SERVICE] Error filtering practitioners:",
|
|
7042
|
+
error
|
|
7043
|
+
);
|
|
6988
7044
|
return { practitioners: [], lastDoc: null };
|
|
6989
7045
|
}
|
|
6990
7046
|
}
|
|
@@ -7004,63 +7060,93 @@ var PractitionerService = class extends BaseService {
|
|
|
7004
7060
|
const fullNameLower = practitioner.fullNameLower || "";
|
|
7005
7061
|
return firstName.includes(searchTerm) || lastName.includes(searchTerm) || fullName.includes(searchTerm) || fullNameLower.includes(searchTerm);
|
|
7006
7062
|
});
|
|
7007
|
-
console.log(
|
|
7063
|
+
console.log(
|
|
7064
|
+
`[PRACTITIONER_SERVICE] Applied name filter, results: ${filteredPractitioners.length}`
|
|
7065
|
+
);
|
|
7008
7066
|
}
|
|
7009
7067
|
if (filters.certifications && filters.certifications.length > 0) {
|
|
7010
7068
|
const certificationsToMatch = filters.certifications;
|
|
7011
7069
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7012
7070
|
var _a;
|
|
7013
7071
|
const practitionerCerts = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
|
|
7014
|
-
return certificationsToMatch.some(
|
|
7072
|
+
return certificationsToMatch.some(
|
|
7073
|
+
(cert) => practitionerCerts.includes(cert)
|
|
7074
|
+
);
|
|
7015
7075
|
});
|
|
7016
|
-
console.log(
|
|
7076
|
+
console.log(
|
|
7077
|
+
`[PRACTITIONER_SERVICE] Applied certifications filter, results: ${filteredPractitioners.length}`
|
|
7078
|
+
);
|
|
7017
7079
|
}
|
|
7018
7080
|
if (filters.specialties && filters.specialties.length > 0) {
|
|
7019
7081
|
const specialtiesToMatch = filters.specialties;
|
|
7020
7082
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7021
7083
|
var _a;
|
|
7022
7084
|
const practitionerSpecs = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
|
|
7023
|
-
return specialtiesToMatch.some(
|
|
7085
|
+
return specialtiesToMatch.some(
|
|
7086
|
+
(spec) => practitionerSpecs.includes(spec)
|
|
7087
|
+
);
|
|
7024
7088
|
});
|
|
7025
|
-
console.log(
|
|
7089
|
+
console.log(
|
|
7090
|
+
`[PRACTITIONER_SERVICE] Applied specialties filter, results: ${filteredPractitioners.length}`
|
|
7091
|
+
);
|
|
7026
7092
|
}
|
|
7027
7093
|
if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
|
|
7028
7094
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7029
7095
|
var _a;
|
|
7030
7096
|
const rating = ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
|
|
7031
|
-
if (filters.minRating !== void 0 && rating < filters.minRating)
|
|
7032
|
-
|
|
7097
|
+
if (filters.minRating !== void 0 && rating < filters.minRating)
|
|
7098
|
+
return false;
|
|
7099
|
+
if (filters.maxRating !== void 0 && rating > filters.maxRating)
|
|
7100
|
+
return false;
|
|
7033
7101
|
return true;
|
|
7034
7102
|
});
|
|
7035
|
-
console.log(
|
|
7103
|
+
console.log(
|
|
7104
|
+
`[PRACTITIONER_SERVICE] Applied rating filter, results: ${filteredPractitioners.length}`
|
|
7105
|
+
);
|
|
7036
7106
|
}
|
|
7037
7107
|
if (filters.procedureFamily) {
|
|
7038
7108
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7039
7109
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7040
|
-
return proceduresInfo.some(
|
|
7110
|
+
return proceduresInfo.some(
|
|
7111
|
+
(proc) => proc.family === filters.procedureFamily
|
|
7112
|
+
);
|
|
7041
7113
|
});
|
|
7042
|
-
console.log(
|
|
7114
|
+
console.log(
|
|
7115
|
+
`[PRACTITIONER_SERVICE] Applied procedure family filter, results: ${filteredPractitioners.length}`
|
|
7116
|
+
);
|
|
7043
7117
|
}
|
|
7044
7118
|
if (filters.procedureCategory) {
|
|
7045
7119
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7046
7120
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7047
|
-
return proceduresInfo.some(
|
|
7121
|
+
return proceduresInfo.some(
|
|
7122
|
+
(proc) => proc.categoryName === filters.procedureCategory
|
|
7123
|
+
);
|
|
7048
7124
|
});
|
|
7049
|
-
console.log(
|
|
7125
|
+
console.log(
|
|
7126
|
+
`[PRACTITIONER_SERVICE] Applied procedure category filter, results: ${filteredPractitioners.length}`
|
|
7127
|
+
);
|
|
7050
7128
|
}
|
|
7051
7129
|
if (filters.procedureSubcategory) {
|
|
7052
7130
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7053
7131
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7054
|
-
return proceduresInfo.some(
|
|
7132
|
+
return proceduresInfo.some(
|
|
7133
|
+
(proc) => proc.subcategoryName === filters.procedureSubcategory
|
|
7134
|
+
);
|
|
7055
7135
|
});
|
|
7056
|
-
console.log(
|
|
7136
|
+
console.log(
|
|
7137
|
+
`[PRACTITIONER_SERVICE] Applied procedure subcategory filter, results: ${filteredPractitioners.length}`
|
|
7138
|
+
);
|
|
7057
7139
|
}
|
|
7058
7140
|
if (filters.procedureTechnology) {
|
|
7059
7141
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7060
7142
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7061
|
-
return proceduresInfo.some(
|
|
7143
|
+
return proceduresInfo.some(
|
|
7144
|
+
(proc) => proc.technologyName === filters.procedureTechnology
|
|
7145
|
+
);
|
|
7062
7146
|
});
|
|
7063
|
-
console.log(
|
|
7147
|
+
console.log(
|
|
7148
|
+
`[PRACTITIONER_SERVICE] Applied procedure technology filter, results: ${filteredPractitioners.length}`
|
|
7149
|
+
);
|
|
7064
7150
|
}
|
|
7065
7151
|
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
7066
7152
|
const location = filters.location;
|
|
@@ -7076,7 +7162,9 @@ var PractitionerService = class extends BaseService {
|
|
|
7076
7162
|
return distanceInKm <= radiusInKm;
|
|
7077
7163
|
});
|
|
7078
7164
|
});
|
|
7079
|
-
console.log(
|
|
7165
|
+
console.log(
|
|
7166
|
+
`[PRACTITIONER_SERVICE] Applied geo filter, results: ${filteredPractitioners.length}`
|
|
7167
|
+
);
|
|
7080
7168
|
}
|
|
7081
7169
|
return filteredPractitioners;
|
|
7082
7170
|
}
|
|
@@ -7141,6 +7229,15 @@ var PractitionerService = class extends BaseService {
|
|
|
7141
7229
|
price: 0,
|
|
7142
7230
|
currency: "EUR" /* EUR */,
|
|
7143
7231
|
pricingMeasure: "per_session" /* PER_SESSION */,
|
|
7232
|
+
productsMetadata: [
|
|
7233
|
+
{
|
|
7234
|
+
productId: "free-consultation-product",
|
|
7235
|
+
price: 0,
|
|
7236
|
+
currency: "EUR" /* EUR */,
|
|
7237
|
+
pricingMeasure: "per_session" /* PER_SESSION */,
|
|
7238
|
+
isDefault: true
|
|
7239
|
+
}
|
|
7240
|
+
],
|
|
7144
7241
|
duration: 30,
|
|
7145
7242
|
// 30 minutes consultation
|
|
7146
7243
|
practitionerId,
|
|
@@ -14732,67 +14829,141 @@ var PatientRequirementsService = class extends BaseService {
|
|
|
14732
14829
|
var import_firestore46 = require("firebase/firestore");
|
|
14733
14830
|
|
|
14734
14831
|
// src/validations/procedure.schema.ts
|
|
14832
|
+
var import_zod25 = require("zod");
|
|
14833
|
+
|
|
14834
|
+
// src/validations/procedure-product.schema.ts
|
|
14735
14835
|
var import_zod24 = require("zod");
|
|
14736
|
-
var
|
|
14737
|
-
|
|
14738
|
-
|
|
14739
|
-
|
|
14740
|
-
|
|
14741
|
-
|
|
14742
|
-
|
|
14743
|
-
|
|
14744
|
-
|
|
14745
|
-
|
|
14746
|
-
price: import_zod24.z.number().min(0),
|
|
14836
|
+
var procedureProductDataSchema = import_zod24.z.object({
|
|
14837
|
+
/**
|
|
14838
|
+
* The ID of the product. Must be a non-empty string.
|
|
14839
|
+
* @validation
|
|
14840
|
+
*/
|
|
14841
|
+
productId: import_zod24.z.string().min(1, "Product ID is required"),
|
|
14842
|
+
/**
|
|
14843
|
+
* The price of the product. Must be a non-negative number.
|
|
14844
|
+
* @validation
|
|
14845
|
+
*/
|
|
14846
|
+
price: import_zod24.z.number().min(0, "Price must be a non-negative number"),
|
|
14847
|
+
/**
|
|
14848
|
+
* The currency for the price. Must be one of the values from the Currency enum.
|
|
14849
|
+
* @validation
|
|
14850
|
+
*/
|
|
14747
14851
|
currency: import_zod24.z.nativeEnum(Currency),
|
|
14852
|
+
/**
|
|
14853
|
+
* The pricing measure for the product. Must be one of the values from the PricingMeasure enum.
|
|
14854
|
+
* @validation
|
|
14855
|
+
*/
|
|
14748
14856
|
pricingMeasure: import_zod24.z.nativeEnum(PricingMeasure),
|
|
14749
|
-
|
|
14857
|
+
/**
|
|
14858
|
+
* Whether this is the default product for the procedure.
|
|
14859
|
+
* @validation
|
|
14860
|
+
*/
|
|
14861
|
+
isDefault: import_zod24.z.boolean().optional()
|
|
14862
|
+
});
|
|
14863
|
+
|
|
14864
|
+
// src/validations/procedure.schema.ts
|
|
14865
|
+
var storedProcedureProductSchema = import_zod25.z.object({
|
|
14866
|
+
/**
|
|
14867
|
+
* The full product object used in the procedure.
|
|
14868
|
+
*/
|
|
14869
|
+
product: import_zod25.z.any(),
|
|
14870
|
+
// We'll validate the full product object separately
|
|
14871
|
+
/**
|
|
14872
|
+
* The price of the procedure when using this specific product.
|
|
14873
|
+
*/
|
|
14874
|
+
price: import_zod25.z.number().min(0, "Price must be a non-negative number"),
|
|
14875
|
+
/**
|
|
14876
|
+
* The currency for the price of this product.
|
|
14877
|
+
*/
|
|
14878
|
+
currency: import_zod25.z.nativeEnum(Currency),
|
|
14879
|
+
/**
|
|
14880
|
+
* How the price is measured (e.g., per ml, per zone).
|
|
14881
|
+
*/
|
|
14882
|
+
pricingMeasure: import_zod25.z.nativeEnum(PricingMeasure),
|
|
14883
|
+
/**
|
|
14884
|
+
* Whether this is the default product for the procedure.
|
|
14885
|
+
*/
|
|
14886
|
+
isDefault: import_zod25.z.boolean().optional()
|
|
14887
|
+
});
|
|
14888
|
+
var createProcedureSchema = import_zod25.z.object({
|
|
14889
|
+
name: import_zod25.z.string().min(1).max(200),
|
|
14890
|
+
// Optional: service will derive from name if not provided by client
|
|
14891
|
+
nameLower: import_zod25.z.string().min(1).max(200).optional(),
|
|
14892
|
+
description: import_zod25.z.string().min(1).max(2e3),
|
|
14893
|
+
family: import_zod25.z.nativeEnum(ProcedureFamily),
|
|
14894
|
+
categoryId: import_zod25.z.string().min(1),
|
|
14895
|
+
subcategoryId: import_zod25.z.string().min(1),
|
|
14896
|
+
technologyId: import_zod25.z.string().min(1),
|
|
14897
|
+
productId: import_zod25.z.string().min(1),
|
|
14898
|
+
price: import_zod25.z.number().min(0),
|
|
14899
|
+
currency: import_zod25.z.nativeEnum(Currency),
|
|
14900
|
+
pricingMeasure: import_zod25.z.nativeEnum(PricingMeasure),
|
|
14901
|
+
productsMetadata: import_zod25.z.array(procedureProductDataSchema).min(1),
|
|
14902
|
+
duration: import_zod25.z.number().min(1).max(480),
|
|
14750
14903
|
// Max 8 hours
|
|
14751
|
-
practitionerId:
|
|
14752
|
-
clinicBranchId:
|
|
14753
|
-
photos:
|
|
14904
|
+
practitionerId: import_zod25.z.string().min(1),
|
|
14905
|
+
clinicBranchId: import_zod25.z.string().min(1),
|
|
14906
|
+
photos: import_zod25.z.array(mediaResourceSchema).optional()
|
|
14754
14907
|
});
|
|
14755
|
-
var updateProcedureSchema =
|
|
14756
|
-
name:
|
|
14757
|
-
nameLower:
|
|
14758
|
-
description:
|
|
14759
|
-
price:
|
|
14760
|
-
currency:
|
|
14761
|
-
pricingMeasure:
|
|
14762
|
-
|
|
14763
|
-
|
|
14764
|
-
|
|
14765
|
-
|
|
14766
|
-
|
|
14767
|
-
|
|
14768
|
-
|
|
14769
|
-
|
|
14770
|
-
|
|
14908
|
+
var updateProcedureSchema = import_zod25.z.object({
|
|
14909
|
+
name: import_zod25.z.string().min(3).max(100).optional(),
|
|
14910
|
+
nameLower: import_zod25.z.string().min(1).max(200).optional(),
|
|
14911
|
+
description: import_zod25.z.string().min(3).max(1e3).optional(),
|
|
14912
|
+
price: import_zod25.z.number().min(0).optional(),
|
|
14913
|
+
currency: import_zod25.z.nativeEnum(Currency).optional(),
|
|
14914
|
+
pricingMeasure: import_zod25.z.nativeEnum(PricingMeasure).optional(),
|
|
14915
|
+
productsMetadata: import_zod25.z.array(procedureProductDataSchema).min(1).optional(),
|
|
14916
|
+
duration: import_zod25.z.number().min(0).optional(),
|
|
14917
|
+
isActive: import_zod25.z.boolean().optional(),
|
|
14918
|
+
practitionerId: import_zod25.z.string().optional(),
|
|
14919
|
+
categoryId: import_zod25.z.string().optional(),
|
|
14920
|
+
subcategoryId: import_zod25.z.string().optional(),
|
|
14921
|
+
technologyId: import_zod25.z.string().optional(),
|
|
14922
|
+
productId: import_zod25.z.string().optional(),
|
|
14923
|
+
clinicBranchId: import_zod25.z.string().optional(),
|
|
14924
|
+
photos: import_zod25.z.array(mediaResourceSchema).optional()
|
|
14771
14925
|
});
|
|
14772
|
-
var procedureSchema =
|
|
14773
|
-
id:
|
|
14774
|
-
|
|
14775
|
-
|
|
14926
|
+
var procedureSchema = import_zod25.z.object({
|
|
14927
|
+
id: import_zod25.z.string().min(1),
|
|
14928
|
+
name: import_zod25.z.string().min(1).max(200),
|
|
14929
|
+
nameLower: import_zod25.z.string().min(1).max(200),
|
|
14930
|
+
description: import_zod25.z.string().min(1).max(2e3),
|
|
14931
|
+
family: import_zod25.z.nativeEnum(ProcedureFamily),
|
|
14932
|
+
category: import_zod25.z.any(),
|
|
14776
14933
|
// We'll validate the full category object separately
|
|
14777
|
-
subcategory:
|
|
14934
|
+
subcategory: import_zod25.z.any(),
|
|
14778
14935
|
// We'll validate the full subcategory object separately
|
|
14779
|
-
technology:
|
|
14936
|
+
technology: import_zod25.z.any(),
|
|
14780
14937
|
// We'll validate the full technology object separately
|
|
14781
|
-
product:
|
|
14938
|
+
product: import_zod25.z.any(),
|
|
14782
14939
|
// We'll validate the full product object separately
|
|
14783
|
-
|
|
14940
|
+
productsMetadata: import_zod25.z.array(storedProcedureProductSchema).min(1),
|
|
14941
|
+
// Use stored format schema
|
|
14942
|
+
price: import_zod25.z.number().min(0),
|
|
14943
|
+
currency: import_zod25.z.nativeEnum(Currency),
|
|
14944
|
+
pricingMeasure: import_zod25.z.nativeEnum(PricingMeasure),
|
|
14945
|
+
duration: import_zod25.z.number().min(1).max(480),
|
|
14946
|
+
practitionerId: import_zod25.z.string().min(1),
|
|
14947
|
+
clinicBranchId: import_zod25.z.string().min(1),
|
|
14948
|
+
photos: import_zod25.z.array(import_zod25.z.string()).optional(),
|
|
14949
|
+
// Stored as URL strings
|
|
14950
|
+
blockingConditions: import_zod25.z.array(import_zod25.z.any()),
|
|
14784
14951
|
// We'll validate blocking conditions separately
|
|
14785
|
-
contraindications:
|
|
14952
|
+
contraindications: import_zod25.z.array(import_zod25.z.any()),
|
|
14786
14953
|
// We'll validate contraindications separately
|
|
14787
|
-
|
|
14954
|
+
contraindicationIds: import_zod25.z.array(import_zod25.z.string()),
|
|
14955
|
+
// Array of IDs for efficient querying
|
|
14956
|
+
treatmentBenefits: import_zod25.z.array(import_zod25.z.any()),
|
|
14788
14957
|
// We'll validate treatment benefits separately
|
|
14789
|
-
|
|
14958
|
+
treatmentBenefitIds: import_zod25.z.array(import_zod25.z.string()),
|
|
14959
|
+
// Array of IDs for efficient querying
|
|
14960
|
+
preRequirements: import_zod25.z.array(import_zod25.z.any()),
|
|
14790
14961
|
// We'll validate requirements separately
|
|
14791
|
-
postRequirements:
|
|
14962
|
+
postRequirements: import_zod25.z.array(import_zod25.z.any()),
|
|
14792
14963
|
// We'll validate requirements separately
|
|
14793
|
-
certificationRequirement:
|
|
14964
|
+
certificationRequirement: import_zod25.z.any(),
|
|
14794
14965
|
// We'll validate certification requirement separately
|
|
14795
|
-
documentationTemplates:
|
|
14966
|
+
documentationTemplates: import_zod25.z.array(import_zod25.z.any()),
|
|
14796
14967
|
// We'll validate documentation templates separately
|
|
14797
14968
|
clinicInfo: clinicInfoSchema,
|
|
14798
14969
|
// Clinic info validation
|
|
@@ -14800,9 +14971,9 @@ var procedureSchema = createProcedureSchema.extend({
|
|
|
14800
14971
|
// Doctor info validation
|
|
14801
14972
|
reviewInfo: procedureReviewInfoSchema,
|
|
14802
14973
|
// Procedure review info validation
|
|
14803
|
-
isActive:
|
|
14804
|
-
createdAt:
|
|
14805
|
-
updatedAt:
|
|
14974
|
+
isActive: import_zod25.z.boolean(),
|
|
14975
|
+
createdAt: import_zod25.z.date(),
|
|
14976
|
+
updatedAt: import_zod25.z.date()
|
|
14806
14977
|
});
|
|
14807
14978
|
|
|
14808
14979
|
// src/services/procedure/procedure.service.ts
|
|
@@ -14864,6 +15035,34 @@ var ProcedureService = class extends BaseService {
|
|
|
14864
15035
|
}
|
|
14865
15036
|
return result;
|
|
14866
15037
|
}
|
|
15038
|
+
/**
|
|
15039
|
+
* Transforms validated procedure product data (with productId) to ProcedureProduct objects (with full product)
|
|
15040
|
+
* @param productsMetadata Array of validated procedure product data
|
|
15041
|
+
* @param technologyId Technology ID to fetch products from
|
|
15042
|
+
* @returns Array of ProcedureProduct objects with full product information
|
|
15043
|
+
*/
|
|
15044
|
+
async transformProductsMetadata(productsMetadata, technologyId) {
|
|
15045
|
+
const transformedProducts = [];
|
|
15046
|
+
for (const productData of productsMetadata) {
|
|
15047
|
+
const product = await this.productService.getById(
|
|
15048
|
+
technologyId,
|
|
15049
|
+
productData.productId
|
|
15050
|
+
);
|
|
15051
|
+
if (!product) {
|
|
15052
|
+
throw new Error(
|
|
15053
|
+
`Product with ID ${productData.productId} not found for technology ${technologyId}`
|
|
15054
|
+
);
|
|
15055
|
+
}
|
|
15056
|
+
transformedProducts.push({
|
|
15057
|
+
product,
|
|
15058
|
+
price: productData.price,
|
|
15059
|
+
currency: productData.currency,
|
|
15060
|
+
pricingMeasure: productData.pricingMeasure,
|
|
15061
|
+
isDefault: productData.isDefault
|
|
15062
|
+
});
|
|
15063
|
+
}
|
|
15064
|
+
return transformedProducts;
|
|
15065
|
+
}
|
|
14867
15066
|
/**
|
|
14868
15067
|
* Creates a new procedure
|
|
14869
15068
|
* @param data - The data for creating a new procedure
|
|
@@ -14920,6 +15119,10 @@ var ProcedureService = class extends BaseService {
|
|
|
14920
15119
|
"procedure-photos"
|
|
14921
15120
|
);
|
|
14922
15121
|
}
|
|
15122
|
+
const transformedProductsMetadata = await this.transformProductsMetadata(
|
|
15123
|
+
validatedData.productsMetadata,
|
|
15124
|
+
validatedData.technologyId
|
|
15125
|
+
);
|
|
14923
15126
|
const clinicInfo = {
|
|
14924
15127
|
id: clinicSnapshot.id,
|
|
14925
15128
|
name: clinic.name,
|
|
@@ -14948,6 +15151,7 @@ var ProcedureService = class extends BaseService {
|
|
|
14948
15151
|
subcategory,
|
|
14949
15152
|
technology,
|
|
14950
15153
|
product,
|
|
15154
|
+
productsMetadata: transformedProductsMetadata,
|
|
14951
15155
|
blockingConditions: technology.blockingConditions,
|
|
14952
15156
|
contraindications: technology.contraindications || [],
|
|
14953
15157
|
contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
|
|
@@ -15030,6 +15234,10 @@ var ProcedureService = class extends BaseService {
|
|
|
15030
15234
|
"procedure-photos-batch"
|
|
15031
15235
|
);
|
|
15032
15236
|
}
|
|
15237
|
+
const transformedProductsMetadata = await this.transformProductsMetadata(
|
|
15238
|
+
validatedData.productsMetadata,
|
|
15239
|
+
validatedData.technologyId
|
|
15240
|
+
);
|
|
15033
15241
|
const practitionersMap = /* @__PURE__ */ new Map();
|
|
15034
15242
|
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
15035
15243
|
const chunk = practitionerIds.slice(i, i + 30);
|
|
@@ -15085,6 +15293,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15085
15293
|
subcategory,
|
|
15086
15294
|
technology,
|
|
15087
15295
|
product,
|
|
15296
|
+
productsMetadata: transformedProductsMetadata,
|
|
15088
15297
|
blockingConditions: technology.blockingConditions,
|
|
15089
15298
|
contraindications: technology.contraindications || [],
|
|
15090
15299
|
contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
|
|
@@ -15192,7 +15401,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15192
15401
|
* @returns The updated procedure
|
|
15193
15402
|
*/
|
|
15194
15403
|
async updateProcedure(id, data) {
|
|
15195
|
-
var _a, _b, _c;
|
|
15404
|
+
var _a, _b, _c, _d;
|
|
15196
15405
|
const validatedData = updateProcedureSchema.parse(data);
|
|
15197
15406
|
const procedureRef = (0, import_firestore46.doc)(this.db, PROCEDURES_COLLECTION, id);
|
|
15198
15407
|
const procedureSnapshot = await (0, import_firestore46.getDoc)(procedureRef);
|
|
@@ -15200,7 +15409,21 @@ var ProcedureService = class extends BaseService {
|
|
|
15200
15409
|
throw new Error(`Procedure with ID ${id} not found`);
|
|
15201
15410
|
}
|
|
15202
15411
|
const existingProcedure = procedureSnapshot.data();
|
|
15203
|
-
let updatedProcedureData = {
|
|
15412
|
+
let updatedProcedureData = {};
|
|
15413
|
+
if (validatedData.name !== void 0)
|
|
15414
|
+
updatedProcedureData.name = validatedData.name;
|
|
15415
|
+
if (validatedData.description !== void 0)
|
|
15416
|
+
updatedProcedureData.description = validatedData.description;
|
|
15417
|
+
if (validatedData.price !== void 0)
|
|
15418
|
+
updatedProcedureData.price = validatedData.price;
|
|
15419
|
+
if (validatedData.currency !== void 0)
|
|
15420
|
+
updatedProcedureData.currency = validatedData.currency;
|
|
15421
|
+
if (validatedData.pricingMeasure !== void 0)
|
|
15422
|
+
updatedProcedureData.pricingMeasure = validatedData.pricingMeasure;
|
|
15423
|
+
if (validatedData.duration !== void 0)
|
|
15424
|
+
updatedProcedureData.duration = validatedData.duration;
|
|
15425
|
+
if (validatedData.isActive !== void 0)
|
|
15426
|
+
updatedProcedureData.isActive = validatedData.isActive;
|
|
15204
15427
|
let practitionerChanged = false;
|
|
15205
15428
|
let clinicChanged = false;
|
|
15206
15429
|
const oldPractitionerId = existingProcedure.practitionerId;
|
|
@@ -15214,6 +15437,18 @@ var ProcedureService = class extends BaseService {
|
|
|
15214
15437
|
"procedure-photos"
|
|
15215
15438
|
);
|
|
15216
15439
|
}
|
|
15440
|
+
if (validatedData.productsMetadata !== void 0) {
|
|
15441
|
+
const technologyId = (_a = validatedData.technologyId) != null ? _a : existingProcedure.technology.id;
|
|
15442
|
+
if (!technologyId) {
|
|
15443
|
+
throw new Error(
|
|
15444
|
+
"Technology ID is required for updating products metadata"
|
|
15445
|
+
);
|
|
15446
|
+
}
|
|
15447
|
+
updatedProcedureData.productsMetadata = await this.transformProductsMetadata(
|
|
15448
|
+
validatedData.productsMetadata,
|
|
15449
|
+
technologyId
|
|
15450
|
+
);
|
|
15451
|
+
}
|
|
15217
15452
|
if (validatedData.practitionerId && validatedData.practitionerId !== oldPractitionerId) {
|
|
15218
15453
|
practitionerChanged = true;
|
|
15219
15454
|
const newPractitionerRef = (0, import_firestore46.doc)(
|
|
@@ -15233,7 +15468,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15233
15468
|
description: newPractitioner.basicInfo.bio || "",
|
|
15234
15469
|
photo: typeof newPractitioner.basicInfo.profileImageUrl === "string" ? newPractitioner.basicInfo.profileImageUrl : "",
|
|
15235
15470
|
// Default to empty string if not a processed URL
|
|
15236
|
-
rating: ((
|
|
15471
|
+
rating: ((_b = newPractitioner.reviewInfo) == null ? void 0 : _b.averageRating) || 0,
|
|
15237
15472
|
services: newPractitioner.procedures || []
|
|
15238
15473
|
};
|
|
15239
15474
|
}
|
|
@@ -15296,9 +15531,9 @@ var ProcedureService = class extends BaseService {
|
|
|
15296
15531
|
finalTechnologyId = technology.id;
|
|
15297
15532
|
updatedProcedureData.blockingConditions = technology.blockingConditions;
|
|
15298
15533
|
updatedProcedureData.contraindications = technology.contraindications || [];
|
|
15299
|
-
updatedProcedureData.contraindicationIds = ((
|
|
15534
|
+
updatedProcedureData.contraindicationIds = ((_c = technology.contraindications) == null ? void 0 : _c.map((c) => c.id)) || [];
|
|
15300
15535
|
updatedProcedureData.treatmentBenefits = technology.benefits;
|
|
15301
|
-
updatedProcedureData.treatmentBenefitIds = ((
|
|
15536
|
+
updatedProcedureData.treatmentBenefitIds = ((_d = technology.benefits) == null ? void 0 : _d.map((b) => b.id)) || [];
|
|
15302
15537
|
updatedProcedureData.preRequirements = technology.requirements.pre;
|
|
15303
15538
|
updatedProcedureData.postRequirements = technology.requirements.post;
|
|
15304
15539
|
updatedProcedureData.certificationRequirement = technology.certificationRequirement;
|
|
@@ -15895,6 +16130,10 @@ var ProcedureService = class extends BaseService {
|
|
|
15895
16130
|
"procedure-photos"
|
|
15896
16131
|
);
|
|
15897
16132
|
}
|
|
16133
|
+
const transformedProductsMetadata = await this.transformProductsMetadata(
|
|
16134
|
+
data.productsMetadata,
|
|
16135
|
+
data.technologyId
|
|
16136
|
+
);
|
|
15898
16137
|
const clinicInfo = {
|
|
15899
16138
|
id: clinicSnapshot.id,
|
|
15900
16139
|
name: clinic.name,
|
|
@@ -15935,6 +16174,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15935
16174
|
technology,
|
|
15936
16175
|
product: consultationProduct,
|
|
15937
16176
|
// Use placeholder product
|
|
16177
|
+
productsMetadata: transformedProductsMetadata,
|
|
15938
16178
|
blockingConditions: technology.blockingConditions,
|
|
15939
16179
|
contraindications: technology.contraindications || [],
|
|
15940
16180
|
contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
|
|
@@ -15994,7 +16234,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15994
16234
|
|
|
15995
16235
|
// src/services/reviews/reviews.service.ts
|
|
15996
16236
|
var import_firestore47 = require("firebase/firestore");
|
|
15997
|
-
var
|
|
16237
|
+
var import_zod26 = require("zod");
|
|
15998
16238
|
var ReviewService = class extends BaseService {
|
|
15999
16239
|
constructor(db, auth, app) {
|
|
16000
16240
|
super(db, auth, app);
|
|
@@ -16081,7 +16321,7 @@ var ReviewService = class extends BaseService {
|
|
|
16081
16321
|
});
|
|
16082
16322
|
return review;
|
|
16083
16323
|
} catch (error) {
|
|
16084
|
-
if (error instanceof
|
|
16324
|
+
if (error instanceof import_zod26.z.ZodError) {
|
|
16085
16325
|
throw new Error(`Invalid review data: ${error.message}`);
|
|
16086
16326
|
}
|
|
16087
16327
|
throw error;
|