@blackcode_sa/metaestetics-api 1.12.62 → 1.12.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/index.d.mts +4 -2
- package/dist/admin/index.d.ts +4 -2
- package/dist/admin/index.js +4 -45
- package/dist/admin/index.mjs +4 -45
- package/dist/backoffice/index.d.mts +9 -0
- package/dist/backoffice/index.d.ts +9 -0
- package/dist/backoffice/index.js +11 -0
- package/dist/backoffice/index.mjs +11 -0
- package/dist/index.d.mts +99 -3
- package/dist/index.d.ts +99 -3
- package/dist/index.js +545 -281
- package/dist/index.mjs +867 -603
- package/package.json +119 -119
- package/src/__mocks__/firstore.ts +10 -10
- package/src/admin/aggregation/README.md +79 -79
- package/src/admin/aggregation/appointment/README.md +128 -128
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +1844 -1844
- package/src/admin/aggregation/appointment/index.ts +1 -1
- package/src/admin/aggregation/clinic/README.md +52 -52
- package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +703 -703
- package/src/admin/aggregation/clinic/index.ts +1 -1
- package/src/admin/aggregation/forms/README.md +13 -13
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +322 -322
- package/src/admin/aggregation/forms/index.ts +1 -1
- package/src/admin/aggregation/index.ts +8 -8
- package/src/admin/aggregation/patient/README.md +27 -27
- package/src/admin/aggregation/patient/index.ts +1 -1
- package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -141
- package/src/admin/aggregation/practitioner/README.md +42 -42
- package/src/admin/aggregation/practitioner/index.ts +1 -1
- package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -433
- package/src/admin/aggregation/practitioner-invite/index.ts +1 -1
- package/src/admin/aggregation/practitioner-invite/practitioner-invite.aggregation.service.ts +961 -961
- package/src/admin/aggregation/procedure/README.md +43 -43
- package/src/admin/aggregation/procedure/index.ts +1 -1
- package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +702 -702
- package/src/admin/aggregation/reviews/index.ts +1 -1
- package/src/admin/aggregation/reviews/reviews.aggregation.service.ts +641 -689
- package/src/admin/booking/README.md +125 -125
- package/src/admin/booking/booking.admin.ts +1037 -1037
- package/src/admin/booking/booking.calculator.ts +712 -712
- package/src/admin/booking/booking.types.ts +59 -59
- package/src/admin/booking/index.ts +3 -3
- package/src/admin/booking/timezones-problem.md +185 -185
- package/src/admin/calendar/README.md +7 -7
- package/src/admin/calendar/calendar.admin.service.ts +345 -345
- package/src/admin/calendar/index.ts +1 -1
- package/src/admin/documentation-templates/document-manager.admin.ts +260 -260
- package/src/admin/documentation-templates/index.ts +1 -1
- package/src/admin/free-consultation/free-consultation-utils.admin.ts +148 -148
- package/src/admin/free-consultation/index.ts +1 -1
- package/src/admin/index.ts +75 -75
- package/src/admin/logger/index.ts +78 -78
- package/src/admin/mailing/README.md +95 -95
- package/src/admin/mailing/appointment/appointment.mailing.service.ts +732 -732
- package/src/admin/mailing/appointment/index.ts +1 -1
- package/src/admin/mailing/appointment/templates/patient/appointment-confirmed.html +40 -40
- package/src/admin/mailing/base.mailing.service.ts +208 -208
- package/src/admin/mailing/index.ts +3 -3
- package/src/admin/mailing/practitionerInvite/existing-practitioner-invite.mailing.ts +611 -611
- package/src/admin/mailing/practitionerInvite/index.ts +2 -2
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +395 -395
- package/src/admin/mailing/practitionerInvite/templates/existing-practitioner-invitation.template.ts +155 -155
- package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -101
- package/src/admin/mailing/practitionerInvite/templates/invite-accepted-notification.template.ts +228 -228
- package/src/admin/mailing/practitionerInvite/templates/invite-rejected-notification.template.ts +242 -242
- package/src/admin/notifications/index.ts +1 -1
- package/src/admin/notifications/notifications.admin.ts +710 -710
- package/src/admin/requirements/README.md +128 -128
- package/src/admin/requirements/index.ts +1 -1
- package/src/admin/requirements/patient-requirements.admin.service.ts +475 -475
- package/src/admin/users/index.ts +1 -1
- package/src/admin/users/user-profile.admin.ts +405 -405
- package/src/backoffice/constants/certification.constants.ts +13 -13
- package/src/backoffice/constants/index.ts +1 -1
- package/src/backoffice/errors/backoffice.errors.ts +181 -181
- package/src/backoffice/errors/index.ts +1 -1
- package/src/backoffice/expo-safe/README.md +26 -26
- package/src/backoffice/expo-safe/index.ts +41 -41
- package/src/backoffice/index.ts +5 -5
- package/src/backoffice/services/FIXES_README.md +102 -102
- package/src/backoffice/services/README.md +40 -40
- package/src/backoffice/services/brand.service.ts +256 -256
- package/src/backoffice/services/category.service.ts +318 -318
- package/src/backoffice/services/constants.service.ts +385 -385
- package/src/backoffice/services/documentation-template.service.ts +202 -202
- package/src/backoffice/services/index.ts +8 -8
- package/src/backoffice/services/migrate-products.ts +116 -116
- package/src/backoffice/services/product.service.ts +553 -553
- package/src/backoffice/services/requirement.service.ts +235 -235
- package/src/backoffice/services/subcategory.service.ts +395 -395
- package/src/backoffice/services/technology.service.ts +1083 -1070
- package/src/backoffice/types/README.md +12 -12
- package/src/backoffice/types/admin-constants.types.ts +69 -69
- package/src/backoffice/types/brand.types.ts +29 -29
- package/src/backoffice/types/category.types.ts +62 -62
- package/src/backoffice/types/documentation-templates.types.ts +28 -28
- package/src/backoffice/types/index.ts +10 -10
- package/src/backoffice/types/procedure-product.types.ts +38 -38
- package/src/backoffice/types/product.types.ts +240 -240
- package/src/backoffice/types/requirement.types.ts +63 -63
- package/src/backoffice/types/static/README.md +18 -18
- package/src/backoffice/types/static/blocking-condition.types.ts +21 -21
- package/src/backoffice/types/static/certification.types.ts +37 -37
- package/src/backoffice/types/static/contraindication.types.ts +19 -19
- package/src/backoffice/types/static/index.ts +6 -6
- package/src/backoffice/types/static/pricing.types.ts +16 -16
- package/src/backoffice/types/static/procedure-family.types.ts +14 -14
- package/src/backoffice/types/static/treatment-benefit.types.ts +22 -22
- package/src/backoffice/types/subcategory.types.ts +34 -34
- package/src/backoffice/types/technology.types.ts +163 -161
- package/src/backoffice/validations/index.ts +1 -1
- package/src/backoffice/validations/schemas.ts +164 -163
- package/src/config/__mocks__/firebase.ts +99 -99
- package/src/config/firebase.ts +78 -78
- package/src/config/index.ts +9 -9
- package/src/errors/auth.error.ts +6 -6
- package/src/errors/auth.errors.ts +200 -200
- package/src/errors/clinic.errors.ts +32 -32
- package/src/errors/firebase.errors.ts +47 -47
- package/src/errors/user.errors.ts +99 -99
- package/src/index.backup.ts +407 -407
- package/src/index.ts +6 -6
- package/src/locales/en.ts +31 -31
- package/src/recommender/admin/index.ts +1 -1
- package/src/recommender/admin/services/recommender.service.admin.ts +5 -5
- package/src/recommender/front/index.ts +1 -1
- package/src/recommender/front/services/onboarding.service.ts +5 -5
- package/src/recommender/front/services/recommender.service.ts +3 -3
- package/src/recommender/index.ts +1 -1
- package/src/services/PATIENTAUTH.MD +197 -197
- package/src/services/README.md +106 -106
- package/src/services/__tests__/auth/auth.mock.test.ts +17 -17
- package/src/services/__tests__/auth/auth.setup.ts +293 -293
- package/src/services/__tests__/auth.service.test.ts +346 -346
- package/src/services/__tests__/base.service.test.ts +77 -77
- package/src/services/__tests__/user.service.test.ts +528 -528
- package/src/services/appointment/README.md +17 -17
- package/src/services/appointment/appointment.service.ts +2505 -2082
- package/src/services/appointment/index.ts +1 -1
- package/src/services/appointment/utils/appointment.utils.ts +552 -552
- package/src/services/appointment/utils/extended-procedure.utils.ts +314 -314
- package/src/services/appointment/utils/form-initialization.utils.ts +225 -225
- package/src/services/appointment/utils/recommended-procedure.utils.ts +195 -195
- package/src/services/appointment/utils/zone-management.utils.ts +353 -353
- package/src/services/appointment/utils/zone-photo.utils.ts +152 -152
- package/src/services/auth/auth.service.ts +989 -989
- package/src/services/auth/auth.v2.service.ts +961 -961
- package/src/services/auth/index.ts +7 -7
- package/src/services/auth/utils/error.utils.ts +90 -90
- package/src/services/auth/utils/firebase.utils.ts +49 -49
- package/src/services/auth/utils/index.ts +21 -21
- package/src/services/auth/utils/practitioner.utils.ts +125 -125
- package/src/services/base.service.ts +41 -41
- package/src/services/calendar/calendar.service.ts +1077 -1077
- package/src/services/calendar/calendar.v2.service.ts +1683 -1683
- package/src/services/calendar/calendar.v3.service.ts +313 -313
- package/src/services/calendar/externalCalendar.service.ts +178 -178
- package/src/services/calendar/index.ts +5 -5
- package/src/services/calendar/synced-calendars.service.ts +743 -743
- package/src/services/calendar/utils/appointment.utils.ts +265 -265
- package/src/services/calendar/utils/calendar-event.utils.ts +646 -646
- package/src/services/calendar/utils/clinic.utils.ts +237 -237
- package/src/services/calendar/utils/docs.utils.ts +157 -157
- package/src/services/calendar/utils/google-calendar.utils.ts +697 -697
- package/src/services/calendar/utils/index.ts +8 -8
- package/src/services/calendar/utils/patient.utils.ts +198 -198
- package/src/services/calendar/utils/practitioner.utils.ts +221 -221
- package/src/services/calendar/utils/synced-calendar.utils.ts +472 -472
- package/src/services/clinic/README.md +204 -204
- package/src/services/clinic/__tests__/clinic-admin.service.test.ts +287 -287
- package/src/services/clinic/__tests__/clinic-group.service.test.ts +352 -352
- package/src/services/clinic/__tests__/clinic.service.test.ts +354 -354
- package/src/services/clinic/billing-transactions.service.ts +217 -217
- package/src/services/clinic/clinic-admin.service.ts +202 -202
- package/src/services/clinic/clinic-group.service.ts +310 -310
- package/src/services/clinic/clinic.service.ts +708 -708
- package/src/services/clinic/index.ts +5 -5
- package/src/services/clinic/practitioner-invite.service.ts +519 -519
- package/src/services/clinic/utils/admin.utils.ts +551 -551
- package/src/services/clinic/utils/clinic-group.utils.ts +646 -646
- package/src/services/clinic/utils/clinic.utils.ts +949 -949
- package/src/services/clinic/utils/filter.utils.d.ts +23 -23
- package/src/services/clinic/utils/filter.utils.ts +446 -446
- package/src/services/clinic/utils/index.ts +11 -11
- package/src/services/clinic/utils/photos.utils.ts +188 -188
- package/src/services/clinic/utils/search.utils.ts +84 -84
- package/src/services/clinic/utils/tag.utils.ts +124 -124
- package/src/services/documentation-templates/documentation-template.service.ts +537 -537
- package/src/services/documentation-templates/filled-document.service.ts +587 -587
- package/src/services/documentation-templates/index.ts +2 -2
- package/src/services/index.ts +13 -13
- package/src/services/media/index.ts +1 -1
- package/src/services/media/media.service.ts +418 -418
- package/src/services/notifications/__tests__/notification.service.test.ts +242 -242
- package/src/services/notifications/index.ts +1 -1
- package/src/services/notifications/notification.service.ts +215 -215
- package/src/services/patient/README.md +48 -48
- package/src/services/patient/To-Do.md +43 -43
- package/src/services/patient/__tests__/patient.service.test.ts +294 -294
- package/src/services/patient/index.ts +2 -2
- package/src/services/patient/patient.service.ts +883 -883
- package/src/services/patient/patientRequirements.service.ts +285 -285
- package/src/services/patient/utils/aesthetic-analysis.utils.ts +176 -176
- package/src/services/patient/utils/clinic.utils.ts +80 -80
- package/src/services/patient/utils/docs.utils.ts +142 -142
- package/src/services/patient/utils/index.ts +9 -9
- package/src/services/patient/utils/location.utils.ts +126 -126
- package/src/services/patient/utils/medical-stuff.utils.ts +143 -143
- package/src/services/patient/utils/medical.utils.ts +458 -458
- package/src/services/patient/utils/practitioner.utils.ts +260 -260
- package/src/services/patient/utils/profile.utils.ts +510 -510
- package/src/services/patient/utils/sensitive.utils.ts +260 -260
- package/src/services/patient/utils/token.utils.ts +211 -211
- package/src/services/practitioner/README.md +145 -145
- package/src/services/practitioner/index.ts +1 -1
- package/src/services/practitioner/practitioner.service.ts +1742 -1742
- package/src/services/procedure/README.md +163 -163
- package/src/services/procedure/index.ts +1 -1
- package/src/services/procedure/procedure.service.ts +1682 -1682
- package/src/services/reviews/index.ts +1 -1
- package/src/services/reviews/reviews.service.ts +636 -683
- package/src/services/user/index.ts +1 -1
- package/src/services/user/user.service.ts +489 -489
- package/src/services/user/user.v2.service.ts +466 -466
- package/src/types/appointment/index.ts +481 -453
- package/src/types/calendar/index.ts +258 -258
- package/src/types/calendar/synced-calendar.types.ts +66 -66
- package/src/types/clinic/index.ts +489 -489
- package/src/types/clinic/practitioner-invite.types.ts +91 -91
- package/src/types/clinic/preferences.types.ts +159 -159
- package/src/types/clinic/to-do +3 -3
- package/src/types/documentation-templates/index.ts +308 -308
- package/src/types/index.ts +44 -44
- package/src/types/notifications/README.md +77 -77
- package/src/types/notifications/index.ts +265 -265
- package/src/types/patient/aesthetic-analysis.types.ts +66 -66
- package/src/types/patient/allergies.ts +58 -58
- package/src/types/patient/index.ts +275 -273
- package/src/types/patient/medical-info.types.ts +152 -152
- package/src/types/patient/patient-requirements.ts +92 -92
- package/src/types/patient/token.types.ts +61 -61
- package/src/types/practitioner/index.ts +206 -206
- package/src/types/procedure/index.ts +181 -181
- package/src/types/profile/index.ts +39 -39
- package/src/types/reviews/index.ts +130 -132
- package/src/types/tz-lookup.d.ts +4 -4
- package/src/types/user/index.ts +38 -38
- package/src/utils/TIMESTAMPS.md +176 -176
- package/src/utils/TimestampUtils.ts +241 -241
- package/src/utils/index.ts +1 -1
- package/src/validations/appointment.schema.ts +574 -574
- package/src/validations/calendar.schema.ts +225 -225
- package/src/validations/clinic.schema.ts +493 -493
- package/src/validations/common.schema.ts +25 -25
- package/src/validations/documentation-templates/index.ts +1 -1
- package/src/validations/documentation-templates/template.schema.ts +220 -220
- package/src/validations/documentation-templates.schema.ts +10 -10
- package/src/validations/index.ts +20 -20
- package/src/validations/media.schema.ts +10 -10
- package/src/validations/notification.schema.ts +90 -90
- package/src/validations/patient/aesthetic-analysis.schema.ts +55 -55
- package/src/validations/patient/medical-info.schema.ts +125 -125
- package/src/validations/patient/patient-requirements.schema.ts +84 -84
- package/src/validations/patient/token.schema.ts +29 -29
- package/src/validations/patient.schema.ts +217 -216
- package/src/validations/practitioner.schema.ts +222 -222
- package/src/validations/procedure-product.schema.ts +41 -41
- package/src/validations/procedure.schema.ts +124 -124
- package/src/validations/profile-info.schema.ts +41 -41
- package/src/validations/reviews.schema.ts +189 -195
- package/src/validations/schemas.ts +104 -104
- package/src/validations/shared.schema.ts +78 -78
|
@@ -1,385 +1,385 @@
|
|
|
1
|
-
import {
|
|
2
|
-
arrayRemove,
|
|
3
|
-
arrayUnion,
|
|
4
|
-
collection,
|
|
5
|
-
doc,
|
|
6
|
-
getDoc,
|
|
7
|
-
setDoc,
|
|
8
|
-
updateDoc,
|
|
9
|
-
} from "firebase/firestore";
|
|
10
|
-
import { BaseService } from "../../services/base.service";
|
|
11
|
-
import {
|
|
12
|
-
ContraindicationDynamic,
|
|
13
|
-
ContraindicationsDocument,
|
|
14
|
-
TreatmentBenefitDynamic,
|
|
15
|
-
TreatmentBenefitsDocument,
|
|
16
|
-
} from "../types/admin-constants.types";
|
|
17
|
-
|
|
18
|
-
const ADMIN_CONSTANTS_COLLECTION = "admin-constants";
|
|
19
|
-
const TREATMENT_BENEFITS_DOC = "treatment-benefits";
|
|
20
|
-
const CONTRAINDICATIONS_DOC = "contraindications";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @class ConstantsService
|
|
24
|
-
* @description Service for managing administrative constants, such as treatment benefits and contraindications.
|
|
25
|
-
* These constants are stored in a single Firestore collection 'admin-constants',
|
|
26
|
-
* with each type of constant in its own document ('treatment-benefits', 'contraindications').
|
|
27
|
-
* The constants themselves are stored in an array within these documents.
|
|
28
|
-
* @extends {BaseService}
|
|
29
|
-
*/
|
|
30
|
-
export class ConstantsService extends BaseService {
|
|
31
|
-
/**
|
|
32
|
-
* @description Gets the reference to the document holding treatment benefits.
|
|
33
|
-
* @private
|
|
34
|
-
* @type {DocumentReference}
|
|
35
|
-
*/
|
|
36
|
-
private get treatmentBenefitsDocRef() {
|
|
37
|
-
return doc(this.db, ADMIN_CONSTANTS_COLLECTION, TREATMENT_BENEFITS_DOC);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @description Gets the reference to the document holding contraindications.
|
|
42
|
-
* @private
|
|
43
|
-
* @type {DocumentReference}
|
|
44
|
-
*/
|
|
45
|
-
private get contraindicationsDocRef() {
|
|
46
|
-
return doc(this.db, ADMIN_CONSTANTS_COLLECTION, CONTRAINDICATIONS_DOC);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// =================================================================
|
|
50
|
-
// Treatment Benefits
|
|
51
|
-
// =================================================================
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @description Retrieves all treatment benefits without pagination.
|
|
55
|
-
* @returns {Promise<TreatmentBenefitDynamic[]>} An array of all treatment benefits.
|
|
56
|
-
*/
|
|
57
|
-
async getAllBenefitsForFilter(): Promise<TreatmentBenefitDynamic[]> {
|
|
58
|
-
const docSnap = await getDoc(this.treatmentBenefitsDocRef);
|
|
59
|
-
if (!docSnap.exists()) {
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
return (docSnap.data() as TreatmentBenefitsDocument).benefits;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @description Retrieves a paginated list of treatment benefits.
|
|
67
|
-
* @param {{ page: number; limit: number }} options - Pagination options.
|
|
68
|
-
* @returns {Promise<{ benefits: TreatmentBenefitDynamic[]; total: number }>} A paginated list of benefits and the total count.
|
|
69
|
-
*/
|
|
70
|
-
async getAllBenefits(options: {
|
|
71
|
-
page: number;
|
|
72
|
-
limit: number;
|
|
73
|
-
}): Promise<{ benefits: TreatmentBenefitDynamic[]; total: number }> {
|
|
74
|
-
const allBenefits = await this.getAllBenefitsForFilter();
|
|
75
|
-
const { page, limit } = options;
|
|
76
|
-
const startIndex = page * limit;
|
|
77
|
-
const endIndex = startIndex + limit;
|
|
78
|
-
const paginatedBenefits = allBenefits.slice(startIndex, endIndex);
|
|
79
|
-
return { benefits: paginatedBenefits, total: allBenefits.length };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @description Adds a new treatment benefit.
|
|
84
|
-
* @param {Omit<TreatmentBenefitDynamic, "id">} benefit - The treatment benefit to add, without an ID.
|
|
85
|
-
* @returns {Promise<TreatmentBenefitDynamic>} The newly created treatment benefit with its generated ID.
|
|
86
|
-
*/
|
|
87
|
-
async addTreatmentBenefit(
|
|
88
|
-
benefit: Omit<TreatmentBenefitDynamic, "id">
|
|
89
|
-
): Promise<TreatmentBenefitDynamic> {
|
|
90
|
-
const newBenefit: TreatmentBenefitDynamic = {
|
|
91
|
-
id: this.generateId(),
|
|
92
|
-
...benefit,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const docSnap = await getDoc(this.treatmentBenefitsDocRef);
|
|
96
|
-
if (!docSnap.exists()) {
|
|
97
|
-
await setDoc(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
|
|
98
|
-
} else {
|
|
99
|
-
await updateDoc(this.treatmentBenefitsDocRef, {
|
|
100
|
-
benefits: arrayUnion(newBenefit),
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return newBenefit;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @description Retrieves a single treatment benefit by its ID.
|
|
109
|
-
* @param {string} benefitId - The ID of the treatment benefit to retrieve.
|
|
110
|
-
* @returns {Promise<TreatmentBenefitDynamic | undefined>} The found treatment benefit or undefined.
|
|
111
|
-
*/
|
|
112
|
-
async getBenefitById(
|
|
113
|
-
benefitId: string
|
|
114
|
-
): Promise<TreatmentBenefitDynamic | undefined> {
|
|
115
|
-
const benefits = await this.getAllBenefitsForFilter();
|
|
116
|
-
return benefits.find((b) => b.id === benefitId);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @description Searches for treatment benefits by name (case-insensitive).
|
|
121
|
-
* @param {string} searchTerm - The term to search for in the benefit names.
|
|
122
|
-
* @returns {Promise<TreatmentBenefitDynamic[]>} An array of matching treatment benefits.
|
|
123
|
-
*/
|
|
124
|
-
async searchBenefitsByName(
|
|
125
|
-
searchTerm: string
|
|
126
|
-
): Promise<TreatmentBenefitDynamic[]> {
|
|
127
|
-
const benefits = await this.getAllBenefitsForFilter();
|
|
128
|
-
const normalizedSearchTerm = searchTerm.toLowerCase();
|
|
129
|
-
return benefits.filter((b) =>
|
|
130
|
-
b.name.toLowerCase().includes(normalizedSearchTerm)
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* @description Updates an existing treatment benefit.
|
|
136
|
-
* @param {TreatmentBenefitDynamic} benefit - The treatment benefit with updated data. Its ID must match an existing benefit.
|
|
137
|
-
* @returns {Promise<TreatmentBenefitDynamic>} The updated treatment benefit.
|
|
138
|
-
* @throws {Error} If the treatment benefit is not found.
|
|
139
|
-
*/
|
|
140
|
-
async updateTreatmentBenefit(
|
|
141
|
-
benefit: TreatmentBenefitDynamic
|
|
142
|
-
): Promise<TreatmentBenefitDynamic> {
|
|
143
|
-
const benefits = await this.getAllBenefitsForFilter();
|
|
144
|
-
const benefitIndex = benefits.findIndex((b) => b.id === benefit.id);
|
|
145
|
-
|
|
146
|
-
if (benefitIndex === -1) {
|
|
147
|
-
throw new Error("Treatment benefit not found.");
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
benefits[benefitIndex] = benefit;
|
|
151
|
-
|
|
152
|
-
await updateDoc(this.treatmentBenefitsDocRef, { benefits });
|
|
153
|
-
return benefit;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* @description Deletes a treatment benefit by its ID.
|
|
158
|
-
* @param {string} benefitId - The ID of the treatment benefit to delete.
|
|
159
|
-
* @returns {Promise<void>}
|
|
160
|
-
*/
|
|
161
|
-
async deleteTreatmentBenefit(benefitId: string): Promise<void> {
|
|
162
|
-
const benefits = await this.getAllBenefitsForFilter();
|
|
163
|
-
const benefitToRemove = benefits.find((b) => b.id === benefitId);
|
|
164
|
-
|
|
165
|
-
if (!benefitToRemove) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
await updateDoc(this.treatmentBenefitsDocRef, {
|
|
170
|
-
benefits: arrayRemove(benefitToRemove),
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// =================================================================
|
|
175
|
-
// Contraindications
|
|
176
|
-
// =================================================================
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* @description Retrieves all contraindications without pagination.
|
|
180
|
-
* @returns {Promise<ContraindicationDynamic[]>} An array of all contraindications.
|
|
181
|
-
*/
|
|
182
|
-
async getAllContraindicationsForFilter(): Promise<ContraindicationDynamic[]> {
|
|
183
|
-
const docSnap = await getDoc(this.contraindicationsDocRef);
|
|
184
|
-
if (!docSnap.exists()) {
|
|
185
|
-
return [];
|
|
186
|
-
}
|
|
187
|
-
return (docSnap.data() as ContraindicationsDocument).contraindications;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* @description Retrieves a paginated list of contraindications.
|
|
192
|
-
* @param {{ page: number; limit: number }} options - Pagination options.
|
|
193
|
-
* @returns {Promise<{ contraindications: ContraindicationDynamic[]; total: number }>} A paginated list and the total count.
|
|
194
|
-
*/
|
|
195
|
-
async getAllContraindications(options: {
|
|
196
|
-
page: number;
|
|
197
|
-
limit: number;
|
|
198
|
-
}): Promise<{ contraindications: ContraindicationDynamic[]; total: number }> {
|
|
199
|
-
const allContraindications = await this.getAllContraindicationsForFilter();
|
|
200
|
-
const { page, limit } = options;
|
|
201
|
-
const startIndex = page * limit;
|
|
202
|
-
const endIndex = startIndex + limit;
|
|
203
|
-
const paginatedContraindications = allContraindications.slice(
|
|
204
|
-
startIndex,
|
|
205
|
-
endIndex
|
|
206
|
-
);
|
|
207
|
-
return {
|
|
208
|
-
contraindications: paginatedContraindications,
|
|
209
|
-
total: allContraindications.length,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* @description Adds a new contraindication.
|
|
215
|
-
* @param {Omit<ContraindicationDynamic, "id">} contraindication - The contraindication to add, without an ID.
|
|
216
|
-
* @returns {Promise<ContraindicationDynamic>} The newly created contraindication with its generated ID.
|
|
217
|
-
*/
|
|
218
|
-
async addContraindication(
|
|
219
|
-
contraindication: Omit<ContraindicationDynamic, "id">
|
|
220
|
-
): Promise<ContraindicationDynamic> {
|
|
221
|
-
const newContraindication: ContraindicationDynamic = {
|
|
222
|
-
id: this.generateId(),
|
|
223
|
-
...contraindication,
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const docSnap = await getDoc(this.contraindicationsDocRef);
|
|
227
|
-
if (!docSnap.exists()) {
|
|
228
|
-
await setDoc(this.contraindicationsDocRef, {
|
|
229
|
-
contraindications: [newContraindication],
|
|
230
|
-
});
|
|
231
|
-
} else {
|
|
232
|
-
await updateDoc(this.contraindicationsDocRef, {
|
|
233
|
-
contraindications: arrayUnion(newContraindication),
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return newContraindication;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* @description Retrieves a single contraindication by its ID.
|
|
242
|
-
* @param {string} contraindicationId - The ID of the contraindication to retrieve.
|
|
243
|
-
* @returns {Promise<ContraindicationDynamic | undefined>} The found contraindication or undefined.
|
|
244
|
-
*/
|
|
245
|
-
async getContraindicationById(
|
|
246
|
-
contraindicationId: string
|
|
247
|
-
): Promise<ContraindicationDynamic | undefined> {
|
|
248
|
-
const contraindications = await this.getAllContraindicationsForFilter();
|
|
249
|
-
return contraindications.find((c) => c.id === contraindicationId);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* @description Searches for contraindications by name (case-insensitive).
|
|
254
|
-
* @param {string} searchTerm - The term to search for in the contraindication names.
|
|
255
|
-
* @returns {Promise<ContraindicationDynamic[]>} An array of matching contraindications.
|
|
256
|
-
*/
|
|
257
|
-
async searchContraindicationsByName(
|
|
258
|
-
searchTerm: string
|
|
259
|
-
): Promise<ContraindicationDynamic[]> {
|
|
260
|
-
const contraindications = await this.getAllContraindicationsForFilter();
|
|
261
|
-
const normalizedSearchTerm = searchTerm.toLowerCase();
|
|
262
|
-
return contraindications.filter((c) =>
|
|
263
|
-
c.name.toLowerCase().includes(normalizedSearchTerm)
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* @description Updates an existing contraindication.
|
|
269
|
-
* @param {ContraindicationDynamic} contraindication - The contraindication with updated data. Its ID must match an existing one.
|
|
270
|
-
* @returns {Promise<ContraindicationDynamic>} The updated contraindication.
|
|
271
|
-
* @throws {Error} If the contraindication is not found.
|
|
272
|
-
*/
|
|
273
|
-
async updateContraindication(
|
|
274
|
-
contraindication: ContraindicationDynamic
|
|
275
|
-
): Promise<ContraindicationDynamic> {
|
|
276
|
-
const contraindications = await this.getAllContraindicationsForFilter();
|
|
277
|
-
const index = contraindications.findIndex(
|
|
278
|
-
(c) => c.id === contraindication.id
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
if (index === -1) {
|
|
282
|
-
throw new Error("Contraindication not found.");
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
contraindications[index] = contraindication;
|
|
286
|
-
|
|
287
|
-
await updateDoc(this.contraindicationsDocRef, { contraindications });
|
|
288
|
-
return contraindication;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* @description Deletes a contraindication by its ID.
|
|
293
|
-
* @param {string} contraindicationId - The ID of the contraindication to delete.
|
|
294
|
-
* @returns {Promise<void>}
|
|
295
|
-
*/
|
|
296
|
-
async deleteContraindication(contraindicationId: string): Promise<void> {
|
|
297
|
-
const contraindications = await this.getAllContraindicationsForFilter();
|
|
298
|
-
const toRemove = contraindications.find((c) => c.id === contraindicationId);
|
|
299
|
-
|
|
300
|
-
if (!toRemove) {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
await updateDoc(this.contraindicationsDocRef, {
|
|
305
|
-
contraindications: arrayRemove(toRemove),
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// =================================================================
|
|
310
|
-
// CSV Export Methods
|
|
311
|
-
// =================================================================
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Exports treatment benefits to CSV string, suitable for Excel/Sheets.
|
|
315
|
-
* Includes headers and optional UTF-8 BOM.
|
|
316
|
-
*/
|
|
317
|
-
async exportBenefitsToCsv(options?: {
|
|
318
|
-
includeBom?: boolean;
|
|
319
|
-
}): Promise<string> {
|
|
320
|
-
const includeBom = options?.includeBom ?? true;
|
|
321
|
-
|
|
322
|
-
const headers = ["id", "name", "description"];
|
|
323
|
-
|
|
324
|
-
const rows: string[] = [];
|
|
325
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
326
|
-
|
|
327
|
-
const benefits = await this.getAllBenefitsForFilter();
|
|
328
|
-
|
|
329
|
-
for (const benefit of benefits) {
|
|
330
|
-
rows.push(this.benefitToCsvRow(benefit));
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const csvBody = rows.join("\r\n");
|
|
334
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Exports contraindications to CSV string, suitable for Excel/Sheets.
|
|
339
|
-
* Includes headers and optional UTF-8 BOM.
|
|
340
|
-
*/
|
|
341
|
-
async exportContraindicationsToCsv(options?: {
|
|
342
|
-
includeBom?: boolean;
|
|
343
|
-
}): Promise<string> {
|
|
344
|
-
const includeBom = options?.includeBom ?? true;
|
|
345
|
-
|
|
346
|
-
const headers = ["id", "name", "description"];
|
|
347
|
-
|
|
348
|
-
const rows: string[] = [];
|
|
349
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
350
|
-
|
|
351
|
-
const contraindications = await this.getAllContraindicationsForFilter();
|
|
352
|
-
|
|
353
|
-
for (const contraindication of contraindications) {
|
|
354
|
-
rows.push(this.contraindicationToCsvRow(contraindication));
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const csvBody = rows.join("\r\n");
|
|
358
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
private benefitToCsvRow(benefit: TreatmentBenefitDynamic): string {
|
|
362
|
-
const values = [
|
|
363
|
-
benefit.id ?? "",
|
|
364
|
-
benefit.name ?? "",
|
|
365
|
-
benefit.description ?? "",
|
|
366
|
-
];
|
|
367
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
private contraindicationToCsvRow(contraindication: ContraindicationDynamic): string {
|
|
371
|
-
const values = [
|
|
372
|
-
contraindication.id ?? "",
|
|
373
|
-
contraindication.name ?? "",
|
|
374
|
-
contraindication.description ?? "",
|
|
375
|
-
];
|
|
376
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
private formatCsvValue(value: any): string {
|
|
380
|
-
const str = value === null || value === undefined ? "" : String(value);
|
|
381
|
-
// Escape double quotes by doubling them and wrap in quotes
|
|
382
|
-
const escaped = str.replace(/"/g, '""');
|
|
383
|
-
return `"${escaped}"`;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
arrayRemove,
|
|
3
|
+
arrayUnion,
|
|
4
|
+
collection,
|
|
5
|
+
doc,
|
|
6
|
+
getDoc,
|
|
7
|
+
setDoc,
|
|
8
|
+
updateDoc,
|
|
9
|
+
} from "firebase/firestore";
|
|
10
|
+
import { BaseService } from "../../services/base.service";
|
|
11
|
+
import {
|
|
12
|
+
ContraindicationDynamic,
|
|
13
|
+
ContraindicationsDocument,
|
|
14
|
+
TreatmentBenefitDynamic,
|
|
15
|
+
TreatmentBenefitsDocument,
|
|
16
|
+
} from "../types/admin-constants.types";
|
|
17
|
+
|
|
18
|
+
const ADMIN_CONSTANTS_COLLECTION = "admin-constants";
|
|
19
|
+
const TREATMENT_BENEFITS_DOC = "treatment-benefits";
|
|
20
|
+
const CONTRAINDICATIONS_DOC = "contraindications";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @class ConstantsService
|
|
24
|
+
* @description Service for managing administrative constants, such as treatment benefits and contraindications.
|
|
25
|
+
* These constants are stored in a single Firestore collection 'admin-constants',
|
|
26
|
+
* with each type of constant in its own document ('treatment-benefits', 'contraindications').
|
|
27
|
+
* The constants themselves are stored in an array within these documents.
|
|
28
|
+
* @extends {BaseService}
|
|
29
|
+
*/
|
|
30
|
+
export class ConstantsService extends BaseService {
|
|
31
|
+
/**
|
|
32
|
+
* @description Gets the reference to the document holding treatment benefits.
|
|
33
|
+
* @private
|
|
34
|
+
* @type {DocumentReference}
|
|
35
|
+
*/
|
|
36
|
+
private get treatmentBenefitsDocRef() {
|
|
37
|
+
return doc(this.db, ADMIN_CONSTANTS_COLLECTION, TREATMENT_BENEFITS_DOC);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @description Gets the reference to the document holding contraindications.
|
|
42
|
+
* @private
|
|
43
|
+
* @type {DocumentReference}
|
|
44
|
+
*/
|
|
45
|
+
private get contraindicationsDocRef() {
|
|
46
|
+
return doc(this.db, ADMIN_CONSTANTS_COLLECTION, CONTRAINDICATIONS_DOC);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// =================================================================
|
|
50
|
+
// Treatment Benefits
|
|
51
|
+
// =================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @description Retrieves all treatment benefits without pagination.
|
|
55
|
+
* @returns {Promise<TreatmentBenefitDynamic[]>} An array of all treatment benefits.
|
|
56
|
+
*/
|
|
57
|
+
async getAllBenefitsForFilter(): Promise<TreatmentBenefitDynamic[]> {
|
|
58
|
+
const docSnap = await getDoc(this.treatmentBenefitsDocRef);
|
|
59
|
+
if (!docSnap.exists()) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return (docSnap.data() as TreatmentBenefitsDocument).benefits;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @description Retrieves a paginated list of treatment benefits.
|
|
67
|
+
* @param {{ page: number; limit: number }} options - Pagination options.
|
|
68
|
+
* @returns {Promise<{ benefits: TreatmentBenefitDynamic[]; total: number }>} A paginated list of benefits and the total count.
|
|
69
|
+
*/
|
|
70
|
+
async getAllBenefits(options: {
|
|
71
|
+
page: number;
|
|
72
|
+
limit: number;
|
|
73
|
+
}): Promise<{ benefits: TreatmentBenefitDynamic[]; total: number }> {
|
|
74
|
+
const allBenefits = await this.getAllBenefitsForFilter();
|
|
75
|
+
const { page, limit } = options;
|
|
76
|
+
const startIndex = page * limit;
|
|
77
|
+
const endIndex = startIndex + limit;
|
|
78
|
+
const paginatedBenefits = allBenefits.slice(startIndex, endIndex);
|
|
79
|
+
return { benefits: paginatedBenefits, total: allBenefits.length };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @description Adds a new treatment benefit.
|
|
84
|
+
* @param {Omit<TreatmentBenefitDynamic, "id">} benefit - The treatment benefit to add, without an ID.
|
|
85
|
+
* @returns {Promise<TreatmentBenefitDynamic>} The newly created treatment benefit with its generated ID.
|
|
86
|
+
*/
|
|
87
|
+
async addTreatmentBenefit(
|
|
88
|
+
benefit: Omit<TreatmentBenefitDynamic, "id">
|
|
89
|
+
): Promise<TreatmentBenefitDynamic> {
|
|
90
|
+
const newBenefit: TreatmentBenefitDynamic = {
|
|
91
|
+
id: this.generateId(),
|
|
92
|
+
...benefit,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const docSnap = await getDoc(this.treatmentBenefitsDocRef);
|
|
96
|
+
if (!docSnap.exists()) {
|
|
97
|
+
await setDoc(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
|
|
98
|
+
} else {
|
|
99
|
+
await updateDoc(this.treatmentBenefitsDocRef, {
|
|
100
|
+
benefits: arrayUnion(newBenefit),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return newBenefit;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @description Retrieves a single treatment benefit by its ID.
|
|
109
|
+
* @param {string} benefitId - The ID of the treatment benefit to retrieve.
|
|
110
|
+
* @returns {Promise<TreatmentBenefitDynamic | undefined>} The found treatment benefit or undefined.
|
|
111
|
+
*/
|
|
112
|
+
async getBenefitById(
|
|
113
|
+
benefitId: string
|
|
114
|
+
): Promise<TreatmentBenefitDynamic | undefined> {
|
|
115
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
116
|
+
return benefits.find((b) => b.id === benefitId);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @description Searches for treatment benefits by name (case-insensitive).
|
|
121
|
+
* @param {string} searchTerm - The term to search for in the benefit names.
|
|
122
|
+
* @returns {Promise<TreatmentBenefitDynamic[]>} An array of matching treatment benefits.
|
|
123
|
+
*/
|
|
124
|
+
async searchBenefitsByName(
|
|
125
|
+
searchTerm: string
|
|
126
|
+
): Promise<TreatmentBenefitDynamic[]> {
|
|
127
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
128
|
+
const normalizedSearchTerm = searchTerm.toLowerCase();
|
|
129
|
+
return benefits.filter((b) =>
|
|
130
|
+
b.name.toLowerCase().includes(normalizedSearchTerm)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @description Updates an existing treatment benefit.
|
|
136
|
+
* @param {TreatmentBenefitDynamic} benefit - The treatment benefit with updated data. Its ID must match an existing benefit.
|
|
137
|
+
* @returns {Promise<TreatmentBenefitDynamic>} The updated treatment benefit.
|
|
138
|
+
* @throws {Error} If the treatment benefit is not found.
|
|
139
|
+
*/
|
|
140
|
+
async updateTreatmentBenefit(
|
|
141
|
+
benefit: TreatmentBenefitDynamic
|
|
142
|
+
): Promise<TreatmentBenefitDynamic> {
|
|
143
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
144
|
+
const benefitIndex = benefits.findIndex((b) => b.id === benefit.id);
|
|
145
|
+
|
|
146
|
+
if (benefitIndex === -1) {
|
|
147
|
+
throw new Error("Treatment benefit not found.");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
benefits[benefitIndex] = benefit;
|
|
151
|
+
|
|
152
|
+
await updateDoc(this.treatmentBenefitsDocRef, { benefits });
|
|
153
|
+
return benefit;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @description Deletes a treatment benefit by its ID.
|
|
158
|
+
* @param {string} benefitId - The ID of the treatment benefit to delete.
|
|
159
|
+
* @returns {Promise<void>}
|
|
160
|
+
*/
|
|
161
|
+
async deleteTreatmentBenefit(benefitId: string): Promise<void> {
|
|
162
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
163
|
+
const benefitToRemove = benefits.find((b) => b.id === benefitId);
|
|
164
|
+
|
|
165
|
+
if (!benefitToRemove) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
await updateDoc(this.treatmentBenefitsDocRef, {
|
|
170
|
+
benefits: arrayRemove(benefitToRemove),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// =================================================================
|
|
175
|
+
// Contraindications
|
|
176
|
+
// =================================================================
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @description Retrieves all contraindications without pagination.
|
|
180
|
+
* @returns {Promise<ContraindicationDynamic[]>} An array of all contraindications.
|
|
181
|
+
*/
|
|
182
|
+
async getAllContraindicationsForFilter(): Promise<ContraindicationDynamic[]> {
|
|
183
|
+
const docSnap = await getDoc(this.contraindicationsDocRef);
|
|
184
|
+
if (!docSnap.exists()) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
return (docSnap.data() as ContraindicationsDocument).contraindications;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @description Retrieves a paginated list of contraindications.
|
|
192
|
+
* @param {{ page: number; limit: number }} options - Pagination options.
|
|
193
|
+
* @returns {Promise<{ contraindications: ContraindicationDynamic[]; total: number }>} A paginated list and the total count.
|
|
194
|
+
*/
|
|
195
|
+
async getAllContraindications(options: {
|
|
196
|
+
page: number;
|
|
197
|
+
limit: number;
|
|
198
|
+
}): Promise<{ contraindications: ContraindicationDynamic[]; total: number }> {
|
|
199
|
+
const allContraindications = await this.getAllContraindicationsForFilter();
|
|
200
|
+
const { page, limit } = options;
|
|
201
|
+
const startIndex = page * limit;
|
|
202
|
+
const endIndex = startIndex + limit;
|
|
203
|
+
const paginatedContraindications = allContraindications.slice(
|
|
204
|
+
startIndex,
|
|
205
|
+
endIndex
|
|
206
|
+
);
|
|
207
|
+
return {
|
|
208
|
+
contraindications: paginatedContraindications,
|
|
209
|
+
total: allContraindications.length,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @description Adds a new contraindication.
|
|
215
|
+
* @param {Omit<ContraindicationDynamic, "id">} contraindication - The contraindication to add, without an ID.
|
|
216
|
+
* @returns {Promise<ContraindicationDynamic>} The newly created contraindication with its generated ID.
|
|
217
|
+
*/
|
|
218
|
+
async addContraindication(
|
|
219
|
+
contraindication: Omit<ContraindicationDynamic, "id">
|
|
220
|
+
): Promise<ContraindicationDynamic> {
|
|
221
|
+
const newContraindication: ContraindicationDynamic = {
|
|
222
|
+
id: this.generateId(),
|
|
223
|
+
...contraindication,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const docSnap = await getDoc(this.contraindicationsDocRef);
|
|
227
|
+
if (!docSnap.exists()) {
|
|
228
|
+
await setDoc(this.contraindicationsDocRef, {
|
|
229
|
+
contraindications: [newContraindication],
|
|
230
|
+
});
|
|
231
|
+
} else {
|
|
232
|
+
await updateDoc(this.contraindicationsDocRef, {
|
|
233
|
+
contraindications: arrayUnion(newContraindication),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return newContraindication;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* @description Retrieves a single contraindication by its ID.
|
|
242
|
+
* @param {string} contraindicationId - The ID of the contraindication to retrieve.
|
|
243
|
+
* @returns {Promise<ContraindicationDynamic | undefined>} The found contraindication or undefined.
|
|
244
|
+
*/
|
|
245
|
+
async getContraindicationById(
|
|
246
|
+
contraindicationId: string
|
|
247
|
+
): Promise<ContraindicationDynamic | undefined> {
|
|
248
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
249
|
+
return contraindications.find((c) => c.id === contraindicationId);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @description Searches for contraindications by name (case-insensitive).
|
|
254
|
+
* @param {string} searchTerm - The term to search for in the contraindication names.
|
|
255
|
+
* @returns {Promise<ContraindicationDynamic[]>} An array of matching contraindications.
|
|
256
|
+
*/
|
|
257
|
+
async searchContraindicationsByName(
|
|
258
|
+
searchTerm: string
|
|
259
|
+
): Promise<ContraindicationDynamic[]> {
|
|
260
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
261
|
+
const normalizedSearchTerm = searchTerm.toLowerCase();
|
|
262
|
+
return contraindications.filter((c) =>
|
|
263
|
+
c.name.toLowerCase().includes(normalizedSearchTerm)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @description Updates an existing contraindication.
|
|
269
|
+
* @param {ContraindicationDynamic} contraindication - The contraindication with updated data. Its ID must match an existing one.
|
|
270
|
+
* @returns {Promise<ContraindicationDynamic>} The updated contraindication.
|
|
271
|
+
* @throws {Error} If the contraindication is not found.
|
|
272
|
+
*/
|
|
273
|
+
async updateContraindication(
|
|
274
|
+
contraindication: ContraindicationDynamic
|
|
275
|
+
): Promise<ContraindicationDynamic> {
|
|
276
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
277
|
+
const index = contraindications.findIndex(
|
|
278
|
+
(c) => c.id === contraindication.id
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
if (index === -1) {
|
|
282
|
+
throw new Error("Contraindication not found.");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
contraindications[index] = contraindication;
|
|
286
|
+
|
|
287
|
+
await updateDoc(this.contraindicationsDocRef, { contraindications });
|
|
288
|
+
return contraindication;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @description Deletes a contraindication by its ID.
|
|
293
|
+
* @param {string} contraindicationId - The ID of the contraindication to delete.
|
|
294
|
+
* @returns {Promise<void>}
|
|
295
|
+
*/
|
|
296
|
+
async deleteContraindication(contraindicationId: string): Promise<void> {
|
|
297
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
298
|
+
const toRemove = contraindications.find((c) => c.id === contraindicationId);
|
|
299
|
+
|
|
300
|
+
if (!toRemove) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await updateDoc(this.contraindicationsDocRef, {
|
|
305
|
+
contraindications: arrayRemove(toRemove),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// =================================================================
|
|
310
|
+
// CSV Export Methods
|
|
311
|
+
// =================================================================
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Exports treatment benefits to CSV string, suitable for Excel/Sheets.
|
|
315
|
+
* Includes headers and optional UTF-8 BOM.
|
|
316
|
+
*/
|
|
317
|
+
async exportBenefitsToCsv(options?: {
|
|
318
|
+
includeBom?: boolean;
|
|
319
|
+
}): Promise<string> {
|
|
320
|
+
const includeBom = options?.includeBom ?? true;
|
|
321
|
+
|
|
322
|
+
const headers = ["id", "name", "description"];
|
|
323
|
+
|
|
324
|
+
const rows: string[] = [];
|
|
325
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
326
|
+
|
|
327
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
328
|
+
|
|
329
|
+
for (const benefit of benefits) {
|
|
330
|
+
rows.push(this.benefitToCsvRow(benefit));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const csvBody = rows.join("\r\n");
|
|
334
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Exports contraindications to CSV string, suitable for Excel/Sheets.
|
|
339
|
+
* Includes headers and optional UTF-8 BOM.
|
|
340
|
+
*/
|
|
341
|
+
async exportContraindicationsToCsv(options?: {
|
|
342
|
+
includeBom?: boolean;
|
|
343
|
+
}): Promise<string> {
|
|
344
|
+
const includeBom = options?.includeBom ?? true;
|
|
345
|
+
|
|
346
|
+
const headers = ["id", "name", "description"];
|
|
347
|
+
|
|
348
|
+
const rows: string[] = [];
|
|
349
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
350
|
+
|
|
351
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
352
|
+
|
|
353
|
+
for (const contraindication of contraindications) {
|
|
354
|
+
rows.push(this.contraindicationToCsvRow(contraindication));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const csvBody = rows.join("\r\n");
|
|
358
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private benefitToCsvRow(benefit: TreatmentBenefitDynamic): string {
|
|
362
|
+
const values = [
|
|
363
|
+
benefit.id ?? "",
|
|
364
|
+
benefit.name ?? "",
|
|
365
|
+
benefit.description ?? "",
|
|
366
|
+
];
|
|
367
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private contraindicationToCsvRow(contraindication: ContraindicationDynamic): string {
|
|
371
|
+
const values = [
|
|
372
|
+
contraindication.id ?? "",
|
|
373
|
+
contraindication.name ?? "",
|
|
374
|
+
contraindication.description ?? "",
|
|
375
|
+
];
|
|
376
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private formatCsvValue(value: any): string {
|
|
380
|
+
const str = value === null || value === undefined ? "" : String(value);
|
|
381
|
+
// Escape double quotes by doubling them and wrap in quotes
|
|
382
|
+
const escaped = str.replace(/"/g, '""');
|
|
383
|
+
return `"${escaped}"`;
|
|
384
|
+
}
|
|
385
|
+
}
|