@blackcode_sa/metaestetics-api 1.12.43 → 1.12.46
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 +6 -12
- package/dist/admin/index.d.ts +6 -12
- package/dist/backoffice/index.d.mts +18 -275
- package/dist/backoffice/index.d.ts +18 -275
- package/dist/backoffice/index.js +14 -802
- package/dist/backoffice/index.mjs +38 -830
- package/dist/index.d.mts +10 -255
- package/dist/index.d.ts +10 -255
- package/dist/index.js +36 -754
- package/dist/index.mjs +44 -765
- package/package.json +1 -1
- package/src/backoffice/services/brand.service.ts +0 -86
- package/src/backoffice/services/category.service.ts +0 -84
- package/src/backoffice/services/constants.service.ts +0 -77
- package/src/backoffice/services/product.service.ts +18 -316
- package/src/backoffice/services/requirement.service.ts +0 -76
- package/src/backoffice/services/subcategory.service.ts +0 -87
- package/src/backoffice/services/technology.service.ts +0 -289
- package/src/backoffice/types/product.types.ts +6 -116
- package/src/services/appointment/utils/zone-management.utils.ts +24 -10
- package/src/backoffice/services/migrate-products.ts +0 -116
|
@@ -169,73 +169,6 @@ 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
|
-
}
|
|
239
172
|
};
|
|
240
173
|
|
|
241
174
|
// src/backoffice/services/category.service.ts
|
|
@@ -434,71 +367,6 @@ var CategoryService = class extends BaseService {
|
|
|
434
367
|
...docSnap.data()
|
|
435
368
|
};
|
|
436
369
|
}
|
|
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
|
-
}
|
|
502
370
|
};
|
|
503
371
|
|
|
504
372
|
// src/services/documentation-templates/documentation-template.service.ts
|
|
@@ -1290,9 +1158,7 @@ import {
|
|
|
1290
1158
|
limit as limit6,
|
|
1291
1159
|
orderBy as orderBy6,
|
|
1292
1160
|
startAfter as startAfter5,
|
|
1293
|
-
getCountFromServer as getCountFromServer4
|
|
1294
|
-
arrayUnion,
|
|
1295
|
-
arrayRemove
|
|
1161
|
+
getCountFromServer as getCountFromServer4
|
|
1296
1162
|
} from "firebase/firestore";
|
|
1297
1163
|
|
|
1298
1164
|
// src/backoffice/types/product.types.ts
|
|
@@ -1304,14 +1170,7 @@ var TECHNOLOGIES_COLLECTION = "technologies";
|
|
|
1304
1170
|
// src/backoffice/services/product.service.ts
|
|
1305
1171
|
var ProductService = class extends BaseService {
|
|
1306
1172
|
/**
|
|
1307
|
-
* Gets reference to
|
|
1308
|
-
* @returns Firestore collection reference
|
|
1309
|
-
*/
|
|
1310
|
-
getTopLevelProductsRef() {
|
|
1311
|
-
return collection6(this.db, PRODUCTS_COLLECTION);
|
|
1312
|
-
}
|
|
1313
|
-
/**
|
|
1314
|
-
* Gets reference to products collection under a technology (backward compatibility)
|
|
1173
|
+
* Gets reference to products collection under a technology
|
|
1315
1174
|
* @param technologyId - ID of the technology
|
|
1316
1175
|
* @returns Firestore collection reference
|
|
1317
1176
|
*/
|
|
@@ -1327,7 +1186,6 @@ var ProductService = class extends BaseService {
|
|
|
1327
1186
|
...product,
|
|
1328
1187
|
brandId,
|
|
1329
1188
|
technologyId,
|
|
1330
|
-
// Required for old subcollection structure
|
|
1331
1189
|
createdAt: now,
|
|
1332
1190
|
updatedAt: now,
|
|
1333
1191
|
isActive: true
|
|
@@ -1387,26 +1245,30 @@ var ProductService = class extends BaseService {
|
|
|
1387
1245
|
}
|
|
1388
1246
|
/**
|
|
1389
1247
|
* Gets counts of active products grouped by category, subcategory, and technology.
|
|
1390
|
-
*
|
|
1248
|
+
* This uses a single collectionGroup query for efficiency.
|
|
1391
1249
|
*/
|
|
1392
1250
|
async getProductCounts() {
|
|
1251
|
+
const q = query6(collectionGroup(this.db, PRODUCTS_COLLECTION), where6("isActive", "==", true));
|
|
1252
|
+
const snapshot = await getDocs6(q);
|
|
1393
1253
|
const counts = {
|
|
1394
1254
|
byCategory: {},
|
|
1395
1255
|
bySubcategory: {},
|
|
1396
1256
|
byTechnology: {}
|
|
1397
1257
|
};
|
|
1398
|
-
|
|
1399
|
-
|
|
1258
|
+
if (snapshot.empty) {
|
|
1259
|
+
return counts;
|
|
1260
|
+
}
|
|
1400
1261
|
snapshot.docs.forEach((doc11) => {
|
|
1401
1262
|
const product = doc11.data();
|
|
1402
|
-
|
|
1403
|
-
|
|
1263
|
+
const { categoryId, subcategoryId, technologyId } = product;
|
|
1264
|
+
if (categoryId) {
|
|
1265
|
+
counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
|
|
1404
1266
|
}
|
|
1405
|
-
if (
|
|
1406
|
-
counts.bySubcategory[
|
|
1267
|
+
if (subcategoryId) {
|
|
1268
|
+
counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
|
|
1407
1269
|
}
|
|
1408
|
-
if (
|
|
1409
|
-
counts.byTechnology[
|
|
1270
|
+
if (technologyId) {
|
|
1271
|
+
counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
|
|
1410
1272
|
}
|
|
1411
1273
|
});
|
|
1412
1274
|
return counts;
|
|
@@ -1485,241 +1347,6 @@ var ProductService = class extends BaseService {
|
|
|
1485
1347
|
...docSnap.data()
|
|
1486
1348
|
};
|
|
1487
1349
|
}
|
|
1488
|
-
// ==========================================
|
|
1489
|
-
// NEW METHODS: Top-level collection (preferred)
|
|
1490
|
-
// ==========================================
|
|
1491
|
-
/**
|
|
1492
|
-
* Creates a new product in the top-level collection
|
|
1493
|
-
*/
|
|
1494
|
-
async createTopLevel(brandId, product, technologyIds = []) {
|
|
1495
|
-
const now = /* @__PURE__ */ new Date();
|
|
1496
|
-
const newProduct = {
|
|
1497
|
-
...product,
|
|
1498
|
-
brandId,
|
|
1499
|
-
assignedTechnologyIds: technologyIds,
|
|
1500
|
-
createdAt: now,
|
|
1501
|
-
updatedAt: now,
|
|
1502
|
-
isActive: true
|
|
1503
|
-
};
|
|
1504
|
-
const productRef = await addDoc3(this.getTopLevelProductsRef(), newProduct);
|
|
1505
|
-
return { id: productRef.id, ...newProduct };
|
|
1506
|
-
}
|
|
1507
|
-
/**
|
|
1508
|
-
* Gets all products from the top-level collection
|
|
1509
|
-
*/
|
|
1510
|
-
async getAllTopLevel(options) {
|
|
1511
|
-
const { rowsPerPage, lastVisible, brandId } = options;
|
|
1512
|
-
const constraints = [where6("isActive", "==", true), orderBy6("name")];
|
|
1513
|
-
if (brandId) {
|
|
1514
|
-
constraints.push(where6("brandId", "==", brandId));
|
|
1515
|
-
}
|
|
1516
|
-
if (lastVisible) {
|
|
1517
|
-
constraints.push(startAfter5(lastVisible));
|
|
1518
|
-
}
|
|
1519
|
-
constraints.push(limit6(rowsPerPage));
|
|
1520
|
-
const q = query6(this.getTopLevelProductsRef(), ...constraints);
|
|
1521
|
-
const snapshot = await getDocs6(q);
|
|
1522
|
-
const products = snapshot.docs.map(
|
|
1523
|
-
(doc11) => ({
|
|
1524
|
-
id: doc11.id,
|
|
1525
|
-
...doc11.data()
|
|
1526
|
-
})
|
|
1527
|
-
);
|
|
1528
|
-
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
1529
|
-
return { products, lastVisible: newLastVisible };
|
|
1530
|
-
}
|
|
1531
|
-
/**
|
|
1532
|
-
* Gets a product by ID from the top-level collection
|
|
1533
|
-
*/
|
|
1534
|
-
async getByIdTopLevel(productId) {
|
|
1535
|
-
const docRef = doc6(this.getTopLevelProductsRef(), productId);
|
|
1536
|
-
const docSnap = await getDoc6(docRef);
|
|
1537
|
-
if (!docSnap.exists()) return null;
|
|
1538
|
-
return {
|
|
1539
|
-
id: docSnap.id,
|
|
1540
|
-
...docSnap.data()
|
|
1541
|
-
};
|
|
1542
|
-
}
|
|
1543
|
-
/**
|
|
1544
|
-
* Updates a product in the top-level collection
|
|
1545
|
-
*/
|
|
1546
|
-
async updateTopLevel(productId, product) {
|
|
1547
|
-
const updateData = {
|
|
1548
|
-
...product,
|
|
1549
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
1550
|
-
};
|
|
1551
|
-
const docRef = doc6(this.getTopLevelProductsRef(), productId);
|
|
1552
|
-
await updateDoc6(docRef, updateData);
|
|
1553
|
-
return this.getByIdTopLevel(productId);
|
|
1554
|
-
}
|
|
1555
|
-
/**
|
|
1556
|
-
* Deletes a product from the top-level collection (soft delete)
|
|
1557
|
-
*/
|
|
1558
|
-
async deleteTopLevel(productId) {
|
|
1559
|
-
await this.updateTopLevel(productId, {
|
|
1560
|
-
isActive: false
|
|
1561
|
-
});
|
|
1562
|
-
}
|
|
1563
|
-
/**
|
|
1564
|
-
* Assigns a product to a technology
|
|
1565
|
-
*/
|
|
1566
|
-
async assignToTechnology(productId, technologyId) {
|
|
1567
|
-
const docRef = doc6(this.getTopLevelProductsRef(), productId);
|
|
1568
|
-
await updateDoc6(docRef, {
|
|
1569
|
-
assignedTechnologyIds: arrayUnion(technologyId),
|
|
1570
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
1571
|
-
});
|
|
1572
|
-
}
|
|
1573
|
-
/**
|
|
1574
|
-
* Unassigns a product from a technology
|
|
1575
|
-
*/
|
|
1576
|
-
async unassignFromTechnology(productId, technologyId) {
|
|
1577
|
-
const docRef = doc6(this.getTopLevelProductsRef(), productId);
|
|
1578
|
-
await updateDoc6(docRef, {
|
|
1579
|
-
assignedTechnologyIds: arrayRemove(technologyId),
|
|
1580
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
1581
|
-
});
|
|
1582
|
-
}
|
|
1583
|
-
/**
|
|
1584
|
-
* Gets products assigned to a specific technology
|
|
1585
|
-
*/
|
|
1586
|
-
async getAssignedProducts(technologyId) {
|
|
1587
|
-
const q = query6(
|
|
1588
|
-
this.getTopLevelProductsRef(),
|
|
1589
|
-
where6("assignedTechnologyIds", "array-contains", technologyId),
|
|
1590
|
-
where6("isActive", "==", true),
|
|
1591
|
-
orderBy6("name")
|
|
1592
|
-
);
|
|
1593
|
-
const snapshot = await getDocs6(q);
|
|
1594
|
-
return snapshot.docs.map(
|
|
1595
|
-
(doc11) => ({
|
|
1596
|
-
id: doc11.id,
|
|
1597
|
-
...doc11.data()
|
|
1598
|
-
})
|
|
1599
|
-
);
|
|
1600
|
-
}
|
|
1601
|
-
/**
|
|
1602
|
-
* Gets products NOT assigned to a specific technology
|
|
1603
|
-
*/
|
|
1604
|
-
async getUnassignedProducts(technologyId) {
|
|
1605
|
-
const q = query6(
|
|
1606
|
-
this.getTopLevelProductsRef(),
|
|
1607
|
-
where6("isActive", "==", true),
|
|
1608
|
-
orderBy6("name")
|
|
1609
|
-
);
|
|
1610
|
-
const snapshot = await getDocs6(q);
|
|
1611
|
-
const allProducts = snapshot.docs.map(
|
|
1612
|
-
(doc11) => ({
|
|
1613
|
-
id: doc11.id,
|
|
1614
|
-
...doc11.data()
|
|
1615
|
-
})
|
|
1616
|
-
);
|
|
1617
|
-
return allProducts.filter(
|
|
1618
|
-
(product) => {
|
|
1619
|
-
var _a;
|
|
1620
|
-
return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
|
|
1621
|
-
}
|
|
1622
|
-
);
|
|
1623
|
-
}
|
|
1624
|
-
/**
|
|
1625
|
-
* Gets all products for a brand (from top-level collection)
|
|
1626
|
-
*/
|
|
1627
|
-
async getByBrand(brandId) {
|
|
1628
|
-
const q = query6(
|
|
1629
|
-
this.getTopLevelProductsRef(),
|
|
1630
|
-
where6("brandId", "==", brandId),
|
|
1631
|
-
where6("isActive", "==", true),
|
|
1632
|
-
orderBy6("name")
|
|
1633
|
-
);
|
|
1634
|
-
const snapshot = await getDocs6(q);
|
|
1635
|
-
return snapshot.docs.map(
|
|
1636
|
-
(doc11) => ({
|
|
1637
|
-
id: doc11.id,
|
|
1638
|
-
...doc11.data()
|
|
1639
|
-
})
|
|
1640
|
-
);
|
|
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
|
-
}
|
|
1723
1350
|
};
|
|
1724
1351
|
|
|
1725
1352
|
// src/backoffice/services/requirement.service.ts
|
|
@@ -1731,8 +1358,7 @@ import {
|
|
|
1731
1358
|
getDocs as getDocs7,
|
|
1732
1359
|
query as query7,
|
|
1733
1360
|
updateDoc as updateDoc7,
|
|
1734
|
-
where as where7
|
|
1735
|
-
orderBy as orderBy7
|
|
1361
|
+
where as where7
|
|
1736
1362
|
} from "firebase/firestore";
|
|
1737
1363
|
|
|
1738
1364
|
// src/backoffice/types/requirement.types.ts
|
|
@@ -1853,65 +1479,6 @@ var RequirementService = class extends BaseService {
|
|
|
1853
1479
|
...docSnap.data()
|
|
1854
1480
|
};
|
|
1855
1481
|
}
|
|
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
|
-
}
|
|
1915
1482
|
};
|
|
1916
1483
|
|
|
1917
1484
|
// src/backoffice/services/subcategory.service.ts
|
|
@@ -1925,7 +1492,7 @@ import {
|
|
|
1925
1492
|
getDoc as getDoc8,
|
|
1926
1493
|
getDocs as getDocs8,
|
|
1927
1494
|
limit as limit7,
|
|
1928
|
-
orderBy as
|
|
1495
|
+
orderBy as orderBy7,
|
|
1929
1496
|
query as query8,
|
|
1930
1497
|
setDoc as setDoc4,
|
|
1931
1498
|
startAfter as startAfter6,
|
|
@@ -1999,7 +1566,7 @@ var SubcategoryService = class extends BaseService {
|
|
|
1999
1566
|
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
2000
1567
|
const constraints = [
|
|
2001
1568
|
where8("isActive", "==", active),
|
|
2002
|
-
|
|
1569
|
+
orderBy7("name"),
|
|
2003
1570
|
queryLimit ? limit7(queryLimit) : void 0,
|
|
2004
1571
|
lastVisible ? startAfter6(lastVisible) : void 0
|
|
2005
1572
|
].filter((c) => !!c);
|
|
@@ -2026,7 +1593,7 @@ var SubcategoryService = class extends BaseService {
|
|
|
2026
1593
|
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
2027
1594
|
const constraints = [
|
|
2028
1595
|
where8("isActive", "==", active),
|
|
2029
|
-
|
|
1596
|
+
orderBy7("name"),
|
|
2030
1597
|
queryLimit ? limit7(queryLimit) : void 0,
|
|
2031
1598
|
lastVisible ? startAfter6(lastVisible) : void 0
|
|
2032
1599
|
].filter((c) => !!c);
|
|
@@ -2155,74 +1722,6 @@ var SubcategoryService = class extends BaseService {
|
|
|
2155
1722
|
...docSnap.data()
|
|
2156
1723
|
};
|
|
2157
1724
|
}
|
|
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
|
-
}
|
|
2226
1725
|
};
|
|
2227
1726
|
|
|
2228
1727
|
// src/backoffice/services/technology.service.ts
|
|
@@ -2233,14 +1732,13 @@ import {
|
|
|
2233
1732
|
getDoc as getDoc9,
|
|
2234
1733
|
getDocs as getDocs9,
|
|
2235
1734
|
limit as limit8,
|
|
2236
|
-
orderBy as
|
|
1735
|
+
orderBy as orderBy8,
|
|
2237
1736
|
query as query9,
|
|
2238
1737
|
startAfter as startAfter7,
|
|
2239
1738
|
updateDoc as updateDoc9,
|
|
2240
1739
|
where as where9,
|
|
2241
|
-
arrayUnion
|
|
2242
|
-
arrayRemove
|
|
2243
|
-
writeBatch
|
|
1740
|
+
arrayUnion,
|
|
1741
|
+
arrayRemove
|
|
2244
1742
|
} from "firebase/firestore";
|
|
2245
1743
|
|
|
2246
1744
|
// src/backoffice/types/static/certification.types.ts
|
|
@@ -2347,7 +1845,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2347
1845
|
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
2348
1846
|
const constraints = [
|
|
2349
1847
|
where9("isActive", "==", active),
|
|
2350
|
-
|
|
1848
|
+
orderBy8("name"),
|
|
2351
1849
|
queryLimit ? limit8(queryLimit) : void 0,
|
|
2352
1850
|
lastVisible ? startAfter7(lastVisible) : void 0
|
|
2353
1851
|
].filter((c) => !!c);
|
|
@@ -2373,7 +1871,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2373
1871
|
const constraints = [
|
|
2374
1872
|
where9("categoryId", "==", categoryId),
|
|
2375
1873
|
where9("isActive", "==", active),
|
|
2376
|
-
|
|
1874
|
+
orderBy8("name"),
|
|
2377
1875
|
queryLimit ? limit8(queryLimit) : void 0,
|
|
2378
1876
|
lastVisible ? startAfter7(lastVisible) : void 0
|
|
2379
1877
|
].filter((c) => !!c);
|
|
@@ -2399,7 +1897,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2399
1897
|
const constraints = [
|
|
2400
1898
|
where9("subcategoryId", "==", subcategoryId),
|
|
2401
1899
|
where9("isActive", "==", active),
|
|
2402
|
-
|
|
1900
|
+
orderBy8("name"),
|
|
2403
1901
|
queryLimit ? limit8(queryLimit) : void 0,
|
|
2404
1902
|
lastVisible ? startAfter7(lastVisible) : void 0
|
|
2405
1903
|
].filter((c) => !!c);
|
|
@@ -2429,18 +1927,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2429
1927
|
});
|
|
2430
1928
|
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
2431
1929
|
const docRef = doc9(this.technologiesRef, id);
|
|
2432
|
-
const beforeTech = await this.getById(id);
|
|
2433
1930
|
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
|
-
}
|
|
2444
1931
|
return this.getById(id);
|
|
2445
1932
|
}
|
|
2446
1933
|
/**
|
|
@@ -2481,7 +1968,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2481
1968
|
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2482
1969
|
const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
2483
1970
|
await updateDoc9(docRef, {
|
|
2484
|
-
[requirementType]:
|
|
1971
|
+
[requirementType]: arrayUnion(requirement),
|
|
2485
1972
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2486
1973
|
});
|
|
2487
1974
|
return this.getById(technologyId);
|
|
@@ -2496,7 +1983,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2496
1983
|
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2497
1984
|
const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
2498
1985
|
await updateDoc9(docRef, {
|
|
2499
|
-
[requirementType]:
|
|
1986
|
+
[requirementType]: arrayRemove(requirement),
|
|
2500
1987
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2501
1988
|
});
|
|
2502
1989
|
return this.getById(technologyId);
|
|
@@ -2535,7 +2022,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2535
2022
|
async addBlockingCondition(technologyId, condition) {
|
|
2536
2023
|
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2537
2024
|
await updateDoc9(docRef, {
|
|
2538
|
-
blockingConditions:
|
|
2025
|
+
blockingConditions: arrayUnion(condition),
|
|
2539
2026
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2540
2027
|
});
|
|
2541
2028
|
return this.getById(technologyId);
|
|
@@ -2549,7 +2036,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2549
2036
|
async removeBlockingCondition(technologyId, condition) {
|
|
2550
2037
|
const docRef = doc9(this.technologiesRef, technologyId);
|
|
2551
2038
|
await updateDoc9(docRef, {
|
|
2552
|
-
blockingConditions:
|
|
2039
|
+
blockingConditions: arrayRemove(condition),
|
|
2553
2040
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2554
2041
|
});
|
|
2555
2042
|
return this.getById(technologyId);
|
|
@@ -2828,7 +2315,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2828
2315
|
collection9(this.db, TECHNOLOGIES_COLLECTION),
|
|
2829
2316
|
where9("isActive", "==", true),
|
|
2830
2317
|
where9("subcategoryId", "==", subcategoryId),
|
|
2831
|
-
|
|
2318
|
+
orderBy8("name")
|
|
2832
2319
|
);
|
|
2833
2320
|
const snapshot = await getDocs9(q);
|
|
2834
2321
|
return snapshot.docs.map(
|
|
@@ -2849,7 +2336,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2849
2336
|
where9("isActive", "==", true),
|
|
2850
2337
|
where9("categoryId", "==", categoryId),
|
|
2851
2338
|
where9("subcategoryId", "==", subcategoryId),
|
|
2852
|
-
|
|
2339
|
+
orderBy8("name")
|
|
2853
2340
|
);
|
|
2854
2341
|
const snapshot = await getDocs9(q);
|
|
2855
2342
|
return snapshot.docs.map(
|
|
@@ -2866,7 +2353,7 @@ var TechnologyService = class extends BaseService {
|
|
|
2866
2353
|
const q = query9(
|
|
2867
2354
|
collection9(this.db, TECHNOLOGIES_COLLECTION),
|
|
2868
2355
|
where9("isActive", "==", true),
|
|
2869
|
-
|
|
2356
|
+
orderBy8("name")
|
|
2870
2357
|
);
|
|
2871
2358
|
const snapshot = await getDocs9(q);
|
|
2872
2359
|
return snapshot.docs.map(
|
|
@@ -2876,231 +2363,12 @@ var TechnologyService = class extends BaseService {
|
|
|
2876
2363
|
})
|
|
2877
2364
|
);
|
|
2878
2365
|
}
|
|
2879
|
-
// ==========================================
|
|
2880
|
-
// NEW METHODS: Product assignment management
|
|
2881
|
-
// ==========================================
|
|
2882
|
-
/**
|
|
2883
|
-
* Assigns multiple products to a technology
|
|
2884
|
-
* Updates each product's assignedTechnologyIds array
|
|
2885
|
-
*/
|
|
2886
|
-
async assignProducts(technologyId, productIds) {
|
|
2887
|
-
const batch = writeBatch(this.db);
|
|
2888
|
-
for (const productId of productIds) {
|
|
2889
|
-
const productRef = doc9(this.db, PRODUCTS_COLLECTION, productId);
|
|
2890
|
-
batch.update(productRef, {
|
|
2891
|
-
assignedTechnologyIds: arrayUnion2(technologyId),
|
|
2892
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2893
|
-
});
|
|
2894
|
-
}
|
|
2895
|
-
await batch.commit();
|
|
2896
|
-
}
|
|
2897
|
-
/**
|
|
2898
|
-
* Unassigns multiple products from a technology
|
|
2899
|
-
* Updates each product's assignedTechnologyIds array
|
|
2900
|
-
*/
|
|
2901
|
-
async unassignProducts(technologyId, productIds) {
|
|
2902
|
-
const batch = writeBatch(this.db);
|
|
2903
|
-
for (const productId of productIds) {
|
|
2904
|
-
const productRef = doc9(this.db, PRODUCTS_COLLECTION, productId);
|
|
2905
|
-
batch.update(productRef, {
|
|
2906
|
-
assignedTechnologyIds: arrayRemove2(technologyId),
|
|
2907
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2908
|
-
});
|
|
2909
|
-
}
|
|
2910
|
-
await batch.commit();
|
|
2911
|
-
}
|
|
2912
|
-
/**
|
|
2913
|
-
* Gets products assigned to a specific technology
|
|
2914
|
-
* Reads from top-level collection for immediate consistency (Cloud Functions may lag)
|
|
2915
|
-
*/
|
|
2916
|
-
async getAssignedProducts(technologyId) {
|
|
2917
|
-
const q = query9(
|
|
2918
|
-
collection9(this.db, PRODUCTS_COLLECTION),
|
|
2919
|
-
where9("assignedTechnologyIds", "array-contains", technologyId),
|
|
2920
|
-
where9("isActive", "==", true),
|
|
2921
|
-
orderBy9("name")
|
|
2922
|
-
);
|
|
2923
|
-
const snapshot = await getDocs9(q);
|
|
2924
|
-
return snapshot.docs.map(
|
|
2925
|
-
(doc11) => ({
|
|
2926
|
-
id: doc11.id,
|
|
2927
|
-
...doc11.data()
|
|
2928
|
-
})
|
|
2929
|
-
);
|
|
2930
|
-
}
|
|
2931
|
-
/**
|
|
2932
|
-
* Gets products NOT assigned to a specific technology
|
|
2933
|
-
*/
|
|
2934
|
-
async getUnassignedProducts(technologyId) {
|
|
2935
|
-
const q = query9(
|
|
2936
|
-
collection9(this.db, PRODUCTS_COLLECTION),
|
|
2937
|
-
where9("isActive", "==", true),
|
|
2938
|
-
orderBy9("name")
|
|
2939
|
-
);
|
|
2940
|
-
const snapshot = await getDocs9(q);
|
|
2941
|
-
const allProducts = snapshot.docs.map(
|
|
2942
|
-
(doc11) => ({
|
|
2943
|
-
id: doc11.id,
|
|
2944
|
-
...doc11.data()
|
|
2945
|
-
})
|
|
2946
|
-
);
|
|
2947
|
-
return allProducts.filter(
|
|
2948
|
-
(product) => {
|
|
2949
|
-
var _a;
|
|
2950
|
-
return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
|
|
2951
|
-
}
|
|
2952
|
-
);
|
|
2953
|
-
}
|
|
2954
|
-
/**
|
|
2955
|
-
* Gets product assignment statistics for a technology
|
|
2956
|
-
*/
|
|
2957
|
-
async getProductStats(technologyId) {
|
|
2958
|
-
const products = await this.getAssignedProducts(technologyId);
|
|
2959
|
-
const byBrand = {};
|
|
2960
|
-
products.forEach((product) => {
|
|
2961
|
-
byBrand[product.brandName] = (byBrand[product.brandName] || 0) + 1;
|
|
2962
|
-
});
|
|
2963
|
-
return {
|
|
2964
|
-
totalAssigned: products.length,
|
|
2965
|
-
byBrand
|
|
2966
|
-
};
|
|
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
|
-
}
|
|
3098
2366
|
};
|
|
3099
2367
|
|
|
3100
2368
|
// src/backoffice/services/constants.service.ts
|
|
3101
2369
|
import {
|
|
3102
|
-
arrayRemove as
|
|
3103
|
-
arrayUnion as
|
|
2370
|
+
arrayRemove as arrayRemove2,
|
|
2371
|
+
arrayUnion as arrayUnion2,
|
|
3104
2372
|
doc as doc10,
|
|
3105
2373
|
getDoc as getDoc10,
|
|
3106
2374
|
setDoc as setDoc5,
|
|
@@ -3168,7 +2436,7 @@ var ConstantsService = class extends BaseService {
|
|
|
3168
2436
|
await setDoc5(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
|
|
3169
2437
|
} else {
|
|
3170
2438
|
await updateDoc10(this.treatmentBenefitsDocRef, {
|
|
3171
|
-
benefits:
|
|
2439
|
+
benefits: arrayUnion2(newBenefit)
|
|
3172
2440
|
});
|
|
3173
2441
|
}
|
|
3174
2442
|
return newBenefit;
|
|
@@ -3222,7 +2490,7 @@ var ConstantsService = class extends BaseService {
|
|
|
3222
2490
|
return;
|
|
3223
2491
|
}
|
|
3224
2492
|
await updateDoc10(this.treatmentBenefitsDocRef, {
|
|
3225
|
-
benefits:
|
|
2493
|
+
benefits: arrayRemove2(benefitToRemove)
|
|
3226
2494
|
});
|
|
3227
2495
|
}
|
|
3228
2496
|
// =================================================================
|
|
@@ -3275,7 +2543,7 @@ var ConstantsService = class extends BaseService {
|
|
|
3275
2543
|
});
|
|
3276
2544
|
} else {
|
|
3277
2545
|
await updateDoc10(this.contraindicationsDocRef, {
|
|
3278
|
-
contraindications:
|
|
2546
|
+
contraindications: arrayUnion2(newContraindication)
|
|
3279
2547
|
});
|
|
3280
2548
|
}
|
|
3281
2549
|
return newContraindication;
|
|
@@ -3331,69 +2599,9 @@ var ConstantsService = class extends BaseService {
|
|
|
3331
2599
|
return;
|
|
3332
2600
|
}
|
|
3333
2601
|
await updateDoc10(this.contraindicationsDocRef, {
|
|
3334
|
-
contraindications:
|
|
2602
|
+
contraindications: arrayRemove2(toRemove)
|
|
3335
2603
|
});
|
|
3336
2604
|
}
|
|
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
|
-
}
|
|
3397
2605
|
};
|
|
3398
2606
|
|
|
3399
2607
|
// src/backoffice/types/static/blocking-condition.types.ts
|