@blackcode_sa/metaestetics-api 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/admin/index.d.mts +956 -848
  2. package/dist/admin/index.d.ts +956 -848
  3. package/dist/admin/index.js +7215 -6757
  4. package/dist/admin/index.mjs +7215 -6754
  5. package/dist/backoffice/index.d.mts +1418 -1418
  6. package/dist/backoffice/index.d.ts +1418 -1418
  7. package/dist/backoffice/index.js +1464 -1454
  8. package/dist/backoffice/index.mjs +1509 -1499
  9. package/dist/index.d.mts +5565 -5565
  10. package/dist/index.d.ts +5565 -5565
  11. package/dist/index.js +9229 -9225
  12. package/dist/index.mjs +9324 -9320
  13. package/package.json +1 -1
  14. package/src/admin/aggregation/appointment/index.ts +1 -0
  15. package/src/admin/aggregation/clinic/index.ts +1 -0
  16. package/src/admin/aggregation/forms/index.ts +1 -0
  17. package/src/admin/aggregation/index.ts +8 -0
  18. package/src/admin/aggregation/patient/index.ts +1 -0
  19. package/src/admin/aggregation/practitioner/index.ts +1 -0
  20. package/src/admin/aggregation/practitioner-invite/index.ts +1 -0
  21. package/src/admin/aggregation/procedure/index.ts +1 -0
  22. package/src/admin/aggregation/reviews/index.ts +1 -0
  23. package/src/admin/booking/index.ts +1 -1
  24. package/src/admin/calendar/index.ts +1 -0
  25. package/src/admin/documentation-templates/index.ts +1 -0
  26. package/src/admin/free-consultation/index.ts +1 -0
  27. package/src/admin/index.ts +18 -120
  28. package/src/admin/mailing/appointment/index.ts +1 -0
  29. package/src/admin/mailing/index.ts +1 -2
  30. package/src/admin/mailing/practitionerInvite/index.ts +1 -0
  31. package/src/admin/notifications/index.ts +1 -0
  32. package/src/admin/requirements/index.ts +1 -0
  33. package/src/admin/users/index.ts +1 -0
  34. package/src/admin/users/user-profile.admin.ts +1 -0
  35. package/src/backoffice/constants/index.ts +1 -0
  36. package/src/backoffice/errors/index.ts +1 -0
  37. package/src/backoffice/index.ts +5 -14
  38. package/src/backoffice/services/index.ts +7 -0
  39. package/src/backoffice/validations/index.ts +1 -0
  40. package/src/services/index.ts +12 -14
@@ -1,323 +1,293 @@
1
+ // src/backoffice/services/brand.service.ts
2
+ import {
3
+ addDoc,
4
+ collection,
5
+ doc,
6
+ getDoc,
7
+ getDocs,
8
+ query,
9
+ updateDoc,
10
+ where
11
+ } from "firebase/firestore";
12
+
1
13
  // src/backoffice/types/brand.types.ts
2
14
  var BRANDS_COLLECTION = "brands";
3
15
 
4
- // src/backoffice/types/category.types.ts
5
- var CATEGORIES_COLLECTION = "backoffice_categories";
6
-
7
- // src/backoffice/types/product.types.ts
8
- var PRODUCTS_COLLECTION = "products";
9
-
10
- // src/backoffice/types/requirement.types.ts
11
- var TimeUnit = /* @__PURE__ */ ((TimeUnit2) => {
12
- TimeUnit2["HOURS"] = "hours";
13
- TimeUnit2["DAYS"] = "days";
14
- return TimeUnit2;
15
- })(TimeUnit || {});
16
- var RequirementType = /* @__PURE__ */ ((RequirementType3) => {
17
- RequirementType3["PRE"] = "pre";
18
- RequirementType3["POST"] = "post";
19
- return RequirementType3;
20
- })(RequirementType || {});
21
- var REQUIREMENTS_COLLECTION = "backoffice_requirements";
22
-
23
- // src/backoffice/types/subcategory.types.ts
24
- var SUBCATEGORIES_COLLECTION = "subcategories";
25
-
26
- // src/backoffice/types/technology.types.ts
27
- var TECHNOLOGIES_COLLECTION = "technologies";
28
-
29
- // src/backoffice/types/static/blocking-condition.types.ts
30
- var BlockingCondition = /* @__PURE__ */ ((BlockingCondition2) => {
31
- BlockingCondition2["PREGNANCY"] = "pregnancy";
32
- BlockingCondition2["BREASTFEEDING"] = "breastfeeding";
33
- BlockingCondition2["ACTIVE_INFECTION"] = "active_infection";
34
- BlockingCondition2["SKIN_CONDITION"] = "skin_condition";
35
- BlockingCondition2["AUTOIMMUNE_DISEASE"] = "autoimmune_disease";
36
- BlockingCondition2["BLOOD_THINNERS"] = "blood_thinners";
37
- BlockingCondition2["RECENT_SURGERY"] = "recent_surgery";
38
- BlockingCondition2["DIABETES"] = "diabetes";
39
- BlockingCondition2["HEART_CONDITION"] = "heart_condition";
40
- BlockingCondition2["HIGH_BLOOD_PRESSURE"] = "high_blood_pressure";
41
- BlockingCondition2["KELOID_SCARRING"] = "keloid_scarring";
42
- BlockingCondition2["METAL_IMPLANTS"] = "metal_implants";
43
- BlockingCondition2["PACEMAKER"] = "pacemaker";
44
- BlockingCondition2["CANCER"] = "cancer";
45
- BlockingCondition2["EPILEPSY"] = "epilepsy";
46
- return BlockingCondition2;
47
- })(BlockingCondition || {});
48
-
49
- // src/backoffice/types/static/certification.types.ts
50
- var CertificationLevel = /* @__PURE__ */ ((CertificationLevel2) => {
51
- CertificationLevel2["AESTHETICIAN"] = "aesthetician";
52
- CertificationLevel2["NURSE_ASSISTANT"] = "nurse_assistant";
53
- CertificationLevel2["NURSE"] = "nurse";
54
- CertificationLevel2["NURSE_PRACTITIONER"] = "nurse_practitioner";
55
- CertificationLevel2["PHYSICIAN_ASSISTANT"] = "physician_assistant";
56
- CertificationLevel2["DOCTOR"] = "doctor";
57
- CertificationLevel2["SPECIALIST"] = "specialist";
58
- CertificationLevel2["PLASTIC_SURGEON"] = "plastic_surgeon";
59
- return CertificationLevel2;
60
- })(CertificationLevel || {});
61
- var CertificationSpecialty = /* @__PURE__ */ ((CertificationSpecialty3) => {
62
- CertificationSpecialty3["LASER"] = "laser";
63
- CertificationSpecialty3["INJECTABLES"] = "injectables";
64
- CertificationSpecialty3["CHEMICAL_PEELS"] = "chemical_peels";
65
- CertificationSpecialty3["MICRODERMABRASION"] = "microdermabrasion";
66
- CertificationSpecialty3["BODY_CONTOURING"] = "body_contouring";
67
- CertificationSpecialty3["SKIN_CARE"] = "skin_care";
68
- CertificationSpecialty3["WOUND_CARE"] = "wound_care";
69
- CertificationSpecialty3["ANESTHESIA"] = "anesthesia";
70
- return CertificationSpecialty3;
71
- })(CertificationSpecialty || {});
72
-
73
- // src/backoffice/types/static/contraindication.types.ts
74
- var Contraindication = /* @__PURE__ */ ((Contraindication2) => {
75
- Contraindication2["SENSITIVE_SKIN"] = "sensitive_skin";
76
- Contraindication2["RECENT_TANNING"] = "recent_tanning";
77
- Contraindication2["RECENT_BOTOX"] = "recent_botox";
78
- Contraindication2["RECENT_FILLERS"] = "recent_fillers";
79
- Contraindication2["SKIN_ALLERGIES"] = "skin_allergies";
80
- Contraindication2["MEDICATIONS"] = "medications";
81
- Contraindication2["RECENT_CHEMICAL_PEEL"] = "recent_chemical_peel";
82
- Contraindication2["RECENT_LASER"] = "recent_laser";
83
- Contraindication2["SKIN_INFLAMMATION"] = "skin_inflammation";
84
- Contraindication2["OPEN_WOUNDS"] = "open_wounds";
85
- Contraindication2["HERPES_SIMPLEX"] = "herpes_simplex";
86
- Contraindication2["COLD_SORES"] = "cold_sores";
87
- return Contraindication2;
88
- })(Contraindication || {});
89
-
90
- // src/backoffice/types/static/procedure-family.types.ts
91
- var ProcedureFamily = /* @__PURE__ */ ((ProcedureFamily2) => {
92
- ProcedureFamily2["AESTHETICS"] = "aesthetics";
93
- ProcedureFamily2["SURGERY"] = "surgery";
94
- return ProcedureFamily2;
95
- })(ProcedureFamily || {});
96
-
97
- // src/backoffice/types/static/treatment-benefit.types.ts
98
- var TreatmentBenefit = /* @__PURE__ */ ((TreatmentBenefit2) => {
99
- TreatmentBenefit2["WRINKLE_REDUCTION"] = "wrinkle_reduction";
100
- TreatmentBenefit2["SKIN_TIGHTENING"] = "skin_tightening";
101
- TreatmentBenefit2["COLLAGEN_PRODUCTION"] = "collagen_production";
102
- TreatmentBenefit2["ACNE_REDUCTION"] = "acne_reduction";
103
- TreatmentBenefit2["SCAR_REDUCTION"] = "scar_reduction";
104
- TreatmentBenefit2["PIGMENTATION_IMPROVEMENT"] = "pigmentation_improvement";
105
- TreatmentBenefit2["HAIR_REMOVAL"] = "hair_removal";
106
- TreatmentBenefit2["MUSCLE_TONING"] = "muscle_toning";
107
- TreatmentBenefit2["FAT_REDUCTION"] = "fat_reduction";
108
- TreatmentBenefit2["CELLULITE_REDUCTION"] = "cellulite_reduction";
109
- TreatmentBenefit2["SKIN_REJUVENATION"] = "skin_rejuvenation";
110
- TreatmentBenefit2["PORE_REDUCTION"] = "pore_reduction";
111
- TreatmentBenefit2["TEXTURE_IMPROVEMENT"] = "texture_improvement";
112
- TreatmentBenefit2["HYDRATION_BOOST"] = "hydration_boost";
113
- TreatmentBenefit2["CIRCULATION_IMPROVEMENT"] = "circulation_improvement";
114
- return TreatmentBenefit2;
115
- })(TreatmentBenefit || {});
116
-
117
- // src/types/documentation-templates/index.ts
118
- var DOCUMENTATION_TEMPLATES_COLLECTION = "documentation-templates";
119
- var DocumentElementType = /* @__PURE__ */ ((DocumentElementType2) => {
120
- DocumentElementType2["HEADING"] = "heading";
121
- DocumentElementType2["PARAGRAPH"] = "paragraph";
122
- DocumentElementType2["LIST"] = "list";
123
- DocumentElementType2["DYNAMIC_TEXT"] = "dynamic_text";
124
- DocumentElementType2["BINARY_CHOICE"] = "binary_choice";
125
- DocumentElementType2["MULTIPLE_CHOICE"] = "multiple_choice";
126
- DocumentElementType2["SINGLE_CHOICE"] = "single_choice";
127
- DocumentElementType2["RATING_SCALE"] = "rating_scale";
128
- DocumentElementType2["TEXT_INPUT"] = "text_input";
129
- DocumentElementType2["DATE_PICKER"] = "date_picker";
130
- DocumentElementType2["SIGNATURE"] = "signature";
131
- DocumentElementType2["DITIGAL_SIGNATURE"] = "digital_signature";
132
- DocumentElementType2["FILE_UPLOAD"] = "file_upload";
133
- return DocumentElementType2;
134
- })(DocumentElementType || {});
135
- var ListType = /* @__PURE__ */ ((ListType2) => {
136
- ListType2["ORDERED"] = "ordered";
137
- ListType2["UNORDERED"] = "unordered";
138
- return ListType2;
139
- })(ListType || {});
140
- var HeadingLevel = /* @__PURE__ */ ((HeadingLevel2) => {
141
- HeadingLevel2["H1"] = "h1";
142
- HeadingLevel2["H2"] = "h2";
143
- HeadingLevel2["H3"] = "h3";
144
- HeadingLevel2["H4"] = "h4";
145
- HeadingLevel2["H5"] = "h5";
146
- HeadingLevel2["H6"] = "h6";
147
- return HeadingLevel2;
148
- })(HeadingLevel || {});
149
- var DynamicVariable = /* @__PURE__ */ ((DynamicVariable2) => {
150
- DynamicVariable2["PATIENT_NAME"] = "$[PATIENT_NAME]";
151
- DynamicVariable2["DOCTOR_NAME"] = "$[DOCTOR_NAME]";
152
- DynamicVariable2["CLINIC_NAME"] = "$[CLINIC_NAME]";
153
- DynamicVariable2["PATIENT_BIRTHDAY"] = "$[PATIENT_BIRTHDAY]";
154
- DynamicVariable2["APPOINTMENT_DATE"] = "$[APPOINTMENT_DATE]";
155
- DynamicVariable2["CURRENT_DATE"] = "$[CURRENT_DATE]";
156
- DynamicVariable2["PROCEDURE_NAME"] = "$[PROCEDURE_NAME]";
157
- DynamicVariable2["PROCEDURE_DESCRIPTION"] = "$[PROCEDURE_DESCRIPTION]";
158
- DynamicVariable2["PROCEDURE_COST"] = "$[PROCEDURE_COST]";
159
- DynamicVariable2["PROCEDURE_DURATION"] = "$[PROCEDURE_DURATION]";
160
- DynamicVariable2["PROCEDURE_RISK"] = "$[PROCEDURE_RISK]";
161
- return DynamicVariable2;
162
- })(DynamicVariable || {});
163
- var FilledDocumentStatus = /* @__PURE__ */ ((FilledDocumentStatus2) => {
164
- FilledDocumentStatus2["DRAFT"] = "draft";
165
- FilledDocumentStatus2["SKIPPED"] = "skipped";
166
- FilledDocumentStatus2["PENDING"] = "pending";
167
- FilledDocumentStatus2["COMPLETED"] = "completed";
168
- FilledDocumentStatus2["SIGNED"] = "signed";
169
- FilledDocumentStatus2["REJECTED"] = "rejected";
170
- return FilledDocumentStatus2;
171
- })(FilledDocumentStatus || {});
172
-
173
- // src/backoffice/constants/certification.constants.ts
174
- var DEFAULT_CERTIFICATION_REQUIREMENT = {
175
- minimumLevel: "aesthetician" /* AESTHETICIAN */,
176
- requiredSpecialties: []
177
- };
178
-
179
- // src/backoffice/errors/backoffice.errors.ts
180
- var BackofficeError = class extends Error {
181
- constructor(message) {
182
- super(message);
183
- this.name = "BackofficeError";
184
- }
185
- };
186
- var CategoryError = class extends BackofficeError {
187
- constructor(message) {
188
- super(message);
189
- this.name = "CategoryError";
190
- }
191
- };
192
- var CategoryNotFoundError = class extends CategoryError {
193
- constructor(id) {
194
- super(`Kategorija sa ID-em ${id} nije prona\u0111ena`);
195
- this.name = "CategoryNotFoundError";
196
- }
197
- };
198
- var InvalidCategoryDataError = class extends CategoryError {
199
- constructor(message) {
200
- super(`Neva\u017Ee\u0107i podaci za kategoriju: ${message}`);
201
- this.name = "InvalidCategoryDataError";
202
- }
203
- };
204
- var SubcategoryError = class extends BackofficeError {
205
- constructor(message) {
206
- super(message);
207
- this.name = "SubcategoryError";
208
- }
209
- };
210
- var SubcategoryNotFoundError = class extends SubcategoryError {
211
- constructor(id) {
212
- super(`Podkategorija sa ID-em ${id} nije prona\u0111ena`);
213
- this.name = "SubcategoryNotFoundError";
214
- }
215
- };
216
- var InvalidSubcategoryDataError = class extends SubcategoryError {
217
- constructor(message) {
218
- super(`Neva\u017Ee\u0107i podaci za podkategoriju: ${message}`);
219
- this.name = "InvalidSubcategoryDataError";
220
- }
221
- };
222
- var TechnologyError = class extends BackofficeError {
223
- constructor(message) {
224
- super(message);
225
- this.name = "TechnologyError";
226
- }
227
- };
228
- var TechnologyNotFoundError = class extends TechnologyError {
229
- constructor(id) {
230
- super(`Tehnologija sa ID-em ${id} nije prona\u0111ena`);
231
- this.name = "TechnologyNotFoundError";
16
+ // src/services/base.service.ts
17
+ import { getStorage } from "firebase/storage";
18
+ var BaseService = class {
19
+ constructor(db, auth, app) {
20
+ this.db = db;
21
+ this.auth = auth;
22
+ this.app = app;
23
+ this.storage = getStorage(app);
232
24
  }
233
- };
234
- var InvalidTechnologyDataError = class extends TechnologyError {
235
- constructor(message) {
236
- super(`Neva\u017Ee\u0107i podaci za tehnologiju: ${message}`);
237
- this.name = "InvalidTechnologyDataError";
25
+ /**
26
+ * Generiše jedinstveni ID za dokumente
27
+ * Format: xxxxxxxxxxxx-timestamp
28
+ * Gde je x random karakter (broj ili slovo)
29
+ */
30
+ generateId() {
31
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
32
+ const timestamp = Date.now().toString(36);
33
+ const randomPart = Array.from(
34
+ { length: 12 },
35
+ () => chars.charAt(Math.floor(Math.random() * chars.length))
36
+ ).join("");
37
+ return `${randomPart}-${timestamp}`;
238
38
  }
239
39
  };
240
- var RequirementError = class extends BackofficeError {
241
- constructor(message) {
242
- super(message);
243
- this.name = "RequirementError";
40
+
41
+ // src/backoffice/services/brand.service.ts
42
+ var BrandService = class extends BaseService {
43
+ /**
44
+ * Gets reference to brands collection
45
+ */
46
+ getBrandsRef() {
47
+ return collection(this.db, BRANDS_COLLECTION);
244
48
  }
245
- };
246
- var RequirementNotFoundError = class extends RequirementError {
247
- constructor(id) {
248
- super(`Zahtev sa ID-em ${id} nije prona\u0111en`);
249
- this.name = "RequirementNotFoundError";
49
+ /**
50
+ * Creates a new brand
51
+ */
52
+ async create(brand) {
53
+ const now = /* @__PURE__ */ new Date();
54
+ const newBrand = {
55
+ ...brand,
56
+ createdAt: now,
57
+ updatedAt: now,
58
+ isActive: true
59
+ };
60
+ const docRef = await addDoc(this.getBrandsRef(), newBrand);
61
+ return { id: docRef.id, ...newBrand };
250
62
  }
251
- };
252
- var InvalidRequirementDataError = class extends RequirementError {
253
- constructor(message) {
254
- super(`Neva\u017Ee\u0107i podaci za zahtev: ${message}`);
255
- this.name = "InvalidRequirementDataError";
63
+ /**
64
+ * Gets all active brands
65
+ */
66
+ async getAll() {
67
+ const q = query(this.getBrandsRef(), where("isActive", "==", true));
68
+ const snapshot = await getDocs(q);
69
+ return snapshot.docs.map(
70
+ (doc10) => ({
71
+ id: doc10.id,
72
+ ...doc10.data()
73
+ })
74
+ );
256
75
  }
257
- };
258
- var InvalidTimeframeError = class extends RequirementError {
259
- constructor(message) {
260
- super(`Invalid timeframe: ${message}`);
261
- this.name = "InvalidTimeframeError";
76
+ /**
77
+ * Updates a brand
78
+ */
79
+ async update(brandId, brand) {
80
+ const updateData = {
81
+ ...brand,
82
+ updatedAt: /* @__PURE__ */ new Date()
83
+ };
84
+ const docRef = doc(this.getBrandsRef(), brandId);
85
+ await updateDoc(docRef, updateData);
86
+ return this.getById(brandId);
262
87
  }
263
- };
264
- var RelationshipError = class extends BackofficeError {
265
- constructor(message) {
266
- super(message);
267
- this.name = "RelationshipError";
88
+ /**
89
+ * Soft deletes a brand
90
+ */
91
+ async delete(brandId) {
92
+ await this.update(brandId, {
93
+ isActive: false
94
+ });
268
95
  }
269
- };
270
- var InvalidHierarchyError = class extends RelationshipError {
271
- constructor(message) {
272
- super(`Invalid hierarchy: ${message}`);
273
- this.name = "InvalidHierarchyError";
96
+ /**
97
+ * Gets a brand by ID
98
+ */
99
+ async getById(brandId) {
100
+ const docRef = doc(this.getBrandsRef(), brandId);
101
+ const docSnap = await getDoc(docRef);
102
+ if (!docSnap.exists()) return null;
103
+ return {
104
+ id: docSnap.id,
105
+ ...docSnap.data()
106
+ };
274
107
  }
275
108
  };
276
- var CircularReferenceError = class extends RelationshipError {
277
- constructor(message) {
278
- super(`Circular reference detected: ${message}`);
279
- this.name = "CircularReferenceError";
109
+
110
+ // src/backoffice/services/category.service.ts
111
+ import {
112
+ addDoc as addDoc2,
113
+ collection as collection2,
114
+ doc as doc2,
115
+ getDoc as getDoc2,
116
+ getDocs as getDocs2,
117
+ query as query2,
118
+ updateDoc as updateDoc2,
119
+ where as where2
120
+ } from "firebase/firestore";
121
+
122
+ // src/backoffice/types/category.types.ts
123
+ var CATEGORIES_COLLECTION = "backoffice_categories";
124
+
125
+ // src/backoffice/services/category.service.ts
126
+ var CategoryService = class extends BaseService {
127
+ /**
128
+ * Referenca na Firestore kolekciju kategorija
129
+ */
130
+ get categoriesRef() {
131
+ return collection2(this.db, CATEGORIES_COLLECTION);
280
132
  }
281
- };
282
- var BlockingConditionError = class extends BackofficeError {
283
- constructor(message) {
284
- super(message);
285
- this.name = "BlockingConditionError";
133
+ /**
134
+ * Kreira novu kategoriju u sistemu
135
+ * @param category - Podaci za novu kategoriju
136
+ * @returns Kreirana kategorija sa generisanim ID-em
137
+ */
138
+ async create(category) {
139
+ const now = /* @__PURE__ */ new Date();
140
+ const newCategory = {
141
+ ...category,
142
+ createdAt: now,
143
+ updatedAt: now,
144
+ isActive: true
145
+ };
146
+ const docRef = await addDoc2(this.categoriesRef, newCategory);
147
+ return { id: docRef.id, ...newCategory };
286
148
  }
287
- };
288
- var InvalidBlockingConditionError = class extends BlockingConditionError {
289
- constructor(condition) {
290
- super(`Neva\u017Ee\u0107i blokiraju\u0107i uslov: ${condition}`);
291
- this.name = "InvalidBlockingConditionError";
149
+ /**
150
+ * Vraća sve aktivne kategorije
151
+ * @returns Lista aktivnih kategorija
152
+ */
153
+ async getAll() {
154
+ const q = query2(this.categoriesRef, where2("isActive", "==", true));
155
+ const snapshot = await getDocs2(q);
156
+ return snapshot.docs.map(
157
+ (doc10) => ({
158
+ id: doc10.id,
159
+ ...doc10.data()
160
+ })
161
+ );
292
162
  }
293
- };
294
- var ContraindicationError = class extends BackofficeError {
295
- constructor(message) {
296
- super(message);
297
- this.name = "ContraindicationError";
163
+ /**
164
+ * Vraća sve aktivne kategorije za određenu familiju procedura
165
+ * @param family - Familija procedura (aesthetics/surgery)
166
+ * @returns Lista kategorija koje pripadaju traženoj familiji
167
+ */
168
+ async getAllByFamily(family) {
169
+ const q = query2(
170
+ this.categoriesRef,
171
+ where2("family", "==", family),
172
+ where2("isActive", "==", true)
173
+ );
174
+ const snapshot = await getDocs2(q);
175
+ return snapshot.docs.map(
176
+ (doc10) => ({
177
+ id: doc10.id,
178
+ ...doc10.data()
179
+ })
180
+ );
298
181
  }
299
- };
300
- var InvalidContraindicationError = class extends ContraindicationError {
301
- constructor(contraindication) {
302
- super(`Neva\u017Ee\u0107a kontraindikacija: ${contraindication}`);
303
- this.name = "InvalidContraindicationError";
182
+ /**
183
+ * Ažurira postojeću kategoriju
184
+ * @param id - ID kategorije koja se ažurira
185
+ * @param category - Novi podaci za kategoriju
186
+ * @returns Ažurirana kategorija
187
+ */
188
+ async update(id, category) {
189
+ const updateData = {
190
+ ...category,
191
+ updatedAt: /* @__PURE__ */ new Date()
192
+ };
193
+ const docRef = doc2(this.categoriesRef, id);
194
+ await updateDoc2(docRef, updateData);
195
+ return this.getById(id);
304
196
  }
305
- };
306
- var TreatmentBenefitError = class extends BackofficeError {
307
- constructor(message) {
308
- super(message);
309
- this.name = "TreatmentBenefitError";
197
+ /**
198
+ * Soft delete kategorije (postavlja isActive na false)
199
+ * @param id - ID kategorije koja se briše
200
+ */
201
+ async delete(id) {
202
+ await this.update(id, { isActive: false });
310
203
  }
311
- };
312
- var InvalidTreatmentBenefitError = class extends TreatmentBenefitError {
313
- constructor(benefit) {
314
- super(`Neva\u017Ee\u0107i benefit tretmana: ${benefit}`);
315
- this.name = "InvalidTreatmentBenefitError";
204
+ /**
205
+ * Vraća kategoriju po ID-u
206
+ * @param id - ID tražene kategorije
207
+ * @returns Kategorija ili null ako ne postoji
208
+ */
209
+ async getById(id) {
210
+ const docRef = doc2(this.categoriesRef, id);
211
+ const docSnap = await getDoc2(docRef);
212
+ if (!docSnap.exists()) return null;
213
+ return {
214
+ id: docSnap.id,
215
+ ...docSnap.data()
216
+ };
316
217
  }
317
218
  };
318
219
 
319
- // src/backoffice/validations/schemas.ts
320
- import { z as z2 } from "zod";
220
+ // src/services/documentation-templates/documentation-template.service.ts
221
+ import {
222
+ collection as collection3,
223
+ doc as doc3,
224
+ getDoc as getDoc3,
225
+ getDocs as getDocs3,
226
+ setDoc,
227
+ updateDoc as updateDoc3,
228
+ deleteDoc,
229
+ query as query3,
230
+ where as where3,
231
+ orderBy,
232
+ limit,
233
+ startAfter
234
+ } from "firebase/firestore";
235
+
236
+ // src/types/documentation-templates/index.ts
237
+ var DOCUMENTATION_TEMPLATES_COLLECTION = "documentation-templates";
238
+ var DocumentElementType = /* @__PURE__ */ ((DocumentElementType2) => {
239
+ DocumentElementType2["HEADING"] = "heading";
240
+ DocumentElementType2["PARAGRAPH"] = "paragraph";
241
+ DocumentElementType2["LIST"] = "list";
242
+ DocumentElementType2["DYNAMIC_TEXT"] = "dynamic_text";
243
+ DocumentElementType2["BINARY_CHOICE"] = "binary_choice";
244
+ DocumentElementType2["MULTIPLE_CHOICE"] = "multiple_choice";
245
+ DocumentElementType2["SINGLE_CHOICE"] = "single_choice";
246
+ DocumentElementType2["RATING_SCALE"] = "rating_scale";
247
+ DocumentElementType2["TEXT_INPUT"] = "text_input";
248
+ DocumentElementType2["DATE_PICKER"] = "date_picker";
249
+ DocumentElementType2["SIGNATURE"] = "signature";
250
+ DocumentElementType2["DITIGAL_SIGNATURE"] = "digital_signature";
251
+ DocumentElementType2["FILE_UPLOAD"] = "file_upload";
252
+ return DocumentElementType2;
253
+ })(DocumentElementType || {});
254
+ var ListType = /* @__PURE__ */ ((ListType2) => {
255
+ ListType2["ORDERED"] = "ordered";
256
+ ListType2["UNORDERED"] = "unordered";
257
+ return ListType2;
258
+ })(ListType || {});
259
+ var HeadingLevel = /* @__PURE__ */ ((HeadingLevel2) => {
260
+ HeadingLevel2["H1"] = "h1";
261
+ HeadingLevel2["H2"] = "h2";
262
+ HeadingLevel2["H3"] = "h3";
263
+ HeadingLevel2["H4"] = "h4";
264
+ HeadingLevel2["H5"] = "h5";
265
+ HeadingLevel2["H6"] = "h6";
266
+ return HeadingLevel2;
267
+ })(HeadingLevel || {});
268
+ var DynamicVariable = /* @__PURE__ */ ((DynamicVariable2) => {
269
+ DynamicVariable2["PATIENT_NAME"] = "$[PATIENT_NAME]";
270
+ DynamicVariable2["DOCTOR_NAME"] = "$[DOCTOR_NAME]";
271
+ DynamicVariable2["CLINIC_NAME"] = "$[CLINIC_NAME]";
272
+ DynamicVariable2["PATIENT_BIRTHDAY"] = "$[PATIENT_BIRTHDAY]";
273
+ DynamicVariable2["APPOINTMENT_DATE"] = "$[APPOINTMENT_DATE]";
274
+ DynamicVariable2["CURRENT_DATE"] = "$[CURRENT_DATE]";
275
+ DynamicVariable2["PROCEDURE_NAME"] = "$[PROCEDURE_NAME]";
276
+ DynamicVariable2["PROCEDURE_DESCRIPTION"] = "$[PROCEDURE_DESCRIPTION]";
277
+ DynamicVariable2["PROCEDURE_COST"] = "$[PROCEDURE_COST]";
278
+ DynamicVariable2["PROCEDURE_DURATION"] = "$[PROCEDURE_DURATION]";
279
+ DynamicVariable2["PROCEDURE_RISK"] = "$[PROCEDURE_RISK]";
280
+ return DynamicVariable2;
281
+ })(DynamicVariable || {});
282
+ var FilledDocumentStatus = /* @__PURE__ */ ((FilledDocumentStatus2) => {
283
+ FilledDocumentStatus2["DRAFT"] = "draft";
284
+ FilledDocumentStatus2["SKIPPED"] = "skipped";
285
+ FilledDocumentStatus2["PENDING"] = "pending";
286
+ FilledDocumentStatus2["COMPLETED"] = "completed";
287
+ FilledDocumentStatus2["SIGNED"] = "signed";
288
+ FilledDocumentStatus2["REJECTED"] = "rejected";
289
+ return FilledDocumentStatus2;
290
+ })(FilledDocumentStatus || {});
321
291
 
322
292
  // src/validations/documentation-templates/template.schema.ts
323
293
  import { z } from "zod";
@@ -493,261 +463,478 @@ var updateFilledDocumentDataSchema = z.object({
493
463
  status: filledDocumentStatusSchema.optional()
494
464
  });
495
465
 
496
- // src/backoffice/validations/schemas.ts
497
- var blockingConditionSchema = z2.nativeEnum(BlockingCondition);
498
- var contraindicationSchema = z2.nativeEnum(Contraindication);
499
- var treatmentBenefitSchema = z2.nativeEnum(TreatmentBenefit);
500
- var procedureFamilySchema = z2.nativeEnum(ProcedureFamily);
501
- var timeUnitSchema = z2.nativeEnum(TimeUnit);
502
- var requirementTypeSchema = z2.nativeEnum(RequirementType);
503
- var certificationLevelSchema = z2.nativeEnum(CertificationLevel);
504
- var certificationSpecialtySchema = z2.nativeEnum(
505
- CertificationSpecialty
506
- );
507
- var certificationRequirementSchema = z2.object({
508
- minimumLevel: certificationLevelSchema,
509
- requiredSpecialties: z2.array(certificationSpecialtySchema).max(5, "Maximum 5 specialties allowed").optional()
510
- });
511
- var timeframeSchema = z2.object({
512
- duration: z2.number().min(1, "Duration must be positive"),
513
- unit: timeUnitSchema,
514
- notifyAt: z2.array(z2.number()).min(1, "At least one notification point is required")
515
- });
516
- var requirementSchema = z2.object({
517
- name: z2.string().min(1, "Name is required").max(100, "Name is too long"),
518
- description: z2.string().min(1, "Description is required"),
519
- type: requirementTypeSchema,
520
- timeframe: timeframeSchema,
521
- importance: z2.enum(["low", "medium", "high"]),
522
- isActive: z2.boolean().default(true)
523
- });
524
- var technologyRequirementsSchema = z2.object({
525
- pre: z2.array(requirementSchema),
526
- post: z2.array(requirementSchema)
527
- });
528
- var technologySchema = z2.object({
529
- name: z2.string().min(1, "Name is required").max(100, "Name is too long"),
530
- description: z2.string().max(1e3, "Description is too long").optional(),
531
- technicalDetails: z2.string().max(2e3, "Technical details are too long").optional(),
532
- family: procedureFamilySchema,
533
- categoryId: z2.string().min(1, "Category ID is required"),
534
- subcategoryId: z2.string().min(1, "Subcategory ID is required"),
535
- requirements: technologyRequirementsSchema.default({
536
- pre: [],
537
- post: []
538
- }),
539
- blockingConditions: z2.array(blockingConditionSchema),
540
- contraindications: z2.array(contraindicationSchema),
541
- documentationTemplates: z2.array(documentTemplateSchema),
542
- benefits: z2.array(treatmentBenefitSchema),
543
- certificationRequirement: certificationRequirementSchema,
544
- isActive: z2.boolean().default(true)
545
- });
546
- var categorySchema = z2.object({
547
- name: z2.string().min(1, "Name is required").max(100, "Name is too long"),
548
- description: z2.string().optional(),
549
- family: procedureFamilySchema,
550
- isActive: z2.boolean().default(true)
551
- });
552
- var subcategorySchema = z2.object({
553
- name: z2.string().min(1, "Name is required").max(100, "Name is too long"),
554
- description: z2.string().optional(),
555
- categoryId: z2.string().min(1, "Category ID is required"),
556
- isActive: z2.boolean().default(true)
557
- });
558
- var categoryUpdateSchema = categorySchema.partial();
559
- var subcategoryUpdateSchema = subcategorySchema.partial();
560
- var technologyUpdateSchema = technologySchema.partial();
561
- var requirementUpdateSchema = requirementSchema.partial();
466
+ // src/services/documentation-templates/documentation-template.service.ts
467
+ var DocumentationTemplateService = class extends BaseService {
468
+ constructor() {
469
+ super(...arguments);
470
+ this.collectionRef = collection3(
471
+ this.db,
472
+ DOCUMENTATION_TEMPLATES_COLLECTION
473
+ );
474
+ }
475
+ /**
476
+ * Create a new document template
477
+ * @param data - Template data
478
+ * @param userId - ID of the user creating the template
479
+ * @returns The created template
480
+ */
481
+ async createTemplate(data, userId) {
482
+ const validatedData = createDocumentTemplateSchema.parse(data);
483
+ const templateId = this.generateId();
484
+ const elementsWithIds = validatedData.elements.map((element) => ({
485
+ ...element,
486
+ id: this.generateId()
487
+ }));
488
+ const now = Date.now();
489
+ const template = {
490
+ id: templateId,
491
+ title: validatedData.title,
492
+ description: validatedData.description,
493
+ elements: elementsWithIds,
494
+ createdAt: now,
495
+ updatedAt: now,
496
+ createdBy: userId,
497
+ version: 1,
498
+ isActive: true,
499
+ tags: validatedData.tags || [],
500
+ isUserForm: validatedData.isUserForm || false,
501
+ isRequired: validatedData.isRequired || false,
502
+ sortingOrder: validatedData.sortingOrder || 0
503
+ };
504
+ const docRef = doc3(this.collectionRef, templateId);
505
+ await setDoc(docRef, template);
506
+ return template;
507
+ }
508
+ /**
509
+ * Get a document template by ID
510
+ * @param templateId - ID of the template to retrieve
511
+ * @param version - Optional version number to retrieve (defaults to latest version)
512
+ * @returns The template or null if not found
513
+ */
514
+ async getTemplateById(templateId, version) {
515
+ const docRef = doc3(this.collectionRef, templateId);
516
+ const docSnap = await getDoc3(docRef);
517
+ if (!docSnap.exists()) {
518
+ return null;
519
+ }
520
+ const currentTemplate = docSnap.data();
521
+ if (version === void 0) {
522
+ return currentTemplate;
523
+ }
524
+ if (currentTemplate.version === version) {
525
+ return currentTemplate;
526
+ }
527
+ try {
528
+ const versionTemplate = await this.getTemplateVersion(
529
+ templateId,
530
+ version
531
+ );
532
+ if (versionTemplate) {
533
+ return versionTemplate;
534
+ }
535
+ } catch (error) {
536
+ console.error(`Error getting template version ${version}:`, error);
537
+ }
538
+ return null;
539
+ }
540
+ /**
541
+ * Update an existing document template
542
+ * @param templateId - ID of the template to update
543
+ * @param data - Updated template data
544
+ * @returns The updated template
545
+ */
546
+ async updateTemplate(templateId, data) {
547
+ var _a, _b, _c;
548
+ const validatedData = updateDocumentTemplateSchema.parse(data);
549
+ console.log("Validated data", validatedData);
550
+ const template = await this.getTemplateById(templateId);
551
+ if (!template) {
552
+ throw new Error(`Template with ID ${templateId} not found`);
553
+ }
554
+ const versionsCollectionRef = collection3(
555
+ this.db,
556
+ `${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions`
557
+ );
558
+ const versionDocRef = doc3(
559
+ versionsCollectionRef,
560
+ template.version.toString()
561
+ );
562
+ await setDoc(versionDocRef, template);
563
+ let updatedElements = template.elements;
564
+ if (validatedData.elements) {
565
+ updatedElements = validatedData.elements.map((element) => ({
566
+ ...element,
567
+ id: element.id || this.generateId()
568
+ }));
569
+ }
570
+ const updatePayload = {
571
+ elements: updatedElements,
572
+ updatedAt: Date.now(),
573
+ version: template.version + 1
574
+ };
575
+ if (validatedData.title !== void 0)
576
+ updatePayload.title = validatedData.title;
577
+ if (validatedData.description !== void 0)
578
+ updatePayload.description = validatedData.description;
579
+ if (validatedData.isActive !== void 0)
580
+ updatePayload.isActive = validatedData.isActive;
581
+ if (validatedData.tags !== void 0)
582
+ updatePayload.tags = validatedData.tags;
583
+ updatePayload.isUserForm = (_a = validatedData.isUserForm) != null ? _a : false;
584
+ updatePayload.isRequired = (_b = validatedData.isRequired) != null ? _b : false;
585
+ updatePayload.sortingOrder = (_c = validatedData.sortingOrder) != null ? _c : 0;
586
+ const docRef = doc3(this.collectionRef, templateId);
587
+ console.log("Update payload", updatePayload);
588
+ await updateDoc3(docRef, updatePayload);
589
+ return { ...template, ...updatePayload };
590
+ }
591
+ /**
592
+ * Get a specific version of a template
593
+ * @param templateId - ID of the template
594
+ * @param versionNumber - Version number to retrieve
595
+ * @returns The template version or null if not found
596
+ */
597
+ async getTemplateVersion(templateId, versionNumber) {
598
+ const versionDocRef = doc3(
599
+ this.db,
600
+ `${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions/${versionNumber}`
601
+ );
602
+ const versionDocSnap = await getDoc3(versionDocRef);
603
+ if (!versionDocSnap.exists()) {
604
+ return null;
605
+ }
606
+ return versionDocSnap.data();
607
+ }
608
+ /**
609
+ * Get all versions of a template
610
+ * @param templateId - ID of the template
611
+ * @returns Array of template versions
612
+ */
613
+ async getTemplateOldVersions(templateId) {
614
+ const versionsCollectionRef = collection3(
615
+ this.db,
616
+ `${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions`
617
+ );
618
+ const q = query3(versionsCollectionRef, orderBy("version", "desc"));
619
+ const querySnapshot = await getDocs3(q);
620
+ const versions = [];
621
+ querySnapshot.forEach((doc10) => {
622
+ versions.push(doc10.data());
623
+ });
624
+ return versions;
625
+ }
626
+ /**
627
+ * Delete a document template
628
+ * @param templateId - ID of the template to delete
629
+ */
630
+ async deleteTemplate(templateId) {
631
+ const docRef = doc3(this.collectionRef, templateId);
632
+ await deleteDoc(docRef);
633
+ }
634
+ /**
635
+ * Get all active templates
636
+ * @param pageSize - Number of templates to retrieve
637
+ * @param lastDoc - Last document from previous page for pagination
638
+ * @returns Array of templates and the last document for pagination
639
+ */
640
+ async getActiveTemplates(pageSize = 20, lastDoc) {
641
+ let q = query3(
642
+ this.collectionRef,
643
+ where3("isActive", "==", true),
644
+ orderBy("updatedAt", "desc"),
645
+ limit(pageSize)
646
+ );
647
+ if (lastDoc) {
648
+ q = query3(q, startAfter(lastDoc));
649
+ }
650
+ const querySnapshot = await getDocs3(q);
651
+ const templates = [];
652
+ let lastVisible = null;
653
+ querySnapshot.forEach((doc10) => {
654
+ templates.push(doc10.data());
655
+ lastVisible = doc10;
656
+ });
657
+ return {
658
+ templates,
659
+ lastDoc: lastVisible
660
+ };
661
+ }
662
+ /**
663
+ * Get templates by tags
664
+ * @param tags - Tags to filter by
665
+ * @param pageSize - Number of templates to retrieve
666
+ * @param lastDoc - Last document from previous page for pagination
667
+ * @returns Array of templates and the last document for pagination
668
+ */
669
+ async getTemplatesByTags(tags, pageSize = 20, lastDoc) {
670
+ let q = query3(
671
+ this.collectionRef,
672
+ where3("isActive", "==", true),
673
+ where3("tags", "array-contains-any", tags),
674
+ orderBy("updatedAt", "desc"),
675
+ limit(pageSize)
676
+ );
677
+ if (lastDoc) {
678
+ q = query3(q, startAfter(lastDoc));
679
+ }
680
+ const querySnapshot = await getDocs3(q);
681
+ const templates = [];
682
+ let lastVisible = null;
683
+ querySnapshot.forEach((doc10) => {
684
+ templates.push(doc10.data());
685
+ lastVisible = doc10;
686
+ });
687
+ return {
688
+ templates,
689
+ lastDoc: lastVisible
690
+ };
691
+ }
692
+ /**
693
+ * Get templates created by a specific user
694
+ * @param userId - ID of the user who created the templates
695
+ * @param pageSize - Number of templates to retrieve
696
+ * @param lastDoc - Last document from previous page for pagination
697
+ * @returns Array of templates and the last document for pagination
698
+ */
699
+ async getTemplatesByCreator(userId, pageSize = 20, lastDoc) {
700
+ let q = query3(
701
+ this.collectionRef,
702
+ where3("createdBy", "==", userId),
703
+ orderBy("updatedAt", "desc"),
704
+ limit(pageSize)
705
+ );
706
+ if (lastDoc) {
707
+ q = query3(q, startAfter(lastDoc));
708
+ }
709
+ const querySnapshot = await getDocs3(q);
710
+ const templates = [];
711
+ let lastVisible = null;
712
+ querySnapshot.forEach((doc10) => {
713
+ templates.push(doc10.data());
714
+ lastVisible = doc10;
715
+ });
716
+ return {
717
+ templates,
718
+ lastDoc: lastVisible
719
+ };
720
+ }
721
+ /**
722
+ * Get all templates for selection with optional filtering
723
+ * @param options - Filtering options
724
+ * @returns Array of templates
725
+ */
726
+ async getAllTemplatesForSelection(options) {
727
+ let q = query3(
728
+ this.collectionRef,
729
+ where3("isActive", "==", true),
730
+ orderBy("updatedAt", "desc")
731
+ );
732
+ if ((options == null ? void 0 : options.isUserForm) !== void 0) {
733
+ q = query3(q, where3("isUserForm", "==", options.isUserForm));
734
+ }
735
+ if ((options == null ? void 0 : options.isRequired) !== void 0) {
736
+ q = query3(q, where3("isRequired", "==", options.isRequired));
737
+ }
738
+ const querySnapshot = await getDocs3(q);
739
+ const templates = [];
740
+ querySnapshot.forEach((doc10) => {
741
+ templates.push(doc10.data());
742
+ });
743
+ return templates;
744
+ }
745
+ };
746
+
747
+ // src/services/documentation-templates/filled-document.service.ts
748
+ import {
749
+ collection as collection5,
750
+ doc as doc5,
751
+ getDoc as getDoc5,
752
+ getDocs as getDocs5,
753
+ setDoc as setDoc3,
754
+ updateDoc as updateDoc5,
755
+ query as query5,
756
+ orderBy as orderBy3,
757
+ limit as limit3,
758
+ startAfter as startAfter2
759
+ } from "firebase/firestore";
562
760
 
563
- // src/backoffice/services/category.service.ts
761
+ // src/services/media/media.service.ts
762
+ import { Timestamp as Timestamp2 } from "firebase/firestore";
564
763
  import {
565
- addDoc,
566
- collection,
567
- doc,
568
- getDoc,
569
- getDocs,
570
- query,
571
- updateDoc,
572
- where
764
+ ref,
765
+ uploadBytes,
766
+ getDownloadURL,
767
+ deleteObject,
768
+ getBytes
769
+ } from "firebase/storage";
770
+ import {
771
+ doc as doc4,
772
+ getDoc as getDoc4,
773
+ setDoc as setDoc2,
774
+ updateDoc as updateDoc4,
775
+ collection as collection4,
776
+ query as query4,
777
+ where as where4,
778
+ limit as limit2,
779
+ getDocs as getDocs4,
780
+ deleteDoc as deleteDoc2,
781
+ orderBy as orderBy2
573
782
  } from "firebase/firestore";
574
783
 
575
- // src/services/base.service.ts
576
- import { getStorage } from "firebase/storage";
577
- var BaseService = class {
784
+ // src/backoffice/services/documentation-template.service.ts
785
+ var DocumentationTemplateService2 = class {
786
+ /**
787
+ * Constructor for DocumentationTemplateService
788
+ * @param db - Firestore instance
789
+ * @param auth - Firebase Auth instance
790
+ * @param app - Firebase App instance
791
+ */
578
792
  constructor(db, auth, app) {
579
- this.db = db;
580
- this.auth = auth;
581
- this.app = app;
582
- this.storage = getStorage(app);
793
+ this.apiService = new DocumentationTemplateService(db, auth, app);
583
794
  }
584
795
  /**
585
- * Generiše jedinstveni ID za dokumente
586
- * Format: xxxxxxxxxxxx-timestamp
587
- * Gde je x random karakter (broj ili slovo)
796
+ * Create a new document template
797
+ * @param data - Template data
798
+ * @param userId - ID of the user creating the template
799
+ * @returns The created template
588
800
  */
589
- generateId() {
590
- const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
591
- const timestamp = Date.now().toString(36);
592
- const randomPart = Array.from(
593
- { length: 12 },
594
- () => chars.charAt(Math.floor(Math.random() * chars.length))
595
- ).join("");
596
- return `${randomPart}-${timestamp}`;
801
+ async createTemplate(data, userId) {
802
+ return this.apiService.createTemplate(data, userId);
597
803
  }
598
- };
599
-
600
- // src/backoffice/services/category.service.ts
601
- var CategoryService = class extends BaseService {
602
804
  /**
603
- * Referenca na Firestore kolekciju kategorija
805
+ * Get a document template by ID
806
+ * @param templateId - ID of the template to retrieve
807
+ * @param version - Optional version number to retrieve (defaults to latest version)
808
+ * @returns The template or null if not found
604
809
  */
605
- get categoriesRef() {
606
- return collection(this.db, CATEGORIES_COLLECTION);
810
+ async getTemplateById(templateId, version) {
811
+ return this.apiService.getTemplateById(templateId, version);
607
812
  }
608
813
  /**
609
- * Kreira novu kategoriju u sistemu
610
- * @param category - Podaci za novu kategoriju
611
- * @returns Kreirana kategorija sa generisanim ID-em
814
+ * Update an existing document template
815
+ * @param templateId - ID of the template to update
816
+ * @param data - Updated template data
817
+ * @returns The updated template
612
818
  */
613
- async create(category) {
614
- const now = /* @__PURE__ */ new Date();
615
- const newCategory = {
616
- ...category,
617
- createdAt: now,
618
- updatedAt: now,
619
- isActive: true
620
- };
621
- const docRef = await addDoc(this.categoriesRef, newCategory);
622
- return { id: docRef.id, ...newCategory };
819
+ async updateTemplate(templateId, data) {
820
+ return this.apiService.updateTemplate(templateId, data);
623
821
  }
624
822
  /**
625
- * Vraća sve aktivne kategorije
626
- * @returns Lista aktivnih kategorija
823
+ * Delete a document template
824
+ * @param templateId - ID of the template to delete
627
825
  */
628
- async getAll() {
629
- const q = query(this.categoriesRef, where("isActive", "==", true));
630
- const snapshot = await getDocs(q);
631
- return snapshot.docs.map(
632
- (doc10) => ({
633
- id: doc10.id,
634
- ...doc10.data()
635
- })
636
- );
826
+ async deleteTemplate(templateId) {
827
+ return this.apiService.deleteTemplate(templateId);
637
828
  }
638
829
  /**
639
- * Vraća sve aktivne kategorije za određenu familiju procedura
640
- * @param family - Familija procedura (aesthetics/surgery)
641
- * @returns Lista kategorija koje pripadaju traženoj familiji
830
+ * Get all active templates
831
+ * @param pageSize - Number of templates to retrieve
832
+ * @param lastDoc - Last document from previous page for pagination
833
+ * @returns Array of templates and the last document for pagination
642
834
  */
643
- async getAllByFamily(family) {
644
- const q = query(
645
- this.categoriesRef,
646
- where("family", "==", family),
647
- where("isActive", "==", true)
648
- );
649
- const snapshot = await getDocs(q);
650
- return snapshot.docs.map(
651
- (doc10) => ({
652
- id: doc10.id,
653
- ...doc10.data()
654
- })
655
- );
835
+ async getActiveTemplates(pageSize = 20, lastDoc) {
836
+ return this.apiService.getActiveTemplates(pageSize, lastDoc);
656
837
  }
657
838
  /**
658
- * Ažurira postojeću kategoriju
659
- * @param id - ID kategorije koja se ažurira
660
- * @param category - Novi podaci za kategoriju
661
- * @returns Ažurirana kategorija
839
+ * Get templates by tags
840
+ * @param tags - Tags to filter by
841
+ * @param pageSize - Number of templates to retrieve
842
+ * @param lastDoc - Last document from previous page for pagination
843
+ * @returns Array of templates and the last document for pagination
662
844
  */
663
- async update(id, category) {
664
- const updateData = {
665
- ...category,
666
- updatedAt: /* @__PURE__ */ new Date()
667
- };
668
- const docRef = doc(this.categoriesRef, id);
669
- await updateDoc(docRef, updateData);
670
- return this.getById(id);
845
+ async getTemplatesByTags(tags, pageSize = 20, lastDoc) {
846
+ return this.apiService.getTemplatesByTags(tags, pageSize, lastDoc);
671
847
  }
672
848
  /**
673
- * Soft delete kategorije (postavlja isActive na false)
674
- * @param id - ID kategorije koja se briše
849
+ * Get templates created by a specific user
850
+ * @param userId - ID of the user who created the templates
851
+ * @param pageSize - Number of templates to retrieve
852
+ * @param lastDoc - Last document from previous page for pagination
853
+ * @returns Array of templates and the last document for pagination
675
854
  */
676
- async delete(id) {
677
- await this.update(id, { isActive: false });
855
+ async getTemplatesByCreator(userId, pageSize = 20, lastDoc) {
856
+ return this.apiService.getTemplatesByCreator(userId, pageSize, lastDoc);
678
857
  }
679
858
  /**
680
- * Vraća kategoriju po ID-u
681
- * @param id - ID tražene kategorije
682
- * @returns Kategorija ili null ako ne postoji
859
+ * Get a specific version of a template
860
+ * @param templateId - ID of the template
861
+ * @param versionNumber - Version number to retrieve
862
+ * @returns The template version or null if not found
683
863
  */
684
- async getById(id) {
685
- const docRef = doc(this.categoriesRef, id);
686
- const docSnap = await getDoc(docRef);
687
- if (!docSnap.exists()) return null;
688
- return {
689
- id: docSnap.id,
690
- ...docSnap.data()
691
- };
864
+ async getTemplateVersion(templateId, versionNumber) {
865
+ return this.apiService.getTemplateVersion(templateId, versionNumber);
866
+ }
867
+ /**
868
+ * Get all versions of a template
869
+ * @param templateId - ID of the template
870
+ * @returns Array of template versions
871
+ */
872
+ async getTemplateVersions(templateId) {
873
+ return this.apiService.getTemplateOldVersions(templateId);
692
874
  }
693
875
  };
694
876
 
695
- // src/backoffice/services/subcategory.service.ts
877
+ // src/backoffice/services/product.service.ts
696
878
  import {
697
- addDoc as addDoc2,
698
- collection as collection2,
699
- doc as doc2,
700
- getDoc as getDoc2,
701
- getDocs as getDocs2,
702
- query as query2,
703
- updateDoc as updateDoc2,
704
- where as where2
879
+ addDoc as addDoc3,
880
+ collection as collection6,
881
+ doc as doc6,
882
+ getDoc as getDoc6,
883
+ getDocs as getDocs6,
884
+ query as query6,
885
+ updateDoc as updateDoc6,
886
+ where as where6
705
887
  } from "firebase/firestore";
706
- var SubcategoryService = class extends BaseService {
888
+
889
+ // src/backoffice/types/product.types.ts
890
+ var PRODUCTS_COLLECTION = "products";
891
+
892
+ // src/backoffice/types/technology.types.ts
893
+ var TECHNOLOGIES_COLLECTION = "technologies";
894
+
895
+ // src/backoffice/services/product.service.ts
896
+ var ProductService = class extends BaseService {
707
897
  /**
708
- * Vraća referencu na Firestore kolekciju podkategorija za određenu kategoriju
709
- * @param categoryId - ID roditeljske kategorije
898
+ * Gets reference to products collection under a technology
899
+ * @param technologyId - ID of the technology
900
+ * @returns Firestore collection reference
710
901
  */
711
- getSubcategoriesRef(categoryId) {
712
- return collection2(
902
+ getProductsRef(technologyId) {
903
+ return collection6(
713
904
  this.db,
714
- CATEGORIES_COLLECTION,
715
- categoryId,
716
- SUBCATEGORIES_COLLECTION
905
+ TECHNOLOGIES_COLLECTION,
906
+ technologyId,
907
+ PRODUCTS_COLLECTION
717
908
  );
718
909
  }
719
910
  /**
720
- * Kreira novu podkategoriju u okviru kategorije
721
- * @param categoryId - ID kategorije kojoj će pripadati nova podkategorija
722
- * @param subcategory - Podaci za novu podkategoriju
723
- * @returns Kreirana podkategorija sa generisanim ID-em
911
+ * Creates a new product under technology
724
912
  */
725
- async create(categoryId, subcategory) {
913
+ async create(technologyId, brandId, product) {
726
914
  const now = /* @__PURE__ */ new Date();
727
- const newSubcategory = {
728
- ...subcategory,
729
- categoryId,
915
+ const newProduct = {
916
+ ...product,
917
+ brandId,
918
+ technologyId,
730
919
  createdAt: now,
731
920
  updatedAt: now,
732
921
  isActive: true
733
922
  };
734
- const docRef = await addDoc2(
735
- this.getSubcategoriesRef(categoryId),
736
- newSubcategory
923
+ const productRef = await addDoc3(
924
+ this.getProductsRef(technologyId),
925
+ newProduct
737
926
  );
738
- return { id: docRef.id, ...newSubcategory };
927
+ return { id: productRef.id, ...newProduct };
739
928
  }
740
929
  /**
741
- * Vraća sve aktivne podkategorije za određenu kategoriju
742
- * @param categoryId - ID kategorije čije podkategorije tražimo
743
- * @returns Lista aktivnih podkategorija
930
+ * Gets all products for a technology
744
931
  */
745
- async getAllByCategoryId(categoryId) {
746
- const q = query2(
747
- this.getSubcategoriesRef(categoryId),
748
- where2("isActive", "==", true)
932
+ async getAllByTechnology(technologyId) {
933
+ const q = query6(
934
+ this.getProductsRef(technologyId),
935
+ where6("isActive", "==", true)
749
936
  );
750
- const snapshot = await getDocs2(q);
937
+ const snapshot = await getDocs6(q);
751
938
  return snapshot.docs.map(
752
939
  (doc10) => ({
753
940
  id: doc10.id,
@@ -756,38 +943,56 @@ var SubcategoryService = class extends BaseService {
756
943
  );
757
944
  }
758
945
  /**
759
- * Ažurira postojeću podkategoriju
760
- * @param categoryId - ID kategorije kojoj pripada podkategorija
761
- * @param subcategoryId - ID podkategorije koja se ažurira
762
- * @param subcategory - Novi podaci za podkategoriju
763
- * @returns Ažurirana podkategorija
946
+ * Gets all products for a brand by filtering through all technologies
764
947
  */
765
- async update(categoryId, subcategoryId, subcategory) {
948
+ async getAllByBrand(brandId) {
949
+ const allTechnologiesRef = collection6(this.db, TECHNOLOGIES_COLLECTION);
950
+ const technologiesSnapshot = await getDocs6(allTechnologiesRef);
951
+ const products = [];
952
+ for (const techDoc of technologiesSnapshot.docs) {
953
+ const q = query6(
954
+ this.getProductsRef(techDoc.id),
955
+ where6("brandId", "==", brandId),
956
+ where6("isActive", "==", true)
957
+ );
958
+ const snapshot = await getDocs6(q);
959
+ products.push(
960
+ ...snapshot.docs.map(
961
+ (doc10) => ({
962
+ id: doc10.id,
963
+ ...doc10.data()
964
+ })
965
+ )
966
+ );
967
+ }
968
+ return products;
969
+ }
970
+ /**
971
+ * Updates a product
972
+ */
973
+ async update(technologyId, productId, product) {
766
974
  const updateData = {
767
- ...subcategory,
975
+ ...product,
768
976
  updatedAt: /* @__PURE__ */ new Date()
769
977
  };
770
- const docRef = doc2(this.getSubcategoriesRef(categoryId), subcategoryId);
771
- await updateDoc2(docRef, updateData);
772
- return this.getById(categoryId, subcategoryId);
978
+ const docRef = doc6(this.getProductsRef(technologyId), productId);
979
+ await updateDoc6(docRef, updateData);
980
+ return this.getById(technologyId, productId);
773
981
  }
774
982
  /**
775
- * Soft delete podkategorije (postavlja isActive na false)
776
- * @param categoryId - ID kategorije kojoj pripada podkategorija
777
- * @param subcategoryId - ID podkategorije koja se briše
983
+ * Soft deletes a product
778
984
  */
779
- async delete(categoryId, subcategoryId) {
780
- await this.update(categoryId, subcategoryId, { isActive: false });
985
+ async delete(technologyId, productId) {
986
+ await this.update(technologyId, productId, {
987
+ isActive: false
988
+ });
781
989
  }
782
990
  /**
783
- * Vraća podkategoriju po ID-u
784
- * @param categoryId - ID kategorije kojoj pripada podkategorija
785
- * @param subcategoryId - ID tražene podkategorije
786
- * @returns Podkategorija ili null ako ne postoji
991
+ * Gets a product by ID
787
992
  */
788
- async getById(categoryId, subcategoryId) {
789
- const docRef = doc2(this.getSubcategoriesRef(categoryId), subcategoryId);
790
- const docSnap = await getDoc2(docRef);
993
+ async getById(technologyId, productId) {
994
+ const docRef = doc6(this.getProductsRef(technologyId), productId);
995
+ const docSnap = await getDoc6(docRef);
791
996
  if (!docSnap.exists()) return null;
792
997
  return {
793
998
  id: docSnap.id,
@@ -796,99 +1001,62 @@ var SubcategoryService = class extends BaseService {
796
1001
  }
797
1002
  };
798
1003
 
799
- // src/backoffice/services/technology.service.ts
1004
+ // src/backoffice/services/requirement.service.ts
800
1005
  import {
801
- addDoc as addDoc3,
802
- collection as collection3,
803
- doc as doc3,
804
- getDoc as getDoc3,
805
- getDocs as getDocs3,
806
- query as query3,
807
- updateDoc as updateDoc3,
808
- where as where3,
809
- arrayUnion,
810
- arrayRemove
1006
+ addDoc as addDoc4,
1007
+ collection as collection7,
1008
+ doc as doc7,
1009
+ getDoc as getDoc7,
1010
+ getDocs as getDocs7,
1011
+ query as query7,
1012
+ updateDoc as updateDoc7,
1013
+ where as where7
811
1014
  } from "firebase/firestore";
812
- var DEFAULT_CERTIFICATION_REQUIREMENT2 = {
813
- minimumLevel: "aesthetician" /* AESTHETICIAN */,
814
- requiredSpecialties: []
815
- };
816
- var TechnologyService = class extends BaseService {
817
- /**
818
- * Vraća referencu na Firestore kolekciju tehnologija
819
- */
820
- getTechnologiesRef() {
821
- return collection3(this.db, TECHNOLOGIES_COLLECTION);
822
- }
823
- /**
824
- * Kreira novu tehnologiju
825
- * @param technology - Podaci za novu tehnologiju
826
- * @returns Kreirana tehnologija sa generisanim ID-em
827
- */
828
- async create(technology) {
829
- const now = /* @__PURE__ */ new Date();
830
- const newTechnology = {
831
- ...technology,
832
- createdAt: now,
833
- updatedAt: now,
834
- isActive: true,
835
- requirements: technology.requirements || {
836
- pre: [],
837
- post: []
838
- },
839
- blockingConditions: technology.blockingConditions || [],
840
- contraindications: technology.contraindications || [],
841
- benefits: technology.benefits || [],
842
- certificationRequirement: technology.certificationRequirement || DEFAULT_CERTIFICATION_REQUIREMENT2
843
- };
844
- const docRef = await addDoc3(this.getTechnologiesRef(), newTechnology);
845
- return { id: docRef.id, ...newTechnology };
846
- }
1015
+
1016
+ // src/backoffice/types/requirement.types.ts
1017
+ var TimeUnit = /* @__PURE__ */ ((TimeUnit2) => {
1018
+ TimeUnit2["HOURS"] = "hours";
1019
+ TimeUnit2["DAYS"] = "days";
1020
+ return TimeUnit2;
1021
+ })(TimeUnit || {});
1022
+ var RequirementType = /* @__PURE__ */ ((RequirementType3) => {
1023
+ RequirementType3["PRE"] = "pre";
1024
+ RequirementType3["POST"] = "post";
1025
+ return RequirementType3;
1026
+ })(RequirementType || {});
1027
+ var REQUIREMENTS_COLLECTION = "backoffice_requirements";
1028
+
1029
+ // src/backoffice/services/requirement.service.ts
1030
+ var RequirementService = class extends BaseService {
847
1031
  /**
848
- * Vraća sve aktivne tehnologije
849
- * @returns Lista aktivnih tehnologija
1032
+ * Referenca na Firestore kolekciju zahteva
850
1033
  */
851
- async getAll() {
852
- const q = query3(this.getTechnologiesRef(), where3("isActive", "==", true));
853
- const snapshot = await getDocs3(q);
854
- return snapshot.docs.map(
855
- (doc10) => ({
856
- id: doc10.id,
857
- ...doc10.data()
858
- })
859
- );
1034
+ get requirementsRef() {
1035
+ return collection7(this.db, REQUIREMENTS_COLLECTION);
860
1036
  }
861
1037
  /**
862
- * Vraća sve aktivne tehnologije za određenu familiju
863
- * @param family - Familija procedura
864
- * @returns Lista aktivnih tehnologija
1038
+ * Kreira novi globalni zahtev
1039
+ * @param requirement - Podaci za novi zahtev
1040
+ * @returns Kreirani zahtev sa generisanim ID-em
865
1041
  */
866
- async getAllByFamily(family) {
867
- const q = query3(
868
- this.getTechnologiesRef(),
869
- where3("isActive", "==", true),
870
- where3("family", "==", family)
871
- );
872
- const snapshot = await getDocs3(q);
873
- return snapshot.docs.map(
874
- (doc10) => ({
875
- id: doc10.id,
876
- ...doc10.data()
877
- })
878
- );
1042
+ async create(requirement) {
1043
+ const now = /* @__PURE__ */ new Date();
1044
+ const newRequirement = {
1045
+ ...requirement,
1046
+ createdAt: now,
1047
+ updatedAt: now,
1048
+ isActive: true
1049
+ };
1050
+ const docRef = await addDoc4(this.requirementsRef, newRequirement);
1051
+ return { id: docRef.id, ...newRequirement };
879
1052
  }
880
1053
  /**
881
- * Vraća sve aktivne tehnologije za određenu kategoriju
882
- * @param categoryId - ID kategorije
883
- * @returns Lista aktivnih tehnologija
1054
+ * Vraća sve aktivne zahteve
1055
+ * @returns Lista aktivnih zahteva
884
1056
  */
885
- async getAllByCategoryId(categoryId) {
886
- const q = query3(
887
- this.getTechnologiesRef(),
888
- where3("isActive", "==", true),
889
- where3("categoryId", "==", categoryId)
890
- );
891
- const snapshot = await getDocs3(q);
1057
+ async getAll() {
1058
+ const q = query7(this.requirementsRef, where7("isActive", "==", true));
1059
+ const snapshot = await getDocs7(q);
892
1060
  return snapshot.docs.map(
893
1061
  (doc10) => ({
894
1062
  id: doc10.id,
@@ -897,17 +1065,17 @@ var TechnologyService = class extends BaseService {
897
1065
  );
898
1066
  }
899
1067
  /**
900
- * Vraća sve aktivne tehnologije za određenu podkategoriju
901
- * @param subcategoryId - ID podkategorije
902
- * @returns Lista aktivnih tehnologija
1068
+ * Vraća sve aktivne zahteve određenog tipa
1069
+ * @param type - Tip zahteva (pre/post)
1070
+ * @returns Lista zahteva određenog tipa
903
1071
  */
904
- async getAllBySubcategoryId(subcategoryId) {
905
- const q = query3(
906
- this.getTechnologiesRef(),
907
- where3("isActive", "==", true),
908
- where3("subcategoryId", "==", subcategoryId)
1072
+ async getAllByType(type) {
1073
+ const q = query7(
1074
+ this.requirementsRef,
1075
+ where7("type", "==", type),
1076
+ where7("isActive", "==", true)
909
1077
  );
910
- const snapshot = await getDocs3(q);
1078
+ const snapshot = await getDocs7(q);
911
1079
  return snapshot.docs.map(
912
1080
  (doc10) => ({
913
1081
  id: doc10.id,
@@ -916,357 +1084,271 @@ var TechnologyService = class extends BaseService {
916
1084
  );
917
1085
  }
918
1086
  /**
919
- * Ažurira postojeću tehnologiju
920
- * @param technologyId - ID tehnologije
921
- * @param technology - Novi podaci za tehnologiju
922
- * @returns Ažurirana tehnologija
1087
+ * Ažurira postojeći zahtev
1088
+ * @param id - ID zahteva koji se ažurira
1089
+ * @param requirement - Novi podaci za zahtev
1090
+ * @returns Ažurirani zahtev
923
1091
  */
924
- async update(technologyId, technology) {
1092
+ async update(id, requirement) {
925
1093
  const updateData = {
926
- ...technology,
1094
+ ...requirement,
927
1095
  updatedAt: /* @__PURE__ */ new Date()
928
1096
  };
929
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
930
- await updateDoc3(docRef, updateData);
931
- return this.getById(technologyId);
1097
+ const docRef = doc7(this.requirementsRef, id);
1098
+ await updateDoc7(docRef, updateData);
1099
+ return this.getById(id);
932
1100
  }
933
1101
  /**
934
- * Soft delete tehnologije (postavlja isActive na false)
935
- * @param technologyId - ID tehnologije koja se briše
1102
+ * Soft delete zahteva (postavlja isActive na false)
1103
+ * @param id - ID zahteva koji se briše
936
1104
  */
937
- async delete(technologyId) {
938
- await this.update(technologyId, {
939
- isActive: false
940
- });
1105
+ async delete(id) {
1106
+ await this.update(id, { isActive: false });
941
1107
  }
942
1108
  /**
943
- * Vraća tehnologiju po ID-u
944
- * @param technologyId - ID tražene tehnologije
945
- * @returns Tehnologija ili null ako ne postoji
1109
+ * Vraća zahtev po ID-u
1110
+ * @param id - ID traženog zahteva
1111
+ * @returns Zahtev ili null ako ne postoji
946
1112
  */
947
- async getById(technologyId) {
948
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
949
- const docSnap = await getDoc3(docRef);
1113
+ async getById(id) {
1114
+ const docRef = doc7(this.requirementsRef, id);
1115
+ const docSnap = await getDoc7(docRef);
950
1116
  if (!docSnap.exists()) return null;
951
1117
  return {
952
1118
  id: docSnap.id,
953
1119
  ...docSnap.data()
954
1120
  };
955
1121
  }
1122
+ };
1123
+
1124
+ // src/backoffice/services/subcategory.service.ts
1125
+ import {
1126
+ addDoc as addDoc5,
1127
+ collection as collection8,
1128
+ doc as doc8,
1129
+ getDoc as getDoc8,
1130
+ getDocs as getDocs8,
1131
+ query as query8,
1132
+ updateDoc as updateDoc8,
1133
+ where as where8
1134
+ } from "firebase/firestore";
1135
+
1136
+ // src/backoffice/types/subcategory.types.ts
1137
+ var SUBCATEGORIES_COLLECTION = "subcategories";
1138
+
1139
+ // src/backoffice/services/subcategory.service.ts
1140
+ var SubcategoryService = class extends BaseService {
956
1141
  /**
957
- * Dodaje novi zahtev tehnologiji
958
- * @param technologyId - ID tehnologije
959
- * @param requirement - Zahtev koji se dodaje
960
- * @returns Ažurirana tehnologija sa novim zahtevom
961
- */
962
- async addRequirement(technologyId, requirement) {
963
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
964
- const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
965
- await updateDoc3(docRef, {
966
- [requirementType]: arrayUnion(requirement),
967
- updatedAt: /* @__PURE__ */ new Date()
968
- });
969
- return this.getById(technologyId);
970
- }
971
- /**
972
- * Uklanja zahtev iz tehnologije
973
- * @param technologyId - ID tehnologije
974
- * @param requirement - Zahtev koji se uklanja
975
- * @returns Ažurirana tehnologija bez uklonjenog zahteva
976
- */
977
- async removeRequirement(technologyId, requirement) {
978
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
979
- const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
980
- await updateDoc3(docRef, {
981
- [requirementType]: arrayRemove(requirement),
982
- updatedAt: /* @__PURE__ */ new Date()
983
- });
984
- return this.getById(technologyId);
985
- }
986
- /**
987
- * Vraća sve zahteve za tehnologiju
988
- * @param technologyId - ID tehnologije
989
- * @param type - Opcioni filter za tip zahteva (pre/post)
990
- * @returns Lista zahteva
991
- */
992
- async getRequirements(technologyId, type) {
993
- const technology = await this.getById(technologyId);
994
- if (!technology || !technology.requirements) return [];
995
- if (type) {
996
- return technology.requirements[type];
997
- }
998
- return [...technology.requirements.pre, ...technology.requirements.post];
999
- }
1000
- /**
1001
- * Ažurira postojeći zahtev
1002
- * @param technologyId - ID tehnologije
1003
- * @param oldRequirement - Stari zahtev koji se menja
1004
- * @param newRequirement - Novi zahtev koji zamenjuje stari
1005
- * @returns Ažurirana tehnologija
1006
- */
1007
- async updateRequirement(technologyId, oldRequirement, newRequirement) {
1008
- await this.removeRequirement(technologyId, oldRequirement);
1009
- return this.addRequirement(technologyId, newRequirement);
1010
- }
1011
- /**
1012
- * Dodaje blokirajući uslov tehnologiji
1013
- * @param technologyId - ID tehnologije
1014
- * @param condition - Blokirajući uslov koji se dodaje
1015
- * @returns Ažurirana tehnologija
1016
- */
1017
- async addBlockingCondition(technologyId, condition) {
1018
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
1019
- await updateDoc3(docRef, {
1020
- blockingConditions: arrayUnion(condition),
1021
- updatedAt: /* @__PURE__ */ new Date()
1022
- });
1023
- return this.getById(technologyId);
1024
- }
1025
- /**
1026
- * Uklanja blokirajući uslov iz tehnologije
1027
- * @param technologyId - ID tehnologije
1028
- * @param condition - Blokirajući uslov koji se uklanja
1029
- * @returns Ažurirana tehnologija
1030
- */
1031
- async removeBlockingCondition(technologyId, condition) {
1032
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
1033
- await updateDoc3(docRef, {
1034
- blockingConditions: arrayRemove(condition),
1035
- updatedAt: /* @__PURE__ */ new Date()
1036
- });
1037
- return this.getById(technologyId);
1038
- }
1039
- /**
1040
- * Dodaje kontraindikaciju tehnologiji
1041
- * @param technologyId - ID tehnologije
1042
- * @param contraindication - Kontraindikacija koja se dodaje
1043
- * @returns Ažurirana tehnologija
1044
- */
1045
- async addContraindication(technologyId, contraindication) {
1046
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
1047
- await updateDoc3(docRef, {
1048
- contraindications: arrayUnion(contraindication),
1049
- updatedAt: /* @__PURE__ */ new Date()
1050
- });
1051
- return this.getById(technologyId);
1052
- }
1053
- /**
1054
- * Uklanja kontraindikaciju iz tehnologije
1055
- * @param technologyId - ID tehnologije
1056
- * @param contraindication - Kontraindikacija koja se uklanja
1057
- * @returns Ažurirana tehnologija
1058
- */
1059
- async removeContraindication(technologyId, contraindication) {
1060
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
1061
- await updateDoc3(docRef, {
1062
- contraindications: arrayRemove(contraindication),
1063
- updatedAt: /* @__PURE__ */ new Date()
1064
- });
1065
- return this.getById(technologyId);
1066
- }
1067
- /**
1068
- * Dodaje benefit tehnologiji
1069
- * @param technologyId - ID tehnologije
1070
- * @param benefit - Benefit koji se dodaje
1071
- * @returns Ažurirana tehnologija
1072
- */
1073
- async addBenefit(technologyId, benefit) {
1074
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
1075
- await updateDoc3(docRef, {
1076
- benefits: arrayUnion(benefit),
1077
- updatedAt: /* @__PURE__ */ new Date()
1078
- });
1079
- return this.getById(technologyId);
1080
- }
1081
- /**
1082
- * Uklanja benefit iz tehnologije
1083
- * @param technologyId - ID tehnologije
1084
- * @param benefit - Benefit koji se uklanja
1085
- * @returns Ažurirana tehnologija
1086
- */
1087
- async removeBenefit(technologyId, benefit) {
1088
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
1089
- await updateDoc3(docRef, {
1090
- benefits: arrayRemove(benefit),
1091
- updatedAt: /* @__PURE__ */ new Date()
1092
- });
1093
- return this.getById(technologyId);
1094
- }
1095
- /**
1096
- * Vraća sve blokirajuće uslove za tehnologiju
1097
- * @param technologyId - ID tehnologije
1098
- * @returns Lista blokirajućih uslova
1099
- */
1100
- async getBlockingConditions(technologyId) {
1101
- const technology = await this.getById(technologyId);
1102
- return (technology == null ? void 0 : technology.blockingConditions) || [];
1103
- }
1104
- /**
1105
- * Vraća sve kontraindikacije za tehnologiju
1106
- * @param technologyId - ID tehnologije
1107
- * @returns Lista kontraindikacija
1142
+ * Vraća referencu na Firestore kolekciju podkategorija za određenu kategoriju
1143
+ * @param categoryId - ID roditeljske kategorije
1108
1144
  */
1109
- async getContraindications(technologyId) {
1110
- const technology = await this.getById(technologyId);
1111
- return (technology == null ? void 0 : technology.contraindications) || [];
1145
+ getSubcategoriesRef(categoryId) {
1146
+ return collection8(
1147
+ this.db,
1148
+ CATEGORIES_COLLECTION,
1149
+ categoryId,
1150
+ SUBCATEGORIES_COLLECTION
1151
+ );
1112
1152
  }
1113
1153
  /**
1114
- * Vraća sve benefite za tehnologiju
1115
- * @param technologyId - ID tehnologije
1116
- * @returns Lista benefita
1154
+ * Kreira novu podkategoriju u okviru kategorije
1155
+ * @param categoryId - ID kategorije kojoj će pripadati nova podkategorija
1156
+ * @param subcategory - Podaci za novu podkategoriju
1157
+ * @returns Kreirana podkategorija sa generisanim ID-em
1117
1158
  */
1118
- async getBenefits(technologyId) {
1119
- const technology = await this.getById(technologyId);
1120
- return (technology == null ? void 0 : technology.benefits) || [];
1159
+ async create(categoryId, subcategory) {
1160
+ const now = /* @__PURE__ */ new Date();
1161
+ const newSubcategory = {
1162
+ ...subcategory,
1163
+ categoryId,
1164
+ createdAt: now,
1165
+ updatedAt: now,
1166
+ isActive: true
1167
+ };
1168
+ const docRef = await addDoc5(
1169
+ this.getSubcategoriesRef(categoryId),
1170
+ newSubcategory
1171
+ );
1172
+ return { id: docRef.id, ...newSubcategory };
1121
1173
  }
1122
1174
  /**
1123
- * Ažurira zahteve sertifikacije za tehnologiju
1124
- * @param technologyId - ID tehnologije
1125
- * @param certificationRequirement - Novi zahtevi sertifikacije
1126
- * @returns Ažurirana tehnologija
1175
+ * Vraća sve aktivne podkategorije za određenu kategoriju
1176
+ * @param categoryId - ID kategorije čije podkategorije tražimo
1177
+ * @returns Lista aktivnih podkategorija
1127
1178
  */
1128
- async updateCertificationRequirement(technologyId, certificationRequirement) {
1129
- const docRef = doc3(this.getTechnologiesRef(), technologyId);
1130
- await updateDoc3(docRef, {
1131
- certificationRequirement,
1132
- updatedAt: /* @__PURE__ */ new Date()
1133
- });
1134
- return this.getById(technologyId);
1179
+ async getAllByCategoryId(categoryId) {
1180
+ const q = query8(
1181
+ this.getSubcategoriesRef(categoryId),
1182
+ where8("isActive", "==", true)
1183
+ );
1184
+ const snapshot = await getDocs8(q);
1185
+ return snapshot.docs.map(
1186
+ (doc10) => ({
1187
+ id: doc10.id,
1188
+ ...doc10.data()
1189
+ })
1190
+ );
1135
1191
  }
1136
1192
  /**
1137
- * Vraća zahteve sertifikacije za tehnologiju
1138
- * @param technologyId - ID tehnologije
1139
- * @returns Zahtevi sertifikacije ili null ako tehnologija ne postoji
1193
+ * Ažurira postojeću podkategoriju
1194
+ * @param categoryId - ID kategorije kojoj pripada podkategorija
1195
+ * @param subcategoryId - ID podkategorije koja se ažurira
1196
+ * @param subcategory - Novi podaci za podkategoriju
1197
+ * @returns Ažurirana podkategorija
1140
1198
  */
1141
- async getCertificationRequirement(technologyId) {
1142
- const technology = await this.getById(technologyId);
1143
- return (technology == null ? void 0 : technology.certificationRequirement) || null;
1199
+ async update(categoryId, subcategoryId, subcategory) {
1200
+ const updateData = {
1201
+ ...subcategory,
1202
+ updatedAt: /* @__PURE__ */ new Date()
1203
+ };
1204
+ const docRef = doc8(this.getSubcategoriesRef(categoryId), subcategoryId);
1205
+ await updateDoc8(docRef, updateData);
1206
+ return this.getById(categoryId, subcategoryId);
1144
1207
  }
1145
1208
  /**
1146
- * Proverava da li doktor ima odgovarajuću sertifikaciju za izvođenje tehnologije
1147
- *
1148
- * @param requiredCertification - Zahtevana sertifikacija za tehnologiju
1149
- * @param practitionerCertification - Sertifikacija zdravstvenog radnika
1150
- * @returns true ako zdravstveni radnik ima odgovarajuću sertifikaciju, false ako nema
1151
- *
1152
- * @example
1153
- * const isValid = technologyService.validateCertification(
1154
- * {
1155
- * minimumLevel: CertificationLevel.DOCTOR,
1156
- * requiredSpecialties: [CertificationSpecialty.INJECTABLES]
1157
- * },
1158
- * {
1159
- * level: CertificationLevel.SPECIALIST,
1160
- * specialties: [CertificationSpecialty.INJECTABLES, CertificationSpecialty.LASER]
1161
- * }
1162
- * );
1209
+ * Soft delete podkategorije (postavlja isActive na false)
1210
+ * @param categoryId - ID kategorije kojoj pripada podkategorija
1211
+ * @param subcategoryId - ID podkategorije koja se briše
1163
1212
  */
1164
- validateCertification(requiredCertification, practitionerCertification) {
1165
- const doctorLevel = Object.values(CertificationLevel).indexOf(
1166
- practitionerCertification.level
1167
- );
1168
- const requiredLevel = Object.values(CertificationLevel).indexOf(
1169
- requiredCertification.minimumLevel
1170
- );
1171
- if (doctorLevel < requiredLevel) return false;
1172
- const requiredSpecialties = requiredCertification.requiredSpecialties || [];
1173
- if (requiredSpecialties.length > 0) {
1174
- const doctorSpecialties = practitionerCertification.specialties;
1175
- const hasAllRequiredSpecialties = requiredSpecialties.every(
1176
- (requiredSpecialty) => doctorSpecialties.includes(requiredSpecialty)
1177
- );
1178
- if (!hasAllRequiredSpecialties) return false;
1179
- }
1180
- return true;
1213
+ async delete(categoryId, subcategoryId) {
1214
+ await this.update(categoryId, subcategoryId, { isActive: false });
1181
1215
  }
1182
1216
  /**
1183
- * Vraća sve tehnologije koje je zdravstveni radnik sertifikovan da izvodi
1184
- * zajedno sa listama dozvoljenih familija, kategorija i podkategorija
1185
- *
1186
- * @param practitioner - Profil zdravstvenog radnika
1187
- * @returns Objekat koji sadrži:
1188
- * - technologies: Lista tehnologija koje zdravstveni radnik može da izvodi
1189
- * - families: Lista familija procedura koje zdravstveni radnik može da izvodi
1190
- * - categories: Lista ID-eva kategorija koje zdravstveni radnik može da izvodi
1191
- * - subcategories: Lista ID-eva podkategorija koje zdravstveni radnik može da izvodi
1192
- *
1193
- * @example
1194
- * const practitioner = {
1195
- * certification: {
1196
- * level: CertificationLevel.DOCTOR,
1197
- * specialties: [CertificationSpecialty.INJECTABLES]
1198
- * }
1199
- * };
1200
- * const allowedTechnologies = await technologyService.getAllowedTechnologies(practitioner);
1201
- * console.log(allowedTechnologies.families); // [ProcedureFamily.AESTHETICS]
1202
- * console.log(allowedTechnologies.categories); // ["category1", "category2"]
1203
- * console.log(allowedTechnologies.subcategories); // ["subcategory1", "subcategory2"]
1217
+ * Vraća podkategoriju po ID-u
1218
+ * @param categoryId - ID kategorije kojoj pripada podkategorija
1219
+ * @param subcategoryId - ID tražene podkategorije
1220
+ * @returns Podkategorija ili null ako ne postoji
1204
1221
  */
1205
- async getAllowedTechnologies(practitioner) {
1206
- const allTechnologies = await this.getAll();
1207
- const allowedTechnologies = allTechnologies.filter(
1208
- (technology) => this.validateCertification(
1209
- technology.certificationRequirement,
1210
- practitioner.certification
1211
- )
1212
- );
1213
- const families = [...new Set(allowedTechnologies.map((t) => t.family))];
1214
- const categories = [
1215
- ...new Set(allowedTechnologies.map((t) => t.categoryId))
1216
- ];
1217
- const subcategories = [
1218
- ...new Set(allowedTechnologies.map((t) => t.subcategoryId))
1219
- ];
1222
+ async getById(categoryId, subcategoryId) {
1223
+ const docRef = doc8(this.getSubcategoriesRef(categoryId), subcategoryId);
1224
+ const docSnap = await getDoc8(docRef);
1225
+ if (!docSnap.exists()) return null;
1220
1226
  return {
1221
- technologies: allowedTechnologies,
1222
- families,
1223
- categories,
1224
- subcategories
1227
+ id: docSnap.id,
1228
+ ...docSnap.data()
1225
1229
  };
1226
1230
  }
1227
1231
  };
1228
1232
 
1229
- // src/backoffice/services/requirement.service.ts
1233
+ // src/backoffice/services/technology.service.ts
1230
1234
  import {
1231
- addDoc as addDoc4,
1232
- collection as collection4,
1233
- doc as doc4,
1234
- getDoc as getDoc4,
1235
- getDocs as getDocs4,
1236
- query as query4,
1237
- updateDoc as updateDoc4,
1238
- where as where4
1235
+ addDoc as addDoc6,
1236
+ collection as collection9,
1237
+ doc as doc9,
1238
+ getDoc as getDoc9,
1239
+ getDocs as getDocs9,
1240
+ query as query9,
1241
+ updateDoc as updateDoc9,
1242
+ where as where9,
1243
+ arrayUnion,
1244
+ arrayRemove
1239
1245
  } from "firebase/firestore";
1240
- var RequirementService = class extends BaseService {
1246
+
1247
+ // src/backoffice/types/static/certification.types.ts
1248
+ var CertificationLevel = /* @__PURE__ */ ((CertificationLevel2) => {
1249
+ CertificationLevel2["AESTHETICIAN"] = "aesthetician";
1250
+ CertificationLevel2["NURSE_ASSISTANT"] = "nurse_assistant";
1251
+ CertificationLevel2["NURSE"] = "nurse";
1252
+ CertificationLevel2["NURSE_PRACTITIONER"] = "nurse_practitioner";
1253
+ CertificationLevel2["PHYSICIAN_ASSISTANT"] = "physician_assistant";
1254
+ CertificationLevel2["DOCTOR"] = "doctor";
1255
+ CertificationLevel2["SPECIALIST"] = "specialist";
1256
+ CertificationLevel2["PLASTIC_SURGEON"] = "plastic_surgeon";
1257
+ return CertificationLevel2;
1258
+ })(CertificationLevel || {});
1259
+ var CertificationSpecialty = /* @__PURE__ */ ((CertificationSpecialty3) => {
1260
+ CertificationSpecialty3["LASER"] = "laser";
1261
+ CertificationSpecialty3["INJECTABLES"] = "injectables";
1262
+ CertificationSpecialty3["CHEMICAL_PEELS"] = "chemical_peels";
1263
+ CertificationSpecialty3["MICRODERMABRASION"] = "microdermabrasion";
1264
+ CertificationSpecialty3["BODY_CONTOURING"] = "body_contouring";
1265
+ CertificationSpecialty3["SKIN_CARE"] = "skin_care";
1266
+ CertificationSpecialty3["WOUND_CARE"] = "wound_care";
1267
+ CertificationSpecialty3["ANESTHESIA"] = "anesthesia";
1268
+ return CertificationSpecialty3;
1269
+ })(CertificationSpecialty || {});
1270
+
1271
+ // src/backoffice/services/technology.service.ts
1272
+ var DEFAULT_CERTIFICATION_REQUIREMENT = {
1273
+ minimumLevel: "aesthetician" /* AESTHETICIAN */,
1274
+ requiredSpecialties: []
1275
+ };
1276
+ var TechnologyService = class extends BaseService {
1241
1277
  /**
1242
- * Referenca na Firestore kolekciju zahteva
1278
+ * Vraća referencu na Firestore kolekciju tehnologija
1243
1279
  */
1244
- get requirementsRef() {
1245
- return collection4(this.db, REQUIREMENTS_COLLECTION);
1280
+ getTechnologiesRef() {
1281
+ return collection9(this.db, TECHNOLOGIES_COLLECTION);
1246
1282
  }
1247
1283
  /**
1248
- * Kreira novi globalni zahtev
1249
- * @param requirement - Podaci za novi zahtev
1250
- * @returns Kreirani zahtev sa generisanim ID-em
1284
+ * Kreira novu tehnologiju
1285
+ * @param technology - Podaci za novu tehnologiju
1286
+ * @returns Kreirana tehnologija sa generisanim ID-em
1251
1287
  */
1252
- async create(requirement) {
1288
+ async create(technology) {
1253
1289
  const now = /* @__PURE__ */ new Date();
1254
- const newRequirement = {
1255
- ...requirement,
1290
+ const newTechnology = {
1291
+ ...technology,
1256
1292
  createdAt: now,
1257
1293
  updatedAt: now,
1258
- isActive: true
1294
+ isActive: true,
1295
+ requirements: technology.requirements || {
1296
+ pre: [],
1297
+ post: []
1298
+ },
1299
+ blockingConditions: technology.blockingConditions || [],
1300
+ contraindications: technology.contraindications || [],
1301
+ benefits: technology.benefits || [],
1302
+ certificationRequirement: technology.certificationRequirement || DEFAULT_CERTIFICATION_REQUIREMENT
1259
1303
  };
1260
- const docRef = await addDoc4(this.requirementsRef, newRequirement);
1261
- return { id: docRef.id, ...newRequirement };
1304
+ const docRef = await addDoc6(this.getTechnologiesRef(), newTechnology);
1305
+ return { id: docRef.id, ...newTechnology };
1262
1306
  }
1263
1307
  /**
1264
- * Vraća sve aktivne zahteve
1265
- * @returns Lista aktivnih zahteva
1308
+ * Vraća sve aktivne tehnologije
1309
+ * @returns Lista aktivnih tehnologija
1310
+ */
1311
+ async getAll() {
1312
+ const q = query9(this.getTechnologiesRef(), where9("isActive", "==", true));
1313
+ const snapshot = await getDocs9(q);
1314
+ return snapshot.docs.map(
1315
+ (doc10) => ({
1316
+ id: doc10.id,
1317
+ ...doc10.data()
1318
+ })
1319
+ );
1320
+ }
1321
+ /**
1322
+ * Vraća sve aktivne tehnologije za određenu familiju
1323
+ * @param family - Familija procedura
1324
+ * @returns Lista aktivnih tehnologija
1325
+ */
1326
+ async getAllByFamily(family) {
1327
+ const q = query9(
1328
+ this.getTechnologiesRef(),
1329
+ where9("isActive", "==", true),
1330
+ where9("family", "==", family)
1331
+ );
1332
+ const snapshot = await getDocs9(q);
1333
+ return snapshot.docs.map(
1334
+ (doc10) => ({
1335
+ id: doc10.id,
1336
+ ...doc10.data()
1337
+ })
1338
+ );
1339
+ }
1340
+ /**
1341
+ * Vraća sve aktivne tehnologije za određenu kategoriju
1342
+ * @param categoryId - ID kategorije
1343
+ * @returns Lista aktivnih tehnologija
1266
1344
  */
1267
- async getAll() {
1268
- const q = query4(this.requirementsRef, where4("isActive", "==", true));
1269
- const snapshot = await getDocs4(q);
1345
+ async getAllByCategoryId(categoryId) {
1346
+ const q = query9(
1347
+ this.getTechnologiesRef(),
1348
+ where9("isActive", "==", true),
1349
+ where9("categoryId", "==", categoryId)
1350
+ );
1351
+ const snapshot = await getDocs9(q);
1270
1352
  return snapshot.docs.map(
1271
1353
  (doc10) => ({
1272
1354
  id: doc10.id,
@@ -1275,17 +1357,17 @@ var RequirementService = class extends BaseService {
1275
1357
  );
1276
1358
  }
1277
1359
  /**
1278
- * Vraća sve aktivne zahteve određenog tipa
1279
- * @param type - Tip zahteva (pre/post)
1280
- * @returns Lista zahteva određenog tipa
1360
+ * Vraća sve aktivne tehnologije za određenu podkategoriju
1361
+ * @param subcategoryId - ID podkategorije
1362
+ * @returns Lista aktivnih tehnologija
1281
1363
  */
1282
- async getAllByType(type) {
1283
- const q = query4(
1284
- this.requirementsRef,
1285
- where4("type", "==", type),
1286
- where4("isActive", "==", true)
1364
+ async getAllBySubcategoryId(subcategoryId) {
1365
+ const q = query9(
1366
+ this.getTechnologiesRef(),
1367
+ where9("isActive", "==", true),
1368
+ where9("subcategoryId", "==", subcategoryId)
1287
1369
  );
1288
- const snapshot = await getDocs4(q);
1370
+ const snapshot = await getDocs9(q);
1289
1371
  return snapshot.docs.map(
1290
1372
  (doc10) => ({
1291
1373
  id: doc10.id,
@@ -1294,663 +1376,591 @@ var RequirementService = class extends BaseService {
1294
1376
  );
1295
1377
  }
1296
1378
  /**
1297
- * Ažurira postojeći zahtev
1298
- * @param id - ID zahteva koji se ažurira
1299
- * @param requirement - Novi podaci za zahtev
1300
- * @returns Ažurirani zahtev
1379
+ * Ažurira postojeću tehnologiju
1380
+ * @param technologyId - ID tehnologije
1381
+ * @param technology - Novi podaci za tehnologiju
1382
+ * @returns Ažurirana tehnologija
1301
1383
  */
1302
- async update(id, requirement) {
1384
+ async update(technologyId, technology) {
1303
1385
  const updateData = {
1304
- ...requirement,
1386
+ ...technology,
1305
1387
  updatedAt: /* @__PURE__ */ new Date()
1306
1388
  };
1307
- const docRef = doc4(this.requirementsRef, id);
1308
- await updateDoc4(docRef, updateData);
1309
- return this.getById(id);
1389
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1390
+ await updateDoc9(docRef, updateData);
1391
+ return this.getById(technologyId);
1310
1392
  }
1311
1393
  /**
1312
- * Soft delete zahteva (postavlja isActive na false)
1313
- * @param id - ID zahteva koji se briše
1394
+ * Soft delete tehnologije (postavlja isActive na false)
1395
+ * @param technologyId - ID tehnologije koja se briše
1314
1396
  */
1315
- async delete(id) {
1316
- await this.update(id, { isActive: false });
1397
+ async delete(technologyId) {
1398
+ await this.update(technologyId, {
1399
+ isActive: false
1400
+ });
1317
1401
  }
1318
1402
  /**
1319
- * Vraća zahtev po ID-u
1320
- * @param id - ID traženog zahteva
1321
- * @returns Zahtev ili null ako ne postoji
1403
+ * Vraća tehnologiju po ID-u
1404
+ * @param technologyId - ID tražene tehnologije
1405
+ * @returns Tehnologija ili null ako ne postoji
1322
1406
  */
1323
- async getById(id) {
1324
- const docRef = doc4(this.requirementsRef, id);
1325
- const docSnap = await getDoc4(docRef);
1407
+ async getById(technologyId) {
1408
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1409
+ const docSnap = await getDoc9(docRef);
1326
1410
  if (!docSnap.exists()) return null;
1327
1411
  return {
1328
1412
  id: docSnap.id,
1329
1413
  ...docSnap.data()
1330
1414
  };
1331
1415
  }
1332
- };
1333
-
1334
- // src/backoffice/services/brand.service.ts
1335
- import {
1336
- addDoc as addDoc5,
1337
- collection as collection5,
1338
- doc as doc5,
1339
- getDoc as getDoc5,
1340
- getDocs as getDocs5,
1341
- query as query5,
1342
- updateDoc as updateDoc5,
1343
- where as where5
1344
- } from "firebase/firestore";
1345
- var BrandService = class extends BaseService {
1346
1416
  /**
1347
- * Gets reference to brands collection
1417
+ * Dodaje novi zahtev tehnologiji
1418
+ * @param technologyId - ID tehnologije
1419
+ * @param requirement - Zahtev koji se dodaje
1420
+ * @returns Ažurirana tehnologija sa novim zahtevom
1348
1421
  */
1349
- getBrandsRef() {
1350
- return collection5(this.db, BRANDS_COLLECTION);
1422
+ async addRequirement(technologyId, requirement) {
1423
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1424
+ const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
1425
+ await updateDoc9(docRef, {
1426
+ [requirementType]: arrayUnion(requirement),
1427
+ updatedAt: /* @__PURE__ */ new Date()
1428
+ });
1429
+ return this.getById(technologyId);
1351
1430
  }
1352
1431
  /**
1353
- * Creates a new brand
1432
+ * Uklanja zahtev iz tehnologije
1433
+ * @param technologyId - ID tehnologije
1434
+ * @param requirement - Zahtev koji se uklanja
1435
+ * @returns Ažurirana tehnologija bez uklonjenog zahteva
1354
1436
  */
1355
- async create(brand) {
1356
- const now = /* @__PURE__ */ new Date();
1357
- const newBrand = {
1358
- ...brand,
1359
- createdAt: now,
1360
- updatedAt: now,
1361
- isActive: true
1362
- };
1363
- const docRef = await addDoc5(this.getBrandsRef(), newBrand);
1364
- return { id: docRef.id, ...newBrand };
1437
+ async removeRequirement(technologyId, requirement) {
1438
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1439
+ const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
1440
+ await updateDoc9(docRef, {
1441
+ [requirementType]: arrayRemove(requirement),
1442
+ updatedAt: /* @__PURE__ */ new Date()
1443
+ });
1444
+ return this.getById(technologyId);
1365
1445
  }
1366
1446
  /**
1367
- * Gets all active brands
1447
+ * Vraća sve zahteve za tehnologiju
1448
+ * @param technologyId - ID tehnologije
1449
+ * @param type - Opcioni filter za tip zahteva (pre/post)
1450
+ * @returns Lista zahteva
1368
1451
  */
1369
- async getAll() {
1370
- const q = query5(this.getBrandsRef(), where5("isActive", "==", true));
1371
- const snapshot = await getDocs5(q);
1372
- return snapshot.docs.map(
1373
- (doc10) => ({
1374
- id: doc10.id,
1375
- ...doc10.data()
1376
- })
1377
- );
1452
+ async getRequirements(technologyId, type) {
1453
+ const technology = await this.getById(technologyId);
1454
+ if (!technology || !technology.requirements) return [];
1455
+ if (type) {
1456
+ return technology.requirements[type];
1457
+ }
1458
+ return [...technology.requirements.pre, ...technology.requirements.post];
1378
1459
  }
1379
1460
  /**
1380
- * Updates a brand
1461
+ * Ažurira postojeći zahtev
1462
+ * @param technologyId - ID tehnologije
1463
+ * @param oldRequirement - Stari zahtev koji se menja
1464
+ * @param newRequirement - Novi zahtev koji zamenjuje stari
1465
+ * @returns Ažurirana tehnologija
1381
1466
  */
1382
- async update(brandId, brand) {
1383
- const updateData = {
1384
- ...brand,
1467
+ async updateRequirement(technologyId, oldRequirement, newRequirement) {
1468
+ await this.removeRequirement(technologyId, oldRequirement);
1469
+ return this.addRequirement(technologyId, newRequirement);
1470
+ }
1471
+ /**
1472
+ * Dodaje blokirajući uslov tehnologiji
1473
+ * @param technologyId - ID tehnologije
1474
+ * @param condition - Blokirajući uslov koji se dodaje
1475
+ * @returns Ažurirana tehnologija
1476
+ */
1477
+ async addBlockingCondition(technologyId, condition) {
1478
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1479
+ await updateDoc9(docRef, {
1480
+ blockingConditions: arrayUnion(condition),
1385
1481
  updatedAt: /* @__PURE__ */ new Date()
1386
- };
1387
- const docRef = doc5(this.getBrandsRef(), brandId);
1388
- await updateDoc5(docRef, updateData);
1389
- return this.getById(brandId);
1482
+ });
1483
+ return this.getById(technologyId);
1390
1484
  }
1391
1485
  /**
1392
- * Soft deletes a brand
1486
+ * Uklanja blokirajući uslov iz tehnologije
1487
+ * @param technologyId - ID tehnologije
1488
+ * @param condition - Blokirajući uslov koji se uklanja
1489
+ * @returns Ažurirana tehnologija
1393
1490
  */
1394
- async delete(brandId) {
1395
- await this.update(brandId, {
1396
- isActive: false
1491
+ async removeBlockingCondition(technologyId, condition) {
1492
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1493
+ await updateDoc9(docRef, {
1494
+ blockingConditions: arrayRemove(condition),
1495
+ updatedAt: /* @__PURE__ */ new Date()
1397
1496
  });
1497
+ return this.getById(technologyId);
1398
1498
  }
1399
1499
  /**
1400
- * Gets a brand by ID
1500
+ * Dodaje kontraindikaciju tehnologiji
1501
+ * @param technologyId - ID tehnologije
1502
+ * @param contraindication - Kontraindikacija koja se dodaje
1503
+ * @returns Ažurirana tehnologija
1401
1504
  */
1402
- async getById(brandId) {
1403
- const docRef = doc5(this.getBrandsRef(), brandId);
1404
- const docSnap = await getDoc5(docRef);
1405
- if (!docSnap.exists()) return null;
1406
- return {
1407
- id: docSnap.id,
1408
- ...docSnap.data()
1409
- };
1505
+ async addContraindication(technologyId, contraindication) {
1506
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1507
+ await updateDoc9(docRef, {
1508
+ contraindications: arrayUnion(contraindication),
1509
+ updatedAt: /* @__PURE__ */ new Date()
1510
+ });
1511
+ return this.getById(technologyId);
1410
1512
  }
1411
- };
1412
-
1413
- // src/backoffice/services/product.service.ts
1414
- import {
1415
- addDoc as addDoc6,
1416
- collection as collection6,
1417
- doc as doc6,
1418
- getDoc as getDoc6,
1419
- getDocs as getDocs6,
1420
- query as query6,
1421
- updateDoc as updateDoc6,
1422
- where as where6
1423
- } from "firebase/firestore";
1424
- var ProductService = class extends BaseService {
1425
1513
  /**
1426
- * Gets reference to products collection under a technology
1427
- * @param technologyId - ID of the technology
1428
- * @returns Firestore collection reference
1514
+ * Uklanja kontraindikaciju iz tehnologije
1515
+ * @param technologyId - ID tehnologije
1516
+ * @param contraindication - Kontraindikacija koja se uklanja
1517
+ * @returns Ažurirana tehnologija
1429
1518
  */
1430
- getProductsRef(technologyId) {
1431
- return collection6(
1432
- this.db,
1433
- TECHNOLOGIES_COLLECTION,
1434
- technologyId,
1435
- PRODUCTS_COLLECTION
1436
- );
1519
+ async removeContraindication(technologyId, contraindication) {
1520
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1521
+ await updateDoc9(docRef, {
1522
+ contraindications: arrayRemove(contraindication),
1523
+ updatedAt: /* @__PURE__ */ new Date()
1524
+ });
1525
+ return this.getById(technologyId);
1437
1526
  }
1438
1527
  /**
1439
- * Creates a new product under technology
1528
+ * Dodaje benefit tehnologiji
1529
+ * @param technologyId - ID tehnologije
1530
+ * @param benefit - Benefit koji se dodaje
1531
+ * @returns Ažurirana tehnologija
1440
1532
  */
1441
- async create(technologyId, brandId, product) {
1442
- const now = /* @__PURE__ */ new Date();
1443
- const newProduct = {
1444
- ...product,
1445
- brandId,
1446
- technologyId,
1447
- createdAt: now,
1448
- updatedAt: now,
1449
- isActive: true
1450
- };
1451
- const productRef = await addDoc6(
1452
- this.getProductsRef(technologyId),
1453
- newProduct
1454
- );
1455
- return { id: productRef.id, ...newProduct };
1533
+ async addBenefit(technologyId, benefit) {
1534
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1535
+ await updateDoc9(docRef, {
1536
+ benefits: arrayUnion(benefit),
1537
+ updatedAt: /* @__PURE__ */ new Date()
1538
+ });
1539
+ return this.getById(technologyId);
1456
1540
  }
1457
1541
  /**
1458
- * Gets all products for a technology
1542
+ * Uklanja benefit iz tehnologije
1543
+ * @param technologyId - ID tehnologije
1544
+ * @param benefit - Benefit koji se uklanja
1545
+ * @returns Ažurirana tehnologija
1459
1546
  */
1460
- async getAllByTechnology(technologyId) {
1461
- const q = query6(
1462
- this.getProductsRef(technologyId),
1463
- where6("isActive", "==", true)
1464
- );
1465
- const snapshot = await getDocs6(q);
1466
- return snapshot.docs.map(
1467
- (doc10) => ({
1468
- id: doc10.id,
1469
- ...doc10.data()
1470
- })
1471
- );
1547
+ async removeBenefit(technologyId, benefit) {
1548
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1549
+ await updateDoc9(docRef, {
1550
+ benefits: arrayRemove(benefit),
1551
+ updatedAt: /* @__PURE__ */ new Date()
1552
+ });
1553
+ return this.getById(technologyId);
1554
+ }
1555
+ /**
1556
+ * Vraća sve blokirajuće uslove za tehnologiju
1557
+ * @param technologyId - ID tehnologije
1558
+ * @returns Lista blokirajućih uslova
1559
+ */
1560
+ async getBlockingConditions(technologyId) {
1561
+ const technology = await this.getById(technologyId);
1562
+ return (technology == null ? void 0 : technology.blockingConditions) || [];
1563
+ }
1564
+ /**
1565
+ * Vraća sve kontraindikacije za tehnologiju
1566
+ * @param technologyId - ID tehnologije
1567
+ * @returns Lista kontraindikacija
1568
+ */
1569
+ async getContraindications(technologyId) {
1570
+ const technology = await this.getById(technologyId);
1571
+ return (technology == null ? void 0 : technology.contraindications) || [];
1472
1572
  }
1473
1573
  /**
1474
- * Gets all products for a brand by filtering through all technologies
1574
+ * Vraća sve benefite za tehnologiju
1575
+ * @param technologyId - ID tehnologije
1576
+ * @returns Lista benefita
1475
1577
  */
1476
- async getAllByBrand(brandId) {
1477
- const allTechnologiesRef = collection6(this.db, TECHNOLOGIES_COLLECTION);
1478
- const technologiesSnapshot = await getDocs6(allTechnologiesRef);
1479
- const products = [];
1480
- for (const techDoc of technologiesSnapshot.docs) {
1481
- const q = query6(
1482
- this.getProductsRef(techDoc.id),
1483
- where6("brandId", "==", brandId),
1484
- where6("isActive", "==", true)
1485
- );
1486
- const snapshot = await getDocs6(q);
1487
- products.push(
1488
- ...snapshot.docs.map(
1489
- (doc10) => ({
1490
- id: doc10.id,
1491
- ...doc10.data()
1492
- })
1493
- )
1494
- );
1495
- }
1496
- return products;
1578
+ async getBenefits(technologyId) {
1579
+ const technology = await this.getById(technologyId);
1580
+ return (technology == null ? void 0 : technology.benefits) || [];
1497
1581
  }
1498
1582
  /**
1499
- * Updates a product
1583
+ * Ažurira zahteve sertifikacije za tehnologiju
1584
+ * @param technologyId - ID tehnologije
1585
+ * @param certificationRequirement - Novi zahtevi sertifikacije
1586
+ * @returns Ažurirana tehnologija
1500
1587
  */
1501
- async update(technologyId, productId, product) {
1502
- const updateData = {
1503
- ...product,
1588
+ async updateCertificationRequirement(technologyId, certificationRequirement) {
1589
+ const docRef = doc9(this.getTechnologiesRef(), technologyId);
1590
+ await updateDoc9(docRef, {
1591
+ certificationRequirement,
1504
1592
  updatedAt: /* @__PURE__ */ new Date()
1505
- };
1506
- const docRef = doc6(this.getProductsRef(technologyId), productId);
1507
- await updateDoc6(docRef, updateData);
1508
- return this.getById(technologyId, productId);
1593
+ });
1594
+ return this.getById(technologyId);
1509
1595
  }
1510
1596
  /**
1511
- * Soft deletes a product
1597
+ * Vraća zahteve sertifikacije za tehnologiju
1598
+ * @param technologyId - ID tehnologije
1599
+ * @returns Zahtevi sertifikacije ili null ako tehnologija ne postoji
1512
1600
  */
1513
- async delete(technologyId, productId) {
1514
- await this.update(technologyId, productId, {
1515
- isActive: false
1516
- });
1601
+ async getCertificationRequirement(technologyId) {
1602
+ const technology = await this.getById(technologyId);
1603
+ return (technology == null ? void 0 : technology.certificationRequirement) || null;
1517
1604
  }
1518
1605
  /**
1519
- * Gets a product by ID
1606
+ * Proverava da li doktor ima odgovarajuću sertifikaciju za izvođenje tehnologije
1607
+ *
1608
+ * @param requiredCertification - Zahtevana sertifikacija za tehnologiju
1609
+ * @param practitionerCertification - Sertifikacija zdravstvenog radnika
1610
+ * @returns true ako zdravstveni radnik ima odgovarajuću sertifikaciju, false ako nema
1611
+ *
1612
+ * @example
1613
+ * const isValid = technologyService.validateCertification(
1614
+ * {
1615
+ * minimumLevel: CertificationLevel.DOCTOR,
1616
+ * requiredSpecialties: [CertificationSpecialty.INJECTABLES]
1617
+ * },
1618
+ * {
1619
+ * level: CertificationLevel.SPECIALIST,
1620
+ * specialties: [CertificationSpecialty.INJECTABLES, CertificationSpecialty.LASER]
1621
+ * }
1622
+ * );
1520
1623
  */
1521
- async getById(technologyId, productId) {
1522
- const docRef = doc6(this.getProductsRef(technologyId), productId);
1523
- const docSnap = await getDoc6(docRef);
1524
- if (!docSnap.exists()) return null;
1624
+ validateCertification(requiredCertification, practitionerCertification) {
1625
+ const doctorLevel = Object.values(CertificationLevel).indexOf(
1626
+ practitionerCertification.level
1627
+ );
1628
+ const requiredLevel = Object.values(CertificationLevel).indexOf(
1629
+ requiredCertification.minimumLevel
1630
+ );
1631
+ if (doctorLevel < requiredLevel) return false;
1632
+ const requiredSpecialties = requiredCertification.requiredSpecialties || [];
1633
+ if (requiredSpecialties.length > 0) {
1634
+ const doctorSpecialties = practitionerCertification.specialties;
1635
+ const hasAllRequiredSpecialties = requiredSpecialties.every(
1636
+ (requiredSpecialty) => doctorSpecialties.includes(requiredSpecialty)
1637
+ );
1638
+ if (!hasAllRequiredSpecialties) return false;
1639
+ }
1640
+ return true;
1641
+ }
1642
+ /**
1643
+ * Vraća sve tehnologije koje je zdravstveni radnik sertifikovan da izvodi
1644
+ * zajedno sa listama dozvoljenih familija, kategorija i podkategorija
1645
+ *
1646
+ * @param practitioner - Profil zdravstvenog radnika
1647
+ * @returns Objekat koji sadrži:
1648
+ * - technologies: Lista tehnologija koje zdravstveni radnik može da izvodi
1649
+ * - families: Lista familija procedura koje zdravstveni radnik može da izvodi
1650
+ * - categories: Lista ID-eva kategorija koje zdravstveni radnik može da izvodi
1651
+ * - subcategories: Lista ID-eva podkategorija koje zdravstveni radnik može da izvodi
1652
+ *
1653
+ * @example
1654
+ * const practitioner = {
1655
+ * certification: {
1656
+ * level: CertificationLevel.DOCTOR,
1657
+ * specialties: [CertificationSpecialty.INJECTABLES]
1658
+ * }
1659
+ * };
1660
+ * const allowedTechnologies = await technologyService.getAllowedTechnologies(practitioner);
1661
+ * console.log(allowedTechnologies.families); // [ProcedureFamily.AESTHETICS]
1662
+ * console.log(allowedTechnologies.categories); // ["category1", "category2"]
1663
+ * console.log(allowedTechnologies.subcategories); // ["subcategory1", "subcategory2"]
1664
+ */
1665
+ async getAllowedTechnologies(practitioner) {
1666
+ const allTechnologies = await this.getAll();
1667
+ const allowedTechnologies = allTechnologies.filter(
1668
+ (technology) => this.validateCertification(
1669
+ technology.certificationRequirement,
1670
+ practitioner.certification
1671
+ )
1672
+ );
1673
+ const families = [...new Set(allowedTechnologies.map((t) => t.family))];
1674
+ const categories = [
1675
+ ...new Set(allowedTechnologies.map((t) => t.categoryId))
1676
+ ];
1677
+ const subcategories = [
1678
+ ...new Set(allowedTechnologies.map((t) => t.subcategoryId))
1679
+ ];
1525
1680
  return {
1526
- id: docSnap.id,
1527
- ...docSnap.data()
1681
+ technologies: allowedTechnologies,
1682
+ families,
1683
+ categories,
1684
+ subcategories
1528
1685
  };
1529
1686
  }
1530
1687
  };
1531
1688
 
1532
- // src/services/documentation-templates/documentation-template.service.ts
1533
- import {
1534
- collection as collection7,
1535
- doc as doc7,
1536
- getDoc as getDoc7,
1537
- getDocs as getDocs7,
1538
- setDoc,
1539
- updateDoc as updateDoc7,
1540
- deleteDoc,
1541
- query as query7,
1542
- where as where7,
1543
- orderBy,
1544
- limit,
1545
- startAfter
1546
- } from "firebase/firestore";
1547
- var DocumentationTemplateService = class extends BaseService {
1548
- constructor() {
1549
- super(...arguments);
1550
- this.collectionRef = collection7(
1551
- this.db,
1552
- DOCUMENTATION_TEMPLATES_COLLECTION
1553
- );
1689
+ // src/backoffice/types/static/blocking-condition.types.ts
1690
+ var BlockingCondition = /* @__PURE__ */ ((BlockingCondition2) => {
1691
+ BlockingCondition2["PREGNANCY"] = "pregnancy";
1692
+ BlockingCondition2["BREASTFEEDING"] = "breastfeeding";
1693
+ BlockingCondition2["ACTIVE_INFECTION"] = "active_infection";
1694
+ BlockingCondition2["SKIN_CONDITION"] = "skin_condition";
1695
+ BlockingCondition2["AUTOIMMUNE_DISEASE"] = "autoimmune_disease";
1696
+ BlockingCondition2["BLOOD_THINNERS"] = "blood_thinners";
1697
+ BlockingCondition2["RECENT_SURGERY"] = "recent_surgery";
1698
+ BlockingCondition2["DIABETES"] = "diabetes";
1699
+ BlockingCondition2["HEART_CONDITION"] = "heart_condition";
1700
+ BlockingCondition2["HIGH_BLOOD_PRESSURE"] = "high_blood_pressure";
1701
+ BlockingCondition2["KELOID_SCARRING"] = "keloid_scarring";
1702
+ BlockingCondition2["METAL_IMPLANTS"] = "metal_implants";
1703
+ BlockingCondition2["PACEMAKER"] = "pacemaker";
1704
+ BlockingCondition2["CANCER"] = "cancer";
1705
+ BlockingCondition2["EPILEPSY"] = "epilepsy";
1706
+ return BlockingCondition2;
1707
+ })(BlockingCondition || {});
1708
+
1709
+ // src/backoffice/types/static/contraindication.types.ts
1710
+ var Contraindication = /* @__PURE__ */ ((Contraindication2) => {
1711
+ Contraindication2["SENSITIVE_SKIN"] = "sensitive_skin";
1712
+ Contraindication2["RECENT_TANNING"] = "recent_tanning";
1713
+ Contraindication2["RECENT_BOTOX"] = "recent_botox";
1714
+ Contraindication2["RECENT_FILLERS"] = "recent_fillers";
1715
+ Contraindication2["SKIN_ALLERGIES"] = "skin_allergies";
1716
+ Contraindication2["MEDICATIONS"] = "medications";
1717
+ Contraindication2["RECENT_CHEMICAL_PEEL"] = "recent_chemical_peel";
1718
+ Contraindication2["RECENT_LASER"] = "recent_laser";
1719
+ Contraindication2["SKIN_INFLAMMATION"] = "skin_inflammation";
1720
+ Contraindication2["OPEN_WOUNDS"] = "open_wounds";
1721
+ Contraindication2["HERPES_SIMPLEX"] = "herpes_simplex";
1722
+ Contraindication2["COLD_SORES"] = "cold_sores";
1723
+ return Contraindication2;
1724
+ })(Contraindication || {});
1725
+
1726
+ // src/backoffice/types/static/procedure-family.types.ts
1727
+ var ProcedureFamily = /* @__PURE__ */ ((ProcedureFamily2) => {
1728
+ ProcedureFamily2["AESTHETICS"] = "aesthetics";
1729
+ ProcedureFamily2["SURGERY"] = "surgery";
1730
+ return ProcedureFamily2;
1731
+ })(ProcedureFamily || {});
1732
+
1733
+ // src/backoffice/types/static/treatment-benefit.types.ts
1734
+ var TreatmentBenefit = /* @__PURE__ */ ((TreatmentBenefit2) => {
1735
+ TreatmentBenefit2["WRINKLE_REDUCTION"] = "wrinkle_reduction";
1736
+ TreatmentBenefit2["SKIN_TIGHTENING"] = "skin_tightening";
1737
+ TreatmentBenefit2["COLLAGEN_PRODUCTION"] = "collagen_production";
1738
+ TreatmentBenefit2["ACNE_REDUCTION"] = "acne_reduction";
1739
+ TreatmentBenefit2["SCAR_REDUCTION"] = "scar_reduction";
1740
+ TreatmentBenefit2["PIGMENTATION_IMPROVEMENT"] = "pigmentation_improvement";
1741
+ TreatmentBenefit2["HAIR_REMOVAL"] = "hair_removal";
1742
+ TreatmentBenefit2["MUSCLE_TONING"] = "muscle_toning";
1743
+ TreatmentBenefit2["FAT_REDUCTION"] = "fat_reduction";
1744
+ TreatmentBenefit2["CELLULITE_REDUCTION"] = "cellulite_reduction";
1745
+ TreatmentBenefit2["SKIN_REJUVENATION"] = "skin_rejuvenation";
1746
+ TreatmentBenefit2["PORE_REDUCTION"] = "pore_reduction";
1747
+ TreatmentBenefit2["TEXTURE_IMPROVEMENT"] = "texture_improvement";
1748
+ TreatmentBenefit2["HYDRATION_BOOST"] = "hydration_boost";
1749
+ TreatmentBenefit2["CIRCULATION_IMPROVEMENT"] = "circulation_improvement";
1750
+ return TreatmentBenefit2;
1751
+ })(TreatmentBenefit || {});
1752
+
1753
+ // src/backoffice/validations/schemas.ts
1754
+ import { z as z2 } from "zod";
1755
+ var blockingConditionSchema = z2.nativeEnum(BlockingCondition);
1756
+ var contraindicationSchema = z2.nativeEnum(Contraindication);
1757
+ var treatmentBenefitSchema = z2.nativeEnum(TreatmentBenefit);
1758
+ var procedureFamilySchema = z2.nativeEnum(ProcedureFamily);
1759
+ var timeUnitSchema = z2.nativeEnum(TimeUnit);
1760
+ var requirementTypeSchema = z2.nativeEnum(RequirementType);
1761
+ var certificationLevelSchema = z2.nativeEnum(CertificationLevel);
1762
+ var certificationSpecialtySchema = z2.nativeEnum(
1763
+ CertificationSpecialty
1764
+ );
1765
+ var certificationRequirementSchema = z2.object({
1766
+ minimumLevel: certificationLevelSchema,
1767
+ requiredSpecialties: z2.array(certificationSpecialtySchema).max(5, "Maximum 5 specialties allowed").optional()
1768
+ });
1769
+ var timeframeSchema = z2.object({
1770
+ duration: z2.number().min(1, "Duration must be positive"),
1771
+ unit: timeUnitSchema,
1772
+ notifyAt: z2.array(z2.number()).min(1, "At least one notification point is required")
1773
+ });
1774
+ var requirementSchema = z2.object({
1775
+ name: z2.string().min(1, "Name is required").max(100, "Name is too long"),
1776
+ description: z2.string().min(1, "Description is required"),
1777
+ type: requirementTypeSchema,
1778
+ timeframe: timeframeSchema,
1779
+ importance: z2.enum(["low", "medium", "high"]),
1780
+ isActive: z2.boolean().default(true)
1781
+ });
1782
+ var technologyRequirementsSchema = z2.object({
1783
+ pre: z2.array(requirementSchema),
1784
+ post: z2.array(requirementSchema)
1785
+ });
1786
+ var technologySchema = z2.object({
1787
+ name: z2.string().min(1, "Name is required").max(100, "Name is too long"),
1788
+ description: z2.string().max(1e3, "Description is too long").optional(),
1789
+ technicalDetails: z2.string().max(2e3, "Technical details are too long").optional(),
1790
+ family: procedureFamilySchema,
1791
+ categoryId: z2.string().min(1, "Category ID is required"),
1792
+ subcategoryId: z2.string().min(1, "Subcategory ID is required"),
1793
+ requirements: technologyRequirementsSchema.default({
1794
+ pre: [],
1795
+ post: []
1796
+ }),
1797
+ blockingConditions: z2.array(blockingConditionSchema),
1798
+ contraindications: z2.array(contraindicationSchema),
1799
+ documentationTemplates: z2.array(documentTemplateSchema),
1800
+ benefits: z2.array(treatmentBenefitSchema),
1801
+ certificationRequirement: certificationRequirementSchema,
1802
+ isActive: z2.boolean().default(true)
1803
+ });
1804
+ var categorySchema = z2.object({
1805
+ name: z2.string().min(1, "Name is required").max(100, "Name is too long"),
1806
+ description: z2.string().optional(),
1807
+ family: procedureFamilySchema,
1808
+ isActive: z2.boolean().default(true)
1809
+ });
1810
+ var subcategorySchema = z2.object({
1811
+ name: z2.string().min(1, "Name is required").max(100, "Name is too long"),
1812
+ description: z2.string().optional(),
1813
+ categoryId: z2.string().min(1, "Category ID is required"),
1814
+ isActive: z2.boolean().default(true)
1815
+ });
1816
+ var categoryUpdateSchema = categorySchema.partial();
1817
+ var subcategoryUpdateSchema = subcategorySchema.partial();
1818
+ var technologyUpdateSchema = technologySchema.partial();
1819
+ var requirementUpdateSchema = requirementSchema.partial();
1820
+
1821
+ // src/backoffice/constants/certification.constants.ts
1822
+ var DEFAULT_CERTIFICATION_REQUIREMENT2 = {
1823
+ minimumLevel: "aesthetician" /* AESTHETICIAN */,
1824
+ requiredSpecialties: []
1825
+ };
1826
+
1827
+ // src/backoffice/errors/backoffice.errors.ts
1828
+ var BackofficeError = class extends Error {
1829
+ constructor(message) {
1830
+ super(message);
1831
+ this.name = "BackofficeError";
1554
1832
  }
1555
- /**
1556
- * Create a new document template
1557
- * @param data - Template data
1558
- * @param userId - ID of the user creating the template
1559
- * @returns The created template
1560
- */
1561
- async createTemplate(data, userId) {
1562
- const validatedData = createDocumentTemplateSchema.parse(data);
1563
- const templateId = this.generateId();
1564
- const elementsWithIds = validatedData.elements.map((element) => ({
1565
- ...element,
1566
- id: this.generateId()
1567
- }));
1568
- const now = Date.now();
1569
- const template = {
1570
- id: templateId,
1571
- title: validatedData.title,
1572
- description: validatedData.description,
1573
- elements: elementsWithIds,
1574
- createdAt: now,
1575
- updatedAt: now,
1576
- createdBy: userId,
1577
- version: 1,
1578
- isActive: true,
1579
- tags: validatedData.tags || [],
1580
- isUserForm: validatedData.isUserForm || false,
1581
- isRequired: validatedData.isRequired || false,
1582
- sortingOrder: validatedData.sortingOrder || 0
1583
- };
1584
- const docRef = doc7(this.collectionRef, templateId);
1585
- await setDoc(docRef, template);
1586
- return template;
1833
+ };
1834
+ var CategoryError = class extends BackofficeError {
1835
+ constructor(message) {
1836
+ super(message);
1837
+ this.name = "CategoryError";
1587
1838
  }
1588
- /**
1589
- * Get a document template by ID
1590
- * @param templateId - ID of the template to retrieve
1591
- * @param version - Optional version number to retrieve (defaults to latest version)
1592
- * @returns The template or null if not found
1593
- */
1594
- async getTemplateById(templateId, version) {
1595
- const docRef = doc7(this.collectionRef, templateId);
1596
- const docSnap = await getDoc7(docRef);
1597
- if (!docSnap.exists()) {
1598
- return null;
1599
- }
1600
- const currentTemplate = docSnap.data();
1601
- if (version === void 0) {
1602
- return currentTemplate;
1603
- }
1604
- if (currentTemplate.version === version) {
1605
- return currentTemplate;
1606
- }
1607
- try {
1608
- const versionTemplate = await this.getTemplateVersion(
1609
- templateId,
1610
- version
1611
- );
1612
- if (versionTemplate) {
1613
- return versionTemplate;
1614
- }
1615
- } catch (error) {
1616
- console.error(`Error getting template version ${version}:`, error);
1617
- }
1618
- return null;
1839
+ };
1840
+ var CategoryNotFoundError = class extends CategoryError {
1841
+ constructor(id) {
1842
+ super(`Kategorija sa ID-em ${id} nije prona\u0111ena`);
1843
+ this.name = "CategoryNotFoundError";
1619
1844
  }
1620
- /**
1621
- * Update an existing document template
1622
- * @param templateId - ID of the template to update
1623
- * @param data - Updated template data
1624
- * @returns The updated template
1625
- */
1626
- async updateTemplate(templateId, data) {
1627
- var _a, _b, _c;
1628
- const validatedData = updateDocumentTemplateSchema.parse(data);
1629
- console.log("Validated data", validatedData);
1630
- const template = await this.getTemplateById(templateId);
1631
- if (!template) {
1632
- throw new Error(`Template with ID ${templateId} not found`);
1633
- }
1634
- const versionsCollectionRef = collection7(
1635
- this.db,
1636
- `${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions`
1637
- );
1638
- const versionDocRef = doc7(
1639
- versionsCollectionRef,
1640
- template.version.toString()
1641
- );
1642
- await setDoc(versionDocRef, template);
1643
- let updatedElements = template.elements;
1644
- if (validatedData.elements) {
1645
- updatedElements = validatedData.elements.map((element) => ({
1646
- ...element,
1647
- id: element.id || this.generateId()
1648
- }));
1649
- }
1650
- const updatePayload = {
1651
- elements: updatedElements,
1652
- updatedAt: Date.now(),
1653
- version: template.version + 1
1654
- };
1655
- if (validatedData.title !== void 0)
1656
- updatePayload.title = validatedData.title;
1657
- if (validatedData.description !== void 0)
1658
- updatePayload.description = validatedData.description;
1659
- if (validatedData.isActive !== void 0)
1660
- updatePayload.isActive = validatedData.isActive;
1661
- if (validatedData.tags !== void 0)
1662
- updatePayload.tags = validatedData.tags;
1663
- updatePayload.isUserForm = (_a = validatedData.isUserForm) != null ? _a : false;
1664
- updatePayload.isRequired = (_b = validatedData.isRequired) != null ? _b : false;
1665
- updatePayload.sortingOrder = (_c = validatedData.sortingOrder) != null ? _c : 0;
1666
- const docRef = doc7(this.collectionRef, templateId);
1667
- console.log("Update payload", updatePayload);
1668
- await updateDoc7(docRef, updatePayload);
1669
- return { ...template, ...updatePayload };
1845
+ };
1846
+ var InvalidCategoryDataError = class extends CategoryError {
1847
+ constructor(message) {
1848
+ super(`Neva\u017Ee\u0107i podaci za kategoriju: ${message}`);
1849
+ this.name = "InvalidCategoryDataError";
1670
1850
  }
1671
- /**
1672
- * Get a specific version of a template
1673
- * @param templateId - ID of the template
1674
- * @param versionNumber - Version number to retrieve
1675
- * @returns The template version or null if not found
1676
- */
1677
- async getTemplateVersion(templateId, versionNumber) {
1678
- const versionDocRef = doc7(
1679
- this.db,
1680
- `${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions/${versionNumber}`
1681
- );
1682
- const versionDocSnap = await getDoc7(versionDocRef);
1683
- if (!versionDocSnap.exists()) {
1684
- return null;
1685
- }
1686
- return versionDocSnap.data();
1851
+ };
1852
+ var SubcategoryError = class extends BackofficeError {
1853
+ constructor(message) {
1854
+ super(message);
1855
+ this.name = "SubcategoryError";
1687
1856
  }
1688
- /**
1689
- * Get all versions of a template
1690
- * @param templateId - ID of the template
1691
- * @returns Array of template versions
1692
- */
1693
- async getTemplateOldVersions(templateId) {
1694
- const versionsCollectionRef = collection7(
1695
- this.db,
1696
- `${DOCUMENTATION_TEMPLATES_COLLECTION}/${templateId}/versions`
1697
- );
1698
- const q = query7(versionsCollectionRef, orderBy("version", "desc"));
1699
- const querySnapshot = await getDocs7(q);
1700
- const versions = [];
1701
- querySnapshot.forEach((doc10) => {
1702
- versions.push(doc10.data());
1703
- });
1704
- return versions;
1857
+ };
1858
+ var SubcategoryNotFoundError = class extends SubcategoryError {
1859
+ constructor(id) {
1860
+ super(`Podkategorija sa ID-em ${id} nije prona\u0111ena`);
1861
+ this.name = "SubcategoryNotFoundError";
1705
1862
  }
1706
- /**
1707
- * Delete a document template
1708
- * @param templateId - ID of the template to delete
1709
- */
1710
- async deleteTemplate(templateId) {
1711
- const docRef = doc7(this.collectionRef, templateId);
1712
- await deleteDoc(docRef);
1863
+ };
1864
+ var InvalidSubcategoryDataError = class extends SubcategoryError {
1865
+ constructor(message) {
1866
+ super(`Neva\u017Ee\u0107i podaci za podkategoriju: ${message}`);
1867
+ this.name = "InvalidSubcategoryDataError";
1713
1868
  }
1714
- /**
1715
- * Get all active templates
1716
- * @param pageSize - Number of templates to retrieve
1717
- * @param lastDoc - Last document from previous page for pagination
1718
- * @returns Array of templates and the last document for pagination
1719
- */
1720
- async getActiveTemplates(pageSize = 20, lastDoc) {
1721
- let q = query7(
1722
- this.collectionRef,
1723
- where7("isActive", "==", true),
1724
- orderBy("updatedAt", "desc"),
1725
- limit(pageSize)
1726
- );
1727
- if (lastDoc) {
1728
- q = query7(q, startAfter(lastDoc));
1729
- }
1730
- const querySnapshot = await getDocs7(q);
1731
- const templates = [];
1732
- let lastVisible = null;
1733
- querySnapshot.forEach((doc10) => {
1734
- templates.push(doc10.data());
1735
- lastVisible = doc10;
1736
- });
1737
- return {
1738
- templates,
1739
- lastDoc: lastVisible
1740
- };
1869
+ };
1870
+ var TechnologyError = class extends BackofficeError {
1871
+ constructor(message) {
1872
+ super(message);
1873
+ this.name = "TechnologyError";
1874
+ }
1875
+ };
1876
+ var TechnologyNotFoundError = class extends TechnologyError {
1877
+ constructor(id) {
1878
+ super(`Tehnologija sa ID-em ${id} nije prona\u0111ena`);
1879
+ this.name = "TechnologyNotFoundError";
1741
1880
  }
1742
- /**
1743
- * Get templates by tags
1744
- * @param tags - Tags to filter by
1745
- * @param pageSize - Number of templates to retrieve
1746
- * @param lastDoc - Last document from previous page for pagination
1747
- * @returns Array of templates and the last document for pagination
1748
- */
1749
- async getTemplatesByTags(tags, pageSize = 20, lastDoc) {
1750
- let q = query7(
1751
- this.collectionRef,
1752
- where7("isActive", "==", true),
1753
- where7("tags", "array-contains-any", tags),
1754
- orderBy("updatedAt", "desc"),
1755
- limit(pageSize)
1756
- );
1757
- if (lastDoc) {
1758
- q = query7(q, startAfter(lastDoc));
1759
- }
1760
- const querySnapshot = await getDocs7(q);
1761
- const templates = [];
1762
- let lastVisible = null;
1763
- querySnapshot.forEach((doc10) => {
1764
- templates.push(doc10.data());
1765
- lastVisible = doc10;
1766
- });
1767
- return {
1768
- templates,
1769
- lastDoc: lastVisible
1770
- };
1881
+ };
1882
+ var InvalidTechnologyDataError = class extends TechnologyError {
1883
+ constructor(message) {
1884
+ super(`Neva\u017Ee\u0107i podaci za tehnologiju: ${message}`);
1885
+ this.name = "InvalidTechnologyDataError";
1771
1886
  }
1772
- /**
1773
- * Get templates created by a specific user
1774
- * @param userId - ID of the user who created the templates
1775
- * @param pageSize - Number of templates to retrieve
1776
- * @param lastDoc - Last document from previous page for pagination
1777
- * @returns Array of templates and the last document for pagination
1778
- */
1779
- async getTemplatesByCreator(userId, pageSize = 20, lastDoc) {
1780
- let q = query7(
1781
- this.collectionRef,
1782
- where7("createdBy", "==", userId),
1783
- orderBy("updatedAt", "desc"),
1784
- limit(pageSize)
1785
- );
1786
- if (lastDoc) {
1787
- q = query7(q, startAfter(lastDoc));
1788
- }
1789
- const querySnapshot = await getDocs7(q);
1790
- const templates = [];
1791
- let lastVisible = null;
1792
- querySnapshot.forEach((doc10) => {
1793
- templates.push(doc10.data());
1794
- lastVisible = doc10;
1795
- });
1796
- return {
1797
- templates,
1798
- lastDoc: lastVisible
1799
- };
1887
+ };
1888
+ var RequirementError = class extends BackofficeError {
1889
+ constructor(message) {
1890
+ super(message);
1891
+ this.name = "RequirementError";
1800
1892
  }
1801
- /**
1802
- * Get all templates for selection with optional filtering
1803
- * @param options - Filtering options
1804
- * @returns Array of templates
1805
- */
1806
- async getAllTemplatesForSelection(options) {
1807
- let q = query7(
1808
- this.collectionRef,
1809
- where7("isActive", "==", true),
1810
- orderBy("updatedAt", "desc")
1811
- );
1812
- if ((options == null ? void 0 : options.isUserForm) !== void 0) {
1813
- q = query7(q, where7("isUserForm", "==", options.isUserForm));
1814
- }
1815
- if ((options == null ? void 0 : options.isRequired) !== void 0) {
1816
- q = query7(q, where7("isRequired", "==", options.isRequired));
1817
- }
1818
- const querySnapshot = await getDocs7(q);
1819
- const templates = [];
1820
- querySnapshot.forEach((doc10) => {
1821
- templates.push(doc10.data());
1822
- });
1823
- return templates;
1893
+ };
1894
+ var RequirementNotFoundError = class extends RequirementError {
1895
+ constructor(id) {
1896
+ super(`Zahtev sa ID-em ${id} nije prona\u0111en`);
1897
+ this.name = "RequirementNotFoundError";
1824
1898
  }
1825
1899
  };
1826
-
1827
- // src/services/documentation-templates/filled-document.service.ts
1828
- import {
1829
- collection as collection9,
1830
- doc as doc9,
1831
- getDoc as getDoc9,
1832
- getDocs as getDocs9,
1833
- setDoc as setDoc3,
1834
- updateDoc as updateDoc9,
1835
- query as query9,
1836
- orderBy as orderBy3,
1837
- limit as limit3,
1838
- startAfter as startAfter2
1839
- } from "firebase/firestore";
1840
-
1841
- // src/services/media/media.service.ts
1842
- import { Timestamp as Timestamp2 } from "firebase/firestore";
1843
- import {
1844
- ref,
1845
- uploadBytes,
1846
- getDownloadURL,
1847
- deleteObject,
1848
- getBytes
1849
- } from "firebase/storage";
1850
- import {
1851
- doc as doc8,
1852
- getDoc as getDoc8,
1853
- setDoc as setDoc2,
1854
- updateDoc as updateDoc8,
1855
- collection as collection8,
1856
- query as query8,
1857
- where as where8,
1858
- limit as limit2,
1859
- getDocs as getDocs8,
1860
- deleteDoc as deleteDoc2,
1861
- orderBy as orderBy2
1862
- } from "firebase/firestore";
1863
-
1864
- // src/backoffice/services/documentation-template.service.ts
1865
- var DocumentationTemplateService2 = class {
1866
- /**
1867
- * Constructor for DocumentationTemplateService
1868
- * @param db - Firestore instance
1869
- * @param auth - Firebase Auth instance
1870
- * @param app - Firebase App instance
1871
- */
1872
- constructor(db, auth, app) {
1873
- this.apiService = new DocumentationTemplateService(db, auth, app);
1900
+ var InvalidRequirementDataError = class extends RequirementError {
1901
+ constructor(message) {
1902
+ super(`Neva\u017Ee\u0107i podaci za zahtev: ${message}`);
1903
+ this.name = "InvalidRequirementDataError";
1874
1904
  }
1875
- /**
1876
- * Create a new document template
1877
- * @param data - Template data
1878
- * @param userId - ID of the user creating the template
1879
- * @returns The created template
1880
- */
1881
- async createTemplate(data, userId) {
1882
- return this.apiService.createTemplate(data, userId);
1905
+ };
1906
+ var InvalidTimeframeError = class extends RequirementError {
1907
+ constructor(message) {
1908
+ super(`Invalid timeframe: ${message}`);
1909
+ this.name = "InvalidTimeframeError";
1883
1910
  }
1884
- /**
1885
- * Get a document template by ID
1886
- * @param templateId - ID of the template to retrieve
1887
- * @param version - Optional version number to retrieve (defaults to latest version)
1888
- * @returns The template or null if not found
1889
- */
1890
- async getTemplateById(templateId, version) {
1891
- return this.apiService.getTemplateById(templateId, version);
1911
+ };
1912
+ var RelationshipError = class extends BackofficeError {
1913
+ constructor(message) {
1914
+ super(message);
1915
+ this.name = "RelationshipError";
1892
1916
  }
1893
- /**
1894
- * Update an existing document template
1895
- * @param templateId - ID of the template to update
1896
- * @param data - Updated template data
1897
- * @returns The updated template
1898
- */
1899
- async updateTemplate(templateId, data) {
1900
- return this.apiService.updateTemplate(templateId, data);
1917
+ };
1918
+ var InvalidHierarchyError = class extends RelationshipError {
1919
+ constructor(message) {
1920
+ super(`Invalid hierarchy: ${message}`);
1921
+ this.name = "InvalidHierarchyError";
1901
1922
  }
1902
- /**
1903
- * Delete a document template
1904
- * @param templateId - ID of the template to delete
1905
- */
1906
- async deleteTemplate(templateId) {
1907
- return this.apiService.deleteTemplate(templateId);
1923
+ };
1924
+ var CircularReferenceError = class extends RelationshipError {
1925
+ constructor(message) {
1926
+ super(`Circular reference detected: ${message}`);
1927
+ this.name = "CircularReferenceError";
1908
1928
  }
1909
- /**
1910
- * Get all active templates
1911
- * @param pageSize - Number of templates to retrieve
1912
- * @param lastDoc - Last document from previous page for pagination
1913
- * @returns Array of templates and the last document for pagination
1914
- */
1915
- async getActiveTemplates(pageSize = 20, lastDoc) {
1916
- return this.apiService.getActiveTemplates(pageSize, lastDoc);
1929
+ };
1930
+ var BlockingConditionError = class extends BackofficeError {
1931
+ constructor(message) {
1932
+ super(message);
1933
+ this.name = "BlockingConditionError";
1917
1934
  }
1918
- /**
1919
- * Get templates by tags
1920
- * @param tags - Tags to filter by
1921
- * @param pageSize - Number of templates to retrieve
1922
- * @param lastDoc - Last document from previous page for pagination
1923
- * @returns Array of templates and the last document for pagination
1924
- */
1925
- async getTemplatesByTags(tags, pageSize = 20, lastDoc) {
1926
- return this.apiService.getTemplatesByTags(tags, pageSize, lastDoc);
1935
+ };
1936
+ var InvalidBlockingConditionError = class extends BlockingConditionError {
1937
+ constructor(condition) {
1938
+ super(`Neva\u017Ee\u0107i blokiraju\u0107i uslov: ${condition}`);
1939
+ this.name = "InvalidBlockingConditionError";
1927
1940
  }
1928
- /**
1929
- * Get templates created by a specific user
1930
- * @param userId - ID of the user who created the templates
1931
- * @param pageSize - Number of templates to retrieve
1932
- * @param lastDoc - Last document from previous page for pagination
1933
- * @returns Array of templates and the last document for pagination
1934
- */
1935
- async getTemplatesByCreator(userId, pageSize = 20, lastDoc) {
1936
- return this.apiService.getTemplatesByCreator(userId, pageSize, lastDoc);
1941
+ };
1942
+ var ContraindicationError = class extends BackofficeError {
1943
+ constructor(message) {
1944
+ super(message);
1945
+ this.name = "ContraindicationError";
1937
1946
  }
1938
- /**
1939
- * Get a specific version of a template
1940
- * @param templateId - ID of the template
1941
- * @param versionNumber - Version number to retrieve
1942
- * @returns The template version or null if not found
1943
- */
1944
- async getTemplateVersion(templateId, versionNumber) {
1945
- return this.apiService.getTemplateVersion(templateId, versionNumber);
1947
+ };
1948
+ var InvalidContraindicationError = class extends ContraindicationError {
1949
+ constructor(contraindication) {
1950
+ super(`Neva\u017Ee\u0107a kontraindikacija: ${contraindication}`);
1951
+ this.name = "InvalidContraindicationError";
1946
1952
  }
1947
- /**
1948
- * Get all versions of a template
1949
- * @param templateId - ID of the template
1950
- * @returns Array of template versions
1951
- */
1952
- async getTemplateVersions(templateId) {
1953
- return this.apiService.getTemplateOldVersions(templateId);
1953
+ };
1954
+ var TreatmentBenefitError = class extends BackofficeError {
1955
+ constructor(message) {
1956
+ super(message);
1957
+ this.name = "TreatmentBenefitError";
1958
+ }
1959
+ };
1960
+ var InvalidTreatmentBenefitError = class extends TreatmentBenefitError {
1961
+ constructor(benefit) {
1962
+ super(`Neva\u017Ee\u0107i benefit tretmana: ${benefit}`);
1963
+ this.name = "InvalidTreatmentBenefitError";
1954
1964
  }
1955
1965
  };
1956
1966
  export {
@@ -1966,7 +1976,7 @@ export {
1966
1976
  CircularReferenceError,
1967
1977
  Contraindication,
1968
1978
  ContraindicationError,
1969
- DEFAULT_CERTIFICATION_REQUIREMENT,
1979
+ DEFAULT_CERTIFICATION_REQUIREMENT2 as DEFAULT_CERTIFICATION_REQUIREMENT,
1970
1980
  DocumentElementType,
1971
1981
  DocumentationTemplateService2 as DocumentationTemplateService,
1972
1982
  DynamicVariable,