@blackcode_sa/metaestetics-api 1.12.67 → 1.12.69
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 +34 -1
- package/dist/admin/index.d.ts +34 -1
- package/dist/admin/index.js +104 -0
- package/dist/admin/index.mjs +104 -0
- package/dist/backoffice/index.d.mts +40 -0
- package/dist/backoffice/index.d.ts +40 -0
- package/dist/backoffice/index.js +118 -18
- package/dist/backoffice/index.mjs +118 -20
- package/dist/index.d.mts +61 -2
- package/dist/index.d.ts +61 -2
- package/dist/index.js +249 -37
- package/dist/index.mjs +249 -39
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +140 -0
- package/src/backoffice/services/README.md +17 -0
- package/src/backoffice/services/analytics.service.proposal.md +859 -0
- package/src/backoffice/services/analytics.service.summary.md +143 -0
- package/src/backoffice/services/category.service.ts +49 -6
- package/src/backoffice/services/subcategory.service.ts +50 -6
- package/src/backoffice/services/technology.service.ts +53 -6
- package/src/services/appointment/appointment.service.ts +59 -6
- package/src/services/procedure/procedure.service.ts +120 -7
- package/src/types/notifications/index.ts +21 -0
package/dist/index.mjs
CHANGED
|
@@ -56,13 +56,13 @@ var AppointmentStatus = /* @__PURE__ */ ((AppointmentStatus2) => {
|
|
|
56
56
|
AppointmentStatus2["RESCHEDULED_BY_CLINIC"] = "rescheduled_by_clinic";
|
|
57
57
|
return AppointmentStatus2;
|
|
58
58
|
})(AppointmentStatus || {});
|
|
59
|
-
var PaymentStatus = /* @__PURE__ */ ((
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return
|
|
59
|
+
var PaymentStatus = /* @__PURE__ */ ((PaymentStatus3) => {
|
|
60
|
+
PaymentStatus3["UNPAID"] = "unpaid";
|
|
61
|
+
PaymentStatus3["PAID"] = "paid";
|
|
62
|
+
PaymentStatus3["PARTIALLY_PAID"] = "partially_paid";
|
|
63
|
+
PaymentStatus3["REFUNDED"] = "refunded";
|
|
64
|
+
PaymentStatus3["NOT_APPLICABLE"] = "not_applicable";
|
|
65
|
+
return PaymentStatus3;
|
|
66
66
|
})(PaymentStatus || {});
|
|
67
67
|
var MediaType = /* @__PURE__ */ ((MediaType2) => {
|
|
68
68
|
MediaType2["BEFORE_PHOTO"] = "before_photo";
|
|
@@ -3329,6 +3329,7 @@ var AppointmentService = class extends BaseService {
|
|
|
3329
3329
|
finalbilling: null,
|
|
3330
3330
|
finalizationNotes: null
|
|
3331
3331
|
};
|
|
3332
|
+
const shouldUpdatePaymentStatus = finalbilling.finalPrice > 0 && appointment.paymentStatus === "not_applicable" /* NOT_APPLICABLE */;
|
|
3332
3333
|
const updateData = {
|
|
3333
3334
|
metadata: {
|
|
3334
3335
|
selectedZones: currentMetadata.selectedZones,
|
|
@@ -3344,6 +3345,9 @@ var AppointmentService = class extends BaseService {
|
|
|
3344
3345
|
finalbilling,
|
|
3345
3346
|
finalizationNotes: currentMetadata.finalizationNotes
|
|
3346
3347
|
},
|
|
3348
|
+
...shouldUpdatePaymentStatus && {
|
|
3349
|
+
paymentStatus: "unpaid" /* UNPAID */
|
|
3350
|
+
},
|
|
3347
3351
|
updatedAt: serverTimestamp7()
|
|
3348
3352
|
};
|
|
3349
3353
|
return await this.updateAppointment(appointmentId, updateData);
|
|
@@ -3557,8 +3561,36 @@ var AppointmentService = class extends BaseService {
|
|
|
3557
3561
|
showCanceled: false,
|
|
3558
3562
|
showNoShow: false
|
|
3559
3563
|
});
|
|
3564
|
+
const now = /* @__PURE__ */ new Date();
|
|
3565
|
+
const allPastAppointments = await this.getPatientAppointments(patientId, {
|
|
3566
|
+
endDate: now,
|
|
3567
|
+
status: [
|
|
3568
|
+
"completed" /* COMPLETED */,
|
|
3569
|
+
"confirmed" /* CONFIRMED */,
|
|
3570
|
+
"checked_in" /* CHECKED_IN */,
|
|
3571
|
+
"in_progress" /* IN_PROGRESS */
|
|
3572
|
+
]
|
|
3573
|
+
});
|
|
3574
|
+
const appointmentsWithRecommendations = allPastAppointments.appointments.filter(
|
|
3575
|
+
(appointment) => {
|
|
3576
|
+
var _a2, _b2, _c2, _d;
|
|
3577
|
+
const endTime = ((_a2 = appointment.appointmentEndTime) == null ? void 0 : _a2.toMillis) ? appointment.appointmentEndTime.toMillis() : ((_b2 = appointment.appointmentEndTime) == null ? void 0 : _b2.seconds) ? appointment.appointmentEndTime.seconds * 1e3 : null;
|
|
3578
|
+
if (!endTime) return false;
|
|
3579
|
+
const isPastEndTime = endTime < now.getTime();
|
|
3580
|
+
const hasRecommendations = (((_d = (_c2 = appointment.metadata) == null ? void 0 : _c2.recommendedProcedures) == null ? void 0 : _d.length) || 0) > 0;
|
|
3581
|
+
return isPastEndTime && hasRecommendations;
|
|
3582
|
+
}
|
|
3583
|
+
);
|
|
3584
|
+
const allAppointmentsMap = /* @__PURE__ */ new Map();
|
|
3585
|
+
pastAppointments.appointments.forEach((apt) => {
|
|
3586
|
+
allAppointmentsMap.set(apt.id, apt);
|
|
3587
|
+
});
|
|
3588
|
+
appointmentsWithRecommendations.forEach((apt) => {
|
|
3589
|
+
allAppointmentsMap.set(apt.id, apt);
|
|
3590
|
+
});
|
|
3591
|
+
const allAppointments = Array.from(allAppointmentsMap.values());
|
|
3560
3592
|
const recommendations = [];
|
|
3561
|
-
for (const appointment of
|
|
3593
|
+
for (const appointment of allAppointments) {
|
|
3562
3594
|
if ((options == null ? void 0 : options.clinicBranchId) && appointment.clinicBranchId !== options.clinicBranchId) {
|
|
3563
3595
|
continue;
|
|
3564
3596
|
}
|
|
@@ -3595,9 +3627,6 @@ var AppointmentService = class extends BaseService {
|
|
|
3595
3627
|
return dateB - dateA;
|
|
3596
3628
|
});
|
|
3597
3629
|
const limitedRecommendations = (options == null ? void 0 : options.limit) ? recommendations.slice(0, options.limit) : recommendations;
|
|
3598
|
-
console.log(
|
|
3599
|
-
`[APPOINTMENT_SERVICE] Found ${limitedRecommendations.length} next steps recommendations for patient ${patientId}`
|
|
3600
|
-
);
|
|
3601
3630
|
return limitedRecommendations;
|
|
3602
3631
|
} catch (error) {
|
|
3603
3632
|
console.error(
|
|
@@ -3885,6 +3914,7 @@ var NotificationType = /* @__PURE__ */ ((NotificationType3) => {
|
|
|
3885
3914
|
NotificationType3["FORM_REMINDER"] = "formReminder";
|
|
3886
3915
|
NotificationType3["FORM_SUBMISSION_CONFIRMATION"] = "formSubmissionConfirmation";
|
|
3887
3916
|
NotificationType3["REVIEW_REQUEST"] = "reviewRequest";
|
|
3917
|
+
NotificationType3["PROCEDURE_RECOMMENDATION"] = "procedureRecommendation";
|
|
3888
3918
|
NotificationType3["PAYMENT_DUE"] = "paymentDue";
|
|
3889
3919
|
NotificationType3["PAYMENT_CONFIRMATION"] = "paymentConfirmation";
|
|
3890
3920
|
NotificationType3["PAYMENT_FAILED"] = "paymentFailed";
|
|
@@ -17739,6 +17769,10 @@ var ProcedureService = class extends BaseService {
|
|
|
17739
17769
|
console.log("[PROCEDURE_SERVICE] Strategy 1: Trying nameLower search");
|
|
17740
17770
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
17741
17771
|
const constraints = getBaseConstraints();
|
|
17772
|
+
const hasNestedFilters = !!(filters.procedureTechnology || filters.procedureCategory || filters.procedureSubcategory);
|
|
17773
|
+
if (hasNestedFilters) {
|
|
17774
|
+
console.log("[PROCEDURE_SERVICE] Strategy 1: Has nested filters, will apply client-side after query");
|
|
17775
|
+
}
|
|
17742
17776
|
constraints.push(where31("nameLower", ">=", searchTerm));
|
|
17743
17777
|
constraints.push(where31("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
17744
17778
|
constraints.push(orderBy18("nameLower"));
|
|
@@ -17754,9 +17788,12 @@ var ProcedureService = class extends BaseService {
|
|
|
17754
17788
|
constraints.push(limit16(filters.pagination || 10));
|
|
17755
17789
|
const q = query31(collection31(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
17756
17790
|
const querySnapshot = await getDocs31(q);
|
|
17757
|
-
|
|
17791
|
+
let procedures = querySnapshot.docs.map(
|
|
17758
17792
|
(doc45) => ({ ...doc45.data(), id: doc45.id })
|
|
17759
17793
|
);
|
|
17794
|
+
if (hasNestedFilters) {
|
|
17795
|
+
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
17796
|
+
}
|
|
17760
17797
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
17761
17798
|
console.log(`[PROCEDURE_SERVICE] Strategy 1 success: ${procedures.length} procedures`);
|
|
17762
17799
|
if (procedures.length < (filters.pagination || 10)) {
|
|
@@ -17772,6 +17809,10 @@ var ProcedureService = class extends BaseService {
|
|
|
17772
17809
|
console.log("[PROCEDURE_SERVICE] Strategy 2: Trying name field search");
|
|
17773
17810
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
17774
17811
|
const constraints = getBaseConstraints();
|
|
17812
|
+
const hasNestedFilters = !!(filters.procedureTechnology || filters.procedureCategory || filters.procedureSubcategory);
|
|
17813
|
+
if (hasNestedFilters) {
|
|
17814
|
+
console.log("[PROCEDURE_SERVICE] Strategy 2: Has nested filters, will apply client-side after query");
|
|
17815
|
+
}
|
|
17775
17816
|
constraints.push(where31("name", ">=", searchTerm));
|
|
17776
17817
|
constraints.push(where31("name", "<=", searchTerm + "\uF8FF"));
|
|
17777
17818
|
constraints.push(orderBy18("name"));
|
|
@@ -17787,9 +17828,12 @@ var ProcedureService = class extends BaseService {
|
|
|
17787
17828
|
constraints.push(limit16(filters.pagination || 10));
|
|
17788
17829
|
const q = query31(collection31(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
17789
17830
|
const querySnapshot = await getDocs31(q);
|
|
17790
|
-
|
|
17831
|
+
let procedures = querySnapshot.docs.map(
|
|
17791
17832
|
(doc45) => ({ ...doc45.data(), id: doc45.id })
|
|
17792
17833
|
);
|
|
17834
|
+
if (hasNestedFilters) {
|
|
17835
|
+
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
17836
|
+
}
|
|
17793
17837
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
17794
17838
|
console.log(`[PROCEDURE_SERVICE] Strategy 2 success: ${procedures.length} procedures`);
|
|
17795
17839
|
if (procedures.length < (filters.pagination || 10)) {
|
|
@@ -17802,9 +17846,47 @@ var ProcedureService = class extends BaseService {
|
|
|
17802
17846
|
}
|
|
17803
17847
|
try {
|
|
17804
17848
|
console.log(
|
|
17805
|
-
"[PROCEDURE_SERVICE] Strategy 3: Using createdAt orderBy with client-side filtering"
|
|
17849
|
+
"[PROCEDURE_SERVICE] Strategy 3: Using createdAt orderBy with client-side filtering",
|
|
17850
|
+
{
|
|
17851
|
+
procedureTechnology: filters.procedureTechnology,
|
|
17852
|
+
hasTechnologyFilter: !!filters.procedureTechnology
|
|
17853
|
+
}
|
|
17854
|
+
);
|
|
17855
|
+
const constraints = [];
|
|
17856
|
+
if (filters.isActive !== void 0) {
|
|
17857
|
+
constraints.push(where31("isActive", "==", filters.isActive));
|
|
17858
|
+
} else {
|
|
17859
|
+
constraints.push(where31("isActive", "==", true));
|
|
17860
|
+
}
|
|
17861
|
+
if (filters.procedureFamily) {
|
|
17862
|
+
constraints.push(where31("family", "==", filters.procedureFamily));
|
|
17863
|
+
}
|
|
17864
|
+
if (filters.practitionerId) {
|
|
17865
|
+
constraints.push(where31("practitionerId", "==", filters.practitionerId));
|
|
17866
|
+
}
|
|
17867
|
+
if (filters.clinicId) {
|
|
17868
|
+
constraints.push(where31("clinicBranchId", "==", filters.clinicId));
|
|
17869
|
+
}
|
|
17870
|
+
if (filters.minPrice !== void 0) {
|
|
17871
|
+
constraints.push(where31("price", ">=", filters.minPrice));
|
|
17872
|
+
}
|
|
17873
|
+
if (filters.maxPrice !== void 0) {
|
|
17874
|
+
constraints.push(where31("price", "<=", filters.maxPrice));
|
|
17875
|
+
}
|
|
17876
|
+
if (filters.minRating !== void 0) {
|
|
17877
|
+
constraints.push(where31("reviewInfo.averageRating", ">=", filters.minRating));
|
|
17878
|
+
}
|
|
17879
|
+
if (filters.maxRating !== void 0) {
|
|
17880
|
+
constraints.push(where31("reviewInfo.averageRating", "<=", filters.maxRating));
|
|
17881
|
+
}
|
|
17882
|
+
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
17883
|
+
const benefitIdsToMatch = filters.treatmentBenefits;
|
|
17884
|
+
constraints.push(where31("treatmentBenefitIds", "array-contains-any", benefitIdsToMatch));
|
|
17885
|
+
}
|
|
17886
|
+
console.log(
|
|
17887
|
+
"[PROCEDURE_SERVICE] Strategy 3 Firestore constraints (nested filters excluded):",
|
|
17888
|
+
constraints.map((c) => c.fieldPath || "unknown")
|
|
17806
17889
|
);
|
|
17807
|
-
const constraints = getBaseConstraints();
|
|
17808
17890
|
constraints.push(orderBy18("createdAt", "desc"));
|
|
17809
17891
|
if (filters.lastDoc) {
|
|
17810
17892
|
if (typeof filters.lastDoc.data === "function") {
|
|
@@ -17821,7 +17903,20 @@ var ProcedureService = class extends BaseService {
|
|
|
17821
17903
|
let procedures = querySnapshot.docs.map(
|
|
17822
17904
|
(doc45) => ({ ...doc45.data(), id: doc45.id })
|
|
17823
17905
|
);
|
|
17906
|
+
console.log("[PROCEDURE_SERVICE] Before applyInMemoryFilters (Strategy 3):", {
|
|
17907
|
+
procedureCount: procedures.length,
|
|
17908
|
+
procedureTechnology: filters.procedureTechnology,
|
|
17909
|
+
filtersObject: {
|
|
17910
|
+
procedureTechnology: filters.procedureTechnology,
|
|
17911
|
+
procedureFamily: filters.procedureFamily,
|
|
17912
|
+
procedureCategory: filters.procedureCategory,
|
|
17913
|
+
procedureSubcategory: filters.procedureSubcategory
|
|
17914
|
+
}
|
|
17915
|
+
});
|
|
17824
17916
|
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
17917
|
+
console.log("[PROCEDURE_SERVICE] After applyInMemoryFilters (Strategy 3):", {
|
|
17918
|
+
procedureCount: procedures.length
|
|
17919
|
+
});
|
|
17825
17920
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
17826
17921
|
console.log(`[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`);
|
|
17827
17922
|
if (procedures.length < (filters.pagination || 10)) {
|
|
@@ -17872,6 +17967,12 @@ var ProcedureService = class extends BaseService {
|
|
|
17872
17967
|
*/
|
|
17873
17968
|
applyInMemoryFilters(procedures, filters) {
|
|
17874
17969
|
let filteredProcedures = [...procedures];
|
|
17970
|
+
console.log("[PROCEDURE_SERVICE] applyInMemoryFilters called:", {
|
|
17971
|
+
procedureCount: procedures.length,
|
|
17972
|
+
procedureTechnology: filters.procedureTechnology,
|
|
17973
|
+
hasTechnologyFilter: !!filters.procedureTechnology,
|
|
17974
|
+
allFilterKeys: Object.keys(filters).filter((k) => filters[k] !== void 0 && filters[k] !== null)
|
|
17975
|
+
});
|
|
17875
17976
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
17876
17977
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
17877
17978
|
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
@@ -17947,6 +18048,7 @@ var ProcedureService = class extends BaseService {
|
|
|
17947
18048
|
);
|
|
17948
18049
|
}
|
|
17949
18050
|
if (filters.procedureTechnology) {
|
|
18051
|
+
const beforeCount = filteredProcedures.length;
|
|
17950
18052
|
filteredProcedures = filteredProcedures.filter(
|
|
17951
18053
|
(procedure) => {
|
|
17952
18054
|
var _a;
|
|
@@ -17954,8 +18056,18 @@ var ProcedureService = class extends BaseService {
|
|
|
17954
18056
|
}
|
|
17955
18057
|
);
|
|
17956
18058
|
console.log(
|
|
17957
|
-
`[PROCEDURE_SERVICE] Applied technology filter,
|
|
18059
|
+
`[PROCEDURE_SERVICE] Applied technology filter (${filters.procedureTechnology}), before: ${beforeCount}, after: ${filteredProcedures.length}`
|
|
17958
18060
|
);
|
|
18061
|
+
if (beforeCount > filteredProcedures.length) {
|
|
18062
|
+
const filteredOut = procedures.filter((p) => {
|
|
18063
|
+
var _a;
|
|
18064
|
+
return ((_a = p.technology) == null ? void 0 : _a.id) !== filters.procedureTechnology;
|
|
18065
|
+
}).slice(0, 3).map((p) => {
|
|
18066
|
+
var _a;
|
|
18067
|
+
return { id: p.id, techId: (_a = p.technology) == null ? void 0 : _a.id, name: p.name };
|
|
18068
|
+
});
|
|
18069
|
+
console.log("[PROCEDURE_SERVICE] Filtered out sample procedures:", filteredOut);
|
|
18070
|
+
}
|
|
17959
18071
|
}
|
|
17960
18072
|
if (filters.practitionerId) {
|
|
17961
18073
|
filteredProcedures = filteredProcedures.filter(
|
|
@@ -18059,9 +18171,9 @@ var ProcedureService = class extends BaseService {
|
|
|
18059
18171
|
var _a, _b;
|
|
18060
18172
|
const procedureId = this.generateId();
|
|
18061
18173
|
const [category, subcategory, technology] = await Promise.all([
|
|
18062
|
-
this.categoryService.
|
|
18063
|
-
this.subcategoryService.
|
|
18064
|
-
this.technologyService.
|
|
18174
|
+
this.categoryService.getByIdInternal(data.categoryId),
|
|
18175
|
+
this.subcategoryService.getByIdInternal(data.categoryId, data.subcategoryId),
|
|
18176
|
+
this.technologyService.getByIdInternal(data.technologyId)
|
|
18065
18177
|
]);
|
|
18066
18178
|
if (!category || !subcategory || !technology) {
|
|
18067
18179
|
throw new Error("One or more required base entities not found");
|
|
@@ -19019,7 +19131,6 @@ import {
|
|
|
19019
19131
|
addDoc as addDoc5,
|
|
19020
19132
|
collection as collection34,
|
|
19021
19133
|
doc as doc40,
|
|
19022
|
-
getCountFromServer as getCountFromServer4,
|
|
19023
19134
|
getDoc as getDoc41,
|
|
19024
19135
|
getDocs as getDocs34,
|
|
19025
19136
|
limit as limit18,
|
|
@@ -19034,7 +19145,16 @@ import {
|
|
|
19034
19145
|
var CATEGORIES_COLLECTION = "backoffice_categories";
|
|
19035
19146
|
|
|
19036
19147
|
// src/backoffice/services/category.service.ts
|
|
19148
|
+
var EXCLUDED_CATEGORY_ID = "consultation";
|
|
19037
19149
|
var CategoryService = class extends BaseService {
|
|
19150
|
+
/**
|
|
19151
|
+
* Filters out excluded categories from a list.
|
|
19152
|
+
* @param categories - List of categories to filter
|
|
19153
|
+
* @returns Filtered list without excluded categories
|
|
19154
|
+
*/
|
|
19155
|
+
filterExcludedCategories(categories) {
|
|
19156
|
+
return categories.filter((cat) => cat.id !== EXCLUDED_CATEGORY_ID);
|
|
19157
|
+
}
|
|
19038
19158
|
/**
|
|
19039
19159
|
* Referenca na Firestore kolekciju kategorija
|
|
19040
19160
|
*/
|
|
@@ -19071,8 +19191,9 @@ var CategoryService = class extends BaseService {
|
|
|
19071
19191
|
where34("family", "==", family),
|
|
19072
19192
|
where34("isActive", "==", active)
|
|
19073
19193
|
);
|
|
19074
|
-
const snapshot = await
|
|
19075
|
-
|
|
19194
|
+
const snapshot = await getDocs34(q);
|
|
19195
|
+
const filteredDocs = snapshot.docs.filter((doc45) => doc45.id !== EXCLUDED_CATEGORY_ID);
|
|
19196
|
+
counts[family] = filteredDocs.length;
|
|
19076
19197
|
}
|
|
19077
19198
|
return counts;
|
|
19078
19199
|
}
|
|
@@ -19083,12 +19204,13 @@ var CategoryService = class extends BaseService {
|
|
|
19083
19204
|
async getAllForFilter() {
|
|
19084
19205
|
const q = query34(this.categoriesRef, where34("isActive", "==", true));
|
|
19085
19206
|
const snapshot = await getDocs34(q);
|
|
19086
|
-
|
|
19207
|
+
const categories = snapshot.docs.map(
|
|
19087
19208
|
(doc45) => ({
|
|
19088
19209
|
id: doc45.id,
|
|
19089
19210
|
...doc45.data()
|
|
19090
19211
|
})
|
|
19091
19212
|
);
|
|
19213
|
+
return this.filterExcludedCategories(categories);
|
|
19092
19214
|
}
|
|
19093
19215
|
/**
|
|
19094
19216
|
* Vraća sve kategorije za određenu familiju za potrebe filtera (bez paginacije)
|
|
@@ -19103,12 +19225,13 @@ var CategoryService = class extends BaseService {
|
|
|
19103
19225
|
orderBy20("name")
|
|
19104
19226
|
);
|
|
19105
19227
|
const snapshot = await getDocs34(q);
|
|
19106
|
-
|
|
19228
|
+
const categories = snapshot.docs.map(
|
|
19107
19229
|
(doc45) => ({
|
|
19108
19230
|
id: doc45.id,
|
|
19109
19231
|
...doc45.data()
|
|
19110
19232
|
})
|
|
19111
19233
|
);
|
|
19234
|
+
return this.filterExcludedCategories(categories);
|
|
19112
19235
|
}
|
|
19113
19236
|
/**
|
|
19114
19237
|
* Vraća sve kategorije sa paginacijom
|
|
@@ -19131,8 +19254,9 @@ var CategoryService = class extends BaseService {
|
|
|
19131
19254
|
...doc45.data()
|
|
19132
19255
|
})
|
|
19133
19256
|
);
|
|
19257
|
+
const filteredCategories = this.filterExcludedCategories(categories);
|
|
19134
19258
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19135
|
-
return { categories, lastVisible: newLastVisible };
|
|
19259
|
+
return { categories: filteredCategories, lastVisible: newLastVisible };
|
|
19136
19260
|
}
|
|
19137
19261
|
/**
|
|
19138
19262
|
* Vraća sve aktivne kategorije za određenu familiju procedura sa paginacijom
|
|
@@ -19157,8 +19281,9 @@ var CategoryService = class extends BaseService {
|
|
|
19157
19281
|
...doc45.data()
|
|
19158
19282
|
})
|
|
19159
19283
|
);
|
|
19284
|
+
const filteredCategories = this.filterExcludedCategories(categories);
|
|
19160
19285
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19161
|
-
return { categories, lastVisible: newLastVisible };
|
|
19286
|
+
return { categories: filteredCategories, lastVisible: newLastVisible };
|
|
19162
19287
|
}
|
|
19163
19288
|
/**
|
|
19164
19289
|
* Ažurira postojeću kategoriju
|
|
@@ -19195,6 +19320,22 @@ var CategoryService = class extends BaseService {
|
|
|
19195
19320
|
* @returns Kategorija ili null ako ne postoji
|
|
19196
19321
|
*/
|
|
19197
19322
|
async getById(id) {
|
|
19323
|
+
if (id === EXCLUDED_CATEGORY_ID) return null;
|
|
19324
|
+
const docRef = doc40(this.categoriesRef, id);
|
|
19325
|
+
const docSnap = await getDoc41(docRef);
|
|
19326
|
+
if (!docSnap.exists()) return null;
|
|
19327
|
+
return {
|
|
19328
|
+
id: docSnap.id,
|
|
19329
|
+
...docSnap.data()
|
|
19330
|
+
};
|
|
19331
|
+
}
|
|
19332
|
+
/**
|
|
19333
|
+
* Internal method to get category by ID without filtering.
|
|
19334
|
+
* Used internally for consultation procedures.
|
|
19335
|
+
* @param id - ID of the category to get
|
|
19336
|
+
* @returns Category or null if not found
|
|
19337
|
+
*/
|
|
19338
|
+
async getByIdInternal(id) {
|
|
19198
19339
|
const docRef = doc40(this.categoriesRef, id);
|
|
19199
19340
|
const docSnap = await getDoc41(docRef);
|
|
19200
19341
|
if (!docSnap.exists()) return null;
|
|
@@ -19220,6 +19361,7 @@ var CategoryService = class extends BaseService {
|
|
|
19220
19361
|
const snapshot = await getDocs34(q);
|
|
19221
19362
|
if (snapshot.empty) return null;
|
|
19222
19363
|
const doc45 = snapshot.docs[0];
|
|
19364
|
+
if (doc45.id === EXCLUDED_CATEGORY_ID) return null;
|
|
19223
19365
|
return {
|
|
19224
19366
|
id: doc45.id,
|
|
19225
19367
|
...doc45.data()
|
|
@@ -19257,6 +19399,7 @@ var CategoryService = class extends BaseService {
|
|
|
19257
19399
|
const snapshot = await getDocs34(q);
|
|
19258
19400
|
if (snapshot.empty) break;
|
|
19259
19401
|
for (const d of snapshot.docs) {
|
|
19402
|
+
if (d.id === EXCLUDED_CATEGORY_ID) continue;
|
|
19260
19403
|
const category = { id: d.id, ...d.data() };
|
|
19261
19404
|
rows.push(this.categoryToCsvRow(category));
|
|
19262
19405
|
}
|
|
@@ -19299,7 +19442,6 @@ import {
|
|
|
19299
19442
|
collectionGroup as collectionGroup2,
|
|
19300
19443
|
deleteDoc as deleteDoc21,
|
|
19301
19444
|
doc as doc41,
|
|
19302
|
-
getCountFromServer as getCountFromServer5,
|
|
19303
19445
|
getDoc as getDoc42,
|
|
19304
19446
|
getDocs as getDocs35,
|
|
19305
19447
|
limit as limit19,
|
|
@@ -19315,7 +19457,16 @@ import {
|
|
|
19315
19457
|
var SUBCATEGORIES_COLLECTION = "subcategories";
|
|
19316
19458
|
|
|
19317
19459
|
// src/backoffice/services/subcategory.service.ts
|
|
19460
|
+
var EXCLUDED_SUBCATEGORY_ID = "free-consultation";
|
|
19318
19461
|
var SubcategoryService = class extends BaseService {
|
|
19462
|
+
/**
|
|
19463
|
+
* Filters out excluded subcategories from a list.
|
|
19464
|
+
* @param subcategories - List of subcategories to filter
|
|
19465
|
+
* @returns Filtered list without excluded subcategories
|
|
19466
|
+
*/
|
|
19467
|
+
filterExcludedSubcategories(subcategories) {
|
|
19468
|
+
return subcategories.filter((sub) => sub.id !== EXCLUDED_SUBCATEGORY_ID);
|
|
19469
|
+
}
|
|
19319
19470
|
/**
|
|
19320
19471
|
* Vraća referencu na Firestore kolekciju podkategorija za određenu kategoriju
|
|
19321
19472
|
* @param categoryId - ID roditeljske kategorije
|
|
@@ -19362,8 +19513,9 @@ var SubcategoryService = class extends BaseService {
|
|
|
19362
19513
|
const categoryId = categoryDoc.id;
|
|
19363
19514
|
const subcategoriesRef = this.getSubcategoriesRef(categoryId);
|
|
19364
19515
|
const q = query35(subcategoriesRef, where35("isActive", "==", active));
|
|
19365
|
-
const snapshot = await
|
|
19366
|
-
|
|
19516
|
+
const snapshot = await getDocs35(q);
|
|
19517
|
+
const filteredDocs = snapshot.docs.filter((doc45) => doc45.id !== EXCLUDED_SUBCATEGORY_ID);
|
|
19518
|
+
counts[categoryId] = filteredDocs.length;
|
|
19367
19519
|
}
|
|
19368
19520
|
return counts;
|
|
19369
19521
|
}
|
|
@@ -19389,8 +19541,9 @@ var SubcategoryService = class extends BaseService {
|
|
|
19389
19541
|
...doc45.data()
|
|
19390
19542
|
})
|
|
19391
19543
|
);
|
|
19544
|
+
const filteredSubcategories = this.filterExcludedSubcategories(subcategories);
|
|
19392
19545
|
const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
19393
|
-
return { subcategories, lastVisible: newLastVisible };
|
|
19546
|
+
return { subcategories: filteredSubcategories, lastVisible: newLastVisible };
|
|
19394
19547
|
}
|
|
19395
19548
|
/**
|
|
19396
19549
|
* Vraća sve podkategorije sa paginacijom koristeći collection group query.
|
|
@@ -19419,8 +19572,9 @@ var SubcategoryService = class extends BaseService {
|
|
|
19419
19572
|
...doc45.data()
|
|
19420
19573
|
})
|
|
19421
19574
|
);
|
|
19575
|
+
const filteredSubcategories = this.filterExcludedSubcategories(subcategories);
|
|
19422
19576
|
const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
19423
|
-
return { subcategories, lastVisible: newLastVisible };
|
|
19577
|
+
return { subcategories: filteredSubcategories, lastVisible: newLastVisible };
|
|
19424
19578
|
}
|
|
19425
19579
|
/**
|
|
19426
19580
|
* Vraća sve subkategorije za određenu kategoriju za potrebe filtera (bez paginacije)
|
|
@@ -19433,12 +19587,13 @@ var SubcategoryService = class extends BaseService {
|
|
|
19433
19587
|
where35("isActive", "==", true)
|
|
19434
19588
|
);
|
|
19435
19589
|
const querySnapshot = await getDocs35(q);
|
|
19436
|
-
|
|
19590
|
+
const subcategories = querySnapshot.docs.map(
|
|
19437
19591
|
(doc45) => ({
|
|
19438
19592
|
id: doc45.id,
|
|
19439
19593
|
...doc45.data()
|
|
19440
19594
|
})
|
|
19441
19595
|
);
|
|
19596
|
+
return this.filterExcludedSubcategories(subcategories);
|
|
19442
19597
|
}
|
|
19443
19598
|
/**
|
|
19444
19599
|
* Vraća sve subkategorije za potrebe filtera (bez paginacije)
|
|
@@ -19450,12 +19605,13 @@ var SubcategoryService = class extends BaseService {
|
|
|
19450
19605
|
where35("isActive", "==", true)
|
|
19451
19606
|
);
|
|
19452
19607
|
const querySnapshot = await getDocs35(q);
|
|
19453
|
-
|
|
19608
|
+
const subcategories = querySnapshot.docs.map(
|
|
19454
19609
|
(doc45) => ({
|
|
19455
19610
|
id: doc45.id,
|
|
19456
19611
|
...doc45.data()
|
|
19457
19612
|
})
|
|
19458
19613
|
);
|
|
19614
|
+
return this.filterExcludedSubcategories(subcategories);
|
|
19459
19615
|
}
|
|
19460
19616
|
/**
|
|
19461
19617
|
* Ažurira postojeću podkategoriju
|
|
@@ -19525,6 +19681,23 @@ var SubcategoryService = class extends BaseService {
|
|
|
19525
19681
|
* @returns Podkategorija ili null ako ne postoji
|
|
19526
19682
|
*/
|
|
19527
19683
|
async getById(categoryId, subcategoryId) {
|
|
19684
|
+
if (subcategoryId === EXCLUDED_SUBCATEGORY_ID) return null;
|
|
19685
|
+
const docRef = doc41(this.getSubcategoriesRef(categoryId), subcategoryId);
|
|
19686
|
+
const docSnap = await getDoc42(docRef);
|
|
19687
|
+
if (!docSnap.exists()) return null;
|
|
19688
|
+
return {
|
|
19689
|
+
id: docSnap.id,
|
|
19690
|
+
...docSnap.data()
|
|
19691
|
+
};
|
|
19692
|
+
}
|
|
19693
|
+
/**
|
|
19694
|
+
* Internal method to get subcategory by ID without filtering.
|
|
19695
|
+
* Used internally for consultation procedures.
|
|
19696
|
+
* @param categoryId - ID of the category
|
|
19697
|
+
* @param subcategoryId - ID of the subcategory to get
|
|
19698
|
+
* @returns Subcategory or null if not found
|
|
19699
|
+
*/
|
|
19700
|
+
async getByIdInternal(categoryId, subcategoryId) {
|
|
19528
19701
|
const docRef = doc41(this.getSubcategoriesRef(categoryId), subcategoryId);
|
|
19529
19702
|
const docSnap = await getDoc42(docRef);
|
|
19530
19703
|
if (!docSnap.exists()) return null;
|
|
@@ -19549,6 +19722,7 @@ var SubcategoryService = class extends BaseService {
|
|
|
19549
19722
|
const querySnapshot = await getDocs35(q);
|
|
19550
19723
|
if (querySnapshot.empty) return null;
|
|
19551
19724
|
const doc45 = querySnapshot.docs[0];
|
|
19725
|
+
if (doc45.id === EXCLUDED_SUBCATEGORY_ID) return null;
|
|
19552
19726
|
return {
|
|
19553
19727
|
id: doc45.id,
|
|
19554
19728
|
...doc45.data()
|
|
@@ -19589,6 +19763,7 @@ var SubcategoryService = class extends BaseService {
|
|
|
19589
19763
|
const snapshot = await getDocs35(q);
|
|
19590
19764
|
if (snapshot.empty) break;
|
|
19591
19765
|
for (const d of snapshot.docs) {
|
|
19766
|
+
if (d.id === EXCLUDED_SUBCATEGORY_ID) continue;
|
|
19592
19767
|
const subcategory = { id: d.id, ...d.data() };
|
|
19593
19768
|
rows.push(this.subcategoryToCsvRow(subcategory));
|
|
19594
19769
|
}
|
|
@@ -19646,11 +19821,20 @@ import {
|
|
|
19646
19821
|
var PRODUCTS_COLLECTION = "products";
|
|
19647
19822
|
|
|
19648
19823
|
// src/backoffice/services/technology.service.ts
|
|
19824
|
+
var EXCLUDED_TECHNOLOGY_ID = "free-consultation-tech";
|
|
19649
19825
|
var DEFAULT_CERTIFICATION_REQUIREMENT = {
|
|
19650
19826
|
minimumLevel: "aesthetician" /* AESTHETICIAN */,
|
|
19651
19827
|
requiredSpecialties: []
|
|
19652
19828
|
};
|
|
19653
19829
|
var TechnologyService = class extends BaseService {
|
|
19830
|
+
/**
|
|
19831
|
+
* Filters out excluded technologies from a list.
|
|
19832
|
+
* @param technologies - List of technologies to filter
|
|
19833
|
+
* @returns Filtered list without excluded technologies
|
|
19834
|
+
*/
|
|
19835
|
+
filterExcludedTechnologies(technologies) {
|
|
19836
|
+
return technologies.filter((tech) => tech.id !== EXCLUDED_TECHNOLOGY_ID);
|
|
19837
|
+
}
|
|
19654
19838
|
/**
|
|
19655
19839
|
* Reference to the Firestore collection of technologies.
|
|
19656
19840
|
*/
|
|
@@ -19699,6 +19883,7 @@ var TechnologyService = class extends BaseService {
|
|
|
19699
19883
|
const snapshot = await getDocs36(q);
|
|
19700
19884
|
const counts = {};
|
|
19701
19885
|
snapshot.docs.forEach((doc45) => {
|
|
19886
|
+
if (doc45.id === EXCLUDED_TECHNOLOGY_ID) return;
|
|
19702
19887
|
const tech = doc45.data();
|
|
19703
19888
|
counts[tech.subcategoryId] = (counts[tech.subcategoryId] || 0) + 1;
|
|
19704
19889
|
});
|
|
@@ -19714,6 +19899,7 @@ var TechnologyService = class extends BaseService {
|
|
|
19714
19899
|
const snapshot = await getDocs36(q);
|
|
19715
19900
|
const counts = {};
|
|
19716
19901
|
snapshot.docs.forEach((doc45) => {
|
|
19902
|
+
if (doc45.id === EXCLUDED_TECHNOLOGY_ID) return;
|
|
19717
19903
|
const tech = doc45.data();
|
|
19718
19904
|
counts[tech.categoryId] = (counts[tech.categoryId] || 0) + 1;
|
|
19719
19905
|
});
|
|
@@ -19740,8 +19926,9 @@ var TechnologyService = class extends BaseService {
|
|
|
19740
19926
|
...doc45.data()
|
|
19741
19927
|
})
|
|
19742
19928
|
);
|
|
19929
|
+
const filteredTechnologies = this.filterExcludedTechnologies(technologies);
|
|
19743
19930
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19744
|
-
return { technologies, lastVisible: newLastVisible };
|
|
19931
|
+
return { technologies: filteredTechnologies, lastVisible: newLastVisible };
|
|
19745
19932
|
}
|
|
19746
19933
|
/**
|
|
19747
19934
|
* Returns all technologies for a specific category with pagination.
|
|
@@ -19766,8 +19953,9 @@ var TechnologyService = class extends BaseService {
|
|
|
19766
19953
|
...doc45.data()
|
|
19767
19954
|
})
|
|
19768
19955
|
);
|
|
19956
|
+
const filteredTechnologies = this.filterExcludedTechnologies(technologies);
|
|
19769
19957
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19770
|
-
return { technologies, lastVisible: newLastVisible };
|
|
19958
|
+
return { technologies: filteredTechnologies, lastVisible: newLastVisible };
|
|
19771
19959
|
}
|
|
19772
19960
|
/**
|
|
19773
19961
|
* Returns all technologies for a specific subcategory with pagination.
|
|
@@ -19792,8 +19980,9 @@ var TechnologyService = class extends BaseService {
|
|
|
19792
19980
|
...doc45.data()
|
|
19793
19981
|
})
|
|
19794
19982
|
);
|
|
19983
|
+
const filteredTechnologies = this.filterExcludedTechnologies(technologies);
|
|
19795
19984
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19796
|
-
return { technologies, lastVisible: newLastVisible };
|
|
19985
|
+
return { technologies: filteredTechnologies, lastVisible: newLastVisible };
|
|
19797
19986
|
}
|
|
19798
19987
|
/**
|
|
19799
19988
|
* Updates an existing technology.
|
|
@@ -19851,6 +20040,22 @@ var TechnologyService = class extends BaseService {
|
|
|
19851
20040
|
* @returns The technology or null if it doesn't exist.
|
|
19852
20041
|
*/
|
|
19853
20042
|
async getById(id) {
|
|
20043
|
+
if (id === EXCLUDED_TECHNOLOGY_ID) return null;
|
|
20044
|
+
const docRef = doc42(this.technologiesRef, id);
|
|
20045
|
+
const docSnap = await getDoc43(docRef);
|
|
20046
|
+
if (!docSnap.exists()) return null;
|
|
20047
|
+
return {
|
|
20048
|
+
id: docSnap.id,
|
|
20049
|
+
...docSnap.data()
|
|
20050
|
+
};
|
|
20051
|
+
}
|
|
20052
|
+
/**
|
|
20053
|
+
* Internal method to get technology by ID without filtering.
|
|
20054
|
+
* Used internally for consultation procedures.
|
|
20055
|
+
* @param id - The ID of the requested technology
|
|
20056
|
+
* @returns The technology or null if it doesn't exist
|
|
20057
|
+
*/
|
|
20058
|
+
async getByIdInternal(id) {
|
|
19854
20059
|
const docRef = doc42(this.technologiesRef, id);
|
|
19855
20060
|
const docSnap = await getDoc43(docRef);
|
|
19856
20061
|
if (!docSnap.exists()) return null;
|
|
@@ -19874,6 +20079,7 @@ var TechnologyService = class extends BaseService {
|
|
|
19874
20079
|
const snapshot = await getDocs36(q);
|
|
19875
20080
|
if (snapshot.empty) return null;
|
|
19876
20081
|
const doc45 = snapshot.docs[0];
|
|
20082
|
+
if (doc45.id === EXCLUDED_TECHNOLOGY_ID) return null;
|
|
19877
20083
|
return {
|
|
19878
20084
|
id: doc45.id,
|
|
19879
20085
|
...doc45.data()
|
|
@@ -20239,12 +20445,13 @@ var TechnologyService = class extends BaseService {
|
|
|
20239
20445
|
orderBy22("name")
|
|
20240
20446
|
);
|
|
20241
20447
|
const snapshot = await getDocs36(q);
|
|
20242
|
-
|
|
20448
|
+
const technologies = snapshot.docs.map(
|
|
20243
20449
|
(doc45) => ({
|
|
20244
20450
|
id: doc45.id,
|
|
20245
20451
|
...doc45.data()
|
|
20246
20452
|
})
|
|
20247
20453
|
);
|
|
20454
|
+
return this.filterExcludedTechnologies(technologies);
|
|
20248
20455
|
}
|
|
20249
20456
|
/**
|
|
20250
20457
|
* Gets all active technologies for a subcategory for filter dropdowns.
|
|
@@ -20260,12 +20467,13 @@ var TechnologyService = class extends BaseService {
|
|
|
20260
20467
|
orderBy22("name")
|
|
20261
20468
|
);
|
|
20262
20469
|
const snapshot = await getDocs36(q);
|
|
20263
|
-
|
|
20470
|
+
const technologies = snapshot.docs.map(
|
|
20264
20471
|
(doc45) => ({
|
|
20265
20472
|
id: doc45.id,
|
|
20266
20473
|
...doc45.data()
|
|
20267
20474
|
})
|
|
20268
20475
|
);
|
|
20476
|
+
return this.filterExcludedTechnologies(technologies);
|
|
20269
20477
|
}
|
|
20270
20478
|
/**
|
|
20271
20479
|
* Gets all active technologies for filter dropdowns.
|
|
@@ -20277,12 +20485,13 @@ var TechnologyService = class extends BaseService {
|
|
|
20277
20485
|
orderBy22("name")
|
|
20278
20486
|
);
|
|
20279
20487
|
const snapshot = await getDocs36(q);
|
|
20280
|
-
|
|
20488
|
+
const technologies = snapshot.docs.map(
|
|
20281
20489
|
(doc45) => ({
|
|
20282
20490
|
id: doc45.id,
|
|
20283
20491
|
...doc45.data()
|
|
20284
20492
|
})
|
|
20285
20493
|
);
|
|
20494
|
+
return this.filterExcludedTechnologies(technologies);
|
|
20286
20495
|
}
|
|
20287
20496
|
// ==========================================
|
|
20288
20497
|
// NEW METHODS: Product assignment management
|
|
@@ -20448,6 +20657,7 @@ var TechnologyService = class extends BaseService {
|
|
|
20448
20657
|
const snapshot = await getDocs36(q);
|
|
20449
20658
|
if (snapshot.empty) break;
|
|
20450
20659
|
for (const d of snapshot.docs) {
|
|
20660
|
+
if (d.id === EXCLUDED_TECHNOLOGY_ID) continue;
|
|
20451
20661
|
const technology = { id: d.id, ...d.data() };
|
|
20452
20662
|
const productNames = await this.getProductNamesForTechnology(technology.id);
|
|
20453
20663
|
rows.push(this.technologyToCsvRow(technology, productNames));
|
package/package.json
CHANGED