@blackcode_sa/metaestetics-api 1.12.66 → 1.12.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backoffice/index.d.mts +73 -0
- package/dist/backoffice/index.d.ts +73 -0
- package/dist/backoffice/index.js +181 -18
- package/dist/backoffice/index.mjs +181 -20
- package/dist/index.d.mts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +184 -21
- package/dist/index.mjs +184 -23
- package/package.json +1 -1
- package/src/backoffice/services/category.service.ts +72 -6
- package/src/backoffice/services/subcategory.service.ts +72 -6
- package/src/backoffice/services/technology.service.ts +74 -6
- package/src/backoffice/types/category.types.ts +5 -0
- package/src/backoffice/types/technology.types.ts +5 -0
- package/src/services/procedure/procedure.service.ts +3 -3
package/dist/index.mjs
CHANGED
|
@@ -18059,9 +18059,9 @@ var ProcedureService = class extends BaseService {
|
|
|
18059
18059
|
var _a, _b;
|
|
18060
18060
|
const procedureId = this.generateId();
|
|
18061
18061
|
const [category, subcategory, technology] = await Promise.all([
|
|
18062
|
-
this.categoryService.
|
|
18063
|
-
this.subcategoryService.
|
|
18064
|
-
this.technologyService.
|
|
18062
|
+
this.categoryService.getByIdInternal(data.categoryId),
|
|
18063
|
+
this.subcategoryService.getByIdInternal(data.categoryId, data.subcategoryId),
|
|
18064
|
+
this.technologyService.getByIdInternal(data.technologyId)
|
|
18065
18065
|
]);
|
|
18066
18066
|
if (!category || !subcategory || !technology) {
|
|
18067
18067
|
throw new Error("One or more required base entities not found");
|
|
@@ -19019,7 +19019,6 @@ import {
|
|
|
19019
19019
|
addDoc as addDoc5,
|
|
19020
19020
|
collection as collection34,
|
|
19021
19021
|
doc as doc40,
|
|
19022
|
-
getCountFromServer as getCountFromServer4,
|
|
19023
19022
|
getDoc as getDoc41,
|
|
19024
19023
|
getDocs as getDocs34,
|
|
19025
19024
|
limit as limit18,
|
|
@@ -19034,7 +19033,16 @@ import {
|
|
|
19034
19033
|
var CATEGORIES_COLLECTION = "backoffice_categories";
|
|
19035
19034
|
|
|
19036
19035
|
// src/backoffice/services/category.service.ts
|
|
19036
|
+
var EXCLUDED_CATEGORY_ID = "consultation";
|
|
19037
19037
|
var CategoryService = class extends BaseService {
|
|
19038
|
+
/**
|
|
19039
|
+
* Filters out excluded categories from a list.
|
|
19040
|
+
* @param categories - List of categories to filter
|
|
19041
|
+
* @returns Filtered list without excluded categories
|
|
19042
|
+
*/
|
|
19043
|
+
filterExcludedCategories(categories) {
|
|
19044
|
+
return categories.filter((cat) => cat.id !== EXCLUDED_CATEGORY_ID);
|
|
19045
|
+
}
|
|
19038
19046
|
/**
|
|
19039
19047
|
* Referenca na Firestore kolekciju kategorija
|
|
19040
19048
|
*/
|
|
@@ -19071,8 +19079,9 @@ var CategoryService = class extends BaseService {
|
|
|
19071
19079
|
where34("family", "==", family),
|
|
19072
19080
|
where34("isActive", "==", active)
|
|
19073
19081
|
);
|
|
19074
|
-
const snapshot = await
|
|
19075
|
-
|
|
19082
|
+
const snapshot = await getDocs34(q);
|
|
19083
|
+
const filteredDocs = snapshot.docs.filter((doc45) => doc45.id !== EXCLUDED_CATEGORY_ID);
|
|
19084
|
+
counts[family] = filteredDocs.length;
|
|
19076
19085
|
}
|
|
19077
19086
|
return counts;
|
|
19078
19087
|
}
|
|
@@ -19083,12 +19092,13 @@ var CategoryService = class extends BaseService {
|
|
|
19083
19092
|
async getAllForFilter() {
|
|
19084
19093
|
const q = query34(this.categoriesRef, where34("isActive", "==", true));
|
|
19085
19094
|
const snapshot = await getDocs34(q);
|
|
19086
|
-
|
|
19095
|
+
const categories = snapshot.docs.map(
|
|
19087
19096
|
(doc45) => ({
|
|
19088
19097
|
id: doc45.id,
|
|
19089
19098
|
...doc45.data()
|
|
19090
19099
|
})
|
|
19091
19100
|
);
|
|
19101
|
+
return this.filterExcludedCategories(categories);
|
|
19092
19102
|
}
|
|
19093
19103
|
/**
|
|
19094
19104
|
* Vraća sve kategorije za određenu familiju za potrebe filtera (bez paginacije)
|
|
@@ -19103,12 +19113,13 @@ var CategoryService = class extends BaseService {
|
|
|
19103
19113
|
orderBy20("name")
|
|
19104
19114
|
);
|
|
19105
19115
|
const snapshot = await getDocs34(q);
|
|
19106
|
-
|
|
19116
|
+
const categories = snapshot.docs.map(
|
|
19107
19117
|
(doc45) => ({
|
|
19108
19118
|
id: doc45.id,
|
|
19109
19119
|
...doc45.data()
|
|
19110
19120
|
})
|
|
19111
19121
|
);
|
|
19122
|
+
return this.filterExcludedCategories(categories);
|
|
19112
19123
|
}
|
|
19113
19124
|
/**
|
|
19114
19125
|
* Vraća sve kategorije sa paginacijom
|
|
@@ -19131,8 +19142,9 @@ var CategoryService = class extends BaseService {
|
|
|
19131
19142
|
...doc45.data()
|
|
19132
19143
|
})
|
|
19133
19144
|
);
|
|
19145
|
+
const filteredCategories = this.filterExcludedCategories(categories);
|
|
19134
19146
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19135
|
-
return { categories, lastVisible: newLastVisible };
|
|
19147
|
+
return { categories: filteredCategories, lastVisible: newLastVisible };
|
|
19136
19148
|
}
|
|
19137
19149
|
/**
|
|
19138
19150
|
* Vraća sve aktivne kategorije za određenu familiju procedura sa paginacijom
|
|
@@ -19157,8 +19169,9 @@ var CategoryService = class extends BaseService {
|
|
|
19157
19169
|
...doc45.data()
|
|
19158
19170
|
})
|
|
19159
19171
|
);
|
|
19172
|
+
const filteredCategories = this.filterExcludedCategories(categories);
|
|
19160
19173
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19161
|
-
return { categories, lastVisible: newLastVisible };
|
|
19174
|
+
return { categories: filteredCategories, lastVisible: newLastVisible };
|
|
19162
19175
|
}
|
|
19163
19176
|
/**
|
|
19164
19177
|
* Ažurira postojeću kategoriju
|
|
@@ -19195,6 +19208,22 @@ var CategoryService = class extends BaseService {
|
|
|
19195
19208
|
* @returns Kategorija ili null ako ne postoji
|
|
19196
19209
|
*/
|
|
19197
19210
|
async getById(id) {
|
|
19211
|
+
if (id === EXCLUDED_CATEGORY_ID) return null;
|
|
19212
|
+
const docRef = doc40(this.categoriesRef, id);
|
|
19213
|
+
const docSnap = await getDoc41(docRef);
|
|
19214
|
+
if (!docSnap.exists()) return null;
|
|
19215
|
+
return {
|
|
19216
|
+
id: docSnap.id,
|
|
19217
|
+
...docSnap.data()
|
|
19218
|
+
};
|
|
19219
|
+
}
|
|
19220
|
+
/**
|
|
19221
|
+
* Internal method to get category by ID without filtering.
|
|
19222
|
+
* Used internally for consultation procedures.
|
|
19223
|
+
* @param id - ID of the category to get
|
|
19224
|
+
* @returns Category or null if not found
|
|
19225
|
+
*/
|
|
19226
|
+
async getByIdInternal(id) {
|
|
19198
19227
|
const docRef = doc40(this.categoriesRef, id);
|
|
19199
19228
|
const docSnap = await getDoc41(docRef);
|
|
19200
19229
|
if (!docSnap.exists()) return null;
|
|
@@ -19203,6 +19232,29 @@ var CategoryService = class extends BaseService {
|
|
|
19203
19232
|
...docSnap.data()
|
|
19204
19233
|
};
|
|
19205
19234
|
}
|
|
19235
|
+
/**
|
|
19236
|
+
* Finds a category by exact name match within a specific family.
|
|
19237
|
+
* Used for CSV import matching.
|
|
19238
|
+
* @param name - Exact name of the category to find
|
|
19239
|
+
* @param family - Procedure family to search within
|
|
19240
|
+
* @returns Category if found, null otherwise
|
|
19241
|
+
*/
|
|
19242
|
+
async findByNameAndFamily(name, family) {
|
|
19243
|
+
const q = query34(
|
|
19244
|
+
this.categoriesRef,
|
|
19245
|
+
where34("name", "==", name),
|
|
19246
|
+
where34("family", "==", family),
|
|
19247
|
+
where34("isActive", "==", true)
|
|
19248
|
+
);
|
|
19249
|
+
const snapshot = await getDocs34(q);
|
|
19250
|
+
if (snapshot.empty) return null;
|
|
19251
|
+
const doc45 = snapshot.docs[0];
|
|
19252
|
+
if (doc45.id === EXCLUDED_CATEGORY_ID) return null;
|
|
19253
|
+
return {
|
|
19254
|
+
id: doc45.id,
|
|
19255
|
+
...doc45.data()
|
|
19256
|
+
};
|
|
19257
|
+
}
|
|
19206
19258
|
/**
|
|
19207
19259
|
* Exports categories to CSV string, suitable for Excel/Sheets.
|
|
19208
19260
|
* Includes headers and optional UTF-8 BOM.
|
|
@@ -19235,6 +19287,7 @@ var CategoryService = class extends BaseService {
|
|
|
19235
19287
|
const snapshot = await getDocs34(q);
|
|
19236
19288
|
if (snapshot.empty) break;
|
|
19237
19289
|
for (const d of snapshot.docs) {
|
|
19290
|
+
if (d.id === EXCLUDED_CATEGORY_ID) continue;
|
|
19238
19291
|
const category = { id: d.id, ...d.data() };
|
|
19239
19292
|
rows.push(this.categoryToCsvRow(category));
|
|
19240
19293
|
}
|
|
@@ -19277,7 +19330,6 @@ import {
|
|
|
19277
19330
|
collectionGroup as collectionGroup2,
|
|
19278
19331
|
deleteDoc as deleteDoc21,
|
|
19279
19332
|
doc as doc41,
|
|
19280
|
-
getCountFromServer as getCountFromServer5,
|
|
19281
19333
|
getDoc as getDoc42,
|
|
19282
19334
|
getDocs as getDocs35,
|
|
19283
19335
|
limit as limit19,
|
|
@@ -19293,7 +19345,16 @@ import {
|
|
|
19293
19345
|
var SUBCATEGORIES_COLLECTION = "subcategories";
|
|
19294
19346
|
|
|
19295
19347
|
// src/backoffice/services/subcategory.service.ts
|
|
19348
|
+
var EXCLUDED_SUBCATEGORY_ID = "free-consultation";
|
|
19296
19349
|
var SubcategoryService = class extends BaseService {
|
|
19350
|
+
/**
|
|
19351
|
+
* Filters out excluded subcategories from a list.
|
|
19352
|
+
* @param subcategories - List of subcategories to filter
|
|
19353
|
+
* @returns Filtered list without excluded subcategories
|
|
19354
|
+
*/
|
|
19355
|
+
filterExcludedSubcategories(subcategories) {
|
|
19356
|
+
return subcategories.filter((sub) => sub.id !== EXCLUDED_SUBCATEGORY_ID);
|
|
19357
|
+
}
|
|
19297
19358
|
/**
|
|
19298
19359
|
* Vraća referencu na Firestore kolekciju podkategorija za određenu kategoriju
|
|
19299
19360
|
* @param categoryId - ID roditeljske kategorije
|
|
@@ -19340,8 +19401,9 @@ var SubcategoryService = class extends BaseService {
|
|
|
19340
19401
|
const categoryId = categoryDoc.id;
|
|
19341
19402
|
const subcategoriesRef = this.getSubcategoriesRef(categoryId);
|
|
19342
19403
|
const q = query35(subcategoriesRef, where35("isActive", "==", active));
|
|
19343
|
-
const snapshot = await
|
|
19344
|
-
|
|
19404
|
+
const snapshot = await getDocs35(q);
|
|
19405
|
+
const filteredDocs = snapshot.docs.filter((doc45) => doc45.id !== EXCLUDED_SUBCATEGORY_ID);
|
|
19406
|
+
counts[categoryId] = filteredDocs.length;
|
|
19345
19407
|
}
|
|
19346
19408
|
return counts;
|
|
19347
19409
|
}
|
|
@@ -19367,8 +19429,9 @@ var SubcategoryService = class extends BaseService {
|
|
|
19367
19429
|
...doc45.data()
|
|
19368
19430
|
})
|
|
19369
19431
|
);
|
|
19432
|
+
const filteredSubcategories = this.filterExcludedSubcategories(subcategories);
|
|
19370
19433
|
const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
19371
|
-
return { subcategories, lastVisible: newLastVisible };
|
|
19434
|
+
return { subcategories: filteredSubcategories, lastVisible: newLastVisible };
|
|
19372
19435
|
}
|
|
19373
19436
|
/**
|
|
19374
19437
|
* Vraća sve podkategorije sa paginacijom koristeći collection group query.
|
|
@@ -19397,8 +19460,9 @@ var SubcategoryService = class extends BaseService {
|
|
|
19397
19460
|
...doc45.data()
|
|
19398
19461
|
})
|
|
19399
19462
|
);
|
|
19463
|
+
const filteredSubcategories = this.filterExcludedSubcategories(subcategories);
|
|
19400
19464
|
const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
19401
|
-
return { subcategories, lastVisible: newLastVisible };
|
|
19465
|
+
return { subcategories: filteredSubcategories, lastVisible: newLastVisible };
|
|
19402
19466
|
}
|
|
19403
19467
|
/**
|
|
19404
19468
|
* Vraća sve subkategorije za određenu kategoriju za potrebe filtera (bez paginacije)
|
|
@@ -19411,12 +19475,13 @@ var SubcategoryService = class extends BaseService {
|
|
|
19411
19475
|
where35("isActive", "==", true)
|
|
19412
19476
|
);
|
|
19413
19477
|
const querySnapshot = await getDocs35(q);
|
|
19414
|
-
|
|
19478
|
+
const subcategories = querySnapshot.docs.map(
|
|
19415
19479
|
(doc45) => ({
|
|
19416
19480
|
id: doc45.id,
|
|
19417
19481
|
...doc45.data()
|
|
19418
19482
|
})
|
|
19419
19483
|
);
|
|
19484
|
+
return this.filterExcludedSubcategories(subcategories);
|
|
19420
19485
|
}
|
|
19421
19486
|
/**
|
|
19422
19487
|
* Vraća sve subkategorije za potrebe filtera (bez paginacije)
|
|
@@ -19428,12 +19493,13 @@ var SubcategoryService = class extends BaseService {
|
|
|
19428
19493
|
where35("isActive", "==", true)
|
|
19429
19494
|
);
|
|
19430
19495
|
const querySnapshot = await getDocs35(q);
|
|
19431
|
-
|
|
19496
|
+
const subcategories = querySnapshot.docs.map(
|
|
19432
19497
|
(doc45) => ({
|
|
19433
19498
|
id: doc45.id,
|
|
19434
19499
|
...doc45.data()
|
|
19435
19500
|
})
|
|
19436
19501
|
);
|
|
19502
|
+
return this.filterExcludedSubcategories(subcategories);
|
|
19437
19503
|
}
|
|
19438
19504
|
/**
|
|
19439
19505
|
* Ažurira postojeću podkategoriju
|
|
@@ -19503,6 +19569,23 @@ var SubcategoryService = class extends BaseService {
|
|
|
19503
19569
|
* @returns Podkategorija ili null ako ne postoji
|
|
19504
19570
|
*/
|
|
19505
19571
|
async getById(categoryId, subcategoryId) {
|
|
19572
|
+
if (subcategoryId === EXCLUDED_SUBCATEGORY_ID) return null;
|
|
19573
|
+
const docRef = doc41(this.getSubcategoriesRef(categoryId), subcategoryId);
|
|
19574
|
+
const docSnap = await getDoc42(docRef);
|
|
19575
|
+
if (!docSnap.exists()) return null;
|
|
19576
|
+
return {
|
|
19577
|
+
id: docSnap.id,
|
|
19578
|
+
...docSnap.data()
|
|
19579
|
+
};
|
|
19580
|
+
}
|
|
19581
|
+
/**
|
|
19582
|
+
* Internal method to get subcategory by ID without filtering.
|
|
19583
|
+
* Used internally for consultation procedures.
|
|
19584
|
+
* @param categoryId - ID of the category
|
|
19585
|
+
* @param subcategoryId - ID of the subcategory to get
|
|
19586
|
+
* @returns Subcategory or null if not found
|
|
19587
|
+
*/
|
|
19588
|
+
async getByIdInternal(categoryId, subcategoryId) {
|
|
19506
19589
|
const docRef = doc41(this.getSubcategoriesRef(categoryId), subcategoryId);
|
|
19507
19590
|
const docSnap = await getDoc42(docRef);
|
|
19508
19591
|
if (!docSnap.exists()) return null;
|
|
@@ -19511,6 +19594,28 @@ var SubcategoryService = class extends BaseService {
|
|
|
19511
19594
|
...docSnap.data()
|
|
19512
19595
|
};
|
|
19513
19596
|
}
|
|
19597
|
+
/**
|
|
19598
|
+
* Finds a subcategory by exact name match within a specific category.
|
|
19599
|
+
* Used for CSV import matching.
|
|
19600
|
+
* @param name - Exact name of the subcategory to find
|
|
19601
|
+
* @param categoryId - ID of the category to search within
|
|
19602
|
+
* @returns Subcategory if found, null otherwise
|
|
19603
|
+
*/
|
|
19604
|
+
async findByNameAndCategory(name, categoryId) {
|
|
19605
|
+
const q = query35(
|
|
19606
|
+
this.getSubcategoriesRef(categoryId),
|
|
19607
|
+
where35("name", "==", name),
|
|
19608
|
+
where35("isActive", "==", true)
|
|
19609
|
+
);
|
|
19610
|
+
const querySnapshot = await getDocs35(q);
|
|
19611
|
+
if (querySnapshot.empty) return null;
|
|
19612
|
+
const doc45 = querySnapshot.docs[0];
|
|
19613
|
+
if (doc45.id === EXCLUDED_SUBCATEGORY_ID) return null;
|
|
19614
|
+
return {
|
|
19615
|
+
id: doc45.id,
|
|
19616
|
+
...doc45.data()
|
|
19617
|
+
};
|
|
19618
|
+
}
|
|
19514
19619
|
/**
|
|
19515
19620
|
* Exports subcategories to CSV string, suitable for Excel/Sheets.
|
|
19516
19621
|
* Includes headers and optional UTF-8 BOM.
|
|
@@ -19546,6 +19651,7 @@ var SubcategoryService = class extends BaseService {
|
|
|
19546
19651
|
const snapshot = await getDocs35(q);
|
|
19547
19652
|
if (snapshot.empty) break;
|
|
19548
19653
|
for (const d of snapshot.docs) {
|
|
19654
|
+
if (d.id === EXCLUDED_SUBCATEGORY_ID) continue;
|
|
19549
19655
|
const subcategory = { id: d.id, ...d.data() };
|
|
19550
19656
|
rows.push(this.subcategoryToCsvRow(subcategory));
|
|
19551
19657
|
}
|
|
@@ -19603,11 +19709,20 @@ import {
|
|
|
19603
19709
|
var PRODUCTS_COLLECTION = "products";
|
|
19604
19710
|
|
|
19605
19711
|
// src/backoffice/services/technology.service.ts
|
|
19712
|
+
var EXCLUDED_TECHNOLOGY_ID = "free-consultation-tech";
|
|
19606
19713
|
var DEFAULT_CERTIFICATION_REQUIREMENT = {
|
|
19607
19714
|
minimumLevel: "aesthetician" /* AESTHETICIAN */,
|
|
19608
19715
|
requiredSpecialties: []
|
|
19609
19716
|
};
|
|
19610
19717
|
var TechnologyService = class extends BaseService {
|
|
19718
|
+
/**
|
|
19719
|
+
* Filters out excluded technologies from a list.
|
|
19720
|
+
* @param technologies - List of technologies to filter
|
|
19721
|
+
* @returns Filtered list without excluded technologies
|
|
19722
|
+
*/
|
|
19723
|
+
filterExcludedTechnologies(technologies) {
|
|
19724
|
+
return technologies.filter((tech) => tech.id !== EXCLUDED_TECHNOLOGY_ID);
|
|
19725
|
+
}
|
|
19611
19726
|
/**
|
|
19612
19727
|
* Reference to the Firestore collection of technologies.
|
|
19613
19728
|
*/
|
|
@@ -19656,6 +19771,7 @@ var TechnologyService = class extends BaseService {
|
|
|
19656
19771
|
const snapshot = await getDocs36(q);
|
|
19657
19772
|
const counts = {};
|
|
19658
19773
|
snapshot.docs.forEach((doc45) => {
|
|
19774
|
+
if (doc45.id === EXCLUDED_TECHNOLOGY_ID) return;
|
|
19659
19775
|
const tech = doc45.data();
|
|
19660
19776
|
counts[tech.subcategoryId] = (counts[tech.subcategoryId] || 0) + 1;
|
|
19661
19777
|
});
|
|
@@ -19671,6 +19787,7 @@ var TechnologyService = class extends BaseService {
|
|
|
19671
19787
|
const snapshot = await getDocs36(q);
|
|
19672
19788
|
const counts = {};
|
|
19673
19789
|
snapshot.docs.forEach((doc45) => {
|
|
19790
|
+
if (doc45.id === EXCLUDED_TECHNOLOGY_ID) return;
|
|
19674
19791
|
const tech = doc45.data();
|
|
19675
19792
|
counts[tech.categoryId] = (counts[tech.categoryId] || 0) + 1;
|
|
19676
19793
|
});
|
|
@@ -19697,8 +19814,9 @@ var TechnologyService = class extends BaseService {
|
|
|
19697
19814
|
...doc45.data()
|
|
19698
19815
|
})
|
|
19699
19816
|
);
|
|
19817
|
+
const filteredTechnologies = this.filterExcludedTechnologies(technologies);
|
|
19700
19818
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19701
|
-
return { technologies, lastVisible: newLastVisible };
|
|
19819
|
+
return { technologies: filteredTechnologies, lastVisible: newLastVisible };
|
|
19702
19820
|
}
|
|
19703
19821
|
/**
|
|
19704
19822
|
* Returns all technologies for a specific category with pagination.
|
|
@@ -19723,8 +19841,9 @@ var TechnologyService = class extends BaseService {
|
|
|
19723
19841
|
...doc45.data()
|
|
19724
19842
|
})
|
|
19725
19843
|
);
|
|
19844
|
+
const filteredTechnologies = this.filterExcludedTechnologies(technologies);
|
|
19726
19845
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19727
|
-
return { technologies, lastVisible: newLastVisible };
|
|
19846
|
+
return { technologies: filteredTechnologies, lastVisible: newLastVisible };
|
|
19728
19847
|
}
|
|
19729
19848
|
/**
|
|
19730
19849
|
* Returns all technologies for a specific subcategory with pagination.
|
|
@@ -19749,8 +19868,9 @@ var TechnologyService = class extends BaseService {
|
|
|
19749
19868
|
...doc45.data()
|
|
19750
19869
|
})
|
|
19751
19870
|
);
|
|
19871
|
+
const filteredTechnologies = this.filterExcludedTechnologies(technologies);
|
|
19752
19872
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19753
|
-
return { technologies, lastVisible: newLastVisible };
|
|
19873
|
+
return { technologies: filteredTechnologies, lastVisible: newLastVisible };
|
|
19754
19874
|
}
|
|
19755
19875
|
/**
|
|
19756
19876
|
* Updates an existing technology.
|
|
@@ -19808,6 +19928,22 @@ var TechnologyService = class extends BaseService {
|
|
|
19808
19928
|
* @returns The technology or null if it doesn't exist.
|
|
19809
19929
|
*/
|
|
19810
19930
|
async getById(id) {
|
|
19931
|
+
if (id === EXCLUDED_TECHNOLOGY_ID) return null;
|
|
19932
|
+
const docRef = doc42(this.technologiesRef, id);
|
|
19933
|
+
const docSnap = await getDoc43(docRef);
|
|
19934
|
+
if (!docSnap.exists()) return null;
|
|
19935
|
+
return {
|
|
19936
|
+
id: docSnap.id,
|
|
19937
|
+
...docSnap.data()
|
|
19938
|
+
};
|
|
19939
|
+
}
|
|
19940
|
+
/**
|
|
19941
|
+
* Internal method to get technology by ID without filtering.
|
|
19942
|
+
* Used internally for consultation procedures.
|
|
19943
|
+
* @param id - The ID of the requested technology
|
|
19944
|
+
* @returns The technology or null if it doesn't exist
|
|
19945
|
+
*/
|
|
19946
|
+
async getByIdInternal(id) {
|
|
19811
19947
|
const docRef = doc42(this.technologiesRef, id);
|
|
19812
19948
|
const docSnap = await getDoc43(docRef);
|
|
19813
19949
|
if (!docSnap.exists()) return null;
|
|
@@ -19816,6 +19952,27 @@ var TechnologyService = class extends BaseService {
|
|
|
19816
19952
|
...docSnap.data()
|
|
19817
19953
|
};
|
|
19818
19954
|
}
|
|
19955
|
+
/**
|
|
19956
|
+
* Finds a technology by exact name match.
|
|
19957
|
+
* Used for CSV import duplicate detection.
|
|
19958
|
+
* @param name - Exact name of the technology to find
|
|
19959
|
+
* @returns Technology if found, null otherwise
|
|
19960
|
+
*/
|
|
19961
|
+
async findByName(name) {
|
|
19962
|
+
const q = query36(
|
|
19963
|
+
this.technologiesRef,
|
|
19964
|
+
where36("name", "==", name),
|
|
19965
|
+
where36("isActive", "==", true)
|
|
19966
|
+
);
|
|
19967
|
+
const snapshot = await getDocs36(q);
|
|
19968
|
+
if (snapshot.empty) return null;
|
|
19969
|
+
const doc45 = snapshot.docs[0];
|
|
19970
|
+
if (doc45.id === EXCLUDED_TECHNOLOGY_ID) return null;
|
|
19971
|
+
return {
|
|
19972
|
+
id: doc45.id,
|
|
19973
|
+
...doc45.data()
|
|
19974
|
+
};
|
|
19975
|
+
}
|
|
19819
19976
|
/**
|
|
19820
19977
|
* Dodaje novi zahtev tehnologiji
|
|
19821
19978
|
* @param technologyId - ID tehnologije
|
|
@@ -20176,12 +20333,13 @@ var TechnologyService = class extends BaseService {
|
|
|
20176
20333
|
orderBy22("name")
|
|
20177
20334
|
);
|
|
20178
20335
|
const snapshot = await getDocs36(q);
|
|
20179
|
-
|
|
20336
|
+
const technologies = snapshot.docs.map(
|
|
20180
20337
|
(doc45) => ({
|
|
20181
20338
|
id: doc45.id,
|
|
20182
20339
|
...doc45.data()
|
|
20183
20340
|
})
|
|
20184
20341
|
);
|
|
20342
|
+
return this.filterExcludedTechnologies(technologies);
|
|
20185
20343
|
}
|
|
20186
20344
|
/**
|
|
20187
20345
|
* Gets all active technologies for a subcategory for filter dropdowns.
|
|
@@ -20197,12 +20355,13 @@ var TechnologyService = class extends BaseService {
|
|
|
20197
20355
|
orderBy22("name")
|
|
20198
20356
|
);
|
|
20199
20357
|
const snapshot = await getDocs36(q);
|
|
20200
|
-
|
|
20358
|
+
const technologies = snapshot.docs.map(
|
|
20201
20359
|
(doc45) => ({
|
|
20202
20360
|
id: doc45.id,
|
|
20203
20361
|
...doc45.data()
|
|
20204
20362
|
})
|
|
20205
20363
|
);
|
|
20364
|
+
return this.filterExcludedTechnologies(technologies);
|
|
20206
20365
|
}
|
|
20207
20366
|
/**
|
|
20208
20367
|
* Gets all active technologies for filter dropdowns.
|
|
@@ -20214,12 +20373,13 @@ var TechnologyService = class extends BaseService {
|
|
|
20214
20373
|
orderBy22("name")
|
|
20215
20374
|
);
|
|
20216
20375
|
const snapshot = await getDocs36(q);
|
|
20217
|
-
|
|
20376
|
+
const technologies = snapshot.docs.map(
|
|
20218
20377
|
(doc45) => ({
|
|
20219
20378
|
id: doc45.id,
|
|
20220
20379
|
...doc45.data()
|
|
20221
20380
|
})
|
|
20222
20381
|
);
|
|
20382
|
+
return this.filterExcludedTechnologies(technologies);
|
|
20223
20383
|
}
|
|
20224
20384
|
// ==========================================
|
|
20225
20385
|
// NEW METHODS: Product assignment management
|
|
@@ -20385,6 +20545,7 @@ var TechnologyService = class extends BaseService {
|
|
|
20385
20545
|
const snapshot = await getDocs36(q);
|
|
20386
20546
|
if (snapshot.empty) break;
|
|
20387
20547
|
for (const d of snapshot.docs) {
|
|
20548
|
+
if (d.id === EXCLUDED_TECHNOLOGY_ID) continue;
|
|
20388
20549
|
const technology = { id: d.id, ...d.data() };
|
|
20389
20550
|
const productNames = await this.getProductNamesForTechnology(technology.id);
|
|
20390
20551
|
rows.push(this.technologyToCsvRow(technology, productNames));
|
package/package.json
CHANGED
|
@@ -17,6 +17,12 @@ import { Category, CATEGORIES_COLLECTION, ICategoryService } from '../types/cate
|
|
|
17
17
|
import { BaseService } from '../../services/base.service';
|
|
18
18
|
import { ProcedureFamily } from '../types/static/procedure-family.types';
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* ID of the consultation category that should be hidden from admin backoffice.
|
|
22
|
+
* This category is used internally for free consultation procedures.
|
|
23
|
+
*/
|
|
24
|
+
const EXCLUDED_CATEGORY_ID = 'consultation';
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* Servis za upravljanje kategorijama procedura.
|
|
22
28
|
* Kategorije su prvi nivo organizacije nakon procedure family (aesthetics/surgery).
|
|
@@ -31,6 +37,14 @@ import { ProcedureFamily } from '../types/static/procedure-family.types';
|
|
|
31
37
|
* });
|
|
32
38
|
*/
|
|
33
39
|
export class CategoryService extends BaseService implements ICategoryService {
|
|
40
|
+
/**
|
|
41
|
+
* Filters out excluded categories from a list.
|
|
42
|
+
* @param categories - List of categories to filter
|
|
43
|
+
* @returns Filtered list without excluded categories
|
|
44
|
+
*/
|
|
45
|
+
private filterExcludedCategories(categories: Category[]): Category[] {
|
|
46
|
+
return categories.filter(cat => cat.id !== EXCLUDED_CATEGORY_ID);
|
|
47
|
+
}
|
|
34
48
|
/**
|
|
35
49
|
* Referenca na Firestore kolekciju kategorija
|
|
36
50
|
*/
|
|
@@ -71,8 +85,10 @@ export class CategoryService extends BaseService implements ICategoryService {
|
|
|
71
85
|
where('family', '==', family),
|
|
72
86
|
where('isActive', '==', active),
|
|
73
87
|
);
|
|
74
|
-
const snapshot = await
|
|
75
|
-
|
|
88
|
+
const snapshot = await getDocs(q);
|
|
89
|
+
// Filter out excluded category and count
|
|
90
|
+
const filteredDocs = snapshot.docs.filter(doc => doc.id !== EXCLUDED_CATEGORY_ID);
|
|
91
|
+
counts[family] = filteredDocs.length;
|
|
76
92
|
}
|
|
77
93
|
return counts;
|
|
78
94
|
}
|
|
@@ -84,13 +100,14 @@ export class CategoryService extends BaseService implements ICategoryService {
|
|
|
84
100
|
async getAllForFilter() {
|
|
85
101
|
const q = query(this.categoriesRef, where('isActive', '==', true));
|
|
86
102
|
const snapshot = await getDocs(q);
|
|
87
|
-
|
|
103
|
+
const categories = snapshot.docs.map(
|
|
88
104
|
doc =>
|
|
89
105
|
({
|
|
90
106
|
id: doc.id,
|
|
91
107
|
...doc.data(),
|
|
92
108
|
} as Category),
|
|
93
109
|
);
|
|
110
|
+
return this.filterExcludedCategories(categories);
|
|
94
111
|
}
|
|
95
112
|
|
|
96
113
|
/**
|
|
@@ -106,13 +123,14 @@ export class CategoryService extends BaseService implements ICategoryService {
|
|
|
106
123
|
orderBy('name'),
|
|
107
124
|
);
|
|
108
125
|
const snapshot = await getDocs(q);
|
|
109
|
-
|
|
126
|
+
const categories = snapshot.docs.map(
|
|
110
127
|
doc =>
|
|
111
128
|
({
|
|
112
129
|
id: doc.id,
|
|
113
130
|
...doc.data(),
|
|
114
131
|
} as Category),
|
|
115
132
|
);
|
|
133
|
+
return this.filterExcludedCategories(categories);
|
|
116
134
|
}
|
|
117
135
|
|
|
118
136
|
/**
|
|
@@ -144,8 +162,9 @@ export class CategoryService extends BaseService implements ICategoryService {
|
|
|
144
162
|
...doc.data(),
|
|
145
163
|
} as Category),
|
|
146
164
|
);
|
|
165
|
+
const filteredCategories = this.filterExcludedCategories(categories);
|
|
147
166
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
148
|
-
return { categories, lastVisible: newLastVisible };
|
|
167
|
+
return { categories: filteredCategories, lastVisible: newLastVisible };
|
|
149
168
|
}
|
|
150
169
|
|
|
151
170
|
/**
|
|
@@ -180,8 +199,9 @@ export class CategoryService extends BaseService implements ICategoryService {
|
|
|
180
199
|
...doc.data(),
|
|
181
200
|
} as Category),
|
|
182
201
|
);
|
|
202
|
+
const filteredCategories = this.filterExcludedCategories(categories);
|
|
183
203
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
184
|
-
return { categories, lastVisible: newLastVisible };
|
|
204
|
+
return { categories: filteredCategories, lastVisible: newLastVisible };
|
|
185
205
|
}
|
|
186
206
|
|
|
187
207
|
/**
|
|
@@ -223,6 +243,25 @@ export class CategoryService extends BaseService implements ICategoryService {
|
|
|
223
243
|
* @returns Kategorija ili null ako ne postoji
|
|
224
244
|
*/
|
|
225
245
|
async getById(id: string) {
|
|
246
|
+
// Prevent access to excluded category
|
|
247
|
+
if (id === EXCLUDED_CATEGORY_ID) return null;
|
|
248
|
+
|
|
249
|
+
const docRef = doc(this.categoriesRef, id);
|
|
250
|
+
const docSnap = await getDoc(docRef);
|
|
251
|
+
if (!docSnap.exists()) return null;
|
|
252
|
+
return {
|
|
253
|
+
id: docSnap.id,
|
|
254
|
+
...docSnap.data(),
|
|
255
|
+
} as Category;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Internal method to get category by ID without filtering.
|
|
260
|
+
* Used internally for consultation procedures.
|
|
261
|
+
* @param id - ID of the category to get
|
|
262
|
+
* @returns Category or null if not found
|
|
263
|
+
*/
|
|
264
|
+
async getByIdInternal(id: string): Promise<Category | null> {
|
|
226
265
|
const docRef = doc(this.categoriesRef, id);
|
|
227
266
|
const docSnap = await getDoc(docRef);
|
|
228
267
|
if (!docSnap.exists()) return null;
|
|
@@ -232,6 +271,31 @@ export class CategoryService extends BaseService implements ICategoryService {
|
|
|
232
271
|
} as Category;
|
|
233
272
|
}
|
|
234
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Finds a category by exact name match within a specific family.
|
|
276
|
+
* Used for CSV import matching.
|
|
277
|
+
* @param name - Exact name of the category to find
|
|
278
|
+
* @param family - Procedure family to search within
|
|
279
|
+
* @returns Category if found, null otherwise
|
|
280
|
+
*/
|
|
281
|
+
async findByNameAndFamily(name: string, family: ProcedureFamily): Promise<Category | null> {
|
|
282
|
+
const q = query(
|
|
283
|
+
this.categoriesRef,
|
|
284
|
+
where('name', '==', name),
|
|
285
|
+
where('family', '==', family),
|
|
286
|
+
where('isActive', '==', true),
|
|
287
|
+
);
|
|
288
|
+
const snapshot = await getDocs(q);
|
|
289
|
+
if (snapshot.empty) return null;
|
|
290
|
+
const doc = snapshot.docs[0];
|
|
291
|
+
// Exclude consultation category
|
|
292
|
+
if (doc.id === EXCLUDED_CATEGORY_ID) return null;
|
|
293
|
+
return {
|
|
294
|
+
id: doc.id,
|
|
295
|
+
...doc.data(),
|
|
296
|
+
} as Category;
|
|
297
|
+
}
|
|
298
|
+
|
|
235
299
|
/**
|
|
236
300
|
* Exports categories to CSV string, suitable for Excel/Sheets.
|
|
237
301
|
* Includes headers and optional UTF-8 BOM.
|
|
@@ -276,6 +340,8 @@ export class CategoryService extends BaseService implements ICategoryService {
|
|
|
276
340
|
if (snapshot.empty) break;
|
|
277
341
|
|
|
278
342
|
for (const d of snapshot.docs) {
|
|
343
|
+
// Exclude consultation category from CSV export
|
|
344
|
+
if (d.id === EXCLUDED_CATEGORY_ID) continue;
|
|
279
345
|
const category = ({ id: d.id, ...d.data() } as unknown) as Category;
|
|
280
346
|
rows.push(this.categoryToCsvRow(category));
|
|
281
347
|
}
|