@blackcode_sa/metaestetics-api 1.11.3 → 1.12.0
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 +329 -318
- package/dist/admin/index.d.ts +329 -318
- package/dist/backoffice/index.d.mts +1166 -430
- package/dist/backoffice/index.d.ts +1166 -430
- package/dist/backoffice/index.js +1128 -245
- package/dist/backoffice/index.mjs +1119 -209
- package/dist/index.d.mts +4428 -4035
- package/dist/index.d.ts +4428 -4035
- package/dist/index.js +1642 -665
- package/dist/index.mjs +1406 -401
- package/package.json +1 -1
- package/src/backoffice/expo-safe/index.ts +3 -0
- package/src/backoffice/services/README.md +40 -0
- package/src/backoffice/services/brand.service.ts +85 -6
- package/src/backoffice/services/category.service.ts +92 -10
- package/src/backoffice/services/constants.service.ts +308 -0
- package/src/backoffice/services/documentation-template.service.ts +56 -2
- package/src/backoffice/services/index.ts +1 -0
- package/src/backoffice/services/product.service.ts +126 -5
- package/src/backoffice/services/requirement.service.ts +13 -0
- package/src/backoffice/services/subcategory.service.ts +184 -13
- package/src/backoffice/services/technology.service.ts +344 -129
- package/src/backoffice/types/admin-constants.types.ts +69 -0
- package/src/backoffice/types/brand.types.ts +1 -0
- package/src/backoffice/types/index.ts +1 -0
- package/src/backoffice/types/product.types.ts +31 -4
- package/src/backoffice/types/static/contraindication.types.ts +1 -0
- package/src/backoffice/types/static/treatment-benefit.types.ts +1 -0
- package/src/backoffice/types/technology.types.ts +113 -4
- package/src/backoffice/validations/schemas.ts +35 -9
- package/src/services/appointment/appointment.service.ts +0 -5
- package/src/services/appointment/utils/appointment.utils.ts +124 -113
- package/src/services/base.service.ts +10 -3
- package/src/services/documentation-templates/documentation-template.service.ts +116 -0
- package/src/services/media/media.service.ts +2 -2
- package/src/services/procedure/procedure.service.ts +436 -234
- package/src/types/appointment/index.ts +2 -3
- package/src/types/clinic/index.ts +1 -6
- package/src/types/patient/medical-info.types.ts +3 -3
- package/src/types/procedure/index.ts +20 -17
- package/src/validations/clinic.schema.ts +1 -6
- package/src/validations/patient/medical-info.schema.ts +7 -2
- package/src/backoffice/services/__tests__/brand.service.test.ts +0 -196
- package/src/backoffice/services/__tests__/category.service.test.ts +0 -201
- package/src/backoffice/services/__tests__/product.service.test.ts +0 -358
- package/src/backoffice/services/__tests__/requirement.service.test.ts +0 -226
- package/src/backoffice/services/__tests__/subcategory.service.test.ts +0 -181
- package/src/backoffice/services/__tests__/technology.service.test.ts +0 -1097
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ export { CategoryService } from "../services/category.service";
|
|
|
10
10
|
export { SubcategoryService } from "../services/subcategory.service";
|
|
11
11
|
export { TechnologyService } from "../services/technology.service";
|
|
12
12
|
export { ProductService } from "../services/product.service";
|
|
13
|
+
export { ConstantsService } from "../services/constants.service";
|
|
13
14
|
|
|
14
15
|
// Backoffice types
|
|
15
16
|
export type { Brand } from "../types/brand.types";
|
|
@@ -34,4 +35,6 @@ export {
|
|
|
34
35
|
export { Contraindication } from "../types/static/contraindication.types";
|
|
35
36
|
export { ProcedureFamily } from "../types/static/procedure-family.types";
|
|
36
37
|
export { TreatmentBenefit } from "../types/static/treatment-benefit.types";
|
|
38
|
+
export { TreatmentBenefitDynamic } from "../types/";
|
|
39
|
+
export { ContraindicationDynamic } from "../types/";
|
|
37
40
|
export { RequirementType, TimeUnit } from "../types/requirement.types";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Backoffice Services
|
|
2
|
+
|
|
3
|
+
This directory contains services used by the backoffice application.
|
|
4
|
+
|
|
5
|
+
## Services
|
|
6
|
+
|
|
7
|
+
### `CategoryService`
|
|
8
|
+
|
|
9
|
+
Manages procedure categories. Categories are the first level of organization after procedure family (aesthetics/surgery).
|
|
10
|
+
|
|
11
|
+
- **`create(category)`**: Creates a new category.
|
|
12
|
+
- **`getAll()`**: Retrieves all active categories.
|
|
13
|
+
- **`getAllByFamily(family)`**: Retrieves all active categories for a specific procedure family.
|
|
14
|
+
- **`update(id, category)`**: Updates an existing category.
|
|
15
|
+
- **`delete(id)`**: Soft deletes a category.
|
|
16
|
+
- **`getById(id)`**: Retrieves a category by its ID.
|
|
17
|
+
|
|
18
|
+
### `ProductService`
|
|
19
|
+
|
|
20
|
+
Manages products, which are sub-items of a `Technology`.
|
|
21
|
+
|
|
22
|
+
- **`create(technologyId, brandId, product)`**: Creates a new product under a technology.
|
|
23
|
+
- **`getAllByTechnology(technologyId)`**: Retrieves all products for a technology.
|
|
24
|
+
- **`getAllByBrand(brandId)`**: Retrieves all products for a brand.
|
|
25
|
+
- **`update(technologyId, productId, product)`**: Updates a product.
|
|
26
|
+
- **`delete(technologyId, productId)`**: Soft deletes a product.
|
|
27
|
+
- **`getById(technologyId, productId)`**: Retrieves a product by its ID.
|
|
28
|
+
|
|
29
|
+
### `ConstantsService`
|
|
30
|
+
|
|
31
|
+
Manages administrative constants like treatment benefits and contraindications.
|
|
32
|
+
|
|
33
|
+
- **`getTreatmentBenefits()`**: Retrieves all treatment benefits.
|
|
34
|
+
- **`addTreatmentBenefit(benefit)`**: Adds a new treatment benefit.
|
|
35
|
+
- **`updateTreatmentBenefit(benefit)`**: Updates an existing treatment benefit.
|
|
36
|
+
- **`deleteTreatmentBenefit(benefitId)`**: Deletes a treatment benefit.
|
|
37
|
+
- **`getContraindications()`**: Retrieves all contraindications.
|
|
38
|
+
- **`addContraindication(contraindication)`**: Adds a new contraindication.
|
|
39
|
+
- **`updateContraindication(contraindication)`**: Updates an existing contraindication.
|
|
40
|
+
- **`deleteContraindication(contraindicationId)`**: Deletes a contraindication.
|
|
@@ -7,6 +7,13 @@ import {
|
|
|
7
7
|
query,
|
|
8
8
|
updateDoc,
|
|
9
9
|
where,
|
|
10
|
+
limit,
|
|
11
|
+
orderBy,
|
|
12
|
+
startAfter,
|
|
13
|
+
getCountFromServer,
|
|
14
|
+
Query,
|
|
15
|
+
DocumentData,
|
|
16
|
+
QueryConstraint,
|
|
10
17
|
} from "firebase/firestore";
|
|
11
18
|
import { Brand, BRANDS_COLLECTION } from "../types/brand.types";
|
|
12
19
|
import { BaseService } from "../../services/base.service";
|
|
@@ -22,10 +29,13 @@ export class BrandService extends BaseService {
|
|
|
22
29
|
/**
|
|
23
30
|
* Creates a new brand
|
|
24
31
|
*/
|
|
25
|
-
async create(
|
|
32
|
+
async create(
|
|
33
|
+
brand: Omit<Brand, "id" | "createdAt" | "updatedAt" | "name_lowercase">
|
|
34
|
+
) {
|
|
26
35
|
const now = new Date();
|
|
27
36
|
const newBrand: Omit<Brand, "id"> = {
|
|
28
37
|
...brand,
|
|
38
|
+
name_lowercase: brand.name.toLowerCase(),
|
|
29
39
|
createdAt: now,
|
|
30
40
|
updatedAt: now,
|
|
31
41
|
isActive: true,
|
|
@@ -36,10 +46,75 @@ export class BrandService extends BaseService {
|
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
/**
|
|
39
|
-
* Gets
|
|
49
|
+
* Gets a paginated list of active brands, optionally filtered by name.
|
|
50
|
+
* @param rowsPerPage - The number of brands to fetch.
|
|
51
|
+
* @param searchTerm - An optional string to filter brand names by (starts-with search).
|
|
52
|
+
* @param lastVisible - An optional document snapshot to use as a cursor for pagination.
|
|
53
|
+
*/
|
|
54
|
+
async getAll(rowsPerPage: number, searchTerm?: string, lastVisible?: any) {
|
|
55
|
+
const constraints: QueryConstraint[] = [
|
|
56
|
+
where("isActive", "==", true),
|
|
57
|
+
orderBy("name_lowercase"),
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
if (searchTerm) {
|
|
61
|
+
const lowercasedSearchTerm = searchTerm.toLowerCase();
|
|
62
|
+
constraints.push(where("name_lowercase", ">=", lowercasedSearchTerm));
|
|
63
|
+
constraints.push(
|
|
64
|
+
where("name_lowercase", "<=", lowercasedSearchTerm + "\uf8ff")
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (lastVisible) {
|
|
69
|
+
constraints.push(startAfter(lastVisible));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
constraints.push(limit(rowsPerPage));
|
|
73
|
+
|
|
74
|
+
const q = query(this.getBrandsRef(), ...constraints);
|
|
75
|
+
const snapshot = await getDocs(q);
|
|
76
|
+
|
|
77
|
+
const brands = snapshot.docs.map(
|
|
78
|
+
(doc) =>
|
|
79
|
+
({
|
|
80
|
+
id: doc.id,
|
|
81
|
+
...doc.data(),
|
|
82
|
+
} as Brand)
|
|
83
|
+
);
|
|
84
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
85
|
+
|
|
86
|
+
return { brands, lastVisible: newLastVisible };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Gets the total count of active brands, optionally filtered by name.
|
|
91
|
+
* @param searchTerm - An optional string to filter brand names by (starts-with search).
|
|
40
92
|
*/
|
|
41
|
-
async
|
|
42
|
-
const
|
|
93
|
+
async getBrandsCount(searchTerm?: string) {
|
|
94
|
+
const constraints: QueryConstraint[] = [where("isActive", "==", true)];
|
|
95
|
+
|
|
96
|
+
if (searchTerm) {
|
|
97
|
+
const lowercasedSearchTerm = searchTerm.toLowerCase();
|
|
98
|
+
constraints.push(where("name_lowercase", ">=", lowercasedSearchTerm));
|
|
99
|
+
constraints.push(
|
|
100
|
+
where("name_lowercase", "<=", lowercasedSearchTerm + "\uf8ff")
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const q = query(this.getBrandsRef(), ...constraints);
|
|
105
|
+
const snapshot = await getCountFromServer(q);
|
|
106
|
+
return snapshot.data().count;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Gets all active brands for filter dropdowns (not paginated).
|
|
111
|
+
*/
|
|
112
|
+
async getAllForFilter(): Promise<Brand[]> {
|
|
113
|
+
const q = query(
|
|
114
|
+
this.getBrandsRef(),
|
|
115
|
+
where("isActive", "==", true),
|
|
116
|
+
orderBy("name")
|
|
117
|
+
);
|
|
43
118
|
const snapshot = await getDocs(q);
|
|
44
119
|
return snapshot.docs.map(
|
|
45
120
|
(doc) =>
|
|
@@ -55,13 +130,17 @@ export class BrandService extends BaseService {
|
|
|
55
130
|
*/
|
|
56
131
|
async update(
|
|
57
132
|
brandId: string,
|
|
58
|
-
brand: Partial<Omit<Brand, "id" | "createdAt">>
|
|
133
|
+
brand: Partial<Omit<Brand, "id" | "createdAt" | "name_lowercase">>
|
|
59
134
|
) {
|
|
60
|
-
const updateData = {
|
|
135
|
+
const updateData: { [key: string]: any } = {
|
|
61
136
|
...brand,
|
|
62
137
|
updatedAt: new Date(),
|
|
63
138
|
};
|
|
64
139
|
|
|
140
|
+
if (brand.name) {
|
|
141
|
+
updateData.name_lowercase = brand.name.toLowerCase();
|
|
142
|
+
}
|
|
143
|
+
|
|
65
144
|
const docRef = doc(this.getBrandsRef(), brandId);
|
|
66
145
|
await updateDoc(docRef, updateData);
|
|
67
146
|
return this.getById(brandId);
|
|
@@ -2,9 +2,14 @@ import {
|
|
|
2
2
|
addDoc,
|
|
3
3
|
collection,
|
|
4
4
|
doc,
|
|
5
|
+
DocumentData,
|
|
6
|
+
getCountFromServer,
|
|
5
7
|
getDoc,
|
|
6
8
|
getDocs,
|
|
9
|
+
limit,
|
|
10
|
+
orderBy,
|
|
7
11
|
query,
|
|
12
|
+
startAfter,
|
|
8
13
|
updateDoc,
|
|
9
14
|
where,
|
|
10
15
|
} from "firebase/firestore";
|
|
@@ -52,10 +57,31 @@ export class CategoryService extends BaseService {
|
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
/**
|
|
55
|
-
*
|
|
56
|
-
* @
|
|
60
|
+
* Returns counts of categories for each family.
|
|
61
|
+
* @param active - Whether to count active or inactive categories.
|
|
62
|
+
* @returns A record mapping family to category count.
|
|
57
63
|
*/
|
|
58
|
-
async
|
|
64
|
+
async getCategoryCounts(active = true) {
|
|
65
|
+
const counts: Record<string, number> = {};
|
|
66
|
+
const families = Object.values(ProcedureFamily);
|
|
67
|
+
|
|
68
|
+
for (const family of families) {
|
|
69
|
+
const q = query(
|
|
70
|
+
this.categoriesRef,
|
|
71
|
+
where("family", "==", family),
|
|
72
|
+
where("isActive", "==", active)
|
|
73
|
+
);
|
|
74
|
+
const snapshot = await getCountFromServer(q);
|
|
75
|
+
counts[family] = snapshot.data().count;
|
|
76
|
+
}
|
|
77
|
+
return counts;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Vraća sve kategorije za potrebe filtera (bez paginacije)
|
|
82
|
+
* @returns Lista svih aktivnih kategorija
|
|
83
|
+
*/
|
|
84
|
+
async getAllForFilter() {
|
|
59
85
|
const q = query(this.categoriesRef, where("isActive", "==", true));
|
|
60
86
|
const snapshot = await getDocs(q);
|
|
61
87
|
return snapshot.docs.map(
|
|
@@ -68,24 +94,72 @@ export class CategoryService extends BaseService {
|
|
|
68
94
|
}
|
|
69
95
|
|
|
70
96
|
/**
|
|
71
|
-
* Vraća sve
|
|
97
|
+
* Vraća sve kategorije sa paginacijom
|
|
98
|
+
* @param options - Pagination and filter options
|
|
99
|
+
* @returns Lista kategorija i poslednji vidljiv dokument
|
|
100
|
+
*/
|
|
101
|
+
async getAll(
|
|
102
|
+
options: {
|
|
103
|
+
active?: boolean;
|
|
104
|
+
limit?: number;
|
|
105
|
+
lastVisible?: DocumentData;
|
|
106
|
+
} = {}
|
|
107
|
+
) {
|
|
108
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
109
|
+
const constraints = [
|
|
110
|
+
where("isActive", "==", active),
|
|
111
|
+
orderBy("name"),
|
|
112
|
+
queryLimit ? limit(queryLimit) : undefined,
|
|
113
|
+
lastVisible ? startAfter(lastVisible) : undefined,
|
|
114
|
+
].filter((c): c is NonNullable<typeof c> => !!c);
|
|
115
|
+
|
|
116
|
+
const q = query(this.categoriesRef, ...constraints);
|
|
117
|
+
const snapshot = await getDocs(q);
|
|
118
|
+
const categories = snapshot.docs.map(
|
|
119
|
+
(doc) =>
|
|
120
|
+
({
|
|
121
|
+
id: doc.id,
|
|
122
|
+
...doc.data(),
|
|
123
|
+
} as Category)
|
|
124
|
+
);
|
|
125
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
126
|
+
return { categories, lastVisible: newLastVisible };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Vraća sve aktivne kategorije za određenu familiju procedura sa paginacijom
|
|
72
131
|
* @param family - Familija procedura (aesthetics/surgery)
|
|
132
|
+
* @param options - Pagination options
|
|
73
133
|
* @returns Lista kategorija koje pripadaju traženoj familiji
|
|
74
134
|
*/
|
|
75
|
-
async getAllByFamily(
|
|
76
|
-
|
|
77
|
-
|
|
135
|
+
async getAllByFamily(
|
|
136
|
+
family: ProcedureFamily,
|
|
137
|
+
options: {
|
|
138
|
+
active?: boolean;
|
|
139
|
+
limit?: number;
|
|
140
|
+
lastVisible?: DocumentData;
|
|
141
|
+
} = {}
|
|
142
|
+
) {
|
|
143
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
144
|
+
const constraints = [
|
|
78
145
|
where("family", "==", family),
|
|
79
|
-
where("isActive", "==",
|
|
80
|
-
|
|
146
|
+
where("isActive", "==", active),
|
|
147
|
+
orderBy("name"),
|
|
148
|
+
queryLimit ? limit(queryLimit) : undefined,
|
|
149
|
+
lastVisible ? startAfter(lastVisible) : undefined,
|
|
150
|
+
].filter((c): c is NonNullable<typeof c> => !!c);
|
|
151
|
+
|
|
152
|
+
const q = query(this.categoriesRef, ...constraints);
|
|
81
153
|
const snapshot = await getDocs(q);
|
|
82
|
-
|
|
154
|
+
const categories = snapshot.docs.map(
|
|
83
155
|
(doc) =>
|
|
84
156
|
({
|
|
85
157
|
id: doc.id,
|
|
86
158
|
...doc.data(),
|
|
87
159
|
} as Category)
|
|
88
160
|
);
|
|
161
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
162
|
+
return { categories, lastVisible: newLastVisible };
|
|
89
163
|
}
|
|
90
164
|
|
|
91
165
|
/**
|
|
@@ -116,6 +190,14 @@ export class CategoryService extends BaseService {
|
|
|
116
190
|
await this.update(id, { isActive: false });
|
|
117
191
|
}
|
|
118
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Reactivates a category by setting its isActive flag to true.
|
|
195
|
+
* @param id - The ID of the category to reactivate.
|
|
196
|
+
*/
|
|
197
|
+
async reactivate(id: string) {
|
|
198
|
+
await this.update(id, { isActive: true });
|
|
199
|
+
}
|
|
200
|
+
|
|
119
201
|
/**
|
|
120
202
|
* Vraća kategoriju po ID-u
|
|
121
203
|
* @param id - ID tražene kategorije
|
|
@@ -0,0 +1,308 @@
|
|
|
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
|
+
}
|