@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.
@@ -261,6 +261,73 @@ var BrandService = class extends BaseService {
261
261
  ...docSnap.data()
262
262
  };
263
263
  }
264
+ /**
265
+ * Exports brands to CSV string, suitable for Excel/Sheets.
266
+ * Includes headers and optional UTF-8 BOM.
267
+ * By default exports only active brands (set includeInactive to true to export all).
268
+ */
269
+ async exportToCsv(options) {
270
+ var _a, _b;
271
+ const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
272
+ const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
273
+ const headers = [
274
+ "id",
275
+ "name",
276
+ "manufacturer",
277
+ "website",
278
+ "description",
279
+ "isActive"
280
+ ];
281
+ const rows = [];
282
+ rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
283
+ const PAGE_SIZE = 1e3;
284
+ let cursor;
285
+ const baseConstraints = [];
286
+ if (!includeInactive) {
287
+ baseConstraints.push((0, import_firestore.where)("isActive", "==", true));
288
+ }
289
+ baseConstraints.push((0, import_firestore.orderBy)("name_lowercase"));
290
+ while (true) {
291
+ const constraints = [...baseConstraints, (0, import_firestore.limit)(PAGE_SIZE)];
292
+ if (cursor) constraints.push((0, import_firestore.startAfter)(cursor));
293
+ const q = (0, import_firestore.query)(this.getBrandsRef(), ...constraints);
294
+ const snapshot = await (0, import_firestore.getDocs)(q);
295
+ if (snapshot.empty) break;
296
+ for (const d of snapshot.docs) {
297
+ const brand = { id: d.id, ...d.data() };
298
+ rows.push(this.brandToCsvRow(brand));
299
+ }
300
+ cursor = snapshot.docs[snapshot.docs.length - 1];
301
+ if (snapshot.size < PAGE_SIZE) break;
302
+ }
303
+ const csvBody = rows.join("\r\n");
304
+ return includeBom ? "\uFEFF" + csvBody : csvBody;
305
+ }
306
+ brandToCsvRow(brand) {
307
+ var _a, _b, _c, _d, _e, _f;
308
+ const values = [
309
+ (_a = brand.id) != null ? _a : "",
310
+ (_b = brand.name) != null ? _b : "",
311
+ (_c = brand.manufacturer) != null ? _c : "",
312
+ (_d = brand.website) != null ? _d : "",
313
+ (_e = brand.description) != null ? _e : "",
314
+ String((_f = brand.isActive) != null ? _f : "")
315
+ ];
316
+ return values.map((v) => this.formatCsvValue(v)).join(",");
317
+ }
318
+ formatDateIso(value) {
319
+ if (value instanceof Date) return value.toISOString();
320
+ if (value && typeof value.toDate === "function") {
321
+ const d = value.toDate();
322
+ return d instanceof Date ? d.toISOString() : String(value);
323
+ }
324
+ return String(value != null ? value : "");
325
+ }
326
+ formatCsvValue(value) {
327
+ const str = value === null || value === void 0 ? "" : String(value);
328
+ const escaped = str.replace(/"/g, '""');
329
+ return `"${escaped}"`;
330
+ }
264
331
  };
265
332
 
266
333
  // src/backoffice/services/category.service.ts
@@ -446,6 +513,71 @@ var CategoryService = class extends BaseService {
446
513
  ...docSnap.data()
447
514
  };
448
515
  }
516
+ /**
517
+ * Exports categories to CSV string, suitable for Excel/Sheets.
518
+ * Includes headers and optional UTF-8 BOM.
519
+ * By default exports only active categories (set includeInactive to true to export all).
520
+ */
521
+ async exportToCsv(options) {
522
+ var _a, _b;
523
+ const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
524
+ const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
525
+ const headers = [
526
+ "id",
527
+ "name",
528
+ "description",
529
+ "family",
530
+ "isActive"
531
+ ];
532
+ const rows = [];
533
+ rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
534
+ const PAGE_SIZE = 1e3;
535
+ let cursor;
536
+ const constraints = [];
537
+ if (!includeInactive) {
538
+ constraints.push((0, import_firestore2.where)("isActive", "==", true));
539
+ }
540
+ constraints.push((0, import_firestore2.orderBy)("name"));
541
+ while (true) {
542
+ const queryConstraints = [...constraints, (0, import_firestore2.limit)(PAGE_SIZE)];
543
+ if (cursor) queryConstraints.push((0, import_firestore2.startAfter)(cursor));
544
+ const q = (0, import_firestore2.query)(this.categoriesRef, ...queryConstraints);
545
+ const snapshot = await (0, import_firestore2.getDocs)(q);
546
+ if (snapshot.empty) break;
547
+ for (const d of snapshot.docs) {
548
+ const category = { id: d.id, ...d.data() };
549
+ rows.push(this.categoryToCsvRow(category));
550
+ }
551
+ cursor = snapshot.docs[snapshot.docs.length - 1];
552
+ if (snapshot.size < PAGE_SIZE) break;
553
+ }
554
+ const csvBody = rows.join("\r\n");
555
+ return includeBom ? "\uFEFF" + csvBody : csvBody;
556
+ }
557
+ categoryToCsvRow(category) {
558
+ var _a, _b, _c, _d, _e;
559
+ const values = [
560
+ (_a = category.id) != null ? _a : "",
561
+ (_b = category.name) != null ? _b : "",
562
+ (_c = category.description) != null ? _c : "",
563
+ (_d = category.family) != null ? _d : "",
564
+ String((_e = category.isActive) != null ? _e : "")
565
+ ];
566
+ return values.map((v) => this.formatCsvValue(v)).join(",");
567
+ }
568
+ formatDateIso(value) {
569
+ if (value instanceof Date) return value.toISOString();
570
+ if (value && typeof value.toDate === "function") {
571
+ const d = value.toDate();
572
+ return d instanceof Date ? d.toISOString() : String(value);
573
+ }
574
+ return String(value != null ? value : "");
575
+ }
576
+ formatCsvValue(value) {
577
+ const str = value === null || value === void 0 ? "" : String(value);
578
+ const escaped = str.replace(/"/g, '""');
579
+ return `"${escaped}"`;
580
+ }
449
581
  };
450
582
 
451
583
  // src/services/documentation-templates/documentation-template.service.ts
@@ -1275,9 +1407,8 @@ var ProductService = class extends BaseService {
1275
1407
  return snapshot.data().count;
1276
1408
  }
1277
1409
  /**
1278
- * Gets counts of active products grouped by technology.
1279
- * NOTE: Only counts top-level collection to avoid duplication during migration.
1280
- * Categories/subcategories not available in top-level structure.
1410
+ * Gets counts of active products grouped by category, subcategory, and technology.
1411
+ * Queries technology subcollections which have the legacy fields synced by Cloud Functions.
1281
1412
  */
1282
1413
  async getProductCounts() {
1283
1414
  const counts = {
@@ -1285,14 +1416,18 @@ var ProductService = class extends BaseService {
1285
1416
  bySubcategory: {},
1286
1417
  byTechnology: {}
1287
1418
  };
1288
- const q = (0, import_firestore8.query)(this.getTopLevelProductsRef(), (0, import_firestore8.where)("isActive", "==", true));
1419
+ const q = (0, import_firestore8.query)((0, import_firestore8.collectionGroup)(this.db, PRODUCTS_COLLECTION), (0, import_firestore8.where)("isActive", "==", true));
1289
1420
  const snapshot = await (0, import_firestore8.getDocs)(q);
1290
1421
  snapshot.docs.forEach((doc11) => {
1291
1422
  const product = doc11.data();
1292
- if (product.assignedTechnologyIds && Array.isArray(product.assignedTechnologyIds)) {
1293
- product.assignedTechnologyIds.forEach((techId) => {
1294
- counts.byTechnology[techId] = (counts.byTechnology[techId] || 0) + 1;
1295
- });
1423
+ if (product.categoryId) {
1424
+ counts.byCategory[product.categoryId] = (counts.byCategory[product.categoryId] || 0) + 1;
1425
+ }
1426
+ if (product.subcategoryId) {
1427
+ counts.bySubcategory[product.subcategoryId] = (counts.bySubcategory[product.subcategoryId] || 0) + 1;
1428
+ }
1429
+ if (product.technologyId) {
1430
+ counts.byTechnology[product.technologyId] = (counts.byTechnology[product.technologyId] || 0) + 1;
1296
1431
  }
1297
1432
  });
1298
1433
  return counts;
@@ -1525,6 +1660,87 @@ var ProductService = class extends BaseService {
1525
1660
  })
1526
1661
  );
1527
1662
  }
1663
+ /**
1664
+ * Exports products to CSV string, suitable for Excel/Sheets.
1665
+ * Includes headers and optional UTF-8 BOM.
1666
+ * By default exports only active products (set includeInactive to true to export all).
1667
+ */
1668
+ async exportToCsv(options) {
1669
+ var _a, _b;
1670
+ const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
1671
+ const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
1672
+ const headers = [
1673
+ "id",
1674
+ "name",
1675
+ "brandId",
1676
+ "brandName",
1677
+ "assignedTechnologyIds",
1678
+ "description",
1679
+ "technicalDetails",
1680
+ "dosage",
1681
+ "composition",
1682
+ "indications",
1683
+ "contraindications",
1684
+ "warnings",
1685
+ "isActive"
1686
+ ];
1687
+ const rows = [];
1688
+ rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
1689
+ const PAGE_SIZE = 1e3;
1690
+ let cursor;
1691
+ const constraints = [];
1692
+ if (!includeInactive) {
1693
+ constraints.push((0, import_firestore8.where)("isActive", "==", true));
1694
+ }
1695
+ constraints.push((0, import_firestore8.orderBy)("name"));
1696
+ while (true) {
1697
+ const queryConstraints = [...constraints, (0, import_firestore8.limit)(PAGE_SIZE)];
1698
+ if (cursor) queryConstraints.push((0, import_firestore8.startAfter)(cursor));
1699
+ const q = (0, import_firestore8.query)(this.getTopLevelProductsRef(), ...queryConstraints);
1700
+ const snapshot = await (0, import_firestore8.getDocs)(q);
1701
+ if (snapshot.empty) break;
1702
+ for (const d of snapshot.docs) {
1703
+ const product = { id: d.id, ...d.data() };
1704
+ rows.push(this.productToCsvRow(product));
1705
+ }
1706
+ cursor = snapshot.docs[snapshot.docs.length - 1];
1707
+ if (snapshot.size < PAGE_SIZE) break;
1708
+ }
1709
+ const csvBody = rows.join("\r\n");
1710
+ return includeBom ? "\uFEFF" + csvBody : csvBody;
1711
+ }
1712
+ productToCsvRow(product) {
1713
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
1714
+ const values = [
1715
+ (_a = product.id) != null ? _a : "",
1716
+ (_b = product.name) != null ? _b : "",
1717
+ (_c = product.brandId) != null ? _c : "",
1718
+ (_d = product.brandName) != null ? _d : "",
1719
+ (_f = (_e = product.assignedTechnologyIds) == null ? void 0 : _e.join(";")) != null ? _f : "",
1720
+ (_g = product.description) != null ? _g : "",
1721
+ (_h = product.technicalDetails) != null ? _h : "",
1722
+ (_i = product.dosage) != null ? _i : "",
1723
+ (_j = product.composition) != null ? _j : "",
1724
+ (_l = (_k = product.indications) == null ? void 0 : _k.join(";")) != null ? _l : "",
1725
+ (_n = (_m = product.contraindications) == null ? void 0 : _m.map((c) => c.name).join(";")) != null ? _n : "",
1726
+ (_p = (_o = product.warnings) == null ? void 0 : _o.join(";")) != null ? _p : "",
1727
+ String((_q = product.isActive) != null ? _q : "")
1728
+ ];
1729
+ return values.map((v) => this.formatCsvValue(v)).join(",");
1730
+ }
1731
+ formatDateIso(value) {
1732
+ if (value instanceof Date) return value.toISOString();
1733
+ if (value && typeof value.toDate === "function") {
1734
+ const d = value.toDate();
1735
+ return d instanceof Date ? d.toISOString() : String(value);
1736
+ }
1737
+ return String(value != null ? value : "");
1738
+ }
1739
+ formatCsvValue(value) {
1740
+ const str = value === null || value === void 0 ? "" : String(value);
1741
+ const escaped = str.replace(/"/g, '""');
1742
+ return `"${escaped}"`;
1743
+ }
1528
1744
  };
1529
1745
 
1530
1746
  // src/backoffice/services/requirement.service.ts
@@ -1648,6 +1864,65 @@ var RequirementService = class extends BaseService {
1648
1864
  ...docSnap.data()
1649
1865
  };
1650
1866
  }
1867
+ /**
1868
+ * Exports requirements to CSV string, suitable for Excel/Sheets.
1869
+ * Includes headers and optional UTF-8 BOM.
1870
+ * By default exports only active requirements (set includeInactive to true to export all).
1871
+ */
1872
+ async exportToCsv(options) {
1873
+ var _a, _b;
1874
+ const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
1875
+ const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
1876
+ const headers = [
1877
+ "id",
1878
+ "type",
1879
+ "name",
1880
+ "description",
1881
+ "timeframe_duration",
1882
+ "timeframe_unit",
1883
+ "timeframe_notifyAt",
1884
+ "importance",
1885
+ "isActive"
1886
+ ];
1887
+ const rows = [];
1888
+ rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
1889
+ const q = includeInactive ? (0, import_firestore9.query)(this.requirementsRef, (0, import_firestore9.orderBy)("name")) : (0, import_firestore9.query)(this.requirementsRef, (0, import_firestore9.where)("isActive", "==", true), (0, import_firestore9.orderBy)("name"));
1890
+ const snapshot = await (0, import_firestore9.getDocs)(q);
1891
+ for (const d of snapshot.docs) {
1892
+ const requirement = { id: d.id, ...d.data() };
1893
+ rows.push(this.requirementToCsvRow(requirement));
1894
+ }
1895
+ const csvBody = rows.join("\r\n");
1896
+ return includeBom ? "\uFEFF" + csvBody : csvBody;
1897
+ }
1898
+ requirementToCsvRow(requirement) {
1899
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
1900
+ const values = [
1901
+ (_a = requirement.id) != null ? _a : "",
1902
+ (_b = requirement.type) != null ? _b : "",
1903
+ (_c = requirement.name) != null ? _c : "",
1904
+ (_d = requirement.description) != null ? _d : "",
1905
+ (_g = (_f = (_e = requirement.timeframe) == null ? void 0 : _e.duration) == null ? void 0 : _f.toString()) != null ? _g : "",
1906
+ (_i = (_h = requirement.timeframe) == null ? void 0 : _h.unit) != null ? _i : "",
1907
+ (_l = (_k = (_j = requirement.timeframe) == null ? void 0 : _j.notifyAt) == null ? void 0 : _k.join(";")) != null ? _l : "",
1908
+ (_m = requirement.importance) != null ? _m : "",
1909
+ String((_n = requirement.isActive) != null ? _n : "")
1910
+ ];
1911
+ return values.map((v) => this.formatCsvValue(v)).join(",");
1912
+ }
1913
+ formatDateIso(value) {
1914
+ if (value instanceof Date) return value.toISOString();
1915
+ if (value && typeof value.toDate === "function") {
1916
+ const d = value.toDate();
1917
+ return d instanceof Date ? d.toISOString() : String(value);
1918
+ }
1919
+ return String(value != null ? value : "");
1920
+ }
1921
+ formatCsvValue(value) {
1922
+ const str = value === null || value === void 0 ? "" : String(value);
1923
+ const escaped = str.replace(/"/g, '""');
1924
+ return `"${escaped}"`;
1925
+ }
1651
1926
  };
1652
1927
 
1653
1928
  // src/backoffice/services/subcategory.service.ts
@@ -1875,6 +2150,74 @@ var SubcategoryService = class extends BaseService {
1875
2150
  ...docSnap.data()
1876
2151
  };
1877
2152
  }
2153
+ /**
2154
+ * Exports subcategories to CSV string, suitable for Excel/Sheets.
2155
+ * Includes headers and optional UTF-8 BOM.
2156
+ * By default exports only active subcategories (set includeInactive to true to export all).
2157
+ */
2158
+ async exportToCsv(options) {
2159
+ var _a, _b;
2160
+ const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
2161
+ const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
2162
+ const headers = [
2163
+ "id",
2164
+ "name",
2165
+ "categoryId",
2166
+ "description",
2167
+ "isActive"
2168
+ ];
2169
+ const rows = [];
2170
+ rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
2171
+ const PAGE_SIZE = 1e3;
2172
+ let cursor;
2173
+ const constraints = [];
2174
+ if (!includeInactive) {
2175
+ constraints.push((0, import_firestore10.where)("isActive", "==", true));
2176
+ }
2177
+ constraints.push((0, import_firestore10.orderBy)("name"));
2178
+ while (true) {
2179
+ const queryConstraints = [...constraints, (0, import_firestore10.limit)(PAGE_SIZE)];
2180
+ if (cursor) queryConstraints.push((0, import_firestore10.startAfter)(cursor));
2181
+ const q = (0, import_firestore10.query)(
2182
+ (0, import_firestore10.collectionGroup)(this.db, SUBCATEGORIES_COLLECTION),
2183
+ ...queryConstraints
2184
+ );
2185
+ const snapshot = await (0, import_firestore10.getDocs)(q);
2186
+ if (snapshot.empty) break;
2187
+ for (const d of snapshot.docs) {
2188
+ const subcategory = { id: d.id, ...d.data() };
2189
+ rows.push(this.subcategoryToCsvRow(subcategory));
2190
+ }
2191
+ cursor = snapshot.docs[snapshot.docs.length - 1];
2192
+ if (snapshot.size < PAGE_SIZE) break;
2193
+ }
2194
+ const csvBody = rows.join("\r\n");
2195
+ return includeBom ? "\uFEFF" + csvBody : csvBody;
2196
+ }
2197
+ subcategoryToCsvRow(subcategory) {
2198
+ var _a, _b, _c, _d, _e;
2199
+ const values = [
2200
+ (_a = subcategory.id) != null ? _a : "",
2201
+ (_b = subcategory.name) != null ? _b : "",
2202
+ (_c = subcategory.categoryId) != null ? _c : "",
2203
+ (_d = subcategory.description) != null ? _d : "",
2204
+ String((_e = subcategory.isActive) != null ? _e : "")
2205
+ ];
2206
+ return values.map((v) => this.formatCsvValue(v)).join(",");
2207
+ }
2208
+ formatDateIso(value) {
2209
+ if (value instanceof Date) return value.toISOString();
2210
+ if (value && typeof value.toDate === "function") {
2211
+ const d = value.toDate();
2212
+ return d instanceof Date ? d.toISOString() : String(value);
2213
+ }
2214
+ return String(value != null ? value : "");
2215
+ }
2216
+ formatCsvValue(value) {
2217
+ const str = value === null || value === void 0 ? "" : String(value);
2218
+ const escaped = str.replace(/"/g, '""');
2219
+ return `"${escaped}"`;
2220
+ }
1878
2221
  };
1879
2222
 
1880
2223
  // src/backoffice/services/technology.service.ts
@@ -2066,7 +2409,18 @@ var TechnologyService = class extends BaseService {
2066
2409
  });
2067
2410
  updateData.updatedAt = /* @__PURE__ */ new Date();
2068
2411
  const docRef = (0, import_firestore11.doc)(this.technologiesRef, id);
2412
+ const beforeTech = await this.getById(id);
2069
2413
  await (0, import_firestore11.updateDoc)(docRef, updateData);
2414
+ const categoryChanged = beforeTech && updateData.categoryId && beforeTech.categoryId !== updateData.categoryId;
2415
+ const subcategoryChanged = beforeTech && updateData.subcategoryId && beforeTech.subcategoryId !== updateData.subcategoryId;
2416
+ const nameChanged = beforeTech && updateData.name && beforeTech.name !== updateData.name;
2417
+ if (categoryChanged || subcategoryChanged || nameChanged) {
2418
+ await this.updateProductsInSubcollection(id, {
2419
+ categoryId: updateData.categoryId,
2420
+ subcategoryId: updateData.subcategoryId,
2421
+ technologyName: updateData.name
2422
+ });
2423
+ }
2070
2424
  return this.getById(id);
2071
2425
  }
2072
2426
  /**
@@ -2591,6 +2945,136 @@ var TechnologyService = class extends BaseService {
2591
2945
  byBrand
2592
2946
  };
2593
2947
  }
2948
+ /**
2949
+ * Updates products in technology subcollection when technology metadata changes
2950
+ * @param technologyId - ID of the technology
2951
+ * @param updates - Fields to update (categoryId, subcategoryId, technologyName)
2952
+ */
2953
+ async updateProductsInSubcollection(technologyId, updates) {
2954
+ const productsRef = (0, import_firestore11.collection)(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
2955
+ const productsSnapshot = await (0, import_firestore11.getDocs)(productsRef);
2956
+ if (productsSnapshot.empty) {
2957
+ return;
2958
+ }
2959
+ const batch = (0, import_firestore11.writeBatch)(this.db);
2960
+ for (const productDoc of productsSnapshot.docs) {
2961
+ const productRef = productDoc.ref;
2962
+ const updateFields = {};
2963
+ if (updates.categoryId !== void 0) {
2964
+ updateFields.categoryId = updates.categoryId;
2965
+ }
2966
+ if (updates.subcategoryId !== void 0) {
2967
+ updateFields.subcategoryId = updates.subcategoryId;
2968
+ }
2969
+ if (updates.technologyName !== void 0) {
2970
+ updateFields.technologyName = updates.technologyName;
2971
+ }
2972
+ if (Object.keys(updateFields).length > 0) {
2973
+ batch.update(productRef, updateFields);
2974
+ }
2975
+ }
2976
+ await batch.commit();
2977
+ }
2978
+ /**
2979
+ * Exports technologies to CSV string, suitable for Excel/Sheets.
2980
+ * Includes headers and optional UTF-8 BOM.
2981
+ * By default exports only active technologies (set includeInactive to true to export all).
2982
+ * Includes product names from subcollections.
2983
+ */
2984
+ async exportToCsv(options) {
2985
+ var _a, _b;
2986
+ const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
2987
+ const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
2988
+ const headers = [
2989
+ "id",
2990
+ "name",
2991
+ "description",
2992
+ "family",
2993
+ "categoryId",
2994
+ "subcategoryId",
2995
+ "technicalDetails",
2996
+ "requirements_pre",
2997
+ "requirements_post",
2998
+ "blockingConditions",
2999
+ "contraindications",
3000
+ "benefits",
3001
+ "certificationMinimumLevel",
3002
+ "certificationRequiredSpecialties",
3003
+ "documentationTemplateIds",
3004
+ "productNames",
3005
+ "isActive"
3006
+ ];
3007
+ const rows = [];
3008
+ rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
3009
+ const PAGE_SIZE = 1e3;
3010
+ let cursor;
3011
+ const constraints = [];
3012
+ if (!includeInactive) {
3013
+ constraints.push((0, import_firestore11.where)("isActive", "==", true));
3014
+ }
3015
+ constraints.push((0, import_firestore11.orderBy)("name"));
3016
+ while (true) {
3017
+ const queryConstraints = [...constraints, (0, import_firestore11.limit)(PAGE_SIZE)];
3018
+ if (cursor) queryConstraints.push((0, import_firestore11.startAfter)(cursor));
3019
+ const q = (0, import_firestore11.query)(this.technologiesRef, ...queryConstraints);
3020
+ const snapshot = await (0, import_firestore11.getDocs)(q);
3021
+ if (snapshot.empty) break;
3022
+ for (const d of snapshot.docs) {
3023
+ const technology = { id: d.id, ...d.data() };
3024
+ const productNames = await this.getProductNamesForTechnology(technology.id);
3025
+ rows.push(this.technologyToCsvRow(technology, productNames));
3026
+ }
3027
+ cursor = snapshot.docs[snapshot.docs.length - 1];
3028
+ if (snapshot.size < PAGE_SIZE) break;
3029
+ }
3030
+ const csvBody = rows.join("\r\n");
3031
+ return includeBom ? "\uFEFF" + csvBody : csvBody;
3032
+ }
3033
+ /**
3034
+ * Gets product names from the technology's product subcollection
3035
+ */
3036
+ async getProductNamesForTechnology(technologyId) {
3037
+ try {
3038
+ const productsRef = (0, import_firestore11.collection)(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
3039
+ const q = (0, import_firestore11.query)(productsRef, (0, import_firestore11.where)("isActive", "==", true));
3040
+ const snapshot = await (0, import_firestore11.getDocs)(q);
3041
+ return snapshot.docs.map((doc11) => {
3042
+ const product = doc11.data();
3043
+ return product.name || "";
3044
+ }).filter((name) => name);
3045
+ } catch (error) {
3046
+ console.error(`Error fetching products for technology ${technologyId}:`, error);
3047
+ return [];
3048
+ }
3049
+ }
3050
+ technologyToCsvRow(technology, productNames = []) {
3051
+ 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;
3052
+ const values = [
3053
+ (_a = technology.id) != null ? _a : "",
3054
+ (_b = technology.name) != null ? _b : "",
3055
+ (_c = technology.description) != null ? _c : "",
3056
+ (_d = technology.family) != null ? _d : "",
3057
+ (_e = technology.categoryId) != null ? _e : "",
3058
+ (_f = technology.subcategoryId) != null ? _f : "",
3059
+ (_g = technology.technicalDetails) != null ? _g : "",
3060
+ (_j = (_i = (_h = technology.requirements) == null ? void 0 : _h.pre) == null ? void 0 : _i.map((r) => r.name).join(";")) != null ? _j : "",
3061
+ (_m = (_l = (_k = technology.requirements) == null ? void 0 : _k.post) == null ? void 0 : _l.map((r) => r.name).join(";")) != null ? _m : "",
3062
+ (_o = (_n = technology.blockingConditions) == null ? void 0 : _n.join(";")) != null ? _o : "",
3063
+ (_q = (_p = technology.contraindications) == null ? void 0 : _p.map((c) => c.name).join(";")) != null ? _q : "",
3064
+ (_s = (_r = technology.benefits) == null ? void 0 : _r.map((b) => b.name).join(";")) != null ? _s : "",
3065
+ (_u = (_t = technology.certificationRequirement) == null ? void 0 : _t.minimumLevel) != null ? _u : "",
3066
+ (_x = (_w = (_v = technology.certificationRequirement) == null ? void 0 : _v.requiredSpecialties) == null ? void 0 : _w.join(";")) != null ? _x : "",
3067
+ (_z = (_y = technology.documentationTemplates) == null ? void 0 : _y.map((t) => t.templateId).join(";")) != null ? _z : "",
3068
+ productNames.join(";"),
3069
+ String((_A = technology.isActive) != null ? _A : "")
3070
+ ];
3071
+ return values.map((v) => this.formatCsvValue(v)).join(",");
3072
+ }
3073
+ formatCsvValue(value) {
3074
+ const str = value === null || value === void 0 ? "" : String(value);
3075
+ const escaped = str.replace(/"/g, '""');
3076
+ return `"${escaped}"`;
3077
+ }
2594
3078
  };
2595
3079
 
2596
3080
  // src/backoffice/services/constants.service.ts
@@ -2823,6 +3307,66 @@ var ConstantsService = class extends BaseService {
2823
3307
  contraindications: (0, import_firestore12.arrayRemove)(toRemove)
2824
3308
  });
2825
3309
  }
3310
+ // =================================================================
3311
+ // CSV Export Methods
3312
+ // =================================================================
3313
+ /**
3314
+ * Exports treatment benefits to CSV string, suitable for Excel/Sheets.
3315
+ * Includes headers and optional UTF-8 BOM.
3316
+ */
3317
+ async exportBenefitsToCsv(options) {
3318
+ var _a;
3319
+ const includeBom = (_a = options == null ? void 0 : options.includeBom) != null ? _a : true;
3320
+ const headers = ["id", "name", "description"];
3321
+ const rows = [];
3322
+ rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
3323
+ const benefits = await this.getAllBenefitsForFilter();
3324
+ for (const benefit of benefits) {
3325
+ rows.push(this.benefitToCsvRow(benefit));
3326
+ }
3327
+ const csvBody = rows.join("\r\n");
3328
+ return includeBom ? "\uFEFF" + csvBody : csvBody;
3329
+ }
3330
+ /**
3331
+ * Exports contraindications to CSV string, suitable for Excel/Sheets.
3332
+ * Includes headers and optional UTF-8 BOM.
3333
+ */
3334
+ async exportContraindicationsToCsv(options) {
3335
+ var _a;
3336
+ const includeBom = (_a = options == null ? void 0 : options.includeBom) != null ? _a : true;
3337
+ const headers = ["id", "name", "description"];
3338
+ const rows = [];
3339
+ rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
3340
+ const contraindications = await this.getAllContraindicationsForFilter();
3341
+ for (const contraindication of contraindications) {
3342
+ rows.push(this.contraindicationToCsvRow(contraindication));
3343
+ }
3344
+ const csvBody = rows.join("\r\n");
3345
+ return includeBom ? "\uFEFF" + csvBody : csvBody;
3346
+ }
3347
+ benefitToCsvRow(benefit) {
3348
+ var _a, _b, _c;
3349
+ const values = [
3350
+ (_a = benefit.id) != null ? _a : "",
3351
+ (_b = benefit.name) != null ? _b : "",
3352
+ (_c = benefit.description) != null ? _c : ""
3353
+ ];
3354
+ return values.map((v) => this.formatCsvValue(v)).join(",");
3355
+ }
3356
+ contraindicationToCsvRow(contraindication) {
3357
+ var _a, _b, _c;
3358
+ const values = [
3359
+ (_a = contraindication.id) != null ? _a : "",
3360
+ (_b = contraindication.name) != null ? _b : "",
3361
+ (_c = contraindication.description) != null ? _c : ""
3362
+ ];
3363
+ return values.map((v) => this.formatCsvValue(v)).join(",");
3364
+ }
3365
+ formatCsvValue(value) {
3366
+ const str = value === null || value === void 0 ? "" : String(value);
3367
+ const escaped = str.replace(/"/g, '""');
3368
+ return `"${escaped}"`;
3369
+ }
2826
3370
  };
2827
3371
 
2828
3372
  // src/backoffice/types/static/blocking-condition.types.ts