@blackcode_sa/metaestetics-api 1.12.0 → 1.12.2
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 +60 -6
- package/dist/index.d.ts +60 -6
- package/dist/index.js +394 -293
- package/dist/index.mjs +394 -293
- 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 +346 -442
- 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/package.json
CHANGED
|
@@ -22,6 +22,7 @@ export type {
|
|
|
22
22
|
Technology,
|
|
23
23
|
TechnologyDocumentationTemplate,
|
|
24
24
|
} from "../types/technology.types";
|
|
25
|
+
export type { ProcedureProduct } from "../types/procedure-product.types";
|
|
25
26
|
|
|
26
27
|
// Static pricing enums
|
|
27
28
|
export { PricingMeasure, Currency } from "../types/static/pricing.types";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the ProcedureProduct type, which represents a product associated with a procedure,
|
|
3
|
+
* including its pricing information. This is used to allow procedures to have multiple products with different prices.
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
import { Product } from "./product.types";
|
|
7
|
+
import { Currency, PricingMeasure } from "./static/pricing.types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a product associated with a procedure, including its specific pricing information.
|
|
11
|
+
*/
|
|
12
|
+
export interface ProcedureProduct {
|
|
13
|
+
/**
|
|
14
|
+
* The product used in the procedure.
|
|
15
|
+
* @see {@link Product}
|
|
16
|
+
*/
|
|
17
|
+
product: Product;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The price of the procedure when using this specific product.
|
|
21
|
+
*/
|
|
22
|
+
price: number;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The currency for the price of this product.
|
|
26
|
+
* @see {@link Currency}
|
|
27
|
+
*/
|
|
28
|
+
currency: Currency;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* How the price is measured (e.g., per ml, per zone).
|
|
32
|
+
* @see {@link PricingMeasure}
|
|
33
|
+
*/
|
|
34
|
+
pricingMeasure: PricingMeasure;
|
|
35
|
+
|
|
36
|
+
// Default product
|
|
37
|
+
isDefault?: boolean;
|
|
38
|
+
}
|
|
@@ -193,7 +193,8 @@ export class PractitionerService extends BaseService {
|
|
|
193
193
|
};
|
|
194
194
|
|
|
195
195
|
// Create practitioner object
|
|
196
|
-
const fullNameLower =
|
|
196
|
+
const fullNameLower =
|
|
197
|
+
`${validData.basicInfo.firstName} ${validData.basicInfo.lastName}`.toLowerCase();
|
|
197
198
|
const practitioner: Omit<Practitioner, "createdAt" | "updatedAt"> & {
|
|
198
199
|
createdAt: FieldValue;
|
|
199
200
|
updatedAt: FieldValue;
|
|
@@ -349,7 +350,8 @@ export class PractitionerService extends BaseService {
|
|
|
349
350
|
const proceduresInfo: ProcedureSummaryInfo[] = [];
|
|
350
351
|
|
|
351
352
|
// Add fullNameLower for draft
|
|
352
|
-
const fullNameLowerDraft =
|
|
353
|
+
const fullNameLowerDraft =
|
|
354
|
+
`${validatedData.basicInfo.firstName} ${validatedData.basicInfo.lastName}`.toLowerCase();
|
|
353
355
|
const practitionerData: Omit<Practitioner, "createdAt" | "updatedAt"> & {
|
|
354
356
|
createdAt: ReturnType<typeof serverTimestamp>;
|
|
355
357
|
updatedAt: ReturnType<typeof serverTimestamp>;
|
|
@@ -726,7 +728,9 @@ export class PractitionerService extends BaseService {
|
|
|
726
728
|
const currentPractitioner = practitionerDoc.data() as Practitioner;
|
|
727
729
|
|
|
728
730
|
// Process basicInfo if it's being updated to handle profile photo uploads
|
|
729
|
-
let processedData: UpdatePractitionerData & { fullNameLower?: string } = {
|
|
731
|
+
let processedData: UpdatePractitionerData & { fullNameLower?: string } = {
|
|
732
|
+
...validData,
|
|
733
|
+
};
|
|
730
734
|
if (validData.basicInfo) {
|
|
731
735
|
processedData.basicInfo = await this.processBasicInfo(
|
|
732
736
|
validData.basicInfo as PractitionerBasicInfo & {
|
|
@@ -735,7 +739,8 @@ export class PractitionerService extends BaseService {
|
|
|
735
739
|
practitionerId
|
|
736
740
|
);
|
|
737
741
|
// Always update fullNameLower when basicInfo changes
|
|
738
|
-
processedData.fullNameLower =
|
|
742
|
+
processedData.fullNameLower =
|
|
743
|
+
`${processedData.basicInfo.firstName} ${processedData.basicInfo.lastName}`.toLowerCase();
|
|
739
744
|
}
|
|
740
745
|
|
|
741
746
|
// Prepare update data
|
|
@@ -1043,19 +1048,24 @@ export class PractitionerService extends BaseService {
|
|
|
1043
1048
|
includeDraftPractitioners?: boolean;
|
|
1044
1049
|
}): Promise<{ practitioners: Practitioner[]; lastDoc: any }> {
|
|
1045
1050
|
try {
|
|
1046
|
-
console.log(
|
|
1051
|
+
console.log(
|
|
1052
|
+
"[PRACTITIONER_SERVICE] Starting practitioner filtering with fallback strategies"
|
|
1053
|
+
);
|
|
1047
1054
|
|
|
1048
1055
|
// Geo query debug i validacija
|
|
1049
1056
|
if (filters.location && filters.radiusInKm) {
|
|
1050
|
-
console.log(
|
|
1057
|
+
console.log("[PRACTITIONER_SERVICE] Executing geo query:", {
|
|
1051
1058
|
location: filters.location,
|
|
1052
1059
|
radius: filters.radiusInKm,
|
|
1053
|
-
serviceName:
|
|
1060
|
+
serviceName: "PractitionerService",
|
|
1054
1061
|
});
|
|
1055
|
-
|
|
1062
|
+
|
|
1056
1063
|
// Validacija location podataka
|
|
1057
1064
|
if (!filters.location.latitude || !filters.location.longitude) {
|
|
1058
|
-
console.warn(
|
|
1065
|
+
console.warn(
|
|
1066
|
+
"[PRACTITIONER_SERVICE] Invalid location data:",
|
|
1067
|
+
filters.location
|
|
1068
|
+
);
|
|
1059
1069
|
filters.location = undefined;
|
|
1060
1070
|
filters.radiusInKm = undefined;
|
|
1061
1071
|
}
|
|
@@ -1064,10 +1074,12 @@ export class PractitionerService extends BaseService {
|
|
|
1064
1074
|
// Strategy 1: Try fullNameLower search if nameSearch exists
|
|
1065
1075
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
1066
1076
|
try {
|
|
1067
|
-
console.log(
|
|
1077
|
+
console.log(
|
|
1078
|
+
"[PRACTITIONER_SERVICE] Strategy 1: Trying fullNameLower search"
|
|
1079
|
+
);
|
|
1068
1080
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
1069
1081
|
const constraints: any[] = [];
|
|
1070
|
-
|
|
1082
|
+
|
|
1071
1083
|
if (!filters.includeDraftPractitioners) {
|
|
1072
1084
|
constraints.push(where("status", "==", PractitionerStatus.ACTIVE));
|
|
1073
1085
|
}
|
|
@@ -1087,13 +1099,23 @@ export class PractitionerService extends BaseService {
|
|
|
1087
1099
|
}
|
|
1088
1100
|
constraints.push(limit(filters.pagination || 10));
|
|
1089
1101
|
|
|
1090
|
-
const q = query(
|
|
1102
|
+
const q = query(
|
|
1103
|
+
collection(this.db, PRACTITIONERS_COLLECTION),
|
|
1104
|
+
...constraints
|
|
1105
|
+
);
|
|
1091
1106
|
const querySnapshot = await getDocs(q);
|
|
1092
|
-
const practitioners = querySnapshot.docs.map(
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1107
|
+
const practitioners = querySnapshot.docs.map(
|
|
1108
|
+
(doc) => ({ ...doc.data(), id: doc.id } as Practitioner)
|
|
1109
|
+
);
|
|
1110
|
+
const lastDoc =
|
|
1111
|
+
querySnapshot.docs.length > 0
|
|
1112
|
+
? querySnapshot.docs[querySnapshot.docs.length - 1]
|
|
1113
|
+
: null;
|
|
1114
|
+
|
|
1115
|
+
console.log(
|
|
1116
|
+
`[PRACTITIONER_SERVICE] Strategy 1 success: ${practitioners.length} practitioners`
|
|
1117
|
+
);
|
|
1118
|
+
|
|
1097
1119
|
// Fix Load More - ako je broj rezultata manji od pagination, nema više
|
|
1098
1120
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
1099
1121
|
return { practitioners, lastDoc: null };
|
|
@@ -1106,9 +1128,11 @@ export class PractitionerService extends BaseService {
|
|
|
1106
1128
|
|
|
1107
1129
|
// Strategy 2: Basic query with createdAt ordering (no name search)
|
|
1108
1130
|
try {
|
|
1109
|
-
console.log(
|
|
1131
|
+
console.log(
|
|
1132
|
+
"[PRACTITIONER_SERVICE] Strategy 2: Basic query with createdAt ordering"
|
|
1133
|
+
);
|
|
1110
1134
|
const constraints: any[] = [];
|
|
1111
|
-
|
|
1135
|
+
|
|
1112
1136
|
if (!filters.includeDraftPractitioners) {
|
|
1113
1137
|
constraints.push(where("status", "==", PractitionerStatus.ACTIVE));
|
|
1114
1138
|
}
|
|
@@ -1116,17 +1140,26 @@ export class PractitionerService extends BaseService {
|
|
|
1116
1140
|
|
|
1117
1141
|
// Add other filters that work well with Firestore
|
|
1118
1142
|
if (filters.certifications && filters.certifications.length > 0) {
|
|
1119
|
-
const certificationsToMatch =
|
|
1143
|
+
const certificationsToMatch =
|
|
1144
|
+
filters.certifications as CertificationSpecialty[];
|
|
1120
1145
|
constraints.push(
|
|
1121
|
-
where(
|
|
1146
|
+
where(
|
|
1147
|
+
"certification.specialties",
|
|
1148
|
+
"array-contains-any",
|
|
1149
|
+
certificationsToMatch
|
|
1150
|
+
)
|
|
1122
1151
|
);
|
|
1123
1152
|
}
|
|
1124
1153
|
|
|
1125
1154
|
if (filters.minRating !== undefined) {
|
|
1126
|
-
constraints.push(
|
|
1155
|
+
constraints.push(
|
|
1156
|
+
where("reviewInfo.averageRating", ">=", filters.minRating)
|
|
1157
|
+
);
|
|
1127
1158
|
}
|
|
1128
1159
|
if (filters.maxRating !== undefined) {
|
|
1129
|
-
constraints.push(
|
|
1160
|
+
constraints.push(
|
|
1161
|
+
where("reviewInfo.averageRating", "<=", filters.maxRating)
|
|
1162
|
+
);
|
|
1130
1163
|
}
|
|
1131
1164
|
|
|
1132
1165
|
constraints.push(orderBy("createdAt", "desc"));
|
|
@@ -1148,9 +1181,14 @@ export class PractitionerService extends BaseService {
|
|
|
1148
1181
|
constraints.push(limit(filters.pagination || 10));
|
|
1149
1182
|
}
|
|
1150
1183
|
|
|
1151
|
-
const q = query(
|
|
1184
|
+
const q = query(
|
|
1185
|
+
collection(this.db, PRACTITIONERS_COLLECTION),
|
|
1186
|
+
...constraints
|
|
1187
|
+
);
|
|
1152
1188
|
const querySnapshot = await getDocs(q);
|
|
1153
|
-
let practitioners = querySnapshot.docs.map(
|
|
1189
|
+
let practitioners = querySnapshot.docs.map(
|
|
1190
|
+
(doc) => ({ ...doc.data(), id: doc.id } as Practitioner)
|
|
1191
|
+
);
|
|
1154
1192
|
|
|
1155
1193
|
// Apply geo filter if needed (this is the only in-memory filter we keep)
|
|
1156
1194
|
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
@@ -1167,7 +1205,7 @@ export class PractitionerService extends BaseService {
|
|
|
1167
1205
|
return distanceInKm <= radiusInKm;
|
|
1168
1206
|
});
|
|
1169
1207
|
});
|
|
1170
|
-
|
|
1208
|
+
|
|
1171
1209
|
// Ograniči na pagination broj nakon geo filtera
|
|
1172
1210
|
practitioners = practitioners.slice(0, filters.pagination || 10);
|
|
1173
1211
|
}
|
|
@@ -1175,9 +1213,14 @@ export class PractitionerService extends BaseService {
|
|
|
1175
1213
|
// Apply all remaining client-side filters using centralized function
|
|
1176
1214
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
1177
1215
|
|
|
1178
|
-
const lastDoc =
|
|
1179
|
-
|
|
1180
|
-
|
|
1216
|
+
const lastDoc =
|
|
1217
|
+
querySnapshot.docs.length > 0
|
|
1218
|
+
? querySnapshot.docs[querySnapshot.docs.length - 1]
|
|
1219
|
+
: null;
|
|
1220
|
+
console.log(
|
|
1221
|
+
`[PRACTITIONER_SERVICE] Strategy 2 success: ${practitioners.length} practitioners`
|
|
1222
|
+
);
|
|
1223
|
+
|
|
1181
1224
|
// Fix Load More - ako je broj rezultata manji od pagination, nema više
|
|
1182
1225
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
1183
1226
|
return { practitioners, lastDoc: null };
|
|
@@ -1189,23 +1232,35 @@ export class PractitionerService extends BaseService {
|
|
|
1189
1232
|
|
|
1190
1233
|
// Strategy 3: Minimal query fallback
|
|
1191
1234
|
try {
|
|
1192
|
-
console.log(
|
|
1235
|
+
console.log(
|
|
1236
|
+
"[PRACTITIONER_SERVICE] Strategy 3: Minimal query fallback"
|
|
1237
|
+
);
|
|
1193
1238
|
const constraints: any[] = [
|
|
1194
1239
|
where("isActive", "==", true),
|
|
1195
1240
|
orderBy("createdAt", "desc"),
|
|
1196
|
-
limit(filters.pagination || 10)
|
|
1241
|
+
limit(filters.pagination || 10),
|
|
1197
1242
|
];
|
|
1198
1243
|
|
|
1199
|
-
const q = query(
|
|
1244
|
+
const q = query(
|
|
1245
|
+
collection(this.db, PRACTITIONERS_COLLECTION),
|
|
1246
|
+
...constraints
|
|
1247
|
+
);
|
|
1200
1248
|
const querySnapshot = await getDocs(q);
|
|
1201
|
-
let practitioners = querySnapshot.docs.map(
|
|
1249
|
+
let practitioners = querySnapshot.docs.map(
|
|
1250
|
+
(doc) => ({ ...doc.data(), id: doc.id } as Practitioner)
|
|
1251
|
+
);
|
|
1202
1252
|
|
|
1203
1253
|
// Apply all client-side filters using centralized function
|
|
1204
1254
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
1205
1255
|
|
|
1206
|
-
const lastDoc =
|
|
1207
|
-
|
|
1208
|
-
|
|
1256
|
+
const lastDoc =
|
|
1257
|
+
querySnapshot.docs.length > 0
|
|
1258
|
+
? querySnapshot.docs[querySnapshot.docs.length - 1]
|
|
1259
|
+
: null;
|
|
1260
|
+
console.log(
|
|
1261
|
+
`[PRACTITIONER_SERVICE] Strategy 3 success: ${practitioners.length} practitioners`
|
|
1262
|
+
);
|
|
1263
|
+
|
|
1209
1264
|
// Fix Load More - ako je broj rezultata manji od pagination, nema više
|
|
1210
1265
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
1211
1266
|
return { practitioners, lastDoc: null };
|
|
@@ -1217,41 +1272,56 @@ export class PractitionerService extends BaseService {
|
|
|
1217
1272
|
|
|
1218
1273
|
// Strategy 4: Client-side filtering fallback (kao u procedure/clinic services)
|
|
1219
1274
|
try {
|
|
1220
|
-
console.log(
|
|
1221
|
-
|
|
1275
|
+
console.log(
|
|
1276
|
+
"[PRACTITIONER_SERVICE] Strategy 4: Client-side filtering fallback"
|
|
1277
|
+
);
|
|
1278
|
+
|
|
1222
1279
|
const constraints: any[] = [
|
|
1223
1280
|
where("isActive", "==", true),
|
|
1224
1281
|
where("status", "==", PractitionerStatus.ACTIVE),
|
|
1225
1282
|
orderBy("createdAt", "desc"),
|
|
1226
|
-
limit(filters.pagination || 10)
|
|
1283
|
+
limit(filters.pagination || 10),
|
|
1227
1284
|
];
|
|
1228
1285
|
|
|
1229
|
-
const q = query(
|
|
1286
|
+
const q = query(
|
|
1287
|
+
collection(this.db, PRACTITIONERS_COLLECTION),
|
|
1288
|
+
...constraints
|
|
1289
|
+
);
|
|
1230
1290
|
const querySnapshot = await getDocs(q);
|
|
1231
|
-
let practitioners = querySnapshot.docs.map(
|
|
1291
|
+
let practitioners = querySnapshot.docs.map(
|
|
1292
|
+
(doc) => ({ ...doc.data(), id: doc.id } as Practitioner)
|
|
1293
|
+
);
|
|
1232
1294
|
|
|
1233
1295
|
// Apply all client-side filters using centralized function
|
|
1234
1296
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
1235
1297
|
|
|
1236
|
-
const lastDoc =
|
|
1237
|
-
|
|
1238
|
-
|
|
1298
|
+
const lastDoc =
|
|
1299
|
+
querySnapshot.docs.length > 0
|
|
1300
|
+
? querySnapshot.docs[querySnapshot.docs.length - 1]
|
|
1301
|
+
: null;
|
|
1302
|
+
console.log(
|
|
1303
|
+
`[PRACTITIONER_SERVICE] Strategy 4 success: ${practitioners.length} practitioners`
|
|
1304
|
+
);
|
|
1305
|
+
|
|
1239
1306
|
// Fix Load More - ako je broj rezultata manji od pagination, nema više
|
|
1240
1307
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
1241
1308
|
return { practitioners, lastDoc: null };
|
|
1242
1309
|
}
|
|
1243
1310
|
return { practitioners, lastDoc };
|
|
1244
|
-
|
|
1245
1311
|
} catch (error) {
|
|
1246
1312
|
console.log("[PRACTITIONER_SERVICE] Strategy 4 failed:", error);
|
|
1247
1313
|
}
|
|
1248
1314
|
|
|
1249
1315
|
// All strategies failed
|
|
1250
|
-
console.log(
|
|
1316
|
+
console.log(
|
|
1317
|
+
"[PRACTITIONER_SERVICE] All strategies failed, returning empty result"
|
|
1318
|
+
);
|
|
1251
1319
|
return { practitioners: [], lastDoc: null };
|
|
1252
|
-
|
|
1253
1320
|
} catch (error) {
|
|
1254
|
-
console.error(
|
|
1321
|
+
console.error(
|
|
1322
|
+
"[PRACTITIONER_SERVICE] Error filtering practitioners:",
|
|
1323
|
+
error
|
|
1324
|
+
);
|
|
1255
1325
|
return { practitioners: [], lastDoc: null };
|
|
1256
1326
|
}
|
|
1257
1327
|
}
|
|
@@ -1260,91 +1330,128 @@ export class PractitionerService extends BaseService {
|
|
|
1260
1330
|
* Applies in-memory filters to practitioners array
|
|
1261
1331
|
* Used when Firestore queries fail or for complex filtering
|
|
1262
1332
|
*/
|
|
1263
|
-
private applyInMemoryFilters(
|
|
1333
|
+
private applyInMemoryFilters(
|
|
1334
|
+
practitioners: Practitioner[],
|
|
1335
|
+
filters: any
|
|
1336
|
+
): Practitioner[] {
|
|
1264
1337
|
let filteredPractitioners = [...practitioners]; // Create copy to avoid mutating original
|
|
1265
1338
|
|
|
1266
1339
|
// Name search filter
|
|
1267
1340
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
1268
1341
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
1269
|
-
filteredPractitioners = filteredPractitioners.filter(practitioner => {
|
|
1270
|
-
const firstName = (
|
|
1271
|
-
|
|
1342
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
1343
|
+
const firstName = (
|
|
1344
|
+
practitioner.basicInfo?.firstName || ""
|
|
1345
|
+
).toLowerCase();
|
|
1346
|
+
const lastName = (practitioner.basicInfo?.lastName || "").toLowerCase();
|
|
1272
1347
|
const fullName = `${firstName} ${lastName}`.trim();
|
|
1273
|
-
const fullNameLower = practitioner.fullNameLower ||
|
|
1274
|
-
|
|
1275
|
-
return
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1348
|
+
const fullNameLower = practitioner.fullNameLower || "";
|
|
1349
|
+
|
|
1350
|
+
return (
|
|
1351
|
+
firstName.includes(searchTerm) ||
|
|
1352
|
+
lastName.includes(searchTerm) ||
|
|
1353
|
+
fullName.includes(searchTerm) ||
|
|
1354
|
+
fullNameLower.includes(searchTerm)
|
|
1355
|
+
);
|
|
1279
1356
|
});
|
|
1280
|
-
console.log(
|
|
1357
|
+
console.log(
|
|
1358
|
+
`[PRACTITIONER_SERVICE] Applied name filter, results: ${filteredPractitioners.length}`
|
|
1359
|
+
);
|
|
1281
1360
|
}
|
|
1282
1361
|
|
|
1283
1362
|
// Certifications filtering
|
|
1284
1363
|
if (filters.certifications && filters.certifications.length > 0) {
|
|
1285
1364
|
const certificationsToMatch = filters.certifications;
|
|
1286
|
-
filteredPractitioners = filteredPractitioners.filter(practitioner => {
|
|
1365
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
1287
1366
|
const practitionerCerts = practitioner.certification?.specialties || [];
|
|
1288
|
-
return certificationsToMatch.some((cert: any) =>
|
|
1367
|
+
return certificationsToMatch.some((cert: any) =>
|
|
1368
|
+
practitionerCerts.includes(cert as CertificationSpecialty)
|
|
1369
|
+
);
|
|
1289
1370
|
});
|
|
1290
|
-
console.log(
|
|
1371
|
+
console.log(
|
|
1372
|
+
`[PRACTITIONER_SERVICE] Applied certifications filter, results: ${filteredPractitioners.length}`
|
|
1373
|
+
);
|
|
1291
1374
|
}
|
|
1292
1375
|
|
|
1293
|
-
// Specialties filtering
|
|
1376
|
+
// Specialties filtering
|
|
1294
1377
|
if (filters.specialties && filters.specialties.length > 0) {
|
|
1295
1378
|
const specialtiesToMatch = filters.specialties;
|
|
1296
|
-
filteredPractitioners = filteredPractitioners.filter(practitioner => {
|
|
1379
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
1297
1380
|
const practitionerSpecs = practitioner.certification?.specialties || [];
|
|
1298
|
-
return specialtiesToMatch.some((spec: any) =>
|
|
1381
|
+
return specialtiesToMatch.some((spec: any) =>
|
|
1382
|
+
practitionerSpecs.includes(spec)
|
|
1383
|
+
);
|
|
1299
1384
|
});
|
|
1300
|
-
console.log(
|
|
1385
|
+
console.log(
|
|
1386
|
+
`[PRACTITIONER_SERVICE] Applied specialties filter, results: ${filteredPractitioners.length}`
|
|
1387
|
+
);
|
|
1301
1388
|
}
|
|
1302
1389
|
|
|
1303
1390
|
// Rating filtering
|
|
1304
1391
|
if (filters.minRating !== undefined || filters.maxRating !== undefined) {
|
|
1305
|
-
filteredPractitioners = filteredPractitioners.filter(practitioner => {
|
|
1392
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
1306
1393
|
const rating = practitioner.reviewInfo?.averageRating || 0;
|
|
1307
|
-
if (filters.minRating !== undefined && rating < filters.minRating)
|
|
1308
|
-
|
|
1394
|
+
if (filters.minRating !== undefined && rating < filters.minRating)
|
|
1395
|
+
return false;
|
|
1396
|
+
if (filters.maxRating !== undefined && rating > filters.maxRating)
|
|
1397
|
+
return false;
|
|
1309
1398
|
return true;
|
|
1310
1399
|
});
|
|
1311
|
-
console.log(
|
|
1400
|
+
console.log(
|
|
1401
|
+
`[PRACTITIONER_SERVICE] Applied rating filter, results: ${filteredPractitioners.length}`
|
|
1402
|
+
);
|
|
1312
1403
|
}
|
|
1313
1404
|
|
|
1314
1405
|
// Procedure family filtering
|
|
1315
1406
|
if (filters.procedureFamily) {
|
|
1316
|
-
filteredPractitioners = filteredPractitioners.filter(practitioner => {
|
|
1407
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
1317
1408
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
1318
|
-
return proceduresInfo.some(
|
|
1409
|
+
return proceduresInfo.some(
|
|
1410
|
+
(proc) => proc.family === filters.procedureFamily
|
|
1411
|
+
);
|
|
1319
1412
|
});
|
|
1320
|
-
console.log(
|
|
1413
|
+
console.log(
|
|
1414
|
+
`[PRACTITIONER_SERVICE] Applied procedure family filter, results: ${filteredPractitioners.length}`
|
|
1415
|
+
);
|
|
1321
1416
|
}
|
|
1322
1417
|
|
|
1323
1418
|
// Procedure category filtering
|
|
1324
1419
|
if (filters.procedureCategory) {
|
|
1325
|
-
filteredPractitioners = filteredPractitioners.filter(practitioner => {
|
|
1420
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
1326
1421
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
1327
|
-
return proceduresInfo.some(
|
|
1422
|
+
return proceduresInfo.some(
|
|
1423
|
+
(proc) => proc.categoryName === filters.procedureCategory
|
|
1424
|
+
);
|
|
1328
1425
|
});
|
|
1329
|
-
console.log(
|
|
1426
|
+
console.log(
|
|
1427
|
+
`[PRACTITIONER_SERVICE] Applied procedure category filter, results: ${filteredPractitioners.length}`
|
|
1428
|
+
);
|
|
1330
1429
|
}
|
|
1331
1430
|
|
|
1332
1431
|
// Procedure subcategory filtering
|
|
1333
1432
|
if (filters.procedureSubcategory) {
|
|
1334
|
-
filteredPractitioners = filteredPractitioners.filter(practitioner => {
|
|
1433
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
1335
1434
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
1336
|
-
return proceduresInfo.some(
|
|
1435
|
+
return proceduresInfo.some(
|
|
1436
|
+
(proc) => proc.subcategoryName === filters.procedureSubcategory
|
|
1437
|
+
);
|
|
1337
1438
|
});
|
|
1338
|
-
console.log(
|
|
1439
|
+
console.log(
|
|
1440
|
+
`[PRACTITIONER_SERVICE] Applied procedure subcategory filter, results: ${filteredPractitioners.length}`
|
|
1441
|
+
);
|
|
1339
1442
|
}
|
|
1340
1443
|
|
|
1341
1444
|
// Procedure technology filtering
|
|
1342
1445
|
if (filters.procedureTechnology) {
|
|
1343
|
-
filteredPractitioners = filteredPractitioners.filter(practitioner => {
|
|
1446
|
+
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
1344
1447
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
1345
|
-
return proceduresInfo.some(
|
|
1448
|
+
return proceduresInfo.some(
|
|
1449
|
+
(proc) => proc.technologyName === filters.procedureTechnology
|
|
1450
|
+
);
|
|
1346
1451
|
});
|
|
1347
|
-
console.log(
|
|
1452
|
+
console.log(
|
|
1453
|
+
`[PRACTITIONER_SERVICE] Applied procedure technology filter, results: ${filteredPractitioners.length}`
|
|
1454
|
+
);
|
|
1348
1455
|
}
|
|
1349
1456
|
|
|
1350
1457
|
// Geo-radius filter
|
|
@@ -1362,7 +1469,9 @@ export class PractitionerService extends BaseService {
|
|
|
1362
1469
|
return distanceInKm <= radiusInKm;
|
|
1363
1470
|
});
|
|
1364
1471
|
});
|
|
1365
|
-
console.log(
|
|
1472
|
+
console.log(
|
|
1473
|
+
`[PRACTITIONER_SERVICE] Applied geo filter, results: ${filteredPractitioners.length}`
|
|
1474
|
+
);
|
|
1366
1475
|
}
|
|
1367
1476
|
|
|
1368
1477
|
return filteredPractitioners;
|
|
@@ -1457,6 +1566,15 @@ export class PractitionerService extends BaseService {
|
|
|
1457
1566
|
price: 0,
|
|
1458
1567
|
currency: Currency.EUR,
|
|
1459
1568
|
pricingMeasure: PricingMeasure.PER_SESSION,
|
|
1569
|
+
productsMetadata: [
|
|
1570
|
+
{
|
|
1571
|
+
productId: "free-consultation-product",
|
|
1572
|
+
price: 0,
|
|
1573
|
+
currency: Currency.EUR,
|
|
1574
|
+
pricingMeasure: PricingMeasure.PER_SESSION,
|
|
1575
|
+
isDefault: true,
|
|
1576
|
+
},
|
|
1577
|
+
],
|
|
1460
1578
|
duration: 30, // 30 minutes consultation
|
|
1461
1579
|
practitionerId: practitionerId,
|
|
1462
1580
|
clinicBranchId: clinicId,
|