@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
|
@@ -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
|
arrayUnion,
|
|
@@ -14,8 +19,8 @@ import {
|
|
|
14
19
|
import { Technology, TECHNOLOGIES_COLLECTION } from "../types/technology.types";
|
|
15
20
|
import { Requirement, RequirementType } from "../types/requirement.types";
|
|
16
21
|
import { BlockingCondition } from "../types/static/blocking-condition.types";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
22
|
+
import { ContraindicationDynamic } from "../types/admin-constants.types";
|
|
23
|
+
import { TreatmentBenefitDynamic } from "../types/admin-constants.types";
|
|
19
24
|
import {
|
|
20
25
|
CertificationLevel,
|
|
21
26
|
CertificationSpecialty,
|
|
@@ -37,186 +42,238 @@ const DEFAULT_CERTIFICATION_REQUIREMENT: CertificationRequirement = {
|
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
/**
|
|
40
|
-
*
|
|
41
|
-
* Tehnologije su sada top-level kolekcija koja referencira svoju putanju u hijerarhiji
|
|
42
|
-
* kroz family, categoryId i subcategoryId.
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* const technologyService = new TechnologyService();
|
|
46
|
-
*
|
|
47
|
-
* // Kreiranje nove tehnologije
|
|
48
|
-
* const technology = await technologyService.create({
|
|
49
|
-
* name: "Botulinum Toxin",
|
|
50
|
-
* description: "Neurotoxin injections for wrinkle reduction",
|
|
51
|
-
* family: ProcedureFamily.AESTHETICS,
|
|
52
|
-
* categoryId: "category123",
|
|
53
|
-
* subcategoryId: "subcategory456"
|
|
54
|
-
* });
|
|
55
|
-
*
|
|
56
|
-
* // Dodavanje zahteva
|
|
57
|
-
* await technologyService.addRequirement(technology.id, {
|
|
58
|
-
* type: "pre",
|
|
59
|
-
* name: "Stay Hydrated",
|
|
60
|
-
* description: "Drink plenty of water"
|
|
61
|
-
* });
|
|
45
|
+
* Service for managing technologies.
|
|
62
46
|
*/
|
|
63
47
|
export class TechnologyService extends BaseService {
|
|
64
48
|
/**
|
|
65
|
-
*
|
|
49
|
+
* Reference to the Firestore collection of technologies.
|
|
66
50
|
*/
|
|
67
|
-
private
|
|
51
|
+
private get technologiesRef() {
|
|
68
52
|
return collection(this.db, TECHNOLOGIES_COLLECTION);
|
|
69
53
|
}
|
|
70
54
|
|
|
71
55
|
/**
|
|
72
|
-
*
|
|
73
|
-
* @param technology -
|
|
74
|
-
* @returns
|
|
56
|
+
* Creates a new technology.
|
|
57
|
+
* @param technology - Data for the new technology.
|
|
58
|
+
* @returns The created technology with its generated ID.
|
|
75
59
|
*/
|
|
76
60
|
async create(technology: Omit<Technology, "id" | "createdAt" | "updatedAt">) {
|
|
77
61
|
const now = new Date();
|
|
62
|
+
// Explicitly construct the object to ensure no undefined values are passed.
|
|
78
63
|
const newTechnology: Omit<Technology, "id"> = {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
post: [],
|
|
86
|
-
},
|
|
64
|
+
name: technology.name,
|
|
65
|
+
description: technology.description,
|
|
66
|
+
family: technology.family,
|
|
67
|
+
categoryId: technology.categoryId,
|
|
68
|
+
subcategoryId: technology.subcategoryId,
|
|
69
|
+
requirements: technology.requirements || { pre: [], post: [] },
|
|
87
70
|
blockingConditions: technology.blockingConditions || [],
|
|
88
71
|
contraindications: technology.contraindications || [],
|
|
89
72
|
benefits: technology.benefits || [],
|
|
90
73
|
certificationRequirement:
|
|
91
74
|
technology.certificationRequirement ||
|
|
92
75
|
DEFAULT_CERTIFICATION_REQUIREMENT,
|
|
76
|
+
documentationTemplates: technology.documentationTemplates || [],
|
|
77
|
+
isActive: true,
|
|
78
|
+
createdAt: now,
|
|
79
|
+
updatedAt: now,
|
|
93
80
|
};
|
|
94
81
|
|
|
95
|
-
|
|
82
|
+
// Add optional fields only if they are not undefined
|
|
83
|
+
if (technology.technicalDetails) {
|
|
84
|
+
newTechnology.technicalDetails = technology.technicalDetails;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const docRef = await addDoc(this.technologiesRef, newTechnology as any);
|
|
96
88
|
return { id: docRef.id, ...newTechnology };
|
|
97
89
|
}
|
|
98
90
|
|
|
99
91
|
/**
|
|
100
|
-
*
|
|
101
|
-
* @
|
|
92
|
+
* Returns counts of technologies for each subcategory.
|
|
93
|
+
* @param active - Whether to count active or inactive technologies.
|
|
94
|
+
* @returns A record mapping subcategory ID to technology count.
|
|
102
95
|
*/
|
|
103
|
-
async
|
|
104
|
-
const q = query(this.
|
|
96
|
+
async getTechnologyCounts(active = true) {
|
|
97
|
+
const q = query(this.technologiesRef, where("isActive", "==", active));
|
|
105
98
|
const snapshot = await getDocs(q);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
);
|
|
99
|
+
const counts: Record<string, number> = {};
|
|
100
|
+
snapshot.docs.forEach((doc) => {
|
|
101
|
+
const tech = doc.data() as Technology;
|
|
102
|
+
counts[tech.subcategoryId] = (counts[tech.subcategoryId] || 0) + 1;
|
|
103
|
+
});
|
|
104
|
+
return counts;
|
|
113
105
|
}
|
|
114
106
|
|
|
115
107
|
/**
|
|
116
|
-
*
|
|
117
|
-
* @param
|
|
118
|
-
* @returns
|
|
108
|
+
* Returns counts of technologies for each category.
|
|
109
|
+
* @param active - Whether to count active or inactive technologies.
|
|
110
|
+
* @returns A record mapping category ID to technology count.
|
|
119
111
|
*/
|
|
120
|
-
async
|
|
121
|
-
const q = query(
|
|
122
|
-
this.getTechnologiesRef(),
|
|
123
|
-
where("isActive", "==", true),
|
|
124
|
-
where("family", "==", family)
|
|
125
|
-
);
|
|
112
|
+
async getTechnologyCountsByCategory(active = true) {
|
|
113
|
+
const q = query(this.technologiesRef, where("isActive", "==", active));
|
|
126
114
|
const snapshot = await getDocs(q);
|
|
127
|
-
|
|
115
|
+
const counts: Record<string, number> = {};
|
|
116
|
+
snapshot.docs.forEach((doc) => {
|
|
117
|
+
const tech = doc.data() as Technology;
|
|
118
|
+
counts[tech.categoryId] = (counts[tech.categoryId] || 0) + 1;
|
|
119
|
+
});
|
|
120
|
+
return counts;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Returns all technologies with pagination.
|
|
125
|
+
* @param options - Pagination and filter options.
|
|
126
|
+
* @returns A list of technologies and the last visible document.
|
|
127
|
+
*/
|
|
128
|
+
async getAll(
|
|
129
|
+
options: {
|
|
130
|
+
active?: boolean;
|
|
131
|
+
limit?: number;
|
|
132
|
+
lastVisible?: DocumentData;
|
|
133
|
+
} = {}
|
|
134
|
+
) {
|
|
135
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
136
|
+
const constraints = [
|
|
137
|
+
where("isActive", "==", active),
|
|
138
|
+
orderBy("name"),
|
|
139
|
+
queryLimit ? limit(queryLimit) : undefined,
|
|
140
|
+
lastVisible ? startAfter(lastVisible) : undefined,
|
|
141
|
+
].filter((c): c is NonNullable<typeof c> => !!c);
|
|
142
|
+
|
|
143
|
+
const q = query(this.technologiesRef, ...constraints);
|
|
144
|
+
const snapshot = await getDocs(q);
|
|
145
|
+
const technologies = snapshot.docs.map(
|
|
128
146
|
(doc) =>
|
|
129
147
|
({
|
|
130
148
|
id: doc.id,
|
|
131
149
|
...doc.data(),
|
|
132
150
|
} as Technology)
|
|
133
151
|
);
|
|
152
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
153
|
+
return { technologies, lastVisible: newLastVisible };
|
|
134
154
|
}
|
|
135
155
|
|
|
136
156
|
/**
|
|
137
|
-
*
|
|
138
|
-
* @param categoryId - ID
|
|
139
|
-
* @
|
|
157
|
+
* Returns all technologies for a specific category with pagination.
|
|
158
|
+
* @param categoryId - The ID of the category.
|
|
159
|
+
* @param options - Pagination options.
|
|
160
|
+
* @returns A list of technologies for the specified category.
|
|
140
161
|
*/
|
|
141
|
-
async getAllByCategoryId(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
async getAllByCategoryId(
|
|
163
|
+
categoryId: string,
|
|
164
|
+
options: {
|
|
165
|
+
active?: boolean;
|
|
166
|
+
limit?: number;
|
|
167
|
+
lastVisible?: DocumentData;
|
|
168
|
+
} = {}
|
|
169
|
+
) {
|
|
170
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
171
|
+
const constraints = [
|
|
172
|
+
where("categoryId", "==", categoryId),
|
|
173
|
+
where("isActive", "==", active),
|
|
174
|
+
orderBy("name"),
|
|
175
|
+
queryLimit ? limit(queryLimit) : undefined,
|
|
176
|
+
lastVisible ? startAfter(lastVisible) : undefined,
|
|
177
|
+
].filter((c): c is NonNullable<typeof c> => !!c);
|
|
178
|
+
|
|
179
|
+
const q = query(this.technologiesRef, ...constraints);
|
|
147
180
|
const snapshot = await getDocs(q);
|
|
148
|
-
|
|
181
|
+
const technologies = snapshot.docs.map(
|
|
149
182
|
(doc) =>
|
|
150
183
|
({
|
|
151
184
|
id: doc.id,
|
|
152
185
|
...doc.data(),
|
|
153
186
|
} as Technology)
|
|
154
187
|
);
|
|
188
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
189
|
+
return { technologies, lastVisible: newLastVisible };
|
|
155
190
|
}
|
|
156
191
|
|
|
157
192
|
/**
|
|
158
|
-
*
|
|
159
|
-
* @param subcategoryId - ID
|
|
160
|
-
* @
|
|
193
|
+
* Returns all technologies for a specific subcategory with pagination.
|
|
194
|
+
* @param subcategoryId - The ID of the subcategory.
|
|
195
|
+
* @param options - Pagination options.
|
|
196
|
+
* @returns A list of technologies for the specified subcategory.
|
|
161
197
|
*/
|
|
162
|
-
async getAllBySubcategoryId(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
198
|
+
async getAllBySubcategoryId(
|
|
199
|
+
subcategoryId: string,
|
|
200
|
+
options: {
|
|
201
|
+
active?: boolean;
|
|
202
|
+
limit?: number;
|
|
203
|
+
lastVisible?: DocumentData;
|
|
204
|
+
} = {}
|
|
205
|
+
) {
|
|
206
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
207
|
+
const constraints = [
|
|
208
|
+
where("subcategoryId", "==", subcategoryId),
|
|
209
|
+
where("isActive", "==", active),
|
|
210
|
+
orderBy("name"),
|
|
211
|
+
queryLimit ? limit(queryLimit) : undefined,
|
|
212
|
+
lastVisible ? startAfter(lastVisible) : undefined,
|
|
213
|
+
].filter((c): c is NonNullable<typeof c> => !!c);
|
|
214
|
+
|
|
215
|
+
const q = query(this.technologiesRef, ...constraints);
|
|
168
216
|
const snapshot = await getDocs(q);
|
|
169
|
-
|
|
217
|
+
const technologies = snapshot.docs.map(
|
|
170
218
|
(doc) =>
|
|
171
219
|
({
|
|
172
220
|
id: doc.id,
|
|
173
221
|
...doc.data(),
|
|
174
222
|
} as Technology)
|
|
175
223
|
);
|
|
224
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
225
|
+
return { technologies, lastVisible: newLastVisible };
|
|
176
226
|
}
|
|
177
227
|
|
|
178
228
|
/**
|
|
179
|
-
*
|
|
180
|
-
* @param
|
|
181
|
-
* @param technology -
|
|
182
|
-
* @returns
|
|
229
|
+
* Updates an existing technology.
|
|
230
|
+
* @param id - The ID of the technology to update.
|
|
231
|
+
* @param technology - New data for the technology.
|
|
232
|
+
* @returns The updated technology.
|
|
183
233
|
*/
|
|
184
234
|
async update(
|
|
185
|
-
|
|
186
|
-
technology: Partial<
|
|
187
|
-
Omit<
|
|
188
|
-
Technology,
|
|
189
|
-
"id" | "createdAt" | "family" | "categoryId" | "subcategoryId"
|
|
190
|
-
>
|
|
191
|
-
>
|
|
235
|
+
id: string,
|
|
236
|
+
technology: Partial<Omit<Technology, "id" | "createdAt">>
|
|
192
237
|
) {
|
|
193
|
-
const updateData = {
|
|
194
|
-
...technology,
|
|
195
|
-
updatedAt: new Date(),
|
|
196
|
-
};
|
|
238
|
+
const updateData: { [key: string]: any } = { ...technology };
|
|
197
239
|
|
|
198
|
-
|
|
240
|
+
// Remove undefined fields to prevent Firestore errors
|
|
241
|
+
Object.keys(updateData).forEach((key) => {
|
|
242
|
+
if (updateData[key] === undefined) {
|
|
243
|
+
delete updateData[key];
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
updateData.updatedAt = new Date();
|
|
248
|
+
|
|
249
|
+
const docRef = doc(this.technologiesRef, id);
|
|
199
250
|
await updateDoc(docRef, updateData);
|
|
200
|
-
return this.getById(
|
|
251
|
+
return this.getById(id);
|
|
201
252
|
}
|
|
202
253
|
|
|
203
254
|
/**
|
|
204
|
-
* Soft
|
|
205
|
-
* @param
|
|
255
|
+
* Soft deletes a technology.
|
|
256
|
+
* @param id - The ID of the technology to delete.
|
|
206
257
|
*/
|
|
207
|
-
async delete(
|
|
208
|
-
await this.update(
|
|
209
|
-
|
|
210
|
-
|
|
258
|
+
async delete(id: string) {
|
|
259
|
+
await this.update(id, { isActive: false });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Reactivates a technology.
|
|
264
|
+
* @param id - The ID of the technology to reactivate.
|
|
265
|
+
*/
|
|
266
|
+
async reactivate(id: string) {
|
|
267
|
+
await this.update(id, { isActive: true });
|
|
211
268
|
}
|
|
212
269
|
|
|
213
270
|
/**
|
|
214
|
-
*
|
|
215
|
-
* @param
|
|
216
|
-
* @returns
|
|
271
|
+
* Returns a technology by its ID.
|
|
272
|
+
* @param id - The ID of the requested technology.
|
|
273
|
+
* @returns The technology or null if it doesn't exist.
|
|
217
274
|
*/
|
|
218
|
-
async getById(
|
|
219
|
-
const docRef = doc(this.
|
|
275
|
+
async getById(id: string): Promise<Technology | null> {
|
|
276
|
+
const docRef = doc(this.technologiesRef, id);
|
|
220
277
|
const docSnap = await getDoc(docRef);
|
|
221
278
|
if (!docSnap.exists()) return null;
|
|
222
279
|
return {
|
|
@@ -232,7 +289,7 @@ export class TechnologyService extends BaseService {
|
|
|
232
289
|
* @returns Ažurirana tehnologija sa novim zahtevom
|
|
233
290
|
*/
|
|
234
291
|
async addRequirement(technologyId: string, requirement: Requirement) {
|
|
235
|
-
const docRef = doc(this.
|
|
292
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
236
293
|
|
|
237
294
|
const requirementType =
|
|
238
295
|
requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
@@ -252,7 +309,7 @@ export class TechnologyService extends BaseService {
|
|
|
252
309
|
* @returns Ažurirana tehnologija bez uklonjenog zahteva
|
|
253
310
|
*/
|
|
254
311
|
async removeRequirement(technologyId: string, requirement: Requirement) {
|
|
255
|
-
const docRef = doc(this.
|
|
312
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
256
313
|
|
|
257
314
|
const requirementType =
|
|
258
315
|
requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
@@ -308,7 +365,7 @@ export class TechnologyService extends BaseService {
|
|
|
308
365
|
technologyId: string,
|
|
309
366
|
condition: BlockingCondition
|
|
310
367
|
) {
|
|
311
|
-
const docRef = doc(this.
|
|
368
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
312
369
|
|
|
313
370
|
await updateDoc(docRef, {
|
|
314
371
|
blockingConditions: arrayUnion(condition),
|
|
@@ -328,7 +385,7 @@ export class TechnologyService extends BaseService {
|
|
|
328
385
|
technologyId: string,
|
|
329
386
|
condition: BlockingCondition
|
|
330
387
|
) {
|
|
331
|
-
const docRef = doc(this.
|
|
388
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
332
389
|
|
|
333
390
|
await updateDoc(docRef, {
|
|
334
391
|
blockingConditions: arrayRemove(condition),
|
|
@@ -346,12 +403,21 @@ export class TechnologyService extends BaseService {
|
|
|
346
403
|
*/
|
|
347
404
|
async addContraindication(
|
|
348
405
|
technologyId: string,
|
|
349
|
-
contraindication:
|
|
406
|
+
contraindication: ContraindicationDynamic
|
|
350
407
|
) {
|
|
351
|
-
const docRef = doc(this.
|
|
408
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
409
|
+
const technology = await this.getById(technologyId);
|
|
410
|
+
if (!technology) {
|
|
411
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const existingContraindications = technology.contraindications || [];
|
|
415
|
+
if (existingContraindications.some((c) => c.id === contraindication.id)) {
|
|
416
|
+
return technology; // Already exists, do nothing
|
|
417
|
+
}
|
|
352
418
|
|
|
353
419
|
await updateDoc(docRef, {
|
|
354
|
-
contraindications:
|
|
420
|
+
contraindications: [...existingContraindications, contraindication],
|
|
355
421
|
updatedAt: new Date(),
|
|
356
422
|
});
|
|
357
423
|
|
|
@@ -366,12 +432,62 @@ export class TechnologyService extends BaseService {
|
|
|
366
432
|
*/
|
|
367
433
|
async removeContraindication(
|
|
368
434
|
technologyId: string,
|
|
369
|
-
contraindication:
|
|
435
|
+
contraindication: ContraindicationDynamic
|
|
370
436
|
) {
|
|
371
|
-
const docRef = doc(this.
|
|
437
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
438
|
+
const technology = await this.getById(technologyId);
|
|
439
|
+
if (!technology) {
|
|
440
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const updatedContraindications = (
|
|
444
|
+
technology.contraindications || []
|
|
445
|
+
).filter((c) => c.id !== contraindication.id);
|
|
372
446
|
|
|
373
447
|
await updateDoc(docRef, {
|
|
374
|
-
contraindications:
|
|
448
|
+
contraindications: updatedContraindications,
|
|
449
|
+
updatedAt: new Date(),
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
return this.getById(technologyId);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Updates an existing contraindication in a technology's list.
|
|
457
|
+
* If the contraindication does not exist, it will not be added.
|
|
458
|
+
* @param technologyId - ID of the technology
|
|
459
|
+
* @param contraindication - The updated contraindication object
|
|
460
|
+
* @returns The updated technology
|
|
461
|
+
*/
|
|
462
|
+
async updateContraindication(
|
|
463
|
+
technologyId: string,
|
|
464
|
+
contraindication: ContraindicationDynamic
|
|
465
|
+
) {
|
|
466
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
467
|
+
const technology = await this.getById(technologyId);
|
|
468
|
+
if (!technology) {
|
|
469
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const contraindications = technology.contraindications || [];
|
|
473
|
+
const index = contraindications.findIndex(
|
|
474
|
+
(c) => c.id === contraindication.id
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
if (index === -1) {
|
|
478
|
+
// If contraindication doesn't exist, do not update
|
|
479
|
+
// Consider throwing an error if this is an unexpected state
|
|
480
|
+
console.warn(
|
|
481
|
+
`Contraindication with id ${contraindication.id} not found for technology ${technologyId}. No update performed.`
|
|
482
|
+
);
|
|
483
|
+
return technology;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const updatedContraindications = [...contraindications];
|
|
487
|
+
updatedContraindications[index] = contraindication;
|
|
488
|
+
|
|
489
|
+
await updateDoc(docRef, {
|
|
490
|
+
contraindications: updatedContraindications,
|
|
375
491
|
updatedAt: new Date(),
|
|
376
492
|
});
|
|
377
493
|
|
|
@@ -384,11 +500,20 @@ export class TechnologyService extends BaseService {
|
|
|
384
500
|
* @param benefit - Benefit koji se dodaje
|
|
385
501
|
* @returns Ažurirana tehnologija
|
|
386
502
|
*/
|
|
387
|
-
async addBenefit(technologyId: string, benefit:
|
|
388
|
-
const docRef = doc(this.
|
|
503
|
+
async addBenefit(technologyId: string, benefit: TreatmentBenefitDynamic) {
|
|
504
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
505
|
+
const technology = await this.getById(technologyId);
|
|
506
|
+
if (!technology) {
|
|
507
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const existingBenefits = technology.benefits || [];
|
|
511
|
+
if (existingBenefits.some((b) => b.id === benefit.id)) {
|
|
512
|
+
return technology; // Already exists, do nothing
|
|
513
|
+
}
|
|
389
514
|
|
|
390
515
|
await updateDoc(docRef, {
|
|
391
|
-
benefits:
|
|
516
|
+
benefits: [...existingBenefits, benefit],
|
|
392
517
|
updatedAt: new Date(),
|
|
393
518
|
});
|
|
394
519
|
|
|
@@ -401,11 +526,55 @@ export class TechnologyService extends BaseService {
|
|
|
401
526
|
* @param benefit - Benefit koji se uklanja
|
|
402
527
|
* @returns Ažurirana tehnologija
|
|
403
528
|
*/
|
|
404
|
-
async removeBenefit(technologyId: string, benefit:
|
|
405
|
-
const docRef = doc(this.
|
|
529
|
+
async removeBenefit(technologyId: string, benefit: TreatmentBenefitDynamic) {
|
|
530
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
531
|
+
const technology = await this.getById(technologyId);
|
|
532
|
+
if (!technology) {
|
|
533
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const updatedBenefits = (technology.benefits || []).filter(
|
|
537
|
+
(b) => b.id !== benefit.id
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
await updateDoc(docRef, {
|
|
541
|
+
benefits: updatedBenefits,
|
|
542
|
+
updatedAt: new Date(),
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
return this.getById(technologyId);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Updates an existing benefit in a technology's list.
|
|
550
|
+
* If the benefit does not exist, it will not be added.
|
|
551
|
+
* @param technologyId - ID of the technology
|
|
552
|
+
* @param benefit - The updated benefit object
|
|
553
|
+
* @returns The updated technology
|
|
554
|
+
*/
|
|
555
|
+
async updateBenefit(technologyId: string, benefit: TreatmentBenefitDynamic) {
|
|
556
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
557
|
+
const technology = await this.getById(technologyId);
|
|
558
|
+
if (!technology) {
|
|
559
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const benefits = technology.benefits || [];
|
|
563
|
+
const index = benefits.findIndex((b) => b.id === benefit.id);
|
|
564
|
+
|
|
565
|
+
if (index === -1) {
|
|
566
|
+
// If benefit doesn't exist, do not update
|
|
567
|
+
console.warn(
|
|
568
|
+
`Benefit with id ${benefit.id} not found for technology ${technologyId}. No update performed.`
|
|
569
|
+
);
|
|
570
|
+
return technology;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const updatedBenefits = [...benefits];
|
|
574
|
+
updatedBenefits[index] = benefit;
|
|
406
575
|
|
|
407
576
|
await updateDoc(docRef, {
|
|
408
|
-
benefits:
|
|
577
|
+
benefits: updatedBenefits,
|
|
409
578
|
updatedAt: new Date(),
|
|
410
579
|
});
|
|
411
580
|
|
|
@@ -452,7 +621,7 @@ export class TechnologyService extends BaseService {
|
|
|
452
621
|
technologyId: string,
|
|
453
622
|
certificationRequirement: CertificationRequirement
|
|
454
623
|
) {
|
|
455
|
-
const docRef = doc(this.
|
|
624
|
+
const docRef = doc(this.technologiesRef, technologyId);
|
|
456
625
|
|
|
457
626
|
await updateDoc(docRef, {
|
|
458
627
|
certificationRequirement,
|
|
@@ -557,11 +726,12 @@ export class TechnologyService extends BaseService {
|
|
|
557
726
|
const allTechnologies = await this.getAll();
|
|
558
727
|
|
|
559
728
|
// Filter technologies based on certification requirements
|
|
560
|
-
const allowedTechnologies = allTechnologies.filter(
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
729
|
+
const allowedTechnologies = allTechnologies.technologies.filter(
|
|
730
|
+
(technology) =>
|
|
731
|
+
this.validateCertification(
|
|
732
|
+
technology.certificationRequirement,
|
|
733
|
+
practitioner.certification
|
|
734
|
+
)
|
|
565
735
|
);
|
|
566
736
|
|
|
567
737
|
// Extract unique families, categories, and subcategories
|
|
@@ -580,4 +750,49 @@ export class TechnologyService extends BaseService {
|
|
|
580
750
|
subcategories,
|
|
581
751
|
};
|
|
582
752
|
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Gets all active technologies for a subcategory for filter dropdowns.
|
|
756
|
+
* @param categoryId - The ID of the parent category.
|
|
757
|
+
* @param subcategoryId - The ID of the subcategory.
|
|
758
|
+
*/
|
|
759
|
+
async getAllForFilterBySubcategoryId(
|
|
760
|
+
categoryId: string,
|
|
761
|
+
subcategoryId: string
|
|
762
|
+
): Promise<Technology[]> {
|
|
763
|
+
const q = query(
|
|
764
|
+
collection(this.db, TECHNOLOGIES_COLLECTION),
|
|
765
|
+
where("isActive", "==", true),
|
|
766
|
+
where("categoryId", "==", categoryId),
|
|
767
|
+
where("subcategoryId", "==", subcategoryId),
|
|
768
|
+
orderBy("name")
|
|
769
|
+
);
|
|
770
|
+
const snapshot = await getDocs(q);
|
|
771
|
+
return snapshot.docs.map(
|
|
772
|
+
(doc) =>
|
|
773
|
+
({
|
|
774
|
+
id: doc.id,
|
|
775
|
+
...doc.data(),
|
|
776
|
+
} as Technology)
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Gets all active technologies for filter dropdowns.
|
|
782
|
+
*/
|
|
783
|
+
async getAllForFilter(): Promise<Technology[]> {
|
|
784
|
+
const q = query(
|
|
785
|
+
collection(this.db, TECHNOLOGIES_COLLECTION),
|
|
786
|
+
where("isActive", "==", true),
|
|
787
|
+
orderBy("name")
|
|
788
|
+
);
|
|
789
|
+
const snapshot = await getDocs(q);
|
|
790
|
+
return snapshot.docs.map(
|
|
791
|
+
(doc) =>
|
|
792
|
+
({
|
|
793
|
+
id: doc.id,
|
|
794
|
+
...doc.data(),
|
|
795
|
+
} as Technology)
|
|
796
|
+
);
|
|
797
|
+
}
|
|
583
798
|
}
|