@blackcode_sa/metaestetics-api 1.11.2 → 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 +331 -318
- package/dist/admin/index.d.ts +331 -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 +4429 -4034
- package/dist/index.d.ts +4429 -4034
- package/dist/index.js +1644 -666
- package/dist/index.mjs +1408 -402
- 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 +4 -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/appointment.schema.ts +1 -0
- 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
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
getDocs,
|
|
8
8
|
query,
|
|
9
9
|
updateDoc,
|
|
10
|
-
where
|
|
10
|
+
where,
|
|
11
|
+
limit,
|
|
12
|
+
orderBy,
|
|
13
|
+
startAfter,
|
|
14
|
+
getCountFromServer
|
|
11
15
|
} from "firebase/firestore";
|
|
12
16
|
|
|
13
17
|
// src/backoffice/types/brand.types.ts
|
|
@@ -16,11 +20,13 @@ var BRANDS_COLLECTION = "brands";
|
|
|
16
20
|
// src/services/base.service.ts
|
|
17
21
|
import { getStorage } from "firebase/storage";
|
|
18
22
|
var BaseService = class {
|
|
19
|
-
constructor(db, auth, app) {
|
|
23
|
+
constructor(db, auth, app, storage) {
|
|
20
24
|
this.db = db;
|
|
21
25
|
this.auth = auth;
|
|
22
26
|
this.app = app;
|
|
23
|
-
|
|
27
|
+
if (app) {
|
|
28
|
+
this.storage = storage || getStorage(app);
|
|
29
|
+
}
|
|
24
30
|
}
|
|
25
31
|
/**
|
|
26
32
|
* Generiše jedinstveni ID za dokumente
|
|
@@ -53,6 +59,7 @@ var BrandService = class extends BaseService {
|
|
|
53
59
|
const now = /* @__PURE__ */ new Date();
|
|
54
60
|
const newBrand = {
|
|
55
61
|
...brand,
|
|
62
|
+
name_lowercase: brand.name.toLowerCase(),
|
|
56
63
|
createdAt: now,
|
|
57
64
|
updatedAt: now,
|
|
58
65
|
isActive: true
|
|
@@ -61,15 +68,69 @@ var BrandService = class extends BaseService {
|
|
|
61
68
|
return { id: docRef.id, ...newBrand };
|
|
62
69
|
}
|
|
63
70
|
/**
|
|
64
|
-
* Gets
|
|
71
|
+
* Gets a paginated list of active brands, optionally filtered by name.
|
|
72
|
+
* @param rowsPerPage - The number of brands to fetch.
|
|
73
|
+
* @param searchTerm - An optional string to filter brand names by (starts-with search).
|
|
74
|
+
* @param lastVisible - An optional document snapshot to use as a cursor for pagination.
|
|
65
75
|
*/
|
|
66
|
-
async getAll() {
|
|
67
|
-
const
|
|
76
|
+
async getAll(rowsPerPage, searchTerm, lastVisible) {
|
|
77
|
+
const constraints = [
|
|
78
|
+
where("isActive", "==", true),
|
|
79
|
+
orderBy("name_lowercase")
|
|
80
|
+
];
|
|
81
|
+
if (searchTerm) {
|
|
82
|
+
const lowercasedSearchTerm = searchTerm.toLowerCase();
|
|
83
|
+
constraints.push(where("name_lowercase", ">=", lowercasedSearchTerm));
|
|
84
|
+
constraints.push(
|
|
85
|
+
where("name_lowercase", "<=", lowercasedSearchTerm + "\uF8FF")
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (lastVisible) {
|
|
89
|
+
constraints.push(startAfter(lastVisible));
|
|
90
|
+
}
|
|
91
|
+
constraints.push(limit(rowsPerPage));
|
|
92
|
+
const q = query(this.getBrandsRef(), ...constraints);
|
|
93
|
+
const snapshot = await getDocs(q);
|
|
94
|
+
const brands = snapshot.docs.map(
|
|
95
|
+
(doc11) => ({
|
|
96
|
+
id: doc11.id,
|
|
97
|
+
...doc11.data()
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
101
|
+
return { brands, lastVisible: newLastVisible };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Gets the total count of active brands, optionally filtered by name.
|
|
105
|
+
* @param searchTerm - An optional string to filter brand names by (starts-with search).
|
|
106
|
+
*/
|
|
107
|
+
async getBrandsCount(searchTerm) {
|
|
108
|
+
const constraints = [where("isActive", "==", true)];
|
|
109
|
+
if (searchTerm) {
|
|
110
|
+
const lowercasedSearchTerm = searchTerm.toLowerCase();
|
|
111
|
+
constraints.push(where("name_lowercase", ">=", lowercasedSearchTerm));
|
|
112
|
+
constraints.push(
|
|
113
|
+
where("name_lowercase", "<=", lowercasedSearchTerm + "\uF8FF")
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const q = query(this.getBrandsRef(), ...constraints);
|
|
117
|
+
const snapshot = await getCountFromServer(q);
|
|
118
|
+
return snapshot.data().count;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Gets all active brands for filter dropdowns (not paginated).
|
|
122
|
+
*/
|
|
123
|
+
async getAllForFilter() {
|
|
124
|
+
const q = query(
|
|
125
|
+
this.getBrandsRef(),
|
|
126
|
+
where("isActive", "==", true),
|
|
127
|
+
orderBy("name")
|
|
128
|
+
);
|
|
68
129
|
const snapshot = await getDocs(q);
|
|
69
130
|
return snapshot.docs.map(
|
|
70
|
-
(
|
|
71
|
-
id:
|
|
72
|
-
...
|
|
131
|
+
(doc11) => ({
|
|
132
|
+
id: doc11.id,
|
|
133
|
+
...doc11.data()
|
|
73
134
|
})
|
|
74
135
|
);
|
|
75
136
|
}
|
|
@@ -81,6 +142,9 @@ var BrandService = class extends BaseService {
|
|
|
81
142
|
...brand,
|
|
82
143
|
updatedAt: /* @__PURE__ */ new Date()
|
|
83
144
|
};
|
|
145
|
+
if (brand.name) {
|
|
146
|
+
updateData.name_lowercase = brand.name.toLowerCase();
|
|
147
|
+
}
|
|
84
148
|
const docRef = doc(this.getBrandsRef(), brandId);
|
|
85
149
|
await updateDoc(docRef, updateData);
|
|
86
150
|
return this.getById(brandId);
|
|
@@ -112,9 +176,13 @@ import {
|
|
|
112
176
|
addDoc as addDoc2,
|
|
113
177
|
collection as collection2,
|
|
114
178
|
doc as doc2,
|
|
179
|
+
getCountFromServer as getCountFromServer2,
|
|
115
180
|
getDoc as getDoc2,
|
|
116
181
|
getDocs as getDocs2,
|
|
182
|
+
limit as limit2,
|
|
183
|
+
orderBy as orderBy2,
|
|
117
184
|
query as query2,
|
|
185
|
+
startAfter as startAfter2,
|
|
118
186
|
updateDoc as updateDoc2,
|
|
119
187
|
where as where2
|
|
120
188
|
} from "firebase/firestore";
|
|
@@ -122,6 +190,13 @@ import {
|
|
|
122
190
|
// src/backoffice/types/category.types.ts
|
|
123
191
|
var CATEGORIES_COLLECTION = "backoffice_categories";
|
|
124
192
|
|
|
193
|
+
// src/backoffice/types/static/procedure-family.types.ts
|
|
194
|
+
var ProcedureFamily = /* @__PURE__ */ ((ProcedureFamily2) => {
|
|
195
|
+
ProcedureFamily2["AESTHETICS"] = "aesthetics";
|
|
196
|
+
ProcedureFamily2["SURGERY"] = "surgery";
|
|
197
|
+
return ProcedureFamily2;
|
|
198
|
+
})(ProcedureFamily || {});
|
|
199
|
+
|
|
125
200
|
// src/backoffice/services/category.service.ts
|
|
126
201
|
var CategoryService = class extends BaseService {
|
|
127
202
|
/**
|
|
@@ -147,37 +222,87 @@ var CategoryService = class extends BaseService {
|
|
|
147
222
|
return { id: docRef.id, ...newCategory };
|
|
148
223
|
}
|
|
149
224
|
/**
|
|
150
|
-
*
|
|
151
|
-
* @
|
|
225
|
+
* Returns counts of categories for each family.
|
|
226
|
+
* @param active - Whether to count active or inactive categories.
|
|
227
|
+
* @returns A record mapping family to category count.
|
|
152
228
|
*/
|
|
153
|
-
async
|
|
229
|
+
async getCategoryCounts(active = true) {
|
|
230
|
+
const counts = {};
|
|
231
|
+
const families = Object.values(ProcedureFamily);
|
|
232
|
+
for (const family of families) {
|
|
233
|
+
const q = query2(
|
|
234
|
+
this.categoriesRef,
|
|
235
|
+
where2("family", "==", family),
|
|
236
|
+
where2("isActive", "==", active)
|
|
237
|
+
);
|
|
238
|
+
const snapshot = await getCountFromServer2(q);
|
|
239
|
+
counts[family] = snapshot.data().count;
|
|
240
|
+
}
|
|
241
|
+
return counts;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Vraća sve kategorije za potrebe filtera (bez paginacije)
|
|
245
|
+
* @returns Lista svih aktivnih kategorija
|
|
246
|
+
*/
|
|
247
|
+
async getAllForFilter() {
|
|
154
248
|
const q = query2(this.categoriesRef, where2("isActive", "==", true));
|
|
155
249
|
const snapshot = await getDocs2(q);
|
|
156
250
|
return snapshot.docs.map(
|
|
157
|
-
(
|
|
158
|
-
id:
|
|
159
|
-
...
|
|
251
|
+
(doc11) => ({
|
|
252
|
+
id: doc11.id,
|
|
253
|
+
...doc11.data()
|
|
160
254
|
})
|
|
161
255
|
);
|
|
162
256
|
}
|
|
163
257
|
/**
|
|
164
|
-
* Vraća sve
|
|
258
|
+
* Vraća sve kategorije sa paginacijom
|
|
259
|
+
* @param options - Pagination and filter options
|
|
260
|
+
* @returns Lista kategorija i poslednji vidljiv dokument
|
|
261
|
+
*/
|
|
262
|
+
async getAll(options = {}) {
|
|
263
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
264
|
+
const constraints = [
|
|
265
|
+
where2("isActive", "==", active),
|
|
266
|
+
orderBy2("name"),
|
|
267
|
+
queryLimit ? limit2(queryLimit) : void 0,
|
|
268
|
+
lastVisible ? startAfter2(lastVisible) : void 0
|
|
269
|
+
].filter((c) => !!c);
|
|
270
|
+
const q = query2(this.categoriesRef, ...constraints);
|
|
271
|
+
const snapshot = await getDocs2(q);
|
|
272
|
+
const categories = snapshot.docs.map(
|
|
273
|
+
(doc11) => ({
|
|
274
|
+
id: doc11.id,
|
|
275
|
+
...doc11.data()
|
|
276
|
+
})
|
|
277
|
+
);
|
|
278
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
279
|
+
return { categories, lastVisible: newLastVisible };
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Vraća sve aktivne kategorije za određenu familiju procedura sa paginacijom
|
|
165
283
|
* @param family - Familija procedura (aesthetics/surgery)
|
|
284
|
+
* @param options - Pagination options
|
|
166
285
|
* @returns Lista kategorija koje pripadaju traženoj familiji
|
|
167
286
|
*/
|
|
168
|
-
async getAllByFamily(family) {
|
|
169
|
-
const
|
|
170
|
-
|
|
287
|
+
async getAllByFamily(family, options = {}) {
|
|
288
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
289
|
+
const constraints = [
|
|
171
290
|
where2("family", "==", family),
|
|
172
|
-
where2("isActive", "==",
|
|
173
|
-
|
|
291
|
+
where2("isActive", "==", active),
|
|
292
|
+
orderBy2("name"),
|
|
293
|
+
queryLimit ? limit2(queryLimit) : void 0,
|
|
294
|
+
lastVisible ? startAfter2(lastVisible) : void 0
|
|
295
|
+
].filter((c) => !!c);
|
|
296
|
+
const q = query2(this.categoriesRef, ...constraints);
|
|
174
297
|
const snapshot = await getDocs2(q);
|
|
175
|
-
|
|
176
|
-
(
|
|
177
|
-
id:
|
|
178
|
-
...
|
|
298
|
+
const categories = snapshot.docs.map(
|
|
299
|
+
(doc11) => ({
|
|
300
|
+
id: doc11.id,
|
|
301
|
+
...doc11.data()
|
|
179
302
|
})
|
|
180
303
|
);
|
|
304
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
305
|
+
return { categories, lastVisible: newLastVisible };
|
|
181
306
|
}
|
|
182
307
|
/**
|
|
183
308
|
* Ažurira postojeću kategoriju
|
|
@@ -201,6 +326,13 @@ var CategoryService = class extends BaseService {
|
|
|
201
326
|
async delete(id) {
|
|
202
327
|
await this.update(id, { isActive: false });
|
|
203
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Reactivates a category by setting its isActive flag to true.
|
|
331
|
+
* @param id - The ID of the category to reactivate.
|
|
332
|
+
*/
|
|
333
|
+
async reactivate(id) {
|
|
334
|
+
await this.update(id, { isActive: true });
|
|
335
|
+
}
|
|
204
336
|
/**
|
|
205
337
|
* Vraća kategoriju po ID-u
|
|
206
338
|
* @param id - ID tražene kategorije
|
|
@@ -228,9 +360,9 @@ import {
|
|
|
228
360
|
deleteDoc,
|
|
229
361
|
query as query3,
|
|
230
362
|
where as where3,
|
|
231
|
-
orderBy,
|
|
232
|
-
limit,
|
|
233
|
-
startAfter
|
|
363
|
+
orderBy as orderBy3,
|
|
364
|
+
limit as limit3,
|
|
365
|
+
startAfter as startAfter3
|
|
234
366
|
} from "firebase/firestore";
|
|
235
367
|
|
|
236
368
|
// src/types/documentation-templates/index.ts
|
|
@@ -465,9 +597,10 @@ var updateFilledDocumentDataSchema = z.object({
|
|
|
465
597
|
});
|
|
466
598
|
|
|
467
599
|
// src/services/documentation-templates/documentation-template.service.ts
|
|
600
|
+
import { getCountFromServer as getCountFromServer3 } from "firebase/firestore";
|
|
468
601
|
var DocumentationTemplateService = class extends BaseService {
|
|
469
|
-
constructor() {
|
|
470
|
-
super(...
|
|
602
|
+
constructor(...args) {
|
|
603
|
+
super(...args);
|
|
471
604
|
this.collectionRef = collection3(
|
|
472
605
|
this.db,
|
|
473
606
|
DOCUMENTATION_TEMPLATES_COLLECTION
|
|
@@ -616,11 +749,11 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
616
749
|
this.db,
|
|
617
750
|
`${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions`
|
|
618
751
|
);
|
|
619
|
-
const q = query3(versionsCollectionRef,
|
|
752
|
+
const q = query3(versionsCollectionRef, orderBy3("version", "desc"));
|
|
620
753
|
const querySnapshot = await getDocs3(q);
|
|
621
754
|
const versions = [];
|
|
622
|
-
querySnapshot.forEach((
|
|
623
|
-
versions.push(
|
|
755
|
+
querySnapshot.forEach((doc11) => {
|
|
756
|
+
versions.push(doc11.data());
|
|
624
757
|
});
|
|
625
758
|
return versions;
|
|
626
759
|
}
|
|
@@ -642,24 +775,106 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
642
775
|
let q = query3(
|
|
643
776
|
this.collectionRef,
|
|
644
777
|
where3("isActive", "==", true),
|
|
645
|
-
|
|
646
|
-
|
|
778
|
+
orderBy3("updatedAt", "desc"),
|
|
779
|
+
limit3(pageSize)
|
|
647
780
|
);
|
|
648
781
|
if (lastDoc) {
|
|
649
|
-
q = query3(q,
|
|
782
|
+
q = query3(q, startAfter3(lastDoc));
|
|
783
|
+
}
|
|
784
|
+
const querySnapshot = await getDocs3(q);
|
|
785
|
+
const templates = [];
|
|
786
|
+
let lastVisible = null;
|
|
787
|
+
querySnapshot.forEach((doc11) => {
|
|
788
|
+
templates.push(doc11.data());
|
|
789
|
+
lastVisible = doc11;
|
|
790
|
+
});
|
|
791
|
+
return {
|
|
792
|
+
templates,
|
|
793
|
+
lastDoc: lastVisible
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Get all active templates with optional filters and pagination.
|
|
798
|
+
* @param options - Options for filtering and pagination.
|
|
799
|
+
* @returns A promise that resolves to the templates and the last visible document.
|
|
800
|
+
*/
|
|
801
|
+
async getTemplates(options) {
|
|
802
|
+
const {
|
|
803
|
+
pageSize = 20,
|
|
804
|
+
lastDoc,
|
|
805
|
+
isUserForm,
|
|
806
|
+
isRequired,
|
|
807
|
+
sortingOrder
|
|
808
|
+
} = options;
|
|
809
|
+
const constraints = [
|
|
810
|
+
where3("isActive", "==", true),
|
|
811
|
+
orderBy3("sortingOrder", "asc"),
|
|
812
|
+
orderBy3("title", "asc"),
|
|
813
|
+
limit3(pageSize)
|
|
814
|
+
];
|
|
815
|
+
if (isUserForm !== void 0) {
|
|
816
|
+
constraints.push(where3("isUserForm", "==", isUserForm));
|
|
817
|
+
}
|
|
818
|
+
if (isRequired !== void 0) {
|
|
819
|
+
constraints.push(where3("isRequired", "==", isRequired));
|
|
820
|
+
}
|
|
821
|
+
if (sortingOrder !== void 0) {
|
|
822
|
+
constraints.push(where3("sortingOrder", "==", sortingOrder));
|
|
823
|
+
}
|
|
824
|
+
if (lastDoc) {
|
|
825
|
+
constraints.push(startAfter3(lastDoc));
|
|
650
826
|
}
|
|
827
|
+
const q = query3(this.collectionRef, ...constraints.filter((c) => c));
|
|
651
828
|
const querySnapshot = await getDocs3(q);
|
|
652
829
|
const templates = [];
|
|
653
830
|
let lastVisible = null;
|
|
654
|
-
querySnapshot.forEach((
|
|
655
|
-
templates.push(
|
|
656
|
-
lastVisible =
|
|
831
|
+
querySnapshot.forEach((doc11) => {
|
|
832
|
+
templates.push(doc11.data());
|
|
833
|
+
lastVisible = doc11;
|
|
657
834
|
});
|
|
658
835
|
return {
|
|
659
836
|
templates,
|
|
660
837
|
lastDoc: lastVisible
|
|
661
838
|
};
|
|
662
839
|
}
|
|
840
|
+
/**
|
|
841
|
+
* Get the total count of active templates with optional filters.
|
|
842
|
+
* @param options - Options for filtering.
|
|
843
|
+
* @returns A promise that resolves to the total count of templates.
|
|
844
|
+
*/
|
|
845
|
+
async getTemplatesCount(options) {
|
|
846
|
+
const { isUserForm, isRequired, sortingOrder } = options;
|
|
847
|
+
const constraints = [where3("isActive", "==", true)];
|
|
848
|
+
if (isUserForm !== void 0) {
|
|
849
|
+
constraints.push(where3("isUserForm", "==", isUserForm));
|
|
850
|
+
}
|
|
851
|
+
if (isRequired !== void 0) {
|
|
852
|
+
constraints.push(where3("isRequired", "==", isRequired));
|
|
853
|
+
}
|
|
854
|
+
if (sortingOrder !== void 0) {
|
|
855
|
+
constraints.push(where3("sortingOrder", "==", sortingOrder));
|
|
856
|
+
}
|
|
857
|
+
const q = query3(this.collectionRef, ...constraints.filter((c) => c));
|
|
858
|
+
const snapshot = await getCountFromServer3(q);
|
|
859
|
+
return snapshot.data().count;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Get all active templates without pagination for filtering purposes.
|
|
863
|
+
* @returns A promise that resolves to an array of all active templates.
|
|
864
|
+
*/
|
|
865
|
+
async getAllActiveTemplates() {
|
|
866
|
+
const q = query3(
|
|
867
|
+
this.collectionRef,
|
|
868
|
+
where3("isActive", "==", true),
|
|
869
|
+
orderBy3("title", "asc")
|
|
870
|
+
);
|
|
871
|
+
const querySnapshot = await getDocs3(q);
|
|
872
|
+
const templates = [];
|
|
873
|
+
querySnapshot.forEach((doc11) => {
|
|
874
|
+
templates.push(doc11.data());
|
|
875
|
+
});
|
|
876
|
+
return templates;
|
|
877
|
+
}
|
|
663
878
|
/**
|
|
664
879
|
* Get templates by tags
|
|
665
880
|
* @param tags - Tags to filter by
|
|
@@ -672,18 +887,18 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
672
887
|
this.collectionRef,
|
|
673
888
|
where3("isActive", "==", true),
|
|
674
889
|
where3("tags", "array-contains-any", tags),
|
|
675
|
-
|
|
676
|
-
|
|
890
|
+
orderBy3("updatedAt", "desc"),
|
|
891
|
+
limit3(pageSize)
|
|
677
892
|
);
|
|
678
893
|
if (lastDoc) {
|
|
679
|
-
q = query3(q,
|
|
894
|
+
q = query3(q, startAfter3(lastDoc));
|
|
680
895
|
}
|
|
681
896
|
const querySnapshot = await getDocs3(q);
|
|
682
897
|
const templates = [];
|
|
683
898
|
let lastVisible = null;
|
|
684
|
-
querySnapshot.forEach((
|
|
685
|
-
templates.push(
|
|
686
|
-
lastVisible =
|
|
899
|
+
querySnapshot.forEach((doc11) => {
|
|
900
|
+
templates.push(doc11.data());
|
|
901
|
+
lastVisible = doc11;
|
|
687
902
|
});
|
|
688
903
|
return {
|
|
689
904
|
templates,
|
|
@@ -701,18 +916,18 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
701
916
|
let q = query3(
|
|
702
917
|
this.collectionRef,
|
|
703
918
|
where3("createdBy", "==", userId),
|
|
704
|
-
|
|
705
|
-
|
|
919
|
+
orderBy3("updatedAt", "desc"),
|
|
920
|
+
limit3(pageSize)
|
|
706
921
|
);
|
|
707
922
|
if (lastDoc) {
|
|
708
|
-
q = query3(q,
|
|
923
|
+
q = query3(q, startAfter3(lastDoc));
|
|
709
924
|
}
|
|
710
925
|
const querySnapshot = await getDocs3(q);
|
|
711
926
|
const templates = [];
|
|
712
927
|
let lastVisible = null;
|
|
713
|
-
querySnapshot.forEach((
|
|
714
|
-
templates.push(
|
|
715
|
-
lastVisible =
|
|
928
|
+
querySnapshot.forEach((doc11) => {
|
|
929
|
+
templates.push(doc11.data());
|
|
930
|
+
lastVisible = doc11;
|
|
716
931
|
});
|
|
717
932
|
return {
|
|
718
933
|
templates,
|
|
@@ -728,7 +943,7 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
728
943
|
let q = query3(
|
|
729
944
|
this.collectionRef,
|
|
730
945
|
where3("isActive", "==", true),
|
|
731
|
-
|
|
946
|
+
orderBy3("updatedAt", "desc")
|
|
732
947
|
);
|
|
733
948
|
if ((options == null ? void 0 : options.isUserForm) !== void 0) {
|
|
734
949
|
q = query3(q, where3("isUserForm", "==", options.isUserForm));
|
|
@@ -738,8 +953,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
738
953
|
}
|
|
739
954
|
const querySnapshot = await getDocs3(q);
|
|
740
955
|
const templates = [];
|
|
741
|
-
querySnapshot.forEach((
|
|
742
|
-
templates.push(
|
|
956
|
+
querySnapshot.forEach((doc11) => {
|
|
957
|
+
templates.push(doc11.data());
|
|
743
958
|
});
|
|
744
959
|
return templates;
|
|
745
960
|
}
|
|
@@ -754,9 +969,9 @@ import {
|
|
|
754
969
|
setDoc as setDoc3,
|
|
755
970
|
updateDoc as updateDoc5,
|
|
756
971
|
query as query5,
|
|
757
|
-
orderBy as
|
|
758
|
-
limit as
|
|
759
|
-
startAfter as
|
|
972
|
+
orderBy as orderBy5,
|
|
973
|
+
limit as limit5,
|
|
974
|
+
startAfter as startAfter4
|
|
760
975
|
} from "firebase/firestore";
|
|
761
976
|
|
|
762
977
|
// src/services/media/media.service.ts
|
|
@@ -776,10 +991,10 @@ import {
|
|
|
776
991
|
collection as collection4,
|
|
777
992
|
query as query4,
|
|
778
993
|
where as where4,
|
|
779
|
-
limit as
|
|
994
|
+
limit as limit4,
|
|
780
995
|
getDocs as getDocs4,
|
|
781
996
|
deleteDoc as deleteDoc2,
|
|
782
|
-
orderBy as
|
|
997
|
+
orderBy as orderBy4
|
|
783
998
|
} from "firebase/firestore";
|
|
784
999
|
|
|
785
1000
|
// src/backoffice/services/documentation-template.service.ts
|
|
@@ -790,8 +1005,8 @@ var DocumentationTemplateServiceBackoffice = class {
|
|
|
790
1005
|
* @param auth - Firebase Auth instance
|
|
791
1006
|
* @param app - Firebase App instance
|
|
792
1007
|
*/
|
|
793
|
-
constructor(
|
|
794
|
-
this.apiService = new DocumentationTemplateService(
|
|
1008
|
+
constructor(...args) {
|
|
1009
|
+
this.apiService = new DocumentationTemplateService(...args);
|
|
795
1010
|
}
|
|
796
1011
|
/**
|
|
797
1012
|
* Create a new document template
|
|
@@ -836,6 +1051,40 @@ var DocumentationTemplateServiceBackoffice = class {
|
|
|
836
1051
|
async getActiveTemplates(pageSize = 20, lastDoc) {
|
|
837
1052
|
return this.apiService.getActiveTemplates(pageSize, lastDoc);
|
|
838
1053
|
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Get all active templates with optional filters and pagination.
|
|
1056
|
+
* @param options - Options for filtering and pagination.
|
|
1057
|
+
* @returns A promise that resolves to the templates and the last visible document.
|
|
1058
|
+
*/
|
|
1059
|
+
async getTemplates(options) {
|
|
1060
|
+
return this.apiService.getTemplates(options);
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Get the total count of active templates with optional filters.
|
|
1064
|
+
* @param options - Options for filtering.
|
|
1065
|
+
* @returns A promise that resolves to the total count of templates.
|
|
1066
|
+
*/
|
|
1067
|
+
async getTemplatesCount(options) {
|
|
1068
|
+
return this.apiService.getTemplatesCount(options);
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Get all active templates without pagination for filtering purposes.
|
|
1072
|
+
* @returns A promise that resolves to an array of all active templates.
|
|
1073
|
+
*/
|
|
1074
|
+
async getAllActiveTemplates() {
|
|
1075
|
+
return this.apiService.getAllActiveTemplates();
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Searches for active templates by title.
|
|
1079
|
+
* @param title - The title to search for.
|
|
1080
|
+
* @returns A list of templates that match the search criteria.
|
|
1081
|
+
*/
|
|
1082
|
+
async search(title) {
|
|
1083
|
+
const { templates } = await this.apiService.getActiveTemplates(1e3);
|
|
1084
|
+
return templates.filter(
|
|
1085
|
+
(t) => t.title.toLowerCase().includes(title.toLowerCase())
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
839
1088
|
/**
|
|
840
1089
|
* Get templates by tags
|
|
841
1090
|
* @param tags - Tags to filter by
|
|
@@ -879,12 +1128,17 @@ var DocumentationTemplateServiceBackoffice = class {
|
|
|
879
1128
|
import {
|
|
880
1129
|
addDoc as addDoc3,
|
|
881
1130
|
collection as collection6,
|
|
1131
|
+
collectionGroup,
|
|
882
1132
|
doc as doc6,
|
|
883
1133
|
getDoc as getDoc6,
|
|
884
1134
|
getDocs as getDocs6,
|
|
885
1135
|
query as query6,
|
|
886
1136
|
updateDoc as updateDoc6,
|
|
887
|
-
where as where6
|
|
1137
|
+
where as where6,
|
|
1138
|
+
limit as limit6,
|
|
1139
|
+
orderBy as orderBy6,
|
|
1140
|
+
startAfter as startAfter5,
|
|
1141
|
+
getCountFromServer as getCountFromServer4
|
|
888
1142
|
} from "firebase/firestore";
|
|
889
1143
|
|
|
890
1144
|
// src/backoffice/types/product.types.ts
|
|
@@ -928,20 +1182,102 @@ var ProductService = class extends BaseService {
|
|
|
928
1182
|
return { id: productRef.id, ...newProduct };
|
|
929
1183
|
}
|
|
930
1184
|
/**
|
|
931
|
-
* Gets all products
|
|
1185
|
+
* Gets a paginated list of all products, with optional filters.
|
|
1186
|
+
* This uses a collectionGroup query to search across all technologies.
|
|
932
1187
|
*/
|
|
933
|
-
async
|
|
1188
|
+
async getAll(options) {
|
|
1189
|
+
const {
|
|
1190
|
+
rowsPerPage,
|
|
1191
|
+
lastVisible,
|
|
1192
|
+
categoryId,
|
|
1193
|
+
subcategoryId,
|
|
1194
|
+
technologyId
|
|
1195
|
+
} = options;
|
|
1196
|
+
const constraints = [
|
|
1197
|
+
where6("isActive", "==", true),
|
|
1198
|
+
orderBy6("name")
|
|
1199
|
+
];
|
|
1200
|
+
if (categoryId) {
|
|
1201
|
+
constraints.push(where6("categoryId", "==", categoryId));
|
|
1202
|
+
}
|
|
1203
|
+
if (subcategoryId) {
|
|
1204
|
+
constraints.push(where6("subcategoryId", "==", subcategoryId));
|
|
1205
|
+
}
|
|
1206
|
+
if (technologyId) {
|
|
1207
|
+
constraints.push(where6("technologyId", "==", technologyId));
|
|
1208
|
+
}
|
|
1209
|
+
if (lastVisible) {
|
|
1210
|
+
constraints.push(startAfter5(lastVisible));
|
|
1211
|
+
}
|
|
1212
|
+
constraints.push(limit6(rowsPerPage));
|
|
934
1213
|
const q = query6(
|
|
935
|
-
this.
|
|
936
|
-
|
|
1214
|
+
collectionGroup(this.db, PRODUCTS_COLLECTION),
|
|
1215
|
+
...constraints
|
|
937
1216
|
);
|
|
938
1217
|
const snapshot = await getDocs6(q);
|
|
939
|
-
|
|
940
|
-
(
|
|
941
|
-
id:
|
|
942
|
-
...
|
|
1218
|
+
const products = snapshot.docs.map(
|
|
1219
|
+
(doc11) => ({
|
|
1220
|
+
id: doc11.id,
|
|
1221
|
+
...doc11.data()
|
|
943
1222
|
})
|
|
944
1223
|
);
|
|
1224
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
1225
|
+
return { products, lastVisible: newLastVisible };
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Gets the total count of active products, with optional filters.
|
|
1229
|
+
*/
|
|
1230
|
+
async getProductsCount(options) {
|
|
1231
|
+
const { categoryId, subcategoryId, technologyId } = options;
|
|
1232
|
+
const constraints = [where6("isActive", "==", true)];
|
|
1233
|
+
if (categoryId) {
|
|
1234
|
+
constraints.push(where6("categoryId", "==", categoryId));
|
|
1235
|
+
}
|
|
1236
|
+
if (subcategoryId) {
|
|
1237
|
+
constraints.push(where6("subcategoryId", "==", subcategoryId));
|
|
1238
|
+
}
|
|
1239
|
+
if (technologyId) {
|
|
1240
|
+
constraints.push(where6("technologyId", "==", technologyId));
|
|
1241
|
+
}
|
|
1242
|
+
const q = query6(
|
|
1243
|
+
collectionGroup(this.db, PRODUCTS_COLLECTION),
|
|
1244
|
+
...constraints
|
|
1245
|
+
);
|
|
1246
|
+
const snapshot = await getCountFromServer4(q);
|
|
1247
|
+
return snapshot.data().count;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Gets counts of active products grouped by category, subcategory, and technology.
|
|
1251
|
+
* This uses a single collectionGroup query for efficiency.
|
|
1252
|
+
*/
|
|
1253
|
+
async getProductCounts() {
|
|
1254
|
+
const q = query6(
|
|
1255
|
+
collectionGroup(this.db, PRODUCTS_COLLECTION),
|
|
1256
|
+
where6("isActive", "==", true)
|
|
1257
|
+
);
|
|
1258
|
+
const snapshot = await getDocs6(q);
|
|
1259
|
+
const counts = {
|
|
1260
|
+
byCategory: {},
|
|
1261
|
+
bySubcategory: {},
|
|
1262
|
+
byTechnology: {}
|
|
1263
|
+
};
|
|
1264
|
+
if (snapshot.empty) {
|
|
1265
|
+
return counts;
|
|
1266
|
+
}
|
|
1267
|
+
snapshot.docs.forEach((doc11) => {
|
|
1268
|
+
const product = doc11.data();
|
|
1269
|
+
const { categoryId, subcategoryId, technologyId } = product;
|
|
1270
|
+
if (categoryId) {
|
|
1271
|
+
counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
|
|
1272
|
+
}
|
|
1273
|
+
if (subcategoryId) {
|
|
1274
|
+
counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
|
|
1275
|
+
}
|
|
1276
|
+
if (technologyId) {
|
|
1277
|
+
counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
return counts;
|
|
945
1281
|
}
|
|
946
1282
|
/**
|
|
947
1283
|
* Gets all products for a brand by filtering through all technologies
|
|
@@ -959,9 +1295,9 @@ var ProductService = class extends BaseService {
|
|
|
959
1295
|
const snapshot = await getDocs6(q);
|
|
960
1296
|
products.push(
|
|
961
1297
|
...snapshot.docs.map(
|
|
962
|
-
(
|
|
963
|
-
id:
|
|
964
|
-
...
|
|
1298
|
+
(doc11) => ({
|
|
1299
|
+
id: doc11.id,
|
|
1300
|
+
...doc11.data()
|
|
965
1301
|
})
|
|
966
1302
|
)
|
|
967
1303
|
);
|
|
@@ -1059,9 +1395,9 @@ var RequirementService = class extends BaseService {
|
|
|
1059
1395
|
const q = query7(this.requirementsRef, where7("isActive", "==", true));
|
|
1060
1396
|
const snapshot = await getDocs7(q);
|
|
1061
1397
|
return snapshot.docs.map(
|
|
1062
|
-
(
|
|
1063
|
-
id:
|
|
1064
|
-
...
|
|
1398
|
+
(doc11) => ({
|
|
1399
|
+
id: doc11.id,
|
|
1400
|
+
...doc11.data()
|
|
1065
1401
|
})
|
|
1066
1402
|
);
|
|
1067
1403
|
}
|
|
@@ -1078,12 +1414,24 @@ var RequirementService = class extends BaseService {
|
|
|
1078
1414
|
);
|
|
1079
1415
|
const snapshot = await getDocs7(q);
|
|
1080
1416
|
return snapshot.docs.map(
|
|
1081
|
-
(
|
|
1082
|
-
id:
|
|
1083
|
-
...
|
|
1417
|
+
(doc11) => ({
|
|
1418
|
+
id: doc11.id,
|
|
1419
|
+
...doc11.data()
|
|
1084
1420
|
})
|
|
1085
1421
|
);
|
|
1086
1422
|
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Searches for requirements by name.
|
|
1425
|
+
* @param name - The name to search for.
|
|
1426
|
+
* @param type - The type of requirement (pre/post).
|
|
1427
|
+
* @returns A list of requirements that match the search criteria.
|
|
1428
|
+
*/
|
|
1429
|
+
async search(name, type) {
|
|
1430
|
+
const requirements = await this.getAllByType(type);
|
|
1431
|
+
return requirements.filter(
|
|
1432
|
+
(r) => r.name.toLowerCase().includes(name.toLowerCase())
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1087
1435
|
/**
|
|
1088
1436
|
* Ažurira postojeći zahtev
|
|
1089
1437
|
* @param id - ID zahteva koji se ažurira
|
|
@@ -1126,10 +1474,17 @@ var RequirementService = class extends BaseService {
|
|
|
1126
1474
|
import {
|
|
1127
1475
|
addDoc as addDoc5,
|
|
1128
1476
|
collection as collection8,
|
|
1477
|
+
collectionGroup as collectionGroup2,
|
|
1478
|
+
deleteDoc as deleteDoc3,
|
|
1129
1479
|
doc as doc8,
|
|
1480
|
+
getCountFromServer as getCountFromServer5,
|
|
1130
1481
|
getDoc as getDoc8,
|
|
1131
1482
|
getDocs as getDocs8,
|
|
1483
|
+
limit as limit7,
|
|
1484
|
+
orderBy as orderBy7,
|
|
1132
1485
|
query as query8,
|
|
1486
|
+
setDoc as setDoc4,
|
|
1487
|
+
startAfter as startAfter6,
|
|
1133
1488
|
updateDoc as updateDoc8,
|
|
1134
1489
|
where as where8
|
|
1135
1490
|
} from "firebase/firestore";
|
|
@@ -1173,20 +1528,110 @@ var SubcategoryService = class extends BaseService {
|
|
|
1173
1528
|
return { id: docRef.id, ...newSubcategory };
|
|
1174
1529
|
}
|
|
1175
1530
|
/**
|
|
1176
|
-
*
|
|
1531
|
+
* Returns counts of subcategories for all categories.
|
|
1532
|
+
* @param active - Whether to count active or inactive subcategories.
|
|
1533
|
+
* @returns A record mapping category ID to subcategory count.
|
|
1534
|
+
*/
|
|
1535
|
+
async getSubcategoryCounts(active = true) {
|
|
1536
|
+
const categoriesRef = collection8(this.db, CATEGORIES_COLLECTION);
|
|
1537
|
+
const categoriesSnapshot = await getDocs8(categoriesRef);
|
|
1538
|
+
const counts = {};
|
|
1539
|
+
for (const categoryDoc of categoriesSnapshot.docs) {
|
|
1540
|
+
const categoryId = categoryDoc.id;
|
|
1541
|
+
const subcategoriesRef = this.getSubcategoriesRef(categoryId);
|
|
1542
|
+
const q = query8(subcategoriesRef, where8("isActive", "==", active));
|
|
1543
|
+
const snapshot = await getCountFromServer5(q);
|
|
1544
|
+
counts[categoryId] = snapshot.data().count;
|
|
1545
|
+
}
|
|
1546
|
+
return counts;
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Vraća sve aktivne podkategorije za određenu kategoriju sa paginacijom
|
|
1177
1550
|
* @param categoryId - ID kategorije čije podkategorije tražimo
|
|
1178
|
-
* @
|
|
1551
|
+
* @param options - Pagination options
|
|
1552
|
+
* @returns Lista aktivnih podkategorija i poslednji vidljiv dokument
|
|
1553
|
+
*/
|
|
1554
|
+
async getAllByCategoryId(categoryId, options = {}) {
|
|
1555
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
1556
|
+
const constraints = [
|
|
1557
|
+
where8("isActive", "==", active),
|
|
1558
|
+
orderBy7("name"),
|
|
1559
|
+
queryLimit ? limit7(queryLimit) : void 0,
|
|
1560
|
+
lastVisible ? startAfter6(lastVisible) : void 0
|
|
1561
|
+
].filter((c) => !!c);
|
|
1562
|
+
const q = query8(this.getSubcategoriesRef(categoryId), ...constraints);
|
|
1563
|
+
const querySnapshot = await getDocs8(q);
|
|
1564
|
+
const subcategories = querySnapshot.docs.map(
|
|
1565
|
+
(doc11) => ({
|
|
1566
|
+
id: doc11.id,
|
|
1567
|
+
...doc11.data()
|
|
1568
|
+
})
|
|
1569
|
+
);
|
|
1570
|
+
const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
1571
|
+
return { subcategories, lastVisible: newLastVisible };
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Vraća sve podkategorije sa paginacijom koristeći collection group query.
|
|
1575
|
+
* NOTE: This query requires a composite index in Firestore on the 'subcategories' collection group.
|
|
1576
|
+
* The index should be on 'isActive' (ascending) and 'name' (ascending).
|
|
1577
|
+
* Firestore will provide a link to create this index in the console error if it's missing.
|
|
1578
|
+
* @param options - Pagination options
|
|
1579
|
+
* @returns Lista podkategorija i poslednji vidljiv dokument
|
|
1580
|
+
*/
|
|
1581
|
+
async getAll(options = {}) {
|
|
1582
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
1583
|
+
const constraints = [
|
|
1584
|
+
where8("isActive", "==", active),
|
|
1585
|
+
orderBy7("name"),
|
|
1586
|
+
queryLimit ? limit7(queryLimit) : void 0,
|
|
1587
|
+
lastVisible ? startAfter6(lastVisible) : void 0
|
|
1588
|
+
].filter((c) => !!c);
|
|
1589
|
+
const q = query8(
|
|
1590
|
+
collectionGroup2(this.db, SUBCATEGORIES_COLLECTION),
|
|
1591
|
+
...constraints
|
|
1592
|
+
);
|
|
1593
|
+
const querySnapshot = await getDocs8(q);
|
|
1594
|
+
const subcategories = querySnapshot.docs.map(
|
|
1595
|
+
(doc11) => ({
|
|
1596
|
+
id: doc11.id,
|
|
1597
|
+
...doc11.data()
|
|
1598
|
+
})
|
|
1599
|
+
);
|
|
1600
|
+
const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
1601
|
+
return { subcategories, lastVisible: newLastVisible };
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Vraća sve subkategorije za određenu kategoriju za potrebe filtera (bez paginacije)
|
|
1605
|
+
* @param categoryId - ID kategorije čije subkategorije tražimo
|
|
1606
|
+
* @returns Lista svih aktivnih subkategorija
|
|
1179
1607
|
*/
|
|
1180
|
-
async
|
|
1608
|
+
async getAllForFilterByCategoryId(categoryId) {
|
|
1181
1609
|
const q = query8(
|
|
1182
1610
|
this.getSubcategoriesRef(categoryId),
|
|
1183
1611
|
where8("isActive", "==", true)
|
|
1184
1612
|
);
|
|
1185
|
-
const
|
|
1186
|
-
return
|
|
1187
|
-
(
|
|
1188
|
-
id:
|
|
1189
|
-
...
|
|
1613
|
+
const querySnapshot = await getDocs8(q);
|
|
1614
|
+
return querySnapshot.docs.map(
|
|
1615
|
+
(doc11) => ({
|
|
1616
|
+
id: doc11.id,
|
|
1617
|
+
...doc11.data()
|
|
1618
|
+
})
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Vraća sve subkategorije za potrebe filtera (bez paginacije)
|
|
1623
|
+
* @returns Lista svih aktivnih subkategorija
|
|
1624
|
+
*/
|
|
1625
|
+
async getAllForFilter() {
|
|
1626
|
+
const q = query8(
|
|
1627
|
+
collectionGroup2(this.db, SUBCATEGORIES_COLLECTION),
|
|
1628
|
+
where8("isActive", "==", true)
|
|
1629
|
+
);
|
|
1630
|
+
const querySnapshot = await getDocs8(q);
|
|
1631
|
+
return querySnapshot.docs.map(
|
|
1632
|
+
(doc11) => ({
|
|
1633
|
+
id: doc11.id,
|
|
1634
|
+
...doc11.data()
|
|
1190
1635
|
})
|
|
1191
1636
|
);
|
|
1192
1637
|
}
|
|
@@ -1198,13 +1643,42 @@ var SubcategoryService = class extends BaseService {
|
|
|
1198
1643
|
* @returns Ažurirana podkategorija
|
|
1199
1644
|
*/
|
|
1200
1645
|
async update(categoryId, subcategoryId, subcategory) {
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1646
|
+
const newCategoryId = subcategory.categoryId;
|
|
1647
|
+
if (newCategoryId && newCategoryId !== categoryId) {
|
|
1648
|
+
const oldDocRef = doc8(
|
|
1649
|
+
this.getSubcategoriesRef(categoryId),
|
|
1650
|
+
subcategoryId
|
|
1651
|
+
);
|
|
1652
|
+
const docSnap = await getDoc8(oldDocRef);
|
|
1653
|
+
if (!docSnap.exists()) {
|
|
1654
|
+
throw new Error("Subcategory to update does not exist.");
|
|
1655
|
+
}
|
|
1656
|
+
const existingData = docSnap.data();
|
|
1657
|
+
const newData = {
|
|
1658
|
+
...existingData,
|
|
1659
|
+
...subcategory,
|
|
1660
|
+
categoryId: newCategoryId,
|
|
1661
|
+
// Ensure categoryId is updated
|
|
1662
|
+
createdAt: existingData.createdAt,
|
|
1663
|
+
// Preserve original creation date
|
|
1664
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1665
|
+
};
|
|
1666
|
+
const newDocRef = doc8(
|
|
1667
|
+
this.getSubcategoriesRef(newCategoryId),
|
|
1668
|
+
subcategoryId
|
|
1669
|
+
);
|
|
1670
|
+
await setDoc4(newDocRef, newData);
|
|
1671
|
+
await deleteDoc3(oldDocRef);
|
|
1672
|
+
return { id: subcategoryId, ...newData };
|
|
1673
|
+
} else {
|
|
1674
|
+
const updateData = {
|
|
1675
|
+
...subcategory,
|
|
1676
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1677
|
+
};
|
|
1678
|
+
const docRef = doc8(this.getSubcategoriesRef(categoryId), subcategoryId);
|
|
1679
|
+
await updateDoc8(docRef, updateData);
|
|
1680
|
+
return this.getById(categoryId, subcategoryId);
|
|
1681
|
+
}
|
|
1208
1682
|
}
|
|
1209
1683
|
/**
|
|
1210
1684
|
* Soft delete podkategorije (postavlja isActive na false)
|
|
@@ -1214,6 +1688,14 @@ var SubcategoryService = class extends BaseService {
|
|
|
1214
1688
|
async delete(categoryId, subcategoryId) {
|
|
1215
1689
|
await this.update(categoryId, subcategoryId, { isActive: false });
|
|
1216
1690
|
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Reactivates a subcategory by setting its isActive flag to true.
|
|
1693
|
+
* @param categoryId - The ID of the category to which the subcategory belongs.
|
|
1694
|
+
* @param subcategoryId - The ID of the subcategory to reactivate.
|
|
1695
|
+
*/
|
|
1696
|
+
async reactivate(categoryId, subcategoryId) {
|
|
1697
|
+
await this.update(categoryId, subcategoryId, { isActive: true });
|
|
1698
|
+
}
|
|
1217
1699
|
/**
|
|
1218
1700
|
* Vraća podkategoriju po ID-u
|
|
1219
1701
|
* @param categoryId - ID kategorije kojoj pripada podkategorija
|
|
@@ -1238,7 +1720,10 @@ import {
|
|
|
1238
1720
|
doc as doc9,
|
|
1239
1721
|
getDoc as getDoc9,
|
|
1240
1722
|
getDocs as getDocs9,
|
|
1723
|
+
limit as limit8,
|
|
1724
|
+
orderBy as orderBy8,
|
|
1241
1725
|
query as query9,
|
|
1726
|
+
startAfter as startAfter7,
|
|
1242
1727
|
updateDoc as updateDoc9,
|
|
1243
1728
|
where as where9,
|
|
1244
1729
|
arrayUnion,
|
|
@@ -1276,137 +1761,185 @@ var DEFAULT_CERTIFICATION_REQUIREMENT = {
|
|
|
1276
1761
|
};
|
|
1277
1762
|
var TechnologyService = class extends BaseService {
|
|
1278
1763
|
/**
|
|
1279
|
-
*
|
|
1764
|
+
* Reference to the Firestore collection of technologies.
|
|
1280
1765
|
*/
|
|
1281
|
-
|
|
1766
|
+
get technologiesRef() {
|
|
1282
1767
|
return collection9(this.db, TECHNOLOGIES_COLLECTION);
|
|
1283
1768
|
}
|
|
1284
1769
|
/**
|
|
1285
|
-
*
|
|
1286
|
-
* @param technology -
|
|
1287
|
-
* @returns
|
|
1770
|
+
* Creates a new technology.
|
|
1771
|
+
* @param technology - Data for the new technology.
|
|
1772
|
+
* @returns The created technology with its generated ID.
|
|
1288
1773
|
*/
|
|
1289
1774
|
async create(technology) {
|
|
1290
1775
|
const now = /* @__PURE__ */ new Date();
|
|
1291
1776
|
const newTechnology = {
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
post: []
|
|
1299
|
-
},
|
|
1777
|
+
name: technology.name,
|
|
1778
|
+
description: technology.description,
|
|
1779
|
+
family: technology.family,
|
|
1780
|
+
categoryId: technology.categoryId,
|
|
1781
|
+
subcategoryId: technology.subcategoryId,
|
|
1782
|
+
requirements: technology.requirements || { pre: [], post: [] },
|
|
1300
1783
|
blockingConditions: technology.blockingConditions || [],
|
|
1301
1784
|
contraindications: technology.contraindications || [],
|
|
1302
1785
|
benefits: technology.benefits || [],
|
|
1303
|
-
certificationRequirement: technology.certificationRequirement || DEFAULT_CERTIFICATION_REQUIREMENT
|
|
1786
|
+
certificationRequirement: technology.certificationRequirement || DEFAULT_CERTIFICATION_REQUIREMENT,
|
|
1787
|
+
documentationTemplates: technology.documentationTemplates || [],
|
|
1788
|
+
isActive: true,
|
|
1789
|
+
createdAt: now,
|
|
1790
|
+
updatedAt: now
|
|
1304
1791
|
};
|
|
1305
|
-
|
|
1792
|
+
if (technology.technicalDetails) {
|
|
1793
|
+
newTechnology.technicalDetails = technology.technicalDetails;
|
|
1794
|
+
}
|
|
1795
|
+
const docRef = await addDoc6(this.technologiesRef, newTechnology);
|
|
1306
1796
|
return { id: docRef.id, ...newTechnology };
|
|
1307
1797
|
}
|
|
1308
1798
|
/**
|
|
1309
|
-
*
|
|
1310
|
-
* @
|
|
1799
|
+
* Returns counts of technologies for each subcategory.
|
|
1800
|
+
* @param active - Whether to count active or inactive technologies.
|
|
1801
|
+
* @returns A record mapping subcategory ID to technology count.
|
|
1311
1802
|
*/
|
|
1312
|
-
async
|
|
1313
|
-
const q = query9(this.
|
|
1803
|
+
async getTechnologyCounts(active = true) {
|
|
1804
|
+
const q = query9(this.technologiesRef, where9("isActive", "==", active));
|
|
1314
1805
|
const snapshot = await getDocs9(q);
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1806
|
+
const counts = {};
|
|
1807
|
+
snapshot.docs.forEach((doc11) => {
|
|
1808
|
+
const tech = doc11.data();
|
|
1809
|
+
counts[tech.subcategoryId] = (counts[tech.subcategoryId] || 0) + 1;
|
|
1810
|
+
});
|
|
1811
|
+
return counts;
|
|
1321
1812
|
}
|
|
1322
1813
|
/**
|
|
1323
|
-
*
|
|
1324
|
-
* @param
|
|
1325
|
-
* @returns
|
|
1814
|
+
* Returns counts of technologies for each category.
|
|
1815
|
+
* @param active - Whether to count active or inactive technologies.
|
|
1816
|
+
* @returns A record mapping category ID to technology count.
|
|
1326
1817
|
*/
|
|
1327
|
-
async
|
|
1328
|
-
const q = query9(
|
|
1329
|
-
this.getTechnologiesRef(),
|
|
1330
|
-
where9("isActive", "==", true),
|
|
1331
|
-
where9("family", "==", family)
|
|
1332
|
-
);
|
|
1818
|
+
async getTechnologyCountsByCategory(active = true) {
|
|
1819
|
+
const q = query9(this.technologiesRef, where9("isActive", "==", active));
|
|
1333
1820
|
const snapshot = await getDocs9(q);
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1821
|
+
const counts = {};
|
|
1822
|
+
snapshot.docs.forEach((doc11) => {
|
|
1823
|
+
const tech = doc11.data();
|
|
1824
|
+
counts[tech.categoryId] = (counts[tech.categoryId] || 0) + 1;
|
|
1825
|
+
});
|
|
1826
|
+
return counts;
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Returns all technologies with pagination.
|
|
1830
|
+
* @param options - Pagination and filter options.
|
|
1831
|
+
* @returns A list of technologies and the last visible document.
|
|
1832
|
+
*/
|
|
1833
|
+
async getAll(options = {}) {
|
|
1834
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
1835
|
+
const constraints = [
|
|
1836
|
+
where9("isActive", "==", active),
|
|
1837
|
+
orderBy8("name"),
|
|
1838
|
+
queryLimit ? limit8(queryLimit) : void 0,
|
|
1839
|
+
lastVisible ? startAfter7(lastVisible) : void 0
|
|
1840
|
+
].filter((c) => !!c);
|
|
1841
|
+
const q = query9(this.technologiesRef, ...constraints);
|
|
1842
|
+
const snapshot = await getDocs9(q);
|
|
1843
|
+
const technologies = snapshot.docs.map(
|
|
1844
|
+
(doc11) => ({
|
|
1845
|
+
id: doc11.id,
|
|
1846
|
+
...doc11.data()
|
|
1338
1847
|
})
|
|
1339
1848
|
);
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
*
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1849
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
1850
|
+
return { technologies, lastVisible: newLastVisible };
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Returns all technologies for a specific category with pagination.
|
|
1854
|
+
* @param categoryId - The ID of the category.
|
|
1855
|
+
* @param options - Pagination options.
|
|
1856
|
+
* @returns A list of technologies for the specified category.
|
|
1857
|
+
*/
|
|
1858
|
+
async getAllByCategoryId(categoryId, options = {}) {
|
|
1859
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
1860
|
+
const constraints = [
|
|
1861
|
+
where9("categoryId", "==", categoryId),
|
|
1862
|
+
where9("isActive", "==", active),
|
|
1863
|
+
orderBy8("name"),
|
|
1864
|
+
queryLimit ? limit8(queryLimit) : void 0,
|
|
1865
|
+
lastVisible ? startAfter7(lastVisible) : void 0
|
|
1866
|
+
].filter((c) => !!c);
|
|
1867
|
+
const q = query9(this.technologiesRef, ...constraints);
|
|
1352
1868
|
const snapshot = await getDocs9(q);
|
|
1353
|
-
|
|
1354
|
-
(
|
|
1355
|
-
id:
|
|
1356
|
-
...
|
|
1869
|
+
const technologies = snapshot.docs.map(
|
|
1870
|
+
(doc11) => ({
|
|
1871
|
+
id: doc11.id,
|
|
1872
|
+
...doc11.data()
|
|
1357
1873
|
})
|
|
1358
1874
|
);
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
*
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1875
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
1876
|
+
return { technologies, lastVisible: newLastVisible };
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Returns all technologies for a specific subcategory with pagination.
|
|
1880
|
+
* @param subcategoryId - The ID of the subcategory.
|
|
1881
|
+
* @param options - Pagination options.
|
|
1882
|
+
* @returns A list of technologies for the specified subcategory.
|
|
1883
|
+
*/
|
|
1884
|
+
async getAllBySubcategoryId(subcategoryId, options = {}) {
|
|
1885
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
1886
|
+
const constraints = [
|
|
1887
|
+
where9("subcategoryId", "==", subcategoryId),
|
|
1888
|
+
where9("isActive", "==", active),
|
|
1889
|
+
orderBy8("name"),
|
|
1890
|
+
queryLimit ? limit8(queryLimit) : void 0,
|
|
1891
|
+
lastVisible ? startAfter7(lastVisible) : void 0
|
|
1892
|
+
].filter((c) => !!c);
|
|
1893
|
+
const q = query9(this.technologiesRef, ...constraints);
|
|
1371
1894
|
const snapshot = await getDocs9(q);
|
|
1372
|
-
|
|
1373
|
-
(
|
|
1374
|
-
id:
|
|
1375
|
-
...
|
|
1895
|
+
const technologies = snapshot.docs.map(
|
|
1896
|
+
(doc11) => ({
|
|
1897
|
+
id: doc11.id,
|
|
1898
|
+
...doc11.data()
|
|
1376
1899
|
})
|
|
1377
1900
|
);
|
|
1901
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
1902
|
+
return { technologies, lastVisible: newLastVisible };
|
|
1378
1903
|
}
|
|
1379
1904
|
/**
|
|
1380
|
-
*
|
|
1381
|
-
* @param
|
|
1382
|
-
* @param technology -
|
|
1383
|
-
* @returns
|
|
1905
|
+
* Updates an existing technology.
|
|
1906
|
+
* @param id - The ID of the technology to update.
|
|
1907
|
+
* @param technology - New data for the technology.
|
|
1908
|
+
* @returns The updated technology.
|
|
1384
1909
|
*/
|
|
1385
|
-
async update(
|
|
1386
|
-
const updateData = {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1910
|
+
async update(id, technology) {
|
|
1911
|
+
const updateData = { ...technology };
|
|
1912
|
+
Object.keys(updateData).forEach((key) => {
|
|
1913
|
+
if (updateData[key] === void 0) {
|
|
1914
|
+
delete updateData[key];
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
1918
|
+
const docRef = doc9(this.technologiesRef, id);
|
|
1391
1919
|
await updateDoc9(docRef, updateData);
|
|
1392
|
-
return this.getById(
|
|
1920
|
+
return this.getById(id);
|
|
1393
1921
|
}
|
|
1394
1922
|
/**
|
|
1395
|
-
* Soft
|
|
1396
|
-
* @param
|
|
1923
|
+
* Soft deletes a technology.
|
|
1924
|
+
* @param id - The ID of the technology to delete.
|
|
1397
1925
|
*/
|
|
1398
|
-
async delete(
|
|
1399
|
-
await this.update(
|
|
1400
|
-
isActive: false
|
|
1401
|
-
});
|
|
1926
|
+
async delete(id) {
|
|
1927
|
+
await this.update(id, { isActive: false });
|
|
1402
1928
|
}
|
|
1403
1929
|
/**
|
|
1404
|
-
*
|
|
1405
|
-
* @param
|
|
1406
|
-
* @returns Tehnologija ili null ako ne postoji
|
|
1930
|
+
* Reactivates a technology.
|
|
1931
|
+
* @param id - The ID of the technology to reactivate.
|
|
1407
1932
|
*/
|
|
1408
|
-
async
|
|
1409
|
-
|
|
1933
|
+
async reactivate(id) {
|
|
1934
|
+
await this.update(id, { isActive: true });
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Returns a technology by its ID.
|
|
1938
|
+
* @param id - The ID of the requested technology.
|
|
1939
|
+
* @returns The technology or null if it doesn't exist.
|
|
1940
|
+
*/
|
|
1941
|
+
async getById(id) {
|
|
1942
|
+
const docRef = doc9(this.technologiesRef, id);
|
|
1410
1943
|
const docSnap = await getDoc9(docRef);
|
|
1411
1944
|
if (!docSnap.exists()) return null;
|
|
1412
1945
|
return {
|
|
@@ -1421,7 +1954,7 @@ var TechnologyService = class extends BaseService {
|
|
|
1421
1954
|
* @returns Ažurirana tehnologija sa novim zahtevom
|
|
1422
1955
|
*/
|
|
1423
1956
|
async addRequirement(technologyId, requirement) {
|
|
1424
|
-
const docRef = doc9(this.
|
|
1957
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
1425
1958
|
const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
1426
1959
|
await updateDoc9(docRef, {
|
|
1427
1960
|
[requirementType]: arrayUnion(requirement),
|
|
@@ -1436,7 +1969,7 @@ var TechnologyService = class extends BaseService {
|
|
|
1436
1969
|
* @returns Ažurirana tehnologija bez uklonjenog zahteva
|
|
1437
1970
|
*/
|
|
1438
1971
|
async removeRequirement(technologyId, requirement) {
|
|
1439
|
-
const docRef = doc9(this.
|
|
1972
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
1440
1973
|
const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
1441
1974
|
await updateDoc9(docRef, {
|
|
1442
1975
|
[requirementType]: arrayRemove(requirement),
|
|
@@ -1476,7 +2009,7 @@ var TechnologyService = class extends BaseService {
|
|
|
1476
2009
|
* @returns Ažurirana tehnologija
|
|
1477
2010
|
*/
|
|
1478
2011
|
async addBlockingCondition(technologyId, condition) {
|
|
1479
|
-
const docRef = doc9(this.
|
|
2012
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
1480
2013
|
await updateDoc9(docRef, {
|
|
1481
2014
|
blockingConditions: arrayUnion(condition),
|
|
1482
2015
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -1490,7 +2023,7 @@ var TechnologyService = class extends BaseService {
|
|
|
1490
2023
|
* @returns Ažurirana tehnologija
|
|
1491
2024
|
*/
|
|
1492
2025
|
async removeBlockingCondition(technologyId, condition) {
|
|
1493
|
-
const docRef = doc9(this.
|
|
2026
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
1494
2027
|
await updateDoc9(docRef, {
|
|
1495
2028
|
blockingConditions: arrayRemove(condition),
|
|
1496
2029
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -1504,9 +2037,17 @@ var TechnologyService = class extends BaseService {
|
|
|
1504
2037
|
* @returns Ažurirana tehnologija
|
|
1505
2038
|
*/
|
|
1506
2039
|
async addContraindication(technologyId, contraindication) {
|
|
1507
|
-
const docRef = doc9(this.
|
|
2040
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2041
|
+
const technology = await this.getById(technologyId);
|
|
2042
|
+
if (!technology) {
|
|
2043
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
2044
|
+
}
|
|
2045
|
+
const existingContraindications = technology.contraindications || [];
|
|
2046
|
+
if (existingContraindications.some((c) => c.id === contraindication.id)) {
|
|
2047
|
+
return technology;
|
|
2048
|
+
}
|
|
1508
2049
|
await updateDoc9(docRef, {
|
|
1509
|
-
contraindications:
|
|
2050
|
+
contraindications: [...existingContraindications, contraindication],
|
|
1510
2051
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1511
2052
|
});
|
|
1512
2053
|
return this.getById(technologyId);
|
|
@@ -1518,9 +2059,45 @@ var TechnologyService = class extends BaseService {
|
|
|
1518
2059
|
* @returns Ažurirana tehnologija
|
|
1519
2060
|
*/
|
|
1520
2061
|
async removeContraindication(technologyId, contraindication) {
|
|
1521
|
-
const docRef = doc9(this.
|
|
2062
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2063
|
+
const technology = await this.getById(technologyId);
|
|
2064
|
+
if (!technology) {
|
|
2065
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
2066
|
+
}
|
|
2067
|
+
const updatedContraindications = (technology.contraindications || []).filter((c) => c.id !== contraindication.id);
|
|
2068
|
+
await updateDoc9(docRef, {
|
|
2069
|
+
contraindications: updatedContraindications,
|
|
2070
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2071
|
+
});
|
|
2072
|
+
return this.getById(technologyId);
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* Updates an existing contraindication in a technology's list.
|
|
2076
|
+
* If the contraindication does not exist, it will not be added.
|
|
2077
|
+
* @param technologyId - ID of the technology
|
|
2078
|
+
* @param contraindication - The updated contraindication object
|
|
2079
|
+
* @returns The updated technology
|
|
2080
|
+
*/
|
|
2081
|
+
async updateContraindication(technologyId, contraindication) {
|
|
2082
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2083
|
+
const technology = await this.getById(technologyId);
|
|
2084
|
+
if (!technology) {
|
|
2085
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
2086
|
+
}
|
|
2087
|
+
const contraindications = technology.contraindications || [];
|
|
2088
|
+
const index = contraindications.findIndex(
|
|
2089
|
+
(c) => c.id === contraindication.id
|
|
2090
|
+
);
|
|
2091
|
+
if (index === -1) {
|
|
2092
|
+
console.warn(
|
|
2093
|
+
`Contraindication with id ${contraindication.id} not found for technology ${technologyId}. No update performed.`
|
|
2094
|
+
);
|
|
2095
|
+
return technology;
|
|
2096
|
+
}
|
|
2097
|
+
const updatedContraindications = [...contraindications];
|
|
2098
|
+
updatedContraindications[index] = contraindication;
|
|
1522
2099
|
await updateDoc9(docRef, {
|
|
1523
|
-
contraindications:
|
|
2100
|
+
contraindications: updatedContraindications,
|
|
1524
2101
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1525
2102
|
});
|
|
1526
2103
|
return this.getById(technologyId);
|
|
@@ -1532,9 +2109,17 @@ var TechnologyService = class extends BaseService {
|
|
|
1532
2109
|
* @returns Ažurirana tehnologija
|
|
1533
2110
|
*/
|
|
1534
2111
|
async addBenefit(technologyId, benefit) {
|
|
1535
|
-
const docRef = doc9(this.
|
|
2112
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2113
|
+
const technology = await this.getById(technologyId);
|
|
2114
|
+
if (!technology) {
|
|
2115
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
2116
|
+
}
|
|
2117
|
+
const existingBenefits = technology.benefits || [];
|
|
2118
|
+
if (existingBenefits.some((b) => b.id === benefit.id)) {
|
|
2119
|
+
return technology;
|
|
2120
|
+
}
|
|
1536
2121
|
await updateDoc9(docRef, {
|
|
1537
|
-
benefits:
|
|
2122
|
+
benefits: [...existingBenefits, benefit],
|
|
1538
2123
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1539
2124
|
});
|
|
1540
2125
|
return this.getById(technologyId);
|
|
@@ -1546,9 +2131,45 @@ var TechnologyService = class extends BaseService {
|
|
|
1546
2131
|
* @returns Ažurirana tehnologija
|
|
1547
2132
|
*/
|
|
1548
2133
|
async removeBenefit(technologyId, benefit) {
|
|
1549
|
-
const docRef = doc9(this.
|
|
2134
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2135
|
+
const technology = await this.getById(technologyId);
|
|
2136
|
+
if (!technology) {
|
|
2137
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
2138
|
+
}
|
|
2139
|
+
const updatedBenefits = (technology.benefits || []).filter(
|
|
2140
|
+
(b) => b.id !== benefit.id
|
|
2141
|
+
);
|
|
2142
|
+
await updateDoc9(docRef, {
|
|
2143
|
+
benefits: updatedBenefits,
|
|
2144
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2145
|
+
});
|
|
2146
|
+
return this.getById(technologyId);
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* Updates an existing benefit in a technology's list.
|
|
2150
|
+
* If the benefit does not exist, it will not be added.
|
|
2151
|
+
* @param technologyId - ID of the technology
|
|
2152
|
+
* @param benefit - The updated benefit object
|
|
2153
|
+
* @returns The updated technology
|
|
2154
|
+
*/
|
|
2155
|
+
async updateBenefit(technologyId, benefit) {
|
|
2156
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2157
|
+
const technology = await this.getById(technologyId);
|
|
2158
|
+
if (!technology) {
|
|
2159
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
2160
|
+
}
|
|
2161
|
+
const benefits = technology.benefits || [];
|
|
2162
|
+
const index = benefits.findIndex((b) => b.id === benefit.id);
|
|
2163
|
+
if (index === -1) {
|
|
2164
|
+
console.warn(
|
|
2165
|
+
`Benefit with id ${benefit.id} not found for technology ${technologyId}. No update performed.`
|
|
2166
|
+
);
|
|
2167
|
+
return technology;
|
|
2168
|
+
}
|
|
2169
|
+
const updatedBenefits = [...benefits];
|
|
2170
|
+
updatedBenefits[index] = benefit;
|
|
1550
2171
|
await updateDoc9(docRef, {
|
|
1551
|
-
benefits:
|
|
2172
|
+
benefits: updatedBenefits,
|
|
1552
2173
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1553
2174
|
});
|
|
1554
2175
|
return this.getById(technologyId);
|
|
@@ -1587,7 +2208,7 @@ var TechnologyService = class extends BaseService {
|
|
|
1587
2208
|
* @returns Ažurirana tehnologija
|
|
1588
2209
|
*/
|
|
1589
2210
|
async updateCertificationRequirement(technologyId, certificationRequirement) {
|
|
1590
|
-
const docRef = doc9(this.
|
|
2211
|
+
const docRef = doc9(this.technologiesRef, technologyId);
|
|
1591
2212
|
await updateDoc9(docRef, {
|
|
1592
2213
|
certificationRequirement,
|
|
1593
2214
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -1665,7 +2286,7 @@ var TechnologyService = class extends BaseService {
|
|
|
1665
2286
|
*/
|
|
1666
2287
|
async getAllowedTechnologies(practitioner) {
|
|
1667
2288
|
const allTechnologies = await this.getAll();
|
|
1668
|
-
const allowedTechnologies = allTechnologies.filter(
|
|
2289
|
+
const allowedTechnologies = allTechnologies.technologies.filter(
|
|
1669
2290
|
(technology) => this.validateCertification(
|
|
1670
2291
|
technology.certificationRequirement,
|
|
1671
2292
|
practitioner.certification
|
|
@@ -1685,6 +2306,283 @@ var TechnologyService = class extends BaseService {
|
|
|
1685
2306
|
subcategories
|
|
1686
2307
|
};
|
|
1687
2308
|
}
|
|
2309
|
+
/**
|
|
2310
|
+
* Gets all active technologies for a subcategory for filter dropdowns.
|
|
2311
|
+
* @param categoryId - The ID of the parent category.
|
|
2312
|
+
* @param subcategoryId - The ID of the subcategory.
|
|
2313
|
+
*/
|
|
2314
|
+
async getAllForFilterBySubcategoryId(categoryId, subcategoryId) {
|
|
2315
|
+
const q = query9(
|
|
2316
|
+
collection9(this.db, TECHNOLOGIES_COLLECTION),
|
|
2317
|
+
where9("isActive", "==", true),
|
|
2318
|
+
where9("categoryId", "==", categoryId),
|
|
2319
|
+
where9("subcategoryId", "==", subcategoryId),
|
|
2320
|
+
orderBy8("name")
|
|
2321
|
+
);
|
|
2322
|
+
const snapshot = await getDocs9(q);
|
|
2323
|
+
return snapshot.docs.map(
|
|
2324
|
+
(doc11) => ({
|
|
2325
|
+
id: doc11.id,
|
|
2326
|
+
...doc11.data()
|
|
2327
|
+
})
|
|
2328
|
+
);
|
|
2329
|
+
}
|
|
2330
|
+
/**
|
|
2331
|
+
* Gets all active technologies for filter dropdowns.
|
|
2332
|
+
*/
|
|
2333
|
+
async getAllForFilter() {
|
|
2334
|
+
const q = query9(
|
|
2335
|
+
collection9(this.db, TECHNOLOGIES_COLLECTION),
|
|
2336
|
+
where9("isActive", "==", true),
|
|
2337
|
+
orderBy8("name")
|
|
2338
|
+
);
|
|
2339
|
+
const snapshot = await getDocs9(q);
|
|
2340
|
+
return snapshot.docs.map(
|
|
2341
|
+
(doc11) => ({
|
|
2342
|
+
id: doc11.id,
|
|
2343
|
+
...doc11.data()
|
|
2344
|
+
})
|
|
2345
|
+
);
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
|
|
2349
|
+
// src/backoffice/services/constants.service.ts
|
|
2350
|
+
import {
|
|
2351
|
+
arrayRemove as arrayRemove2,
|
|
2352
|
+
arrayUnion as arrayUnion2,
|
|
2353
|
+
doc as doc10,
|
|
2354
|
+
getDoc as getDoc10,
|
|
2355
|
+
setDoc as setDoc5,
|
|
2356
|
+
updateDoc as updateDoc10
|
|
2357
|
+
} from "firebase/firestore";
|
|
2358
|
+
var ADMIN_CONSTANTS_COLLECTION = "admin-constants";
|
|
2359
|
+
var TREATMENT_BENEFITS_DOC = "treatment-benefits";
|
|
2360
|
+
var CONTRAINDICATIONS_DOC = "contraindications";
|
|
2361
|
+
var ConstantsService = class extends BaseService {
|
|
2362
|
+
/**
|
|
2363
|
+
* @description Gets the reference to the document holding treatment benefits.
|
|
2364
|
+
* @private
|
|
2365
|
+
* @type {DocumentReference}
|
|
2366
|
+
*/
|
|
2367
|
+
get treatmentBenefitsDocRef() {
|
|
2368
|
+
return doc10(this.db, ADMIN_CONSTANTS_COLLECTION, TREATMENT_BENEFITS_DOC);
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* @description Gets the reference to the document holding contraindications.
|
|
2372
|
+
* @private
|
|
2373
|
+
* @type {DocumentReference}
|
|
2374
|
+
*/
|
|
2375
|
+
get contraindicationsDocRef() {
|
|
2376
|
+
return doc10(this.db, ADMIN_CONSTANTS_COLLECTION, CONTRAINDICATIONS_DOC);
|
|
2377
|
+
}
|
|
2378
|
+
// =================================================================
|
|
2379
|
+
// Treatment Benefits
|
|
2380
|
+
// =================================================================
|
|
2381
|
+
/**
|
|
2382
|
+
* @description Retrieves all treatment benefits without pagination.
|
|
2383
|
+
* @returns {Promise<TreatmentBenefitDynamic[]>} An array of all treatment benefits.
|
|
2384
|
+
*/
|
|
2385
|
+
async getAllBenefitsForFilter() {
|
|
2386
|
+
const docSnap = await getDoc10(this.treatmentBenefitsDocRef);
|
|
2387
|
+
if (!docSnap.exists()) {
|
|
2388
|
+
return [];
|
|
2389
|
+
}
|
|
2390
|
+
return docSnap.data().benefits;
|
|
2391
|
+
}
|
|
2392
|
+
/**
|
|
2393
|
+
* @description Retrieves a paginated list of treatment benefits.
|
|
2394
|
+
* @param {{ page: number; limit: number }} options - Pagination options.
|
|
2395
|
+
* @returns {Promise<{ benefits: TreatmentBenefitDynamic[]; total: number }>} A paginated list of benefits and the total count.
|
|
2396
|
+
*/
|
|
2397
|
+
async getAllBenefits(options) {
|
|
2398
|
+
const allBenefits = await this.getAllBenefitsForFilter();
|
|
2399
|
+
const { page, limit: limit9 } = options;
|
|
2400
|
+
const startIndex = page * limit9;
|
|
2401
|
+
const endIndex = startIndex + limit9;
|
|
2402
|
+
const paginatedBenefits = allBenefits.slice(startIndex, endIndex);
|
|
2403
|
+
return { benefits: paginatedBenefits, total: allBenefits.length };
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* @description Adds a new treatment benefit.
|
|
2407
|
+
* @param {Omit<TreatmentBenefitDynamic, "id">} benefit - The treatment benefit to add, without an ID.
|
|
2408
|
+
* @returns {Promise<TreatmentBenefitDynamic>} The newly created treatment benefit with its generated ID.
|
|
2409
|
+
*/
|
|
2410
|
+
async addTreatmentBenefit(benefit) {
|
|
2411
|
+
const newBenefit = {
|
|
2412
|
+
id: this.generateId(),
|
|
2413
|
+
...benefit
|
|
2414
|
+
};
|
|
2415
|
+
const docSnap = await getDoc10(this.treatmentBenefitsDocRef);
|
|
2416
|
+
if (!docSnap.exists()) {
|
|
2417
|
+
await setDoc5(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
|
|
2418
|
+
} else {
|
|
2419
|
+
await updateDoc10(this.treatmentBenefitsDocRef, {
|
|
2420
|
+
benefits: arrayUnion2(newBenefit)
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
2423
|
+
return newBenefit;
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* @description Retrieves a single treatment benefit by its ID.
|
|
2427
|
+
* @param {string} benefitId - The ID of the treatment benefit to retrieve.
|
|
2428
|
+
* @returns {Promise<TreatmentBenefitDynamic | undefined>} The found treatment benefit or undefined.
|
|
2429
|
+
*/
|
|
2430
|
+
async getBenefitById(benefitId) {
|
|
2431
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
2432
|
+
return benefits.find((b) => b.id === benefitId);
|
|
2433
|
+
}
|
|
2434
|
+
/**
|
|
2435
|
+
* @description Searches for treatment benefits by name (case-insensitive).
|
|
2436
|
+
* @param {string} searchTerm - The term to search for in the benefit names.
|
|
2437
|
+
* @returns {Promise<TreatmentBenefitDynamic[]>} An array of matching treatment benefits.
|
|
2438
|
+
*/
|
|
2439
|
+
async searchBenefitsByName(searchTerm) {
|
|
2440
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
2441
|
+
const normalizedSearchTerm = searchTerm.toLowerCase();
|
|
2442
|
+
return benefits.filter(
|
|
2443
|
+
(b) => b.name.toLowerCase().includes(normalizedSearchTerm)
|
|
2444
|
+
);
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* @description Updates an existing treatment benefit.
|
|
2448
|
+
* @param {TreatmentBenefitDynamic} benefit - The treatment benefit with updated data. Its ID must match an existing benefit.
|
|
2449
|
+
* @returns {Promise<TreatmentBenefitDynamic>} The updated treatment benefit.
|
|
2450
|
+
* @throws {Error} If the treatment benefit is not found.
|
|
2451
|
+
*/
|
|
2452
|
+
async updateTreatmentBenefit(benefit) {
|
|
2453
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
2454
|
+
const benefitIndex = benefits.findIndex((b) => b.id === benefit.id);
|
|
2455
|
+
if (benefitIndex === -1) {
|
|
2456
|
+
throw new Error("Treatment benefit not found.");
|
|
2457
|
+
}
|
|
2458
|
+
benefits[benefitIndex] = benefit;
|
|
2459
|
+
await updateDoc10(this.treatmentBenefitsDocRef, { benefits });
|
|
2460
|
+
return benefit;
|
|
2461
|
+
}
|
|
2462
|
+
/**
|
|
2463
|
+
* @description Deletes a treatment benefit by its ID.
|
|
2464
|
+
* @param {string} benefitId - The ID of the treatment benefit to delete.
|
|
2465
|
+
* @returns {Promise<void>}
|
|
2466
|
+
*/
|
|
2467
|
+
async deleteTreatmentBenefit(benefitId) {
|
|
2468
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
2469
|
+
const benefitToRemove = benefits.find((b) => b.id === benefitId);
|
|
2470
|
+
if (!benefitToRemove) {
|
|
2471
|
+
return;
|
|
2472
|
+
}
|
|
2473
|
+
await updateDoc10(this.treatmentBenefitsDocRef, {
|
|
2474
|
+
benefits: arrayRemove2(benefitToRemove)
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
// =================================================================
|
|
2478
|
+
// Contraindications
|
|
2479
|
+
// =================================================================
|
|
2480
|
+
/**
|
|
2481
|
+
* @description Retrieves all contraindications without pagination.
|
|
2482
|
+
* @returns {Promise<ContraindicationDynamic[]>} An array of all contraindications.
|
|
2483
|
+
*/
|
|
2484
|
+
async getAllContraindicationsForFilter() {
|
|
2485
|
+
const docSnap = await getDoc10(this.contraindicationsDocRef);
|
|
2486
|
+
if (!docSnap.exists()) {
|
|
2487
|
+
return [];
|
|
2488
|
+
}
|
|
2489
|
+
return docSnap.data().contraindications;
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* @description Retrieves a paginated list of contraindications.
|
|
2493
|
+
* @param {{ page: number; limit: number }} options - Pagination options.
|
|
2494
|
+
* @returns {Promise<{ contraindications: ContraindicationDynamic[]; total: number }>} A paginated list and the total count.
|
|
2495
|
+
*/
|
|
2496
|
+
async getAllContraindications(options) {
|
|
2497
|
+
const allContraindications = await this.getAllContraindicationsForFilter();
|
|
2498
|
+
const { page, limit: limit9 } = options;
|
|
2499
|
+
const startIndex = page * limit9;
|
|
2500
|
+
const endIndex = startIndex + limit9;
|
|
2501
|
+
const paginatedContraindications = allContraindications.slice(
|
|
2502
|
+
startIndex,
|
|
2503
|
+
endIndex
|
|
2504
|
+
);
|
|
2505
|
+
return {
|
|
2506
|
+
contraindications: paginatedContraindications,
|
|
2507
|
+
total: allContraindications.length
|
|
2508
|
+
};
|
|
2509
|
+
}
|
|
2510
|
+
/**
|
|
2511
|
+
* @description Adds a new contraindication.
|
|
2512
|
+
* @param {Omit<ContraindicationDynamic, "id">} contraindication - The contraindication to add, without an ID.
|
|
2513
|
+
* @returns {Promise<ContraindicationDynamic>} The newly created contraindication with its generated ID.
|
|
2514
|
+
*/
|
|
2515
|
+
async addContraindication(contraindication) {
|
|
2516
|
+
const newContraindication = {
|
|
2517
|
+
id: this.generateId(),
|
|
2518
|
+
...contraindication
|
|
2519
|
+
};
|
|
2520
|
+
const docSnap = await getDoc10(this.contraindicationsDocRef);
|
|
2521
|
+
if (!docSnap.exists()) {
|
|
2522
|
+
await setDoc5(this.contraindicationsDocRef, {
|
|
2523
|
+
contraindications: [newContraindication]
|
|
2524
|
+
});
|
|
2525
|
+
} else {
|
|
2526
|
+
await updateDoc10(this.contraindicationsDocRef, {
|
|
2527
|
+
contraindications: arrayUnion2(newContraindication)
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
return newContraindication;
|
|
2531
|
+
}
|
|
2532
|
+
/**
|
|
2533
|
+
* @description Retrieves a single contraindication by its ID.
|
|
2534
|
+
* @param {string} contraindicationId - The ID of the contraindication to retrieve.
|
|
2535
|
+
* @returns {Promise<ContraindicationDynamic | undefined>} The found contraindication or undefined.
|
|
2536
|
+
*/
|
|
2537
|
+
async getContraindicationById(contraindicationId) {
|
|
2538
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
2539
|
+
return contraindications.find((c) => c.id === contraindicationId);
|
|
2540
|
+
}
|
|
2541
|
+
/**
|
|
2542
|
+
* @description Searches for contraindications by name (case-insensitive).
|
|
2543
|
+
* @param {string} searchTerm - The term to search for in the contraindication names.
|
|
2544
|
+
* @returns {Promise<ContraindicationDynamic[]>} An array of matching contraindications.
|
|
2545
|
+
*/
|
|
2546
|
+
async searchContraindicationsByName(searchTerm) {
|
|
2547
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
2548
|
+
const normalizedSearchTerm = searchTerm.toLowerCase();
|
|
2549
|
+
return contraindications.filter(
|
|
2550
|
+
(c) => c.name.toLowerCase().includes(normalizedSearchTerm)
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* @description Updates an existing contraindication.
|
|
2555
|
+
* @param {ContraindicationDynamic} contraindication - The contraindication with updated data. Its ID must match an existing one.
|
|
2556
|
+
* @returns {Promise<ContraindicationDynamic>} The updated contraindication.
|
|
2557
|
+
* @throws {Error} If the contraindication is not found.
|
|
2558
|
+
*/
|
|
2559
|
+
async updateContraindication(contraindication) {
|
|
2560
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
2561
|
+
const index = contraindications.findIndex(
|
|
2562
|
+
(c) => c.id === contraindication.id
|
|
2563
|
+
);
|
|
2564
|
+
if (index === -1) {
|
|
2565
|
+
throw new Error("Contraindication not found.");
|
|
2566
|
+
}
|
|
2567
|
+
contraindications[index] = contraindication;
|
|
2568
|
+
await updateDoc10(this.contraindicationsDocRef, { contraindications });
|
|
2569
|
+
return contraindication;
|
|
2570
|
+
}
|
|
2571
|
+
/**
|
|
2572
|
+
* @description Deletes a contraindication by its ID.
|
|
2573
|
+
* @param {string} contraindicationId - The ID of the contraindication to delete.
|
|
2574
|
+
* @returns {Promise<void>}
|
|
2575
|
+
*/
|
|
2576
|
+
async deleteContraindication(contraindicationId) {
|
|
2577
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
2578
|
+
const toRemove = contraindications.find((c) => c.id === contraindicationId);
|
|
2579
|
+
if (!toRemove) {
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
await updateDoc10(this.contraindicationsDocRef, {
|
|
2583
|
+
contraindications: arrayRemove2(toRemove)
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
1688
2586
|
};
|
|
1689
2587
|
|
|
1690
2588
|
// src/backoffice/types/static/blocking-condition.types.ts
|
|
@@ -1743,13 +2641,6 @@ var Currency = /* @__PURE__ */ ((Currency2) => {
|
|
|
1743
2641
|
return Currency2;
|
|
1744
2642
|
})(Currency || {});
|
|
1745
2643
|
|
|
1746
|
-
// src/backoffice/types/static/procedure-family.types.ts
|
|
1747
|
-
var ProcedureFamily = /* @__PURE__ */ ((ProcedureFamily2) => {
|
|
1748
|
-
ProcedureFamily2["AESTHETICS"] = "aesthetics";
|
|
1749
|
-
ProcedureFamily2["SURGERY"] = "surgery";
|
|
1750
|
-
return ProcedureFamily2;
|
|
1751
|
-
})(ProcedureFamily || {});
|
|
1752
|
-
|
|
1753
2644
|
// src/backoffice/types/static/treatment-benefit.types.ts
|
|
1754
2645
|
var TreatmentBenefit = /* @__PURE__ */ ((TreatmentBenefit2) => {
|
|
1755
2646
|
TreatmentBenefit2["WRINKLE_REDUCTION"] = "wrinkle_reduction";
|
|
@@ -1772,9 +2663,25 @@ var TreatmentBenefit = /* @__PURE__ */ ((TreatmentBenefit2) => {
|
|
|
1772
2663
|
|
|
1773
2664
|
// src/backoffice/validations/schemas.ts
|
|
1774
2665
|
import { z as z2 } from "zod";
|
|
2666
|
+
var contraindicationDynamicSchema = z2.object({
|
|
2667
|
+
id: z2.string().min(1, "Contraindication ID is required").regex(
|
|
2668
|
+
/^[a-z0-9_]+$/,
|
|
2669
|
+
"ID must be in snake_case (lowercase, numbers, and underscores only)"
|
|
2670
|
+
),
|
|
2671
|
+
name: z2.string().min(1, "Contraindication name is required"),
|
|
2672
|
+
description: z2.string().optional()
|
|
2673
|
+
});
|
|
2674
|
+
var treatmentBenefitDynamicSchema = z2.object({
|
|
2675
|
+
id: z2.string().min(1, "Benefit ID is required").regex(
|
|
2676
|
+
/^[a-z0-9_]+$/,
|
|
2677
|
+
"ID must be in snake_case (lowercase, numbers, and underscores only)"
|
|
2678
|
+
),
|
|
2679
|
+
name: z2.string().min(1, "Benefit name is required"),
|
|
2680
|
+
description: z2.string().optional()
|
|
2681
|
+
});
|
|
1775
2682
|
var blockingConditionSchemaBackoffice = z2.nativeEnum(BlockingCondition);
|
|
1776
|
-
var contraindicationSchemaBackoffice =
|
|
1777
|
-
var treatmentBenefitSchemaBackoffice =
|
|
2683
|
+
var contraindicationSchemaBackoffice = contraindicationDynamicSchema;
|
|
2684
|
+
var treatmentBenefitSchemaBackoffice = treatmentBenefitDynamicSchema;
|
|
1778
2685
|
var procedureFamilySchemaBackoffice = z2.nativeEnum(ProcedureFamily);
|
|
1779
2686
|
var timeUnitSchemaBackoffice = z2.nativeEnum(TimeUnit);
|
|
1780
2687
|
var requirementTypeSchema = z2.nativeEnum(RequirementType);
|
|
@@ -1996,6 +2903,7 @@ export {
|
|
|
1996
2903
|
CertificationLevel,
|
|
1997
2904
|
CertificationSpecialty,
|
|
1998
2905
|
CircularReferenceError,
|
|
2906
|
+
ConstantsService,
|
|
1999
2907
|
Contraindication,
|
|
2000
2908
|
ContraindicationError,
|
|
2001
2909
|
Currency,
|
|
@@ -2043,6 +2951,7 @@ export {
|
|
|
2043
2951
|
certificationLevelSchema,
|
|
2044
2952
|
certificationRequirementSchema,
|
|
2045
2953
|
certificationSpecialtySchema,
|
|
2954
|
+
contraindicationDynamicSchema,
|
|
2046
2955
|
contraindicationSchemaBackoffice,
|
|
2047
2956
|
createDocumentTemplateSchema,
|
|
2048
2957
|
documentElementSchema,
|
|
@@ -2059,6 +2968,7 @@ export {
|
|
|
2059
2968
|
technologyUpdateSchema,
|
|
2060
2969
|
timeUnitSchemaBackoffice,
|
|
2061
2970
|
timeframeSchema,
|
|
2971
|
+
treatmentBenefitDynamicSchema,
|
|
2062
2972
|
treatmentBenefitSchemaBackoffice,
|
|
2063
2973
|
updateDocumentTemplateSchema
|
|
2064
2974
|
};
|