@blackcode_sa/metaestetics-api 1.12.61 → 1.12.62

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.
Files changed (269) hide show
  1. package/dist/admin/index.d.mts +2 -0
  2. package/dist/admin/index.d.ts +2 -0
  3. package/dist/admin/index.js +45 -4
  4. package/dist/admin/index.mjs +45 -4
  5. package/dist/index.d.mts +2 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +53 -11
  8. package/dist/index.mjs +53 -11
  9. package/package.json +119 -119
  10. package/src/__mocks__/firstore.ts +10 -10
  11. package/src/admin/aggregation/README.md +79 -79
  12. package/src/admin/aggregation/appointment/README.md +128 -128
  13. package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +1844 -1844
  14. package/src/admin/aggregation/appointment/index.ts +1 -1
  15. package/src/admin/aggregation/clinic/README.md +52 -52
  16. package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +703 -703
  17. package/src/admin/aggregation/clinic/index.ts +1 -1
  18. package/src/admin/aggregation/forms/README.md +13 -13
  19. package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +322 -322
  20. package/src/admin/aggregation/forms/index.ts +1 -1
  21. package/src/admin/aggregation/index.ts +8 -8
  22. package/src/admin/aggregation/patient/README.md +27 -27
  23. package/src/admin/aggregation/patient/index.ts +1 -1
  24. package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -141
  25. package/src/admin/aggregation/practitioner/README.md +42 -42
  26. package/src/admin/aggregation/practitioner/index.ts +1 -1
  27. package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -433
  28. package/src/admin/aggregation/practitioner-invite/index.ts +1 -1
  29. package/src/admin/aggregation/practitioner-invite/practitioner-invite.aggregation.service.ts +961 -961
  30. package/src/admin/aggregation/procedure/README.md +43 -43
  31. package/src/admin/aggregation/procedure/index.ts +1 -1
  32. package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +702 -702
  33. package/src/admin/aggregation/reviews/index.ts +1 -1
  34. package/src/admin/aggregation/reviews/reviews.aggregation.service.ts +689 -641
  35. package/src/admin/booking/README.md +125 -125
  36. package/src/admin/booking/booking.admin.ts +1037 -1037
  37. package/src/admin/booking/booking.calculator.ts +712 -712
  38. package/src/admin/booking/booking.types.ts +59 -59
  39. package/src/admin/booking/index.ts +3 -3
  40. package/src/admin/booking/timezones-problem.md +185 -185
  41. package/src/admin/calendar/README.md +7 -7
  42. package/src/admin/calendar/calendar.admin.service.ts +345 -345
  43. package/src/admin/calendar/index.ts +1 -1
  44. package/src/admin/documentation-templates/document-manager.admin.ts +260 -260
  45. package/src/admin/documentation-templates/index.ts +1 -1
  46. package/src/admin/free-consultation/free-consultation-utils.admin.ts +148 -148
  47. package/src/admin/free-consultation/index.ts +1 -1
  48. package/src/admin/index.ts +75 -75
  49. package/src/admin/logger/index.ts +78 -78
  50. package/src/admin/mailing/README.md +95 -95
  51. package/src/admin/mailing/appointment/appointment.mailing.service.ts +732 -732
  52. package/src/admin/mailing/appointment/index.ts +1 -1
  53. package/src/admin/mailing/appointment/templates/patient/appointment-confirmed.html +40 -40
  54. package/src/admin/mailing/base.mailing.service.ts +208 -208
  55. package/src/admin/mailing/index.ts +3 -3
  56. package/src/admin/mailing/practitionerInvite/existing-practitioner-invite.mailing.ts +611 -611
  57. package/src/admin/mailing/practitionerInvite/index.ts +2 -2
  58. package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +395 -395
  59. package/src/admin/mailing/practitionerInvite/templates/existing-practitioner-invitation.template.ts +155 -155
  60. package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -101
  61. package/src/admin/mailing/practitionerInvite/templates/invite-accepted-notification.template.ts +228 -228
  62. package/src/admin/mailing/practitionerInvite/templates/invite-rejected-notification.template.ts +242 -242
  63. package/src/admin/notifications/index.ts +1 -1
  64. package/src/admin/notifications/notifications.admin.ts +710 -710
  65. package/src/admin/requirements/README.md +128 -128
  66. package/src/admin/requirements/index.ts +1 -1
  67. package/src/admin/requirements/patient-requirements.admin.service.ts +475 -475
  68. package/src/admin/users/index.ts +1 -1
  69. package/src/admin/users/user-profile.admin.ts +405 -405
  70. package/src/backoffice/constants/certification.constants.ts +13 -13
  71. package/src/backoffice/constants/index.ts +1 -1
  72. package/src/backoffice/errors/backoffice.errors.ts +181 -181
  73. package/src/backoffice/errors/index.ts +1 -1
  74. package/src/backoffice/expo-safe/README.md +26 -26
  75. package/src/backoffice/expo-safe/index.ts +41 -41
  76. package/src/backoffice/index.ts +5 -5
  77. package/src/backoffice/services/FIXES_README.md +102 -102
  78. package/src/backoffice/services/README.md +40 -40
  79. package/src/backoffice/services/brand.service.ts +256 -256
  80. package/src/backoffice/services/category.service.ts +318 -318
  81. package/src/backoffice/services/constants.service.ts +385 -385
  82. package/src/backoffice/services/documentation-template.service.ts +202 -202
  83. package/src/backoffice/services/index.ts +8 -8
  84. package/src/backoffice/services/migrate-products.ts +116 -116
  85. package/src/backoffice/services/product.service.ts +553 -553
  86. package/src/backoffice/services/requirement.service.ts +235 -235
  87. package/src/backoffice/services/subcategory.service.ts +395 -395
  88. package/src/backoffice/services/technology.service.ts +1070 -1070
  89. package/src/backoffice/types/README.md +12 -12
  90. package/src/backoffice/types/admin-constants.types.ts +69 -69
  91. package/src/backoffice/types/brand.types.ts +29 -29
  92. package/src/backoffice/types/category.types.ts +62 -62
  93. package/src/backoffice/types/documentation-templates.types.ts +28 -28
  94. package/src/backoffice/types/index.ts +10 -10
  95. package/src/backoffice/types/procedure-product.types.ts +38 -38
  96. package/src/backoffice/types/product.types.ts +240 -240
  97. package/src/backoffice/types/requirement.types.ts +63 -63
  98. package/src/backoffice/types/static/README.md +18 -18
  99. package/src/backoffice/types/static/blocking-condition.types.ts +21 -21
  100. package/src/backoffice/types/static/certification.types.ts +37 -37
  101. package/src/backoffice/types/static/contraindication.types.ts +19 -19
  102. package/src/backoffice/types/static/index.ts +6 -6
  103. package/src/backoffice/types/static/pricing.types.ts +16 -16
  104. package/src/backoffice/types/static/procedure-family.types.ts +14 -14
  105. package/src/backoffice/types/static/treatment-benefit.types.ts +22 -22
  106. package/src/backoffice/types/subcategory.types.ts +34 -34
  107. package/src/backoffice/types/technology.types.ts +161 -161
  108. package/src/backoffice/validations/index.ts +1 -1
  109. package/src/backoffice/validations/schemas.ts +163 -163
  110. package/src/config/__mocks__/firebase.ts +99 -99
  111. package/src/config/firebase.ts +78 -78
  112. package/src/config/index.ts +9 -9
  113. package/src/errors/auth.error.ts +6 -6
  114. package/src/errors/auth.errors.ts +200 -200
  115. package/src/errors/clinic.errors.ts +32 -32
  116. package/src/errors/firebase.errors.ts +47 -47
  117. package/src/errors/user.errors.ts +99 -99
  118. package/src/index.backup.ts +407 -407
  119. package/src/index.ts +6 -6
  120. package/src/locales/en.ts +31 -31
  121. package/src/recommender/admin/index.ts +1 -1
  122. package/src/recommender/admin/services/recommender.service.admin.ts +5 -5
  123. package/src/recommender/front/index.ts +1 -1
  124. package/src/recommender/front/services/onboarding.service.ts +5 -5
  125. package/src/recommender/front/services/recommender.service.ts +3 -3
  126. package/src/recommender/index.ts +1 -1
  127. package/src/services/PATIENTAUTH.MD +197 -197
  128. package/src/services/README.md +106 -106
  129. package/src/services/__tests__/auth/auth.mock.test.ts +17 -17
  130. package/src/services/__tests__/auth/auth.setup.ts +293 -293
  131. package/src/services/__tests__/auth.service.test.ts +346 -346
  132. package/src/services/__tests__/base.service.test.ts +77 -77
  133. package/src/services/__tests__/user.service.test.ts +528 -528
  134. package/src/services/appointment/README.md +17 -17
  135. package/src/services/appointment/appointment.service.ts +2082 -2082
  136. package/src/services/appointment/index.ts +1 -1
  137. package/src/services/appointment/utils/appointment.utils.ts +552 -552
  138. package/src/services/appointment/utils/extended-procedure.utils.ts +314 -314
  139. package/src/services/appointment/utils/form-initialization.utils.ts +225 -225
  140. package/src/services/appointment/utils/recommended-procedure.utils.ts +195 -195
  141. package/src/services/appointment/utils/zone-management.utils.ts +353 -353
  142. package/src/services/appointment/utils/zone-photo.utils.ts +152 -152
  143. package/src/services/auth/auth.service.ts +989 -989
  144. package/src/services/auth/auth.v2.service.ts +961 -961
  145. package/src/services/auth/index.ts +7 -7
  146. package/src/services/auth/utils/error.utils.ts +90 -90
  147. package/src/services/auth/utils/firebase.utils.ts +49 -49
  148. package/src/services/auth/utils/index.ts +21 -21
  149. package/src/services/auth/utils/practitioner.utils.ts +125 -125
  150. package/src/services/base.service.ts +41 -41
  151. package/src/services/calendar/calendar.service.ts +1077 -1077
  152. package/src/services/calendar/calendar.v2.service.ts +1683 -1683
  153. package/src/services/calendar/calendar.v3.service.ts +313 -313
  154. package/src/services/calendar/externalCalendar.service.ts +178 -178
  155. package/src/services/calendar/index.ts +5 -5
  156. package/src/services/calendar/synced-calendars.service.ts +743 -743
  157. package/src/services/calendar/utils/appointment.utils.ts +265 -265
  158. package/src/services/calendar/utils/calendar-event.utils.ts +646 -646
  159. package/src/services/calendar/utils/clinic.utils.ts +237 -237
  160. package/src/services/calendar/utils/docs.utils.ts +157 -157
  161. package/src/services/calendar/utils/google-calendar.utils.ts +697 -697
  162. package/src/services/calendar/utils/index.ts +8 -8
  163. package/src/services/calendar/utils/patient.utils.ts +198 -198
  164. package/src/services/calendar/utils/practitioner.utils.ts +221 -221
  165. package/src/services/calendar/utils/synced-calendar.utils.ts +472 -472
  166. package/src/services/clinic/README.md +204 -204
  167. package/src/services/clinic/__tests__/clinic-admin.service.test.ts +287 -287
  168. package/src/services/clinic/__tests__/clinic-group.service.test.ts +352 -352
  169. package/src/services/clinic/__tests__/clinic.service.test.ts +354 -354
  170. package/src/services/clinic/billing-transactions.service.ts +217 -217
  171. package/src/services/clinic/clinic-admin.service.ts +202 -202
  172. package/src/services/clinic/clinic-group.service.ts +310 -310
  173. package/src/services/clinic/clinic.service.ts +708 -708
  174. package/src/services/clinic/index.ts +5 -5
  175. package/src/services/clinic/practitioner-invite.service.ts +519 -519
  176. package/src/services/clinic/utils/admin.utils.ts +551 -551
  177. package/src/services/clinic/utils/clinic-group.utils.ts +646 -646
  178. package/src/services/clinic/utils/clinic.utils.ts +949 -949
  179. package/src/services/clinic/utils/filter.utils.d.ts +23 -23
  180. package/src/services/clinic/utils/filter.utils.ts +446 -446
  181. package/src/services/clinic/utils/index.ts +11 -11
  182. package/src/services/clinic/utils/photos.utils.ts +188 -188
  183. package/src/services/clinic/utils/search.utils.ts +84 -84
  184. package/src/services/clinic/utils/tag.utils.ts +124 -124
  185. package/src/services/documentation-templates/documentation-template.service.ts +537 -537
  186. package/src/services/documentation-templates/filled-document.service.ts +587 -587
  187. package/src/services/documentation-templates/index.ts +2 -2
  188. package/src/services/index.ts +13 -13
  189. package/src/services/media/index.ts +1 -1
  190. package/src/services/media/media.service.ts +418 -418
  191. package/src/services/notifications/__tests__/notification.service.test.ts +242 -242
  192. package/src/services/notifications/index.ts +1 -1
  193. package/src/services/notifications/notification.service.ts +215 -215
  194. package/src/services/patient/README.md +48 -48
  195. package/src/services/patient/To-Do.md +43 -43
  196. package/src/services/patient/__tests__/patient.service.test.ts +294 -294
  197. package/src/services/patient/index.ts +2 -2
  198. package/src/services/patient/patient.service.ts +883 -883
  199. package/src/services/patient/patientRequirements.service.ts +285 -285
  200. package/src/services/patient/utils/aesthetic-analysis.utils.ts +176 -176
  201. package/src/services/patient/utils/clinic.utils.ts +80 -80
  202. package/src/services/patient/utils/docs.utils.ts +142 -142
  203. package/src/services/patient/utils/index.ts +9 -9
  204. package/src/services/patient/utils/location.utils.ts +126 -126
  205. package/src/services/patient/utils/medical-stuff.utils.ts +143 -143
  206. package/src/services/patient/utils/medical.utils.ts +458 -458
  207. package/src/services/patient/utils/practitioner.utils.ts +260 -260
  208. package/src/services/patient/utils/profile.utils.ts +510 -510
  209. package/src/services/patient/utils/sensitive.utils.ts +260 -260
  210. package/src/services/patient/utils/token.utils.ts +211 -211
  211. package/src/services/practitioner/README.md +145 -145
  212. package/src/services/practitioner/index.ts +1 -1
  213. package/src/services/practitioner/practitioner.service.ts +1742 -1742
  214. package/src/services/procedure/README.md +163 -163
  215. package/src/services/procedure/index.ts +1 -1
  216. package/src/services/procedure/procedure.service.ts +1682 -1682
  217. package/src/services/reviews/index.ts +1 -1
  218. package/src/services/reviews/reviews.service.ts +683 -636
  219. package/src/services/user/index.ts +1 -1
  220. package/src/services/user/user.service.ts +489 -489
  221. package/src/services/user/user.v2.service.ts +466 -466
  222. package/src/types/appointment/index.ts +453 -453
  223. package/src/types/calendar/index.ts +258 -258
  224. package/src/types/calendar/synced-calendar.types.ts +66 -66
  225. package/src/types/clinic/index.ts +489 -489
  226. package/src/types/clinic/practitioner-invite.types.ts +91 -91
  227. package/src/types/clinic/preferences.types.ts +159 -159
  228. package/src/types/clinic/to-do +3 -3
  229. package/src/types/documentation-templates/index.ts +308 -308
  230. package/src/types/index.ts +44 -44
  231. package/src/types/notifications/README.md +77 -77
  232. package/src/types/notifications/index.ts +265 -265
  233. package/src/types/patient/aesthetic-analysis.types.ts +66 -66
  234. package/src/types/patient/allergies.ts +58 -58
  235. package/src/types/patient/index.ts +273 -273
  236. package/src/types/patient/medical-info.types.ts +152 -152
  237. package/src/types/patient/patient-requirements.ts +92 -92
  238. package/src/types/patient/token.types.ts +61 -61
  239. package/src/types/practitioner/index.ts +206 -206
  240. package/src/types/procedure/index.ts +181 -181
  241. package/src/types/profile/index.ts +39 -39
  242. package/src/types/reviews/index.ts +132 -130
  243. package/src/types/tz-lookup.d.ts +4 -4
  244. package/src/types/user/index.ts +38 -38
  245. package/src/utils/TIMESTAMPS.md +176 -176
  246. package/src/utils/TimestampUtils.ts +241 -241
  247. package/src/utils/index.ts +1 -1
  248. package/src/validations/appointment.schema.ts +574 -574
  249. package/src/validations/calendar.schema.ts +225 -225
  250. package/src/validations/clinic.schema.ts +493 -493
  251. package/src/validations/common.schema.ts +25 -25
  252. package/src/validations/documentation-templates/index.ts +1 -1
  253. package/src/validations/documentation-templates/template.schema.ts +220 -220
  254. package/src/validations/documentation-templates.schema.ts +10 -10
  255. package/src/validations/index.ts +20 -20
  256. package/src/validations/media.schema.ts +10 -10
  257. package/src/validations/notification.schema.ts +90 -90
  258. package/src/validations/patient/aesthetic-analysis.schema.ts +55 -55
  259. package/src/validations/patient/medical-info.schema.ts +125 -125
  260. package/src/validations/patient/patient-requirements.schema.ts +84 -84
  261. package/src/validations/patient/token.schema.ts +29 -29
  262. package/src/validations/patient.schema.ts +216 -216
  263. package/src/validations/practitioner.schema.ts +222 -222
  264. package/src/validations/procedure-product.schema.ts +41 -41
  265. package/src/validations/procedure.schema.ts +124 -124
  266. package/src/validations/profile-info.schema.ts +41 -41
  267. package/src/validations/reviews.schema.ts +195 -189
  268. package/src/validations/schemas.ts +104 -104
  269. 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
+ }