myio-js-library 0.1.529 → 0.1.530

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
@@ -63,6 +63,7 @@ __export(src_exports, {
63
63
  CONSUMPTION_CHART_COLORS: () => DEFAULT_COLORS,
64
64
  CONSUMPTION_CHART_DEFAULTS: () => DEFAULT_CONFIG,
65
65
  CONSUMPTION_THEME_COLORS: () => THEME_COLORS2,
66
+ COVERAGE_STYLE_ID: () => COVERAGE_STYLE_ID,
66
67
  CUSTOMER_CODE_RE: () => CUSTOMER_CODE_RE,
67
68
  CUSTOMER_NAME_STOPWORDS: () => CUSTOMER_NAME_STOPWORDS,
68
69
  CardGridPanel: () => CardGridPanel,
@@ -130,6 +131,7 @@ __export(src_exports, {
130
131
  DEFAULT_SWITCH_STATE: () => DEFAULT_SWITCH_STATE,
131
132
  DEFAULT_TABS: () => DEFAULT_TABS,
132
133
  DEFAULT_WATER_GROUP_COLORS: () => DEFAULT_WATER_GROUP_COLORS,
134
+ DEVICE_CATEGORY_STYLE_ID: () => DEVICE_CATEGORY_STYLE_ID,
133
135
  DEVICE_COUNT_KEYS: () => DEVICE_COUNT_KEYS,
134
136
  DEVICE_OPERATIONAL_CARD_GRID_STYLES: () => DEVICE_OPERATIONAL_CARD_GRID_STYLES,
135
137
  DEVICE_OPERATIONAL_CARD_STYLES: () => DEVICE_OPERATIONAL_CARD_STYLES,
@@ -141,6 +143,7 @@ __export(src_exports, {
141
143
  DOMAIN_CODES: () => DOMAIN_CODES,
142
144
  DOMAIN_MAP: () => DOMAIN_MAP,
143
145
  DeviceCategory: () => DeviceCategory,
146
+ DeviceCategoryConflictError: () => DeviceCategoryConflictError,
144
147
  DeviceComparisonTooltip: () => DeviceComparisonTooltip,
145
148
  DeviceContextType: () => ContextType,
146
149
  DeviceDomainType: () => DomainType2,
@@ -188,6 +191,8 @@ __export(src_exports, {
188
191
  GRANULARITY_SELECTOR_CSS_PREFIX: () => GRANULARITY_SELECTOR_CSS_PREFIX,
189
192
  GRID_SORT_OPTIONS: () => GRID_SORT_OPTIONS,
190
193
  GoalsModal: () => GoalsModal,
194
+ GoalsMoneyApiError: () => GoalsMoneyApiError,
195
+ GoalsMoneyClient: () => GoalsMoneyClient,
191
196
  HEADER_CSS_PREFIX: () => HEADER_CSS_PREFIX,
192
197
  HEADER_DEFAULT_CARD_COLORS: () => DEFAULT_CARD_COLORS,
193
198
  HEADER_DEFAULT_CONFIG_TEMPLATE: () => HEADER_DEFAULT_CONFIG_TEMPLATE,
@@ -215,6 +220,9 @@ __export(src_exports, {
215
220
  MEASUREMENT_DOMAIN_CONFIG: () => DOMAIN_CONFIG5,
216
221
  MENU_SHOPPING_CSS_PREFIX: () => MENU_SHOPPING_CSS_PREFIX,
217
222
  METRO_TILE_COLORS: () => METRO_TILE_COLORS,
223
+ MONEY_DASH: () => DASH,
224
+ MONEY_REQUIRES_DEVICE_GRANULARITY: () => MONEY_REQUIRES_DEVICE_GRANULARITY,
225
+ MONEY_VARIANCE_HEADER: () => MONEY_VARIANCE_HEADER,
218
226
  MOTOR_SORT_OPTIONS: () => MOTOR_SORT_OPTIONS,
219
227
  MYIO_CHART_PALETTE: () => MYIO_CHART_PALETTE,
220
228
  MYIO_CHART_PALETTE_DARK: () => MYIO_CHART_PALETTE_DARK,
@@ -259,6 +267,7 @@ __export(src_exports, {
259
267
  POWER_LIMITS_DEVICE_TYPES: () => DEVICE_TYPES,
260
268
  POWER_LIMITS_STATUS_CONFIG: () => STATUS_CONFIG,
261
269
  POWER_LIMITS_TELEMETRY_TYPES: () => TELEMETRY_TYPES2,
270
+ REPORT_MONEY_HEADER: () => REPORT_MONEY_HEADER,
262
271
  SCHEDULE_HOLIDAY_CSS_PREFIX: () => SCHEDULE_HOLIDAY_CSS_PREFIX,
263
272
  SCHEDULE_IR_CSS_PREFIX: () => SCHEDULE_IR_CSS_PREFIX,
264
273
  SCHEDULE_ON_OFF_CSS_PREFIX: () => SCHEDULE_ON_OFF_CSS_PREFIX,
@@ -300,6 +309,9 @@ __export(src_exports, {
300
309
  TELEMETRY_INFO_WATER_CATEGORY_CONFIG: () => WATER_CATEGORY_CONFIG,
301
310
  TEMPERATURE_SORT_OPTIONS: () => TEMPERATURE_SORT_OPTIONS,
302
311
  TEMPERATURE_UNITS: () => TEMPERATURE_UNITS,
312
+ TariffApiAdapter: () => TariffApiAdapter,
313
+ TariffApiClient: () => TariffApiClient,
314
+ TariffApiError: () => TariffApiError,
303
315
  TelemetryGridController: () => TelemetryGridController,
304
316
  TelemetryGridView: () => TelemetryGridView,
305
317
  TempComparisonTooltip: () => TempComparisonTooltip,
@@ -307,6 +319,10 @@ __export(src_exports, {
307
319
  TempSensorSummaryTooltip: () => TempSensorSummaryTooltip,
308
320
  Temperature: () => Temperature,
309
321
  UsersSummaryTooltip: () => UsersSummaryTooltip,
322
+ VARIANCE_LABEL_ABOVE: () => VARIANCE_LABEL_ABOVE,
323
+ VARIANCE_LABEL_BELOW: () => VARIANCE_LABEL_BELOW,
324
+ VARIANCE_LABEL_ONTARGET: () => VARIANCE_LABEL_ONTARGET,
325
+ VARIANCE_LABEL_WITHHELD: () => VARIANCE_LABEL_WITHHELD,
310
326
  VirtualList: () => VirtualList,
311
327
  WAITING_STATUSES: () => WAITING_STATUSES,
312
328
  WATER_DEVICE_CATEGORIES: () => WATER_DEVICE_CATEGORIES,
@@ -332,15 +348,20 @@ __export(src_exports, {
332
348
  buildAnnotationServiceOrchestrator: () => buildAnnotationServiceOrchestrator,
333
349
  buildAnnotationsCsv: () => buildAnnotationsCsv,
334
350
  buildAnnotationsExportFilename: () => buildExportFilename,
351
+ buildBudgetHTML: () => buildBudgetHTML,
335
352
  buildByStatusFromDevices: () => buildByStatusFromDevices,
353
+ buildCoverageHTML: () => buildCoverageHTML,
336
354
  buildCoverageWarningTextPtBR: () => buildCoverageWarningTextPtBR,
337
355
  buildDeviceGridEntityObject: () => buildEntityObject,
338
356
  buildEquipmentCategoryDataForTooltip: () => buildEquipmentCategoryDataForTooltip,
339
357
  buildEquipmentCategorySummary: () => buildEquipmentCategorySummary,
358
+ buildFinancialRowHTML: () => buildFinancialRowHTML,
340
359
  buildListItemsThingsboardByUniqueDatasource: () => buildListItemsThingsboardByUniqueDatasource,
360
+ buildMoneyVarianceHTML: () => buildMoneyVarianceHTML,
341
361
  buildMyioIngestionAuth: () => buildMyioIngestionAuth,
342
362
  buildTemplateExport: () => buildTemplateExport,
343
363
  buildTicketServiceOrchestrator: () => buildTicketServiceOrchestrator,
364
+ buildVerdictHTML: () => buildVerdictHTML,
344
365
  buildWaterReportCSV: () => buildWaterReportCSV,
345
366
  buildWaterStoresCSV: () => buildWaterStoresCSV,
346
367
  calcDeltaPercent: () => calcDeltaPercent,
@@ -361,6 +382,7 @@ __export(src_exports, {
361
382
  calculateStats: () => calculateStats,
362
383
  canEditAlarmRules: () => canEditAlarmRules,
363
384
  canModifyAnnotation: () => canModifyAnnotation,
385
+ centsToDecimalString: () => centsToDecimalString,
364
386
  checkCustomerCodeAvailable: () => checkCustomerCodeAvailable,
365
387
  clampTemperature: () => clampTemperature,
366
388
  classify: () => classify,
@@ -372,9 +394,14 @@ __export(src_exports, {
372
394
  clearAllAuthCaches: () => clearAllAuthCaches,
373
395
  clearFreshdeskTicketsOnTB: () => clearFreshdeskTicketsOnTB,
374
396
  closeAnnotationsExportModal: () => closeExportModal,
397
+ collapseHoursToBands: () => collapseHoursToBands,
398
+ collapseTreeToBands: () => collapseTreeToBands,
375
399
  collectDeviceOverrides: () => collectDeviceOverrides,
376
400
  computeBaseGroupResidual: () => computeBaseGroupResidual,
401
+ computeDeltaPct: () => computeDeltaPct,
402
+ computeMoneyVariance: () => computeMoneyVariance,
377
403
  connectionStatusIcons: () => connectionStatusIcons,
404
+ coveragePercentLabel: () => coveragePercentLabel,
378
405
  createActionButton: () => createActionButton,
379
406
  createAlarmCardElement: () => createAlarmCardElement,
380
407
  createAlarmsNotificationsPanelComponent: () => createAlarmsNotificationsPanelComponent,
@@ -387,6 +414,7 @@ __export(src_exports, {
387
414
  createConsumption7DaysChart: () => createConsumption7DaysChart,
388
415
  createConsumptionChartWidget: () => createConsumptionChartWidget,
389
416
  createConsumptionModal: () => createConsumptionModal,
417
+ createCoverageDeepLink: () => createCoverageDeepLink,
390
418
  createCsvButton: () => createCsvButton,
391
419
  createCustomerCardV1: () => createCustomerCardV1,
392
420
  createCustomerCardV2: () => createCustomerCardV2,
@@ -406,16 +434,19 @@ __export(src_exports, {
406
434
  createEnergyPanelComponent: () => createEnergyPanelComponent,
407
435
  createErrorSpan: () => createErrorSpan,
408
436
  createExportButton: () => createExportButton,
437
+ createFakeDeviceCategoryPort: () => createFakeDeviceCategoryPort,
409
438
  createFancoilRemote: () => createFancoilRemote,
410
439
  createFilterModalComponent: () => createFilterModalComponent,
411
440
  createFooterComponent: () => createFooterComponent,
412
441
  createFreshdeskTicket: () => createTicket,
413
442
  createGoalsBarTooltip: () => createGoalsBarTooltip,
443
+ createGoalsMoneyClient: () => createGoalsMoneyClient,
414
444
  createGranularitySelector: () => createGranularitySelector,
415
445
  createGroupScheduleCard: () => createGroupScheduleCard,
416
446
  createHeaderComponent: () => createHeaderComponent,
417
447
  createHeaderDevicesGridComponent: () => createHeaderDevicesGridComponent,
418
448
  createHeaderShoppingComponent: () => createHeaderShoppingComponent,
449
+ createHttpDeviceCategoryPort: () => createHttpDeviceCategoryPort,
419
450
  createImgGallery: () => createImgGallery,
420
451
  createInputDateRangePickerInsideDIV: () => createInputDateRangePickerInsideDIV,
421
452
  createLibraryVersionChecker: () => createLibraryVersionChecker,
@@ -425,6 +456,7 @@ __export(src_exports, {
425
456
  createMenuShoppingComponent: () => createMenuShoppingComponent,
426
457
  createModalFooter: () => createModalFooter,
427
458
  createModalHeader: () => createModalHeader,
459
+ createMoneyVarianceColumn: () => createMoneyVarianceColumn,
428
460
  createMyIOTheme: () => createMyIOTheme,
429
461
  createNewTicketWizard: () => createNewTicketWizard,
430
462
  createOnOffDeviceModal: () => createOnOffDeviceModal,
@@ -436,6 +468,7 @@ __export(src_exports, {
436
468
  createPdfButton: () => createPdfButton,
437
469
  createPresetupGateway: () => createPresetupGateway,
438
470
  createRealTimeClock: () => createRealTimeClock,
471
+ createReportMoneyColumn: () => createReportMoneyColumn,
439
472
  createScheduleCard: () => createScheduleCard,
440
473
  createScheduleHoliday: () => createScheduleHoliday,
441
474
  createScheduleIR: () => createScheduleIR,
@@ -445,6 +478,8 @@ __export(src_exports, {
445
478
  createSidebarMenu: () => createSidebarMenu,
446
479
  createSolenoidControl: () => createSolenoidControl,
447
480
  createSwitchControl: () => createSwitchControl,
481
+ createTariffApiAdapter: () => createTariffApiAdapter,
482
+ createTariffApiClient: () => createTariffApiClient,
448
483
  createTelemetryGridComponent: () => createTelemetryGridComponent,
449
484
  createTelemetryGridShoppingComponent: () => createTelemetryGridShoppingComponent,
450
485
  createTelemetryInfoShoppingComponent: () => createTelemetryInfoShoppingComponent,
@@ -455,6 +490,8 @@ __export(src_exports, {
455
490
  createWidgetController: () => createWidgetController,
456
491
  createXlsButton: () => createXlsButton,
457
492
  csvEscapeAnnotation: () => csvEscape,
493
+ daysInMonthYear: () => daysInMonthYear,
494
+ decimalStringToCents: () => decimalStringToCents,
458
495
  decodePayload: () => decodePayload,
459
496
  decodePayloadBase64Xor: () => decodePayloadBase64Xor,
460
497
  deleteFreshdeskTicket: () => deleteTicket,
@@ -465,6 +502,8 @@ __export(src_exports, {
465
502
  detectSuperAdminHolding: () => detectSuperAdminHolding,
466
503
  detectSuperAdminMyio: () => detectSuperAdminMyio,
467
504
  determineInterval: () => determineInterval,
505
+ deviceCategoryLabel: () => deviceCategoryLabel,
506
+ deviceCategoryToPanelTerm: () => deviceCategoryToPanelTerm,
468
507
  deviceIconLabels: () => deviceIconLabels,
469
508
  deviceIcons: () => deviceIcons,
470
509
  deviceStatusIcons: () => deviceStatusIcons,
@@ -473,6 +512,9 @@ __export(src_exports, {
473
512
  downloadAnnotationsTextFile: () => downloadTextFile,
474
513
  escapeAnnotationHtml: () => escapeHtml16,
475
514
  evaluateDeviceStatus: () => evaluateDeviceStatus,
515
+ expandBandHours: () => expandBandHours,
516
+ expandBandToHourBucketsByYear: () => expandBandToHourBucketsByYear,
517
+ expandDayToHourBuckets: () => expandDayToHourBuckets,
476
518
  exportAnnotationsCsv: () => exportAnnotationsCsv,
477
519
  exportAnnotationsPdf: () => exportAnnotationsPdf,
478
520
  exportGridCsv: () => exportGridCsv,
@@ -504,14 +546,17 @@ __export(src_exports, {
504
546
  formatAllInSameUnit: () => formatAllInSameUnit,
505
547
  formatAllInSameWaterUnit: () => formatAllInSameWaterUnit,
506
548
  formatAnnotationRelativeTime: () => formatRelative,
549
+ formatBRLDelta: () => formatBRLDelta,
507
550
  formatCoverageRefPtBR: () => formatCoverageRefPtBR,
508
551
  formatDashboardPercentage: () => formatPercentage4,
509
552
  formatDateForInput: () => formatDateForInput,
510
553
  formatDateToYMD: () => formatDateToYMD,
511
554
  formatDateWithTimezoneOffset: () => formatDateWithTimezoneOffset,
555
+ formatDeltaPct: () => formatDeltaPct,
512
556
  formatDuration: () => formatDuration,
513
557
  formatEnergy: () => formatEnergy,
514
558
  formatHours: () => formatHours,
559
+ formatMoneyBRL: () => formatBRL,
515
560
  formatNumberReadable: () => formatNumberReadable,
516
561
  formatPower: () => formatPower,
517
562
  formatRelativeTime: () => formatRelativeTime,
@@ -600,8 +645,10 @@ __export(src_exports, {
600
645
  injectAmbienteGroupModalStyles: () => injectAmbienteGroupModalStyles,
601
646
  injectAmbienteModalStyles: () => injectAmbienteModalStyles,
602
647
  injectBASDashboardStyles: () => injectBASDashboardStyles,
648
+ injectCoverageStyles: () => injectCoverageStyles,
603
649
  injectCustomerCardV1Styles: () => injectCustomerCardV1Styles,
604
650
  injectCustomerCardV2Styles: () => injectCustomerCardV2Styles,
651
+ injectDeviceCategoryStyles: () => injectDeviceCategoryStyles,
605
652
  injectDeviceGridV6Styles: () => injectStyles18,
606
653
  injectDeviceOperationalCardGridStyles: () => injectDeviceOperationalCardGridStyles,
607
654
  injectDeviceOperationalCardStyles: () => injectDeviceOperationalCardStyles,
@@ -641,6 +688,8 @@ __export(src_exports, {
641
688
  isEquipmentDevice: () => isEquipmentDevice,
642
689
  isHydrometerDevice: () => isHydrometerDevice,
643
690
  isInRange: () => isInRange,
691
+ isLeapYear: () => isLeapYear,
692
+ isMoneyColumnConfident: () => isMoneyColumnConfident,
644
693
  isOnOffDeviceProfile: () => isOnOffDeviceProfile,
645
694
  isSolenoidDevice: () => isSolenoidDevice,
646
695
  isStoreDevice: () => isStoreDevice,
@@ -661,9 +710,12 @@ __export(src_exports, {
661
710
  metasGuideDeriveTotal: () => deriveTotal,
662
711
  myioExportData: () => myioExportData,
663
712
  nfdNormalizeAnnotationSearch: () => nfdNormalize,
713
+ normalizeBudgetBlock: () => normalizeBudgetBlock,
664
714
  normalizeConnectionStatus: () => normalizeConnectionStatus,
665
715
  normalizeDeviceOverrideId: () => normalizeDeviceOverrideId,
716
+ normalizeMoneyBlock: () => normalizeMoneyBlock,
666
717
  normalizeOnOffStatus: () => normalizeOnOff,
718
+ normalizePriceString: () => normalizePriceString,
667
719
  normalizeProfile: () => normalizeProfile,
668
720
  normalizeRecipients: () => normalizeRecipients,
669
721
  numbers: () => numbers_exports,
@@ -682,6 +734,7 @@ __export(src_exports, {
682
734
  openDashboardPopupSettings: () => openDashboardPopupSettings,
683
735
  openDashboardPopupWaterTank: () => openDashboardPopupWaterTank,
684
736
  openDemandModal: () => openDemandModal,
737
+ openDeviceCategoryPanel: () => openDeviceCategoryPanel,
685
738
  openDeviceProfileModal: () => openDeviceProfileModal,
686
739
  openGCDRSyncModal: () => openGCDRSyncModal,
687
740
  openGenericModal: () => openGenericModal,
@@ -705,6 +758,10 @@ __export(src_exports, {
705
758
  openUpsellModal: () => openUpsellModal,
706
759
  openUserManagementModal: () => openUserManagementModal,
707
760
  openWelcomeModal: () => openWelcomeModal,
761
+ overlayWithholdsVerdict: () => overlayWithholdsVerdict,
762
+ panelCategoryToWire: () => panelCategoryToWire,
763
+ panelDomainToWire: () => panelDomainToWire,
764
+ panelTermToDeviceCategory: () => panelTermToDeviceCategory,
708
765
  parseClassificationEntities: () => parseClassificationEntities,
709
766
  parseInputDateToDate: () => parseInputDateToDate,
710
767
  parseLogAnnotations: () => parseLogAnnotations,
@@ -725,17 +782,22 @@ __export(src_exports, {
725
782
  removeSchedulingSharedStyles: () => removeSchedulingSharedStyles,
726
783
  renderAlarmCard: () => renderAlarmCard,
727
784
  renderAnnotationItemCard: () => renderAnnotationItemCard,
785
+ renderBudgetView: () => renderBudgetView,
728
786
  renderCardAmbienteV6: () => renderCardAmbienteV6,
729
787
  renderCardComponentHeadOffice: () => renderCardComponentHeadOffice,
730
788
  renderCardComponentV5: () => renderCardComponent,
731
789
  renderCardComponentV6: () => renderCardComponentV6,
732
790
  renderCardComponentV6Alias: () => renderCardComponent2,
733
791
  renderCardV5: () => renderCardComponentV5,
792
+ renderCoverageView: () => renderCoverageView,
734
793
  renderDashboardDowntimeList: () => renderDowntimeList,
735
794
  renderDeviceGridList: () => renderList,
736
795
  renderDualLineChart: () => renderDualLineChart,
796
+ renderFinancialIndicators: () => renderFinancialIndicators,
797
+ renderGatedMoney: () => renderGatedMoney,
737
798
  renderKPICards: () => renderKPICards,
738
799
  renderLineChart: () => renderLineChart,
800
+ renderMoneyVariance: () => renderMoneyVariance,
739
801
  renderOnOffTimelineChart: () => renderOnOffTimelineChart,
740
802
  renderSeverityBarChart: () => renderSeverityBarChart,
741
803
  renderStateDonutChart: () => renderStateDonutChart,
@@ -743,12 +805,17 @@ __export(src_exports, {
743
805
  renderTrendChart: () => renderTrendChart,
744
806
  reopenFreshdeskTicket: () => reopenTicket,
745
807
  resolveActiveProfile: () => resolveActiveProfile,
808
+ resolveBudget: () => resolveBudget,
746
809
  resolveCategory: () => resolveCategory,
747
810
  resolveClassification: () => resolveClassification,
811
+ resolveGoalAmount: () => resolveGoalAmount,
748
812
  resolveGroup: () => resolveGroup,
813
+ resolveMoneyRollout: () => resolveMoneyRollout,
814
+ resolveMoneyRowValues: () => resolveMoneyRowValues,
749
815
  resolvePercentDecimals: () => resolvePercentDecimals,
750
816
  resolveSubcategory: () => resolveSubcategory,
751
817
  resolveWithFallback: () => resolveWithFallback,
818
+ routeMoneyRender: () => routeMoneyRender,
752
819
  schedCreateDateInput: () => createDateInput,
753
820
  schedCreateNumberInput: () => createNumberInput,
754
821
  schedCreateSelect: () => createSelect,
@@ -760,10 +827,13 @@ __export(src_exports, {
760
827
  setActiveProfile: () => setActiveProfile,
761
828
  shouldFlashIcon: () => shouldFlashIcon,
762
829
  shouldVirtualizeAnnotationList: () => shouldVirtualize,
830
+ signOf: () => signOf,
763
831
  slugifyCustomerName: () => slugifyCustomerName,
764
832
  sortAnnotationGroups: () => sortGroups,
765
833
  sortDeviceGridDevices: () => sortDevices,
766
834
  strings: () => strings_exports,
835
+ subtractDecimals: () => subtractDecimals,
836
+ sumMoneyDecimals: () => sumMoneyDecimals,
767
837
  telemetryInfoFormatEnergy: () => formatEnergy2,
768
838
  telemetryInfoFormatPercentage: () => formatPercentage3,
769
839
  telemetryInfoFormatWater: () => formatWater2,
@@ -781,6 +851,8 @@ __export(src_exports, {
781
851
  validateTree: () => validateTree,
782
852
  version: () => version,
783
853
  waterDeviceStatusIcons: () => waterDeviceStatusIcons,
854
+ wireCategoryToPanel: () => wireCategoryToPanel,
855
+ wireDomainToPanel: () => wireDomainToPanel,
784
856
  writeFreshdeskSyncedAtToTB: () => writeFreshdeskSyncedAtToTB,
785
857
  writeFreshdeskTicketsToTB: () => writeFreshdeskTicketsToTB
786
858
  });
@@ -789,7 +861,7 @@ module.exports = __toCommonJS(src_exports);
789
861
  // package.json
790
862
  var package_default = {
791
863
  name: "myio-js-library",
792
- version: "0.1.529",
864
+ version: "0.1.530",
793
865
  description: "A clean, standalone JS SDK for MYIO projects",
794
866
  license: "MIT",
795
867
  repository: "github:gh-myio/myio-js-library",
@@ -15376,9 +15448,6 @@ if (typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined
15376
15448
  } else {
15377
15449
  MyIOSelectionStore = new MyIOSelectionStoreClass();
15378
15450
  }
15379
- if (typeof module !== "undefined" && module.exports) {
15380
- module.exports = { MyIOSelectionStore, MyIOSelectionStoreClass };
15381
- }
15382
15451
 
15383
15452
  // src/components/DraggableCard.js
15384
15453
  var MyIODraggableCard = class {
@@ -15704,9 +15773,6 @@ var MyIODraggableCard = class {
15704
15773
  if (typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined") {
15705
15774
  globalThis.window.MyIODraggableCard = MyIODraggableCard;
15706
15775
  }
15707
- if (typeof module !== "undefined" && module.exports) {
15708
- module.exports = { MyIODraggableCard };
15709
- }
15710
15776
 
15711
15777
  // src/utils/tooltips/DeviceComparisonTooltip.ts
15712
15778
  var DEVICE_COMPARISON_TOOLTIP_CSS = `
@@ -24043,42 +24109,6 @@ var MyIOChartModalClass = class {
24043
24109
  </div>
24044
24110
  `;
24045
24111
  }
24046
- _generateSummaryHTML(totals) {
24047
- const items = [];
24048
- if (totals.energyKwh > 0) {
24049
- items.push(`<div class="summary-item"
24050
- style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">
24051
- <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Energia Total:</span>
24052
- <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(
24053
- totals.energyKwh
24054
- )} kWh</span>
24055
- </div>`);
24056
- }
24057
- if (totals.waterM3 > 0) {
24058
- items.push(`<div class="summary-item"
24059
- style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">
24060
- <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">\xC1gua Total:</span>
24061
- <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(
24062
- totals.waterM3
24063
- )} m\xB3</span>
24064
- </div>`);
24065
- }
24066
- if (totals.tempC > 0) {
24067
- items.push(`<div class="summary-item"
24068
- style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">
24069
- <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Temperatura M\xE9dia:</span>
24070
- <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(
24071
- totals.tempC / totals.count
24072
- )} \xB0C</span>
24073
- </div>`);
24074
- }
24075
- items.push(`<div class="summary-item"
24076
- style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">
24077
- <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Dispositivos:</span>
24078
- <span class="summary-value" style="font-weight: bold; font-size: 14px;">${totals.count}</span>
24079
- </div>`);
24080
- return items.join("");
24081
- }
24082
24112
  _generateSummaryHTML(totals) {
24083
24113
  const items = [];
24084
24114
  if (totals.energyKwh > 0) {
@@ -24386,9 +24416,6 @@ var MyIOChartModal = new MyIOChartModalClass();
24386
24416
  if (typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined") {
24387
24417
  globalThis.window.MyIOChartModal = MyIOChartModal;
24388
24418
  }
24389
- if (typeof module !== "undefined" && module.exports) {
24390
- module.exports = { MyIOChartModal };
24391
- }
24392
24419
 
24393
24420
  // src/components/MyIOToast.js
24394
24421
  var TOAST_MAX_MESSAGE_LENGTH = 100;
@@ -24608,9 +24635,6 @@ var MyIOToast = (function() {
24608
24635
  error
24609
24636
  };
24610
24637
  })();
24611
- if (typeof module !== "undefined" && module.exports) {
24612
- module.exports = { MyIOToast };
24613
- }
24614
24638
 
24615
24639
  // src/components/loading-spinner/types.ts
24616
24640
  var DEFAULT_LOADING_CONFIG = {
@@ -29266,8 +29290,8 @@ var EnergyDataFetcher = class {
29266
29290
  // src/components/premium-modals/internal/engines/CsvExporter.ts
29267
29291
  var toCsv = (rows, locale = "pt-BR", sep = ";") => {
29268
29292
  const fmt2 = new Intl.NumberFormat(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
29269
- const esc6 = (v) => (typeof v === "number" ? fmt2.format(v) : String(v)).replace(/"/g, '""');
29270
- return rows.map((r) => r.map((c) => `"${esc6(c)}"`).join(sep)).join("\r\n");
29293
+ const esc8 = (v) => (typeof v === "number" ? fmt2.format(v) : String(v)).replace(/"/g, '""');
29294
+ return rows.map((r) => r.map((c) => `"${esc8(c)}"`).join(sep)).join("\r\n");
29271
29295
  };
29272
29296
 
29273
29297
  // src/utils/telemetryUtils.ts
@@ -37056,6 +37080,823 @@ function attachFilterOrderingModal(props) {
37056
37080
  };
37057
37081
  }
37058
37082
 
37083
+ // src/components/financial-goals/moneyFormat.ts
37084
+ var DASH = "\u2014";
37085
+ function toDecimalParts(input) {
37086
+ const s = input.trim();
37087
+ if (!s) return null;
37088
+ const m = /^([+-]?)(\d+)(?:\.(\d+))?$/.exec(s);
37089
+ if (!m) return null;
37090
+ const negative = m[1] === "-";
37091
+ let intDigits = m[2].replace(/^0+(?=\d)/, "");
37092
+ const frac = m[3] || "";
37093
+ let cents = frac.slice(0, 2).padEnd(2, "0");
37094
+ const roundDigit = frac.charAt(2);
37095
+ if (roundDigit && roundDigit >= "5") {
37096
+ const bumped = String(Number(cents) + 1).padStart(2, "0");
37097
+ if (bumped.length > 2) {
37098
+ cents = "00";
37099
+ intDigits = incrementDigits(intDigits);
37100
+ } else {
37101
+ cents = bumped;
37102
+ }
37103
+ }
37104
+ return { negative, intDigits, frac2: cents };
37105
+ }
37106
+ function incrementDigits(digits) {
37107
+ const arr = digits.split("");
37108
+ let i = arr.length - 1;
37109
+ for (; i >= 0; i--) {
37110
+ if (arr[i] === "9") {
37111
+ arr[i] = "0";
37112
+ } else {
37113
+ arr[i] = String(Number(arr[i]) + 1);
37114
+ return arr.join("");
37115
+ }
37116
+ }
37117
+ return "1" + arr.join("");
37118
+ }
37119
+ function groupThousands(intDigits) {
37120
+ return intDigits.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
37121
+ }
37122
+ function formatBRL(amount) {
37123
+ if (amount == null) return DASH;
37124
+ const parts = toDecimalParts(String(amount));
37125
+ if (!parts) return DASH;
37126
+ const grouped = groupThousands(parts.intDigits);
37127
+ const sign = parts.negative && !(parts.intDigits === "0" && parts.frac2 === "00") ? "-" : "";
37128
+ return `R$ ${sign}${grouped},${parts.frac2}`;
37129
+ }
37130
+ function signOf(value) {
37131
+ if (value == null || !Number.isFinite(value) || value === 0) return "";
37132
+ return value > 0 ? "+" : "\u2212";
37133
+ }
37134
+ function computeDeltaPct(current, baseline) {
37135
+ if (current == null || baseline == null) return null;
37136
+ const c = Number(String(current).trim());
37137
+ const b = Number(String(baseline).trim());
37138
+ if (!Number.isFinite(c) || !Number.isFinite(b) || b === 0) return null;
37139
+ return (c - b) / Math.abs(b) * 100;
37140
+ }
37141
+ function formatDeltaPct(pct, digits = 1) {
37142
+ if (pct == null || !Number.isFinite(pct)) return DASH;
37143
+ const abs = Math.abs(pct).toFixed(digits).replace(".", ",");
37144
+ const sign = pct > 0 ? "+" : pct < 0 ? "\u2212" : "";
37145
+ return `${sign}${abs}%`;
37146
+ }
37147
+ function formatBRLDelta(amount) {
37148
+ if (amount == null) return DASH;
37149
+ const parts = toDecimalParts(String(amount));
37150
+ if (!parts) return DASH;
37151
+ const isZero = parts.intDigits === "0" && parts.frac2 === "00";
37152
+ const sign = isZero ? "" : parts.negative ? "\u2212" : "+";
37153
+ const grouped = groupThousands(parts.intDigits);
37154
+ return `${sign}R$ ${grouped},${parts.frac2}`;
37155
+ }
37156
+
37157
+ // src/components/financial-goals/moneyTypes.ts
37158
+ var MONEY_REQUIRES_DEVICE_GRANULARITY = "MONEY_REQUIRES_DEVICE_GRANULARITY";
37159
+ function normalizeMoneyBlock(raw, rawBudget) {
37160
+ if (raw == null) {
37161
+ return { state: "unavailable", reason: MONEY_REQUIRES_DEVICE_GRANULARITY };
37162
+ }
37163
+ const hasCoverageFields = typeof raw.currency === "string" || typeof raw.coverageComplete === "boolean";
37164
+ if (!hasCoverageFields) {
37165
+ const reason = typeof raw.reason === "string" && raw.reason ? raw.reason : MONEY_REQUIRES_DEVICE_GRANULARITY;
37166
+ return { state: "unavailable", reason };
37167
+ }
37168
+ const overlay = {
37169
+ state: "available",
37170
+ currency: typeof raw.currency === "string" ? raw.currency : "BRL",
37171
+ coverageComplete: raw.coverageComplete === true,
37172
+ uncategorizedDevices: Array.isArray(raw.uncategorizedDevices) ? raw.uncategorizedDevices : []
37173
+ };
37174
+ if (typeof raw.pricedHours === "number") overlay.pricedHours = raw.pricedHours;
37175
+ if (typeof raw.totalHours === "number") overlay.totalHours = raw.totalHours;
37176
+ if (raw.tariffCoverageGaps) overlay.tariffCoverageGaps = raw.tariffCoverageGaps;
37177
+ const budget = normalizeBudgetBlock(rawBudget);
37178
+ if (budget) overlay.budget = budget;
37179
+ return overlay;
37180
+ }
37181
+ function normalizeBudgetBlock(rawBudget) {
37182
+ if (rawBudget == null || typeof rawBudget !== "object") return void 0;
37183
+ const projected = rawBudget.projected;
37184
+ if (!projected || typeof projected.amount !== "string") return void 0;
37185
+ const overlay = {
37186
+ projected: {
37187
+ amount: projected.amount,
37188
+ source: typeof projected.source === "string" ? projected.source : "OVERLAY",
37189
+ coverageComplete: projected.coverageComplete === true
37190
+ },
37191
+ verdict: {
37192
+ // Explicit `null` when withheld; only pass a boolean through unchanged.
37193
+ withinBudget: typeof rawBudget.withinBudget === "boolean" ? rawBudget.withinBudget : null,
37194
+ // Variance stays a decimal string, or null while incomplete.
37195
+ variance: typeof rawBudget.variance === "string" ? rawBudget.variance : null
37196
+ }
37197
+ };
37198
+ if (rawBudget.target && typeof rawBudget.target === "object" && typeof rawBudget.target.amount === "string") {
37199
+ overlay.target = {
37200
+ amount: rawBudget.target.amount,
37201
+ source: typeof rawBudget.target.source === "string" ? rawBudget.target.source : "NATIVE"
37202
+ };
37203
+ }
37204
+ return overlay;
37205
+ }
37206
+
37207
+ // src/components/financial-goals/coverageStyles.ts
37208
+ var COVERAGE_STYLE_ID = "myio-fin-coverage-styles";
37209
+ var COVERAGE_CSS = `
37210
+ .myio-cov {
37211
+ --_cov-bg: var(--myio-bg, #ffffff);
37212
+ --_cov-card: var(--myio-card, #f8fafc);
37213
+ --_cov-text: var(--myio-text, #1f2937);
37214
+ --_cov-muted: var(--myio-text-muted, #6b7280);
37215
+ --_cov-border: var(--myio-border, #e5e7eb);
37216
+ --_cov-brand: var(--myio-brand-700, #5b2c9d);
37217
+ --_cov-warn-bg: #fffbeb;
37218
+ --_cov-warn-border: #fde68a;
37219
+ --_cov-warn-text: #92400e;
37220
+ --_cov-ok-bg: #ecfdf5;
37221
+ --_cov-ok-border: #a7f3d0;
37222
+ --_cov-ok-text: #065f46;
37223
+ --_cov-info-bg: #eff6ff;
37224
+ --_cov-info-border: #bfdbfe;
37225
+ --_cov-info-text: #1e40af;
37226
+ font-family: 'Nunito', system-ui, -apple-system, sans-serif;
37227
+ color: var(--_cov-text);
37228
+ display: flex;
37229
+ flex-direction: column;
37230
+ gap: 10px;
37231
+ font-size: 13px;
37232
+ line-height: 1.45;
37233
+ }
37234
+
37235
+ /* ---- Unavailable panel (money view withheld \u2014 not an error, not R$ 0) ---- */
37236
+ .myio-cov__panel {
37237
+ border: 1px solid var(--_cov-border);
37238
+ border-left: 3px solid var(--_cov-brand);
37239
+ border-radius: 10px;
37240
+ background: var(--_cov-card);
37241
+ padding: 14px 16px;
37242
+ display: flex;
37243
+ flex-direction: column;
37244
+ gap: 6px;
37245
+ }
37246
+ .myio-cov__panel-title {
37247
+ font-weight: 800;
37248
+ font-size: 14px;
37249
+ display: flex;
37250
+ align-items: center;
37251
+ gap: 8px;
37252
+ }
37253
+ .myio-cov__panel-msg {
37254
+ color: var(--_cov-muted);
37255
+ margin: 0;
37256
+ }
37257
+
37258
+ /* ---- Complete: quiet, non-intrusive affordance ---- */
37259
+ .myio-cov__complete {
37260
+ display: inline-flex;
37261
+ align-items: center;
37262
+ gap: 6px;
37263
+ align-self: flex-start;
37264
+ padding: 3px 10px;
37265
+ border-radius: 999px;
37266
+ font-size: 12px;
37267
+ font-weight: 700;
37268
+ background: var(--_cov-ok-bg);
37269
+ color: var(--_cov-ok-text);
37270
+ border: 1px solid var(--_cov-ok-border);
37271
+ }
37272
+
37273
+ /* ---- Incomplete badge + covered fraction ---- */
37274
+ .myio-cov__badge {
37275
+ display: inline-flex;
37276
+ align-items: center;
37277
+ gap: 8px;
37278
+ align-self: flex-start;
37279
+ padding: 4px 12px;
37280
+ border-radius: 999px;
37281
+ font-size: 12px;
37282
+ font-weight: 800;
37283
+ background: var(--_cov-warn-bg);
37284
+ color: var(--_cov-warn-text);
37285
+ border: 1px solid var(--_cov-warn-border);
37286
+ }
37287
+ .myio-cov__badge-pct {
37288
+ font-variant-numeric: tabular-nums;
37289
+ font-weight: 800;
37290
+ }
37291
+ .myio-cov__hours {
37292
+ color: var(--_cov-muted);
37293
+ font-size: 12px;
37294
+ font-variant-numeric: tabular-nums;
37295
+ }
37296
+ .myio-cov__note {
37297
+ margin: 0;
37298
+ color: var(--_cov-muted);
37299
+ font-size: 12px;
37300
+ font-style: italic;
37301
+ }
37302
+
37303
+ /* ---- Uncategorized devices: each a deep-link click affordance to A5a ---- */
37304
+ .myio-cov__section-title {
37305
+ font-weight: 700;
37306
+ font-size: 12px;
37307
+ color: var(--_cov-text);
37308
+ margin: 4px 0 0;
37309
+ text-transform: uppercase;
37310
+ letter-spacing: 0.03em;
37311
+ }
37312
+ .myio-cov__devices {
37313
+ list-style: none;
37314
+ margin: 0;
37315
+ padding: 0;
37316
+ display: flex;
37317
+ flex-direction: column;
37318
+ gap: 6px;
37319
+ }
37320
+ .myio-cov__device {
37321
+ display: flex;
37322
+ align-items: center;
37323
+ justify-content: space-between;
37324
+ gap: 12px;
37325
+ width: 100%;
37326
+ text-align: left;
37327
+ padding: 9px 12px;
37328
+ border: 1px solid var(--_cov-border);
37329
+ border-radius: 8px;
37330
+ background: var(--_cov-bg);
37331
+ color: var(--_cov-text);
37332
+ font-family: inherit;
37333
+ font-size: 13px;
37334
+ cursor: pointer;
37335
+ transition: border-color 0.15s ease, background 0.15s ease;
37336
+ }
37337
+ .myio-cov__device:hover,
37338
+ .myio-cov__device:focus-visible {
37339
+ border-color: var(--_cov-brand);
37340
+ background: var(--_cov-card);
37341
+ outline: none;
37342
+ }
37343
+ .myio-cov__device-label {
37344
+ font-weight: 700;
37345
+ min-width: 0;
37346
+ overflow: hidden;
37347
+ text-overflow: ellipsis;
37348
+ white-space: nowrap;
37349
+ }
37350
+ .myio-cov__device-code {
37351
+ color: var(--_cov-muted);
37352
+ font-size: 12px;
37353
+ font-variant-numeric: tabular-nums;
37354
+ }
37355
+ .myio-cov__device-cta {
37356
+ color: var(--_cov-brand);
37357
+ font-weight: 800;
37358
+ font-size: 12px;
37359
+ white-space: nowrap;
37360
+ }
37361
+
37362
+ /* ---- Tariff coverage gaps summary ---- */
37363
+ .myio-cov__gaps {
37364
+ border: 1px solid var(--_cov-info-border);
37365
+ background: var(--_cov-info-bg);
37366
+ color: var(--_cov-info-text);
37367
+ border-radius: 8px;
37368
+ padding: 9px 12px;
37369
+ font-size: 12px;
37370
+ display: flex;
37371
+ flex-direction: column;
37372
+ gap: 2px;
37373
+ }
37374
+ .myio-cov__gaps-truncated {
37375
+ opacity: 0.85;
37376
+ font-style: italic;
37377
+ }
37378
+
37379
+ /* ---- Deep-link (unavailable state, generic manage-categories) ---- */
37380
+ .myio-cov__deeplink {
37381
+ align-self: flex-start;
37382
+ margin-top: 4px;
37383
+ padding: 7px 14px;
37384
+ border: 1px solid var(--_cov-brand);
37385
+ border-radius: 8px;
37386
+ background: transparent;
37387
+ color: var(--_cov-brand);
37388
+ font-family: inherit;
37389
+ font-size: 12px;
37390
+ font-weight: 800;
37391
+ cursor: pointer;
37392
+ transition: background 0.15s ease;
37393
+ }
37394
+ .myio-cov__deeplink:hover,
37395
+ .myio-cov__deeplink:focus-visible {
37396
+ background: var(--_cov-card);
37397
+ outline: none;
37398
+ }
37399
+
37400
+ @media (prefers-color-scheme: dark) {
37401
+ .myio-cov {
37402
+ --_cov-bg: var(--myio-bg, #0f172a);
37403
+ --_cov-card: var(--myio-card, #1e293b);
37404
+ --_cov-text: var(--myio-text, #e2e8f0);
37405
+ --_cov-muted: var(--myio-text-muted, #94a3b8);
37406
+ --_cov-border: var(--myio-border, #334155);
37407
+ --_cov-brand: var(--myio-brand-700, #a78bfa);
37408
+ --_cov-warn-bg: #3a2e0a;
37409
+ --_cov-warn-border: #7c5e12;
37410
+ --_cov-warn-text: #fcd34d;
37411
+ --_cov-ok-bg: #08312a;
37412
+ --_cov-ok-border: #14654f;
37413
+ --_cov-ok-text: #6ee7b7;
37414
+ --_cov-info-bg: #0e2748;
37415
+ --_cov-info-border: #1e4a86;
37416
+ --_cov-info-text: #93c5fd;
37417
+ }
37418
+ }
37419
+ /* Host theme toggle wins in both directions. */
37420
+ :root[data-theme="dark"] .myio-cov {
37421
+ --_cov-bg: var(--myio-bg, #0f172a);
37422
+ --_cov-card: var(--myio-card, #1e293b);
37423
+ --_cov-text: var(--myio-text, #e2e8f0);
37424
+ --_cov-muted: var(--myio-text-muted, #94a3b8);
37425
+ --_cov-border: var(--myio-border, #334155);
37426
+ --_cov-brand: var(--myio-brand-700, #a78bfa);
37427
+ --_cov-warn-bg: #3a2e0a; --_cov-warn-border: #7c5e12; --_cov-warn-text: #fcd34d;
37428
+ --_cov-ok-bg: #08312a; --_cov-ok-border: #14654f; --_cov-ok-text: #6ee7b7;
37429
+ --_cov-info-bg: #0e2748; --_cov-info-border: #1e4a86; --_cov-info-text: #93c5fd;
37430
+ }
37431
+ :root[data-theme="light"] .myio-cov {
37432
+ --_cov-bg: var(--myio-bg, #ffffff);
37433
+ --_cov-card: var(--myio-card, #f8fafc);
37434
+ --_cov-text: var(--myio-text, #1f2937);
37435
+ --_cov-muted: var(--myio-text-muted, #6b7280);
37436
+ --_cov-border: var(--myio-border, #e5e7eb);
37437
+ --_cov-brand: var(--myio-brand-700, #5b2c9d);
37438
+ --_cov-warn-bg: #fffbeb; --_cov-warn-border: #fde68a; --_cov-warn-text: #92400e;
37439
+ --_cov-ok-bg: #ecfdf5; --_cov-ok-border: #a7f3d0; --_cov-ok-text: #065f46;
37440
+ --_cov-info-bg: #eff6ff; --_cov-info-border: #bfdbfe; --_cov-info-text: #1e40af;
37441
+ }
37442
+ `;
37443
+ function injectCoverageStyles(doc) {
37444
+ if (!doc || !doc.head) return;
37445
+ if (doc.getElementById(COVERAGE_STYLE_ID)) return;
37446
+ const style = doc.createElement("style");
37447
+ style.id = COVERAGE_STYLE_ID;
37448
+ style.textContent = COVERAGE_CSS;
37449
+ doc.head.appendChild(style);
37450
+ }
37451
+
37452
+ // src/components/financial-goals/coverageView.ts
37453
+ function groupInt(n) {
37454
+ return String(Math.trunc(Math.abs(n))).replace(/\B(?=(\d{3})+(?!\d))/g, ".");
37455
+ }
37456
+ function coveragePercentLabel(pricedHours, totalHours) {
37457
+ if (typeof pricedHours !== "number" || typeof totalHours !== "number" || !Number.isFinite(pricedHours) || !Number.isFinite(totalHours) || totalHours <= 0) {
37458
+ return DASH;
37459
+ }
37460
+ const pct = pricedHours / totalHours * 100;
37461
+ if (!Number.isFinite(pct)) return DASH;
37462
+ const rounded = Math.round(pct * 10) / 10;
37463
+ const txt = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1).replace(".", ",");
37464
+ return `${txt}%`;
37465
+ }
37466
+ function esc(s) {
37467
+ return String(s ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
37468
+ }
37469
+ function deviceDisplayName(d) {
37470
+ return esc(d.label || d.code || d.deviceId || "Dispositivo");
37471
+ }
37472
+ function unavailableHTML(reason, offerDeepLink) {
37473
+ const isGranularity2 = reason === MONEY_REQUIRES_DEVICE_GRANULARITY;
37474
+ const msg = isGranularity2 ? "A meta precisa ser por dispositivo (device-granular) com categoria de tarifa." : "Vis\xE3o em R$ n\xE3o dispon\xEDvel para esta meta.";
37475
+ const deepLink = offerDeepLink ? `<button type="button" class="myio-cov__deeplink" data-cov-manage="1">Gerenciar categorias de tarifa</button>` : "";
37476
+ return `<div class="myio-cov__panel" role="status"><div class="myio-cov__panel-title"><span aria-hidden="true">\u{1F4B8}</span>Vis\xE3o em R$ indispon\xEDvel</div><p class="myio-cov__panel-msg">${msg}</p>` + deepLink + `</div>`;
37477
+ }
37478
+ function completeHTML() {
37479
+ return `<span class="myio-cov__complete" role="status"><span aria-hidden="true">\u2713</span>Cobertura completa</span>`;
37480
+ }
37481
+ function devicesHTML(devices) {
37482
+ if (!devices.length) return "";
37483
+ const items = devices.map((d) => {
37484
+ const name = deviceDisplayName(d);
37485
+ const code = d.code ? `<span class="myio-cov__device-code">${esc(d.code)}</span>` : "";
37486
+ return `<li><button type="button" class="myio-cov__device" data-cov-device="${esc(d.deviceId)}"><span class="myio-cov__device-label">${name}</span>` + code + `<span class="myio-cov__device-cta">Categorizar \u2192</span></button></li>`;
37487
+ }).join("");
37488
+ return `<div class="myio-cov__section-title">Dispositivos sem categoria de tarifa</div><ul class="myio-cov__devices">${items}</ul>`;
37489
+ }
37490
+ function gapsHTML(gaps) {
37491
+ if (!gaps) return "";
37492
+ const hours = typeof gaps.missingHours === "number" && Number.isFinite(gaps.missingHours) ? groupInt(gaps.missingHours) : DASH;
37493
+ const truncated = gaps.truncated ? `<span class="myio-cov__gaps-truncated">Lista de horas resumida (truncada).</span>` : "";
37494
+ return `<div class="myio-cov__gaps" role="status"><span><strong>${hours}</strong> horas-dispositivo sem tarifa.</span>` + truncated + `</div>`;
37495
+ }
37496
+ function incompleteHTML(overlay) {
37497
+ const pctLabel = coveragePercentLabel(overlay.pricedHours, overlay.totalHours);
37498
+ const hoursDetail = typeof overlay.pricedHours === "number" && typeof overlay.totalHours === "number" ? `<span class="myio-cov__hours">${groupInt(overlay.pricedHours)} / ${groupInt(
37499
+ overlay.totalHours
37500
+ )} horas-dispositivo cobertas</span>` : "";
37501
+ return `<span class="myio-cov__badge" role="status"><span aria-hidden="true">\u26A0\uFE0F</span>Cobertura incompleta<span class="myio-cov__badge-pct">${pctLabel}</span></span>` + hoursDetail + `<p class="myio-cov__note">A Estimativa em R$ \xE9 uma soma parcial coberta, n\xE3o um total.</p>` + devicesHTML(overlay.uncategorizedDevices) + gapsHTML(overlay.tariffCoverageGaps);
37502
+ }
37503
+ function buildCoverageHTML(overlay, offerDeepLink = false) {
37504
+ if (!overlay || overlay.state == null) {
37505
+ return unavailableHTML(MONEY_REQUIRES_DEVICE_GRANULARITY, offerDeepLink);
37506
+ }
37507
+ if (overlay.state === "unavailable") {
37508
+ return unavailableHTML(overlay.reason, offerDeepLink);
37509
+ }
37510
+ if (overlay.coverageComplete) {
37511
+ return completeHTML();
37512
+ }
37513
+ return incompleteHTML(overlay);
37514
+ }
37515
+ function renderCoverageView(overlay, options = {}) {
37516
+ const doc = options.document || (typeof document !== "undefined" ? document : void 0);
37517
+ if (!doc) {
37518
+ throw new Error("renderCoverageView requires a document (pass options.document in non-DOM envs).");
37519
+ }
37520
+ injectCoverageStyles(doc);
37521
+ const offerDeepLink = (!overlay || overlay.state === "unavailable") && typeof options.onManageCategories === "function";
37522
+ const root = doc.createElement("div");
37523
+ root.className = "myio-cov";
37524
+ root.setAttribute(
37525
+ "data-cov-state",
37526
+ !overlay || overlay.state === "unavailable" ? "unavailable" : overlay.coverageComplete ? "available-complete" : "available-incomplete"
37527
+ );
37528
+ root.innerHTML = buildCoverageHTML(overlay, offerDeepLink);
37529
+ if (typeof options.onCategorizeDevice === "function") {
37530
+ const cb = options.onCategorizeDevice;
37531
+ root.querySelectorAll("[data-cov-device]").forEach((btn) => {
37532
+ btn.addEventListener("click", (ev) => {
37533
+ ev.preventDefault();
37534
+ ev.stopPropagation();
37535
+ const id = btn.getAttribute("data-cov-device") || "";
37536
+ cb(id);
37537
+ });
37538
+ });
37539
+ }
37540
+ if (offerDeepLink && typeof options.onManageCategories === "function") {
37541
+ const mcb = options.onManageCategories;
37542
+ const manageBtn = root.querySelector("[data-cov-manage]");
37543
+ if (manageBtn) {
37544
+ manageBtn.addEventListener("click", (ev) => {
37545
+ ev.preventDefault();
37546
+ ev.stopPropagation();
37547
+ mcb();
37548
+ });
37549
+ }
37550
+ }
37551
+ return root;
37552
+ }
37553
+
37554
+ // src/components/financial-goals/reportMoneyColumn.ts
37555
+ var REPORT_MONEY_HEADER = "Custo projetado (R$)";
37556
+ function isMoneyColumnConfident(overlay) {
37557
+ return !!overlay && overlay.state === "available" && overlay.coverageComplete === true;
37558
+ }
37559
+ function hasAmount(s) {
37560
+ return typeof s === "string" && s.trim() !== "";
37561
+ }
37562
+ function decimalStringToCents(s) {
37563
+ if (!hasAmount(s)) return null;
37564
+ const m = /^([+-]?)(\d+)(?:\.(\d+))?$/.exec(s.trim());
37565
+ if (!m) return null;
37566
+ const negative = m[1] === "-";
37567
+ const whole = BigInt(m[2]);
37568
+ const frac = m[3] || "";
37569
+ let cents = BigInt(frac.slice(0, 2).padEnd(2, "0"));
37570
+ if (frac.charAt(2) >= "5") cents += 1n;
37571
+ const total = whole * 100n + cents;
37572
+ return negative ? -total : total;
37573
+ }
37574
+ function centsToDecimalString(cents) {
37575
+ const negative = cents < 0n;
37576
+ const abs = negative ? -cents : cents;
37577
+ const whole = abs / 100n;
37578
+ const rem = abs % 100n;
37579
+ return `${negative ? "-" : ""}${whole.toString()}.${rem.toString().padStart(2, "0")}`;
37580
+ }
37581
+ function sumMoneyDecimals(values) {
37582
+ if (!Array.isArray(values) || values.length === 0) return null;
37583
+ let acc = 0n;
37584
+ for (const v of values) {
37585
+ const c = decimalStringToCents(v);
37586
+ if (c == null) return null;
37587
+ acc += c;
37588
+ }
37589
+ return centsToDecimalString(acc);
37590
+ }
37591
+ function cellInner(value) {
37592
+ return `<span data-money-cell="1">${formatBRL(value ?? null)}</span>`;
37593
+ }
37594
+ function varianceNoteHTML(variance) {
37595
+ if (!hasAmount(variance)) return "";
37596
+ return `<span data-money-variance="1" style="margin-left:6px;font:600 10px Nunito,sans-serif;color:var(--myio-text-muted,#94a3b8);">vs Meta ${formatBRLDelta(variance)}</span>`;
37597
+ }
37598
+ var DISABLED_COLUMN = {
37599
+ enabled: false,
37600
+ headerCellHTML: () => "",
37601
+ bodyCellHTML: () => "",
37602
+ cellValueHTML: () => "",
37603
+ resolveTotal: () => null,
37604
+ totalHTML: () => ""
37605
+ };
37606
+ function createReportMoneyColumn(config2) {
37607
+ if (!config2 || typeof config2.overlay?.state !== "string") {
37608
+ return DISABLED_COLUMN;
37609
+ }
37610
+ const overlay = config2.overlay;
37611
+ const perDevice = config2.perDevice || {};
37612
+ const label = config2.headerLabel || REPORT_MONEY_HEADER;
37613
+ const valueFor = (deviceId) => hasAmount(deviceId) ? perDevice[deviceId] : null;
37614
+ const resolveTotal = (rowValues) => {
37615
+ if (!isMoneyColumnConfident(overlay)) {
37616
+ return { kind: "coverage", overlay };
37617
+ }
37618
+ if (hasAmount(config2.total)) {
37619
+ return {
37620
+ kind: "amount",
37621
+ amount: config2.total,
37622
+ formatted: formatBRL(config2.total),
37623
+ source: "backend"
37624
+ };
37625
+ }
37626
+ const summed = sumMoneyDecimals(rowValues || []);
37627
+ if (summed == null) {
37628
+ return { kind: "coverage", overlay };
37629
+ }
37630
+ return {
37631
+ kind: "amount",
37632
+ amount: summed,
37633
+ formatted: formatBRL(summed),
37634
+ source: "row-sum"
37635
+ };
37636
+ };
37637
+ return {
37638
+ enabled: true,
37639
+ headerCellHTML: () => `<th data-money-col="1" style="text-align:right;white-space:nowrap;">${label}</th>`,
37640
+ bodyCellHTML: (deviceId) => `<td data-money-col="1" data-label="${label}" style="text-align:right;font-weight:bold;">${cellInner(
37641
+ valueFor(deviceId)
37642
+ )}</td>`,
37643
+ cellValueHTML: (deviceId) => cellInner(valueFor(deviceId)),
37644
+ resolveTotal,
37645
+ totalHTML: (rowValues) => {
37646
+ const total = resolveTotal(rowValues);
37647
+ if (total.kind === "coverage") {
37648
+ return `<span data-money-total="coverage">${buildCoverageHTML(total.overlay, false)}</span>`;
37649
+ }
37650
+ return `<span data-money-total="amount" data-money-source="${total.source}">${total.formatted}</span>` + varianceNoteHTML(config2.variance);
37651
+ }
37652
+ };
37653
+ }
37654
+
37655
+ // src/components/financial-goals/financialIndicators.ts
37656
+ var DEFAULT_CHIP = {
37657
+ overBg: "#fee2e2",
37658
+ overText: "#b91c1c",
37659
+ underBg: "#dcfce7",
37660
+ underText: "#15803d",
37661
+ neutralBg: "#f1f5f9",
37662
+ neutralText: "#64748b"
37663
+ };
37664
+ var DEFAULT_VALUE_COLORS = {
37665
+ realized: "#2563eb",
37666
+ orcado: "#f59e0b",
37667
+ meta: "#7c3aed",
37668
+ prevYear: "#94a3b8"
37669
+ };
37670
+ var APPROX_PCT = 1;
37671
+ function hasAmount2(s) {
37672
+ return typeof s === "string" && s.trim() !== "";
37673
+ }
37674
+ function resolveMoneyRowValues(overlay, money) {
37675
+ const budget = overlay && overlay.state === "available" ? overlay.budget : void 0;
37676
+ const orcado = hasAmount2(money?.orcado) ? money.orcado : hasAmount2(budget?.projected?.amount) ? budget.projected.amount : null;
37677
+ const meta = hasAmount2(money?.meta) ? money.meta : hasAmount2(budget?.target?.amount) ? budget.target.amount : null;
37678
+ return {
37679
+ realized: hasAmount2(money?.realized) ? money.realized : null,
37680
+ orcado,
37681
+ meta,
37682
+ prevYear: hasAmount2(money?.prevYear) ? money.prevYear : null
37683
+ };
37684
+ }
37685
+ var CHIP_BASE = "border-radius:999px;padding:2px 10px;font:700 11px Nunito,sans-serif;white-space:nowrap;";
37686
+ function chipHTML(realized, reference, colors2) {
37687
+ const pct = computeDeltaPct(realized, reference);
37688
+ if (pct == null) {
37689
+ return `<span data-fin-chip="none" style="background:${colors2.neutralBg};color:${colors2.neutralText};${CHIP_BASE}">${DASH}</span>`;
37690
+ }
37691
+ const deltaAmount = hasAmount2(realized) && hasAmount2(reference) ? formatBRLDelta(subtractDecimals(realized, reference)) : DASH;
37692
+ const pctTxt = formatDeltaPct(pct);
37693
+ if (Math.abs(pct) < APPROX_PCT) {
37694
+ return `<span data-fin-chip="approx" title="${deltaAmount}" style="background:${colors2.neutralBg};color:${colors2.neutralText};${CHIP_BASE}">&#8776; ${pctTxt}</span>`;
37695
+ }
37696
+ if (pct > 0) {
37697
+ return `<span data-fin-chip="over" title="${deltaAmount}" style="background:${colors2.overBg};color:${colors2.overText};${CHIP_BASE}">&#8593; ${pctTxt}</span>`;
37698
+ }
37699
+ return `<span data-fin-chip="under" title="${deltaAmount}" style="background:${colors2.underBg};color:${colors2.underText};${CHIP_BASE}">&#8595; ${pctTxt}</span>`;
37700
+ }
37701
+ function parseCents(s) {
37702
+ if (!hasAmount2(s)) return null;
37703
+ const m = /^([+-]?)(\d+)(?:\.(\d+))?$/.exec(s.trim());
37704
+ if (!m) return null;
37705
+ const sign = m[1] === "-" ? -1 : 1;
37706
+ const whole = Number(m[2]);
37707
+ const frac = (m[3] || "").padEnd(3, "0");
37708
+ let cents = Number(frac.slice(0, 2));
37709
+ if (frac.charAt(2) >= "5") cents += 1;
37710
+ const total = whole * 100 + cents;
37711
+ return Number.isFinite(total) ? sign * total : null;
37712
+ }
37713
+ function subtractDecimals(a, b) {
37714
+ const pa = parseCents(a);
37715
+ const pb = parseCents(b);
37716
+ if (pa == null || pb == null) return null;
37717
+ const diff = pa - pb;
37718
+ const neg = diff < 0;
37719
+ const abs = Math.abs(diff);
37720
+ const intPart = Math.trunc(abs / 100);
37721
+ const cents = String(abs % 100).padStart(2, "0");
37722
+ return `${neg ? "-" : ""}${intPart}.${cents}`;
37723
+ }
37724
+ function figureHTML(label, amount, color) {
37725
+ return `<span style="display:inline-flex;align-items:center;gap:4px;white-space:nowrap;"><span style="color:${color};font-weight:700;">${label}</span><b style="color:var(--gc-text2, #334155);font-weight:700;">${formatBRL(amount)}</b></span>`;
37726
+ }
37727
+ function buildFinancialRowHTML(values, chipBaseline, chipColors, valueColors) {
37728
+ const reference = chipBaseline === "prevYear" ? values.prevYear : hasAmount2(values.meta) ? values.meta : values.prevYear;
37729
+ const figures = [
37730
+ figureHTML("Realizado", values.realized, valueColors.realized),
37731
+ figureHTML("Or\xE7ado", values.orcado, valueColors.orcado),
37732
+ figureHTML("Meta", values.meta, valueColors.meta)
37733
+ ].join("");
37734
+ return `<div class="myio-fin__row" data-fin-row="1" style="display:flex;align-items:center;justify-content:space-between;gap:8px;"><div style="font:600 11px Nunito,sans-serif;color:var(--gc-muted, #64748b);display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0;"><span aria-hidden="true">\u{1F4B0}</span>` + figures + `</div>` + chipHTML(values.realized, reference, chipColors) + `</div><div class="myio-fin__note" style="font:600 10px Nunito,sans-serif;color:var(--gc-muted, #94a3b8);margin-top:2px;">Estimativa em R$</div>`;
37735
+ }
37736
+ function renderFinancialIndicators(options) {
37737
+ const overlay = options?.overlay;
37738
+ if (!overlay || typeof overlay.state !== "string") {
37739
+ return null;
37740
+ }
37741
+ const doc = options.document || (typeof document !== "undefined" ? document : void 0);
37742
+ if (!doc) {
37743
+ throw new Error(
37744
+ "renderFinancialIndicators requires a document (pass options.document in non-DOM envs)."
37745
+ );
37746
+ }
37747
+ const coverageOpts = {
37748
+ onCategorizeDevice: options.onCategorizeDevice,
37749
+ onManageCategories: options.onManageCategories,
37750
+ document: doc
37751
+ };
37752
+ if (overlay.state === "unavailable" || overlay.coverageComplete !== true) {
37753
+ return renderCoverageView(overlay, coverageOpts);
37754
+ }
37755
+ const values = resolveMoneyRowValues(overlay, options.money);
37756
+ const chipColors = { ...DEFAULT_CHIP, ...options.chipColors };
37757
+ const valueColors = {
37758
+ ...DEFAULT_VALUE_COLORS,
37759
+ ...options.valueColors
37760
+ };
37761
+ const chipBaseline = options.chipBaseline === "prevYear" ? "prevYear" : "meta";
37762
+ const root = doc.createElement("div");
37763
+ root.className = "myio-fin";
37764
+ root.setAttribute("data-fin-state", "available-money");
37765
+ root.innerHTML = buildFinancialRowHTML(values, chipBaseline, chipColors, valueColors);
37766
+ return root;
37767
+ }
37768
+
37769
+ // src/components/financial-goals/moneyVariance.ts
37770
+ var VARIANCE_LABEL_ABOVE = "Acima da meta (R$)";
37771
+ var VARIANCE_LABEL_BELOW = "Abaixo da meta (R$)";
37772
+ var VARIANCE_LABEL_ONTARGET = "Na meta (R$)";
37773
+ var VARIANCE_LABEL_WITHHELD = "Varia\xE7\xE3o indispon\xEDvel";
37774
+ var MONEY_VARIANCE_HEADER = "Varia\xE7\xE3o vs meta (R$)";
37775
+ var WITHHELD_RESULT = Object.freeze({
37776
+ verdict: "withheld",
37777
+ variance: null,
37778
+ pct: null
37779
+ });
37780
+ function hasAmount3(s) {
37781
+ return typeof s === "string" && s.trim() !== "";
37782
+ }
37783
+ function overlayWithholdsVerdict(overlay) {
37784
+ if (!overlay || typeof overlay.state !== "string") return false;
37785
+ if (overlay.state === "unavailable") return true;
37786
+ if (overlay.coverageComplete !== true) return true;
37787
+ const verdict = overlay.budget?.verdict;
37788
+ if (verdict && verdict.withinBudget !== true && verdict.withinBudget !== false) {
37789
+ return true;
37790
+ }
37791
+ return false;
37792
+ }
37793
+ function resolveGoalAmount(input) {
37794
+ if (hasAmount3(input.currencyBudget)) return input.currencyBudget;
37795
+ const overlay = input.overlay;
37796
+ if (overlay && overlay.state === "available" && hasAmount3(overlay.budget?.target?.amount)) {
37797
+ return overlay.budget.target.amount;
37798
+ }
37799
+ return null;
37800
+ }
37801
+ function computeMoneyVariance(input) {
37802
+ if (!input || input.withheld === true) return WITHHELD_RESULT;
37803
+ if (overlayWithholdsVerdict(input.overlay)) return WITHHELD_RESULT;
37804
+ const realized = hasAmount3(input.monetaryProjection) ? input.monetaryProjection : null;
37805
+ const goal = resolveGoalAmount(input);
37806
+ if (realized == null || goal == null) return WITHHELD_RESULT;
37807
+ const variance = subtractDecimals(realized, goal);
37808
+ if (variance == null) return WITHHELD_RESULT;
37809
+ const varCents = decimalStringToCents(variance);
37810
+ const goalCents = decimalStringToCents(goal);
37811
+ if (varCents == null) return WITHHELD_RESULT;
37812
+ const pct = goalCents != null && goalCents !== 0n ? computeDeltaPct(realized, goal) : null;
37813
+ const verdict = varCents > 0n ? "above" : varCents < 0n ? "below" : "onTarget";
37814
+ return { verdict, variance, pct };
37815
+ }
37816
+ var DEFAULT_CHIP2 = {
37817
+ overBg: "#fee2e2",
37818
+ overText: "#b91c1c",
37819
+ underBg: "#dcfce7",
37820
+ underText: "#15803d",
37821
+ neutralBg: "#f1f5f9",
37822
+ neutralText: "#64748b"
37823
+ };
37824
+ var CHIP_BASE2 = "border-radius:999px;padding:2px 10px;font:700 11px Nunito,sans-serif;white-space:nowrap;display:inline-flex;align-items:center;gap:4px;";
37825
+ function buildMoneyVarianceHTML(input, chipColors = DEFAULT_CHIP2) {
37826
+ const r = computeMoneyVariance(input);
37827
+ if (r.verdict === "withheld") {
37828
+ return `<span data-money-variance="withheld" style="background:${chipColors.neutralBg};color:${chipColors.neutralText};${CHIP_BASE2}">${VARIANCE_LABEL_WITHHELD}</span>`;
37829
+ }
37830
+ const amount = hasAmount3(r.variance) ? formatBRLDelta(r.variance) : DASH;
37831
+ const pctTxt = r.pct == null ? "" : ` &middot; ${formatDeltaPct(r.pct)}`;
37832
+ if (r.verdict === "above") {
37833
+ return `<span data-money-variance="above" title="${VARIANCE_LABEL_ABOVE}" style="background:${chipColors.overBg};color:${chipColors.overText};${CHIP_BASE2}">&#8593; ${VARIANCE_LABEL_ABOVE} ${amount}${pctTxt}</span>`;
37834
+ }
37835
+ if (r.verdict === "below") {
37836
+ return `<span data-money-variance="below" title="${VARIANCE_LABEL_BELOW}" style="background:${chipColors.underBg};color:${chipColors.underText};${CHIP_BASE2}">&#8595; ${VARIANCE_LABEL_BELOW} ${amount}${pctTxt}</span>`;
37837
+ }
37838
+ return `<span data-money-variance="ontarget" title="${VARIANCE_LABEL_ONTARGET}" style="background:${chipColors.neutralBg};color:${chipColors.neutralText};${CHIP_BASE2}">&#8776; ${VARIANCE_LABEL_ONTARGET} ${amount}${pctTxt}</span>`;
37839
+ }
37840
+ function renderMoneyVariance(options) {
37841
+ if (!options) return null;
37842
+ const hasOverlay = !!options.overlay && typeof options.overlay.state === "string";
37843
+ const hasAnyAmount = hasAmount3(options.monetaryProjection) || hasAmount3(options.currencyBudget);
37844
+ if (!hasOverlay && !hasAnyAmount && options.withheld !== true) return null;
37845
+ const doc = options.document || (typeof document !== "undefined" ? document : void 0);
37846
+ if (!doc) {
37847
+ throw new Error(
37848
+ "renderMoneyVariance requires a document (pass options.document in non-DOM envs)."
37849
+ );
37850
+ }
37851
+ const chipColors = { ...DEFAULT_CHIP2, ...options.chipColors };
37852
+ const root = doc.createElement("div");
37853
+ root.className = "myio-money-variance";
37854
+ root.setAttribute("data-money-variance-row", "1");
37855
+ const label = hasAmount3(options.label) ? `<span class="myio-money-variance__label" style="font:600 11px Nunito,sans-serif;color:var(--gc-muted, #64748b);margin-right:6px;">${escapeText(
37856
+ options.label
37857
+ )}</span>` : "";
37858
+ root.innerHTML = `<div style="display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px;">` + label + buildMoneyVarianceHTML(options, chipColors) + `</div>`;
37859
+ return root;
37860
+ }
37861
+ function escapeText(s) {
37862
+ return String(s ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
37863
+ }
37864
+ var DISABLED_COLUMN2 = {
37865
+ enabled: false,
37866
+ headerCellHTML: () => "",
37867
+ bodyCellHTML: () => "",
37868
+ cellValueHTML: () => ""
37869
+ };
37870
+ function createMoneyVarianceColumn(config2) {
37871
+ if (!config2 || typeof config2.overlay?.state !== "string" || !config2.perDeviceGoal) {
37872
+ return DISABLED_COLUMN2;
37873
+ }
37874
+ const overlay = config2.overlay;
37875
+ const perDeviceRealized = config2.perDeviceRealized || {};
37876
+ const perDeviceGoal = config2.perDeviceGoal || {};
37877
+ const label = config2.headerLabel || MONEY_VARIANCE_HEADER;
37878
+ const chipColors = { ...DEFAULT_CHIP2, ...config2.chipColors };
37879
+ const chipFor = (deviceId) => {
37880
+ const id = hasAmount3(deviceId) ? deviceId : null;
37881
+ return buildMoneyVarianceHTML(
37882
+ {
37883
+ overlay,
37884
+ monetaryProjection: id ? perDeviceRealized[id] : null,
37885
+ currencyBudget: id ? perDeviceGoal[id] : null
37886
+ },
37887
+ chipColors
37888
+ );
37889
+ };
37890
+ return {
37891
+ enabled: true,
37892
+ headerCellHTML: () => `<th data-money-variance-col="1" style="text-align:right;white-space:nowrap;">${label}</th>`,
37893
+ bodyCellHTML: (deviceId) => `<td data-money-variance-col="1" data-label="${label}" style="text-align:right;">${chipFor(
37894
+ deviceId
37895
+ )}</td>`,
37896
+ cellValueHTML: (deviceId) => chipFor(deviceId)
37897
+ };
37898
+ }
37899
+
37059
37900
  // src/components/premium-modals/report-all/AllReportModal.ts
37060
37901
  var DOMAIN_CONFIG2 = {
37061
37902
  energy: {
@@ -37088,6 +37929,18 @@ var AllReportModal = class {
37088
37929
  const domain = params.domain || "energy";
37089
37930
  this.domainConfig = DOMAIN_CONFIG2[domain];
37090
37931
  this.debugEnabled = params.debug === 1;
37932
+ this.moneyColumn = createReportMoneyColumn(params.money ?? null);
37933
+ if (this.moneyColumn.enabled && typeof document !== "undefined") {
37934
+ injectCoverageStyles(document);
37935
+ }
37936
+ this.varianceColumn = createMoneyVarianceColumn(
37937
+ params.money && params.money.perDeviceGoal ? {
37938
+ overlay: params.money.overlay,
37939
+ perDeviceRealized: params.money.perDevice,
37940
+ perDeviceGoal: params.money.perDeviceGoal,
37941
+ headerLabel: params.money.varianceHeaderLabel
37942
+ } : null
37943
+ );
37091
37944
  this.debugLog("\u{1F680} AllReportModal initialized", {
37092
37945
  customerId: params.customerId,
37093
37946
  itemsListLength: params.itemsList?.length || 0,
@@ -37142,6 +37995,12 @@ var AllReportModal = class {
37142
37995
  // Footer premium: Customer · relógio · versão | Powered by MYIO | PDF/CSV/XLSX.
37143
37996
  // Os botões de export vivem AQUI (removidos da toolbar).
37144
37997
  modalFooter = null;
37998
+ // RFC-0228 A6 — R$ money column. Disabled (all fragments '') when params.money is
37999
+ // absent, so the table/summary stay byte-identical to the pre-A6 report.
38000
+ moneyColumn;
38001
+ // RFC-0228 A7 — per-consumer realized-vs-goal R$ variance column. Disabled (all
38002
+ // fragments '') when params.money.perDeviceGoal is absent → byte-identical.
38003
+ varianceColumn;
37145
38004
  // Debug logging helper
37146
38005
  debugLog(message, data) {
37147
38006
  if (this.debugEnabled) {
@@ -37606,8 +38465,25 @@ var AllReportModal = class {
37606
38465
  }
37607
38466
  ];
37608
38467
  if (!isTemperature) kpis.push({ value: String(zeroCount), label: "Sem Consumo" });
38468
+ const moneyKpi = this.computeMoneyKpi();
38469
+ if (moneyKpi) kpis.push(moneyKpi);
37609
38470
  return kpis;
37610
38471
  }
38472
+ // RFC-0228 A6 — the "Custo projetado (R$)" summary KPI, or null when money is off.
38473
+ // Reuses the A6 column's honest-coverage resolution: a formatted R$ total only when
38474
+ // the overlay is confidently priced (DEC-8), otherwise a coverage state label.
38475
+ computeMoneyKpi() {
38476
+ if (!this.moneyColumn.enabled) return null;
38477
+ const rowValues = this.data.map((r) => r.id ? this.params.money?.perDevice?.[r.id] : null);
38478
+ const total = this.moneyColumn.resolveTotal(rowValues);
38479
+ if (!total) return null;
38480
+ const label = this.params.money?.headerLabel || "Custo projetado (R$)";
38481
+ if (total.kind === "amount") {
38482
+ return { value: total.formatted, label };
38483
+ }
38484
+ const value = total.overlay.state === "unavailable" ? "Indispon\xEDvel" : "Cobertura incompleta";
38485
+ return { value, label };
38486
+ }
37611
38487
  renderTable() {
37612
38488
  const container = document.getElementById("table-container");
37613
38489
  if (!container) return;
@@ -37625,13 +38501,14 @@ var AllReportModal = class {
37625
38501
  const isGrouped = paginatedData.some((r) => r.groupLabel);
37626
38502
  const grandTotal = this.calculateTotalConsumption();
37627
38503
  const pct = (v) => grandTotal > 0 ? `${fmtPt(v / grandTotal * 100)}%` : "\u2014";
38504
+ const moneyCell = (row) => this.moneyColumn.bodyCellHTML(row.id);
37628
38505
  const tableRows = isGrouped ? this.renderGroupedRows(paginatedData, grandTotal) : paginatedData.map(
37629
38506
  (row) => `
37630
38507
  <tr>
37631
38508
  <td data-label="Identificador" style="font-family: monospace; font-weight: bold; text-transform: uppercase;">${row.identifier}</td>
37632
38509
  <td data-label="Nome"><strong>${row.name}</strong></td>
37633
38510
  <td data-label="${this.domainConfig.label}" style="text-align: right; font-weight: bold;">${fmtPt(row.consumption)}</td>
37634
- <td data-label="%" style="text-align: right; color: var(--myio-text-muted);">${pct(row.consumption)}</td>
38511
+ ${moneyCell(row)}${this.varianceColumn.bodyCellHTML(row.id)}<td data-label="%" style="text-align: right; color: var(--myio-text-muted);">${pct(row.consumption)}</td>
37635
38512
  </tr>
37636
38513
  `
37637
38514
  ).join("");
@@ -37682,7 +38559,7 @@ var AllReportModal = class {
37682
38559
  ${this.domainConfig.label}
37683
38560
  <span style="margin-left: 4px; opacity: ${this.getSortOpacity("consumption")};">${this.getSortIcon("consumption")}</span>
37684
38561
  </th>
37685
- <th style="text-align: right; width: 14%;">%</th>
38562
+ ${this.moneyColumn.headerCellHTML()}${this.varianceColumn.headerCellHTML()}<th style="text-align: right; width: 14%;">%</th>
37686
38563
  </tr>
37687
38564
  </thead>
37688
38565
  <tbody>${tableRows}</tbody>
@@ -37754,7 +38631,7 @@ var AllReportModal = class {
37754
38631
  const groupTotal = items.reduce((s, r) => s + r.consumption, 0);
37755
38632
  const header = `
37756
38633
  <tr class="rp-group-header">
37757
- <td colspan="4">
38634
+ <td colspan="${4 + (this.moneyColumn.enabled ? 1 : 0) + (this.varianceColumn.enabled ? 1 : 0)}">
37758
38635
  ${groupLabel}
37759
38636
  <span class="rp-group-total">${items.length} dispositivos \xB7 ${fmtPt(groupTotal)} ${this.domainConfig.unit}</span>
37760
38637
  </td>
@@ -37765,7 +38642,7 @@ var AllReportModal = class {
37765
38642
  <td data-label="Identificador" style="font-family: monospace; font-weight: bold; text-transform: uppercase;">${row.identifier}</td>
37766
38643
  <td data-label="Nome"><strong>${row.name}</strong></td>
37767
38644
  <td data-label="${this.domainConfig.label}" style="text-align: right; font-weight: bold;">${fmtPt(row.consumption)}</td>
37768
- <td data-label="%" style="text-align: right; color: var(--myio-text-muted);">${pct(row.consumption)}</td>
38645
+ ${this.moneyColumn.bodyCellHTML(row.id)}${this.varianceColumn.bodyCellHTML(row.id)}<td data-label="%" style="text-align: right; color: var(--myio-text-muted);">${pct(row.consumption)}</td>
37769
38646
  </tr>`
37770
38647
  ).join("");
37771
38648
  return header + dataRows;
@@ -44098,12 +44975,12 @@ var WelcomeModalView = class _WelcomeModalView {
44098
44975
  if (this.metaTipEl) this.metaTipEl.style.display = "none";
44099
44976
  }
44100
44977
  buildMetaTipHtml(type, card) {
44101
- const esc6 = _WelcomeModalView.escapeMetaHtml;
44978
+ const esc8 = _WelcomeModalView.escapeMetaHtml;
44102
44979
  const chip = _WelcomeModalView.metaChip;
44103
44980
  const meta = card.metaCounts || {};
44104
44981
  const details = card.metaDetails || {};
44105
44982
  const loadingBody = '<div style="opacity:.7;padding:6px 0">Carregando\u2026</div>';
44106
- const header = (icon, label, count) => `<div style="font-weight:800;font-size:13px;margin-bottom:8px">${icon} ${label}${count === null || count === void 0 ? "" : ` (${count})`} \u2014 ${esc6(card.title)}</div>`;
44983
+ const header = (icon, label, count) => `<div style="font-weight:800;font-size:13px;margin-bottom:8px">${icon} ${label}${count === null || count === void 0 ? "" : ` (${count})`} \u2014 ${esc8(card.title)}</div>`;
44107
44984
  const row = (titleHtml, subHtml) => `<div style="padding:6px 0;border-top:1px solid #334155">
44108
44985
  <div style="font-weight:600">${titleHtml}</div>
44109
44986
  ${subHtml ? `<div style="opacity:.7">${subHtml}</div>` : ""}
@@ -44119,8 +44996,8 @@ var WelcomeModalView = class _WelcomeModalView {
44119
44996
  const chips = Object.entries(byState).map(([s, n]) => chip(`${s}: ${n}`)).join("") || '<span style="opacity:.7">Sem alarmes ativos</span>';
44120
44997
  const rows = alarms.slice(0, 10).map(
44121
44998
  (a) => row(
44122
- esc6(a.deviceName ? `${a.deviceName} \u2014 ${a.title || "Alarme"}` : a.title || "Alarme"),
44123
- `${esc6(a.severity || "")} \xB7 ${esc6(a.state || "")}`
44999
+ esc8(a.deviceName ? `${a.deviceName} \u2014 ${a.title || "Alarme"}` : a.title || "Alarme"),
45000
+ `${esc8(a.severity || "")} \xB7 ${esc8(a.state || "")}`
44124
45001
  )
44125
45002
  ).join("");
44126
45003
  return `${header("\u{1F6A8}", "Alarmes", meta.alarms ?? alarms.length)}<div style="margin-bottom:6px">${chips}</div>${rows}`;
@@ -44140,14 +45017,14 @@ var WelcomeModalView = class _WelcomeModalView {
44140
45017
  const rows = annotations.slice(0, 10).map((a) => {
44141
45018
  const when = a.createdAt ? new Date(a.createdAt).toLocaleDateString("pt-BR") : "";
44142
45019
  const text2 = a.text.length > 90 ? `${a.text.slice(0, 90)}\u2026` : a.text;
44143
- return row(esc6(a.deviceLabel || "Dispositivo"), `${esc6(text2)}${when ? ` \xB7 ${esc6(when)}` : ""}`);
45020
+ return row(esc8(a.deviceLabel || "Dispositivo"), `${esc8(text2)}${when ? ` \xB7 ${esc8(when)}` : ""}`);
44144
45021
  }).join("");
44145
45022
  return `${header("\u{1F4CB}", "Anota\xE7\xF5es", meta.annotations ?? annotations.length)}<div style="margin-bottom:6px">${chips}</div>${rows}`;
44146
45023
  }
44147
45024
  if (type === "users") {
44148
45025
  if (meta.users === null) return header("\u{1F465}", "Usu\xE1rios") + loadingBody;
44149
45026
  const users = details.users || [];
44150
- const rows = users.slice(0, 10).map((u) => row(esc6(u.name), esc6(u.email || ""))).join("") || '<div style="opacity:.7;padding:6px 0">Sem usu\xE1rios</div>';
45027
+ const rows = users.slice(0, 10).map((u) => row(esc8(u.name), esc8(u.email || ""))).join("") || '<div style="opacity:.7;padding:6px 0">Sem usu\xE1rios</div>';
44151
45028
  const more = users.length > 10 ? `<div style="opacity:.7;padding:6px 0 0">\u2026 e mais ${users.length - 10}</div>` : "";
44152
45029
  return `${header("\u{1F465}", "Usu\xE1rios", meta.users ?? users.length)}${rows}${more}`;
44153
45030
  }
@@ -56547,7 +57424,7 @@ var IMPORTANCE_COLORS2 = {
56547
57424
  5: "#ff7675"
56548
57425
  };
56549
57426
  var ORDERED_TYPES = ["observation", "pending", "maintenance", "activity"];
56550
- function esc(s) {
57427
+ function esc2(s) {
56551
57428
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
56552
57429
  }
56553
57430
  function fmtDt(iso) {
@@ -56577,11 +57454,11 @@ function renderCard(ann) {
56577
57454
  <span class="adm-annot-imp-badge" style="background:${ic};color:${darkText ? "#fff" : "#333"}" title="${IMPORTANCE_LABELS2[ann.importance]}">${stars}</span>
56578
57455
  ${ann.archived ? '<span class="adm-annot-archived-badge">Arquivada</span>' : ""}
56579
57456
  </div>
56580
- <div class="adm-annot-card-text">${esc(ann.text)}</div>
57457
+ <div class="adm-annot-card-text">${esc2(ann.text)}</div>
56581
57458
  ${ann.dueDate ? `<div class="adm-annot-due">\u23F0 Prazo: ${new Date(ann.dueDate).toLocaleDateString("pt-BR")}</div>` : ""}
56582
57459
  <div class="adm-annot-card-meta">
56583
57460
  <div class="adm-annot-avatar">${ini}</div>
56584
- <span class="adm-annot-author">${esc(ann.createdBy)}</span>
57461
+ <span class="adm-annot-author">${esc2(ann.createdBy)}</span>
56585
57462
  <span class="adm-annot-date">${fmtDt(ann.createdAt)}</span>
56586
57463
  </div>
56587
57464
  ${!ann.archived ? `<div class="adm-annot-actions">
@@ -56609,7 +57486,7 @@ function renderForm(editAnn) {
56609
57486
  <div class="adm-annot-form" data-edit-id="${editAnn?.id ?? ""}">
56610
57487
  <div class="adm-annot-form-title">${editAnn ? "\u270F\uFE0F Editar Anota\xE7\xE3o" : "\u270F\uFE0F Nova Anota\xE7\xE3o"}</div>
56611
57488
  <textarea class="adm-annot-textarea" id="admAnnotText" maxlength="255"
56612
- placeholder="Digite sua anota\xE7\xE3o (m\xE1x. 255 caracteres)...">${editAnn ? esc(editAnn.text) : ""}</textarea>
57489
+ placeholder="Digite sua anota\xE7\xE3o (m\xE1x. 255 caracteres)...">${editAnn ? esc2(editAnn.text) : ""}</textarea>
56613
57490
  <div class="adm-annot-char-count"><span id="admAnnotCC">${editAnn ? editAnn.text.length : 0}</span> / 255</div>
56614
57491
  <div class="adm-annot-form-row">
56615
57492
  <div class="adm-annot-form-field">
@@ -56995,7 +57872,7 @@ function initials2(name) {
56995
57872
  const parts = name.trim().split(/\s+/);
56996
57873
  return parts.length >= 2 ? (parts[0][0] + parts[parts.length - 1][0]).toUpperCase() : name.substring(0, 2).toUpperCase();
56997
57874
  }
56998
- function esc2(s) {
57875
+ function esc3(s) {
56999
57876
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
57000
57877
  }
57001
57878
  function buildTooltipHtml(annotations) {
@@ -57016,11 +57893,11 @@ function buildTooltipHtml(annotations) {
57016
57893
  <span class="annot-tooltip__type-badge" style="background:${tc}18;color:${tc};border:1px solid ${tc}40">${TYPE_LABELS2[ann.type]}</span>
57017
57894
  <span class="annot-tooltip__stars" style="color:${ic}">${stars}</span>
57018
57895
  </div>
57019
- <div class="annot-tooltip__text">${esc2(ann.text)}</div>
57896
+ <div class="annot-tooltip__text">${esc3(ann.text)}</div>
57020
57897
  ${ann.dueDate ? `<div class="annot-tooltip__due">\u23F0 Prazo: ${new Date(ann.dueDate).toLocaleDateString("pt-BR")}</div>` : ""}
57021
57898
  <div class="annot-tooltip__meta">
57022
57899
  <span class="annot-tooltip__avatar">${ini}</span>
57023
- <span class="annot-tooltip__author">${esc2(ann.createdBy)}</span>
57900
+ <span class="annot-tooltip__author">${esc3(ann.createdBy)}</span>
57024
57901
  <span class="annot-tooltip__date">${fmtDt2(ann.createdAt)}</span>
57025
57902
  </div>
57026
57903
  </div>`;
@@ -70671,7 +71548,7 @@ function buildYearOptions() {
70671
71548
  for (let y = now + 2; y >= now - 2; y--) out.push(y);
70672
71549
  return out;
70673
71550
  }
70674
- function esc3(s) {
71551
+ function esc4(s) {
70675
71552
  return String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
70676
71553
  }
70677
71554
  function openGoalsPanel(params) {
@@ -71467,13 +72344,13 @@ function openGoalsPanel(params) {
71467
72344
  const g = state6.goals || {};
71468
72345
  const parts = [];
71469
72346
  if (hasCoverageGaps(g.coverageGaps)) {
71470
- parts.push(`<p style="margin:0 0 8px;">${esc3(buildCoverageWarningTextPtBR(g.coverageGaps, "GERAL"))}</p>`);
72347
+ parts.push(`<p style="margin:0 0 8px;">${esc4(buildCoverageWarningTextPtBR(g.coverageGaps, "GERAL"))}</p>`);
71471
72348
  }
71472
72349
  (Array.isArray(g.devices) ? g.devices : []).forEach((d) => {
71473
72350
  if (!hasCoverageGaps(d && d.coverageGaps)) return;
71474
72351
  const label = d.label || d.code || "medidor";
71475
72352
  parts.push(
71476
- `<p style="margin:0 0 8px;">${esc3(buildCoverageWarningTextPtBR(d.coverageGaps, `do medidor ${label}`))}</p>`
72353
+ `<p style="margin:0 0 8px;">${esc4(buildCoverageWarningTextPtBR(d.coverageGaps, `do medidor ${label}`))}</p>`
71477
72354
  );
71478
72355
  });
71479
72356
  return `<div style="font-size:12px;line-height:1.55;color:#334155;">${parts.join("")}</div>`;
@@ -71513,7 +72390,7 @@ function openGoalsPanel(params) {
71513
72390
  html += `<div class="myio-goals-alert myio-goals-alert-amber">${icon("alert")} ${i18n2.versionConflict}</div>`;
71514
72391
  }
71515
72392
  if (state6.saveError && !state6.conflict) {
71516
- html += `<div class="myio-goals-alert myio-goals-alert-red">${icon("alert")} ${esc3(state6.saveError)}</div>`;
72393
+ html += `<div class="myio-goals-alert myio-goals-alert-red">${icon("alert")} ${esc4(state6.saveError)}</div>`;
71517
72394
  }
71518
72395
  if (state6.saved && !state6.saveError) {
71519
72396
  html += `<div class="myio-goals-alert myio-goals-alert-green">${icon("check")} ${i18n2.saveSuccess}</div>`;
@@ -71528,7 +72405,7 @@ function openGoalsPanel(params) {
71528
72405
  return `
71529
72406
  <div class="myio-goals-center myio-goals-center-col">
71530
72407
  ${icon("alert-lg")}
71531
- <p>${esc3(state6.error)}</p>
72408
+ <p>${esc4(state6.error)}</p>
71532
72409
  <button class="myio-goals-btn myio-goals-btn-outline" data-action="retry">${i18n2.retry}</button>
71533
72410
  </div>`;
71534
72411
  }
@@ -71618,7 +72495,7 @@ function openGoalsPanel(params) {
71618
72495
  const cellLabel = state6.level === "month" ? i18n2.months[key] : state6.level === "day" ? i18n2.dayCell.replace("{day}", Number(key)) : `${key}:00`;
71619
72496
  const canDrill = state6.level !== "hour";
71620
72497
  const labelEl = canDrill ? `<button class="myio-goals-cell-drill" data-action="${state6.level === "month" ? "open-month" : "open-day"}" data-key="${key}" title="${i18n2.drill.openTitle}">${cellLabel} ${icon("chevron-sm")}</button>` : `<span class="myio-goals-cell-label">${cellLabel}</span>`;
71621
- const val = state6.draft[key] !== void 0 ? esc3(state6.draft[key]) : "";
72498
+ const val = state6.draft[key] !== void 0 ? esc4(state6.draft[key]) : "";
71622
72499
  const minAttr = cfg.allowsNegative ? "" : 'min="0"';
71623
72500
  return `
71624
72501
  <div class="myio-goals-cell">
@@ -71686,7 +72563,7 @@ function openGoalsPanel(params) {
71686
72563
  const actor = ev.actor ? `${String(ev.actor).slice(0, 8)}\u2026` : i18n2.history.system;
71687
72564
  const extra = ev.bucketCount - (ev.details ? ev.details.length : 0);
71688
72565
  const chips = (ev.details || []).slice(0, DETAIL_PREVIEW).map(
71689
- (d) => `<span class="myio-goals-tl-chip"><span class="myio-goals-tl-ref">${esc3(formatRef(d.ref))}</span>${d.value === null ? "\u2014" : fmt2(d.value)}</span>`
72566
+ (d) => `<span class="myio-goals-tl-chip"><span class="myio-goals-tl-ref">${esc4(formatRef(d.ref))}</span>${d.value === null ? "\u2014" : fmt2(d.value)}</span>`
71690
72567
  ).join("");
71691
72568
  const more = extra > 0 ? `<span class="myio-goals-tl-more">${i18n2.history.moreDetails.replace("{n}", extra)}</span>` : "";
71692
72569
  return `
@@ -71696,7 +72573,7 @@ function openGoalsPanel(params) {
71696
72573
  <span class="myio-goals-tl-title">${i18n2.history.source[ev.source] || i18n2.history.source.EDIT}</span>
71697
72574
  <span class="myio-goals-tl-ver">v${ev.version}</span>
71698
72575
  </div>
71699
- <p class="myio-goals-tl-meta" title="${esc3(new Date(ev.changedAt).toLocaleString(locale))}">${esc3(actor)} \xB7 ${esc3(timeAgo(ev.changedAt))}</p>
72576
+ <p class="myio-goals-tl-meta" title="${esc4(new Date(ev.changedAt).toLocaleString(locale))}">${esc4(actor)} \xB7 ${esc4(timeAgo(ev.changedAt))}</p>
71700
72577
  <p class="myio-goals-tl-records">${i18n2.history.records.replace("{n}", ev.bucketCount)} \xB7 ${i18n2.levels[ev.actionLevel] || ev.actionLevel}</p>
71701
72578
  ${chips || more ? `<div class="myio-goals-tl-chips">${chips}${more}</div>` : ""}
71702
72579
  </li>`;
@@ -71743,7 +72620,7 @@ ${state6.year}-03-15T08,15`;
71743
72620
  let html = `
71744
72621
  <div class="myio-goals-import-hint">
71745
72622
  <p>${i18n2.import.formatHint}</p>
71746
- <code>${esc3(sample)}</code>
72623
+ <code>${esc4(sample)}</code>
71747
72624
  </div>`;
71748
72625
  if (!applied) {
71749
72626
  html += `
@@ -71757,16 +72634,16 @@ ${state6.year}-03-15T08,15`;
71757
72634
  <input type="file" id="myio-goals-csv-file" accept=".csv,.txt,text/csv,text/plain" style="display:none" />
71758
72635
  </div>
71759
72636
  </div>
71760
- <textarea id="myio-goals-csv-textarea" rows="8" spellcheck="false" placeholder="${esc3(`bucket,value
72637
+ <textarea id="myio-goals-csv-textarea" rows="8" spellcheck="false" placeholder="${esc4(`bucket,value
71761
72638
  ${state6.year},100000
71762
72639
  ${state6.year}-03,9000
71763
72640
  ${state6.year}-03-15,310
71764
- ${state6.year}-03-15T08,15`)}">${esc3(state6.csv)}</textarea>
72641
+ ${state6.year}-03-15T08,15`)}">${esc4(state6.csv)}</textarea>
71765
72642
  <p class="myio-goals-microhint">${i18n2.import.unitNote.replace("{unit}", unitLabel2())}</p>
71766
72643
  </div>`;
71767
72644
  }
71768
72645
  if (state6.saveError && state6.conflictVersion === null) {
71769
- html += `<div class="myio-goals-alert myio-goals-alert-red">${icon("alert")} ${esc3(state6.saveError)}</div>`;
72646
+ html += `<div class="myio-goals-alert myio-goals-alert-red">${icon("alert")} ${esc4(state6.saveError)}</div>`;
71770
72647
  }
71771
72648
  if (state6.conflictVersion !== null) {
71772
72649
  html += `<div class="myio-goals-alert myio-goals-alert-amber">${icon("alert")} ${i18n2.import.conflict}</div>`;
@@ -71795,7 +72672,7 @@ ${state6.year}-03-15T08,15`)}">${esc3(state6.csv)}</textarea>
71795
72672
  <div class="myio-goals-import-diags">
71796
72673
  <p class="myio-goals-import-diags-title">${i18n2.import.diagnosticsTitle}</p>
71797
72674
  <ul>${result.diagnostics.map(
71798
- (d) => `<li>${icon("alert-sm")} <span><span class="myio-goals-diag-line">${i18n2.import.line.replace("{line}", d.line)}</span>${d.bucket ? ` <span class="myio-goals-diag-bucket">[${esc3(d.bucket)}]</span>` : ""} ${esc3(d.reason)}</span></li>`
72675
+ (d) => `<li>${icon("alert-sm")} <span><span class="myio-goals-diag-line">${i18n2.import.line.replace("{line}", d.line)}</span>${d.bucket ? ` <span class="myio-goals-diag-bucket">[${esc4(d.bucket)}]</span>` : ""} ${esc4(d.reason)}</span></li>`
71799
72676
  ).join("")}</ul>
71800
72677
  </div>`;
71801
72678
  }
@@ -71803,7 +72680,7 @@ ${state6.year}-03-15T08,15`)}">${esc3(state6.csv)}</textarea>
71803
72680
  html += `
71804
72681
  <div class="myio-goals-import-log">
71805
72682
  <p class="myio-goals-import-log-title">${i18n2.import.logTitle}</p>
71806
- <ul>${result.log.map((line) => `<li>${esc3(line)}</li>`).join("")}</ul>
72683
+ <ul>${result.log.map((line) => `<li>${esc4(line)}</li>`).join("")}</ul>
71807
72684
  </div>`;
71808
72685
  }
71809
72686
  html += `</div>`;
@@ -75545,6 +76422,26 @@ var EnergySummaryTooltip = {
75545
76422
  }
75546
76423
  return formatConsumption3(summary.totalConsumption, unit);
75547
76424
  },
76425
+ /**
76426
+ * RFC-0228 A6 — the R$ figure shown beside "Consumo Total", or `''` when no money
76427
+ * overlay is present (gate off → byte-identical to the pre-A6 tooltip).
76428
+ *
76429
+ * DEC-8 (GCDR RFC-0054): clients consume the backend decimal string and never
76430
+ * re-derive. When the overlay is confidently priced this shows the backend
76431
+ * aggregate verbatim; otherwise it shows A4's honest coverage indicator — never a
76432
+ * fabricated R$ 0. Amounts are formatted string-in/string-out via A2a/F0.
76433
+ */
76434
+ formatMoneyTotal(summary) {
76435
+ const money = summary.money;
76436
+ const column = createReportMoneyColumn(
76437
+ money ? { overlay: money.overlay, perDevice: money.perDevice || {}, total: money.total, variance: money.variance } : null
76438
+ );
76439
+ if (!column.enabled) return "";
76440
+ if (typeof document !== "undefined") injectCoverageStyles(document);
76441
+ const rowValues = money?.perDevice ? Object.values(money.perDevice) : [];
76442
+ const inner = column.totalHTML(rowValues);
76443
+ return `<div class="energy-summary-tooltip__total energy-summary-tooltip__total--money" data-money-total-row="1"><span class="energy-summary-tooltip__total-label">Custo projetado (R$)</span><span class="energy-summary-tooltip__total-value">${inner}</span></div>`;
76444
+ },
75548
76445
  /**
75549
76446
  * Render shopping view rows (for "Por Shopping" tab)
75550
76447
  * Shows device count and consumption breakdown by shopping with expandable subcategories
@@ -75699,7 +76596,7 @@ var EnergySummaryTooltip = {
75699
76596
  <div class="energy-summary-tooltip__total">
75700
76597
  <span class="energy-summary-tooltip__total-label">Consumo Total</span>
75701
76598
  <span class="energy-summary-tooltip__total-value">${this.formatTotalWithFilter(summary)}</span>
75702
- </div>
76599
+ </div>${this.formatMoneyTotal(summary)}
75703
76600
  </div>
75704
76601
  `;
75705
76602
  },
@@ -78624,7 +79521,7 @@ function injectCSS10() {
78624
79521
  document.head.appendChild(style);
78625
79522
  _cssInjected2 = true;
78626
79523
  }
78627
- function esc4(value) {
79524
+ function esc5(value) {
78628
79525
  return String(value == null ? "" : value).replace(
78629
79526
  /[&<>"]/g,
78630
79527
  (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]
@@ -78674,7 +79571,7 @@ function buildPieSvg(visible, total) {
78674
79571
  const val = Number(v.d.value) || 0;
78675
79572
  return `<svg class="myio-col-summary__pie" viewBox="0 0 220 220">
78676
79573
  <circle class="myio-col-summary__slice" data-idx="${v.idx}" cx="110" cy="110" r="100"
78677
- fill="${sliceColor(v.idx)}"><title>${esc4(v.d.name)} \u2014 ${esc4(fmt2(val))} (${fmtPct2(
79574
+ fill="${sliceColor(v.idx)}"><title>${esc5(v.d.name)} \u2014 ${esc5(fmt2(val))} (${fmtPct2(
78678
79575
  val,
78679
79576
  total,
78680
79577
  pd
@@ -78689,7 +79586,7 @@ function buildPieSvg(visible, total) {
78689
79586
  const a1 = acc / total * 360;
78690
79587
  return `<path class="myio-col-summary__slice" data-idx="${v.idx}"
78691
79588
  d="${arcPath(110, 110, 100, a0, a1)}" fill="${sliceColor(v.idx)}"
78692
- ><title>${esc4(v.d.name)} \u2014 ${esc4(fmt2(val))} (${fmtPct2(val, total, pd)})</title></path>`;
79589
+ ><title>${esc5(v.d.name)} \u2014 ${esc5(fmt2(val))} (${fmtPct2(val, total, pd)})</title></path>`;
78693
79590
  }).join("");
78694
79591
  return `<svg class="myio-col-summary__pie" viewBox="0 0 220 220">${paths}</svg>`;
78695
79592
  }
@@ -78703,8 +79600,8 @@ function buildLegend(total) {
78703
79600
  return `<div class="myio-col-summary__legend-row${isExcl ? " is-excluded" : ""}" data-idx="${idx}"
78704
79601
  title="Clique para ${isExcl ? "incluir" : "remover"} da pizza">
78705
79602
  <span class="myio-col-summary__legend-dot" style="background:${sliceColor(idx)};"></span>
78706
- <span class="myio-col-summary__legend-name">${esc4(d.name)}</span>
78707
- <span class="myio-col-summary__legend-val">${esc4(fmt2(val))}</span>
79603
+ <span class="myio-col-summary__legend-name">${esc5(d.name)}</span>
79604
+ <span class="myio-col-summary__legend-val">${esc5(fmt2(val))}</span>
78708
79605
  <span class="myio-col-summary__legend-pct">${pct}</span>
78709
79606
  </div>`;
78710
79607
  }).join("");
@@ -78719,7 +79616,7 @@ function buildInner() {
78719
79616
  const avg = count ? total / count : 0;
78720
79617
  const periodRow = data.periodLabel ? `<div class="myio-col-summary__kpi">
78721
79618
  <span class="myio-col-summary__kpi-label">Per\xEDodo</span>
78722
- <span class="myio-col-summary__kpi-value">${esc4(data.periodLabel)}</span>
79619
+ <span class="myio-col-summary__kpi-value">${esc5(data.periodLabel)}</span>
78723
79620
  </div>` : "";
78724
79621
  const kpis = `<div class="myio-col-summary__kpis">
78725
79622
  ${periodRow}
@@ -78729,13 +79626,13 @@ function buildInner() {
78729
79626
  </div>
78730
79627
  <div class="myio-col-summary__kpi">
78731
79628
  <span class="myio-col-summary__kpi-label">Consumo m\xE9dio</span>
78732
- <span class="myio-col-summary__kpi-value myio-col-summary__kpi-value--accent">${esc4(
79629
+ <span class="myio-col-summary__kpi-value myio-col-summary__kpi-value--accent">${esc5(
78733
79630
  fmt2(avg)
78734
79631
  )}</span>
78735
79632
  </div>
78736
79633
  <div class="myio-col-summary__kpi">
78737
79634
  <span class="myio-col-summary__kpi-label">Consumo total</span>
78738
- <span class="myio-col-summary__kpi-value">${esc4(fmt2(total))}</span>
79635
+ <span class="myio-col-summary__kpi-value">${esc5(fmt2(total))}</span>
78739
79636
  </div>
78740
79637
  </div>`;
78741
79638
  if (!data.devices.length) {
@@ -78749,8 +79646,8 @@ function buildInner() {
78749
79646
  ).slice(0, 3);
78750
79647
  const row = (v) => `
78751
79648
  <div class="myio-col-summary__row">
78752
- <span class="myio-col-summary__name" title="${esc4(v.d.name)}">${esc4(v.d.name)}</span>
78753
- <span class="myio-col-summary__val">${esc4(fmt2(Number(v.d.value) || 0))}</span>
79649
+ <span class="myio-col-summary__name" title="${esc5(v.d.name)}">${esc5(v.d.name)}</span>
79650
+ <span class="myio-col-summary__val">${esc5(fmt2(Number(v.d.value) || 0))}</span>
78754
79651
  <span class="myio-col-summary__pct">${fmtPct2(Number(v.d.value) || 0, total, pd)}</span>
78755
79652
  </div>`;
78756
79653
  const group = (label, list) => list.length ? `<div class="myio-col-summary__group">
@@ -78773,7 +79670,7 @@ function buildInner() {
78773
79670
  </div>
78774
79671
  <div class="myio-col-summary__footer">
78775
79672
  <span class="myio-col-summary__footer-label">Consumo Total \xB7 ${count} dispositivos</span>
78776
- <span class="myio-col-summary__footer-value">${esc4(fmt2(total))}</span>
79673
+ <span class="myio-col-summary__footer-value">${esc5(fmt2(total))}</span>
78777
79674
  </div>`;
78778
79675
  }
78779
79676
  function setHighlight(idx) {
@@ -86553,7 +87450,7 @@ function injectStyles11(doc) {
86553
87450
  `;
86554
87451
  (doc.head || doc.documentElement).appendChild(s);
86555
87452
  }
86556
- function esc5(v) {
87453
+ function esc6(v) {
86557
87454
  return String(v ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
86558
87455
  }
86559
87456
  function fmtPct3(pct) {
@@ -86574,8 +87471,8 @@ function renderDeltaChip(delta) {
86574
87471
  const tone = delta.tone === "good" ? "good" : delta.tone === "bad" ? "bad" : "neutral";
86575
87472
  const arrow = delta.arrow === "up" ? "\u2191" : delta.arrow === "down" ? "\u2193" : "\u2248";
86576
87473
  const pctTxt = fmtPct3(Math.abs(Number(delta.pct)));
86577
- const txt = delta.text ? ` ${esc5(delta.text)}` : "";
86578
- return `<span class="myio-gbt__chip myio-gbt__chip--${tone}">${arrow} ${esc5(pctTxt)}${txt}</span>`;
87474
+ const txt = delta.text ? ` ${esc6(delta.text)}` : "";
87475
+ return `<span class="myio-gbt__chip myio-gbt__chip--${tone}">${arrow} ${esc6(pctTxt)}${txt}</span>`;
86579
87476
  }
86580
87477
  function renderRow(row, level) {
86581
87478
  const hasChildren = Array.isArray(row.children) && row.children.length > 0;
@@ -86583,12 +87480,12 @@ function renderRow(row, level) {
86583
87480
  const id = `gbtr-${++_rowSeq}`;
86584
87481
  const pct = row.pct;
86585
87482
  const caret = hasChildren ? `<button class="myio-gbt__caret${open ? " is-open" : ""}" data-caret="${id}" title="Expandir/recolher">\u25B8</button>` : `<span class="myio-gbt__caret-spacer"></span>`;
86586
- const swatch = row.color ? `<span class="myio-gbt__swatch" style="background:${esc5(row.color)}"></span>` : "";
86587
- const icon = row.icon ? `<span class="myio-gbt__icon">${esc5(row.icon)}</span>` : "";
87483
+ const swatch = row.color ? `<span class="myio-gbt__swatch" style="background:${esc6(row.color)}"></span>` : "";
87484
+ const icon = row.icon ? `<span class="myio-gbt__icon">${esc6(row.icon)}</span>` : "";
86588
87485
  const chipHtml = row.delta ? renderDeltaChip(row.delta) : "";
86589
- const pctHtml = !row.delta && pct != null && isFinite(pct) ? `<span class="myio-gbt__pct">${esc5(fmtPct3(pct))}</span>` : "";
87486
+ const pctHtml = !row.delta && pct != null && isFinite(pct) ? `<span class="myio-gbt__pct">${esc6(fmtPct3(pct))}</span>` : "";
86590
87487
  const rowClass = level > 0 ? "myio-gbt__row myio-gbt__row--child" : "myio-gbt__row";
86591
- let html = `<div class="${rowClass}">` + caret + swatch + icon + `<span class="myio-gbt__label">${esc5(row.label)}</span><span class="myio-gbt__value">${esc5(row.valueText)}</span>` + chipHtml + pctHtml + `</div>`;
87488
+ let html = `<div class="${rowClass}">` + caret + swatch + icon + `<span class="myio-gbt__label">${esc6(row.label)}</span><span class="myio-gbt__value">${esc6(row.valueText)}</span>` + chipHtml + pctHtml + `</div>`;
86592
87489
  if (hasChildren) {
86593
87490
  const childHtml = row.children.map((c) => renderRow(c, level + 1)).join("");
86594
87491
  html += `<div class="myio-gbt__children${open ? " is-open" : ""}" data-children="${id}">${childHtml}</div>`;
@@ -86671,8 +87568,8 @@ function createGoalsBarTooltip(opts) {
86671
87568
  <div class="myio-gbt__card">
86672
87569
  <div class="myio-gbt__header">
86673
87570
  <div class="myio-gbt__title-wrap">
86674
- <div class="myio-gbt__title">${esc5(data.title)}</div>
86675
- ${data.subtitle ? `<div class="myio-gbt__subtitle">${esc5(data.subtitle)}</div>` : ""}
87571
+ <div class="myio-gbt__title">${esc6(data.title)}</div>
87572
+ ${data.subtitle ? `<div class="myio-gbt__subtitle">${esc6(data.subtitle)}</div>` : ""}
86676
87573
  </div>
86677
87574
  <button class="myio-gbt__btn myio-gbt__btn--pin${pinned ? " is-pinned" : ""}" data-action="pin" title="${pinned ? "Desafixar" : "Fixar na tela"}">\u{1F4CC}</button>
86678
87575
  <button class="myio-gbt__btn myio-gbt__btn--close" data-action="close" title="Fechar">\u2715</button>
@@ -87681,19 +88578,19 @@ function _updateCoverageHint(topDoc) {
87681
88578
  const deviceCount = data.granularity === "DEVICE" ? data.devices?.length ?? 0 : 0;
87682
88579
  hint.textContent = `\u26A0 Meta incompleta para este dom\xEDnio/ano` + (deviceCount > 0 ? ` \xB7 Por medidor (${deviceCount})` : "") + ` \u2014 passe o mouse para detalhes`;
87683
88580
  tabs.insertAdjacentElement("afterend", hint);
87684
- const esc6 = (s) => String(s ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
88581
+ const esc8 = (s) => String(s ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
87685
88582
  try {
87686
88583
  _coverageDetach = InfoTooltip.attach(hint, () => {
87687
88584
  const d = _getGoalsData(_currentDomain);
87688
88585
  const parts = [];
87689
88586
  if (hasCoverageGaps(d?.coverageGaps)) {
87690
- parts.push(`<p style="margin:0 0 8px;">${esc6(buildCoverageWarningTextPtBR(d?.coverageGaps, "GERAL"))}</p>`);
88587
+ parts.push(`<p style="margin:0 0 8px;">${esc8(buildCoverageWarningTextPtBR(d?.coverageGaps, "GERAL"))}</p>`);
87691
88588
  }
87692
88589
  (d?.devices || []).forEach((dev) => {
87693
88590
  if (!hasCoverageGaps(dev?.coverageGaps)) return;
87694
88591
  const label = dev.label || dev.code || "medidor";
87695
88592
  parts.push(
87696
- `<p style="margin:0 0 8px;">${esc6(buildCoverageWarningTextPtBR(dev.coverageGaps, `do medidor ${label}`))}</p>`
88593
+ `<p style="margin:0 0 8px;">${esc8(buildCoverageWarningTextPtBR(dev.coverageGaps, `do medidor ${label}`))}</p>`
87697
88594
  );
87698
88595
  });
87699
88596
  return {
@@ -90611,7 +91508,7 @@ function isPricingAllowed(email, superAdmin) {
90611
91508
  const e = (email ?? "").trim().toLowerCase();
90612
91509
  return e.endsWith("@myio.com.br");
90613
91510
  }
90614
- function formatBRL(value) {
91511
+ function formatBRL2(value) {
90615
91512
  if (!Number.isFinite(value)) return "";
90616
91513
  const amount = new Intl.NumberFormat("pt-BR", {
90617
91514
  minimumFractionDigits: 2,
@@ -91146,6 +92043,608 @@ function injectPricingPanelStyles(doc) {
91146
92043
  doc.head.appendChild(style);
91147
92044
  }
91148
92045
 
92046
+ // src/components/pricing-panel/tariffApiClient.ts
92047
+ var TariffApiError = class extends Error {
92048
+ code;
92049
+ status;
92050
+ details;
92051
+ currentVersion;
92052
+ constructor(code, status, message, details) {
92053
+ super(message || code);
92054
+ this.name = "TariffApiError";
92055
+ this.code = code;
92056
+ this.status = status;
92057
+ this.details = details;
92058
+ const cv = details && details.currentVersion;
92059
+ this.currentVersion = typeof cv === "number" ? cv : void 0;
92060
+ }
92061
+ };
92062
+ function joinUrl(baseUrl, basePath, path) {
92063
+ const b = baseUrl.replace(/\/+$/, "");
92064
+ const bp = basePath ? `/${basePath.replace(/^\/+|\/+$/g, "")}` : "";
92065
+ const p = path.startsWith("/") ? path : `/${path}`;
92066
+ return `${b}${bp}${p}`;
92067
+ }
92068
+ function tariffQuery(sel, granularity) {
92069
+ const qs = new URLSearchParams();
92070
+ qs.set("domain", sel.domain);
92071
+ qs.set("category", sel.category);
92072
+ qs.set("year", String(sel.year));
92073
+ if (granularity) qs.set("granularity", granularity);
92074
+ return qs.toString();
92075
+ }
92076
+ function parseETagVersion(headers) {
92077
+ const raw = headers.get("ETag") || headers.get("etag");
92078
+ if (!raw) return void 0;
92079
+ const m = /"?(-?\d+)"?/.exec(raw);
92080
+ return m ? Number(m[1]) : void 0;
92081
+ }
92082
+ var TariffApiClient = class {
92083
+ cfg;
92084
+ fetchImpl;
92085
+ basePath;
92086
+ constructor(config2) {
92087
+ if (!config2 || !config2.baseUrl) {
92088
+ throw new Error("TariffApiClient requires a baseUrl.");
92089
+ }
92090
+ this.cfg = config2;
92091
+ const injected = config2.fetchImpl || globalThis.fetch;
92092
+ if (typeof injected !== "function") {
92093
+ throw new Error("TariffApiClient requires a fetch implementation.");
92094
+ }
92095
+ this.fetchImpl = config2.fetchImpl ? config2.fetchImpl : injected.bind(globalThis);
92096
+ this.basePath = config2.basePath ?? "api/v1";
92097
+ }
92098
+ authHeaders() {
92099
+ const h2 = {};
92100
+ if (this.cfg.apiKey) h2["X-API-Key"] = this.cfg.apiKey;
92101
+ if (this.cfg.jwt) h2["Authorization"] = `Bearer ${this.cfg.jwt}`;
92102
+ if (this.cfg.tenantId) h2["X-Tenant-Id"] = this.cfg.tenantId;
92103
+ return h2;
92104
+ }
92105
+ /** Read the tariff tree. `granularity` defaults to `day` server-side. */
92106
+ async getTariff(sel, granularity = "hour") {
92107
+ const url = joinUrl(
92108
+ this.cfg.baseUrl,
92109
+ this.basePath,
92110
+ `/customers/${encodeURIComponent(sel.customerId)}/tariffs?${tariffQuery(sel, granularity)}`
92111
+ );
92112
+ const res = await this.fetchImpl(url, {
92113
+ method: "GET",
92114
+ headers: { Accept: "application/json", ...this.authHeaders() }
92115
+ });
92116
+ const body = await this.readBody(res);
92117
+ if (!res.ok) throw this.toError(res, body);
92118
+ const etagVersion = parseETagVersion(res.headers);
92119
+ const parsed = body || {};
92120
+ return {
92121
+ customerId: sel.customerId,
92122
+ domain: sel.domain,
92123
+ category: sel.category,
92124
+ year: sel.year,
92125
+ unit: parsed.unit,
92126
+ currency: parsed.currency,
92127
+ tariffModel: parsed.tariffModel,
92128
+ timezone: parsed.timezone,
92129
+ version: typeof parsed.version === "number" ? parsed.version : etagVersion ?? 0,
92130
+ tree: parsed.tree || {}
92131
+ };
92132
+ }
92133
+ /** REPLACE the whole year with a nested price tree. */
92134
+ async putTariff(sel, tree, expectedVersion) {
92135
+ return this.write("PUT", sel, { ...tree, ...guardBody(expectedVersion) }, expectedVersion);
92136
+ }
92137
+ /** MERGE sparse buckets into the year (finest level wins). */
92138
+ async patchTariff(sel, buckets, expectedVersion) {
92139
+ return this.write("PATCH", sel, { buckets, ...guardBody(expectedVersion) }, expectedVersion);
92140
+ }
92141
+ /**
92142
+ * DELETE the whole year (no `bucket`) or one sub-bucket (`bucket` present).
92143
+ * Whole-year delete is idempotent (204 even if absent).
92144
+ */
92145
+ async deleteTariff(sel, opts = {}) {
92146
+ const hasBody = Boolean(opts.bucket) || typeof opts.expectedVersion === "number";
92147
+ const url = joinUrl(
92148
+ this.cfg.baseUrl,
92149
+ this.basePath,
92150
+ `/customers/${encodeURIComponent(sel.customerId)}/tariffs?${tariffQuery(sel)}`
92151
+ );
92152
+ const headers = { Accept: "application/json", ...this.authHeaders() };
92153
+ if (typeof opts.expectedVersion === "number") headers["If-Match"] = `"${opts.expectedVersion}"`;
92154
+ const init = { method: "DELETE", headers };
92155
+ if (hasBody) {
92156
+ headers["Content-Type"] = "application/json";
92157
+ init.body = JSON.stringify({
92158
+ ...opts.bucket ? { bucket: opts.bucket } : {},
92159
+ ...guardBody(opts.expectedVersion)
92160
+ });
92161
+ }
92162
+ const res = await this.fetchImpl(url, init);
92163
+ if (res.status === 204) return {};
92164
+ const body = await this.readBody(res);
92165
+ if (!res.ok) throw this.toError(res, body);
92166
+ const etagVersion = parseETagVersion(res.headers);
92167
+ const v = (body && body.version) ?? etagVersion;
92168
+ return typeof v === "number" ? { version: v } : {};
92169
+ }
92170
+ async write(method, sel, body, expectedVersion) {
92171
+ const url = joinUrl(
92172
+ this.cfg.baseUrl,
92173
+ this.basePath,
92174
+ `/customers/${encodeURIComponent(sel.customerId)}/tariffs?${tariffQuery(sel)}`
92175
+ );
92176
+ const headers = {
92177
+ Accept: "application/json",
92178
+ "Content-Type": "application/json",
92179
+ ...this.authHeaders()
92180
+ };
92181
+ if (typeof expectedVersion === "number") headers["If-Match"] = `"${expectedVersion}"`;
92182
+ const res = await this.fetchImpl(url, { method, headers, body: JSON.stringify(body) });
92183
+ const parsed = await this.readBody(res);
92184
+ if (!res.ok) throw this.toError(res, parsed);
92185
+ const etagVersion = parseETagVersion(res.headers);
92186
+ const v = (parsed && parsed.version) ?? etagVersion;
92187
+ return { version: typeof v === "number" ? v : (expectedVersion ?? 0) + 1 };
92188
+ }
92189
+ async readBody(res) {
92190
+ try {
92191
+ const text2 = await res.text();
92192
+ if (!text2) return null;
92193
+ return JSON.parse(text2);
92194
+ } catch {
92195
+ return null;
92196
+ }
92197
+ }
92198
+ toError(res, body) {
92199
+ const err = body && body.error;
92200
+ if (err && typeof err === "object") {
92201
+ const e = err;
92202
+ return new TariffApiError(
92203
+ e.code || `HTTP_${res.status}`,
92204
+ res.status,
92205
+ e.message,
92206
+ e.details
92207
+ );
92208
+ }
92209
+ return new TariffApiError(`HTTP_${res.status}`, res.status, res.statusText);
92210
+ }
92211
+ };
92212
+ function guardBody(expectedVersion) {
92213
+ return typeof expectedVersion === "number" ? { expectedVersion } : {};
92214
+ }
92215
+ function createTariffApiClient(config2) {
92216
+ return new TariffApiClient(config2);
92217
+ }
92218
+
92219
+ // src/components/pricing-panel/tariffApiAdapter.ts
92220
+ var PANEL_TO_WIRE_DOMAIN = {
92221
+ energy: "ENERGY",
92222
+ water: "WATER"
92223
+ };
92224
+ var WIRE_TO_PANEL_DOMAIN = {
92225
+ ENERGY: "energy",
92226
+ WATER: "water"
92227
+ };
92228
+ var PANEL_TO_WIRE_CATEGORY = {
92229
+ lojas: "SPECIFIC",
92230
+ area_comum: "COMMON_AREA"
92231
+ };
92232
+ var WIRE_TO_PANEL_CATEGORY = {
92233
+ SPECIFIC: "lojas",
92234
+ COMMON_AREA: "area_comum"
92235
+ };
92236
+ function panelDomainToWire(d) {
92237
+ const w = PANEL_TO_WIRE_DOMAIN[d];
92238
+ if (!w) throw new Error(`Unknown panel domain: ${String(d)}`);
92239
+ return w;
92240
+ }
92241
+ function wireDomainToPanel(w) {
92242
+ const d = WIRE_TO_PANEL_DOMAIN[w];
92243
+ if (!d) throw new Error(`Unknown wire domain: ${String(w)}`);
92244
+ return d;
92245
+ }
92246
+ function panelCategoryToWire(c) {
92247
+ const w = PANEL_TO_WIRE_CATEGORY[c];
92248
+ if (!w) throw new Error(`Unknown panel category: ${String(c)}`);
92249
+ return w;
92250
+ }
92251
+ function wireCategoryToPanel(w) {
92252
+ const c = WIRE_TO_PANEL_CATEGORY[w];
92253
+ if (!c) throw new Error(`Unknown wire category: ${String(w)}`);
92254
+ return c;
92255
+ }
92256
+ function normalizePriceString(input) {
92257
+ if (typeof input.price === "string" && input.price.trim() !== "") {
92258
+ const s2 = input.price.trim();
92259
+ if (!(Number(s2) > 0)) throw new Error(`Price must be > 0 (got "${s2}").`);
92260
+ return s2;
92261
+ }
92262
+ const numeric = typeof input.price === "number" ? input.price : typeof input.pricePerKwh === "number" ? input.pricePerKwh : NaN;
92263
+ if (!Number.isFinite(numeric) || numeric <= 0) {
92264
+ throw new Error("A positive price (string) or legacy numeric pricePerKwh is required.");
92265
+ }
92266
+ let s = numeric.toFixed(6).replace(/0+$/, "");
92267
+ if (/\.\d?$/.test(s)) s = numeric.toFixed(2);
92268
+ return s;
92269
+ }
92270
+ function priceToNumber(price) {
92271
+ return Number(price);
92272
+ }
92273
+ function isLeapYear(year) {
92274
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
92275
+ }
92276
+ var MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
92277
+ function daysInMonthYear(year, month) {
92278
+ if (month === 2 && isLeapYear(year)) return 29;
92279
+ return MONTH_DAYS[month - 1];
92280
+ }
92281
+ function pad23(n) {
92282
+ return String(n).padStart(2, "0");
92283
+ }
92284
+ function assertValidCivilDate(date) {
92285
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
92286
+ if (!m) throw new Error(`Invalid date: ${date}`);
92287
+ const y = Number(m[1]);
92288
+ const mo = Number(m[2]);
92289
+ const d = Number(m[3]);
92290
+ if (mo < 1 || mo > 12) throw new Error(`Invalid month in date: ${date}`);
92291
+ if (d < 1 || d > daysInMonthYear(y, mo)) {
92292
+ throw new Error(`Invalid day for ${y}-${pad23(mo)} (leap=${isLeapYear(y)}): ${date}`);
92293
+ }
92294
+ }
92295
+ function* eachDate(start, end) {
92296
+ const [ys, ms, ds] = start.split("-").map(Number);
92297
+ const [ye, me, de] = end.split("-").map(Number);
92298
+ const t1 = Date.UTC(ye, me - 1, de);
92299
+ for (let t = Date.UTC(ys, ms - 1, ds); t <= t1; t += 864e5) {
92300
+ const dt = new Date(t);
92301
+ yield `${dt.getUTCFullYear()}-${pad23(dt.getUTCMonth() + 1)}-${pad23(dt.getUTCDate())}`;
92302
+ }
92303
+ }
92304
+ function normalizeBand(input) {
92305
+ const price = normalizePriceString(input);
92306
+ const band = {
92307
+ domain: input.domain,
92308
+ category: input.category,
92309
+ periodType: input.periodType,
92310
+ price
92311
+ };
92312
+ if (input.year != null) band.year = input.year;
92313
+ if (input.periodKey != null) band.periodKey = input.periodKey;
92314
+ if (input.start != null) band.start = input.start;
92315
+ if (input.end != null) band.end = input.end;
92316
+ if (input.date != null) band.date = input.date;
92317
+ if (input.startHour != null) band.startHour = input.startHour;
92318
+ if (input.endHour != null) band.endHour = input.endHour;
92319
+ return band;
92320
+ }
92321
+ function selectorKey(sel) {
92322
+ return `${sel.domain}|${sel.category}|${sel.year}`;
92323
+ }
92324
+ function bandSelector(band, customerId, year) {
92325
+ return {
92326
+ customerId,
92327
+ domain: panelDomainToWire(band.domain),
92328
+ category: panelCategoryToWire(band.category),
92329
+ year
92330
+ };
92331
+ }
92332
+ function panelEntryToBand(e) {
92333
+ const price = normalizePriceString({ price: e.price, pricePerKwh: e.pricePerKwh });
92334
+ if (e.periodType === "month") {
92335
+ if (!e.periodKey) throw new Error("month entry missing periodKey");
92336
+ return { domain: e.domain, category: e.category, periodType: "month", periodKey: e.periodKey, price };
92337
+ }
92338
+ if (!e.start || !e.end) throw new Error("range entry missing start/end");
92339
+ return { domain: e.domain, category: e.category, periodType: "range", start: e.start, end: e.end, price };
92340
+ }
92341
+ function bandToPanelEntry(band, customerId) {
92342
+ const base = {
92343
+ customerId,
92344
+ domain: band.domain,
92345
+ category: band.category,
92346
+ currency: "BRL",
92347
+ price: band.price,
92348
+ pricePerKwh: priceToNumber(band.price)
92349
+ };
92350
+ if (band.periodType === "month" && band.periodKey) {
92351
+ return { ...base, periodType: "month", periodKey: band.periodKey };
92352
+ }
92353
+ if (band.periodType === "year" && band.year != null) {
92354
+ return { ...base, periodType: "range", start: `${band.year}-01-01`, end: `${band.year}-12-31` };
92355
+ }
92356
+ if (band.periodType === "range" && band.start && band.end) {
92357
+ return { ...base, periodType: "range", start: band.start, end: band.end };
92358
+ }
92359
+ const date = band.date || band.start || "";
92360
+ return { ...base, periodType: "range", start: date, end: date };
92361
+ }
92362
+ function expandDayToHourBuckets(date, price) {
92363
+ assertValidCivilDate(date);
92364
+ const out = [];
92365
+ for (let h2 = 0; h2 < 24; h2++) out.push({ level: "HOUR", ref: `${date}T${pad23(h2)}`, price });
92366
+ return out;
92367
+ }
92368
+ function expandBandHours(date, startHour, endHour, price) {
92369
+ assertValidCivilDate(date);
92370
+ if (startHour < 0 || endHour > 23 || startHour > endHour) {
92371
+ throw new Error(`Invalid hour band ${startHour}-${endHour} for ${date}`);
92372
+ }
92373
+ const out = [];
92374
+ for (let h2 = startHour; h2 <= endHour; h2++) out.push({ level: "HOUR", ref: `${date}T${pad23(h2)}`, price });
92375
+ return out;
92376
+ }
92377
+ function bandDateBounds(band) {
92378
+ switch (band.periodType) {
92379
+ case "month": {
92380
+ if (!band.periodKey) throw new Error("month band missing periodKey");
92381
+ const [y, m] = band.periodKey.split("-").map(Number);
92382
+ return { start: `${band.periodKey}-01`, end: `${band.periodKey}-${pad23(daysInMonthYear(y, m))}` };
92383
+ }
92384
+ case "range": {
92385
+ if (!band.start || !band.end) throw new Error("range band missing start/end");
92386
+ return { start: band.start, end: band.end };
92387
+ }
92388
+ case "day":
92389
+ case "band": {
92390
+ const d = band.date || band.start;
92391
+ if (!d) throw new Error("day/band missing date");
92392
+ return { start: d, end: d };
92393
+ }
92394
+ case "year": {
92395
+ const y = band.year;
92396
+ if (y == null) throw new Error("year band missing year");
92397
+ return { start: `${y}-01-01`, end: `${y}-12-31` };
92398
+ }
92399
+ default:
92400
+ throw new Error(`Unsupported band periodType: ${String(band.periodType)}`);
92401
+ }
92402
+ }
92403
+ function expandBandToHourBucketsByYear(band) {
92404
+ const byYear = /* @__PURE__ */ new Map();
92405
+ const push = (year, buckets) => {
92406
+ const arr = byYear.get(year) || [];
92407
+ arr.push(...buckets);
92408
+ byYear.set(year, arr);
92409
+ };
92410
+ if (band.periodType === "band") {
92411
+ const date = band.date || band.start;
92412
+ push(Number(date.slice(0, 4)), expandBandHours(date, band.startHour ?? 0, band.endHour ?? 23, band.price));
92413
+ return byYear;
92414
+ }
92415
+ const { start, end } = bandDateBounds(band);
92416
+ for (const date of eachDate(start, end)) {
92417
+ push(Number(date.slice(0, 4)), expandDayToHourBuckets(date, band.price));
92418
+ }
92419
+ return byYear;
92420
+ }
92421
+ function collapseHoursToBands(date, hours, dims) {
92422
+ const sorted = [...hours].sort((a, b) => a.hour - b.hour);
92423
+ const runs = [];
92424
+ for (const h2 of sorted) {
92425
+ const cur = runs[runs.length - 1];
92426
+ if (cur && h2.hour === cur.endHour + 1 && h2.price === cur.price) {
92427
+ cur.endHour = h2.hour;
92428
+ } else {
92429
+ runs.push({ startHour: h2.hour, endHour: h2.hour, price: h2.price });
92430
+ }
92431
+ }
92432
+ return runs.map(
92433
+ (r) => r.startHour === 0 && r.endHour === 23 ? { domain: dims.domain, category: dims.category, periodType: "day", date, price: r.price } : {
92434
+ domain: dims.domain,
92435
+ category: dims.category,
92436
+ periodType: "band",
92437
+ date,
92438
+ startHour: r.startHour,
92439
+ endHour: r.endHour,
92440
+ price: r.price
92441
+ }
92442
+ );
92443
+ }
92444
+ function dayNodeToHours(node) {
92445
+ const dayPrice = typeof node.price === "string" ? node.price : void 0;
92446
+ const hourly = node.hourly || {};
92447
+ const hourKeys = Object.keys(hourly);
92448
+ if (hourKeys.length === 0) {
92449
+ return dayPrice != null ? Array.from({ length: 24 }, (_, h2) => ({ hour: h2, price: dayPrice })) : null;
92450
+ }
92451
+ const out = [];
92452
+ for (let h2 = 0; h2 < 24; h2++) {
92453
+ const ov = hourly[String(h2)];
92454
+ const price = ov && typeof ov.price === "string" ? ov.price : dayPrice;
92455
+ if (price != null) out.push({ hour: h2, price });
92456
+ }
92457
+ return out.length ? out : null;
92458
+ }
92459
+ function normalizeDayKey(key, monthKey) {
92460
+ if (/^\d{2}-\d{2}$/.test(key)) return key;
92461
+ if (/^\d{2}$/.test(key) && monthKey) return `${monthKey}-${key}`;
92462
+ return key;
92463
+ }
92464
+ function mergeContiguousDays(days, year) {
92465
+ const simple = days.every((d) => d.periodType === "day" && d.date);
92466
+ if (!simple || days.length === 0) return days;
92467
+ const sorted = [...days].sort((a, b) => a.date < b.date ? -1 : 1);
92468
+ const out = [];
92469
+ let run = null;
92470
+ const flush = () => {
92471
+ if (!run) return;
92472
+ out.push(
92473
+ run.start === run.end ? { domain: run.dims.domain, category: run.dims.category, periodType: "day", date: run.start, price: run.price } : {
92474
+ domain: run.dims.domain,
92475
+ category: run.dims.category,
92476
+ periodType: "range",
92477
+ start: run.start,
92478
+ end: run.end,
92479
+ price: run.price
92480
+ }
92481
+ );
92482
+ run = null;
92483
+ };
92484
+ for (const d of sorted) {
92485
+ if (run && d.price === run.price && isNextDay(run.end, d.date)) {
92486
+ run.end = d.date;
92487
+ } else {
92488
+ flush();
92489
+ run = { start: d.date, end: d.date, price: d.price, dims: d };
92490
+ }
92491
+ }
92492
+ flush();
92493
+ void year;
92494
+ return out;
92495
+ }
92496
+ function isNextDay(prev, next) {
92497
+ const [py, pm, pd] = prev.split("-").map(Number);
92498
+ const [ny, nm, nd] = next.split("-").map(Number);
92499
+ return Date.UTC(py, pm - 1, pd) + 864e5 === Date.UTC(ny, nm - 1, nd);
92500
+ }
92501
+ function collapseTreeToBands(tree, dims) {
92502
+ if (!tree || typeof tree !== "object") return [];
92503
+ const dimOnly = { domain: dims.domain, category: dims.category };
92504
+ if (tree.annual && typeof tree.annual.price === "string" && !tree.daily && !tree.monthly) {
92505
+ return [{ ...dimOnly, periodType: "year", year: dims.year, price: tree.annual.price }];
92506
+ }
92507
+ const dayBands = [];
92508
+ const collectDaily = (daily, monthKey) => {
92509
+ for (const rawKey of Object.keys(daily)) {
92510
+ const mmdd = normalizeDayKey(rawKey, monthKey);
92511
+ const date = `${dims.year}-${mmdd}`;
92512
+ const hours = dayNodeToHours(daily[rawKey]);
92513
+ if (!hours) continue;
92514
+ dayBands.push(...collapseHoursToBands(date, hours, dimOnly));
92515
+ }
92516
+ };
92517
+ if (tree.daily) collectDaily(tree.daily);
92518
+ if (tree.monthly) {
92519
+ for (const mm of Object.keys(tree.monthly)) {
92520
+ const node = tree.monthly[mm];
92521
+ if (node.daily) {
92522
+ collectDaily(node.daily, mm);
92523
+ } else if (typeof node.price === "string") {
92524
+ const last = daysInMonthYear(dims.year, Number(mm));
92525
+ for (let d = 1; d <= last; d++) {
92526
+ dayBands.push({ ...dimOnly, periodType: "day", date: `${dims.year}-${mm}-${pad23(d)}`, price: node.price });
92527
+ }
92528
+ }
92529
+ }
92530
+ }
92531
+ return mergeContiguousDays(dayBands, dims.year);
92532
+ }
92533
+ var WIRE_COMBOS = [
92534
+ ["ENERGY", "SPECIFIC"],
92535
+ ["ENERGY", "COMMON_AREA"],
92536
+ ["WATER", "SPECIFIC"],
92537
+ ["WATER", "COMMON_AREA"]
92538
+ ];
92539
+ var TariffApiAdapter = class {
92540
+ client;
92541
+ year;
92542
+ /** Last-seen version per `(domain|category|year)` for optimistic concurrency. */
92543
+ versions = /* @__PURE__ */ new Map();
92544
+ constructor(client, opts = {}) {
92545
+ this.client = client;
92546
+ this.year = opts.year ?? (/* @__PURE__ */ new Date()).getFullYear();
92547
+ }
92548
+ /** Default load year. */
92549
+ getYear() {
92550
+ return this.year;
92551
+ }
92552
+ /**
92553
+ * Load every `(domain × category)` tariff for a customer and collapse each into
92554
+ * panel entries. An empty tariff (`version: 0`) contributes no entries — it is
92555
+ * "no entries", never an error. Read errors propagate (caller surfaces them).
92556
+ */
92557
+ async loadEntriesForCustomer(customerId, year = this.year) {
92558
+ const out = [];
92559
+ for (const [domain, category] of WIRE_COMBOS) {
92560
+ const sel = { customerId, domain, category, year };
92561
+ const resp = await this.client.getTariff(sel, "hour");
92562
+ this.versions.set(selectorKey(sel), resp.version);
92563
+ if (resp.version === 0) continue;
92564
+ const bands = collapseTreeToBands(resp.tree, {
92565
+ year,
92566
+ domain: wireDomainToPanel(domain),
92567
+ category: wireCategoryToPanel(category)
92568
+ });
92569
+ for (const b of bands) out.push(bandToPanelEntry(b, customerId));
92570
+ }
92571
+ return out;
92572
+ }
92573
+ /** Convenience: read + collapse one specific tariff. */
92574
+ async loadBands(customerId, domain, category, year = this.year) {
92575
+ const sel = { customerId, domain: panelDomainToWire(domain), category: panelCategoryToWire(category), year };
92576
+ const resp = await this.client.getTariff(sel, "hour");
92577
+ this.versions.set(selectorKey(sel), resp.version);
92578
+ const bands = resp.version === 0 ? [] : collapseTreeToBands(resp.tree, { year, domain, category });
92579
+ return { bands, version: resp.version };
92580
+ }
92581
+ /**
92582
+ * Persist one panel edit. Annual bands REPLACE the year (PUT); every other
92583
+ * period MERGES its hourly buckets (PATCH), so sibling periods survive. A range
92584
+ * spanning a year boundary writes once per year. `expectedVersion` is carried
92585
+ * from the last-seen version; a stale guard throws `TARIFF_VERSION_CONFLICT`
92586
+ * (surfaced, never swallowed).
92587
+ */
92588
+ async saveEntry(customerId, entryOrBand) {
92589
+ const band = toBand(entryOrBand);
92590
+ if (band.periodType === "year") {
92591
+ const year = band.year ?? this.year;
92592
+ const sel = bandSelector(band, customerId, year);
92593
+ const key = selectorKey(sel);
92594
+ const res = await this.client.putTariff(sel, { annual: { price: band.price } }, this.versions.get(key));
92595
+ this.versions.set(key, res.version);
92596
+ return;
92597
+ }
92598
+ const byYear = expandBandToHourBucketsByYear(band);
92599
+ for (const [year, buckets] of byYear) {
92600
+ const sel = bandSelector(band, customerId, year);
92601
+ const key = selectorKey(sel);
92602
+ const res = await this.client.patchTariff(sel, buckets, this.versions.get(key));
92603
+ this.versions.set(key, res.version);
92604
+ }
92605
+ }
92606
+ /**
92607
+ * Remove a panel period. The contract offers whole-year DELETE or a single
92608
+ * sub-bucket per call, so a partial period is deleted day-by-day (`level:'DAY'`).
92609
+ * Removing a full year deletes the whole year in one call.
92610
+ */
92611
+ async deleteEntry(customerId, entryOrBand) {
92612
+ const band = toBand(entryOrBand);
92613
+ if (band.periodType === "year") {
92614
+ const year = band.year ?? this.year;
92615
+ const sel = bandSelector(band, customerId, year);
92616
+ const key = selectorKey(sel);
92617
+ const res = await this.client.deleteTariff(sel, { expectedVersion: this.versions.get(key) });
92618
+ if (typeof res.version === "number") this.versions.set(key, res.version);
92619
+ return;
92620
+ }
92621
+ const { start, end } = bandDateBounds(band);
92622
+ for (const date of eachDate(start, end)) {
92623
+ const year = Number(date.slice(0, 4));
92624
+ const sel = bandSelector(band, customerId, year);
92625
+ const key = selectorKey(sel);
92626
+ const res = await this.client.deleteTariff(sel, {
92627
+ bucket: { level: "DAY", ref: date },
92628
+ expectedVersion: this.versions.get(key)
92629
+ });
92630
+ if (typeof res.version === "number") this.versions.set(key, res.version);
92631
+ }
92632
+ }
92633
+ /** Seed the version cache from an out-of-band read (e.g. a raw client call). */
92634
+ rememberVersion(resp) {
92635
+ this.versions.set(`${resp.domain}|${resp.category}|${resp.year}`, resp.version);
92636
+ }
92637
+ };
92638
+ function toBand(entryOrBand) {
92639
+ if ("periodType" in entryOrBand && (entryOrBand.periodType === "month" || entryOrBand.periodType === "range") && !("startHour" in entryOrBand)) {
92640
+ if ("customerId" in entryOrBand) return panelEntryToBand(entryOrBand);
92641
+ }
92642
+ return normalizeBand(entryOrBand);
92643
+ }
92644
+ function createTariffApiAdapter(client, opts) {
92645
+ return new TariffApiAdapter(client, opts);
92646
+ }
92647
+
91149
92648
  // src/components/pricing-panel/openPricingPanel.ts
91150
92649
  var MODAL_ID2 = "myio-pricing-panel";
91151
92650
  function escapeHtml8(value) {
@@ -91226,8 +92725,20 @@ function openPricingPanel(params) {
91226
92725
  doc.getElementById(MODAL_ID2)?.remove();
91227
92726
  const allowed = isPricingAllowed(params.currentUserEmail, isSuperAdmin());
91228
92727
  const customers = params.customers || [];
91229
- let entries = allowed ? mergeEntries(loadJson(doc, keyEntries, []), params.initialEntries || [], author) : [];
91230
- let auditLog = allowed ? loadJson(doc, keyAudit, []) : [];
92728
+ const tariffApi = params.tariffApi;
92729
+ let adapter = null;
92730
+ if (allowed && tariffApi) {
92731
+ const client = new TariffApiClient({
92732
+ baseUrl: tariffApi.baseUrl,
92733
+ apiKey: tariffApi.apiKey,
92734
+ jwt: tariffApi.jwt,
92735
+ tenantId: tariffApi.tenantId,
92736
+ fetchImpl: tariffApi.fetchImpl
92737
+ });
92738
+ adapter = new TariffApiAdapter(client, { year: tariffApi.year });
92739
+ }
92740
+ let entries = allowed ? adapter ? (params.initialEntries || []).map((e) => ({ ...e })) : mergeEntries(loadJson(doc, keyEntries, []), params.initialEntries || [], author) : [];
92741
+ let auditLog = allowed && !adapter ? loadJson(doc, keyAudit, []) : [];
91231
92742
  const modal = doc.createElement("div");
91232
92743
  modal.id = MODAL_ID2;
91233
92744
  modal.className = "myio-pricing";
@@ -91457,9 +92968,47 @@ function openPricingPanel(params) {
91457
92968
  renderList2();
91458
92969
  })
91459
92970
  );
92971
+ if (adapter) {
92972
+ customerSel.addEventListener("change", () => {
92973
+ void loadFromApi();
92974
+ });
92975
+ }
91460
92976
  syncPeriodFields();
91461
92977
  syncUnit();
91462
- const persist = () => {
92978
+ function surfaceApiError(err) {
92979
+ const code = err?.code;
92980
+ if (code === "TARIFF_VERSION_CONFLICT") {
92981
+ const cv = err.currentVersion;
92982
+ showError(
92983
+ `Conflito de vers\xE3o: a tarifa mudou no servidor${cv != null ? ` (vers\xE3o atual ${cv})` : ""}. Reabra o painel e tente novamente.`
92984
+ );
92985
+ return;
92986
+ }
92987
+ showError(`Falha ao salvar a tarifa no servidor${code ? ` (${code})` : ""}.`);
92988
+ }
92989
+ async function loadFromApi() {
92990
+ if (!adapter) return;
92991
+ const customerId = customerSel.value;
92992
+ if (!customerId) return;
92993
+ try {
92994
+ const loaded = await adapter.loadEntriesForCustomer(customerId);
92995
+ entries = entries.filter((e) => e.customerId !== customerId).concat(loaded);
92996
+ renderList2();
92997
+ } catch (err) {
92998
+ surfaceApiError(err);
92999
+ }
93000
+ }
93001
+ const persist = (changed) => {
93002
+ if (adapter) {
93003
+ if (changed?.kind === "upsert") {
93004
+ adapter.saveEntry(changed.entry.customerId, changed.entry).catch(surfaceApiError);
93005
+ } else if (changed?.kind === "remove") {
93006
+ adapter.deleteEntry(changed.entry.customerId, changed.entry).catch(surfaceApiError);
93007
+ }
93008
+ if (typeof params.onSave === "function") params.onSave(entries.map((e) => ({ ...e })));
93009
+ emit("save", entries.map((e) => ({ ...e })));
93010
+ return;
93011
+ }
91463
93012
  saveJson(doc, keyEntries, entries);
91464
93013
  saveJson(doc, keyAudit, auditLog);
91465
93014
  if (typeof params.onSave === "function") params.onSave(entries.map((e) => ({ ...e })));
@@ -91486,7 +93035,7 @@ function openPricingPanel(params) {
91486
93035
  <span class="myio-pricing__item-period">${badge(e.domain, e.category)} ${escapeHtml8(
91487
93036
  formatPeriodLabel(e)
91488
93037
  )}</span>
91489
- <span class="myio-pricing__item-price">${escapeHtml8(formatBRL(e.pricePerKwh))} ${escapeHtml8(
93038
+ <span class="myio-pricing__item-price">${escapeHtml8(formatBRL2(e.pricePerKwh))} ${escapeHtml8(
91490
93039
  unitLabel(e.domain)
91491
93040
  )}</span>
91492
93041
  <button class="myio-pricing__remove" data-remove="${escapeHtml8(e.domain)}|${escapeHtml8(
@@ -91498,6 +93047,11 @@ function openPricingPanel(params) {
91498
93047
  listEl.querySelectorAll("[data-remove]").forEach((btn) => {
91499
93048
  btn.addEventListener("click", () => {
91500
93049
  const [domain, category, boundsKey] = String(btn.getAttribute("data-remove")).split("|");
93050
+ const removedEntry = entries.find((e) => {
93051
+ if (e.customerId !== customerId || e.domain !== domain || e.category !== category) return false;
93052
+ const b = entryBounds(e);
93053
+ return Boolean(b && `${b.start}__${b.end}` === boundsKey);
93054
+ });
91501
93055
  const result = removeEntryByBounds(
91502
93056
  entries,
91503
93057
  {
@@ -91510,7 +93064,7 @@ function openPricingPanel(params) {
91510
93064
  );
91511
93065
  entries = result.entries;
91512
93066
  pushAudit(result.audit);
91513
- persist();
93067
+ persist(removedEntry ? { kind: "remove", entry: removedEntry } : void 0);
91514
93068
  renderList2();
91515
93069
  emit("remove", result.audit);
91516
93070
  });
@@ -91553,7 +93107,7 @@ function openPricingPanel(params) {
91553
93107
  }
91554
93108
  entries = result.entries;
91555
93109
  pushAudit(result.audit);
91556
- persist();
93110
+ persist({ kind: "upsert", entry: candidate });
91557
93111
  customerSel.value = candidate.customerId;
91558
93112
  domainSel.value = candidate.domain;
91559
93113
  categorySel.value = candidate.category;
@@ -91569,11 +93123,11 @@ function openPricingPanel(params) {
91569
93123
  { value: String(kpis.periodsByDomain.energy), label: "Per\xEDodos \xB7 Energia" },
91570
93124
  { value: String(kpis.periodsByDomain.water), label: "Per\xEDodos \xB7 \xC1gua" },
91571
93125
  {
91572
- value: kpis.avgEnergyPrice != null ? `${formatBRL(kpis.avgEnergyPrice)}` : "\u2014",
93126
+ value: kpis.avgEnergyPrice != null ? `${formatBRL2(kpis.avgEnergyPrice)}` : "\u2014",
91573
93127
  label: "Pre\xE7o m\xE9dio R$/kWh"
91574
93128
  },
91575
93129
  {
91576
- value: kpis.avgWaterPrice != null ? `${formatBRL(kpis.avgWaterPrice)}` : "\u2014",
93130
+ value: kpis.avgWaterPrice != null ? `${formatBRL2(kpis.avgWaterPrice)}` : "\u2014",
91577
93131
  label: "Pre\xE7o m\xE9dio R$/m\xB3"
91578
93132
  },
91579
93133
  {
@@ -91609,13 +93163,13 @@ function openPricingPanel(params) {
91609
93163
  <td>${escapeHtml8(DOMAIN_LABEL[e.domain])}</td>
91610
93164
  <td>${escapeHtml8(CATEGORY_LABEL[e.category])}</td>
91611
93165
  <td>${escapeHtml8(formatPeriodLabel(e))}</td>
91612
- <td>${escapeHtml8(formatBRL(e.pricePerKwh))} ${escapeHtml8(unitLabel(e.domain))}</td>
93166
+ <td>${escapeHtml8(formatBRL2(e.pricePerKwh))} ${escapeHtml8(unitLabel(e.domain))}</td>
91613
93167
  </tr>`
91614
93168
  ).join("") : `<tr><td colspan="5" style="text-align:center;color:var(--myio-text-muted,#6b7280);padding:24px;">Nenhum pre\xE7o para o filtro selecionado.</td></tr>`;
91615
93169
  }
91616
93170
  function renderHistory() {
91617
93171
  historyList.innerHTML = auditLog.map((rec) => {
91618
- const priceInfo = rec.action === "price-changed" ? ` \u2014 ${formatBRL(rec.before?.pricePerKwh ?? NaN)} \u2192 ${formatBRL(rec.after?.pricePerKwh ?? NaN)}` : rec.after?.pricePerKwh != null ? ` \u2014 ${formatBRL(rec.after.pricePerKwh)}` : rec.before?.pricePerKwh != null ? ` \u2014 ${formatBRL(rec.before.pricePerKwh)}` : "";
93172
+ const priceInfo = rec.action === "price-changed" ? ` \u2014 ${formatBRL2(rec.before?.pricePerKwh ?? NaN)} \u2192 ${formatBRL2(rec.after?.pricePerKwh ?? NaN)}` : rec.after?.pricePerKwh != null ? ` \u2014 ${formatBRL2(rec.after.pricePerKwh)}` : rec.before?.pricePerKwh != null ? ` \u2014 ${formatBRL2(rec.before.pricePerKwh)}` : "";
91619
93173
  return `
91620
93174
  <li class="myio-pricing__history-item">
91621
93175
  <div class="myio-pricing__history-head">
@@ -91633,9 +93187,1062 @@ function openPricingPanel(params) {
91633
93187
  historyEmpty.style.display = auditLog.length ? "none" : "";
91634
93188
  }
91635
93189
  renderList2();
93190
+ if (adapter) void loadFromApi();
91636
93191
  return handle;
91637
93192
  }
91638
93193
 
93194
+ // src/components/financial-goals/goalsMoneyClient.ts
93195
+ var GoalsMoneyApiError = class extends Error {
93196
+ code;
93197
+ status;
93198
+ details;
93199
+ currentVersion;
93200
+ constructor(code, status, message, details) {
93201
+ super(message || code);
93202
+ this.name = "GoalsMoneyApiError";
93203
+ this.code = code;
93204
+ this.status = status;
93205
+ this.details = details;
93206
+ const cv = details && details.currentVersion;
93207
+ this.currentVersion = typeof cv === "number" ? cv : void 0;
93208
+ }
93209
+ };
93210
+ function joinUrl2(baseUrl, basePath, path) {
93211
+ const b = baseUrl.replace(/\/+$/, "");
93212
+ const bp = basePath ? `/${basePath.replace(/^\/+|\/+$/g, "")}` : "";
93213
+ const p = path.startsWith("/") ? path : `/${path}`;
93214
+ return `${b}${bp}${p}`;
93215
+ }
93216
+ function parseETagVersion2(headers) {
93217
+ const raw = headers.get("ETag") || headers.get("etag");
93218
+ if (!raw) return void 0;
93219
+ const m = /"?(-?\d+)"?/.exec(raw);
93220
+ return m ? Number(m[1]) : void 0;
93221
+ }
93222
+ function guardBody2(expectedVersion) {
93223
+ return typeof expectedVersion === "number" ? { expectedVersion } : {};
93224
+ }
93225
+ var GoalsMoneyClient = class {
93226
+ cfg;
93227
+ fetchImpl;
93228
+ basePath;
93229
+ constructor(config2) {
93230
+ if (!config2 || !config2.baseUrl) {
93231
+ throw new Error("GoalsMoneyClient requires a baseUrl.");
93232
+ }
93233
+ this.cfg = config2;
93234
+ const injected = config2.fetchImpl || globalThis.fetch;
93235
+ if (typeof injected !== "function") {
93236
+ throw new Error("GoalsMoneyClient requires a fetch implementation.");
93237
+ }
93238
+ this.fetchImpl = config2.fetchImpl ? config2.fetchImpl : injected.bind(globalThis);
93239
+ this.basePath = config2.basePath ?? "api/v1";
93240
+ }
93241
+ authHeaders() {
93242
+ const h2 = {};
93243
+ if (this.cfg.apiKey) h2["X-API-Key"] = this.cfg.apiKey;
93244
+ if (this.cfg.jwt) h2["Authorization"] = `Bearer ${this.cfg.jwt}`;
93245
+ if (this.cfg.tenantId) h2["X-Tenant-Id"] = this.cfg.tenantId;
93246
+ return h2;
93247
+ }
93248
+ goalsPath(sel) {
93249
+ return `/customers/${encodeURIComponent(sel.customerId)}/goals`;
93250
+ }
93251
+ /**
93252
+ * Read a quantity goal **in money** (`?withMoney=true`). Returns the quantity
93253
+ * tree (values are quantities, `monetaryValue`s are decimal strings), plus the
93254
+ * normalized money overlay (`available` | `unavailable`) and, when a CURRENCY
93255
+ * goal exists, the folded budget verdict. Coverage states are NOT thrown.
93256
+ */
93257
+ async getGoalWithMoney(args) {
93258
+ const qs = new URLSearchParams();
93259
+ qs.set("domain", args.domain);
93260
+ qs.set("year", String(args.year));
93261
+ if (args.granularity) qs.set("granularity", args.granularity);
93262
+ qs.set("withMoney", "true");
93263
+ const url = joinUrl2(this.cfg.baseUrl, this.basePath, `${this.goalsPath(args)}?${qs.toString()}`);
93264
+ const res = await this.fetchImpl(url, {
93265
+ method: "GET",
93266
+ headers: { Accept: "application/json", ...this.authHeaders() }
93267
+ });
93268
+ const body = await this.readBody(res);
93269
+ if (!res.ok) throw this.toError(res, body);
93270
+ const etagVersion = parseETagVersion2(res.headers);
93271
+ const parsed = body || {};
93272
+ const version2 = typeof parsed.version === "number" ? parsed.version : etagVersion ?? 0;
93273
+ const measure = parsed.measure === "CURRENCY" ? "CURRENCY" : "QUANTITY";
93274
+ return {
93275
+ customerId: args.customerId,
93276
+ domain: args.domain,
93277
+ year: args.year,
93278
+ measure,
93279
+ version: version2,
93280
+ goal: {
93281
+ domain: args.domain,
93282
+ unit: typeof parsed.unit === "string" ? parsed.unit : "",
93283
+ // Tree is passed through verbatim — amounts/monetaryValues stay strings.
93284
+ tree: parsed.tree || {},
93285
+ version: version2
93286
+ },
93287
+ money: normalizeMoneyBlock(parsed.money, parsed.budget)
93288
+ };
93289
+ }
93290
+ /** Read the native CURRENCY budget goal (R$ target tree). */
93291
+ async getBudget(sel) {
93292
+ const qs = new URLSearchParams();
93293
+ qs.set("domain", sel.domain);
93294
+ qs.set("year", String(sel.year));
93295
+ qs.set("measure", "CURRENCY");
93296
+ const url = joinUrl2(this.cfg.baseUrl, this.basePath, `${this.goalsPath(sel)}?${qs.toString()}`);
93297
+ const res = await this.fetchImpl(url, {
93298
+ method: "GET",
93299
+ headers: { Accept: "application/json", ...this.authHeaders() }
93300
+ });
93301
+ const body = await this.readBody(res);
93302
+ if (!res.ok) throw this.toError(res, body);
93303
+ const etagVersion = parseETagVersion2(res.headers);
93304
+ const parsed = body || {};
93305
+ return {
93306
+ customerId: sel.customerId,
93307
+ domain: sel.domain,
93308
+ year: sel.year,
93309
+ measure: "CURRENCY",
93310
+ currency: parsed.currency,
93311
+ version: typeof parsed.version === "number" ? parsed.version : etagVersion ?? 0,
93312
+ tree: parsed.tree || {}
93313
+ };
93314
+ }
93315
+ /**
93316
+ * Set the native CURRENCY budget (PUT replace). The `tree` is sent **verbatim**
93317
+ * (`{ annual: { value: "120000.00" } }` or `{ monthly: {…} }`) — the client
93318
+ * does not coerce amounts, keeping the decimal-string contract end to end.
93319
+ * The optimistic guard rides both `If-Match` and body `expectedVersion`.
93320
+ */
93321
+ async putBudget(args) {
93322
+ const qs = new URLSearchParams();
93323
+ qs.set("domain", args.domain);
93324
+ qs.set("year", String(args.year));
93325
+ qs.set("measure", "CURRENCY");
93326
+ const url = joinUrl2(this.cfg.baseUrl, this.basePath, `${this.goalsPath(args)}?${qs.toString()}`);
93327
+ const headers = {
93328
+ Accept: "application/json",
93329
+ "Content-Type": "application/json",
93330
+ ...this.authHeaders()
93331
+ };
93332
+ if (typeof args.expectedVersion === "number") {
93333
+ headers["If-Match"] = `"${args.expectedVersion}"`;
93334
+ }
93335
+ const res = await this.fetchImpl(url, {
93336
+ method: "PUT",
93337
+ headers,
93338
+ body: JSON.stringify({ ...args.tree, ...guardBody2(args.expectedVersion) })
93339
+ });
93340
+ const parsed = await this.readBody(res);
93341
+ if (!res.ok) throw this.toError(res, parsed);
93342
+ const etagVersion = parseETagVersion2(res.headers);
93343
+ const v = (parsed && parsed.version) ?? etagVersion;
93344
+ return { version: typeof v === "number" ? v : (args.expectedVersion ?? 0) + 1 };
93345
+ }
93346
+ /**
93347
+ * Delete the native CURRENCY budget. Whole-year delete is idempotent (204).
93348
+ * The optimistic guard rides both `If-Match` and body `expectedVersion`.
93349
+ */
93350
+ async deleteBudget(args) {
93351
+ const qs = new URLSearchParams();
93352
+ qs.set("domain", args.domain);
93353
+ qs.set("year", String(args.year));
93354
+ qs.set("measure", "CURRENCY");
93355
+ const url = joinUrl2(this.cfg.baseUrl, this.basePath, `${this.goalsPath(args)}?${qs.toString()}`);
93356
+ const headers = { Accept: "application/json", ...this.authHeaders() };
93357
+ const init = { method: "DELETE", headers };
93358
+ if (typeof args.expectedVersion === "number") {
93359
+ headers["If-Match"] = `"${args.expectedVersion}"`;
93360
+ headers["Content-Type"] = "application/json";
93361
+ init.body = JSON.stringify(guardBody2(args.expectedVersion));
93362
+ }
93363
+ const res = await this.fetchImpl(url, init);
93364
+ if (res.status === 204) return {};
93365
+ const body = await this.readBody(res);
93366
+ if (!res.ok) throw this.toError(res, body);
93367
+ const etagVersion = parseETagVersion2(res.headers);
93368
+ const v = (body && body.version) ?? etagVersion;
93369
+ return typeof v === "number" ? { version: v } : {};
93370
+ }
93371
+ async readBody(res) {
93372
+ try {
93373
+ const text2 = await res.text();
93374
+ if (!text2) return null;
93375
+ return JSON.parse(text2);
93376
+ } catch {
93377
+ return null;
93378
+ }
93379
+ }
93380
+ toError(res, body) {
93381
+ const err = body && body.error;
93382
+ if (err && typeof err === "object") {
93383
+ const e = err;
93384
+ return new GoalsMoneyApiError(e.code || `HTTP_${res.status}`, res.status, e.message, e.details);
93385
+ }
93386
+ return new GoalsMoneyApiError(`HTTP_${res.status}`, res.status, res.statusText);
93387
+ }
93388
+ };
93389
+ function createGoalsMoneyClient(config2) {
93390
+ return new GoalsMoneyClient(config2);
93391
+ }
93392
+
93393
+ // src/components/financial-goals/budgetView.ts
93394
+ var DEFAULT_VALUE_COLORS2 = {
93395
+ target: "#7c3aed",
93396
+ projected: "#2563eb"
93397
+ };
93398
+ var DEFAULT_CHIP3 = {
93399
+ overBg: "#fee2e2",
93400
+ overText: "#b91c1c",
93401
+ withinBg: "#dcfce7",
93402
+ withinText: "#15803d",
93403
+ neutralBg: "#f1f5f9",
93404
+ neutralText: "#64748b"
93405
+ };
93406
+ var CHIP_BASE3 = "border-radius:999px;padding:3px 12px;font:800 12px Nunito,sans-serif;white-space:nowrap;display:inline-flex;align-items:center;gap:6px;";
93407
+ function hasAmount4(s) {
93408
+ return typeof s === "string" && s.trim() !== "";
93409
+ }
93410
+ function resolveBudget(overlay) {
93411
+ if (!overlay || typeof overlay.state !== "string") {
93412
+ return void 0;
93413
+ }
93414
+ if (overlay.state !== "available") return void 0;
93415
+ return overlay.budget;
93416
+ }
93417
+ function figureHTML2(label, amount, color) {
93418
+ return `<span style="display:inline-flex;align-items:center;gap:4px;white-space:nowrap;"><span style="color:${color};font-weight:700;">${label}</span><b style="color:var(--gc-text2, #334155);font-weight:800;">${formatBRL(amount ?? null)}</b></span>`;
93419
+ }
93420
+ function buildVerdictHTML(budget, chipColors) {
93421
+ const withinBudget = budget.verdict?.withinBudget;
93422
+ const varianceR$ = hasAmount4(budget.verdict?.variance) ? formatBRLDelta(budget.verdict.variance) : DASH;
93423
+ const pct = computeDeltaPct(
93424
+ budget.projected?.amount ?? null,
93425
+ budget.target?.amount ?? null
93426
+ );
93427
+ const pctTxt = pct == null ? DASH : formatDeltaPct(pct);
93428
+ const varianceLabel = varianceR$ === DASH && pctTxt === DASH ? "" : `<span class="myio-budget__variance" style="font-weight:700;margin-left:8px;color:var(--gc-muted, #64748b);">${varianceR$} \xB7 ${pctTxt}</span>`;
93429
+ if (withinBudget === true) {
93430
+ return `<span data-budget-verdict="within" style="background:${chipColors.withinBg};color:${chipColors.withinText};${CHIP_BASE3}"><span aria-hidden="true">\u2713</span>Dentro do or\xE7amento</span>${varianceLabel}`;
93431
+ }
93432
+ if (withinBudget === false) {
93433
+ return `<span data-budget-verdict="over" style="background:${chipColors.overBg};color:${chipColors.overText};${CHIP_BASE3}"><span aria-hidden="true">\u25B2</span>Acima do or\xE7amento</span>${varianceLabel}`;
93434
+ }
93435
+ return `<span data-budget-verdict="withheld" style="background:${chipColors.neutralBg};color:${chipColors.neutralText};${CHIP_BASE3}"><span aria-hidden="true">\u23F3</span>Veredito indispon\xEDvel \u2014 cobertura incompleta</span>`;
93436
+ }
93437
+ function buildBudgetHTML(budget, chipColors = DEFAULT_CHIP3, valueColors = DEFAULT_VALUE_COLORS2) {
93438
+ const figures = `<div style="font:600 12px Nunito,sans-serif;color:var(--gc-muted, #64748b);display:flex;align-items:center;gap:14px;flex-wrap:wrap;min-width:0;"><span aria-hidden="true">\u{1F3AF}</span>` + figureHTML2("Meta (or\xE7amento)", budget.target?.amount, valueColors.target) + figureHTML2("Proje\xE7\xE3o", budget.projected?.amount, valueColors.projected) + `</div>`;
93439
+ return `<div class="myio-budget__row" data-budget-row="1" style="display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;">` + figures + `<span class="myio-budget__verdict-wrap" style="display:inline-flex;align-items:center;">` + buildVerdictHTML(budget, chipColors) + `</span></div><div class="myio-budget__note" style="font:600 10px Nunito,sans-serif;color:var(--gc-muted, #94a3b8);margin-top:4px;">Or\xE7amento nativo em R$ \xB7 Proje\xE7\xE3o estimada</div>`;
93440
+ }
93441
+ function emptyStateHTML() {
93442
+ return `<div class="myio-budget__empty" role="status" style="font:600 12px Nunito,sans-serif;color:var(--gc-muted, #94a3b8);display:flex;align-items:center;gap:6px;"><span aria-hidden="true">\u{1F4B8}</span>Nenhum or\xE7amento em R$ definido.</div>`;
93443
+ }
93444
+ function renderBudgetView(options) {
93445
+ const overlay = options?.overlay;
93446
+ if (!overlay || typeof overlay.state !== "string") {
93447
+ return null;
93448
+ }
93449
+ const doc = options.document || (typeof document !== "undefined" ? document : void 0);
93450
+ if (!doc) {
93451
+ throw new Error(
93452
+ "renderBudgetView requires a document (pass options.document in non-DOM envs)."
93453
+ );
93454
+ }
93455
+ const budget = resolveBudget(overlay);
93456
+ const root = doc.createElement("div");
93457
+ root.className = "myio-budget";
93458
+ if (!budget) {
93459
+ root.setAttribute("data-budget-state", "empty");
93460
+ root.innerHTML = emptyStateHTML();
93461
+ return root;
93462
+ }
93463
+ const chipColors = { ...DEFAULT_CHIP3, ...options.chipColors };
93464
+ const valueColors = {
93465
+ ...DEFAULT_VALUE_COLORS2,
93466
+ ...options.valueColors
93467
+ };
93468
+ root.setAttribute("data-budget-state", "budget");
93469
+ root.innerHTML = buildBudgetHTML(budget, chipColors, valueColors);
93470
+ const withheld = budget.verdict?.withinBudget !== true && budget.verdict?.withinBudget !== false;
93471
+ if (withheld && overlay.state === "available" && overlay.coverageComplete !== true) {
93472
+ const coverageOpts = {
93473
+ onCategorizeDevice: options.onCategorizeDevice,
93474
+ onManageCategories: options.onManageCategories,
93475
+ document: doc
93476
+ };
93477
+ const why = renderCoverageView(overlay, coverageOpts);
93478
+ why.setAttribute("data-budget-why", "1");
93479
+ root.appendChild(why);
93480
+ }
93481
+ if (typeof options.onSaveBudget === "function") {
93482
+ const cb = options.onSaveBudget;
93483
+ const btn = doc.createElement("button");
93484
+ btn.type = "button";
93485
+ btn.className = "myio-budget__edit";
93486
+ btn.setAttribute("data-budget-edit", "1");
93487
+ btn.textContent = "Editar or\xE7amento";
93488
+ btn.setAttribute(
93489
+ "style",
93490
+ "align-self:flex-start;margin-top:8px;padding:6px 12px;border:1px solid #7c3aed;border-radius:8px;background:transparent;color:#7c3aed;font:800 12px Nunito,sans-serif;cursor:pointer;"
93491
+ );
93492
+ btn.addEventListener("click", (ev) => {
93493
+ ev.preventDefault();
93494
+ ev.stopPropagation();
93495
+ const tree = hasAmount4(budget.target?.amount) ? { annual: { value: Number(budget.target.amount) } } : {};
93496
+ cb(tree);
93497
+ });
93498
+ root.appendChild(btn);
93499
+ }
93500
+ return root;
93501
+ }
93502
+
93503
+ // src/components/financial-goals/deviceCategoryPort.ts
93504
+ var DeviceCategoryConflictError = class extends Error {
93505
+ deviceId;
93506
+ code = "DEVICE_CATEGORY_VERSION_CONFLICT";
93507
+ constructor(deviceId, message) {
93508
+ super(message || `Version conflict for device ${deviceId}`);
93509
+ this.name = "DeviceCategoryConflictError";
93510
+ this.deviceId = deviceId;
93511
+ }
93512
+ };
93513
+ function createFakeDeviceCategoryPort(seed = [], options = {}) {
93514
+ const supportsBulk = options.supportsBulk !== false;
93515
+ const latency = Math.max(0, options.latencyMs || 0);
93516
+ const rows = /* @__PURE__ */ new Map();
93517
+ for (const s of seed) {
93518
+ const v = s.version != null ? Number(s.version) || 0 : 0;
93519
+ rows.set(s.deviceId, {
93520
+ deviceId: s.deviceId,
93521
+ code: s.code,
93522
+ label: s.label,
93523
+ tariffCategory: s.tariffCategory ?? null,
93524
+ version: String(v),
93525
+ domain: s.domain,
93526
+ _v: v
93527
+ });
93528
+ }
93529
+ const delay = (value) => latency > 0 ? new Promise((res) => setTimeout(() => res(value), latency)) : Promise.resolve(value);
93530
+ const toRow = (r) => ({
93531
+ deviceId: r.deviceId,
93532
+ code: r.code,
93533
+ label: r.label,
93534
+ tariffCategory: r.tariffCategory,
93535
+ version: r.version
93536
+ });
93537
+ const port = {
93538
+ listDevices(args) {
93539
+ const list = [...rows.values()].filter((r) => !args.domain || !r.domain || r.domain === args.domain).map(toRow);
93540
+ return delay(list);
93541
+ },
93542
+ setCategory(args) {
93543
+ const r = rows.get(args.deviceId);
93544
+ if (!r) {
93545
+ return Promise.reject(new Error(`Unknown device ${args.deviceId}`));
93546
+ }
93547
+ if (args.expectedVersion != null && args.expectedVersion !== r.version) {
93548
+ return Promise.reject(
93549
+ new DeviceCategoryConflictError(
93550
+ args.deviceId,
93551
+ `Version conflict: expected ${args.expectedVersion}, current ${r.version}`
93552
+ )
93553
+ );
93554
+ }
93555
+ r.tariffCategory = args.category;
93556
+ r._v += 1;
93557
+ r.version = String(r._v);
93558
+ return delay(toRow(r));
93559
+ },
93560
+ snapshot() {
93561
+ return [...rows.values()].map(toRow);
93562
+ },
93563
+ bumpVersion(deviceId) {
93564
+ const r = rows.get(deviceId);
93565
+ if (!r) return;
93566
+ r._v += 1;
93567
+ r.version = String(r._v);
93568
+ }
93569
+ };
93570
+ if (supportsBulk) {
93571
+ port.setCategoryBulk = (args) => {
93572
+ const failed = [];
93573
+ let updated = 0;
93574
+ for (const id of args.deviceIds) {
93575
+ const r = rows.get(id);
93576
+ if (!r) {
93577
+ failed.push({ deviceId: id, reason: "UNKNOWN_DEVICE" });
93578
+ continue;
93579
+ }
93580
+ r.tariffCategory = args.category;
93581
+ r._v += 1;
93582
+ r.version = String(r._v);
93583
+ updated += 1;
93584
+ }
93585
+ return delay({ updated, failed });
93586
+ };
93587
+ }
93588
+ return port;
93589
+ }
93590
+ function createHttpDeviceCategoryPort(config2) {
93591
+ void config2;
93592
+ const notReady = () => Promise.reject(
93593
+ new Error(
93594
+ "createHttpDeviceCategoryPort: the GCDR device tariffCategory API (RFC-0228 B6) is not implemented yet. Inject createFakeDeviceCategoryPort for now, or a real DeviceCategoryPort once B6 ships."
93595
+ )
93596
+ );
93597
+ return {
93598
+ listDevices: () => notReady(),
93599
+ setCategory: () => notReady()
93600
+ };
93601
+ }
93602
+
93603
+ // src/components/financial-goals/deviceCategoryStyles.ts
93604
+ var DEVICE_CATEGORY_STYLE_ID = "myio-fin-device-category-styles";
93605
+ var DEVICE_CATEGORY_CSS = `
93606
+ .myio-devcat {
93607
+ --_dc-bg: var(--myio-bg, #ffffff);
93608
+ --_dc-card: var(--myio-card, #f8fafc);
93609
+ --_dc-text: var(--myio-text, #1f2937);
93610
+ --_dc-muted: var(--myio-text-muted, #6b7280);
93611
+ --_dc-border: var(--myio-border, #e5e7eb);
93612
+ --_dc-brand: var(--myio-brand-700, #5b2c9d);
93613
+ --_dc-brand-soft: var(--myio-brand-100, #efe7fb);
93614
+ --_dc-danger-bg: #fef2f2;
93615
+ --_dc-danger-border: #fecaca;
93616
+ --_dc-danger-text: #991b1b;
93617
+ --_dc-focus-bg: #fffbeb;
93618
+ --_dc-focus-border: #f59e0b;
93619
+ position: fixed;
93620
+ inset: 0;
93621
+ z-index: 2147483000;
93622
+ display: flex;
93623
+ align-items: center;
93624
+ justify-content: center;
93625
+ font-family: 'Nunito', system-ui, -apple-system, sans-serif;
93626
+ color: var(--_dc-text);
93627
+ opacity: 0;
93628
+ transition: opacity 0.18s ease;
93629
+ }
93630
+ .myio-devcat.show { opacity: 1; }
93631
+
93632
+ .myio-devcat__overlay {
93633
+ position: absolute;
93634
+ inset: 0;
93635
+ background: rgba(15, 23, 42, 0.55);
93636
+ backdrop-filter: blur(2px);
93637
+ }
93638
+
93639
+ .myio-devcat__card {
93640
+ position: relative;
93641
+ width: min(760px, 94vw);
93642
+ max-height: 90vh;
93643
+ display: flex;
93644
+ flex-direction: column;
93645
+ background: var(--_dc-bg);
93646
+ border: 1px solid var(--_dc-border);
93647
+ border-radius: 16px;
93648
+ box-shadow: 0 24px 60px rgba(15, 23, 42, 0.35);
93649
+ overflow: hidden;
93650
+ }
93651
+
93652
+ .myio-devcat__header {
93653
+ display: flex;
93654
+ align-items: center;
93655
+ justify-content: space-between;
93656
+ gap: 12px;
93657
+ padding: 16px 20px;
93658
+ border-bottom: 1px solid var(--_dc-border);
93659
+ background: var(--_dc-card);
93660
+ }
93661
+ .myio-devcat__title {
93662
+ display: flex;
93663
+ align-items: center;
93664
+ gap: 10px;
93665
+ font-weight: 800;
93666
+ font-size: 16px;
93667
+ margin: 0;
93668
+ }
93669
+ .myio-devcat__close {
93670
+ border: none;
93671
+ background: transparent;
93672
+ color: var(--_dc-muted);
93673
+ font-size: 22px;
93674
+ line-height: 1;
93675
+ cursor: pointer;
93676
+ padding: 4px 8px;
93677
+ border-radius: 8px;
93678
+ }
93679
+ .myio-devcat__close:hover { background: var(--_dc-border); color: var(--_dc-text); }
93680
+
93681
+ .myio-devcat__toolbar {
93682
+ display: flex;
93683
+ flex-wrap: wrap;
93684
+ gap: 10px;
93685
+ align-items: center;
93686
+ padding: 12px 20px;
93687
+ border-bottom: 1px solid var(--_dc-border);
93688
+ }
93689
+ .myio-devcat__search {
93690
+ flex: 1 1 200px;
93691
+ min-width: 140px;
93692
+ padding: 8px 12px;
93693
+ border: 1px solid var(--_dc-border);
93694
+ border-radius: 8px;
93695
+ background: var(--_dc-bg);
93696
+ color: var(--_dc-text);
93697
+ font-family: inherit;
93698
+ font-size: 13px;
93699
+ }
93700
+ .myio-devcat__filter {
93701
+ padding: 8px 12px;
93702
+ border: 1px solid var(--_dc-border);
93703
+ border-radius: 8px;
93704
+ background: var(--_dc-bg);
93705
+ color: var(--_dc-text);
93706
+ font-family: inherit;
93707
+ font-size: 13px;
93708
+ cursor: pointer;
93709
+ }
93710
+
93711
+ .myio-devcat__bulkbar {
93712
+ display: none;
93713
+ align-items: center;
93714
+ flex-wrap: wrap;
93715
+ gap: 10px;
93716
+ padding: 10px 20px;
93717
+ background: var(--_dc-brand-soft);
93718
+ border-bottom: 1px solid var(--_dc-border);
93719
+ font-size: 13px;
93720
+ font-weight: 700;
93721
+ }
93722
+ .myio-devcat__bulkbar.show { display: flex; }
93723
+ .myio-devcat__bulkbar select {
93724
+ padding: 6px 10px;
93725
+ border: 1px solid var(--_dc-border);
93726
+ border-radius: 8px;
93727
+ background: var(--_dc-bg);
93728
+ color: var(--_dc-text);
93729
+ font-family: inherit;
93730
+ font-size: 13px;
93731
+ }
93732
+ .myio-devcat__bulk-apply {
93733
+ padding: 6px 14px;
93734
+ border: none;
93735
+ border-radius: 8px;
93736
+ background: var(--_dc-brand);
93737
+ color: #fff;
93738
+ font-family: inherit;
93739
+ font-weight: 800;
93740
+ font-size: 13px;
93741
+ cursor: pointer;
93742
+ }
93743
+ .myio-devcat__bulk-apply:disabled { opacity: 0.5; cursor: not-allowed; }
93744
+
93745
+ .myio-devcat__body {
93746
+ flex: 1 1 auto;
93747
+ overflow-y: auto;
93748
+ padding: 6px 0;
93749
+ }
93750
+ .myio-devcat__empty {
93751
+ padding: 40px 20px;
93752
+ text-align: center;
93753
+ color: var(--_dc-muted);
93754
+ font-size: 14px;
93755
+ }
93756
+
93757
+ .myio-devcat__row {
93758
+ display: flex;
93759
+ align-items: center;
93760
+ gap: 12px;
93761
+ padding: 10px 20px;
93762
+ border-bottom: 1px solid var(--_dc-border);
93763
+ }
93764
+ .myio-devcat__row[data-focused="1"] {
93765
+ background: var(--_dc-focus-bg);
93766
+ box-shadow: inset 3px 0 0 var(--_dc-focus-border);
93767
+ }
93768
+ .myio-devcat__row-check { flex: 0 0 auto; width: 16px; height: 16px; cursor: pointer; }
93769
+ .myio-devcat__row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
93770
+ .myio-devcat__row-label {
93771
+ font-weight: 700;
93772
+ font-size: 14px;
93773
+ overflow: hidden;
93774
+ text-overflow: ellipsis;
93775
+ white-space: nowrap;
93776
+ }
93777
+ .myio-devcat__row-code {
93778
+ font-size: 12px;
93779
+ color: var(--_dc-muted);
93780
+ font-variant-numeric: tabular-nums;
93781
+ }
93782
+ .myio-devcat__row-select {
93783
+ flex: 0 0 auto;
93784
+ padding: 7px 10px;
93785
+ border: 1px solid var(--_dc-border);
93786
+ border-radius: 8px;
93787
+ background: var(--_dc-bg);
93788
+ color: var(--_dc-text);
93789
+ font-family: inherit;
93790
+ font-size: 13px;
93791
+ font-weight: 700;
93792
+ cursor: pointer;
93793
+ }
93794
+ .myio-devcat__row[data-cat="null"] .myio-devcat__row-select { color: var(--_dc-muted); }
93795
+ .myio-devcat__row-conflict {
93796
+ display: none;
93797
+ flex: 1 1 100%;
93798
+ margin-top: 6px;
93799
+ padding: 7px 12px;
93800
+ border-radius: 8px;
93801
+ background: var(--_dc-danger-bg);
93802
+ border: 1px solid var(--_dc-danger-border);
93803
+ color: var(--_dc-danger-text);
93804
+ font-size: 12px;
93805
+ font-weight: 700;
93806
+ }
93807
+ .myio-devcat__row[data-conflict="1"] { flex-wrap: wrap; }
93808
+ .myio-devcat__row[data-conflict="1"] .myio-devcat__row-conflict { display: block; }
93809
+
93810
+ .myio-devcat__footer {
93811
+ display: flex;
93812
+ align-items: center;
93813
+ justify-content: space-between;
93814
+ gap: 12px;
93815
+ padding: 12px 20px;
93816
+ border-top: 1px solid var(--_dc-border);
93817
+ background: var(--_dc-card);
93818
+ font-size: 12px;
93819
+ color: var(--_dc-muted);
93820
+ }
93821
+
93822
+ @media (prefers-color-scheme: dark) {
93823
+ .myio-devcat {
93824
+ --_dc-bg: var(--myio-bg, #0f172a);
93825
+ --_dc-card: var(--myio-card, #1e293b);
93826
+ --_dc-text: var(--myio-text, #e2e8f0);
93827
+ --_dc-muted: var(--myio-text-muted, #94a3b8);
93828
+ --_dc-border: var(--myio-border, #334155);
93829
+ --_dc-brand: var(--myio-brand-700, #a78bfa);
93830
+ --_dc-brand-soft: var(--myio-brand-100, #2a2350);
93831
+ --_dc-danger-bg: #3a1414; --_dc-danger-border: #7f1d1d; --_dc-danger-text: #fca5a5;
93832
+ --_dc-focus-bg: #3a2e0a; --_dc-focus-border: #b45309;
93833
+ }
93834
+ }
93835
+ :root[data-theme="dark"] .myio-devcat {
93836
+ --_dc-bg: var(--myio-bg, #0f172a);
93837
+ --_dc-card: var(--myio-card, #1e293b);
93838
+ --_dc-text: var(--myio-text, #e2e8f0);
93839
+ --_dc-muted: var(--myio-text-muted, #94a3b8);
93840
+ --_dc-border: var(--myio-border, #334155);
93841
+ --_dc-brand: var(--myio-brand-700, #a78bfa);
93842
+ --_dc-brand-soft: var(--myio-brand-100, #2a2350);
93843
+ --_dc-danger-bg: #3a1414; --_dc-danger-border: #7f1d1d; --_dc-danger-text: #fca5a5;
93844
+ --_dc-focus-bg: #3a2e0a; --_dc-focus-border: #b45309;
93845
+ }
93846
+ :root[data-theme="light"] .myio-devcat {
93847
+ --_dc-bg: var(--myio-bg, #ffffff);
93848
+ --_dc-card: var(--myio-card, #f8fafc);
93849
+ --_dc-text: var(--myio-text, #1f2937);
93850
+ --_dc-muted: var(--myio-text-muted, #6b7280);
93851
+ --_dc-border: var(--myio-border, #e5e7eb);
93852
+ --_dc-brand: var(--myio-brand-700, #5b2c9d);
93853
+ --_dc-brand-soft: var(--myio-brand-100, #efe7fb);
93854
+ --_dc-danger-bg: #fef2f2; --_dc-danger-border: #fecaca; --_dc-danger-text: #991b1b;
93855
+ --_dc-focus-bg: #fffbeb; --_dc-focus-border: #f59e0b;
93856
+ }
93857
+ `;
93858
+ function injectDeviceCategoryStyles(doc) {
93859
+ if (!doc || !doc.head) return;
93860
+ if (doc.getElementById(DEVICE_CATEGORY_STYLE_ID)) return;
93861
+ const style = doc.createElement("style");
93862
+ style.id = DEVICE_CATEGORY_STYLE_ID;
93863
+ style.textContent = DEVICE_CATEGORY_CSS;
93864
+ doc.head.appendChild(style);
93865
+ }
93866
+
93867
+ // src/components/financial-goals/deviceCategoryPanel.ts
93868
+ var PANEL_TERM_LABEL = {
93869
+ lojas: "Lojas",
93870
+ area_comum: "\xC1rea Comum"
93871
+ };
93872
+ var NONE_LABEL = "\u2014 Sem categoria \u2014";
93873
+ function deviceCategoryLabel(cat) {
93874
+ if (cat == null) return NONE_LABEL;
93875
+ return PANEL_TERM_LABEL[wireCategoryToPanel(cat)];
93876
+ }
93877
+ function deviceCategoryToPanelTerm(cat) {
93878
+ return cat == null ? null : wireCategoryToPanel(cat);
93879
+ }
93880
+ function panelTermToDeviceCategory(term) {
93881
+ return term == null ? null : panelCategoryToWire(term);
93882
+ }
93883
+ function esc7(s) {
93884
+ return String(s ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
93885
+ }
93886
+ function categoryOptionsHTML(selected) {
93887
+ const opt = (value, label, isSel) => `<option value="${value}"${isSel ? " selected" : ""}>${esc7(label)}</option>`;
93888
+ return opt("", NONE_LABEL, selected == null) + opt("COMMON_AREA", deviceCategoryLabel("COMMON_AREA"), selected === "COMMON_AREA") + opt("SPECIFIC", deviceCategoryLabel("SPECIFIC"), selected === "SPECIFIC");
93889
+ }
93890
+ function openDeviceCategoryPanel(params) {
93891
+ const doc = params.document || (typeof document !== "undefined" ? document : void 0);
93892
+ if (!doc) {
93893
+ throw new Error(
93894
+ "openDeviceCategoryPanel requires a document (pass params.document in non-DOM envs)."
93895
+ );
93896
+ }
93897
+ injectDeviceCategoryStyles(doc);
93898
+ const { port, customerId } = params;
93899
+ let rows = [];
93900
+ let search = "";
93901
+ let filter = params.initialFilter || "all";
93902
+ const selected = /* @__PURE__ */ new Set();
93903
+ const root = doc.createElement("div");
93904
+ root.className = "myio-devcat";
93905
+ root.setAttribute("data-devcat", "1");
93906
+ if (params.theme) {
93907
+ for (const [k, v] of Object.entries(params.theme)) {
93908
+ if (k.startsWith("--") && typeof v === "string") root.style.setProperty(k, v);
93909
+ }
93910
+ }
93911
+ root.innerHTML = `
93912
+ <div class="myio-devcat__overlay" data-devcat-overlay="1"></div>
93913
+ <div class="myio-devcat__card" role="dialog" aria-modal="true" aria-label="Categorias de tarifa por dispositivo">
93914
+ <div class="myio-devcat__header">
93915
+ <h3 class="myio-devcat__title"><span aria-hidden="true">\u{1F3F7}\uFE0F</span>Categorias de tarifa por dispositivo</h3>
93916
+ <button type="button" class="myio-devcat__close" data-devcat-close="1" aria-label="Fechar">&times;</button>
93917
+ </div>
93918
+ <div class="myio-devcat__toolbar">
93919
+ <input type="search" class="myio-devcat__search" data-devcat-search="1"
93920
+ placeholder="Buscar por nome ou c\xF3digo\u2026" aria-label="Buscar dispositivo" />
93921
+ <select class="myio-devcat__filter" data-devcat-filter="1" aria-label="Filtrar por categoria">
93922
+ <option value="all">Todas as categorias</option>
93923
+ <option value="uncategorized">Sem categoria</option>
93924
+ <option value="COMMON_AREA">${esc7(deviceCategoryLabel("COMMON_AREA"))}</option>
93925
+ <option value="SPECIFIC">${esc7(deviceCategoryLabel("SPECIFIC"))}</option>
93926
+ </select>
93927
+ <label class="myio-devcat__filter" style="display:flex;align-items:center;gap:6px;cursor:pointer;">
93928
+ <input type="checkbox" data-devcat-selectall="1" /> Selecionar vis\xEDveis
93929
+ </label>
93930
+ </div>
93931
+ <div class="myio-devcat__bulkbar" data-devcat-bulkbar="1">
93932
+ <span data-devcat-bulkcount="1">0 selecionados</span>
93933
+ <select data-devcat-bulkcat="1" aria-label="Categoria para os selecionados">
93934
+ <option value="">${esc7(NONE_LABEL)}</option>
93935
+ <option value="COMMON_AREA">${esc7(deviceCategoryLabel("COMMON_AREA"))}</option>
93936
+ <option value="SPECIFIC">${esc7(deviceCategoryLabel("SPECIFIC"))}</option>
93937
+ </select>
93938
+ <button type="button" class="myio-devcat__bulk-apply" data-devcat-bulkapply="1">Aplicar a selecionados</button>
93939
+ </div>
93940
+ <div class="myio-devcat__body" data-devcat-body="1"></div>
93941
+ <div class="myio-devcat__footer">
93942
+ <span data-devcat-count="1"></span>
93943
+ <span>Categoria expl\xEDcita \u2014 nunca inferida do nome (RFC-0207).</span>
93944
+ </div>
93945
+ </div>
93946
+ `;
93947
+ const body = root.querySelector("[data-devcat-body]");
93948
+ const countEl = root.querySelector("[data-devcat-count]");
93949
+ const bulkbar = root.querySelector("[data-devcat-bulkbar]");
93950
+ const bulkCountEl = root.querySelector("[data-devcat-bulkcount]");
93951
+ const selectAll = root.querySelector("[data-devcat-selectall]");
93952
+ function matchesFilter(r) {
93953
+ if (filter === "all") return true;
93954
+ if (filter === "uncategorized") return r.tariffCategory == null;
93955
+ return r.tariffCategory === filter;
93956
+ }
93957
+ function matchesSearch(r) {
93958
+ if (!search) return true;
93959
+ const hay = `${r.label || ""} ${r.code || ""}`.toLowerCase();
93960
+ return hay.includes(search.toLowerCase());
93961
+ }
93962
+ function visibleRows() {
93963
+ return rows.filter((r) => matchesFilter(r) && matchesSearch(r));
93964
+ }
93965
+ function rowHTML(r) {
93966
+ const name = esc7(r.label || r.code || r.deviceId);
93967
+ const code = r.code ? `<span class="myio-devcat__row-code">${esc7(r.code)}</span>` : "";
93968
+ const checked = selected.has(r.deviceId) ? " checked" : "";
93969
+ const focused = r.deviceId === params.focusDeviceId ? ' data-focused="1"' : "";
93970
+ const catAttr = r.tariffCategory == null ? "null" : r.tariffCategory;
93971
+ return `<div class="myio-devcat__row" data-devcat-row="${esc7(r.deviceId)}" data-cat="${catAttr}"${focused}><input type="checkbox" class="myio-devcat__row-check" data-devcat-rowcheck="${esc7(
93972
+ r.deviceId
93973
+ )}"${checked} aria-label="Selecionar ${name}" /><div class="myio-devcat__row-main"><span class="myio-devcat__row-label" title="${name}">${name}</span>` + code + `</div><select class="myio-devcat__row-select" data-devcat-rowselect="${esc7(
93974
+ r.deviceId
93975
+ )}" aria-label="Categoria de ${name}">${categoryOptionsHTML(r.tariffCategory)}</select><div class="myio-devcat__row-conflict" data-devcat-conflict="${esc7(r.deviceId)}"></div></div>`;
93976
+ }
93977
+ function render3() {
93978
+ const vis = visibleRows();
93979
+ if (vis.length === 0) {
93980
+ body.innerHTML = `<div class="myio-devcat__empty">Nenhum dispositivo encontrado.</div>`;
93981
+ } else {
93982
+ body.innerHTML = vis.map(rowHTML).join("");
93983
+ }
93984
+ countEl.textContent = `${vis.length} de ${rows.length} dispositivo(s)`;
93985
+ wireRows();
93986
+ updateBulkBar();
93987
+ if (params.focusDeviceId) {
93988
+ const el2 = body.querySelector(
93989
+ `[data-devcat-row="${cssEscape(params.focusDeviceId)}"]`
93990
+ );
93991
+ if (el2 && typeof el2.scrollIntoView === "function") {
93992
+ el2.scrollIntoView({ block: "center" });
93993
+ }
93994
+ }
93995
+ }
93996
+ function updateBulkBar() {
93997
+ const n = selected.size;
93998
+ bulkCountEl.textContent = `${n} selecionado(s)`;
93999
+ bulkbar.classList.toggle("show", n > 0);
94000
+ }
94001
+ function wireRows() {
94002
+ body.querySelectorAll("[data-devcat-rowselect]").forEach((sel) => {
94003
+ sel.addEventListener("change", () => {
94004
+ const id = sel.getAttribute("data-devcat-rowselect");
94005
+ const value = sel.value;
94006
+ const category = value === "" ? null : value;
94007
+ void applySingle(id, category);
94008
+ });
94009
+ });
94010
+ body.querySelectorAll("[data-devcat-rowcheck]").forEach((cb) => {
94011
+ cb.addEventListener("change", () => {
94012
+ const id = cb.getAttribute("data-devcat-rowcheck");
94013
+ if (cb.checked) selected.add(id);
94014
+ else selected.delete(id);
94015
+ updateBulkBar();
94016
+ });
94017
+ });
94018
+ }
94019
+ function showConflict(deviceId, message) {
94020
+ const rowEl = body.querySelector(
94021
+ `[data-devcat-row="${cssEscape(deviceId)}"]`
94022
+ );
94023
+ const banner = body.querySelector(
94024
+ `[data-devcat-conflict="${cssEscape(deviceId)}"]`
94025
+ );
94026
+ if (rowEl) rowEl.setAttribute("data-conflict", "1");
94027
+ if (banner) banner.textContent = message;
94028
+ }
94029
+ function clearConflict(deviceId) {
94030
+ const rowEl = body.querySelector(
94031
+ `[data-devcat-row="${cssEscape(deviceId)}"]`
94032
+ );
94033
+ const banner = body.querySelector(
94034
+ `[data-devcat-conflict="${cssEscape(deviceId)}"]`
94035
+ );
94036
+ if (rowEl) rowEl.removeAttribute("data-conflict");
94037
+ if (banner) banner.textContent = "";
94038
+ }
94039
+ async function applySingle(deviceId, category) {
94040
+ const row = rows.find((r) => r.deviceId === deviceId);
94041
+ if (!row) return;
94042
+ clearConflict(deviceId);
94043
+ try {
94044
+ const updated = await port.setCategory({
94045
+ deviceId,
94046
+ category,
94047
+ expectedVersion: row.version
94048
+ });
94049
+ row.tariffCategory = updated.tariffCategory;
94050
+ row.version = updated.version;
94051
+ const rowEl = body.querySelector(
94052
+ `[data-devcat-row="${cssEscape(deviceId)}"]`
94053
+ );
94054
+ if (rowEl) rowEl.setAttribute("data-cat", updated.tariffCategory == null ? "null" : updated.tariffCategory);
94055
+ } catch (err) {
94056
+ const isConflict = err instanceof DeviceCategoryConflictError || err?.code === "DEVICE_CATEGORY_VERSION_CONFLICT";
94057
+ showConflict(
94058
+ deviceId,
94059
+ isConflict ? "Este dispositivo foi alterado por outra pessoa. Recarregue para ver a vers\xE3o atual e reaplique." : `N\xE3o foi poss\xEDvel salvar: ${err?.message || "erro desconhecido"}`
94060
+ );
94061
+ }
94062
+ }
94063
+ async function applyBulk(category) {
94064
+ const ids = [...selected];
94065
+ if (ids.length === 0) return;
94066
+ if (typeof port.setCategoryBulk === "function") {
94067
+ const res = await port.setCategoryBulk({ deviceIds: ids, category });
94068
+ const failedIds = new Set(res.failed.map((f) => f.deviceId));
94069
+ for (const id of ids) {
94070
+ const row = rows.find((r) => r.deviceId === id);
94071
+ if (row && !failedIds.has(id)) {
94072
+ row.tariffCategory = category;
94073
+ if (row.version != null) row.version = String((Number(row.version) || 0) + 1);
94074
+ }
94075
+ }
94076
+ for (const f of res.failed) showConflict(f.deviceId, `Falha: ${f.reason}`);
94077
+ } else {
94078
+ await Promise.all(ids.map((id) => applySingle(id, category)));
94079
+ }
94080
+ render3();
94081
+ }
94082
+ const searchEl = root.querySelector("[data-devcat-search]");
94083
+ const filterEl = root.querySelector("[data-devcat-filter]");
94084
+ filterEl.value = filter;
94085
+ searchEl.addEventListener("input", () => {
94086
+ search = searchEl.value;
94087
+ render3();
94088
+ });
94089
+ filterEl.addEventListener("change", () => {
94090
+ filter = filterEl.value;
94091
+ render3();
94092
+ });
94093
+ selectAll.addEventListener("change", () => {
94094
+ const vis = visibleRows();
94095
+ if (selectAll.checked) vis.forEach((r) => selected.add(r.deviceId));
94096
+ else vis.forEach((r) => selected.delete(r.deviceId));
94097
+ render3();
94098
+ });
94099
+ root.querySelector("[data-devcat-bulkapply]").addEventListener("click", () => {
94100
+ const bulkCat = root.querySelector("[data-devcat-bulkcat]").value;
94101
+ const category = bulkCat === "" ? null : bulkCat;
94102
+ void applyBulk(category);
94103
+ });
94104
+ let closed = false;
94105
+ function close() {
94106
+ if (closed) return;
94107
+ closed = true;
94108
+ root.classList.remove("show");
94109
+ doc.removeEventListener("keydown", escHandler);
94110
+ const remove = () => {
94111
+ if (root.parentNode) root.parentNode.removeChild(root);
94112
+ };
94113
+ if (typeof setTimeout === "function") setTimeout(remove, 180);
94114
+ else remove();
94115
+ params.onClose?.();
94116
+ }
94117
+ function escHandler(e) {
94118
+ if (e.key === "Escape") close();
94119
+ }
94120
+ root.querySelector("[data-devcat-close]")?.addEventListener("click", close);
94121
+ root.querySelector("[data-devcat-overlay]")?.addEventListener("click", close);
94122
+ doc.addEventListener("keydown", escHandler);
94123
+ (doc.body || doc.documentElement).appendChild(root);
94124
+ if (typeof requestAnimationFrame === "function") {
94125
+ requestAnimationFrame(() => root.classList.add("show"));
94126
+ } else {
94127
+ root.classList.add("show");
94128
+ }
94129
+ async function load() {
94130
+ body.innerHTML = `<div class="myio-devcat__empty">Carregando dispositivos\u2026</div>`;
94131
+ try {
94132
+ rows = await port.listDevices({ customerId, domain: params.domain });
94133
+ } catch (err) {
94134
+ rows = [];
94135
+ body.innerHTML = `<div class="myio-devcat__empty">N\xE3o foi poss\xEDvel carregar os dispositivos: ${esc7(
94136
+ err?.message || "erro"
94137
+ )}</div>`;
94138
+ countEl.textContent = "";
94139
+ return;
94140
+ }
94141
+ render3();
94142
+ }
94143
+ const ready = load();
94144
+ return {
94145
+ ready,
94146
+ close,
94147
+ getRoot: () => root,
94148
+ refresh: () => load(),
94149
+ getRows: () => rows.map((r) => ({ ...r })),
94150
+ getVisibleDeviceIds: () => visibleRows().map((r) => r.deviceId),
94151
+ setSearch: (text2) => {
94152
+ search = text2;
94153
+ searchEl.value = text2;
94154
+ render3();
94155
+ },
94156
+ setCategoryFilter: (f) => {
94157
+ filter = f;
94158
+ filterEl.value = f;
94159
+ render3();
94160
+ }
94161
+ };
94162
+ }
94163
+ function createCoverageDeepLink(config2) {
94164
+ const open = (focusDeviceId, initialFilter) => openDeviceCategoryPanel({
94165
+ port: config2.port,
94166
+ customerId: config2.customerId,
94167
+ domain: config2.domain,
94168
+ focusDeviceId,
94169
+ initialFilter,
94170
+ theme: config2.theme,
94171
+ document: config2.document
94172
+ });
94173
+ return {
94174
+ // Deep-link from one uncategorized device → open focused on it.
94175
+ onCategorizeDevice: (deviceId) => open(deviceId),
94176
+ // Generic "manage categories" → open pre-filtered to the uncategorized set.
94177
+ onManageCategories: () => open(void 0, "uncategorized")
94178
+ };
94179
+ }
94180
+ function cssEscape(s) {
94181
+ if (typeof globalThis.CSS?.escape === "function") {
94182
+ return globalThis.CSS.escape(s);
94183
+ }
94184
+ return String(s).replace(/["\\\]#.:>~+*^$|()[{}]/g, "\\$&");
94185
+ }
94186
+
94187
+ // src/components/financial-goals/moneyRolloutGate.ts
94188
+ function normalizeAllowlist(list) {
94189
+ const out = /* @__PURE__ */ new Set();
94190
+ if (!list) return out;
94191
+ const iter = list instanceof Set ? list : Array.isArray(list) ? list : [];
94192
+ for (const raw of iter) {
94193
+ const id = String(raw ?? "").trim();
94194
+ if (id) out.add(id);
94195
+ }
94196
+ return out;
94197
+ }
94198
+ function normalizeId(customerId) {
94199
+ return customerId == null ? "" : String(customerId).trim();
94200
+ }
94201
+ function isBrokenOverlay(overlay) {
94202
+ return !!overlay && overlay.state === "unavailable";
94203
+ }
94204
+ function isCustomerEligible(params, settings) {
94205
+ if (settings && settings.goalsMoneyRolloutBaseWide === true) return true;
94206
+ const set = normalizeAllowlist(params.allowlist ?? settings?.goalsMoneyRolloutAllowlist);
94207
+ if (set.has("*")) return true;
94208
+ const id = normalizeId(params.customerId);
94209
+ if (!id) return false;
94210
+ return set.has(id);
94211
+ }
94212
+ function resolveMoneyRollout(params) {
94213
+ const p = params || {};
94214
+ const settings = p.settings ?? null;
94215
+ const featureAvailable = typeof p.featureAvailable === "boolean" ? p.featureAvailable : !!(settings && settings.goalsMoneyApi);
94216
+ if (!featureAvailable) return { enabled: false, reason: "disabled" };
94217
+ if (!isCustomerEligible(p, settings)) {
94218
+ return { enabled: false, reason: "not-eligible" };
94219
+ }
94220
+ if (isBrokenOverlay(p.overlaySample)) {
94221
+ return { enabled: false, reason: "coverage-gap" };
94222
+ }
94223
+ return { enabled: true, reason: "eligible" };
94224
+ }
94225
+ function routeMoneyRender(params) {
94226
+ const decision = resolveMoneyRollout(params);
94227
+ if (decision.reason === "disabled") {
94228
+ return { decision, action: "render-nothing" };
94229
+ }
94230
+ if (decision.enabled) {
94231
+ return { decision, action: "render-money" };
94232
+ }
94233
+ const sample = params?.overlaySample;
94234
+ const coverageOverlay = sample && sample.state === "unavailable" ? sample : { state: "unavailable", reason: MONEY_REQUIRES_DEVICE_GRANULARITY };
94235
+ return { decision, action: "render-coverage", coverageOverlay };
94236
+ }
94237
+ function renderGatedMoney(params, renderers) {
94238
+ const routing = routeMoneyRender(params);
94239
+ if (routing.action === "render-nothing") return null;
94240
+ if (routing.action === "render-money") {
94241
+ return renderers.renderMoney(params?.overlaySample);
94242
+ }
94243
+ return renderers.renderCoverage(routing.coverageOverlay);
94244
+ }
94245
+
91639
94246
  // src/components/premium-modals/measurement-setup/types.ts
91640
94247
  var WATER_UNITS = [
91641
94248
  { value: "m3", label: "Metros C\xFAbicos (m\xB3)" },
@@ -113497,6 +116104,7 @@ var CustomerGoalsCard = class {
113497
116104
  if (partial.title !== void 0) this.params.title = partial.title;
113498
116105
  if (partial.series !== void 0) this.params.series = { ...partial.series };
113499
116106
  if (partial.totals !== void 0) this.params.totals = partial.totals;
116107
+ if (partial.moneyVariance !== void 0) this.params.moneyVariance = partial.moneyVariance;
113500
116108
  this.renderInner();
113501
116109
  this.renderChart();
113502
116110
  }
@@ -113747,6 +116355,31 @@ var CustomerGoalsCard = class {
113747
116355
  this.toggleExpand();
113748
116356
  });
113749
116357
  }
116358
+ this.renderMoneyVariance();
116359
+ }
116360
+ /**
116361
+ * RFC-0228 A7 — append the R$ variance readout below the totals/deltas strip when a
116362
+ * money overlay/amount is present. `renderMoneyVariance` returns `null` when the gate
116363
+ * is off (no `params.moneyVariance`), so the card stays byte-identical to before.
116364
+ * Reads only money names (`monetaryProjection`/`currencyBudget`) — never the quantity
116365
+ * `series.budget` prop (naming bridge). Honors DEC-6 withholding inside the renderer.
116366
+ */
116367
+ renderMoneyVariance() {
116368
+ const mv = this.params.moneyVariance;
116369
+ if (!mv) return;
116370
+ const el2 = renderMoneyVariance({
116371
+ monetaryProjection: mv.monetaryProjection,
116372
+ currencyBudget: mv.currencyBudget,
116373
+ overlay: mv.overlay,
116374
+ withheld: mv.withheld,
116375
+ label: mv.label != null ? mv.label : "Varia\xE7\xE3o vs meta (R$)",
116376
+ document
116377
+ });
116378
+ if (!el2) return;
116379
+ el2.classList.add("myio-cgc__money-variance");
116380
+ el2.style.padding = "6px 8px 8px";
116381
+ el2.style.borderTop = "1px solid var(--cgc-border)";
116382
+ this.el.appendChild(el2);
113750
116383
  }
113751
116384
  destroyChart() {
113752
116385
  try {
@@ -131020,10 +133653,10 @@ var AlarmsNotificationsPanelView = class {
131020
133653
  const renderListPanel = (group, items, sel) => {
131021
133654
  if (items.length === 0) return '<div class="afm-empty">Nenhum item encontrado</div>';
131022
133655
  return items.map((item) => {
131023
- const esc6 = this.esc(item);
133656
+ const esc8 = this.esc(item);
131024
133657
  return `<label class="afm-check-item">
131025
- <input type="checkbox" class="afm-checkbox" data-group="${group}" data-value="${esc6}"${sel.has(item) ? " checked" : ""}>
131026
- <span class="afm-check-label">${esc6}</span>
133658
+ <input type="checkbox" class="afm-checkbox" data-group="${group}" data-value="${esc8}"${sel.has(item) ? " checked" : ""}>
133659
+ <span class="afm-check-label">${esc8}</span>
131027
133660
  </label>`;
131028
133661
  }).join("");
131029
133662
  };
@@ -143858,7 +146491,7 @@ function exportTimelineToPDF(data, labels = {}, locale = "pt-BR", opts = {}) {
143858
146491
  const docTitle = buildActivationReportTitle(deviceName, opts.customerName);
143859
146492
  const issuedAt = formatIssuedAt(/* @__PURE__ */ new Date(), locale);
143860
146493
  const utilization = data.totalHours > 0 ? (data.totalOnMinutes / (data.totalHours * 60) * 100).toFixed(1) : "0.0";
143861
- const esc6 = (s) => String(s).replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
146494
+ const esc8 = (s) => String(s).replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
143862
146495
  const htmlContent = `
143863
146496
  <!DOCTYPE html>
143864
146497
  <html lang="pt-BR">
@@ -143910,14 +146543,14 @@ function exportTimelineToPDF(data, labels = {}, locale = "pt-BR", opts = {}) {
143910
146543
  <div class="brand">MyIO BAS</div>
143911
146544
  <h1>Relat\xF3rio de Acionamentos</h1>
143912
146545
  <div class="meta">
143913
- <div><b>Dispositivo:</b> ${esc6(deviceName)}</div>
146546
+ <div><b>Dispositivo:</b> ${esc8(deviceName)}</div>
143914
146547
  <div><b>Per\xEDodo:</b> ${periodStart} \u2014 ${periodEnd}</div>
143915
146548
  </div>
143916
146549
  </div>
143917
146550
  ${customerName ? `
143918
146551
  <div class="customer-badge">
143919
146552
  <div class="label">Cliente</div>
143920
- <div class="value">${esc6(customerName)}</div>
146553
+ <div class="value">${esc8(customerName)}</div>
143921
146554
  </div>` : ""}
143922
146555
  </div>
143923
146556
 
@@ -143928,7 +146561,7 @@ function exportTimelineToPDF(data, labels = {}, locale = "pt-BR", opts = {}) {
143928
146561
  </div>
143929
146562
  <div class="summary-card accent-green">
143930
146563
  <div class="summary-value">${formatDurationMinutes(data.totalOnMinutes)}</div>
143931
- <div class="summary-label">Tempo ${esc6(labelOn)}</div>
146564
+ <div class="summary-label">Tempo ${esc8(labelOn)}</div>
143932
146565
  </div>
143933
146566
  <div class="summary-card accent-amber">
143934
146567
  <div class="summary-value">${utilization}%</div>
@@ -143956,7 +146589,7 @@ function exportTimelineToPDF(data, labels = {}, locale = "pt-BR", opts = {}) {
143956
146589
  <tr>
143957
146590
  <td>${formatDateForExport(seg.startTime, locale)}</td>
143958
146591
  <td>${formatDateForExport(seg.endTime, locale)}</td>
143959
- <td><span class="pill ${seg.state === "on" ? "pill-on" : "pill-off"}">${seg.state === "on" ? esc6(labelOn) : esc6(labelOff)}</span></td>
146592
+ <td><span class="pill ${seg.state === "on" ? "pill-on" : "pill-off"}">${seg.state === "on" ? esc8(labelOn) : esc8(labelOff)}</span></td>
143960
146593
  <td>${formatDurationMinutes(seg.durationMinutes)}</td>
143961
146594
  <td>${seg.source === "manual" ? "Manual" : seg.source === "schedule" ? "Agendamento" : seg.source === "automation" ? "Automa\xE7\xE3o" : "Desconhecido"}</td>
143962
146595
  </tr>
@@ -143965,7 +146598,7 @@ function exportTimelineToPDF(data, labels = {}, locale = "pt-BR", opts = {}) {
143965
146598
  </table>
143966
146599
 
143967
146600
  <div class="footer">
143968
- <span class="issued">Emitido em ${issuedAt}</span>${customerName ? ` &middot; ${esc6(customerName)}` : ""} &middot; <span class="brand">MyIO BAS</span>
146601
+ <span class="issued">Emitido em ${issuedAt}</span>${customerName ? ` &middot; ${esc8(customerName)}` : ""} &middot; <span class="brand">MyIO BAS</span>
143969
146602
  </div>
143970
146603
  </body>
143971
146604
  </html>
@@ -147149,9 +149782,9 @@ function renderModalHTML2(data, config2) {
147149
149782
  const subAmbientesHTML = data.subAmbientes.map((sub, index) => {
147150
149783
  const ambData = sub.ambienteData;
147151
149784
  const status = ambData.status || "offline";
147152
- let devicesHTML = "";
149785
+ let devicesHTML2 = "";
147153
149786
  if (ambData.energyDevices && ambData.energyDevices.length > 0) {
147154
- devicesHTML = ambData.energyDevices.map((device) => `
149787
+ devicesHTML2 = ambData.energyDevices.map((device) => `
147155
149788
  <div class="${AMBIENTE_GROUP_CSS_PREFIX}__device-item">
147156
149789
  <span class="${AMBIENTE_GROUP_CSS_PREFIX}__device-icon">${getDeviceIcon6(device.deviceProfile || device.deviceType || "")}</span>
147157
149790
  <div class="${AMBIENTE_GROUP_CSS_PREFIX}__device-info">
@@ -147162,7 +149795,7 @@ function renderModalHTML2(data, config2) {
147162
149795
  </div>
147163
149796
  `).join("");
147164
149797
  } else if (ambData.devices && ambData.devices.length > 0) {
147165
- devicesHTML = ambData.devices.slice(0, 6).map((device) => `
149798
+ devicesHTML2 = ambData.devices.slice(0, 6).map((device) => `
147166
149799
  <div class="${AMBIENTE_GROUP_CSS_PREFIX}__device-item">
147167
149800
  <span class="${AMBIENTE_GROUP_CSS_PREFIX}__device-icon">${getDeviceIcon6(device.deviceProfile || device.deviceType || "")}</span>
147168
149801
  <div class="${AMBIENTE_GROUP_CSS_PREFIX}__device-info">
@@ -147210,7 +149843,7 @@ function renderModalHTML2(data, config2) {
147210
149843
  <span class="${AMBIENTE_GROUP_CSS_PREFIX}__subambiente-arrow">\u25B6</span>
147211
149844
  </div>
147212
149845
  <div class="${AMBIENTE_GROUP_CSS_PREFIX}__subambiente-details">
147213
- ${devicesHTML ? `<div class="${AMBIENTE_GROUP_CSS_PREFIX}__devices-grid">${devicesHTML}</div>` : '<p style="color:#6c757d;font-size:13px;">Nenhum dispositivo configurado</p>'}
149846
+ ${devicesHTML2 ? `<div class="${AMBIENTE_GROUP_CSS_PREFIX}__devices-grid">${devicesHTML2}</div>` : '<p style="color:#6c757d;font-size:13px;">Nenhum dispositivo configurado</p>'}
147214
149847
  ${remotesHTML}
147215
149848
  </div>
147216
149849
  </div>
@@ -156865,6 +159498,7 @@ var version = package_default.version || "0.0.0";
156865
159498
  CONSUMPTION_CHART_COLORS,
156866
159499
  CONSUMPTION_CHART_DEFAULTS,
156867
159500
  CONSUMPTION_THEME_COLORS,
159501
+ COVERAGE_STYLE_ID,
156868
159502
  CUSTOMER_CODE_RE,
156869
159503
  CUSTOMER_NAME_STOPWORDS,
156870
159504
  CardGridPanel,
@@ -156932,6 +159566,7 @@ var version = package_default.version || "0.0.0";
156932
159566
  DEFAULT_SWITCH_STATE,
156933
159567
  DEFAULT_TABS,
156934
159568
  DEFAULT_WATER_GROUP_COLORS,
159569
+ DEVICE_CATEGORY_STYLE_ID,
156935
159570
  DEVICE_COUNT_KEYS,
156936
159571
  DEVICE_OPERATIONAL_CARD_GRID_STYLES,
156937
159572
  DEVICE_OPERATIONAL_CARD_STYLES,
@@ -156943,6 +159578,7 @@ var version = package_default.version || "0.0.0";
156943
159578
  DOMAIN_CODES,
156944
159579
  DOMAIN_MAP,
156945
159580
  DeviceCategory,
159581
+ DeviceCategoryConflictError,
156946
159582
  DeviceComparisonTooltip,
156947
159583
  DeviceContextType,
156948
159584
  DeviceDomainType,
@@ -156990,6 +159626,8 @@ var version = package_default.version || "0.0.0";
156990
159626
  GRANULARITY_SELECTOR_CSS_PREFIX,
156991
159627
  GRID_SORT_OPTIONS,
156992
159628
  GoalsModal,
159629
+ GoalsMoneyApiError,
159630
+ GoalsMoneyClient,
156993
159631
  HEADER_CSS_PREFIX,
156994
159632
  HEADER_DEFAULT_CARD_COLORS,
156995
159633
  HEADER_DEFAULT_CONFIG_TEMPLATE,
@@ -157017,6 +159655,9 @@ var version = package_default.version || "0.0.0";
157017
159655
  MEASUREMENT_DOMAIN_CONFIG,
157018
159656
  MENU_SHOPPING_CSS_PREFIX,
157019
159657
  METRO_TILE_COLORS,
159658
+ MONEY_DASH,
159659
+ MONEY_REQUIRES_DEVICE_GRANULARITY,
159660
+ MONEY_VARIANCE_HEADER,
157020
159661
  MOTOR_SORT_OPTIONS,
157021
159662
  MYIO_CHART_PALETTE,
157022
159663
  MYIO_CHART_PALETTE_DARK,
@@ -157061,6 +159702,7 @@ var version = package_default.version || "0.0.0";
157061
159702
  POWER_LIMITS_DEVICE_TYPES,
157062
159703
  POWER_LIMITS_STATUS_CONFIG,
157063
159704
  POWER_LIMITS_TELEMETRY_TYPES,
159705
+ REPORT_MONEY_HEADER,
157064
159706
  SCHEDULE_HOLIDAY_CSS_PREFIX,
157065
159707
  SCHEDULE_IR_CSS_PREFIX,
157066
159708
  SCHEDULE_ON_OFF_CSS_PREFIX,
@@ -157102,6 +159744,9 @@ var version = package_default.version || "0.0.0";
157102
159744
  TELEMETRY_INFO_WATER_CATEGORY_CONFIG,
157103
159745
  TEMPERATURE_SORT_OPTIONS,
157104
159746
  TEMPERATURE_UNITS,
159747
+ TariffApiAdapter,
159748
+ TariffApiClient,
159749
+ TariffApiError,
157105
159750
  TelemetryGridController,
157106
159751
  TelemetryGridView,
157107
159752
  TempComparisonTooltip,
@@ -157109,6 +159754,10 @@ var version = package_default.version || "0.0.0";
157109
159754
  TempSensorSummaryTooltip,
157110
159755
  Temperature,
157111
159756
  UsersSummaryTooltip,
159757
+ VARIANCE_LABEL_ABOVE,
159758
+ VARIANCE_LABEL_BELOW,
159759
+ VARIANCE_LABEL_ONTARGET,
159760
+ VARIANCE_LABEL_WITHHELD,
157112
159761
  VirtualList,
157113
159762
  WAITING_STATUSES,
157114
159763
  WATER_DEVICE_CATEGORIES,
@@ -157134,15 +159783,20 @@ var version = package_default.version || "0.0.0";
157134
159783
  buildAnnotationServiceOrchestrator,
157135
159784
  buildAnnotationsCsv,
157136
159785
  buildAnnotationsExportFilename,
159786
+ buildBudgetHTML,
157137
159787
  buildByStatusFromDevices,
159788
+ buildCoverageHTML,
157138
159789
  buildCoverageWarningTextPtBR,
157139
159790
  buildDeviceGridEntityObject,
157140
159791
  buildEquipmentCategoryDataForTooltip,
157141
159792
  buildEquipmentCategorySummary,
159793
+ buildFinancialRowHTML,
157142
159794
  buildListItemsThingsboardByUniqueDatasource,
159795
+ buildMoneyVarianceHTML,
157143
159796
  buildMyioIngestionAuth,
157144
159797
  buildTemplateExport,
157145
159798
  buildTicketServiceOrchestrator,
159799
+ buildVerdictHTML,
157146
159800
  buildWaterReportCSV,
157147
159801
  buildWaterStoresCSV,
157148
159802
  calcDeltaPercent,
@@ -157163,6 +159817,7 @@ var version = package_default.version || "0.0.0";
157163
159817
  calculateStats,
157164
159818
  canEditAlarmRules,
157165
159819
  canModifyAnnotation,
159820
+ centsToDecimalString,
157166
159821
  checkCustomerCodeAvailable,
157167
159822
  clampTemperature,
157168
159823
  classify,
@@ -157174,9 +159829,14 @@ var version = package_default.version || "0.0.0";
157174
159829
  clearAllAuthCaches,
157175
159830
  clearFreshdeskTicketsOnTB,
157176
159831
  closeAnnotationsExportModal,
159832
+ collapseHoursToBands,
159833
+ collapseTreeToBands,
157177
159834
  collectDeviceOverrides,
157178
159835
  computeBaseGroupResidual,
159836
+ computeDeltaPct,
159837
+ computeMoneyVariance,
157179
159838
  connectionStatusIcons,
159839
+ coveragePercentLabel,
157180
159840
  createActionButton,
157181
159841
  createAlarmCardElement,
157182
159842
  createAlarmsNotificationsPanelComponent,
@@ -157189,6 +159849,7 @@ var version = package_default.version || "0.0.0";
157189
159849
  createConsumption7DaysChart,
157190
159850
  createConsumptionChartWidget,
157191
159851
  createConsumptionModal,
159852
+ createCoverageDeepLink,
157192
159853
  createCsvButton,
157193
159854
  createCustomerCardV1,
157194
159855
  createCustomerCardV2,
@@ -157208,16 +159869,19 @@ var version = package_default.version || "0.0.0";
157208
159869
  createEnergyPanelComponent,
157209
159870
  createErrorSpan,
157210
159871
  createExportButton,
159872
+ createFakeDeviceCategoryPort,
157211
159873
  createFancoilRemote,
157212
159874
  createFilterModalComponent,
157213
159875
  createFooterComponent,
157214
159876
  createFreshdeskTicket,
157215
159877
  createGoalsBarTooltip,
159878
+ createGoalsMoneyClient,
157216
159879
  createGranularitySelector,
157217
159880
  createGroupScheduleCard,
157218
159881
  createHeaderComponent,
157219
159882
  createHeaderDevicesGridComponent,
157220
159883
  createHeaderShoppingComponent,
159884
+ createHttpDeviceCategoryPort,
157221
159885
  createImgGallery,
157222
159886
  createInputDateRangePickerInsideDIV,
157223
159887
  createLibraryVersionChecker,
@@ -157227,6 +159891,7 @@ var version = package_default.version || "0.0.0";
157227
159891
  createMenuShoppingComponent,
157228
159892
  createModalFooter,
157229
159893
  createModalHeader,
159894
+ createMoneyVarianceColumn,
157230
159895
  createMyIOTheme,
157231
159896
  createNewTicketWizard,
157232
159897
  createOnOffDeviceModal,
@@ -157238,6 +159903,7 @@ var version = package_default.version || "0.0.0";
157238
159903
  createPdfButton,
157239
159904
  createPresetupGateway,
157240
159905
  createRealTimeClock,
159906
+ createReportMoneyColumn,
157241
159907
  createScheduleCard,
157242
159908
  createScheduleHoliday,
157243
159909
  createScheduleIR,
@@ -157247,6 +159913,8 @@ var version = package_default.version || "0.0.0";
157247
159913
  createSidebarMenu,
157248
159914
  createSolenoidControl,
157249
159915
  createSwitchControl,
159916
+ createTariffApiAdapter,
159917
+ createTariffApiClient,
157250
159918
  createTelemetryGridComponent,
157251
159919
  createTelemetryGridShoppingComponent,
157252
159920
  createTelemetryInfoShoppingComponent,
@@ -157257,6 +159925,8 @@ var version = package_default.version || "0.0.0";
157257
159925
  createWidgetController,
157258
159926
  createXlsButton,
157259
159927
  csvEscapeAnnotation,
159928
+ daysInMonthYear,
159929
+ decimalStringToCents,
157260
159930
  decodePayload,
157261
159931
  decodePayloadBase64Xor,
157262
159932
  deleteFreshdeskTicket,
@@ -157267,6 +159937,8 @@ var version = package_default.version || "0.0.0";
157267
159937
  detectSuperAdminHolding,
157268
159938
  detectSuperAdminMyio,
157269
159939
  determineInterval,
159940
+ deviceCategoryLabel,
159941
+ deviceCategoryToPanelTerm,
157270
159942
  deviceIconLabels,
157271
159943
  deviceIcons,
157272
159944
  deviceStatusIcons,
@@ -157275,6 +159947,9 @@ var version = package_default.version || "0.0.0";
157275
159947
  downloadAnnotationsTextFile,
157276
159948
  escapeAnnotationHtml,
157277
159949
  evaluateDeviceStatus,
159950
+ expandBandHours,
159951
+ expandBandToHourBucketsByYear,
159952
+ expandDayToHourBuckets,
157278
159953
  exportAnnotationsCsv,
157279
159954
  exportAnnotationsPdf,
157280
159955
  exportGridCsv,
@@ -157306,14 +159981,17 @@ var version = package_default.version || "0.0.0";
157306
159981
  formatAllInSameUnit,
157307
159982
  formatAllInSameWaterUnit,
157308
159983
  formatAnnotationRelativeTime,
159984
+ formatBRLDelta,
157309
159985
  formatCoverageRefPtBR,
157310
159986
  formatDashboardPercentage,
157311
159987
  formatDateForInput,
157312
159988
  formatDateToYMD,
157313
159989
  formatDateWithTimezoneOffset,
159990
+ formatDeltaPct,
157314
159991
  formatDuration,
157315
159992
  formatEnergy,
157316
159993
  formatHours,
159994
+ formatMoneyBRL,
157317
159995
  formatNumberReadable,
157318
159996
  formatPower,
157319
159997
  formatRelativeTime,
@@ -157402,8 +160080,10 @@ var version = package_default.version || "0.0.0";
157402
160080
  injectAmbienteGroupModalStyles,
157403
160081
  injectAmbienteModalStyles,
157404
160082
  injectBASDashboardStyles,
160083
+ injectCoverageStyles,
157405
160084
  injectCustomerCardV1Styles,
157406
160085
  injectCustomerCardV2Styles,
160086
+ injectDeviceCategoryStyles,
157407
160087
  injectDeviceGridV6Styles,
157408
160088
  injectDeviceOperationalCardGridStyles,
157409
160089
  injectDeviceOperationalCardStyles,
@@ -157443,6 +160123,8 @@ var version = package_default.version || "0.0.0";
157443
160123
  isEquipmentDevice,
157444
160124
  isHydrometerDevice,
157445
160125
  isInRange,
160126
+ isLeapYear,
160127
+ isMoneyColumnConfident,
157446
160128
  isOnOffDeviceProfile,
157447
160129
  isSolenoidDevice,
157448
160130
  isStoreDevice,
@@ -157463,9 +160145,12 @@ var version = package_default.version || "0.0.0";
157463
160145
  metasGuideDeriveTotal,
157464
160146
  myioExportData,
157465
160147
  nfdNormalizeAnnotationSearch,
160148
+ normalizeBudgetBlock,
157466
160149
  normalizeConnectionStatus,
157467
160150
  normalizeDeviceOverrideId,
160151
+ normalizeMoneyBlock,
157468
160152
  normalizeOnOffStatus,
160153
+ normalizePriceString,
157469
160154
  normalizeProfile,
157470
160155
  normalizeRecipients,
157471
160156
  numbers,
@@ -157484,6 +160169,7 @@ var version = package_default.version || "0.0.0";
157484
160169
  openDashboardPopupSettings,
157485
160170
  openDashboardPopupWaterTank,
157486
160171
  openDemandModal,
160172
+ openDeviceCategoryPanel,
157487
160173
  openDeviceProfileModal,
157488
160174
  openGCDRSyncModal,
157489
160175
  openGenericModal,
@@ -157507,6 +160193,10 @@ var version = package_default.version || "0.0.0";
157507
160193
  openUpsellModal,
157508
160194
  openUserManagementModal,
157509
160195
  openWelcomeModal,
160196
+ overlayWithholdsVerdict,
160197
+ panelCategoryToWire,
160198
+ panelDomainToWire,
160199
+ panelTermToDeviceCategory,
157510
160200
  parseClassificationEntities,
157511
160201
  parseInputDateToDate,
157512
160202
  parseLogAnnotations,
@@ -157527,17 +160217,22 @@ var version = package_default.version || "0.0.0";
157527
160217
  removeSchedulingSharedStyles,
157528
160218
  renderAlarmCard,
157529
160219
  renderAnnotationItemCard,
160220
+ renderBudgetView,
157530
160221
  renderCardAmbienteV6,
157531
160222
  renderCardComponentHeadOffice,
157532
160223
  renderCardComponentV5,
157533
160224
  renderCardComponentV6,
157534
160225
  renderCardComponentV6Alias,
157535
160226
  renderCardV5,
160227
+ renderCoverageView,
157536
160228
  renderDashboardDowntimeList,
157537
160229
  renderDeviceGridList,
157538
160230
  renderDualLineChart,
160231
+ renderFinancialIndicators,
160232
+ renderGatedMoney,
157539
160233
  renderKPICards,
157540
160234
  renderLineChart,
160235
+ renderMoneyVariance,
157541
160236
  renderOnOffTimelineChart,
157542
160237
  renderSeverityBarChart,
157543
160238
  renderStateDonutChart,
@@ -157545,12 +160240,17 @@ var version = package_default.version || "0.0.0";
157545
160240
  renderTrendChart,
157546
160241
  reopenFreshdeskTicket,
157547
160242
  resolveActiveProfile,
160243
+ resolveBudget,
157548
160244
  resolveCategory,
157549
160245
  resolveClassification,
160246
+ resolveGoalAmount,
157550
160247
  resolveGroup,
160248
+ resolveMoneyRollout,
160249
+ resolveMoneyRowValues,
157551
160250
  resolvePercentDecimals,
157552
160251
  resolveSubcategory,
157553
160252
  resolveWithFallback,
160253
+ routeMoneyRender,
157554
160254
  schedCreateDateInput,
157555
160255
  schedCreateNumberInput,
157556
160256
  schedCreateSelect,
@@ -157562,10 +160262,13 @@ var version = package_default.version || "0.0.0";
157562
160262
  setActiveProfile,
157563
160263
  shouldFlashIcon,
157564
160264
  shouldVirtualizeAnnotationList,
160265
+ signOf,
157565
160266
  slugifyCustomerName,
157566
160267
  sortAnnotationGroups,
157567
160268
  sortDeviceGridDevices,
157568
160269
  strings,
160270
+ subtractDecimals,
160271
+ sumMoneyDecimals,
157569
160272
  telemetryInfoFormatEnergy,
157570
160273
  telemetryInfoFormatPercentage,
157571
160274
  telemetryInfoFormatWater,
@@ -157583,6 +160286,8 @@ var version = package_default.version || "0.0.0";
157583
160286
  validateTree,
157584
160287
  version,
157585
160288
  waterDeviceStatusIcons,
160289
+ wireCategoryToPanel,
160290
+ wireDomainToPanel,
157586
160291
  writeFreshdeskSyncedAtToTB,
157587
160292
  writeFreshdeskTicketsToTB
157588
160293
  });