@blackcode_sa/metaestetics-api 1.12.41 → 1.12.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/index.d.mts +4 -4
- package/dist/admin/index.d.ts +4 -4
- package/dist/backoffice/index.d.mts +105 -7
- package/dist/backoffice/index.d.ts +105 -7
- package/dist/backoffice/index.js +552 -8
- package/dist/backoffice/index.mjs +566 -21
- package/dist/index.d.mts +93 -7
- package/dist/index.d.ts +93 -7
- package/dist/index.js +493 -8
- package/dist/index.mjs +493 -8
- package/package.json +1 -1
- package/src/backoffice/services/brand.service.ts +86 -0
- package/src/backoffice/services/category.service.ts +84 -0
- package/src/backoffice/services/constants.service.ts +77 -0
- package/src/backoffice/services/product.service.ts +113 -10
- package/src/backoffice/services/requirement.service.ts +76 -0
- package/src/backoffice/services/subcategory.service.ts +87 -0
- package/src/backoffice/services/technology.service.ts +178 -0
- package/src/backoffice/types/product.types.ts +7 -6
|
@@ -169,6 +169,73 @@ var BrandService = class extends BaseService {
|
|
|
169
169
|
...docSnap.data()
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Exports brands to CSV string, suitable for Excel/Sheets.
|
|
174
|
+
* Includes headers and optional UTF-8 BOM.
|
|
175
|
+
* By default exports only active brands (set includeInactive to true to export all).
|
|
176
|
+
*/
|
|
177
|
+
async exportToCsv(options) {
|
|
178
|
+
var _a, _b;
|
|
179
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
180
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
181
|
+
const headers = [
|
|
182
|
+
"id",
|
|
183
|
+
"name",
|
|
184
|
+
"manufacturer",
|
|
185
|
+
"website",
|
|
186
|
+
"description",
|
|
187
|
+
"isActive"
|
|
188
|
+
];
|
|
189
|
+
const rows = [];
|
|
190
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
191
|
+
const PAGE_SIZE = 1e3;
|
|
192
|
+
let cursor;
|
|
193
|
+
const baseConstraints = [];
|
|
194
|
+
if (!includeInactive) {
|
|
195
|
+
baseConstraints.push(where("isActive", "==", true));
|
|
196
|
+
}
|
|
197
|
+
baseConstraints.push(orderBy("name_lowercase"));
|
|
198
|
+
while (true) {
|
|
199
|
+
const constraints = [...baseConstraints, limit(PAGE_SIZE)];
|
|
200
|
+
if (cursor) constraints.push(startAfter(cursor));
|
|
201
|
+
const q = query(this.getBrandsRef(), ...constraints);
|
|
202
|
+
const snapshot = await getDocs(q);
|
|
203
|
+
if (snapshot.empty) break;
|
|
204
|
+
for (const d of snapshot.docs) {
|
|
205
|
+
const brand = { id: d.id, ...d.data() };
|
|
206
|
+
rows.push(this.brandToCsvRow(brand));
|
|
207
|
+
}
|
|
208
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
209
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
210
|
+
}
|
|
211
|
+
const csvBody = rows.join("\r\n");
|
|
212
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
213
|
+
}
|
|
214
|
+
brandToCsvRow(brand) {
|
|
215
|
+
var _a, _b, _c, _d, _e, _f;
|
|
216
|
+
const values = [
|
|
217
|
+
(_a = brand.id) != null ? _a : "",
|
|
218
|
+
(_b = brand.name) != null ? _b : "",
|
|
219
|
+
(_c = brand.manufacturer) != null ? _c : "",
|
|
220
|
+
(_d = brand.website) != null ? _d : "",
|
|
221
|
+
(_e = brand.description) != null ? _e : "",
|
|
222
|
+
String((_f = brand.isActive) != null ? _f : "")
|
|
223
|
+
];
|
|
224
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
225
|
+
}
|
|
226
|
+
formatDateIso(value) {
|
|
227
|
+
if (value instanceof Date) return value.toISOString();
|
|
228
|
+
if (value && typeof value.toDate === "function") {
|
|
229
|
+
const d = value.toDate();
|
|
230
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
231
|
+
}
|
|
232
|
+
return String(value != null ? value : "");
|
|
233
|
+
}
|
|
234
|
+
formatCsvValue(value) {
|
|
235
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
236
|
+
const escaped = str.replace(/"/g, '""');
|
|
237
|
+
return `"${escaped}"`;
|
|
238
|
+
}
|
|
172
239
|
};
|
|
173
240
|
|
|
174
241
|
// src/backoffice/services/category.service.ts
|
|
@@ -367,6 +434,71 @@ var CategoryService = class extends BaseService {
|
|
|
367
434
|
...docSnap.data()
|
|
368
435
|
};
|
|
369
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Exports categories to CSV string, suitable for Excel/Sheets.
|
|
439
|
+
* Includes headers and optional UTF-8 BOM.
|
|
440
|
+
* By default exports only active categories (set includeInactive to true to export all).
|
|
441
|
+
*/
|
|
442
|
+
async exportToCsv(options) {
|
|
443
|
+
var _a, _b;
|
|
444
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
445
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
446
|
+
const headers = [
|
|
447
|
+
"id",
|
|
448
|
+
"name",
|
|
449
|
+
"description",
|
|
450
|
+
"family",
|
|
451
|
+
"isActive"
|
|
452
|
+
];
|
|
453
|
+
const rows = [];
|
|
454
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
455
|
+
const PAGE_SIZE = 1e3;
|
|
456
|
+
let cursor;
|
|
457
|
+
const constraints = [];
|
|
458
|
+
if (!includeInactive) {
|
|
459
|
+
constraints.push(where2("isActive", "==", true));
|
|
460
|
+
}
|
|
461
|
+
constraints.push(orderBy2("name"));
|
|
462
|
+
while (true) {
|
|
463
|
+
const queryConstraints = [...constraints, limit2(PAGE_SIZE)];
|
|
464
|
+
if (cursor) queryConstraints.push(startAfter2(cursor));
|
|
465
|
+
const q = query2(this.categoriesRef, ...queryConstraints);
|
|
466
|
+
const snapshot = await getDocs2(q);
|
|
467
|
+
if (snapshot.empty) break;
|
|
468
|
+
for (const d of snapshot.docs) {
|
|
469
|
+
const category = { id: d.id, ...d.data() };
|
|
470
|
+
rows.push(this.categoryToCsvRow(category));
|
|
471
|
+
}
|
|
472
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
473
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
474
|
+
}
|
|
475
|
+
const csvBody = rows.join("\r\n");
|
|
476
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
477
|
+
}
|
|
478
|
+
categoryToCsvRow(category) {
|
|
479
|
+
var _a, _b, _c, _d, _e;
|
|
480
|
+
const values = [
|
|
481
|
+
(_a = category.id) != null ? _a : "",
|
|
482
|
+
(_b = category.name) != null ? _b : "",
|
|
483
|
+
(_c = category.description) != null ? _c : "",
|
|
484
|
+
(_d = category.family) != null ? _d : "",
|
|
485
|
+
String((_e = category.isActive) != null ? _e : "")
|
|
486
|
+
];
|
|
487
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
488
|
+
}
|
|
489
|
+
formatDateIso(value) {
|
|
490
|
+
if (value instanceof Date) return value.toISOString();
|
|
491
|
+
if (value && typeof value.toDate === "function") {
|
|
492
|
+
const d = value.toDate();
|
|
493
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
494
|
+
}
|
|
495
|
+
return String(value != null ? value : "");
|
|
496
|
+
}
|
|
497
|
+
formatCsvValue(value) {
|
|
498
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
499
|
+
const escaped = str.replace(/"/g, '""');
|
|
500
|
+
return `"${escaped}"`;
|
|
501
|
+
}
|
|
370
502
|
};
|
|
371
503
|
|
|
372
504
|
// src/services/documentation-templates/documentation-template.service.ts
|
|
@@ -1254,9 +1386,8 @@ var ProductService = class extends BaseService {
|
|
|
1254
1386
|
return snapshot.data().count;
|
|
1255
1387
|
}
|
|
1256
1388
|
/**
|
|
1257
|
-
* Gets counts of active products grouped by technology.
|
|
1258
|
-
*
|
|
1259
|
-
* Categories/subcategories not available in top-level structure.
|
|
1389
|
+
* Gets counts of active products grouped by category, subcategory, and technology.
|
|
1390
|
+
* Queries technology subcollections which have the legacy fields synced by Cloud Functions.
|
|
1260
1391
|
*/
|
|
1261
1392
|
async getProductCounts() {
|
|
1262
1393
|
const counts = {
|
|
@@ -1264,14 +1395,18 @@ var ProductService = class extends BaseService {
|
|
|
1264
1395
|
bySubcategory: {},
|
|
1265
1396
|
byTechnology: {}
|
|
1266
1397
|
};
|
|
1267
|
-
const q = query6(this.
|
|
1398
|
+
const q = query6(collectionGroup(this.db, PRODUCTS_COLLECTION), where6("isActive", "==", true));
|
|
1268
1399
|
const snapshot = await getDocs6(q);
|
|
1269
1400
|
snapshot.docs.forEach((doc11) => {
|
|
1270
1401
|
const product = doc11.data();
|
|
1271
|
-
if (product.
|
|
1272
|
-
product.
|
|
1273
|
-
|
|
1274
|
-
|
|
1402
|
+
if (product.categoryId) {
|
|
1403
|
+
counts.byCategory[product.categoryId] = (counts.byCategory[product.categoryId] || 0) + 1;
|
|
1404
|
+
}
|
|
1405
|
+
if (product.subcategoryId) {
|
|
1406
|
+
counts.bySubcategory[product.subcategoryId] = (counts.bySubcategory[product.subcategoryId] || 0) + 1;
|
|
1407
|
+
}
|
|
1408
|
+
if (product.technologyId) {
|
|
1409
|
+
counts.byTechnology[product.technologyId] = (counts.byTechnology[product.technologyId] || 0) + 1;
|
|
1275
1410
|
}
|
|
1276
1411
|
});
|
|
1277
1412
|
return counts;
|
|
@@ -1504,6 +1639,87 @@ var ProductService = class extends BaseService {
|
|
|
1504
1639
|
})
|
|
1505
1640
|
);
|
|
1506
1641
|
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Exports products to CSV string, suitable for Excel/Sheets.
|
|
1644
|
+
* Includes headers and optional UTF-8 BOM.
|
|
1645
|
+
* By default exports only active products (set includeInactive to true to export all).
|
|
1646
|
+
*/
|
|
1647
|
+
async exportToCsv(options) {
|
|
1648
|
+
var _a, _b;
|
|
1649
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
1650
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
1651
|
+
const headers = [
|
|
1652
|
+
"id",
|
|
1653
|
+
"name",
|
|
1654
|
+
"brandId",
|
|
1655
|
+
"brandName",
|
|
1656
|
+
"assignedTechnologyIds",
|
|
1657
|
+
"description",
|
|
1658
|
+
"technicalDetails",
|
|
1659
|
+
"dosage",
|
|
1660
|
+
"composition",
|
|
1661
|
+
"indications",
|
|
1662
|
+
"contraindications",
|
|
1663
|
+
"warnings",
|
|
1664
|
+
"isActive"
|
|
1665
|
+
];
|
|
1666
|
+
const rows = [];
|
|
1667
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
1668
|
+
const PAGE_SIZE = 1e3;
|
|
1669
|
+
let cursor;
|
|
1670
|
+
const constraints = [];
|
|
1671
|
+
if (!includeInactive) {
|
|
1672
|
+
constraints.push(where6("isActive", "==", true));
|
|
1673
|
+
}
|
|
1674
|
+
constraints.push(orderBy6("name"));
|
|
1675
|
+
while (true) {
|
|
1676
|
+
const queryConstraints = [...constraints, limit6(PAGE_SIZE)];
|
|
1677
|
+
if (cursor) queryConstraints.push(startAfter5(cursor));
|
|
1678
|
+
const q = query6(this.getTopLevelProductsRef(), ...queryConstraints);
|
|
1679
|
+
const snapshot = await getDocs6(q);
|
|
1680
|
+
if (snapshot.empty) break;
|
|
1681
|
+
for (const d of snapshot.docs) {
|
|
1682
|
+
const product = { id: d.id, ...d.data() };
|
|
1683
|
+
rows.push(this.productToCsvRow(product));
|
|
1684
|
+
}
|
|
1685
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
1686
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
1687
|
+
}
|
|
1688
|
+
const csvBody = rows.join("\r\n");
|
|
1689
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
1690
|
+
}
|
|
1691
|
+
productToCsvRow(product) {
|
|
1692
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
|
|
1693
|
+
const values = [
|
|
1694
|
+
(_a = product.id) != null ? _a : "",
|
|
1695
|
+
(_b = product.name) != null ? _b : "",
|
|
1696
|
+
(_c = product.brandId) != null ? _c : "",
|
|
1697
|
+
(_d = product.brandName) != null ? _d : "",
|
|
1698
|
+
(_f = (_e = product.assignedTechnologyIds) == null ? void 0 : _e.join(";")) != null ? _f : "",
|
|
1699
|
+
(_g = product.description) != null ? _g : "",
|
|
1700
|
+
(_h = product.technicalDetails) != null ? _h : "",
|
|
1701
|
+
(_i = product.dosage) != null ? _i : "",
|
|
1702
|
+
(_j = product.composition) != null ? _j : "",
|
|
1703
|
+
(_l = (_k = product.indications) == null ? void 0 : _k.join(";")) != null ? _l : "",
|
|
1704
|
+
(_n = (_m = product.contraindications) == null ? void 0 : _m.map((c) => c.name).join(";")) != null ? _n : "",
|
|
1705
|
+
(_p = (_o = product.warnings) == null ? void 0 : _o.join(";")) != null ? _p : "",
|
|
1706
|
+
String((_q = product.isActive) != null ? _q : "")
|
|
1707
|
+
];
|
|
1708
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
1709
|
+
}
|
|
1710
|
+
formatDateIso(value) {
|
|
1711
|
+
if (value instanceof Date) return value.toISOString();
|
|
1712
|
+
if (value && typeof value.toDate === "function") {
|
|
1713
|
+
const d = value.toDate();
|
|
1714
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
1715
|
+
}
|
|
1716
|
+
return String(value != null ? value : "");
|
|
1717
|
+
}
|
|
1718
|
+
formatCsvValue(value) {
|
|
1719
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
1720
|
+
const escaped = str.replace(/"/g, '""');
|
|
1721
|
+
return `"${escaped}"`;
|
|
1722
|
+
}
|
|
1507
1723
|
};
|
|
1508
1724
|
|
|
1509
1725
|
// src/backoffice/services/requirement.service.ts
|
|
@@ -1515,7 +1731,8 @@ import {
|
|
|
1515
1731
|
getDocs as getDocs7,
|
|
1516
1732
|
query as query7,
|
|
1517
1733
|
updateDoc as updateDoc7,
|
|
1518
|
-
where as where7
|
|
1734
|
+
where as where7,
|
|
1735
|
+
orderBy as orderBy7
|
|
1519
1736
|
} from "firebase/firestore";
|
|
1520
1737
|
|
|
1521
1738
|
// src/backoffice/types/requirement.types.ts
|
|
@@ -1636,6 +1853,65 @@ var RequirementService = class extends BaseService {
|
|
|
1636
1853
|
...docSnap.data()
|
|
1637
1854
|
};
|
|
1638
1855
|
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Exports requirements to CSV string, suitable for Excel/Sheets.
|
|
1858
|
+
* Includes headers and optional UTF-8 BOM.
|
|
1859
|
+
* By default exports only active requirements (set includeInactive to true to export all).
|
|
1860
|
+
*/
|
|
1861
|
+
async exportToCsv(options) {
|
|
1862
|
+
var _a, _b;
|
|
1863
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
1864
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
1865
|
+
const headers = [
|
|
1866
|
+
"id",
|
|
1867
|
+
"type",
|
|
1868
|
+
"name",
|
|
1869
|
+
"description",
|
|
1870
|
+
"timeframe_duration",
|
|
1871
|
+
"timeframe_unit",
|
|
1872
|
+
"timeframe_notifyAt",
|
|
1873
|
+
"importance",
|
|
1874
|
+
"isActive"
|
|
1875
|
+
];
|
|
1876
|
+
const rows = [];
|
|
1877
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
1878
|
+
const q = includeInactive ? query7(this.requirementsRef, orderBy7("name")) : query7(this.requirementsRef, where7("isActive", "==", true), orderBy7("name"));
|
|
1879
|
+
const snapshot = await getDocs7(q);
|
|
1880
|
+
for (const d of snapshot.docs) {
|
|
1881
|
+
const requirement = { id: d.id, ...d.data() };
|
|
1882
|
+
rows.push(this.requirementToCsvRow(requirement));
|
|
1883
|
+
}
|
|
1884
|
+
const csvBody = rows.join("\r\n");
|
|
1885
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
1886
|
+
}
|
|
1887
|
+
requirementToCsvRow(requirement) {
|
|
1888
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
1889
|
+
const values = [
|
|
1890
|
+
(_a = requirement.id) != null ? _a : "",
|
|
1891
|
+
(_b = requirement.type) != null ? _b : "",
|
|
1892
|
+
(_c = requirement.name) != null ? _c : "",
|
|
1893
|
+
(_d = requirement.description) != null ? _d : "",
|
|
1894
|
+
(_g = (_f = (_e = requirement.timeframe) == null ? void 0 : _e.duration) == null ? void 0 : _f.toString()) != null ? _g : "",
|
|
1895
|
+
(_i = (_h = requirement.timeframe) == null ? void 0 : _h.unit) != null ? _i : "",
|
|
1896
|
+
(_l = (_k = (_j = requirement.timeframe) == null ? void 0 : _j.notifyAt) == null ? void 0 : _k.join(";")) != null ? _l : "",
|
|
1897
|
+
(_m = requirement.importance) != null ? _m : "",
|
|
1898
|
+
String((_n = requirement.isActive) != null ? _n : "")
|
|
1899
|
+
];
|
|
1900
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
1901
|
+
}
|
|
1902
|
+
formatDateIso(value) {
|
|
1903
|
+
if (value instanceof Date) return value.toISOString();
|
|
1904
|
+
if (value && typeof value.toDate === "function") {
|
|
1905
|
+
const d = value.toDate();
|
|
1906
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
1907
|
+
}
|
|
1908
|
+
return String(value != null ? value : "");
|
|
1909
|
+
}
|
|
1910
|
+
formatCsvValue(value) {
|
|
1911
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
1912
|
+
const escaped = str.replace(/"/g, '""');
|
|
1913
|
+
return `"${escaped}"`;
|
|
1914
|
+
}
|
|
1639
1915
|
};
|
|
1640
1916
|
|
|
1641
1917
|
// src/backoffice/services/subcategory.service.ts
|
|
@@ -1649,7 +1925,7 @@ import {
|
|
|
1649
1925
|
getDoc as getDoc8,
|
|
1650
1926
|
getDocs as getDocs8,
|
|
1651
1927
|
limit as limit7,
|
|
1652
|
-
orderBy as
|
|
1928
|
+
orderBy as orderBy8,
|
|
1653
1929
|
query as query8,
|
|
1654
1930
|
setDoc as setDoc4,
|
|
1655
1931
|
startAfter as startAfter6,
|
|
@@ -1723,7 +1999,7 @@ var SubcategoryService = class extends BaseService {
|
|
|
1723
1999
|
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
1724
2000
|
const constraints = [
|
|
1725
2001
|
where8("isActive", "==", active),
|
|
1726
|
-
|
|
2002
|
+
orderBy8("name"),
|
|
1727
2003
|
queryLimit ? limit7(queryLimit) : void 0,
|
|
1728
2004
|
lastVisible ? startAfter6(lastVisible) : void 0
|
|
1729
2005
|
].filter((c) => !!c);
|
|
@@ -1750,7 +2026,7 @@ var SubcategoryService = class extends BaseService {
|
|
|
1750
2026
|
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
1751
2027
|
const constraints = [
|
|
1752
2028
|
where8("isActive", "==", active),
|
|
1753
|
-
|
|
2029
|
+
orderBy8("name"),
|
|
1754
2030
|
queryLimit ? limit7(queryLimit) : void 0,
|
|
1755
2031
|
lastVisible ? startAfter6(lastVisible) : void 0
|
|
1756
2032
|
].filter((c) => !!c);
|
|
@@ -1879,6 +2155,74 @@ var SubcategoryService = class extends BaseService {
|
|
|
1879
2155
|
...docSnap.data()
|
|
1880
2156
|
};
|
|
1881
2157
|
}
|
|
2158
|
+
/**
|
|
2159
|
+
* Exports subcategories to CSV string, suitable for Excel/Sheets.
|
|
2160
|
+
* Includes headers and optional UTF-8 BOM.
|
|
2161
|
+
* By default exports only active subcategories (set includeInactive to true to export all).
|
|
2162
|
+
*/
|
|
2163
|
+
async exportToCsv(options) {
|
|
2164
|
+
var _a, _b;
|
|
2165
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
2166
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
2167
|
+
const headers = [
|
|
2168
|
+
"id",
|
|
2169
|
+
"name",
|
|
2170
|
+
"categoryId",
|
|
2171
|
+
"description",
|
|
2172
|
+
"isActive"
|
|
2173
|
+
];
|
|
2174
|
+
const rows = [];
|
|
2175
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
2176
|
+
const PAGE_SIZE = 1e3;
|
|
2177
|
+
let cursor;
|
|
2178
|
+
const constraints = [];
|
|
2179
|
+
if (!includeInactive) {
|
|
2180
|
+
constraints.push(where8("isActive", "==", true));
|
|
2181
|
+
}
|
|
2182
|
+
constraints.push(orderBy8("name"));
|
|
2183
|
+
while (true) {
|
|
2184
|
+
const queryConstraints = [...constraints, limit7(PAGE_SIZE)];
|
|
2185
|
+
if (cursor) queryConstraints.push(startAfter6(cursor));
|
|
2186
|
+
const q = query8(
|
|
2187
|
+
collectionGroup2(this.db, SUBCATEGORIES_COLLECTION),
|
|
2188
|
+
...queryConstraints
|
|
2189
|
+
);
|
|
2190
|
+
const snapshot = await getDocs8(q);
|
|
2191
|
+
if (snapshot.empty) break;
|
|
2192
|
+
for (const d of snapshot.docs) {
|
|
2193
|
+
const subcategory = { id: d.id, ...d.data() };
|
|
2194
|
+
rows.push(this.subcategoryToCsvRow(subcategory));
|
|
2195
|
+
}
|
|
2196
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
2197
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
2198
|
+
}
|
|
2199
|
+
const csvBody = rows.join("\r\n");
|
|
2200
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
2201
|
+
}
|
|
2202
|
+
subcategoryToCsvRow(subcategory) {
|
|
2203
|
+
var _a, _b, _c, _d, _e;
|
|
2204
|
+
const values = [
|
|
2205
|
+
(_a = subcategory.id) != null ? _a : "",
|
|
2206
|
+
(_b = subcategory.name) != null ? _b : "",
|
|
2207
|
+
(_c = subcategory.categoryId) != null ? _c : "",
|
|
2208
|
+
(_d = subcategory.description) != null ? _d : "",
|
|
2209
|
+
String((_e = subcategory.isActive) != null ? _e : "")
|
|
2210
|
+
];
|
|
2211
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
2212
|
+
}
|
|
2213
|
+
formatDateIso(value) {
|
|
2214
|
+
if (value instanceof Date) return value.toISOString();
|
|
2215
|
+
if (value && typeof value.toDate === "function") {
|
|
2216
|
+
const d = value.toDate();
|
|
2217
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
2218
|
+
}
|
|
2219
|
+
return String(value != null ? value : "");
|
|
2220
|
+
}
|
|
2221
|
+
formatCsvValue(value) {
|
|
2222
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
2223
|
+
const escaped = str.replace(/"/g, '""');
|
|
2224
|
+
return `"${escaped}"`;
|
|
2225
|
+
}
|
|
1882
2226
|
};
|
|
1883
2227
|
|
|
1884
2228
|
// src/backoffice/services/technology.service.ts
|
|
@@ -1889,7 +2233,7 @@ import {
|
|
|
1889
2233
|
getDoc as getDoc9,
|
|
1890
2234
|
getDocs as getDocs9,
|
|
1891
2235
|
limit as limit8,
|
|
1892
|
-
orderBy as
|
|
2236
|
+
orderBy as orderBy9,
|
|
1893
2237
|
query as query9,
|
|
1894
2238
|
startAfter as startAfter7,
|
|
1895
2239
|
updateDoc as updateDoc9,
|
|
@@ -2003,7 +2347,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2003
2347
|
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
2004
2348
|
const constraints = [
|
|
2005
2349
|
where9("isActive", "==", active),
|
|
2006
|
-
|
|
2350
|
+
orderBy9("name"),
|
|
2007
2351
|
queryLimit ? limit8(queryLimit) : void 0,
|
|
2008
2352
|
lastVisible ? startAfter7(lastVisible) : void 0
|
|
2009
2353
|
].filter((c) => !!c);
|
|
@@ -2029,7 +2373,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2029
2373
|
const constraints = [
|
|
2030
2374
|
where9("categoryId", "==", categoryId),
|
|
2031
2375
|
where9("isActive", "==", active),
|
|
2032
|
-
|
|
2376
|
+
orderBy9("name"),
|
|
2033
2377
|
queryLimit ? limit8(queryLimit) : void 0,
|
|
2034
2378
|
lastVisible ? startAfter7(lastVisible) : void 0
|
|
2035
2379
|
].filter((c) => !!c);
|
|
@@ -2055,7 +2399,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2055
2399
|
const constraints = [
|
|
2056
2400
|
where9("subcategoryId", "==", subcategoryId),
|
|
2057
2401
|
where9("isActive", "==", active),
|
|
2058
|
-
|
|
2402
|
+
orderBy9("name"),
|
|
2059
2403
|
queryLimit ? limit8(queryLimit) : void 0,
|
|
2060
2404
|
lastVisible ? startAfter7(lastVisible) : void 0
|
|
2061
2405
|
].filter((c) => !!c);
|
|
@@ -2085,7 +2429,18 @@ var TechnologyService = class extends BaseService {
|
|
|
2085
2429
|
});
|
|
2086
2430
|
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
2087
2431
|
const docRef = doc9(this.technologiesRef, id);
|
|
2432
|
+
const beforeTech = await this.getById(id);
|
|
2088
2433
|
await updateDoc9(docRef, updateData);
|
|
2434
|
+
const categoryChanged = beforeTech && updateData.categoryId && beforeTech.categoryId !== updateData.categoryId;
|
|
2435
|
+
const subcategoryChanged = beforeTech && updateData.subcategoryId && beforeTech.subcategoryId !== updateData.subcategoryId;
|
|
2436
|
+
const nameChanged = beforeTech && updateData.name && beforeTech.name !== updateData.name;
|
|
2437
|
+
if (categoryChanged || subcategoryChanged || nameChanged) {
|
|
2438
|
+
await this.updateProductsInSubcollection(id, {
|
|
2439
|
+
categoryId: updateData.categoryId,
|
|
2440
|
+
subcategoryId: updateData.subcategoryId,
|
|
2441
|
+
technologyName: updateData.name
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2089
2444
|
return this.getById(id);
|
|
2090
2445
|
}
|
|
2091
2446
|
/**
|
|
@@ -2473,7 +2828,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2473
2828
|
collection9(this.db, TECHNOLOGIES_COLLECTION),
|
|
2474
2829
|
where9("isActive", "==", true),
|
|
2475
2830
|
where9("subcategoryId", "==", subcategoryId),
|
|
2476
|
-
|
|
2831
|
+
orderBy9("name")
|
|
2477
2832
|
);
|
|
2478
2833
|
const snapshot = await getDocs9(q);
|
|
2479
2834
|
return snapshot.docs.map(
|
|
@@ -2494,7 +2849,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2494
2849
|
where9("isActive", "==", true),
|
|
2495
2850
|
where9("categoryId", "==", categoryId),
|
|
2496
2851
|
where9("subcategoryId", "==", subcategoryId),
|
|
2497
|
-
|
|
2852
|
+
orderBy9("name")
|
|
2498
2853
|
);
|
|
2499
2854
|
const snapshot = await getDocs9(q);
|
|
2500
2855
|
return snapshot.docs.map(
|
|
@@ -2511,7 +2866,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2511
2866
|
const q = query9(
|
|
2512
2867
|
collection9(this.db, TECHNOLOGIES_COLLECTION),
|
|
2513
2868
|
where9("isActive", "==", true),
|
|
2514
|
-
|
|
2869
|
+
orderBy9("name")
|
|
2515
2870
|
);
|
|
2516
2871
|
const snapshot = await getDocs9(q);
|
|
2517
2872
|
return snapshot.docs.map(
|
|
@@ -2563,7 +2918,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2563
2918
|
collection9(this.db, PRODUCTS_COLLECTION),
|
|
2564
2919
|
where9("assignedTechnologyIds", "array-contains", technologyId),
|
|
2565
2920
|
where9("isActive", "==", true),
|
|
2566
|
-
|
|
2921
|
+
orderBy9("name")
|
|
2567
2922
|
);
|
|
2568
2923
|
const snapshot = await getDocs9(q);
|
|
2569
2924
|
return snapshot.docs.map(
|
|
@@ -2580,7 +2935,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2580
2935
|
const q = query9(
|
|
2581
2936
|
collection9(this.db, PRODUCTS_COLLECTION),
|
|
2582
2937
|
where9("isActive", "==", true),
|
|
2583
|
-
|
|
2938
|
+
orderBy9("name")
|
|
2584
2939
|
);
|
|
2585
2940
|
const snapshot = await getDocs9(q);
|
|
2586
2941
|
const allProducts = snapshot.docs.map(
|
|
@@ -2610,6 +2965,136 @@ var TechnologyService = class extends BaseService {
|
|
|
2610
2965
|
byBrand
|
|
2611
2966
|
};
|
|
2612
2967
|
}
|
|
2968
|
+
/**
|
|
2969
|
+
* Updates products in technology subcollection when technology metadata changes
|
|
2970
|
+
* @param technologyId - ID of the technology
|
|
2971
|
+
* @param updates - Fields to update (categoryId, subcategoryId, technologyName)
|
|
2972
|
+
*/
|
|
2973
|
+
async updateProductsInSubcollection(technologyId, updates) {
|
|
2974
|
+
const productsRef = collection9(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
|
|
2975
|
+
const productsSnapshot = await getDocs9(productsRef);
|
|
2976
|
+
if (productsSnapshot.empty) {
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
const batch = writeBatch(this.db);
|
|
2980
|
+
for (const productDoc of productsSnapshot.docs) {
|
|
2981
|
+
const productRef = productDoc.ref;
|
|
2982
|
+
const updateFields = {};
|
|
2983
|
+
if (updates.categoryId !== void 0) {
|
|
2984
|
+
updateFields.categoryId = updates.categoryId;
|
|
2985
|
+
}
|
|
2986
|
+
if (updates.subcategoryId !== void 0) {
|
|
2987
|
+
updateFields.subcategoryId = updates.subcategoryId;
|
|
2988
|
+
}
|
|
2989
|
+
if (updates.technologyName !== void 0) {
|
|
2990
|
+
updateFields.technologyName = updates.technologyName;
|
|
2991
|
+
}
|
|
2992
|
+
if (Object.keys(updateFields).length > 0) {
|
|
2993
|
+
batch.update(productRef, updateFields);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
await batch.commit();
|
|
2997
|
+
}
|
|
2998
|
+
/**
|
|
2999
|
+
* Exports technologies to CSV string, suitable for Excel/Sheets.
|
|
3000
|
+
* Includes headers and optional UTF-8 BOM.
|
|
3001
|
+
* By default exports only active technologies (set includeInactive to true to export all).
|
|
3002
|
+
* Includes product names from subcollections.
|
|
3003
|
+
*/
|
|
3004
|
+
async exportToCsv(options) {
|
|
3005
|
+
var _a, _b;
|
|
3006
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
3007
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
3008
|
+
const headers = [
|
|
3009
|
+
"id",
|
|
3010
|
+
"name",
|
|
3011
|
+
"description",
|
|
3012
|
+
"family",
|
|
3013
|
+
"categoryId",
|
|
3014
|
+
"subcategoryId",
|
|
3015
|
+
"technicalDetails",
|
|
3016
|
+
"requirements_pre",
|
|
3017
|
+
"requirements_post",
|
|
3018
|
+
"blockingConditions",
|
|
3019
|
+
"contraindications",
|
|
3020
|
+
"benefits",
|
|
3021
|
+
"certificationMinimumLevel",
|
|
3022
|
+
"certificationRequiredSpecialties",
|
|
3023
|
+
"documentationTemplateIds",
|
|
3024
|
+
"productNames",
|
|
3025
|
+
"isActive"
|
|
3026
|
+
];
|
|
3027
|
+
const rows = [];
|
|
3028
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
3029
|
+
const PAGE_SIZE = 1e3;
|
|
3030
|
+
let cursor;
|
|
3031
|
+
const constraints = [];
|
|
3032
|
+
if (!includeInactive) {
|
|
3033
|
+
constraints.push(where9("isActive", "==", true));
|
|
3034
|
+
}
|
|
3035
|
+
constraints.push(orderBy9("name"));
|
|
3036
|
+
while (true) {
|
|
3037
|
+
const queryConstraints = [...constraints, limit8(PAGE_SIZE)];
|
|
3038
|
+
if (cursor) queryConstraints.push(startAfter7(cursor));
|
|
3039
|
+
const q = query9(this.technologiesRef, ...queryConstraints);
|
|
3040
|
+
const snapshot = await getDocs9(q);
|
|
3041
|
+
if (snapshot.empty) break;
|
|
3042
|
+
for (const d of snapshot.docs) {
|
|
3043
|
+
const technology = { id: d.id, ...d.data() };
|
|
3044
|
+
const productNames = await this.getProductNamesForTechnology(technology.id);
|
|
3045
|
+
rows.push(this.technologyToCsvRow(technology, productNames));
|
|
3046
|
+
}
|
|
3047
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
3048
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
3049
|
+
}
|
|
3050
|
+
const csvBody = rows.join("\r\n");
|
|
3051
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
3052
|
+
}
|
|
3053
|
+
/**
|
|
3054
|
+
* Gets product names from the technology's product subcollection
|
|
3055
|
+
*/
|
|
3056
|
+
async getProductNamesForTechnology(technologyId) {
|
|
3057
|
+
try {
|
|
3058
|
+
const productsRef = collection9(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
|
|
3059
|
+
const q = query9(productsRef, where9("isActive", "==", true));
|
|
3060
|
+
const snapshot = await getDocs9(q);
|
|
3061
|
+
return snapshot.docs.map((doc11) => {
|
|
3062
|
+
const product = doc11.data();
|
|
3063
|
+
return product.name || "";
|
|
3064
|
+
}).filter((name) => name);
|
|
3065
|
+
} catch (error) {
|
|
3066
|
+
console.error(`Error fetching products for technology ${technologyId}:`, error);
|
|
3067
|
+
return [];
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
technologyToCsvRow(technology, productNames = []) {
|
|
3071
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A;
|
|
3072
|
+
const values = [
|
|
3073
|
+
(_a = technology.id) != null ? _a : "",
|
|
3074
|
+
(_b = technology.name) != null ? _b : "",
|
|
3075
|
+
(_c = technology.description) != null ? _c : "",
|
|
3076
|
+
(_d = technology.family) != null ? _d : "",
|
|
3077
|
+
(_e = technology.categoryId) != null ? _e : "",
|
|
3078
|
+
(_f = technology.subcategoryId) != null ? _f : "",
|
|
3079
|
+
(_g = technology.technicalDetails) != null ? _g : "",
|
|
3080
|
+
(_j = (_i = (_h = technology.requirements) == null ? void 0 : _h.pre) == null ? void 0 : _i.map((r) => r.name).join(";")) != null ? _j : "",
|
|
3081
|
+
(_m = (_l = (_k = technology.requirements) == null ? void 0 : _k.post) == null ? void 0 : _l.map((r) => r.name).join(";")) != null ? _m : "",
|
|
3082
|
+
(_o = (_n = technology.blockingConditions) == null ? void 0 : _n.join(";")) != null ? _o : "",
|
|
3083
|
+
(_q = (_p = technology.contraindications) == null ? void 0 : _p.map((c) => c.name).join(";")) != null ? _q : "",
|
|
3084
|
+
(_s = (_r = technology.benefits) == null ? void 0 : _r.map((b) => b.name).join(";")) != null ? _s : "",
|
|
3085
|
+
(_u = (_t = technology.certificationRequirement) == null ? void 0 : _t.minimumLevel) != null ? _u : "",
|
|
3086
|
+
(_x = (_w = (_v = technology.certificationRequirement) == null ? void 0 : _v.requiredSpecialties) == null ? void 0 : _w.join(";")) != null ? _x : "",
|
|
3087
|
+
(_z = (_y = technology.documentationTemplates) == null ? void 0 : _y.map((t) => t.templateId).join(";")) != null ? _z : "",
|
|
3088
|
+
productNames.join(";"),
|
|
3089
|
+
String((_A = technology.isActive) != null ? _A : "")
|
|
3090
|
+
];
|
|
3091
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
3092
|
+
}
|
|
3093
|
+
formatCsvValue(value) {
|
|
3094
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
3095
|
+
const escaped = str.replace(/"/g, '""');
|
|
3096
|
+
return `"${escaped}"`;
|
|
3097
|
+
}
|
|
2613
3098
|
};
|
|
2614
3099
|
|
|
2615
3100
|
// src/backoffice/services/constants.service.ts
|
|
@@ -2849,6 +3334,66 @@ var ConstantsService = class extends BaseService {
|
|
|
2849
3334
|
contraindications: arrayRemove3(toRemove)
|
|
2850
3335
|
});
|
|
2851
3336
|
}
|
|
3337
|
+
// =================================================================
|
|
3338
|
+
// CSV Export Methods
|
|
3339
|
+
// =================================================================
|
|
3340
|
+
/**
|
|
3341
|
+
* Exports treatment benefits to CSV string, suitable for Excel/Sheets.
|
|
3342
|
+
* Includes headers and optional UTF-8 BOM.
|
|
3343
|
+
*/
|
|
3344
|
+
async exportBenefitsToCsv(options) {
|
|
3345
|
+
var _a;
|
|
3346
|
+
const includeBom = (_a = options == null ? void 0 : options.includeBom) != null ? _a : true;
|
|
3347
|
+
const headers = ["id", "name", "description"];
|
|
3348
|
+
const rows = [];
|
|
3349
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
3350
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
3351
|
+
for (const benefit of benefits) {
|
|
3352
|
+
rows.push(this.benefitToCsvRow(benefit));
|
|
3353
|
+
}
|
|
3354
|
+
const csvBody = rows.join("\r\n");
|
|
3355
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
3356
|
+
}
|
|
3357
|
+
/**
|
|
3358
|
+
* Exports contraindications to CSV string, suitable for Excel/Sheets.
|
|
3359
|
+
* Includes headers and optional UTF-8 BOM.
|
|
3360
|
+
*/
|
|
3361
|
+
async exportContraindicationsToCsv(options) {
|
|
3362
|
+
var _a;
|
|
3363
|
+
const includeBom = (_a = options == null ? void 0 : options.includeBom) != null ? _a : true;
|
|
3364
|
+
const headers = ["id", "name", "description"];
|
|
3365
|
+
const rows = [];
|
|
3366
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
3367
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
3368
|
+
for (const contraindication of contraindications) {
|
|
3369
|
+
rows.push(this.contraindicationToCsvRow(contraindication));
|
|
3370
|
+
}
|
|
3371
|
+
const csvBody = rows.join("\r\n");
|
|
3372
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
3373
|
+
}
|
|
3374
|
+
benefitToCsvRow(benefit) {
|
|
3375
|
+
var _a, _b, _c;
|
|
3376
|
+
const values = [
|
|
3377
|
+
(_a = benefit.id) != null ? _a : "",
|
|
3378
|
+
(_b = benefit.name) != null ? _b : "",
|
|
3379
|
+
(_c = benefit.description) != null ? _c : ""
|
|
3380
|
+
];
|
|
3381
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
3382
|
+
}
|
|
3383
|
+
contraindicationToCsvRow(contraindication) {
|
|
3384
|
+
var _a, _b, _c;
|
|
3385
|
+
const values = [
|
|
3386
|
+
(_a = contraindication.id) != null ? _a : "",
|
|
3387
|
+
(_b = contraindication.name) != null ? _b : "",
|
|
3388
|
+
(_c = contraindication.description) != null ? _c : ""
|
|
3389
|
+
];
|
|
3390
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
3391
|
+
}
|
|
3392
|
+
formatCsvValue(value) {
|
|
3393
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
3394
|
+
const escaped = str.replace(/"/g, '""');
|
|
3395
|
+
return `"${escaped}"`;
|
|
3396
|
+
}
|
|
2852
3397
|
};
|
|
2853
3398
|
|
|
2854
3399
|
// src/backoffice/types/static/blocking-condition.types.ts
|