@blackcode_sa/metaestetics-api 1.10.0 → 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/admin/index.d.mts +337 -319
  2. package/dist/admin/index.d.ts +337 -319
  3. package/dist/admin/index.js +98 -79
  4. package/dist/admin/index.mjs +98 -79
  5. package/dist/backoffice/index.d.mts +284 -67
  6. package/dist/backoffice/index.d.ts +284 -67
  7. package/dist/backoffice/index.js +114 -6
  8. package/dist/backoffice/index.mjs +112 -6
  9. package/dist/index.d.mts +3145 -3065
  10. package/dist/index.d.ts +3145 -3065
  11. package/dist/index.js +460 -141
  12. package/dist/index.mjs +463 -143
  13. package/package.json +3 -1
  14. package/src/admin/booking/booking.admin.ts +2 -0
  15. package/src/admin/booking/booking.calculator.ts +121 -117
  16. package/src/admin/booking/booking.types.ts +3 -0
  17. package/src/backoffice/expo-safe/index.ts +2 -0
  18. package/src/backoffice/services/README.md +40 -0
  19. package/src/backoffice/services/constants.service.ts +268 -0
  20. package/src/backoffice/services/technology.service.ts +122 -10
  21. package/src/backoffice/types/admin-constants.types.ts +69 -0
  22. package/src/backoffice/types/index.ts +1 -0
  23. package/src/backoffice/types/product.types.ts +3 -1
  24. package/src/backoffice/types/technology.types.ts +4 -4
  25. package/src/backoffice/validations/schemas.ts +35 -9
  26. package/src/services/appointment/appointment.service.ts +0 -5
  27. package/src/services/appointment/utils/appointment.utils.ts +124 -113
  28. package/src/services/clinic/clinic.service.ts +163 -82
  29. package/src/services/procedure/procedure.service.ts +435 -234
  30. package/src/types/appointment/index.ts +9 -3
  31. package/src/types/clinic/index.ts +3 -6
  32. package/src/types/patient/medical-info.types.ts +3 -3
  33. package/src/types/procedure/index.ts +20 -17
  34. package/src/validations/appointment.schema.ts +2 -0
  35. package/src/validations/clinic.schema.ts +3 -6
  36. package/src/validations/patient/medical-info.schema.ts +7 -2
  37. package/src/validations/procedure.schema.ts +8 -10
  38. package/src/backoffice/services/__tests__/brand.service.test.ts +0 -196
  39. package/src/backoffice/services/__tests__/category.service.test.ts +0 -201
  40. package/src/backoffice/services/__tests__/product.service.test.ts +0 -358
  41. package/src/backoffice/services/__tests__/requirement.service.test.ts +0 -226
  42. package/src/backoffice/services/__tests__/subcategory.service.test.ts +0 -181
  43. package/src/backoffice/services/__tests__/technology.service.test.ts +0 -1097
@@ -0,0 +1,268 @@
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.
55
+ * @returns {Promise<TreatmentBenefitDynamic[]>} An array of all treatment benefits.
56
+ */
57
+ async getAllBenefits(): 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 Adds a new treatment benefit.
67
+ * @param {Omit<TreatmentBenefitDynamic, "id">} benefit - The treatment benefit to add, without an ID.
68
+ * @returns {Promise<TreatmentBenefitDynamic>} The newly created treatment benefit with its generated ID.
69
+ */
70
+ async addTreatmentBenefit(
71
+ benefit: Omit<TreatmentBenefitDynamic, "id">
72
+ ): Promise<TreatmentBenefitDynamic> {
73
+ const newBenefit: TreatmentBenefitDynamic = {
74
+ id: this.generateId(),
75
+ ...benefit,
76
+ };
77
+
78
+ const docSnap = await getDoc(this.treatmentBenefitsDocRef);
79
+ if (!docSnap.exists()) {
80
+ await setDoc(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
81
+ } else {
82
+ await updateDoc(this.treatmentBenefitsDocRef, {
83
+ benefits: arrayUnion(newBenefit),
84
+ });
85
+ }
86
+
87
+ return newBenefit;
88
+ }
89
+
90
+ /**
91
+ * @description Retrieves a single treatment benefit by its ID.
92
+ * @param {string} benefitId - The ID of the treatment benefit to retrieve.
93
+ * @returns {Promise<TreatmentBenefitDynamic | undefined>} The found treatment benefit or undefined.
94
+ */
95
+ async getBenefitById(
96
+ benefitId: string
97
+ ): Promise<TreatmentBenefitDynamic | undefined> {
98
+ const benefits = await this.getAllBenefits();
99
+ return benefits.find((b) => b.id === benefitId);
100
+ }
101
+
102
+ /**
103
+ * @description Searches for treatment benefits by name (case-insensitive).
104
+ * @param {string} searchTerm - The term to search for in the benefit names.
105
+ * @returns {Promise<TreatmentBenefitDynamic[]>} An array of matching treatment benefits.
106
+ */
107
+ async searchBenefitsByName(
108
+ searchTerm: string
109
+ ): Promise<TreatmentBenefitDynamic[]> {
110
+ const benefits = await this.getAllBenefits();
111
+ const normalizedSearchTerm = searchTerm.toLowerCase();
112
+ return benefits.filter((b) =>
113
+ b.name.toLowerCase().includes(normalizedSearchTerm)
114
+ );
115
+ }
116
+
117
+ /**
118
+ * @description Updates an existing treatment benefit.
119
+ * @param {TreatmentBenefitDynamic} benefit - The treatment benefit with updated data. Its ID must match an existing benefit.
120
+ * @returns {Promise<TreatmentBenefitDynamic>} The updated treatment benefit.
121
+ * @throws {Error} If the treatment benefit is not found.
122
+ */
123
+ async updateTreatmentBenefit(
124
+ benefit: TreatmentBenefitDynamic
125
+ ): Promise<TreatmentBenefitDynamic> {
126
+ const benefits = await this.getAllBenefits();
127
+ const benefitIndex = benefits.findIndex((b) => b.id === benefit.id);
128
+
129
+ if (benefitIndex === -1) {
130
+ throw new Error("Treatment benefit not found.");
131
+ }
132
+
133
+ benefits[benefitIndex] = benefit;
134
+
135
+ await updateDoc(this.treatmentBenefitsDocRef, { benefits });
136
+ return benefit;
137
+ }
138
+
139
+ /**
140
+ * @description Deletes a treatment benefit by its ID.
141
+ * @param {string} benefitId - The ID of the treatment benefit to delete.
142
+ * @returns {Promise<void>}
143
+ */
144
+ async deleteTreatmentBenefit(benefitId: string): Promise<void> {
145
+ const benefits = await this.getAllBenefits();
146
+ const benefitToRemove = benefits.find((b) => b.id === benefitId);
147
+
148
+ if (!benefitToRemove) {
149
+ return;
150
+ }
151
+
152
+ await updateDoc(this.treatmentBenefitsDocRef, {
153
+ benefits: arrayRemove(benefitToRemove),
154
+ });
155
+ }
156
+
157
+ // =================================================================
158
+ // Contraindications
159
+ // =================================================================
160
+
161
+ /**
162
+ * @description Retrieves all contraindications.
163
+ * @returns {Promise<ContraindicationDynamic[]>} An array of all contraindications.
164
+ */
165
+ async getAllContraindications(): Promise<ContraindicationDynamic[]> {
166
+ const docSnap = await getDoc(this.contraindicationsDocRef);
167
+ if (!docSnap.exists()) {
168
+ return [];
169
+ }
170
+ return (docSnap.data() as ContraindicationsDocument).contraindications;
171
+ }
172
+
173
+ /**
174
+ * @description Adds a new contraindication.
175
+ * @param {Omit<ContraindicationDynamic, "id">} contraindication - The contraindication to add, without an ID.
176
+ * @returns {Promise<ContraindicationDynamic>} The newly created contraindication with its generated ID.
177
+ */
178
+ async addContraindication(
179
+ contraindication: Omit<ContraindicationDynamic, "id">
180
+ ): Promise<ContraindicationDynamic> {
181
+ const newContraindication: ContraindicationDynamic = {
182
+ id: this.generateId(),
183
+ ...contraindication,
184
+ };
185
+
186
+ const docSnap = await getDoc(this.contraindicationsDocRef);
187
+ if (!docSnap.exists()) {
188
+ await setDoc(this.contraindicationsDocRef, {
189
+ contraindications: [newContraindication],
190
+ });
191
+ } else {
192
+ await updateDoc(this.contraindicationsDocRef, {
193
+ contraindications: arrayUnion(newContraindication),
194
+ });
195
+ }
196
+
197
+ return newContraindication;
198
+ }
199
+
200
+ /**
201
+ * @description Retrieves a single contraindication by its ID.
202
+ * @param {string} contraindicationId - The ID of the contraindication to retrieve.
203
+ * @returns {Promise<ContraindicationDynamic | undefined>} The found contraindication or undefined.
204
+ */
205
+ async getContraindicationById(
206
+ contraindicationId: string
207
+ ): Promise<ContraindicationDynamic | undefined> {
208
+ const contraindications = await this.getAllContraindications();
209
+ return contraindications.find((c) => c.id === contraindicationId);
210
+ }
211
+
212
+ /**
213
+ * @description Searches for contraindications by name (case-insensitive).
214
+ * @param {string} searchTerm - The term to search for in the contraindication names.
215
+ * @returns {Promise<ContraindicationDynamic[]>} An array of matching contraindications.
216
+ */
217
+ async searchContraindicationsByName(
218
+ searchTerm: string
219
+ ): Promise<ContraindicationDynamic[]> {
220
+ const contraindications = await this.getAllContraindications();
221
+ const normalizedSearchTerm = searchTerm.toLowerCase();
222
+ return contraindications.filter((c) =>
223
+ c.name.toLowerCase().includes(normalizedSearchTerm)
224
+ );
225
+ }
226
+
227
+ /**
228
+ * @description Updates an existing contraindication.
229
+ * @param {ContraindicationDynamic} contraindication - The contraindication with updated data. Its ID must match an existing one.
230
+ * @returns {Promise<ContraindicationDynamic>} The updated contraindication.
231
+ * @throws {Error} If the contraindication is not found.
232
+ */
233
+ async updateContraindication(
234
+ contraindication: ContraindicationDynamic
235
+ ): Promise<ContraindicationDynamic> {
236
+ const contraindications = await this.getAllContraindications();
237
+ const index = contraindications.findIndex(
238
+ (c) => c.id === contraindication.id
239
+ );
240
+
241
+ if (index === -1) {
242
+ throw new Error("Contraindication not found.");
243
+ }
244
+
245
+ contraindications[index] = contraindication;
246
+
247
+ await updateDoc(this.contraindicationsDocRef, { contraindications });
248
+ return contraindication;
249
+ }
250
+
251
+ /**
252
+ * @description Deletes a contraindication by its ID.
253
+ * @param {string} contraindicationId - The ID of the contraindication to delete.
254
+ * @returns {Promise<void>}
255
+ */
256
+ async deleteContraindication(contraindicationId: string): Promise<void> {
257
+ const contraindications = await this.getAllContraindications();
258
+ const toRemove = contraindications.find((c) => c.id === contraindicationId);
259
+
260
+ if (!toRemove) {
261
+ return;
262
+ }
263
+
264
+ await updateDoc(this.contraindicationsDocRef, {
265
+ contraindications: arrayRemove(toRemove),
266
+ });
267
+ }
268
+ }
@@ -14,8 +14,8 @@ import {
14
14
  import { Technology, TECHNOLOGIES_COLLECTION } from "../types/technology.types";
15
15
  import { Requirement, RequirementType } from "../types/requirement.types";
16
16
  import { BlockingCondition } from "../types/static/blocking-condition.types";
17
- import { Contraindication } from "../types/static/contraindication.types";
18
- import { TreatmentBenefit } from "../types/static/treatment-benefit.types";
17
+ import { ContraindicationDynamic } from "../types/admin-constants.types";
18
+ import { TreatmentBenefitDynamic } from "../types/admin-constants.types";
19
19
  import {
20
20
  CertificationLevel,
21
21
  CertificationSpecialty,
@@ -346,12 +346,21 @@ export class TechnologyService extends BaseService {
346
346
  */
347
347
  async addContraindication(
348
348
  technologyId: string,
349
- contraindication: Contraindication
349
+ contraindication: ContraindicationDynamic
350
350
  ) {
351
351
  const docRef = doc(this.getTechnologiesRef(), technologyId);
352
+ const technology = await this.getById(technologyId);
353
+ if (!technology) {
354
+ throw new Error(`Technology with id ${technologyId} not found`);
355
+ }
356
+
357
+ const existingContraindications = technology.contraindications || [];
358
+ if (existingContraindications.some((c) => c.id === contraindication.id)) {
359
+ return technology; // Already exists, do nothing
360
+ }
352
361
 
353
362
  await updateDoc(docRef, {
354
- contraindications: arrayUnion(contraindication),
363
+ contraindications: [...existingContraindications, contraindication],
355
364
  updatedAt: new Date(),
356
365
  });
357
366
 
@@ -366,12 +375,62 @@ export class TechnologyService extends BaseService {
366
375
  */
367
376
  async removeContraindication(
368
377
  technologyId: string,
369
- contraindication: Contraindication
378
+ contraindication: ContraindicationDynamic
370
379
  ) {
371
380
  const docRef = doc(this.getTechnologiesRef(), technologyId);
381
+ const technology = await this.getById(technologyId);
382
+ if (!technology) {
383
+ throw new Error(`Technology with id ${technologyId} not found`);
384
+ }
385
+
386
+ const updatedContraindications = (
387
+ technology.contraindications || []
388
+ ).filter((c) => c.id !== contraindication.id);
389
+
390
+ await updateDoc(docRef, {
391
+ contraindications: updatedContraindications,
392
+ updatedAt: new Date(),
393
+ });
394
+
395
+ return this.getById(technologyId);
396
+ }
397
+
398
+ /**
399
+ * Updates an existing contraindication in a technology's list.
400
+ * If the contraindication does not exist, it will not be added.
401
+ * @param technologyId - ID of the technology
402
+ * @param contraindication - The updated contraindication object
403
+ * @returns The updated technology
404
+ */
405
+ async updateContraindication(
406
+ technologyId: string,
407
+ contraindication: ContraindicationDynamic
408
+ ) {
409
+ const docRef = doc(this.getTechnologiesRef(), technologyId);
410
+ const technology = await this.getById(technologyId);
411
+ if (!technology) {
412
+ throw new Error(`Technology with id ${technologyId} not found`);
413
+ }
414
+
415
+ const contraindications = technology.contraindications || [];
416
+ const index = contraindications.findIndex(
417
+ (c) => c.id === contraindication.id
418
+ );
419
+
420
+ if (index === -1) {
421
+ // If contraindication doesn't exist, do not update
422
+ // Consider throwing an error if this is an unexpected state
423
+ console.warn(
424
+ `Contraindication with id ${contraindication.id} not found for technology ${technologyId}. No update performed.`
425
+ );
426
+ return technology;
427
+ }
428
+
429
+ const updatedContraindications = [...contraindications];
430
+ updatedContraindications[index] = contraindication;
372
431
 
373
432
  await updateDoc(docRef, {
374
- contraindications: arrayRemove(contraindication),
433
+ contraindications: updatedContraindications,
375
434
  updatedAt: new Date(),
376
435
  });
377
436
 
@@ -384,11 +443,20 @@ export class TechnologyService extends BaseService {
384
443
  * @param benefit - Benefit koji se dodaje
385
444
  * @returns Ažurirana tehnologija
386
445
  */
387
- async addBenefit(technologyId: string, benefit: TreatmentBenefit) {
446
+ async addBenefit(technologyId: string, benefit: TreatmentBenefitDynamic) {
388
447
  const docRef = doc(this.getTechnologiesRef(), technologyId);
448
+ const technology = await this.getById(technologyId);
449
+ if (!technology) {
450
+ throw new Error(`Technology with id ${technologyId} not found`);
451
+ }
452
+
453
+ const existingBenefits = technology.benefits || [];
454
+ if (existingBenefits.some((b) => b.id === benefit.id)) {
455
+ return technology; // Already exists, do nothing
456
+ }
389
457
 
390
458
  await updateDoc(docRef, {
391
- benefits: arrayUnion(benefit),
459
+ benefits: [...existingBenefits, benefit],
392
460
  updatedAt: new Date(),
393
461
  });
394
462
 
@@ -401,11 +469,55 @@ export class TechnologyService extends BaseService {
401
469
  * @param benefit - Benefit koji se uklanja
402
470
  * @returns Ažurirana tehnologija
403
471
  */
404
- async removeBenefit(technologyId: string, benefit: TreatmentBenefit) {
472
+ async removeBenefit(technologyId: string, benefit: TreatmentBenefitDynamic) {
405
473
  const docRef = doc(this.getTechnologiesRef(), technologyId);
474
+ const technology = await this.getById(technologyId);
475
+ if (!technology) {
476
+ throw new Error(`Technology with id ${technologyId} not found`);
477
+ }
478
+
479
+ const updatedBenefits = (technology.benefits || []).filter(
480
+ (b) => b.id !== benefit.id
481
+ );
482
+
483
+ await updateDoc(docRef, {
484
+ benefits: updatedBenefits,
485
+ updatedAt: new Date(),
486
+ });
487
+
488
+ return this.getById(technologyId);
489
+ }
490
+
491
+ /**
492
+ * Updates an existing benefit in a technology's list.
493
+ * If the benefit does not exist, it will not be added.
494
+ * @param technologyId - ID of the technology
495
+ * @param benefit - The updated benefit object
496
+ * @returns The updated technology
497
+ */
498
+ async updateBenefit(technologyId: string, benefit: TreatmentBenefitDynamic) {
499
+ const docRef = doc(this.getTechnologiesRef(), technologyId);
500
+ const technology = await this.getById(technologyId);
501
+ if (!technology) {
502
+ throw new Error(`Technology with id ${technologyId} not found`);
503
+ }
504
+
505
+ const benefits = technology.benefits || [];
506
+ const index = benefits.findIndex((b) => b.id === benefit.id);
507
+
508
+ if (index === -1) {
509
+ // If benefit doesn't exist, do not update
510
+ console.warn(
511
+ `Benefit with id ${benefit.id} not found for technology ${technologyId}. No update performed.`
512
+ );
513
+ return technology;
514
+ }
515
+
516
+ const updatedBenefits = [...benefits];
517
+ updatedBenefits[index] = benefit;
406
518
 
407
519
  await updateDoc(docRef, {
408
- benefits: arrayRemove(benefit),
520
+ benefits: updatedBenefits,
409
521
  updatedAt: new Date(),
410
522
  });
411
523
 
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @file Defines types for dynamically managed administrative constants,
3
+ * such as treatment benefits and contraindications. These are stored in
4
+ * the 'admin-constants' collection in Firestore.
5
+ */
6
+
7
+ /**
8
+ * Represents a single dynamic treatment benefit.
9
+ * These are positive effects or results a patient can expect from a treatment.
10
+ */
11
+ export interface TreatmentBenefitDynamic {
12
+ /**
13
+ * A unique identifier for the benefit, typically in snake_case.
14
+ * @example "wrinkle_reduction"
15
+ */
16
+ id: string;
17
+
18
+ /**
19
+ * A human-readable name for the treatment benefit.
20
+ * @example "Wrinkle Reduction"
21
+ */
22
+ name: string;
23
+
24
+ /**
25
+ * A detailed description of the benefit.
26
+ */
27
+ description?: string;
28
+ }
29
+
30
+ /**
31
+ * Defines the structure of the document storing all treatment benefits
32
+ * in the 'admin-constants' collection.
33
+ * The document ID for this type should be 'treatment-benefits'.
34
+ */
35
+ export interface TreatmentBenefitsDocument {
36
+ benefits: TreatmentBenefitDynamic[];
37
+ }
38
+
39
+ /**
40
+ * Represents a single dynamic contraindication.
41
+ * These are conditions or factors that can affect a procedure and require special attention.
42
+ */
43
+ export interface ContraindicationDynamic {
44
+ /**
45
+ * A unique identifier for the contraindication, typically in snake_case.
46
+ * @example "sensitive_skin"
47
+ */
48
+ id: string;
49
+
50
+ /**
51
+ * A human-readable name for the contraindication.
52
+ * @example "Sensitive Skin"
53
+ */
54
+ name: string;
55
+
56
+ /**
57
+ * A detailed description of the contraindication.
58
+ */
59
+ description?: string;
60
+ }
61
+
62
+ /**
63
+ * Defines the structure of the document storing all contraindications
64
+ * in the 'admin-constants' collection.
65
+ * The document ID for this type should be 'contraindications'.
66
+ */
67
+ export interface ContraindicationsDocument {
68
+ contraindications: ContraindicationDynamic[];
69
+ }
@@ -6,3 +6,4 @@ export * from "./requirement.types";
6
6
  export * from "./subcategory.types";
7
7
  export * from "./technology.types";
8
8
  export * from "./static";
9
+ export * from "./admin-constants.types";
@@ -1,3 +1,5 @@
1
+ import type { ContraindicationDynamic } from "./admin-constants.types";
2
+
1
3
  /**
2
4
  * Product used in procedures
3
5
  * Can be consumables, equipment, or any other product needed for performing procedures
@@ -33,7 +35,7 @@ export interface Product {
33
35
  dosage?: string;
34
36
  composition?: string;
35
37
  indications?: string[];
36
- contraindications?: string[];
38
+ contraindications?: ContraindicationDynamic[];
37
39
  }
38
40
 
39
41
  /**
@@ -1,10 +1,10 @@
1
1
  import { Requirement } from "./requirement.types";
2
2
  import { BlockingCondition } from "./static/blocking-condition.types";
3
- import { Contraindication } from "./static/contraindication.types";
4
- import { TreatmentBenefit } from "./static/treatment-benefit.types";
5
3
  import { CertificationRequirement } from "./static/certification.types";
6
4
  import { DocumentTemplate } from "../../types/documentation-templates";
7
5
  import { ProcedureFamily } from "./static/procedure-family.types";
6
+ import { ContraindicationDynamic } from "./admin-constants.types";
7
+ import { TreatmentBenefitDynamic } from "./admin-constants.types";
8
8
 
9
9
  /**
10
10
  * Reference to a documentation template with metadata
@@ -65,8 +65,8 @@ export interface Technology {
65
65
  post: Requirement[];
66
66
  };
67
67
  blockingConditions: BlockingCondition[];
68
- contraindications: Contraindication[];
69
- benefits: TreatmentBenefit[];
68
+ contraindications: ContraindicationDynamic[];
69
+ benefits: TreatmentBenefitDynamic[];
70
70
  certificationRequirement: CertificationRequirement;
71
71
  documentationTemplates?: TechnologyDocumentationTemplate[];
72
72
  isActive: boolean;
@@ -1,27 +1,53 @@
1
1
  import { z } from "zod";
2
2
  import { ProcedureFamily } from "../types/static/procedure-family.types";
3
3
  import { BlockingCondition } from "../types/static/blocking-condition.types";
4
- import { Contraindication } from "../types/static/contraindication.types";
5
- import { TreatmentBenefit } from "../types/static/treatment-benefit.types";
6
4
  import { TimeUnit, RequirementType } from "../types/requirement.types";
7
5
  import {
8
6
  CertificationLevel,
9
7
  CertificationSpecialty,
10
8
  } from "../types/static/certification.types";
11
- import {
12
- DocumentElementType,
13
- HeadingLevel,
14
- ListType,
15
- } from "../types/documentation-templates.types";
9
+
16
10
  import { documentTemplateSchema } from "../../validations/documentation-templates.schema";
17
11
 
12
+ /**
13
+ * Zod validation schema for a single dynamic contraindication.
14
+ * @see ContraindicationDynamic in admin-constants.types.ts
15
+ */
16
+ export const contraindicationDynamicSchema = z.object({
17
+ id: z
18
+ .string()
19
+ .min(1, "Contraindication ID is required")
20
+ .regex(
21
+ /^[a-z0-9_]+$/,
22
+ "ID must be in snake_case (lowercase, numbers, and underscores only)"
23
+ ),
24
+ name: z.string().min(1, "Contraindication name is required"),
25
+ description: z.string().optional(),
26
+ });
27
+
28
+ /**
29
+ * Zod validation schema for a single dynamic treatment benefit.
30
+ * @see TreatmentBenefitDynamic in admin-constants.types.ts
31
+ */
32
+ export const treatmentBenefitDynamicSchema = z.object({
33
+ id: z
34
+ .string()
35
+ .min(1, "Benefit ID is required")
36
+ .regex(
37
+ /^[a-z0-9_]+$/,
38
+ "ID must be in snake_case (lowercase, numbers, and underscores only)"
39
+ ),
40
+ name: z.string().min(1, "Benefit name is required"),
41
+ description: z.string().optional(),
42
+ });
43
+
18
44
  /**
19
45
  * Base validation schemas for enums
20
46
  */
21
47
  export const blockingConditionSchemaBackoffice =
22
48
  z.nativeEnum(BlockingCondition);
23
- export const contraindicationSchemaBackoffice = z.nativeEnum(Contraindication);
24
- export const treatmentBenefitSchemaBackoffice = z.nativeEnum(TreatmentBenefit);
49
+ export const contraindicationSchemaBackoffice = contraindicationDynamicSchema;
50
+ export const treatmentBenefitSchemaBackoffice = treatmentBenefitDynamicSchema;
25
51
  export const procedureFamilySchemaBackoffice = z.nativeEnum(ProcedureFamily);
26
52
  export const timeUnitSchemaBackoffice = z.nativeEnum(TimeUnit);
27
53
  export const requirementTypeSchema = z.nativeEnum(RequirementType);
@@ -21,18 +21,15 @@ import { BaseService } from "../base.service";
21
21
  import {
22
22
  Appointment,
23
23
  AppointmentStatus,
24
- CreateAppointmentData,
25
24
  UpdateAppointmentData,
26
25
  SearchAppointmentsParams,
27
26
  PaymentStatus,
28
27
  AppointmentMediaItem,
29
28
  PatientReviewInfo,
30
- LinkedFormInfo,
31
29
  type CreateAppointmentHttpData,
32
30
  APPOINTMENTS_COLLECTION,
33
31
  } from "../../types/appointment";
34
32
  import {
35
- createAppointmentSchema,
36
33
  updateAppointmentSchema,
37
34
  searchAppointmentsSchema,
38
35
  rescheduleAppointmentSchema,
@@ -47,8 +44,6 @@ import { FilledDocumentService } from "../documentation-templates/filled-documen
47
44
 
48
45
  // Import utility functions
49
46
  import {
50
- fetchAggregatedInfoUtil,
51
- createAppointmentUtil,
52
47
  updateAppointmentUtil,
53
48
  getAppointmentByIdUtil,
54
49
  searchAppointmentsUtil,