myio-js-library 0.1.563 → 0.1.565

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/index.cjs CHANGED
@@ -130,6 +130,7 @@ __export(src_exports, {
130
130
  DEFAULT_SIDEBAR_CONFIG: () => DEFAULT_SIDEBAR_CONFIG,
131
131
  DEFAULT_SOLENOID_SETTINGS: () => DEFAULT_SOLENOID_SETTINGS,
132
132
  DEFAULT_SOLENOID_STATE: () => DEFAULT_SOLENOID_STATE,
133
+ DEFAULT_STORE_SEGMENTS: () => DEFAULT_STORE_SEGMENTS,
133
134
  DEFAULT_SWITCH_SETTINGS: () => DEFAULT_SWITCH_SETTINGS,
134
135
  DEFAULT_SWITCH_STATE: () => DEFAULT_SWITCH_STATE,
135
136
  DEFAULT_TABS: () => DEFAULT_TABS,
@@ -352,7 +353,7 @@ __export(src_exports, {
352
353
  appendFreshdeskTicketToTB: () => appendFreshdeskTicketToTB,
353
354
  applyCustomerCodeToAssetName: () => applyCustomerCodeToAssetName,
354
355
  applyDashboardPalette: () => applyDashboardPalette,
355
- applyDeviceGridFilters: () => applyFilters,
356
+ applyDeviceGridFilters: () => applyFilters2,
356
357
  archiveAlarmAnnotation: () => archiveAlarmAnnotation,
357
358
  assetLocalToken: () => assetLocalToken,
358
359
  assignShoppingColors: () => assignShoppingColors,
@@ -711,6 +712,7 @@ __export(src_exports, {
711
712
  injectSolenoidControlStyles: () => injectSolenoidControlStyles,
712
713
  injectSwitchControlStyles: () => injectSwitchControlStyles,
713
714
  interpolateTemperature: () => interpolateTemperature,
715
+ inventory: () => inventory_exports,
714
716
  isAlarmActive: () => isAlarmActive,
715
717
  isAnnotationOverdue: () => isOverdue,
716
718
  isAssetCode: () => isAssetCode,
@@ -921,7 +923,7 @@ module.exports = __toCommonJS(src_exports);
921
923
  // package.json
922
924
  var package_default = {
923
925
  name: "myio-js-library",
924
- version: "0.1.563",
926
+ version: "0.1.565",
925
927
  description: "A clean, standalone JS SDK for MYIO projects",
926
928
  license: "MIT",
927
929
  repository: "github:gh-myio/myio-js-library",
@@ -2442,6 +2444,719 @@ function getTypesByCategory(category) {
2442
2444
  );
2443
2445
  }
2444
2446
 
2447
+ // src/utils/devices/inventory/index.ts
2448
+ var inventory_exports = {};
2449
+ __export(inventory_exports, {
2450
+ ATIVIDADE_LABELS: () => ATIVIDADE_LABELS,
2451
+ CADASTRO_LABELS: () => CADASTRO_LABELS,
2452
+ DOMAIN_LABELS: () => DOMAIN_LABELS,
2453
+ FILTER_DIMENSIONS: () => FILTER_DIMENSIONS,
2454
+ INVENTORY_ENTITY_FIELDS: () => INVENTORY_ENTITY_FIELDS,
2455
+ INVENTORY_EXPORT_COLUMNS: () => INVENTORY_EXPORT_COLUMNS,
2456
+ INVENTORY_PDF_COLUMN_KEYS: () => INVENTORY_PDF_COLUMN_KEYS,
2457
+ INVENTORY_RULES: () => INVENTORY_RULES,
2458
+ INVENTORY_SERVER_ATTRIBUTES: () => INVENTORY_SERVER_ATTRIBUTES,
2459
+ LIFECYCLE_LABELS: () => LIFECYCLE_LABELS,
2460
+ NO_CUSTOMER_KEY: () => NO_CUSTOMER_KEY,
2461
+ UNCLASSIFIED_PROFILE_LABEL: () => UNCLASSIFIED_PROFILE_LABEL,
2462
+ applyFilters: () => applyFilters,
2463
+ buildDashboardSummary: () => buildDashboardSummary,
2464
+ buildEntitiesQueryBody: () => buildEntitiesQueryBody,
2465
+ buildExportSummary: () => buildExportSummary,
2466
+ buildInventoryRows: () => buildInventoryRows,
2467
+ buildRuleContext: () => buildRuleContext,
2468
+ compareRows: () => compareRows,
2469
+ createEmptyFilters: () => createEmptyFilters,
2470
+ customerKeyLabel: () => customerKeyLabel,
2471
+ dimensionValues: () => dimensionValues,
2472
+ displayName: () => displayName,
2473
+ evaluateDevice: () => evaluateDevice,
2474
+ exportFileName: () => exportFileName,
2475
+ facetCounts: () => facetCounts,
2476
+ failedRulesText: () => failedRulesText,
2477
+ formatDateTime: () => formatDateTime,
2478
+ formatDeviceLabel: () => formatDeviceLabel,
2479
+ getInventoryRule: () => getInventoryRule,
2480
+ getVisibleGroups: () => getVisibleGroups,
2481
+ getVisibleRows: () => getVisibleRows,
2482
+ groupKey: () => groupKey,
2483
+ guardFormula: () => guardFormula,
2484
+ isBlank: () => isBlank,
2485
+ isRecognizedProfile: () => isRecognizedProfile,
2486
+ matchesFilters: () => matchesFilters,
2487
+ normalizeText: () => normalizeText,
2488
+ parseEntityRow: () => parseEntityRow,
2489
+ pickColumns: () => pickColumns,
2490
+ resolveLifecycle: () => resolveLifecycle,
2491
+ summarizeFilters: () => summarizeFilters,
2492
+ toBooleanStrict: () => toBooleanStrict,
2493
+ toCSV: () => toCSV2,
2494
+ toExportMatrix: () => toExportMatrix,
2495
+ toText: () => toText,
2496
+ toTimestamp: () => toTimestamp
2497
+ });
2498
+
2499
+ // src/utils/devices/inventory/loader.ts
2500
+ var INVENTORY_ENTITY_FIELDS = ["name", "label", "type", "createdTime", "ownerName", "ownerType"];
2501
+ var INVENTORY_SERVER_ATTRIBUTES = [
2502
+ "deviceProfile",
2503
+ "identifier",
2504
+ "ingestionId",
2505
+ "gcdrDeviceId",
2506
+ "centralId",
2507
+ "slaveId",
2508
+ "lifecycleStatus",
2509
+ "active",
2510
+ "lastActivityTime"
2511
+ ];
2512
+ function buildEntitiesQueryBody(page, pageSize) {
2513
+ return {
2514
+ entityFilter: { type: "entityType", entityType: "DEVICE" },
2515
+ entityFields: INVENTORY_ENTITY_FIELDS.map((key) => ({ type: "ENTITY_FIELD", key })),
2516
+ latestValues: INVENTORY_SERVER_ATTRIBUTES.map((key) => ({ type: "SERVER_ATTRIBUTE", key })),
2517
+ pageLink: {
2518
+ page,
2519
+ pageSize,
2520
+ sortOrder: { key: { type: "ENTITY_FIELD", key: "name" }, direction: "ASC" }
2521
+ }
2522
+ };
2523
+ }
2524
+ function isBlank(value) {
2525
+ if (value === null || value === void 0) return true;
2526
+ const s = String(value).trim();
2527
+ return s === "" || /^(null|undefined)$/i.test(s);
2528
+ }
2529
+ function toText(value) {
2530
+ return isBlank(value) ? null : String(value).trim();
2531
+ }
2532
+ function toBooleanStrict(value) {
2533
+ if (value === true || value === false) return value;
2534
+ const s = toText(value);
2535
+ if (s === null) return null;
2536
+ const lower = s.toLowerCase();
2537
+ if (lower === "true") return true;
2538
+ if (lower === "false") return false;
2539
+ return null;
2540
+ }
2541
+ function toTimestamp(value) {
2542
+ if (isBlank(value)) return null;
2543
+ const n = Number(value);
2544
+ return Number.isFinite(n) && n > 0 ? n : null;
2545
+ }
2546
+ function parseEntityRow(raw, options = {}) {
2547
+ const fields = raw.latest?.ENTITY_FIELD || {};
2548
+ const attrs = raw.latest?.SERVER_ATTRIBUTE || {};
2549
+ const field = (key) => fields[key]?.value;
2550
+ const attr = (key) => attrs[key]?.value;
2551
+ const ownerName = toText(field("ownerName"));
2552
+ const ownerType = toText(field("ownerType"));
2553
+ const customerId = ownerType === "CUSTOMER" && ownerName ? options.customerIdByName?.get(ownerName) ?? null : null;
2554
+ return {
2555
+ id: String(raw.entityId?.id || ""),
2556
+ name: toText(field("name")) ?? "",
2557
+ label: toText(field("label")),
2558
+ type: toText(field("type")),
2559
+ createdTime: toTimestamp(field("createdTime")),
2560
+ ownerName,
2561
+ ownerType: ownerType ? ownerType.toUpperCase() : null,
2562
+ customerId,
2563
+ deviceProfile: toText(attr("deviceProfile")),
2564
+ identifier: toText(attr("identifier")),
2565
+ ingestionId: toText(attr("ingestionId")),
2566
+ gcdrDeviceId: toText(attr("gcdrDeviceId")),
2567
+ centralId: toText(attr("centralId")),
2568
+ slaveId: toText(attr("slaveId")),
2569
+ lifecycleStatus: toText(attr("lifecycleStatus")),
2570
+ active: toBooleanStrict(attr("active")),
2571
+ lastActivityTime: toTimestamp(attr("lastActivityTime")),
2572
+ attributesLoaded: options.attributesLoaded !== false
2573
+ };
2574
+ }
2575
+
2576
+ // src/utils/devices/inventory/lifecycle.ts
2577
+ var VALID = /* @__PURE__ */ new Set(["active", "stock", "archived"]);
2578
+ function resolveLifecycle(device) {
2579
+ const raw = (device.lifecycleStatus || "").trim().toLowerCase();
2580
+ const fromAttribute = VALID.has(raw);
2581
+ const status = fromAttribute ? raw : "active";
2582
+ let legacyHint = null;
2583
+ if (status === "active") {
2584
+ if (/ARQUIVAD/i.test(device.deviceProfile || "")) legacyHint = "deviceProfile";
2585
+ else if (/DESATIVAR/i.test(device.ownerName || "")) legacyHint = "customerName";
2586
+ }
2587
+ return { status, source: fromAttribute ? "attribute" : "default", legacyHint };
2588
+ }
2589
+
2590
+ // src/utils/devices/inventory/rules.ts
2591
+ function isRecognizedProfile(deviceProfile) {
2592
+ return !!deviceProfile && Object.prototype.hasOwnProperty.call(DEVICE_TYPE_CONFIG, deviceProfile.toUpperCase());
2593
+ }
2594
+ function isIntegrated(device, ctx) {
2595
+ return !!device.ownerName && device.ownerType === "CUSTOMER" && ctx.integratedCustomers.has(device.ownerName);
2596
+ }
2597
+ function requiredOnIntegrated(pick) {
2598
+ return (device, ctx) => {
2599
+ if (!device.attributesLoaded) return "unknown";
2600
+ if (!isIntegrated(device, ctx)) return "notApplicable";
2601
+ return pick(device) === null ? "fail" : "pass";
2602
+ };
2603
+ }
2604
+ var INVENTORY_RULES = [
2605
+ {
2606
+ id: "C1",
2607
+ axis: "cadastro",
2608
+ severity: "pending",
2609
+ label: "Sem cliente",
2610
+ description: "O dispositivo pertence ao tenant e n\xE3o est\xE1 associado a nenhum cliente.",
2611
+ evaluate: (d) => {
2612
+ if (d.ownerType === null) return "unknown";
2613
+ return d.ownerType === "TENANT" ? "fail" : "pass";
2614
+ }
2615
+ },
2616
+ {
2617
+ id: "C2",
2618
+ axis: "cadastro",
2619
+ severity: "critical",
2620
+ label: "Sem deviceProfile",
2621
+ description: "O atributo deviceProfile n\xE3o est\xE1 preenchido; o dispositivo n\xE3o \xE9 classificado nos dashboards.",
2622
+ evaluate: requiredOnIntegrated((d) => d.deviceProfile)
2623
+ },
2624
+ {
2625
+ id: "C3",
2626
+ axis: "cadastro",
2627
+ severity: "critical",
2628
+ label: "Sem ingestionId",
2629
+ description: "Sem ingestionId, os dados do dispositivo n\xE3o chegam aos relat\xF3rios.",
2630
+ evaluate: requiredOnIntegrated((d) => d.ingestionId)
2631
+ },
2632
+ {
2633
+ id: "C4",
2634
+ axis: "cadastro",
2635
+ severity: "pending",
2636
+ label: "Sem gcdrDeviceId",
2637
+ description: "Sem gcdrDeviceId, alarmes e recursos do GCDR ficam indispon\xEDveis para o dispositivo.",
2638
+ evaluate: requiredOnIntegrated((d) => d.gcdrDeviceId)
2639
+ },
2640
+ {
2641
+ id: "C5",
2642
+ axis: "cadastro",
2643
+ severity: "pending",
2644
+ label: "Perfil n\xE3o reconhecido",
2645
+ description: 'O deviceProfile n\xE3o est\xE1 na lista de perfis conhecidos; o dispositivo aparece como "N\xE3o classificado".',
2646
+ evaluate: (d, ctx) => {
2647
+ if (!d.attributesLoaded) return "unknown";
2648
+ if (!isIntegrated(d, ctx) || d.deviceProfile === null) return "notApplicable";
2649
+ return isRecognizedProfile(d.deviceProfile) ? "pass" : "fail";
2650
+ }
2651
+ },
2652
+ {
2653
+ id: "C6",
2654
+ axis: "cadastro",
2655
+ severity: "info",
2656
+ label: "Perfil diverge do TB",
2657
+ description: "O deviceProfile difere do perfil do dispositivo no ThingsBoard.",
2658
+ evaluate: (d) => {
2659
+ if (!d.attributesLoaded) return "unknown";
2660
+ if (d.deviceProfile === null || d.type === null) return "notApplicable";
2661
+ return d.deviceProfile.toUpperCase() === d.type.toUpperCase() ? "pass" : "fail";
2662
+ }
2663
+ },
2664
+ {
2665
+ id: "C7",
2666
+ axis: "cadastro",
2667
+ severity: "pending",
2668
+ label: "Sem central/slave",
2669
+ description: "Dispositivo de dom\xEDnio ligado a central sem centralId ou slaveId.",
2670
+ evaluate: (d, ctx) => {
2671
+ if (!d.attributesLoaded) return "unknown";
2672
+ if (!isIntegrated(d, ctx) || !isRecognizedProfile(d.deviceProfile)) return "notApplicable";
2673
+ if (!ctx.centralDomains.has(getDeviceCategory(d.deviceProfile))) return "notApplicable";
2674
+ return d.centralId === null || d.slaveId === null ? "fail" : "pass";
2675
+ }
2676
+ },
2677
+ {
2678
+ id: "C8",
2679
+ axis: "cadastro",
2680
+ severity: "critical",
2681
+ label: "ID duplicado",
2682
+ description: "Outro dispositivo ativo usa o mesmo ingestionId ou gcdrDeviceId.",
2683
+ evaluate: (d, ctx) => {
2684
+ if (!d.attributesLoaded) return "unknown";
2685
+ if (d.ingestionId === null && d.gcdrDeviceId === null) return "notApplicable";
2686
+ const dupIngestion = d.ingestionId !== null && (ctx.ingestionIdCounts.get(d.ingestionId) || 0) > 1;
2687
+ const dupGcdr = d.gcdrDeviceId !== null && (ctx.gcdrDeviceIdCounts.get(d.gcdrDeviceId) || 0) > 1;
2688
+ return dupIngestion || dupGcdr ? "fail" : "pass";
2689
+ }
2690
+ },
2691
+ {
2692
+ id: "A1",
2693
+ axis: "atividade",
2694
+ severity: "inactive",
2695
+ label: "Inativo no TB",
2696
+ description: "O ThingsBoard marca o dispositivo como inativo (active = false).",
2697
+ evaluate: (d) => {
2698
+ if (d.active === null) return "unknown";
2699
+ return d.active ? "pass" : "fail";
2700
+ }
2701
+ },
2702
+ {
2703
+ id: "A2",
2704
+ axis: "atividade",
2705
+ severity: "inactive",
2706
+ label: "Sem atividade recente",
2707
+ description: "A \xFAltima atividade registrada \xE9 mais antiga que o limite configurado.",
2708
+ evaluate: (d, ctx) => {
2709
+ if (d.lastActivityTime === null) return "unknown";
2710
+ return ctx.now - d.lastActivityTime > ctx.staleHours * 3600 * 1e3 ? "fail" : "pass";
2711
+ }
2712
+ }
2713
+ ];
2714
+ var RULE_BY_ID = new Map(INVENTORY_RULES.map((r) => [r.id, r]));
2715
+ function getInventoryRule(id) {
2716
+ return RULE_BY_ID.get(id);
2717
+ }
2718
+ function buildRuleContext(devices, options) {
2719
+ const integrated = /* @__PURE__ */ new Set();
2720
+ const ingestionIdCounts = /* @__PURE__ */ new Map();
2721
+ const gcdrDeviceIdCounts = /* @__PURE__ */ new Map();
2722
+ for (const d of devices) {
2723
+ if (d.ownerType === "CUSTOMER" && d.ownerName && (d.ingestionId !== null || d.gcdrDeviceId !== null)) {
2724
+ integrated.add(d.ownerName);
2725
+ }
2726
+ if (resolveLifecycle(d).status === "archived") continue;
2727
+ if (d.ingestionId !== null) ingestionIdCounts.set(d.ingestionId, (ingestionIdCounts.get(d.ingestionId) || 0) + 1);
2728
+ if (d.gcdrDeviceId !== null) gcdrDeviceIdCounts.set(d.gcdrDeviceId, (gcdrDeviceIdCounts.get(d.gcdrDeviceId) || 0) + 1);
2729
+ }
2730
+ for (const [name, expected] of Object.entries(options.integrationOverrides || {})) {
2731
+ if (expected) integrated.add(name);
2732
+ else integrated.delete(name);
2733
+ }
2734
+ return {
2735
+ integratedCustomers: integrated,
2736
+ ingestionIdCounts,
2737
+ gcdrDeviceIdCounts,
2738
+ staleHours: options.staleHours ?? 24,
2739
+ disabledRules: new Set(options.disabledRules ?? ["C6"]),
2740
+ centralDomains: new Set(options.centralDomains ?? ["energy", "water"]),
2741
+ now: options.now
2742
+ };
2743
+ }
2744
+ var CADASTRO_RANK = { critical: 2, pending: 1 };
2745
+ function evaluateDevice(device, ctx) {
2746
+ const lifecycle = resolveLifecycle(device);
2747
+ if (lifecycle.status !== "active") {
2748
+ return {
2749
+ lifecycle,
2750
+ results: INVENTORY_RULES.map((r) => ({ id: r.id, outcome: "notApplicable" })),
2751
+ failed: [],
2752
+ cadastro: { level: "excluded", partial: false },
2753
+ atividade: { level: "excluded", partial: false }
2754
+ };
2755
+ }
2756
+ const results = INVENTORY_RULES.map((rule) => ({
2757
+ id: rule.id,
2758
+ outcome: ctx.disabledRules.has(rule.id) ? "notApplicable" : rule.evaluate(device, ctx)
2759
+ }));
2760
+ const failed = results.filter((r) => r.outcome === "fail").map((r) => r.id);
2761
+ let cadastroRank = 0;
2762
+ let cadastroUnknown = false;
2763
+ let atividadeFail = false;
2764
+ let atividadeUnknown = false;
2765
+ for (const result of results) {
2766
+ const rule = RULE_BY_ID.get(result.id);
2767
+ if (rule.axis === "cadastro") {
2768
+ if (rule.severity === "info") continue;
2769
+ if (result.outcome === "fail") cadastroRank = Math.max(cadastroRank, CADASTRO_RANK[rule.severity] || 0);
2770
+ if (result.outcome === "unknown") cadastroUnknown = true;
2771
+ } else {
2772
+ if (result.outcome === "fail") atividadeFail = true;
2773
+ if (result.outcome === "unknown") atividadeUnknown = true;
2774
+ }
2775
+ }
2776
+ const cadastroLevel = cadastroRank === 2 ? "critical" : cadastroRank === 1 ? "pending" : cadastroUnknown ? "partial" : "ok";
2777
+ const atividadeLevel = atividadeFail ? "inactive" : atividadeUnknown ? "nodata" : "ok";
2778
+ return {
2779
+ lifecycle,
2780
+ results,
2781
+ failed,
2782
+ cadastro: { level: cadastroLevel, partial: cadastroRank > 0 && cadastroUnknown },
2783
+ atividade: { level: atividadeLevel, partial: atividadeFail && atividadeUnknown }
2784
+ };
2785
+ }
2786
+
2787
+ // src/utils/devices/inventory/labels.ts
2788
+ var CADASTRO_LABELS = {
2789
+ ok: "\xCDntegro",
2790
+ pending: "Pendente",
2791
+ critical: "Cr\xEDtico",
2792
+ partial: "Parcial",
2793
+ excluded: "Fora dos indicadores"
2794
+ };
2795
+ var ATIVIDADE_LABELS = {
2796
+ ok: "Reportando",
2797
+ inactive: "Sem atividade",
2798
+ nodata: "Sem dado",
2799
+ excluded: "Fora dos indicadores"
2800
+ };
2801
+ var LIFECYCLE_LABELS = {
2802
+ active: "Ativo",
2803
+ stock: "Estoque",
2804
+ archived: "Arquivado"
2805
+ };
2806
+ var DOMAIN_LABELS = {
2807
+ energy: "Energia",
2808
+ water: "\xC1gua",
2809
+ temperature: "Temperatura",
2810
+ tank: "Reservat\xF3rio",
2811
+ solenoid: "Solenoide",
2812
+ unclassified: "N\xE3o classificado"
2813
+ };
2814
+
2815
+ // src/utils/devices/inventory/filters.ts
2816
+ var NO_CUSTOMER_KEY = "__no_customer__";
2817
+ var UNCLASSIFIED_PROFILE_LABEL = "N\xE3o classificado";
2818
+ function buildInventoryRows(devices, options) {
2819
+ const ctx = "context" in options ? options.context : buildRuleContext(devices, options);
2820
+ return devices.map((device) => {
2821
+ const recognized = isRecognizedProfile(device.deviceProfile);
2822
+ return {
2823
+ device,
2824
+ evaluation: evaluateDevice(device, ctx),
2825
+ domain: recognized ? getDeviceCategory(device.deviceProfile) : "unclassified",
2826
+ profileLabel: recognized ? device.deviceProfile.toUpperCase() : UNCLASSIFIED_PROFILE_LABEL,
2827
+ customerKey: device.ownerType === "TENANT" || !device.ownerName ? NO_CUSTOMER_KEY : device.ownerName
2828
+ };
2829
+ });
2830
+ }
2831
+ var FILTER_DIMENSIONS = [
2832
+ "customers",
2833
+ "domains",
2834
+ "profiles",
2835
+ "cadastro",
2836
+ "atividade",
2837
+ "rules",
2838
+ "lifecycle"
2839
+ ];
2840
+ function createEmptyFilters() {
2841
+ return {
2842
+ customers: /* @__PURE__ */ new Set(),
2843
+ domains: /* @__PURE__ */ new Set(),
2844
+ profiles: /* @__PURE__ */ new Set(),
2845
+ cadastro: /* @__PURE__ */ new Set(),
2846
+ atividade: /* @__PURE__ */ new Set(),
2847
+ rules: /* @__PURE__ */ new Set(),
2848
+ lifecycle: /* @__PURE__ */ new Set(["active"]),
2849
+ text: ""
2850
+ };
2851
+ }
2852
+ function normalizeText(value) {
2853
+ return String(value || "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim();
2854
+ }
2855
+ function dimensionValues(row, dimension) {
2856
+ switch (dimension) {
2857
+ case "customers":
2858
+ return [row.customerKey];
2859
+ case "domains":
2860
+ return [row.domain];
2861
+ case "profiles":
2862
+ return [row.profileLabel];
2863
+ case "cadastro":
2864
+ return row.evaluation.cadastro.level === "excluded" ? [] : [row.evaluation.cadastro.level];
2865
+ case "atividade":
2866
+ return row.evaluation.atividade.level === "excluded" ? [] : [row.evaluation.atividade.level];
2867
+ case "rules":
2868
+ return row.evaluation.failed;
2869
+ case "lifecycle":
2870
+ return [row.evaluation.lifecycle.status];
2871
+ }
2872
+ }
2873
+ function matchesText(row, needle) {
2874
+ if (!needle) return true;
2875
+ const d = row.device;
2876
+ return [d.label, d.name, d.identifier, row.profileLabel, d.deviceProfile, d.ownerName, d.ingestionId].some(
2877
+ (v) => normalizeText(v).includes(needle)
2878
+ );
2879
+ }
2880
+ function matchesFilters(row, filters, except) {
2881
+ for (const dimension of FILTER_DIMENSIONS) {
2882
+ if (dimension === except) continue;
2883
+ const selected = filters[dimension];
2884
+ if (selected.size === 0) continue;
2885
+ if (!dimensionValues(row, dimension).some((v) => selected.has(v))) return false;
2886
+ }
2887
+ return matchesText(row, normalizeText(filters.text));
2888
+ }
2889
+ function applyFilters(rows, filters) {
2890
+ return rows.filter((row) => matchesFilters(row, filters));
2891
+ }
2892
+ function facetCounts(rows, filters, dimension) {
2893
+ const counts = /* @__PURE__ */ new Map();
2894
+ for (const row of rows) {
2895
+ if (!matchesFilters(row, filters, dimension)) continue;
2896
+ for (const value of dimensionValues(row, dimension)) counts.set(value, (counts.get(value) || 0) + 1);
2897
+ }
2898
+ return counts;
2899
+ }
2900
+ var CADASTRO_ORDER = { critical: 0, pending: 1, partial: 2, ok: 3, excluded: 4 };
2901
+ var ATIVIDADE_ORDER = { inactive: 0, nodata: 1, ok: 2, excluded: 3 };
2902
+ function displayName(row) {
2903
+ return row.device.label || row.device.name;
2904
+ }
2905
+ function compareText(a, b) {
2906
+ return a.localeCompare(b, "pt-BR", { sensitivity: "base", numeric: true });
2907
+ }
2908
+ function compareRows(a, b, sort) {
2909
+ switch (sort) {
2910
+ case "gravity": {
2911
+ const c = CADASTRO_ORDER[a.evaluation.cadastro.level] - CADASTRO_ORDER[b.evaluation.cadastro.level];
2912
+ if (c) return c;
2913
+ const t = ATIVIDADE_ORDER[a.evaluation.atividade.level] - ATIVIDADE_ORDER[b.evaluation.atividade.level];
2914
+ if (t) return t;
2915
+ break;
2916
+ }
2917
+ case "customer": {
2918
+ const c = compareText(a.device.ownerName || "", b.device.ownerName || "");
2919
+ if (c) return c;
2920
+ break;
2921
+ }
2922
+ case "lastActivity": {
2923
+ const ta = a.device.lastActivityTime ?? -Infinity;
2924
+ const tb = b.device.lastActivityTime ?? -Infinity;
2925
+ if (ta !== tb) return ta < tb ? -1 : 1;
2926
+ break;
2927
+ }
2928
+ case "label":
2929
+ break;
2930
+ }
2931
+ return compareText(displayName(a), displayName(b)) || compareText(a.device.name, b.device.name);
2932
+ }
2933
+ function groupKey(row, groupBy) {
2934
+ switch (groupBy) {
2935
+ case "customer":
2936
+ return row.customerKey;
2937
+ case "domain":
2938
+ return row.domain;
2939
+ case "profile":
2940
+ return row.profileLabel;
2941
+ case "none":
2942
+ return "";
2943
+ }
2944
+ }
2945
+ function getVisibleGroups(rows, filters, sort, groupBy) {
2946
+ const groups = /* @__PURE__ */ new Map();
2947
+ for (const row of applyFilters(rows, filters)) {
2948
+ const key = groupKey(row, groupBy);
2949
+ if (!groups.has(key)) groups.set(key, []);
2950
+ groups.get(key).push(row);
2951
+ }
2952
+ const title = (key) => groupBy === "domain" ? DOMAIN_LABELS[key] || key : key;
2953
+ const keys = [...groups.keys()].sort((a, b) => {
2954
+ if (a === NO_CUSTOMER_KEY) return -1;
2955
+ if (b === NO_CUSTOMER_KEY) return 1;
2956
+ return compareText(title(a), title(b));
2957
+ });
2958
+ return keys.map((key) => ({ key, rows: groups.get(key).sort((x, y) => compareRows(x, y, sort)) }));
2959
+ }
2960
+ function getVisibleRows(rows, filters, sort, groupBy) {
2961
+ return getVisibleGroups(rows, filters, sort, groupBy).flatMap((g) => g.rows);
2962
+ }
2963
+
2964
+ // src/utils/devices/inventory/dashboard.ts
2965
+ function buildDashboardSummary(rows, options = {}) {
2966
+ const cadastro = { ok: 0, pending: 0, critical: 0, partial: 0 };
2967
+ const atividade = { ok: 0, inactive: 0, nodata: 0 };
2968
+ const excluded = { stock: 0, archived: 0 };
2969
+ const byCustomer = /* @__PURE__ */ new Map();
2970
+ const ruleCounts = /* @__PURE__ */ new Map();
2971
+ const failuresByCustomerRule = /* @__PURE__ */ new Map();
2972
+ for (const row of rows) {
2973
+ const { lifecycle, cadastro: cad, atividade: atv, failed } = row.evaluation;
2974
+ if (lifecycle.status !== "active") {
2975
+ excluded[lifecycle.status]++;
2976
+ continue;
2977
+ }
2978
+ if (cad.level !== "excluded") cadastro[cad.level]++;
2979
+ if (atv.level !== "excluded") atividade[atv.level]++;
2980
+ const customer = byCustomer.get(row.customerKey) || { key: row.customerKey, critical: 0, pending: 0, total: 0 };
2981
+ customer.total++;
2982
+ if (cad.level === "critical") customer.critical++;
2983
+ if (cad.level === "pending") customer.pending++;
2984
+ byCustomer.set(row.customerKey, customer);
2985
+ for (const id of failed) {
2986
+ ruleCounts.set(id, (ruleCounts.get(id) || 0) + 1);
2987
+ const perRule = failuresByCustomerRule.get(row.customerKey) || /* @__PURE__ */ new Map();
2988
+ perRule.set(id, (perRule.get(id) || 0) + 1);
2989
+ failuresByCustomerRule.set(row.customerKey, perRule);
2990
+ }
2991
+ }
2992
+ const ruleOrder = new Map(INVENTORY_RULES.map((r, i) => [r.id, i]));
2993
+ const topRules = [...ruleCounts.entries()].map(([id, count]) => ({ id, label: getInventoryRule(id)?.label || id, count })).sort((a, b) => b.count - a.count || (ruleOrder.get(a.id) ?? 99) - (ruleOrder.get(b.id) ?? 99));
2994
+ const worstFirst = (a, b) => b.critical - a.critical || b.pending - a.pending || a.key.localeCompare(b.key, "pt-BR");
2995
+ const topCustomers = [...byCustomer.values()].filter((c) => c.critical + c.pending > 0).sort(worstFirst).slice(0, options.topCustomers ?? 10);
2996
+ const failureTotal = (key) => [...failuresByCustomerRule.get(key)?.values() || []].reduce((s, n) => s + n, 0);
2997
+ const matrixCustomers = [...failuresByCustomerRule.keys()].sort((a, b) => failureTotal(b) - failureTotal(a) || a.localeCompare(b, "pt-BR")).slice(0, options.matrixCustomers ?? 15);
2998
+ const matrixRules = topRules.map((r) => r.id);
2999
+ return {
3000
+ total: rows.length,
3001
+ inIndicators: rows.length - excluded.stock - excluded.archived,
3002
+ excluded,
3003
+ cadastro,
3004
+ atividade,
3005
+ topCustomers,
3006
+ topRules,
3007
+ matrix: {
3008
+ customers: matrixCustomers,
3009
+ rules: matrixRules,
3010
+ cells: matrixCustomers.map((c) => matrixRules.map((r) => failuresByCustomerRule.get(c)?.get(r) || 0))
3011
+ }
3012
+ };
3013
+ }
3014
+ function customerKeyLabel(key) {
3015
+ return key === NO_CUSTOMER_KEY ? "Sem cliente" : key;
3016
+ }
3017
+
3018
+ // src/utils/devices/inventory/formatLabel.ts
3019
+ function formatDeviceLabel(device) {
3020
+ const name = String(device.name ?? "").trim();
3021
+ const label = String(device.label ?? "").trim();
3022
+ if (!label || label === name) return { primary: name, secondary: null };
3023
+ return { primary: label, secondary: name || null };
3024
+ }
3025
+
3026
+ // src/utils/devices/inventory/exportRows.ts
3027
+ function formatDateTime(ms) {
3028
+ if (ms === null) return "";
3029
+ const d = new Date(ms);
3030
+ if (Number.isNaN(d.getTime())) return "";
3031
+ return d.toLocaleString("pt-BR", {
3032
+ timeZone: "America/Sao_Paulo",
3033
+ day: "2-digit",
3034
+ month: "2-digit",
3035
+ year: "numeric",
3036
+ hour: "2-digit",
3037
+ minute: "2-digit"
3038
+ });
3039
+ }
3040
+ function cadastroText(row) {
3041
+ const { level, partial } = row.evaluation.cadastro;
3042
+ return CADASTRO_LABELS[level] + (partial ? " \xB7 avalia\xE7\xE3o parcial" : "");
3043
+ }
3044
+ function failedRulesText(row) {
3045
+ return row.evaluation.failed.map((id) => getInventoryRule(id)?.label || id).join("; ");
3046
+ }
3047
+ var col = (key, header, value) => ({
3048
+ key,
3049
+ header,
3050
+ value: (row) => {
3051
+ const v = value(row);
3052
+ return v === null || v === void 0 ? "" : String(v);
3053
+ }
3054
+ });
3055
+ var INVENTORY_EXPORT_COLUMNS = [
3056
+ col("cadastro", "Cadastro", cadastroText),
3057
+ col("atividade", "Atividade", (r) => ATIVIDADE_LABELS[r.evaluation.atividade.level]),
3058
+ col("reasons", "Regras com falha", failedRulesText),
3059
+ col("label", "Label", (r) => r.device.label),
3060
+ col("name", "Name", (r) => r.device.name),
3061
+ col("deviceId", "deviceId", (r) => r.device.id),
3062
+ col("identifier", "Identificador", (r) => r.device.identifier),
3063
+ col("profile", "Perfil", (r) => r.profileLabel),
3064
+ col("domain", "Dom\xEDnio", (r) => DOMAIN_LABELS[r.domain] || r.domain),
3065
+ col("customer", "Cliente", (r) => r.customerKey === NO_CUSTOMER_KEY ? "" : r.device.ownerName),
3066
+ col("customerId", "customerId", (r) => r.device.customerId),
3067
+ col("lifecycle", "Ciclo de vida", (r) => LIFECYCLE_LABELS[r.evaluation.lifecycle.status]),
3068
+ col("active", "Ativo (TB)", (r) => r.device.active === null ? "" : r.device.active ? "Sim" : "N\xE3o"),
3069
+ col("lastActivity", "\xDAltima atividade", (r) => formatDateTime(r.device.lastActivityTime)),
3070
+ col("ingestionId", "ingestionId", (r) => r.device.ingestionId),
3071
+ col("gcdrDeviceId", "gcdrDeviceId", (r) => r.device.gcdrDeviceId),
3072
+ col("centralId", "centralId", (r) => r.device.centralId),
3073
+ col("slaveId", "slaveId", (r) => r.device.slaveId),
3074
+ col("created", "Criado em", (r) => formatDateTime(r.device.createdTime))
3075
+ ];
3076
+ var INVENTORY_PDF_COLUMN_KEYS = [
3077
+ "cadastro",
3078
+ "atividade",
3079
+ "label",
3080
+ "name",
3081
+ "profile",
3082
+ "customer",
3083
+ "reasons",
3084
+ "lastActivity"
3085
+ ];
3086
+ function pickColumns(keys) {
3087
+ return keys.map((k) => {
3088
+ const c = INVENTORY_EXPORT_COLUMNS.find((x) => x.key === k);
3089
+ if (!c) throw new Error(`Unknown inventory export column: ${k}`);
3090
+ return c;
3091
+ });
3092
+ }
3093
+ function toExportMatrix(rows, columns = INVENTORY_EXPORT_COLUMNS) {
3094
+ return {
3095
+ header: columns.map((c) => c.header),
3096
+ rows: rows.map((row) => columns.map((c) => c.value(row)))
3097
+ };
3098
+ }
3099
+ function guardFormula(value) {
3100
+ return /^[=+\-@\t\r]/.test(value) ? `'${value}` : value;
3101
+ }
3102
+ function csvCell(value) {
3103
+ const safe = guardFormula(value);
3104
+ return /[";\r\n]/.test(safe) ? `"${safe.replace(/"/g, '""')}"` : safe;
3105
+ }
3106
+ function toCSV2(matrix) {
3107
+ const lines = [matrix.header, ...matrix.rows].map((cells) => cells.map(csvCell).join(";"));
3108
+ return "\uFEFF" + lines.join("\r\n") + "\r\n";
3109
+ }
3110
+ function summarizeFilters(filters) {
3111
+ const out = [];
3112
+ const list = (set, map) => [...set].map((v) => map ? map(v) : v).join(", ");
3113
+ if (filters.customers.size) {
3114
+ out.push(["Cliente", list(filters.customers, (v) => v === NO_CUSTOMER_KEY ? "Sem cliente" : v)]);
3115
+ }
3116
+ if (filters.domains.size) out.push(["Dom\xEDnio", list(filters.domains, (v) => DOMAIN_LABELS[v] || v)]);
3117
+ if (filters.profiles.size) out.push(["Perfil", list(filters.profiles)]);
3118
+ if (filters.cadastro.size) out.push(["Cadastro", list(filters.cadastro, (v) => CADASTRO_LABELS[v])]);
3119
+ if (filters.atividade.size) {
3120
+ out.push(["Atividade", list(filters.atividade, (v) => ATIVIDADE_LABELS[v])]);
3121
+ }
3122
+ if (filters.rules.size) out.push(["Regra", list(filters.rules, (v) => getInventoryRule(v)?.label || v)]);
3123
+ if (filters.lifecycle.size) {
3124
+ out.push(["Ciclo de vida", list(filters.lifecycle, (v) => LIFECYCLE_LABELS[v])]);
3125
+ }
3126
+ if (filters.text.trim()) out.push(["Busca", filters.text.trim()]);
3127
+ return out;
3128
+ }
3129
+ function buildExportSummary(filters, dashboard, meta) {
3130
+ const n = (v) => v.toLocaleString("pt-BR");
3131
+ const active = summarizeFilters(filters);
3132
+ return [
3133
+ ["Gerado em", formatDateTime(meta.generatedAt)],
3134
+ ["Gerado por", meta.generatedBy || ""],
3135
+ ["Dispositivos exportados", n(meta.exported)],
3136
+ ...active.length ? active : [["Filtros", "Nenhum (todos os dispositivos)"]],
3137
+ ["Nos indicadores", n(dashboard.inIndicators)],
3138
+ [`Cadastro \xB7 ${CADASTRO_LABELS.critical}`, n(dashboard.cadastro.critical)],
3139
+ [`Cadastro \xB7 ${CADASTRO_LABELS.pending}`, n(dashboard.cadastro.pending)],
3140
+ [`Cadastro \xB7 ${CADASTRO_LABELS.ok}`, n(dashboard.cadastro.ok)],
3141
+ [`Cadastro \xB7 ${CADASTRO_LABELS.partial}`, n(dashboard.cadastro.partial)],
3142
+ [`Atividade \xB7 ${ATIVIDADE_LABELS.ok}`, n(dashboard.atividade.ok)],
3143
+ [`Atividade \xB7 ${ATIVIDADE_LABELS.inactive}`, n(dashboard.atividade.inactive)],
3144
+ [`Atividade \xB7 ${ATIVIDADE_LABELS.nodata}`, n(dashboard.atividade.nodata)],
3145
+ ["Fora dos indicadores (estoque / arquivados)", `${n(dashboard.excluded.stock)} / ${n(dashboard.excluded.archived)}`]
3146
+ ];
3147
+ }
3148
+ function exportFileName(filters, ext, now) {
3149
+ const only = filters.customers.size === 1 ? [...filters.customers][0] : null;
3150
+ const scope = !only ? "todos" : only === NO_CUSTOMER_KEY ? "sem-cliente" : only;
3151
+ const slug = normalizeSlug(scope);
3152
+ const pad3 = (n) => String(n).padStart(2, "0");
3153
+ const stamp = `${now.getFullYear()}-${pad3(now.getMonth() + 1)}-${pad3(now.getDate())}_${pad3(now.getHours())}${pad3(now.getMinutes())}`;
3154
+ return `inventario_${slug}_${stamp}.${ext}`;
3155
+ }
3156
+ function normalizeSlug(value) {
3157
+ return value.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "todos";
3158
+ }
3159
+
2445
3160
  // src/utils/devices/device.ts
2446
3161
  var LETTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ";
2447
3162
  var DIGITS = "23456789";
@@ -3553,9 +4268,9 @@ function selectBreakdownItems(groups, profile = getActiveProfile(), domain = "en
3553
4268
  out.push({ item, forcedCategory: null, sourceGroup: baseGroup, fromBaseGroup: true });
3554
4269
  }
3555
4270
  if (includes.size === 0) return out;
3556
- for (const groupKey of Object.keys(groups || {})) {
3557
- if (groupKey === baseGroup) continue;
3558
- for (const item of groups[groupKey] ?? []) {
4271
+ for (const groupKey2 of Object.keys(groups || {})) {
4272
+ if (groupKey2 === baseGroup) continue;
4273
+ for (const item of groups[groupKey2] ?? []) {
3559
4274
  const key = normalizeDeviceOverrideId(item?.id);
3560
4275
  if (!key || excludes.has(key) || seen.has(key)) continue;
3561
4276
  const forced = includes.get(key);
@@ -3564,7 +4279,7 @@ function selectBreakdownItems(groups, profile = getActiveProfile(), domain = "en
3564
4279
  out.push({
3565
4280
  item,
3566
4281
  forcedCategory: forced,
3567
- sourceGroup: groupKey,
4282
+ sourceGroup: groupKey2,
3568
4283
  fromBaseGroup: false,
3569
4284
  ...parentFlag(key)
3570
4285
  });
@@ -4015,13 +4730,13 @@ function resolveClassification(item, tree) {
4015
4730
  if (!current) return null;
4016
4731
  return { key: current.key, nodePath: path, matchedBy };
4017
4732
  }
4018
- function resolveSubcategory(item, groupKey, tree) {
4019
- const group = findNode(tree, groupKey);
4733
+ function resolveSubcategory(item, groupKey2, tree) {
4734
+ const group = findNode(tree, groupKey2);
4020
4735
  if (!group) return null;
4021
4736
  const kids = classifiable(group.children ?? []);
4022
4737
  if (kids.length === 0) return null;
4023
4738
  const r = resolveClassification(item, kids);
4024
- return r ? { ...r, nodePath: [groupKey, ...r.nodePath] } : null;
4739
+ return r ? { ...r, nodePath: [groupKey2, ...r.nodePath] } : null;
4025
4740
  }
4026
4741
  function findNode(tree, key) {
4027
4742
  for (const n of tree ?? []) {
@@ -9089,7 +9804,7 @@ function sortDevices(a, b, sortMode, config2) {
9089
9804
  return (a.label || "").localeCompare(b.label || "", "pt-BR", { sensitivity: "base" }) || a.value - b.value;
9090
9805
  }
9091
9806
  }
9092
- function applyFilters(config2, STATE, enriched) {
9807
+ function applyFilters2(config2, STATE, enriched) {
9093
9808
  let v = enriched.slice();
9094
9809
  if (STATE.selectedShoppingIds?.length > 0) {
9095
9810
  v = v.filter((x) => {
@@ -9588,7 +10303,7 @@ function createWidgetController(config2) {
9588
10303
  };
9589
10304
  let $root = null;
9590
10305
  async function reflow() {
9591
- const filtered = applyFilters(config2, STATE, STATE.itemsEnriched);
10306
+ const filtered = applyFilters2(config2, STATE, STATE.itemsEnriched);
9592
10307
  const { visible } = recomputePercentages(filtered);
9593
10308
  updateStats(config2, visible, context);
9594
10309
  await renderList(config2, STATE, visible, context);
@@ -9797,7 +10512,7 @@ function createWidgetController(config2) {
9797
10512
  var DeviceGridWidgetFactory = {
9798
10513
  createWidgetController,
9799
10514
  createState,
9800
- applyFilters,
10515
+ applyFilters: applyFilters2,
9801
10516
  recomputePercentages,
9802
10517
  buildEntityObject,
9803
10518
  renderList,
@@ -63221,6 +63936,171 @@ function createTicketsTab(config2) {
63221
63936
  return { destroy: () => tab.destroy() };
63222
63937
  }
63223
63938
 
63939
+ // src/components/premium-modals/settings/commercialCatalog.ts
63940
+ var DEFAULT_STORE_SEGMENTS = [
63941
+ {
63942
+ segment: "Conveni\xEAncia / Servi\xE7os",
63943
+ activities: [
63944
+ "Ag\xEAncias De Viagens",
63945
+ "Alimentos Especias",
63946
+ "Consertos",
63947
+ "Correio",
63948
+ "Educa\xE7\xE3o/Ensino",
63949
+ "Emp\xF3rios",
63950
+ "Farm\xE1cias E Drogarias",
63951
+ "Foto Revela\xE7\xE3o",
63952
+ "Lavanderia",
63953
+ "Lot\xE9rica",
63954
+ "Outros Servi\xE7os",
63955
+ "Pet Shop",
63956
+ "Servi\xE7os Automotivos",
63957
+ "Servi\xE7os Est\xE9ticos",
63958
+ "Servi\xE7os Financeiros",
63959
+ "Servi\xE7os Gr\xE1ficos",
63960
+ "Servi\xE7os M\xE9dicos(Cl\xEDnicas/Laborat\xF3rios)",
63961
+ "Servi\xE7os P\xFAblicos",
63962
+ "Tatuagem/Piercing",
63963
+ "Venda E Loca\xE7\xE3o De Ve\xEDculos"
63964
+ ]
63965
+ },
63966
+ {
63967
+ segment: "Entretenimento",
63968
+ activities: [
63969
+ "Arena Gamer",
63970
+ "Boliche",
63971
+ "Cinemas",
63972
+ "Outros(Museu,Espa\xE7o Cultural,Etc)",
63973
+ "Parques E Games"
63974
+ ]
63975
+ },
63976
+ {
63977
+ segment: "Lojas \xC2ncoras",
63978
+ activities: [
63979
+ "Academias",
63980
+ "Artigos Esportivos",
63981
+ "Centros Educacionais / Escolas / Faculdades / Universidades",
63982
+ "Constru\xE7\xE3o E Decora\xE7\xE3o",
63983
+ "Eletrodom\xE9sticos, Eletr\xF4nicos",
63984
+ "Hipermercado / Supermercado / Atacarejo",
63985
+ "Magazines E/Ou Utens\xEDlios Para O Lar E Conveni\xEAncia",
63986
+ "Moda",
63987
+ "Outros"
63988
+ ]
63989
+ },
63990
+ {
63991
+ segment: "Lojas Sat\xE9lites",
63992
+ activities: [
63993
+ "Acess\xF3rios",
63994
+ "Acess\xF3rios E Artigos Para Viagens",
63995
+ "Artigos Eletr\xF4nicos/Tecnologia Da Informa\xE7\xE3o(Ti)",
63996
+ "Brinquedos",
63997
+ "Caf\xE9s, Bares E Ch\xE1s",
63998
+ "Cal\xE7ados Esportivos",
63999
+ "Cal\xE7ados Feminimos",
64000
+ "Cal\xE7ados Geral",
64001
+ "Cal\xE7ados Infantis",
64002
+ "Cal\xE7ados Masculinos",
64003
+ "Cama/Mesa/Banho",
64004
+ "Colch\xF5es",
64005
+ "Docerias",
64006
+ "Fast-Food",
64007
+ "Livraria",
64008
+ "Moda Esportiva",
64009
+ "Moda Feminina",
64010
+ "Moda Geral",
64011
+ "Moda Infantil",
64012
+ "Moda \xCDntima E Roupas Para Dormir",
64013
+ "Moda Masculina",
64014
+ "Moda Praia",
64015
+ "Moda Unissex",
64016
+ "\xD3ticas",
64017
+ "Outros",
64018
+ "Perfumaria, Maquiagem E Cosm\xE9ticos",
64019
+ "Puericultura",
64020
+ "Relojoarias, Joalherias",
64021
+ "Restaurantes Com Servi\xE7os",
64022
+ "Restaurantes Sem Servi\xE7os",
64023
+ "Surfewear / Streetwear",
64024
+ "Telefonia Geral",
64025
+ "Utens\xEDlios P/ Lar, Acess\xF3rios P/ Decora\xE7\xE3o E Presentes"
64026
+ ]
64027
+ },
64028
+ {
64029
+ segment: "Mall E Merchandising",
64030
+ activities: [
64031
+ "Acess\xF3rios",
64032
+ "Alimenta\xE7\xE3o E Bebidas",
64033
+ "Antenas De Operadoras",
64034
+ "Artigos Esportivos",
64035
+ "Artigos Para O Lar",
64036
+ "Brinquedos",
64037
+ "Cal\xE7ados",
64038
+ "Conveni\xEAncia / Servi\xE7os",
64039
+ "Eletr\xF4nicos",
64040
+ "Eventos",
64041
+ "Geek",
64042
+ "Livraria",
64043
+ "M\xEDdia",
64044
+ "\xD3ticas",
64045
+ "Outros",
64046
+ "Papelaria",
64047
+ "Perfumaria, Maquiagem E Cosm\xE9ticos",
64048
+ "Relojoarias E Joalherias",
64049
+ "Telefonia E Acess\xF3rios",
64050
+ "Vending Machines",
64051
+ "Vestu\xE1rio"
64052
+ ]
64053
+ },
64054
+ {
64055
+ segment: "Megalojas",
64056
+ activities: [
64057
+ "Alimenta\xE7\xE3o E Bebidas",
64058
+ "Artigos Esportivos",
64059
+ "Brinquedos",
64060
+ "Centros Educacionais / Escolas / Faculdades / Universidades",
64061
+ "Constru\xE7\xE3o, Decora\xE7\xE3o",
64062
+ "Eletrodom\xE9sticos, Eletr\xF4nicos, Magazines E/Ou Utens. P/Lar",
64063
+ "Livrarias",
64064
+ "Magazines E/Ou Utens\xEDlios Para O Lar E Conveni\xEAncia",
64065
+ "Material De Escrit\xF3rio / Papelaria / Inform\xE1tica",
64066
+ "Moda",
64067
+ "Outros",
64068
+ "Puericultura"
64069
+ ]
64070
+ },
64071
+ {
64072
+ segment: "Semi-\xC2ncoras",
64073
+ activities: [
64074
+ "Academias",
64075
+ "Alimenta\xE7\xE3o E Bebidas",
64076
+ "Artigos Esportivos",
64077
+ "Brinquedos",
64078
+ "Centros Educacionais / Escolas / Faculdades / Universidades",
64079
+ "Eletrodom\xE9sticos, Eletr\xF4nicos, Magazines E/Ou Utens. P/Lar",
64080
+ "Livrarias",
64081
+ "Magazines E/Ou Utens\xEDlios Para O Lar E Conveni\xEAncia",
64082
+ "Material De Escrit\xF3rio / Papelaria / Inform\xE1tica",
64083
+ "Moda",
64084
+ "Outros"
64085
+ ]
64086
+ }
64087
+ ];
64088
+ async function loadStoreSegmentCatalog(source) {
64089
+ if (!source) return DEFAULT_STORE_SEGMENTS;
64090
+ try {
64091
+ const list = typeof source === "function" ? await source() : source;
64092
+ return Array.isArray(list) && list.length > 0 ? list : DEFAULT_STORE_SEGMENTS;
64093
+ } catch (err) {
64094
+ console.warn("[SettingsModal] segmentCatalog loader falhou \u2014 usando cat\xE1logo padr\xE3o:", err);
64095
+ return DEFAULT_STORE_SEGMENTS;
64096
+ }
64097
+ }
64098
+ function activitiesForSegment(catalog, segment, currentActivity) {
64099
+ const list = catalog.find((s) => s.segment === segment)?.activities ?? [];
64100
+ if (currentActivity && !list.includes(currentActivity)) return [...list, currentActivity];
64101
+ return list;
64102
+ }
64103
+
63224
64104
  // src/components/createInputDateRangePickerInsideDIV.ts
63225
64105
  var PREMIUM_STYLES = `
63226
64106
  .myio-daterange-wrapper {
@@ -63569,6 +64449,9 @@ var SettingsModalView = class _SettingsModalView {
63569
64449
  if (lastUpdatedEl) {
63570
64450
  lastUpdatedEl.textContent = formData.lastUpdatedTime ? this.formatTs(formData.lastUpdatedTime) : "\u2014";
63571
64451
  }
64452
+ if (this.isSuperAdmin()) {
64453
+ this.initCommercialFields(formData);
64454
+ }
63572
64455
  if (!this.config.gcdrDeviceId && formData.gcdrDeviceId) {
63573
64456
  this.config.gcdrDeviceId = formData.gcdrDeviceId;
63574
64457
  }
@@ -64202,6 +65085,7 @@ var SettingsModalView = class _SettingsModalView {
64202
65085
  </div>
64203
65086
 
64204
65087
  </div>
65088
+ ${this.isSuperAdmin() ? this.getCommercialFieldsHTML() : ""}
64205
65089
  </div>
64206
65090
 
64207
65091
  <!-- Central/gateway identity+telemetry \u2014 only for CentralSettingsModal (isGateway);
@@ -64219,6 +65103,71 @@ var SettingsModalView = class _SettingsModalView {
64219
65103
  </div>
64220
65104
  `;
64221
65105
  }
65106
+ /**
65107
+ * Campos comerciais — só @myio / superadmin (o bloco nem é renderizado para os demais,
65108
+ * então getFormData não envia essas chaves e o save não as sobrescreve).
65109
+ * Linha 1: ABL + Contrato (texto). Linha 2: Segmento → Atividade (selects em cascata,
65110
+ * opções preenchidas em initCommercialFields após carregar o catálogo).
65111
+ */
65112
+ getCommercialFieldsHTML() {
65113
+ const customerName = String(
65114
+ this.config.customerName || window.MyIOUtils?.customerName || ""
65115
+ ).trim();
65116
+ const escName = customerName.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
65117
+ return `
65118
+ <div class="commercial-block">
65119
+ <div class="commercial-title">Dados de Cadastro${escName ? ` <span class="commercial-sep">|</span> <span class="commercial-customer">${escName.toLocaleUpperCase("pt-BR")}</span>` : ""}</div>
65120
+ <div class="commercial-grid">
65121
+ <div class="commercial-field">
65122
+ <label class="identity-field-label" for="abl">ABL</label>
65123
+ <input type="text" id="abl" name="abl" class="identity-input" maxlength="50" placeholder="Ex.: 120,50 m\xB2">
65124
+ </div>
65125
+ <div class="commercial-field">
65126
+ <label class="identity-field-label" for="contract">Contrato</label>
65127
+ <input type="text" id="contract" name="contract" class="identity-input" maxlength="100" placeholder="N\xBA / identifica\xE7\xE3o do contrato">
65128
+ </div>
65129
+ <div class="commercial-field">
65130
+ <label class="identity-field-label" for="segment">Segmento</label>
65131
+ <select id="segment" name="segment" class="identity-input" disabled>
65132
+ <option value="">Carregando segmentos\u2026</option>
65133
+ </select>
65134
+ </div>
65135
+ <div class="commercial-field">
65136
+ <label class="identity-field-label" for="activity">Atividade</label>
65137
+ <select id="activity" name="activity" class="identity-input" disabled>
65138
+ <option value="">Selecione o segmento primeiro</option>
65139
+ </select>
65140
+ </div>
65141
+ </div>
65142
+ </div>
65143
+ `;
65144
+ }
65145
+ /** Carrega o catálogo de segmentos e liga a cascata Segmento → Atividade, restaurando os valores salvos. */
65146
+ async initCommercialFields(saved) {
65147
+ const segmentEl = this.modal.querySelector("#segment");
65148
+ const activityEl = this.modal.querySelector("#activity");
65149
+ if (!segmentEl || !activityEl) return;
65150
+ const esc9 = (s) => s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
65151
+ const options = (values, selected, placeholder) => `<option value="">${placeholder}</option>` + values.map((v) => `<option value="${esc9(v)}"${v === selected ? " selected" : ""}>${esc9(v)}</option>`).join("");
65152
+ const catalog = await loadStoreSegmentCatalog(this.config.segmentCatalog);
65153
+ const savedSegment = String(saved.segment || "");
65154
+ const savedActivity = String(saved.activity || "");
65155
+ const segments = catalog.map((s) => s.segment);
65156
+ if (savedSegment && !segments.includes(savedSegment)) segments.push(savedSegment);
65157
+ segmentEl.innerHTML = options(segments, savedSegment, "Selecione\u2026");
65158
+ segmentEl.disabled = false;
65159
+ const fillActivities = (segment, selected) => {
65160
+ if (!segment) {
65161
+ activityEl.innerHTML = options([], "", "Selecione o segmento primeiro");
65162
+ activityEl.disabled = true;
65163
+ return;
65164
+ }
65165
+ activityEl.innerHTML = options(activitiesForSegment(catalog, segment, selected), selected, "Selecione\u2026");
65166
+ activityEl.disabled = false;
65167
+ };
65168
+ fillActivities(savedSegment, savedActivity);
65169
+ segmentEl.addEventListener("change", () => fillActivities(segmentEl.value, ""));
65170
+ }
64222
65171
  /** RFC-0171: Offset de Temperatura — sempre visível para domain=temperature; editável só para @myio.com.br */
64223
65172
  getTemperatureOffsetHTML() {
64224
65173
  const superAdmin = this.isSuperAdmin();
@@ -65902,6 +66851,49 @@ var SettingsModalView = class _SettingsModalView {
65902
66851
  cursor: default;
65903
66852
  }
65904
66853
 
66854
+ /* Campos comerciais (s\xF3 @myio / superadmin) */
66855
+ .commercial-block {
66856
+ margin-top: 14px;
66857
+ padding-top: 12px;
66858
+ border-top: 1px dashed #e5e7eb;
66859
+ }
66860
+ .commercial-title {
66861
+ display: flex;
66862
+ align-items: center;
66863
+ gap: 8px;
66864
+ font-size: 12px;
66865
+ font-weight: 700;
66866
+ color: #495057;
66867
+ margin-bottom: 8px;
66868
+ }
66869
+ .commercial-sep {
66870
+ color: #ced4da;
66871
+ font-weight: 400;
66872
+ }
66873
+ .commercial-customer {
66874
+ color: var(--myio-brand-700, #3e1a7d);
66875
+ letter-spacing: 0.3px;
66876
+ }
66877
+ .commercial-grid {
66878
+ display: grid;
66879
+ grid-template-columns: 1fr 1fr;
66880
+ gap: 10px 16px;
66881
+ }
66882
+ .commercial-field {
66883
+ display: flex;
66884
+ flex-direction: column;
66885
+ gap: 5px;
66886
+ }
66887
+ select.identity-input {
66888
+ height: 30px;
66889
+ cursor: pointer;
66890
+ }
66891
+ select.identity-input:disabled {
66892
+ background: #f8f9fa;
66893
+ color: #9ca3af;
66894
+ cursor: not-allowed;
66895
+ }
66896
+
65905
66897
  /* Col 3: date info block */
65906
66898
  .identity-dates-block {
65907
66899
  grid-column: 3;
@@ -66900,6 +67892,11 @@ var SettingsModalView = class _SettingsModalView {
66900
67892
  color: #6c757d;
66901
67893
  border-color: #373a40;
66902
67894
  }
67895
+ .theme-dark .commercial-block { border-top-color: #373a40; }
67896
+ .theme-dark .commercial-title { color: #ced4da; }
67897
+ .theme-dark .commercial-sep { color: #495057; }
67898
+ .theme-dark .commercial-customer { color: #c4b5fd; }
67899
+ .theme-dark select.identity-input:disabled { background: #1a1b1e; color: #6c757d; }
66903
67900
  .theme-dark .device-name-subtitle {
66904
67901
  color: #6c757d;
66905
67902
  }
@@ -77387,6 +78384,8 @@ var DefaultSettingsFetcher = class _DefaultSettingsFetcher {
77387
78384
  attributes.lastUpdatedTime = attr.value;
77388
78385
  } else if (attr.key === "gcdrDeviceId") {
77389
78386
  attributes.gcdrDeviceId = attr.value;
78387
+ } else if (["abl", "contract", "segment", "activity"].includes(attr.key)) {
78388
+ attributes[attr.key] = String(attr.value);
77390
78389
  }
77391
78390
  }
77392
78391
  }
@@ -77416,7 +78415,12 @@ var DefaultSettingsFetcher = class _DefaultSettingsFetcher {
77416
78415
  const stringFields = [
77417
78416
  "label",
77418
78417
  "floor",
77419
- "identifier"
78418
+ "identifier",
78419
+ // Campos comerciais (só @myio / superadmin no modal)
78420
+ "abl",
78421
+ "contract",
78422
+ "segment",
78423
+ "activity"
77420
78424
  ];
77421
78425
  for (const field of stringFields) {
77422
78426
  if (data[field] && typeof data[field] === "string") {
@@ -77526,6 +78530,7 @@ var SettingsController = class {
77526
78530
  superadmin: params.superadmin,
77527
78531
  userEmail: params.userEmail || window.MyIOUtils?.currentUserEmail || null,
77528
78532
  holdingAdmin: params.holdingAdmin ?? window.MyIOUtils?.HoldingAdmin ?? false,
78533
+ segmentCatalog: params.segmentCatalog,
77529
78534
  // RFC-0144: Pass enableAnnotationsOnboarding flag for onboarding control
77530
78535
  enableAnnotationsOnboarding: params.enableAnnotationsOnboarding ?? false,
77531
78536
  // RFC-0180: Raw device name + GCDR identifiers for Alarms tab
@@ -77849,6 +78854,12 @@ var SettingsController = class {
77849
78854
  if (formData.floor && formData.floor.length > 50) {
77850
78855
  errors.push("Floor must be 50 characters or less");
77851
78856
  }
78857
+ if (formData.abl && formData.abl.length > 50) {
78858
+ errors.push("ABL must be 50 characters or less");
78859
+ }
78860
+ if (formData.contract && formData.contract.length > 100) {
78861
+ errors.push("Contract must be 100 characters or less");
78862
+ }
77852
78863
  if (formData.storeNumber && formData.storeNumber.length > 20) {
77853
78864
  errors.push("Store number must be 20 characters or less");
77854
78865
  }
@@ -78602,7 +79613,7 @@ function formatDate4(date, locale) {
78602
79613
  year: "numeric"
78603
79614
  });
78604
79615
  }
78605
- function formatDateTime(date, locale) {
79616
+ function formatDateTime2(date, locale) {
78606
79617
  return date.toLocaleDateString(locale, {
78607
79618
  day: "2-digit",
78608
79619
  month: "2-digit",
@@ -78728,7 +79739,7 @@ function processMultiSeriesChartData(rawData, keys, correctionFactor, locale, ag
78728
79739
  value: maxPoint.y,
78729
79740
  timestamp: maxPoint.x,
78730
79741
  formattedValue: maxPoint.y.toFixed(2),
78731
- formattedTime: formatDateTime(new Date(maxPoint.x), locale),
79742
+ formattedTime: formatDateTime2(new Date(maxPoint.x), locale),
78732
79743
  key
78733
79744
  };
78734
79745
  if (!globalPeak || seriesPeak.value > globalPeak.value) {
@@ -82257,8 +83268,8 @@ function buildReportItemsList(groups, domain, group) {
82257
83268
  if (group === "todos") {
82258
83269
  return Object.entries(g).filter(([key]) => key !== "ocultos").flatMap(([key, items]) => (items || []).map((d) => toItem(d, domain, GROUP_LABELS[key] || key)));
82259
83270
  }
82260
- const groupKey = group === "area_comum" ? "areacomum" : group;
82261
- return (g[groupKey] || []).map((d) => toItem(d, domain, null));
83271
+ const groupKey2 = group === "area_comum" ? "areacomum" : group;
83272
+ return (g[groupKey2] || []).map((d) => toItem(d, domain, null));
82262
83273
  }
82263
83274
 
82264
83275
  // src/components/premium-modals/reports-hub/handlers.ts
@@ -100360,7 +101371,7 @@ var DOMAIN_ICONS = {
100360
101371
  water: DOMAIN_MAP.water.icon,
100361
101372
  temperature: DOMAIN_MAP.temperature.icon
100362
101373
  };
100363
- var DOMAIN_LABELS = {
101374
+ var DOMAIN_LABELS2 = {
100364
101375
  energy: DOMAIN_MAP.energy.name,
100365
101376
  water: DOMAIN_MAP.water.name,
100366
101377
  temperature: DOMAIN_MAP.temperature.name
@@ -100679,10 +101690,10 @@ function buildTemplateExport(params) {
100679
101690
  locale,
100680
101691
  includeChart,
100681
101692
  includeStats,
100682
- headerText: headerText || `${DOMAIN_LABELS[domain]} Report`,
101693
+ headerText: headerText || `${DOMAIN_LABELS2[domain]} Report`,
100683
101694
  footerText: footerText || "Generated by MyIO Platform",
100684
101695
  domainIcon: DOMAIN_ICONS[domain],
100685
- domainLabel: DOMAIN_LABELS[domain],
101696
+ domainLabel: DOMAIN_LABELS2[domain],
100686
101697
  domainUnit: DOMAIN_UNITS[domain]
100687
101698
  };
100688
101699
  }
@@ -100774,7 +101785,7 @@ function myioExportData(data, config2, options) {
100774
101785
  }
100775
101786
  var EXPORT_DEFAULT_COLORS = DEFAULT_COLORS4;
100776
101787
  var EXPORT_DOMAIN_ICONS = DOMAIN_ICONS;
100777
- var EXPORT_DOMAIN_LABELS = DOMAIN_LABELS;
101788
+ var EXPORT_DOMAIN_LABELS = DOMAIN_LABELS2;
100778
101789
  var EXPORT_DOMAIN_UNITS = DOMAIN_UNITS;
100779
101790
 
100780
101791
  // src/components/DistributionChart/colorManager.ts
@@ -108641,11 +109652,11 @@ function renderStep2(state6, modalId, colors2, t) {
108641
109652
  const sortedDevices = sortDevices2(searchFilteredDevices, sortField, sortOrder);
108642
109653
  const gridHeight = state6.isMaximized ? "calc(100vh - 340px)" : "360px";
108643
109654
  const hasActiveFilters = filterTypes.length > 0 || filterDeviceTypes.length > 0 || filterDeviceProfiles.length > 0 || filterStatuses.length > 0 || filterTelemetryKeys.length > 0;
108644
- const renderSortableHeader = (col, label, field, width) => {
109655
+ const renderSortableHeader = (col2, label, field, width) => {
108645
109656
  const isActive = sortField === field;
108646
109657
  const arrow = isActive ? sortOrder === "asc" ? "\u25B2" : "\u25BC" : "\u25BD";
108647
109658
  return `
108648
- <div id="${modalId}-sort-col-${col}" data-sort-field="${field}" style="
109659
+ <div id="${modalId}-sort-col-${col2}" data-sort-field="${field}" style="
108649
109660
  width: ${width}px; padding: 0 6px; display: flex; align-items: center; justify-content: space-between;
108650
109661
  border-right: 1px solid ${colors2.border}; cursor: pointer; user-select: none;
108651
109662
  ${isActive ? `background: rgba(62,26,125,0.1);` : ""}
@@ -110519,8 +111530,8 @@ function setupEventListeners5(container, state6, modalId, t, onClose) {
110519
111530
  { col: "centralId", field: "centralId" },
110520
111531
  { col: "slaveId", field: "slaveId" }
110521
111532
  ];
110522
- sortableColumns.forEach(({ col, field }) => {
110523
- document.getElementById(`${modalId}-sort-col-${col}`)?.addEventListener("click", () => {
111533
+ sortableColumns.forEach(({ col: col2, field }) => {
111534
+ document.getElementById(`${modalId}-sort-col-${col2}`)?.addEventListener("click", () => {
110524
111535
  if (state6.deviceSort.field === field) {
110525
111536
  state6.deviceSort.order = state6.deviceSort.order === "asc" ? "desc" : "asc";
110526
111537
  } else {
@@ -116420,10 +117431,10 @@ var UserListTab = class {
116420
117431
  const popup = document.createElement("div");
116421
117432
  popup.className = "um-assign-popup";
116422
117433
  popup.setAttribute("data-theme", this.config.theme || "light");
116423
- const displayName = this.displayName(user);
117434
+ const displayName2 = this.displayName(user);
116424
117435
  popup.innerHTML = `
116425
117436
  <div class="um-assign-popup-header">
116426
- <span class="um-assign-popup-title">\u{1F511} Fun\xE7\xF5es \u2014 ${this.esc(displayName)}</span>
117437
+ <span class="um-assign-popup-title">\u{1F511} Fun\xE7\xF5es \u2014 ${this.esc(displayName2)}</span>
116427
117438
  <button class="um-assign-popup-close" type="button">\u2715</button>
116428
117439
  </div>
116429
117440
  <div class="um-assign-popup-body">
@@ -116545,7 +117556,7 @@ var UserListTab = class {
116545
117556
  tooltip.setAttribute("data-theme", this.config.theme || "light");
116546
117557
  this.syncTooltipEl = tooltip;
116547
117558
  const syncing = this.gcdrSyncing.has(uid2);
116548
- const displayName = this.displayName(user);
117559
+ const displayName2 = this.displayName(user);
116549
117560
  let bodyHtml;
116550
117561
  if (syncing) {
116551
117562
  bodyHtml = `<div class="um-sync-tooltip-row" style="justify-content:center;padding:8px 0;">
@@ -116595,7 +117606,7 @@ var UserListTab = class {
116595
117606
  <polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/>
116596
117607
  <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
116597
117608
  </svg>
116598
- <span>GCDR Sync \u2014 ${this.esc(displayName)}</span>
117609
+ <span>GCDR Sync \u2014 ${this.esc(displayName2)}</span>
116599
117610
  </div>
116600
117611
  <div class="um-sync-tooltip-body">${bodyHtml}</div>
116601
117612
  `;
@@ -123663,9 +124674,9 @@ var MenuView = class {
123663
124674
  const isActive = isActiveTab && contextId === currentContextId;
123664
124675
  el2.classList.toggle("is-active", isActive);
123665
124676
  });
123666
- this.root.querySelectorAll(".myio-unified-column").forEach((col) => {
123667
- const tabId = col.classList.contains("energy") ? "energy" : col.classList.contains("water") ? "water" : col.classList.contains("temperature") ? "temperature" : "";
123668
- col.classList.toggle("is-active-column", tabId === this.activeTabId);
124677
+ this.root.querySelectorAll(".myio-unified-column").forEach((col2) => {
124678
+ const tabId = col2.classList.contains("energy") ? "energy" : col2.classList.contains("water") ? "water" : col2.classList.contains("temperature") ? "temperature" : "";
124679
+ col2.classList.toggle("is-active-column", tabId === this.activeTabId);
123669
124680
  });
123670
124681
  }
123671
124682
  /**
@@ -137632,8 +138643,8 @@ var TelemetryGridShoppingView = class {
137632
138643
  this.controller.setSortMode(mode);
137633
138644
  });
137634
138645
  });
137635
- const applyFilters2 = this.filterModalEl.querySelector("#applyFilters");
137636
- applyFilters2?.addEventListener("click", () => {
138646
+ const applyFilters3 = this.filterModalEl.querySelector("#applyFilters");
138647
+ applyFilters3?.addEventListener("click", () => {
137637
138648
  this.applySelectedFilters();
137638
138649
  this.closeFilterModal();
137639
138650
  this.renderCards();
@@ -147064,11 +148075,11 @@ var AlarmsNotificationsPanelView = class {
147064
148075
  this.root.addEventListener("click", (e) => {
147065
148076
  const th = e.target.closest("[data-sort-col]");
147066
148077
  if (!th) return;
147067
- const col = th.getAttribute("data-sort-col");
147068
- if (this.sortCol === col) {
148078
+ const col2 = th.getAttribute("data-sort-col");
148079
+ if (this.sortCol === col2) {
147069
148080
  this.sortDir = this.sortDir === "asc" ? "desc" : "asc";
147070
148081
  } else {
147071
- this.sortCol = col;
148082
+ this.sortCol = col2;
147072
148083
  this.sortDir = "asc";
147073
148084
  }
147074
148085
  this.renderListContent(this.controller.getState());
@@ -147712,10 +148723,10 @@ var AlarmsNotificationsPanelView = class {
147712
148723
  }
147713
148724
  });
147714
148725
  }
147715
- const th = (label, col, extraClass = "") => {
147716
- const active = this.sortCol === col;
148726
+ const th = (label, col2, extraClass = "") => {
148727
+ const active = this.sortCol === col2;
147717
148728
  const icon2 = active ? `<span class="atbl-sort-icon is-active">${this.sortDir === "asc" ? "\u25B2" : "\u25BC"}</span>` : `<span class="atbl-sort-icon">\u21C5</span>`;
147718
- return `<th class="atbl-th atbl-th--sortable${extraClass ? " " + extraClass : ""}${active ? " is-sorted" : ""}" data-sort-col="${col}">${label}${icon2}</th>`;
148729
+ return `<th class="atbl-th atbl-th--sortable${extraClass ? " " + extraClass : ""}${active ? " is-sorted" : ""}" data-sort-col="${col2}">${label}${icon2}</th>`;
147719
148730
  };
147720
148731
  const rows = alarms.map((alarm) => {
147721
148732
  const sev = SEVERITY_CONFIG[alarm.severity];
@@ -148442,10 +149453,10 @@ var AlarmsNotificationsPanelView = class {
148442
149453
  const gOcc = totalOcc(groupAlarms);
148443
149454
  const gSCfg = severityCfg(gSev);
148444
149455
  const gStCfg = stateCfg(gSt);
148445
- const groupKey = `${ruleKey}||${central}`;
148446
- const isGroupOpen = this.ruleFirstOpenGroups.has(groupKey);
149456
+ const groupKey2 = `${ruleKey}||${central}`;
149457
+ const isGroupOpen = this.ruleFirstOpenGroups.has(groupKey2);
148447
149458
  const escCentral = this.esc(central);
148448
- const escGroupKey = this.esc(groupKey);
149459
+ const escGroupKey = this.esc(groupKey2);
148449
149460
  const deviceMap = /* @__PURE__ */ new Map();
148450
149461
  for (const atom of atoms) {
148451
149462
  if (!deviceMap.has(atom.device)) deviceMap.set(atom.device, []);
@@ -151649,7 +152660,7 @@ function formatDateShort(dateStr) {
151649
152660
  const month = String(date.getMonth() + 1).padStart(2, "0");
151650
152661
  return `${day}/${month}`;
151651
152662
  }
151652
- function formatDateTime2(dateStr) {
152663
+ function formatDateTime3(dateStr) {
151653
152664
  const date = new Date(dateStr);
151654
152665
  const day = String(date.getDate()).padStart(2, "0");
151655
152666
  const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -151723,7 +152734,7 @@ function renderMTBFTimelineChart(data) {
151723
152734
  segmentPaths += `
151724
152735
  <rect x="${x1}" y="${onY}" width="${segWidth}" height="${barHeight}"
151725
152736
  fill="#22c55e" rx="3" class="mtbf-segment operating"
151726
- data-tooltip="Operacao|Inicio: ${formatDateTime2(seg.startTime)}|Fim: ${formatDateTime2(seg.endTime)}|Duracao: ${formatHoursDisplay(seg.durationHours)}${seg.activeEquipments ? `|Equipamentos ativos: ${seg.activeEquipments}/${data.totalEquipment}` : ""}" />
152737
+ data-tooltip="Operacao|Inicio: ${formatDateTime3(seg.startTime)}|Fim: ${formatDateTime3(seg.endTime)}|Duracao: ${formatHoursDisplay(seg.durationHours)}${seg.activeEquipments ? `|Equipamentos ativos: ${seg.activeEquipments}/${data.totalEquipment}` : ""}" />
151727
152738
  `;
151728
152739
  const centerX = x1 + segWidth / 2;
151729
152740
  if (segWidth > 50) {
@@ -151738,8 +152749,8 @@ function renderMTBFTimelineChart(data) {
151738
152749
  <rect x="${x1}" y="${onY}" width="${segWidth}" height="${barHeight}"
151739
152750
  fill="transparent" class="tooltip-trigger"
151740
152751
  data-type="operating"
151741
- data-start="${formatDateTime2(seg.startTime)}"
151742
- data-end="${formatDateTime2(seg.endTime)}"
152752
+ data-start="${formatDateTime3(seg.startTime)}"
152753
+ data-end="${formatDateTime3(seg.endTime)}"
151743
152754
  data-duration="${formatHoursDisplay(seg.durationHours)}"
151744
152755
  data-active="${seg.activeEquipments || data.totalEquipment}" />
151745
152756
  `;
@@ -151748,7 +152759,7 @@ function renderMTBFTimelineChart(data) {
151748
152759
  segmentPaths += `
151749
152760
  <rect x="${x1}" y="${offY - 15}" width="${segWidth}" height="20"
151750
152761
  fill="#ef4444" rx="2" class="mtbf-segment stopped"
151751
- data-tooltip="\u26A0\uFE0F Falha detectada|Data: ${formatDateTime2(seg.startTime)}|Tempo parado: ${formatHoursDisplay(seg.durationHours)}${seg.affectedEquipments ? `|Equipamentos afetados: ${seg.affectedEquipments}` : ""}${seg.failureType ? `|Tipo: ${seg.failureType}` : ""}" />
152762
+ data-tooltip="\u26A0\uFE0F Falha detectada|Data: ${formatDateTime3(seg.startTime)}|Tempo parado: ${formatHoursDisplay(seg.durationHours)}${seg.affectedEquipments ? `|Equipamentos afetados: ${seg.affectedEquipments}` : ""}${seg.failureType ? `|Tipo: ${seg.failureType}` : ""}" />
151752
152763
  `;
151753
152764
  failureMarkers += `
151754
152765
  <g class="failure-marker" transform="translate(${markerX}, ${offY + 10})">
@@ -151766,7 +152777,7 @@ function renderMTBFTimelineChart(data) {
151766
152777
  <rect x="${x1 - 10}" y="${offY - 20}" width="${segWidth + 20}" height="45"
151767
152778
  fill="transparent" class="tooltip-trigger"
151768
152779
  data-type="stopped"
151769
- data-date="${formatDateTime2(seg.startTime)}"
152780
+ data-date="${formatDateTime3(seg.startTime)}"
151770
152781
  data-duration="${formatHoursDisplay(seg.durationHours)}"
151771
152782
  data-affected="${seg.affectedEquipments || "?"}"
151772
152783
  data-failure-type="${seg.failureType || "Nao especificado"}" />
@@ -151789,7 +152800,7 @@ function renderMTBFTimelineChart(data) {
151789
152800
  <rect x="${x1 - 5}" y="${offY - 20}" width="${segWidth + 10}" height="45"
151790
152801
  fill="transparent" class="tooltip-trigger"
151791
152802
  data-type="maintenance"
151792
- data-start="${formatDateTime2(seg.startTime)}"
152803
+ data-start="${formatDateTime3(seg.startTime)}"
151793
152804
  data-duration="${formatHoursDisplay(seg.durationHours)}" />
151794
152805
  `;
151795
152806
  }
@@ -159698,7 +160709,7 @@ function formatDateShort2(dateStr) {
159698
160709
  const month = String(date.getMonth() + 1).padStart(2, "0");
159699
160710
  return `${day}/${month}`;
159700
160711
  }
159701
- function formatDateTime3(dateStr) {
160712
+ function formatDateTime4(dateStr) {
159702
160713
  const date = new Date(dateStr);
159703
160714
  const day = String(date.getDate()).padStart(2, "0");
159704
160715
  const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -159777,8 +160788,8 @@ function renderTimelineSVG(data, config2) {
159777
160788
  fill="transparent"
159778
160789
  class="tooltip-trigger"
159779
160790
  data-state="${stateClass}"
159780
- data-start="${formatDateTime3(seg.startTime)}"
159781
- data-end="${formatDateTime3(seg.endTime)}"
160791
+ data-start="${formatDateTime4(seg.startTime)}"
160792
+ data-end="${formatDateTime4(seg.endTime)}"
159782
160793
  data-duration="${formatDuration2(seg.durationMinutes)}"
159783
160794
  data-source="${seg.source || "unknown"}"
159784
160795
  />
@@ -168544,8 +169555,8 @@ async function drawTag(doc, x, y, w, h3, device, gateway, allDevices) {
168544
169555
  const titleAreaH = h3 * 0.15;
168545
169556
  const qrAreaTop = y + titleAreaH;
168546
169557
  const qrAreaH = h3 - titleAreaH - 12;
168547
- const displayName = generateDeviceNameWithPrefix(device, void 0, allDevices);
168548
- const rawName = sanitizePdf(displayName);
169558
+ const displayName2 = generateDeviceNameWithPrefix(device, void 0, allDevices);
169559
+ const rawName = sanitizePdf(displayName2);
168549
169560
  const nameFont = Math.max(6, Math.min(11, titleAreaH * 0.45));
168550
169561
  doc.setFont("helvetica", "bold");
168551
169562
  doc.setFontSize(nameFont);
@@ -168622,8 +169633,8 @@ async function exportDeviceTagsPdf(devices, gateway, layout = "grid_4x7") {
168622
169633
  if (page > 0) doc.addPage();
168623
169634
  paintPage(page + 1, totalPages);
168624
169635
  for (let row = 0; row < rows && idx < devices.length; row++) {
168625
- for (let col = 0; col < cols && idx < devices.length; col++) {
168626
- const tx = usableX + col * (tagW + gutterX);
169636
+ for (let col2 = 0; col2 < cols && idx < devices.length; col2++) {
169637
+ const tx = usableX + col2 * (tagW + gutterX);
168627
169638
  const ty = usableY + row * (tagH + gutterY);
168628
169639
  await drawTag(doc, tx, ty, tagW, tagH, devices[idx], gateway, devices);
168629
169640
  idx++;
@@ -170764,7 +171775,7 @@ var DOMAIN_ICONS2 = {
170764
171775
  temperature: "\u{1F321}\uFE0F",
170765
171776
  unknown: "\xB7"
170766
171777
  };
170767
- var DOMAIN_LABELS2 = {
171778
+ var DOMAIN_LABELS3 = {
170768
171779
  energy: "Energia",
170769
171780
  water: "\xC1gua",
170770
171781
  temperature: "Temperatura",
@@ -170997,7 +172008,7 @@ function _applyFilterToDevice(device, filter) {
170997
172008
  function _labelForGroup(key, groupBy, sample) {
170998
172009
  if (groupBy === "identifier") return key;
170999
172010
  if (groupBy === "device") return sample?.label || sample?.name || key;
171000
- return DOMAIN_LABELS2[key] ?? key;
172011
+ return DOMAIN_LABELS3[key] ?? key;
171001
172012
  }
171002
172013
  function _iconForGroup(key, groupBy) {
171003
172014
  if (groupBy === "domain") return DOMAIN_ICONS2[key];
@@ -173032,8 +174043,8 @@ ${this._renderToolbar(filteredCount, totalAllUnfiltered)}
173032
174043
  </section>`;
173033
174044
  }
173034
174045
  /** Toggle a single group's collapsed state (scoped to current tab). */
173035
- _toggleGroupCollapsed(groupKey) {
173036
- const compoundKey = `${this.activeTab}::${groupKey}`;
174046
+ _toggleGroupCollapsed(groupKey2) {
174047
+ const compoundKey = `${this.activeTab}::${groupKey2}`;
173037
174048
  if (this.collapsedKeys.has(compoundKey)) {
173038
174049
  this.collapsedKeys.delete(compoundKey);
173039
174050
  } else {
@@ -174145,7 +175156,7 @@ function formatRoundedDuration(ms) {
174145
175156
  if (s < 86400) return `${Math.round(s / 3600)}h`;
174146
175157
  return `${Math.round(s / 86400)}d`;
174147
175158
  }
174148
- function formatDateTime4(iso, language) {
175159
+ function formatDateTime5(iso, language) {
174149
175160
  const t = Date.parse(iso);
174150
175161
  if (Number.isNaN(t)) return iso;
174151
175162
  return new Date(t).toLocaleString(language === "en" ? "en-US" : "pt-BR", {
@@ -174176,7 +175187,7 @@ function renderBarHtml(segments, language, legend) {
174176
175187
  return `<div class="myio-tlmodal__bar" role="img" aria-label="${escAttr5(
174177
175188
  segments.map((s) => `${legend[s.status]} ${formatRoundedDuration(s.durationMs)}`).join(", ")
174178
175189
  )}">` + segments.map((s, i) => {
174179
- const title = `${legend[s.status]} \xB7 ${formatDateTime4(s.start, language)} \u2192 ${formatDateTime4(
175190
+ const title = `${legend[s.status]} \xB7 ${formatDateTime5(s.start, language)} \u2192 ${formatDateTime5(
174180
175191
  s.end,
174181
175192
  language
174182
175193
  )} (${formatRoundedDuration(s.durationMs)})`;
@@ -174193,7 +175204,7 @@ function renderListHtml(transitions, language, emptyText) {
174193
175204
  return `<ul class="myio-tlmodal__list">` + sorted.map(
174194
175205
  (t) => `<li class="myio-tlmodal__list-item">
174195
175206
  <span class="myio-tlmodal__list-transition">${escAttr5(t.from_status)} \u2192 ${escAttr5(t.to_status)}</span>
174196
- <span class="myio-tlmodal__list-time">${escAttr5(formatDateTime4(t.created_at, language))}</span>
175207
+ <span class="myio-tlmodal__list-time">${escAttr5(formatDateTime5(t.created_at, language))}</span>
174197
175208
  ${t.probe_result ? `<span class="myio-tlmodal__list-probe">${escAttr5(t.probe_result)}</span>` : ""}
174198
175209
  <span class="myio-tlmodal__list-mode myio-tlmodal__list-mode--${t.mode}">${escAttr5(t.mode)}</span>
174199
175210
  </li>`
@@ -175255,6 +176266,7 @@ var version = package_default.version || "0.0.0";
175255
176266
  DEFAULT_SIDEBAR_CONFIG,
175256
176267
  DEFAULT_SOLENOID_SETTINGS,
175257
176268
  DEFAULT_SOLENOID_STATE,
176269
+ DEFAULT_STORE_SEGMENTS,
175258
176270
  DEFAULT_SWITCH_SETTINGS,
175259
176271
  DEFAULT_SWITCH_STATE,
175260
176272
  DEFAULT_TABS,
@@ -175836,6 +176848,7 @@ var version = package_default.version || "0.0.0";
175836
176848
  injectSolenoidControlStyles,
175837
176849
  injectSwitchControlStyles,
175838
176850
  interpolateTemperature,
176851
+ inventory,
175839
176852
  isAlarmActive,
175840
176853
  isAnnotationOverdue,
175841
176854
  isAssetCode,