@zaip-ai/cli 3.8.0 → 4.0.0

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.
Files changed (244) hide show
  1. package/README.md +5 -5
  2. package/dist/services/adapters.d.ts +1 -1
  3. package/dist/services/adapters.js +1 -1
  4. package/dist/services/agent-adapters.js +1 -1
  5. package/dist/services/agent-adapters.js.map +1 -1
  6. package/dist/services/backup-gitignore.d.ts +9 -0
  7. package/dist/services/backup-gitignore.d.ts.map +1 -0
  8. package/dist/services/backup-gitignore.js +66 -0
  9. package/dist/services/backup-gitignore.js.map +1 -0
  10. package/dist/services/backup-gitignore.test.d.ts +2 -0
  11. package/dist/services/backup-gitignore.test.d.ts.map +1 -0
  12. package/dist/services/backup-gitignore.test.js +27 -0
  13. package/dist/services/backup-gitignore.test.js.map +1 -0
  14. package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
  15. package/dist/services/codex-conversation-lifecycle-hook.js +15 -7
  16. package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
  17. package/dist/services/install-agents-template.d.ts.map +1 -1
  18. package/dist/services/install-agents-template.js +43 -127
  19. package/dist/services/install-agents-template.js.map +1 -1
  20. package/dist/services/install-template-policy.d.ts.map +1 -1
  21. package/dist/services/install-template-policy.js +2 -0
  22. package/dist/services/install-template-policy.js.map +1 -1
  23. package/dist/services/legacy-records-migration.d.ts +15 -0
  24. package/dist/services/legacy-records-migration.d.ts.map +1 -0
  25. package/dist/services/legacy-records-migration.js +109 -0
  26. package/dist/services/legacy-records-migration.js.map +1 -0
  27. package/dist/services/local-state-migration.d.ts +8 -1
  28. package/dist/services/local-state-migration.d.ts.map +1 -1
  29. package/dist/services/local-state-migration.js +67 -32
  30. package/dist/services/local-state-migration.js.map +1 -1
  31. package/dist/services/logger.d.ts.map +1 -1
  32. package/dist/services/logger.js +2 -0
  33. package/dist/services/logger.js.map +1 -1
  34. package/dist/services/project-model-migration.d.ts +3 -0
  35. package/dist/services/project-model-migration.d.ts.map +1 -0
  36. package/dist/services/project-model-migration.js +163 -0
  37. package/dist/services/project-model-migration.js.map +1 -0
  38. package/dist/services/project-model-migration.test.d.ts +2 -0
  39. package/dist/services/project-model-migration.test.d.ts.map +1 -0
  40. package/dist/services/project-model-migration.test.js +62 -0
  41. package/dist/services/project-model-migration.test.js.map +1 -0
  42. package/dist/services/template-manifest.d.ts.map +1 -1
  43. package/dist/services/template-manifest.js +2 -0
  44. package/dist/services/template-manifest.js.map +1 -1
  45. package/dist/services/transactional-files.d.ts +1 -0
  46. package/dist/services/transactional-files.d.ts.map +1 -1
  47. package/dist/services/transactional-files.js +3 -0
  48. package/dist/services/transactional-files.js.map +1 -1
  49. package/dist/types.d.ts +1 -0
  50. package/dist/types.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/templates/.agents/README-ENG.md +88 -32
  53. package/templates/.agents/README.md +57 -30
  54. package/templates/.agents/config/agents/discovery.json +1 -1
  55. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -1
  56. package/templates/.agents/config/project.yaml +21 -7
  57. package/templates/.agents/dashboard/.zaip-managed.json +184 -112
  58. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-D2PL4UOu.js → CostCalculationGuidePage-f4ZGJRg3.js} +1 -1
  59. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-CocoP9CA.js → DashboardHelpDialog-DlN5EMmD.js} +238 -124
  60. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-D255_hNf.js → PricingSettingsDialog-v9zGRTlk.js} +1 -1
  61. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BRdoeTWk.js +3 -0
  62. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-BTFSLQ69.js → TraceabilityWorkspace-B_5u4Xnp.js} +1 -1
  63. package/templates/.agents/dashboard/apps/frontend/dist/assets/{cost-monitor-BsMfGfQi.css → cost-monitor-C2AFYBu_.css} +1 -1
  64. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-D7PrDZy9.js +68 -0
  65. package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-058lag33.js → dashboard-localization-CF7B1Hjn.js} +88 -32
  66. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DeFPiN32.js → icons-vendor-Cv3WBFIF.js} +1 -1
  67. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4rLXrz_.js +2 -0
  68. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-BIWcbEAc.js → react-vendor-mHgsmxb6.js} +1 -1
  69. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-fZZL0ohD.js → traceability-B0CPyEVq.js} +1 -1
  70. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  71. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  72. package/templates/.agents/dashboard/apps/frontend/src/AppDashboardContent.tsx +11 -1
  73. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +3 -2
  74. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingPanels.tsx +1 -1
  75. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +1 -1
  76. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +9 -13
  77. package/templates/.agents/dashboard/apps/frontend/src/cost-monitor-population.test.tsx +4 -2
  78. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +9 -5
  79. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-settings.ts +3 -2
  80. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +2 -2
  81. package/templates/.agents/dashboard/apps/frontend/src/dashboard-identity.test.ts +3 -3
  82. package/templates/.agents/dashboard/apps/frontend/src/dashboard-identity.ts +1 -1
  83. package/templates/.agents/dashboard/apps/frontend/src/dashboard-localization.test.ts +2 -1
  84. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +40 -3
  85. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +12 -1
  86. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +19 -0
  87. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +1 -1
  88. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +3 -1
  89. package/templates/.agents/dashboard/apps/frontend/src/use-dashboard-app-model.tsx +14 -23
  90. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  91. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +3 -3
  92. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +2 -3
  93. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionTable.tsx +2 -6
  94. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.period-values.test.tsx +66 -0
  95. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.tsx +29 -34
  96. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.embedded.test.ts +11 -0
  97. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +1 -13
  98. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostPeriodAverageCards.test.tsx +23 -0
  99. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostPeriodAverageCards.tsx +2 -1
  100. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +6 -11
  101. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +6 -6
  102. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +10 -12
  103. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-group-page-totals.ts +23 -0
  104. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-group-period-values.tsx +34 -0
  105. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-embedded.css +0 -7
  106. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +4 -9
  107. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-presentation.ts +2 -3
  108. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  109. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.tsx +2 -12
  110. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +2 -2
  111. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +1 -6
  112. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +34 -36
  113. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewCharts.tsx +12 -3
  114. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +12 -14
  115. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +4 -4
  116. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +23 -4
  117. package/templates/.agents/dashboard/modules/operations/src/react/OverviewCostKpi.tsx +30 -2
  118. package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +2 -13
  119. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.priority.test.ts +8 -1
  120. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +8 -16
  121. package/templates/.agents/dashboard/modules/operations/src/react/WorkColumnState.tsx +34 -0
  122. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +1 -1
  123. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +1 -1
  124. package/templates/.agents/dashboard/modules/operations/src/react/operations-average-unmeasured.test.ts +10 -0
  125. package/templates/.agents/dashboard/modules/operations/src/react/operations-content-visibility.ts +3 -0
  126. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +4 -3
  127. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +12 -1
  128. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +1 -0
  129. package/templates/.agents/dashboard/modules/operations/src/react/operations-skeleton.css +80 -0
  130. package/templates/.agents/dashboard/modules/operations/src/react/operations.css +2 -11
  131. package/templates/.agents/dashboard/modules/operations/src/react/overview-cost-card.test.tsx +36 -0
  132. package/templates/.agents/dashboard/modules/operations/src/react/overview-metric-format.test.ts +11 -0
  133. package/templates/.agents/dashboard/modules/operations/src/react/overview-metric-format.ts +28 -3
  134. package/templates/.agents/dashboard/modules/operations/src/react/overview-metric-stats.ts +44 -0
  135. package/templates/.agents/dashboard/modules/operations/src/react/work-card-label.ts +5 -0
  136. package/templates/.agents/dashboard/modules/operations/src/react/work-column-state.ts +11 -0
  137. package/templates/.agents/dashboard/modules/operations/src/react/work-item-filter.ts +13 -0
  138. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  139. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  140. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  141. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  142. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +157 -157
  143. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +153 -153
  144. package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
  145. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +159 -159
  146. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +31 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +74 -1
  148. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +1 -1
  149. package/templates/.agents/dashboard/packages/local-runtime/src/server-identity.test.ts +1 -1
  150. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.test.ts +25 -3
  151. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +51 -16
  152. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +1 -1
  153. package/templates/.agents/dashboard/packages/local-runtime/src/server-ui-security.ts +37 -0
  154. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +1 -1
  155. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +11 -33
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-rebuild.ts +12 -13
  157. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +71 -1
  158. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +141 -25
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-worktree-sources.test.ts +60 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-rebuild-fingerprint-options.ts +13 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +26 -2
  162. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +5 -0
  163. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  164. package/templates/.agents/dashboard/packages/ui/src/DashboardHeader.test.mjs +5 -0
  165. package/templates/.agents/dashboard/packages/ui/src/DashboardHeader.tsx +4 -4
  166. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  167. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -0
  168. package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +4 -4
  169. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +1 -1
  170. package/templates/.agents/dashboard/tests/e2e/specs/performance-work-row-identity.ts +6 -0
  171. package/templates/.agents/design/.zaip-managed.json +13 -9
  172. package/templates/.agents/design/apps/catalog/dist/assets/index-DkpQYoyC.js +72 -0
  173. package/templates/.agents/design/apps/catalog/dist/index.html +1 -1
  174. package/templates/.agents/design/apps/catalog/package.json +1 -1
  175. package/templates/.agents/design/apps/catalog/src/components/CostPopulationSpecimen.test.tsx +8 -4
  176. package/templates/.agents/design/apps/catalog/src/components/CostPopulationSpecimen.tsx +10 -3
  177. package/templates/.agents/design/apps/catalog/src/components/DashboardFiltersSpecimen.tsx +0 -15
  178. package/templates/.agents/design/apps/catalog/src/pages/ComponentsPage.tsx +3 -3
  179. package/templates/.agents/design/apps/catalog/src/stories/DashboardFilters.stories.tsx +0 -1
  180. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  181. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  182. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
  183. package/templates/.agents/requirements/comparative-audit.md +1 -1
  184. package/templates/.agents/requirements/context-engine.md +1 -1
  185. package/templates/.agents/requirements/cost-observability.md +1 -1
  186. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  187. package/templates/.agents/requirements/data-export.md +1 -1
  188. package/templates/.agents/requirements/design-system.md +1 -1
  189. package/templates/.agents/requirements/execution-engine.md +1 -1
  190. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  191. package/templates/.agents/requirements/model-orchestration.md +1 -1
  192. package/templates/.agents/requirements/public-distribution.md +1 -1
  193. package/templates/.agents/requirements/software-architecture.md +1 -1
  194. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  195. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  196. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  197. package/templates/.agents/rules/autonomy-decision.md +2 -2
  198. package/templates/.agents/rules/workflow.md +1 -1
  199. package/templates/.agents/runtime/VERSION +1 -1
  200. package/templates/.agents/runtime/bin/zaip.mjs +48 -39
  201. package/templates/.agents/runtime/checksums.sha256 +5 -5
  202. package/templates/.agents/runtime/manifest.json +1 -1
  203. package/templates/.agents/runtime/provenance.json +4 -4
  204. package/templates/.agents/runtime/sbom.cdx.json +1 -1
  205. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/SKILL.md +7 -7
  206. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.mjs +32 -7
  207. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.test.mjs +134 -1
  208. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-lease.mjs +57 -0
  209. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/scheduled-ready-queue-contract.test.mjs +9 -6
  210. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
  211. package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +2 -2
  212. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +18 -6
  213. package/templates/.agents/skills/zaip-skill-instalar-agendamento/SKILL.md +2 -2
  214. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +6 -0
  215. package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +47 -5
  216. package/templates/.agents/zaip-aios/README.md +4 -4
  217. package/templates/.agents/zaip-aios/VERSION +1 -1
  218. package/templates/.agents/zaip-aios/guides/01-primeiros-passos.md +4 -3
  219. package/templates/.agents/zaip-aios/guides/03-tasks.md +5 -4
  220. package/templates/.agents/zaip-aios/guides/04-specs.md +4 -1
  221. package/templates/.agents/zaip-aios/guides/05-prds.md +7 -3
  222. package/templates/.agents/zaip-aios/guides/06-execucao.md +9 -4
  223. package/templates/.agents/zaip-aios/guides/09-agentes-e-squads.md +9 -5
  224. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +10 -2
  225. package/templates/.agents/zaip-aios/guides/12-instalacao-e-upgrade.md +10 -5
  226. package/templates/.agents/zaip-aios/guides/13-boas-praticas.md +12 -1
  227. package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +3 -1
  228. package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +12 -7
  229. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +1 -1
  230. package/templates/.agents/zaip-aios/guides/18-catalogo-de-agentes.md +4 -0
  231. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +19 -15
  232. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +12 -8
  233. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +4 -3
  234. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +9 -7
  235. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +38 -22
  236. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +1 -1
  237. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +14 -5
  238. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +6 -0
  239. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +129 -11
  240. package/templates/template-manifest.json +270 -186
  241. package/templates/tsconfig.base.json +22 -0
  242. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-0f_x2TzJ.js +0 -3
  243. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-zhY2JoLc.js +0 -68
  244. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Dh95Yodm.js +0 -2
@@ -1,4 +1,5 @@
1
- import { formatSummaryMetricValue } from "./overview-metric-format.js";
1
+ import { formatSummaryMetricValue, unmeasuredZeroDescription } from "./overview-metric-format.js";
2
+ import { overviewMetricStats } from "./overview-metric-stats.js";
2
3
 
3
4
  export { formatSummaryMetricValue } from "./overview-metric-format.js";
4
5
 
@@ -104,6 +105,7 @@ export function OperationsOverview({
104
105
  tasks = data.tasks,
105
106
  factualCostTotal,
106
107
  factualCostFact,
108
+ costLoading = false,
107
109
  resolveRunCost,
108
110
  includeLegacyEffort: _includeLegacyEffort = false,
109
111
  }: {
@@ -141,6 +143,7 @@ export function OperationsOverview({
141
143
  factualCostTotal?: number | null | undefined;
142
144
  /** Measurement completeness for the matching Cost Monitor snapshot. */
143
145
  factualCostFact?: FactualCostFact | undefined;
146
+ costLoading?: boolean | undefined;
144
147
  resolveRunCost?: ((runId: string) => number | null) | undefined;
145
148
  includeLegacyEffort?: boolean;
146
149
  }) {
@@ -392,39 +395,23 @@ export function OperationsOverview({
392
395
  ? (overviewMetrics?.latest_task_created_at ?? null)
393
396
  : findLatestCreatedAt(tasks)
394
397
  : (selectedMacroAggregate?.latest_created_at ?? findLatestCreatedAt(selectedMacros));
395
- const metricStats = [
396
- [
397
- text.averageTime,
398
- formatSummaryMetricValue(
399
- averageDuration,
400
- selectedCount,
401
- (value) => formatDurationMs(value, locale),
402
- locale,
403
- ),
404
- ],
405
- [
406
- text.averageTokens,
407
- formatSummaryMetricValue(
408
- averageTokens,
409
- selectedCount,
410
- (value) => formatCompactNumber(value, locale),
411
- locale,
412
- ),
413
- ],
414
- [
415
- factualCostAverageLabel(
416
- overviewView === "tasks" ? factualCostFact : undefined,
417
- selectedCount,
418
- locale,
419
- ),
420
- formatSummaryMetricValue(
421
- averageCost,
422
- selectedCount,
423
- (value) => formatCurrency(value, currency, locale),
424
- locale,
425
- ),
426
- ],
427
- ] as const;
398
+ const metricStats = overviewMetricStats({
399
+ averageCost,
400
+ averageDuration,
401
+ averageTokens,
402
+ costLabel: factualCostAverageLabel(
403
+ overviewView === "tasks" ? factualCostFact : undefined,
404
+ selectedCount,
405
+ locale,
406
+ ),
407
+ count: selectedCount,
408
+ formatCost: (value) => formatCurrency(value, currency, locale),
409
+ formatDuration: (value) => formatDurationMs(value, locale),
410
+ formatTokens: (value) => formatCompactNumber(value, locale),
411
+ locale,
412
+ timeLabel: text.averageTime,
413
+ tokensLabel: text.averageTokens,
414
+ });
428
415
  const aggregateTopTasks: EntityMetric[] = (overviewMetrics?.top_tasks ?? []).map((metric) => ({
429
416
  id: metric.task_id,
430
417
  title: tasks.find((task) => task.id === metric.task_id)?.title ?? metric.task_id,
@@ -605,6 +592,12 @@ export function OperationsOverview({
605
592
  metricPopulationCount,
606
593
  (value) => formatDurationMs(value, locale),
607
594
  locale,
595
+ true,
596
+ )}
597
+ description={unmeasuredZeroDescription(
598
+ totalDurationMetric,
599
+ metricPopulationCount,
600
+ locale,
608
601
  )}
609
602
  />{" "}
610
603
  <Kpi
@@ -615,8 +608,8 @@ export function OperationsOverview({
615
608
  ? `0 / ${metricUnit} (0 / hora)`
616
609
  : `0 / ${metricUnit} (0 / hour)`
617
610
  : locale === "pt-BR"
618
- ? "Tokens por tarefa e por hora indisponíveis"
619
- : "Tokens per task and hour unavailable"
611
+ ? "Sem tokens medidos para calcular médias"
612
+ : "No measured tokens for averages"
620
613
  : locale === "pt-BR"
621
614
  ? `${formatCompactNumber(tokensPerTask, locale)} / ${metricUnit} (${formatCompactNumber(tokensPerHour, locale, 0)} / hora)`
622
615
  : `${formatCompactNumber(tokensPerTask, locale)} / ${metricUnit} (${formatCompactNumber(tokensPerHour, locale, 0)} / hour)`
@@ -635,9 +628,12 @@ export function OperationsOverview({
635
628
  metricPopulationCount,
636
629
  (value) => formatCompactNumber(value, locale),
637
630
  locale,
631
+ true,
638
632
  )}
633
+ description={unmeasuredZeroDescription(totalTokensMetric, metricPopulationCount, locale)}
639
634
  />{" "}
640
635
  <OverviewCostKpi
636
+ loading={costLoading && overviewView === "tasks"}
641
637
  fact={overviewView === "tasks" ? factualCostFact : undefined}
642
638
  unit={metricUnit}
643
639
  locale={locale}
@@ -658,6 +654,7 @@ export function OperationsOverview({
658
654
  <div className="operations-chart-grid">
659
655
  {" "}
660
656
  <OperationsOverviewCharts
657
+ costLoading={costLoading && overviewView === "tasks"}
661
658
  locale={locale}
662
659
  copy={copy}
663
660
  text={text}
@@ -685,6 +682,7 @@ export function OperationsOverview({
685
682
  }}
686
683
  />
687
684
  <OperationsOverviewTopPanel
685
+ costLoading={costLoading && overviewView === "tasks"}
688
686
  copy={{ empty: copy.emptyTop, hint: topHint, title: copy.topTitle }}
689
687
  currency={currency}
690
688
  locale={locale}
@@ -5,6 +5,7 @@ import type { OverviewClassification } from "./operations-page-contract.js";
5
5
  import type { OperationsLocale } from "./status-labels.js";
6
6
  import type { WorkStage } from "./work-board-model.js";
7
7
  export function OperationsOverviewCharts({
8
+ costLoading = false,
8
9
  locale,
9
10
  copy,
10
11
  text,
@@ -17,6 +18,7 @@ export function OperationsOverviewCharts({
17
18
  onStatusSelect,
18
19
  onClassificationSelect,
19
20
  }: {
21
+ costLoading?: boolean;
20
22
  locale: OperationsLocale;
21
23
  copy: ReturnType<typeof overviewCopy>;
22
24
  text: ReturnType<typeof overviewText>;
@@ -151,9 +153,16 @@ export function OperationsOverviewCharts({
151
153
  <PanelHeader hint={copy.averageHint} title={copy.averageTitle} />{" "}
152
154
  <div className="operations-type-summary">
153
155
  {" "}
154
- {metricStats.map(([label, value]) => (
155
- <div key={label}>
156
- <span>{label}</span> <strong>{value}</strong>{" "}
156
+ {metricStats.map(([label, value], index) => (
157
+ <div aria-busy={(costLoading && index === 2) || undefined} key={label}>
158
+ <span>{label}</span>{" "}
159
+ <strong>
160
+ {costLoading && index === 2 ? (
161
+ <i className="operations-skeleton-line" aria-hidden="true" />
162
+ ) : (
163
+ value
164
+ )}
165
+ </strong>{" "}
157
166
  </div>
158
167
  ))}{" "}
159
168
  </div>{" "}
@@ -10,13 +10,10 @@ import type { OperationsLocale } from "./status-labels.js";
10
10
  type OverviewView = "tasks" | "specs" | "prds";
11
11
 
12
12
  const duration = (value: number | null, locale: OperationsLocale) =>
13
- value === null
14
- ? locale === "pt-BR"
15
- ? "Indisponível"
16
- : "Unavailable"
17
- : formatDurationMs(value, locale);
13
+ value === null ? "0" : formatDurationMs(value, locale);
18
14
 
19
15
  export function OperationsOverviewTopPanel({
16
+ costLoading = false,
20
17
  copy,
21
18
  currency,
22
19
  locale,
@@ -28,6 +25,7 @@ export function OperationsOverviewTopPanel({
28
25
  topEntities,
29
26
  view,
30
27
  }: {
28
+ costLoading?: boolean;
31
29
  copy: { empty: string; hint: string; title: string };
32
30
  currency: "BRL" | "USD";
33
31
  locale: OperationsLocale;
@@ -108,16 +106,16 @@ export function OperationsOverviewTopPanel({
108
106
  </div>
109
107
  <div>
110
108
  <span>{text.topFiveCost}</span>
111
- <strong>
112
- {topCost === null
113
- ? duration(null, locale)
114
- : formatCurrency(topCost, currency, locale)}
109
+ <strong aria-busy={costLoading || undefined}>
110
+ {costLoading ? (
111
+ <i className="operations-skeleton-line" aria-hidden="true" />
112
+ ) : topCost === null ? (
113
+ duration(null, locale)
114
+ ) : (
115
+ formatCurrency(topCost, currency, locale)
116
+ )}
115
117
  </strong>
116
- <small>
117
- {summary.tokens === null
118
- ? text.tokensUnavailable
119
- : `${formatCompactNumber(summary.tokens, locale)} tokens`}
120
- </small>
118
+ <small>{`${formatCompactNumber(summary.tokens, locale)} tokens`}</small>
121
119
  </div>
122
120
  </div>
123
121
  </>
@@ -13,6 +13,7 @@ import {
13
13
  import { OperationsMainContent } from "./OperationsMainContent.js";
14
14
  import { OperationsViewTabs } from "./OperationsPageNavigation.js";
15
15
  import { OperationsPageLoading, OperationsPageOverview } from "./OperationsPageOverview.js";
16
+ import { shouldRenderOperationsContent } from "./operations-content-visibility.js";
16
17
  import {
17
18
  type OperationsPageProps,
18
19
  resolveOperationsPeriodControl,
@@ -77,7 +78,7 @@ export function OperationsPage({
77
78
  capabilityNavigation,
78
79
  currency,
79
80
  data,
80
- dashboardTitle = "Dashboard Zaip AIOS",
81
+ dashboardTitle = "Dashboard ZAIP AIOS",
81
82
  lastUpdatedAt = data.summary.latest_token_collected_at ?? null,
82
83
  loading,
83
84
  onLoadingChange,
@@ -110,6 +111,7 @@ export function OperationsPage({
110
111
  settings,
111
112
  factualCostTotal,
112
113
  factualCostFact,
114
+ costLoading,
113
115
  factualCostPeriods,
114
116
  resolveRunCost,
115
117
  }: OperationsPageProps) {
@@ -662,6 +664,7 @@ export function OperationsPage({
662
664
  query={query}
663
665
  factualCostTotal={factualCostTotal}
664
666
  factualCostFact={factualCostFact}
667
+ costLoading={costLoading}
665
668
  resolveRunCost={resolveRunCost}
666
669
  runs={runs}
667
670
  searchInputRef={searchInputRef}
@@ -758,6 +761,3 @@ export function OperationsPage({
758
761
  }
759
762
 
760
763
  /** Loading is never a valid zero-valued Operations projection. */
761
- export function shouldRenderOperationsContent(loading: boolean): boolean {
762
- return !loading;
763
- }
@@ -20,6 +20,7 @@ export function OperationsPageOverview({
20
20
  defaultDateRange,
21
21
  factualCostTotal,
22
22
  factualCostFact,
23
+ costLoading,
23
24
  includeLegacyEffort,
24
25
  locale,
25
26
  onOpenTask,
@@ -49,6 +50,7 @@ export function OperationsPageOverview({
49
50
  defaultDateRange: OperationDateRange;
50
51
  factualCostTotal: OverviewProps["factualCostTotal"];
51
52
  factualCostFact: OverviewProps["factualCostFact"];
53
+ costLoading: OverviewProps["costLoading"];
52
54
  includeLegacyEffort: boolean;
53
55
  locale: OperationsLocale;
54
56
  onOpenTask: OverviewProps["onOpenTask"];
@@ -77,6 +79,7 @@ export function OperationsPageOverview({
77
79
  currency={currency}
78
80
  factualCostTotal={factualCostTotal}
79
81
  factualCostFact={factualCostFact}
82
+ costLoading={costLoading}
80
83
  datePreset={datePreset}
81
84
  dateRange={dateRange}
82
85
  locale={locale}
@@ -163,13 +166,29 @@ export function OperationsPageLoading({ locale }: { locale: OperationsLocale })
163
166
  return (
164
167
  <section
165
168
  aria-busy="true"
166
- aria-live="polite"
169
+ aria-label={
170
+ locale === "pt-BR" ? "Carregando o período selecionado" : "Loading selected period"
171
+ }
167
172
  className="operations-content-skeleton"
168
173
  role="status"
169
174
  >
170
- <span>
171
- {locale === "pt-BR" ? "Carregando o período selecionado…" : "Loading selected period…"}
172
- </span>
175
+ <div className="operations-skeleton-kpis" aria-hidden="true">
176
+ {["tasks", "specs", "prds", "duration", "tokens", "cost"].map((metric) => (
177
+ <article className="operations-skeleton-block" key={metric}>
178
+ <i className="operations-skeleton-line is-short" />
179
+ <i className="operations-skeleton-line is-value" />
180
+ <i className="operations-skeleton-line" />
181
+ </article>
182
+ ))}
183
+ </div>
184
+ <div className="operations-skeleton-panels" aria-hidden="true">
185
+ {["status", "classification", "average", "top"].map((panel) => (
186
+ <article className="operations-skeleton-block" key={panel}>
187
+ <i className="operations-skeleton-line is-short" />
188
+ <i className="operations-skeleton-chart" />
189
+ </article>
190
+ ))}
191
+ </div>
173
192
  </section>
174
193
  );
175
194
  }
@@ -5,7 +5,11 @@ import {
5
5
  overviewCostRateHint,
6
6
  } from "./OperationsOverview.helpers.js";
7
7
  import type { FactualCostFact } from "./operations-page-contract.js";
8
- import { formatRoundedCurrency, formatSummaryMetricValue } from "./overview-metric-format.js";
8
+ import {
9
+ formatRoundedCurrency,
10
+ formatSummaryMetricValue,
11
+ unmeasuredZeroDescription,
12
+ } from "./overview-metric-format.js";
9
13
  import type { OperationsLocale } from "./status-labels.js";
10
14
 
11
15
  /** Compact presentation, preserving completeness and matching population semantics. */
@@ -19,6 +23,7 @@ export function OverviewCostKpi({
19
23
  perHour,
20
24
  total,
21
25
  label,
26
+ loading = false,
22
27
  }: {
23
28
  fact: FactualCostFact | undefined;
24
29
  unit: string;
@@ -29,17 +34,40 @@ export function OverviewCostKpi({
29
34
  perHour: number | null;
30
35
  total: number | null;
31
36
  label?: string | undefined;
37
+ loading?: boolean;
32
38
  }) {
39
+ if (loading)
40
+ return (
41
+ <article
42
+ aria-busy="true"
43
+ aria-label={locale === "pt-BR" ? "Carregando custo estimado" : "Loading estimated cost"}
44
+ className="operations-kpi"
45
+ role="status"
46
+ >
47
+ <span>{label ?? (locale === "pt-BR" ? "Custo Estimado" : "Estimated Cost")}</span>
48
+ <strong>
49
+ <i className="operations-skeleton-line is-value" aria-hidden="true" />
50
+ </strong>
51
+ <small>
52
+ <i className="operations-skeleton-line" aria-hidden="true" />
53
+ </small>
54
+ </article>
55
+ );
33
56
  return (
34
57
  <Kpi
35
58
  hint={overviewCostRateHint(fact, unit, locale, currency, count, perItem, perHour)}
36
- description={factualCostMetricHint(fact, unit, locale) ?? undefined}
59
+ description={
60
+ unmeasuredZeroDescription(total, count, locale) ??
61
+ factualCostMetricHint(fact, unit, locale) ??
62
+ undefined
63
+ }
37
64
  label={label ?? factualCostMetricLabel(fact, locale)}
38
65
  value={formatSummaryMetricValue(
39
66
  total,
40
67
  count,
41
68
  (value) => formatRoundedCurrency(value, currency, locale),
42
69
  locale,
70
+ true,
43
71
  )}
44
72
  />
45
73
  );
@@ -69,23 +69,12 @@ export function TaskDetailsDialog({
69
69
  label={copy.duration}
70
70
  value={formatOperationDuration(telemetry.durationMs, locale)}
71
71
  />
72
- <Detail
73
- label="Tokens"
74
- value={
75
- telemetry.tokens === null
76
- ? locale === "pt-BR"
77
- ? "Indisponível"
78
- : "Unavailable"
79
- : formatCompactNumber(telemetry.tokens, locale)
80
- }
81
- />
72
+ <Detail label="Tokens" value={formatCompactNumber(telemetry.tokens, locale)} />
82
73
  <Detail
83
74
  label={locale === "pt-BR" ? "Custo" : "Cost"}
84
75
  value={
85
76
  telemetry.costBrl === null
86
- ? locale === "pt-BR"
87
- ? "Indisponível"
88
- : "Unavailable"
77
+ ? "0"
89
78
  : new Intl.NumberFormat(locale, { style: "currency", currency: "BRL" }).format(
90
79
  telemetry.costBrl,
91
80
  )
@@ -1,7 +1,14 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { mergeManualWorkOrder, sortWorkItems } from "./UnifiedWorkBoard.js";
2
+ import { mergeManualWorkOrder, sortWorkItems, workColumnState } from "./UnifiedWorkBoard.js";
3
3
 
4
4
  describe("UnifiedWorkBoard priority path", () => {
5
+ it("does not claim an unrequested or in-flight column is empty", () => {
6
+ expect(workColumnState(0, 0, undefined)).toBe("loading");
7
+ expect(workColumnState(0, 2, { loading: false, error: null })).toBe("loading");
8
+ expect(workColumnState(0, 0, { loading: true, error: null })).toBe("loading");
9
+ expect(workColumnState(0, 0, { loading: false, error: null })).toBe("empty");
10
+ expect(workColumnState(0, 0, { loading: false, error: "falhou" })).toBe("error");
11
+ });
5
12
  const item = (id: string, rank: number | null, createdAt = "2026-08-04T10:00:00.000Z") =>
6
13
  ({ kind: "task", row: { id, manual_order: rank, updated_at: createdAt } }) as never;
7
14
 
@@ -18,6 +18,7 @@ import { TaskDetailsDialog } from "./TaskDetailsDialog.js";
18
18
  import { taskOrigin } from "./task-origin.js";
19
19
  import { useCurrentTime } from "./use-current-time.js";
20
20
  import { useInfiniteColumn } from "./use-infinite-column.js";
21
+ import { WorkColumnState } from "./WorkColumnState.js";
21
22
  import {
22
23
  isWorkItemVisible,
23
24
  type MacroWorkProgress,
@@ -30,6 +31,9 @@ import {
30
31
  workStageLabel,
31
32
  workStages,
32
33
  } from "./work-board-model.js";
34
+ import { openWorkCardLabel } from "./work-card-label.js";
35
+ import { workColumnState } from "./work-column-state.js";
36
+ import { filterWorkItems } from "./work-item-filter.js";
33
37
  import "./unified-work-board.css";
34
38
 
35
39
  type WorkItem =
@@ -323,6 +327,7 @@ function WorkColumn({
323
327
  totalItems,
324
328
  onLoadMore,
325
329
  );
330
+ const displayState = workColumnState(items.length, totalItems, state);
326
331
  return (
327
332
  <article className={`operations-column is-${stage}`}>
328
333
  <header>
@@ -371,9 +376,7 @@ function WorkColumn({
371
376
  totalsByTask={totalsByTask}
372
377
  />
373
378
  ))}
374
- {items.length === 0 && !state?.loading ? (
375
- <p className="operations-empty">{locale === "pt-BR" ? "Sem itens" : "No items"}</p>
376
- ) : null}
379
+ <WorkColumnState error={state?.error} locale={locale} state={displayState} />
377
380
  {hasMore ? (
378
381
  <div
379
382
  aria-live="polite"
@@ -543,9 +546,8 @@ function WorkCard({
543
546
  );
544
547
  }
545
548
 
546
- export function openWorkCardLabel(locale: OperationsLocale, title: string): string {
547
- return locale === "pt-BR" ? `Abrir ${title}` : `Open ${title}`;
548
- }
549
+ export { openWorkCardLabel } from "./work-card-label.js";
550
+ export { workColumnState } from "./work-column-state.js";
549
551
 
550
552
  /** Replaces only visible-column slots, preserving the global order of all other work. */
551
553
  export function mergeManualWorkOrder(
@@ -561,16 +563,6 @@ export function mergeManualWorkOrder(
561
563
  );
562
564
  }
563
565
 
564
- export function filterWorkItems(items: readonly WorkItem[], query: string): WorkItem[] {
565
- const normalized = query.trim().toLocaleLowerCase();
566
- if (!normalized) return [...items];
567
- return items.filter((item) =>
568
- [item.row.id, item.row.title, item.kind].some((value) =>
569
- value.toLocaleLowerCase().includes(normalized),
570
- ),
571
- );
572
- }
573
-
574
566
  function WorkColumnDialog({
575
567
  items,
576
568
  locale,
@@ -0,0 +1,34 @@
1
+ import type { OperationsLocale } from "./status-labels.js";
2
+
3
+ export function WorkColumnState({
4
+ state,
5
+ error,
6
+ locale,
7
+ }: {
8
+ state: "loading" | "empty" | "error" | "content";
9
+ error: string | null | undefined;
10
+ locale: OperationsLocale;
11
+ }) {
12
+ if (state === "loading")
13
+ return (
14
+ <div
15
+ aria-busy="true"
16
+ aria-label={locale === "pt-BR" ? "Carregando itens da coluna" : "Loading column items"}
17
+ className="operations-column-skeleton"
18
+ role="status"
19
+ >
20
+ <span />
21
+ <span />
22
+ <span />
23
+ </div>
24
+ );
25
+ if (state === "error")
26
+ return (
27
+ <p className="operations-empty" role="alert">
28
+ {error}
29
+ </p>
30
+ );
31
+ return state === "empty" ? (
32
+ <p className="operations-empty">{locale === "pt-BR" ? "Sem itens" : "No items"}</p>
33
+ ) : null;
34
+ }
@@ -11,7 +11,7 @@ import {
11
11
 
12
12
  describe("formatOperationDuration", () => {
13
13
  it("preserves unavailable and observed zero as different states", () => {
14
- expect(formatOperationDuration(null, "pt-BR")).toBe("Indisponível");
14
+ expect(formatOperationDuration(null, "pt-BR")).toBe("0");
15
15
  expect(formatOperationDuration(0, "pt-BR")).toBe("0 seg");
16
16
  });
17
17
 
@@ -9,7 +9,7 @@ export interface OperationalTiming {
9
9
  }
10
10
 
11
11
  export function formatOperationDuration(value: number | null, locale: OperationsLocale): string {
12
- if (value === null) return locale === "pt-BR" ? "Indisponível" : "Unavailable";
12
+ if (value === null) return "0";
13
13
  if (value === 0) return locale === "pt-BR" ? "0 seg" : "0 sec";
14
14
  return formatDurationMs(value, locale);
15
15
  }
@@ -0,0 +1,10 @@
1
+ import { expect, it } from "vitest";
2
+ import { formatExecutionAverageHint } from "./OperationsOverview.js";
3
+
4
+ it("keeps the known macro average when execution days are unavailable", () => {
5
+ expect(formatExecutionAverageHint(3_600_000, 3, 0, "pt-BR")).toBe("20 min / item");
6
+ expect(formatExecutionAverageHint(3_600_000, 3, 0, "en-US")).toBe("20 min / item");
7
+ expect(formatExecutionAverageHint(0, 3, 0, "pt-BR")).toBe(
8
+ "Sem tempo medido para calcular médias",
9
+ );
10
+ });
@@ -0,0 +1,3 @@
1
+ export function shouldRenderOperationsContent(loading: boolean): boolean {
2
+ return !loading;
3
+ }
@@ -38,7 +38,7 @@ describe("Operations persisted aggregate display", () => {
38
38
  expect(totals).toMatchObject({ duration_ms: null, tokens: 0, cost_brl: 0 });
39
39
  });
40
40
 
41
- it("preserves unavailable aggregate totals and renders zero only for an empty population", () => {
41
+ it("preserves unavailable aggregate facts and opts overview cards into disclosed zero", () => {
42
42
  const overview = ["OperationsOverview.tsx", "overview-metric-format.ts"]
43
43
  .map((file) => readFileSync(new URL(file, import.meta.url), "utf8"))
44
44
  .join("\n");
@@ -52,8 +52,9 @@ describe("Operations persisted aggregate display", () => {
52
52
  expect(overview).toMatch(
53
53
  /durationMs: totalDurationMetric,\s*tokens: totalTokensMetric,\s*costBrl: rateCost/,
54
54
  );
55
- expect(overview).toContain("formatSummaryMetricValue(\n averageDuration,");
56
- expect(overview).toContain('populationCount === 0 ? "0"');
55
+ expect(overview).toContain("overviewMetricStats");
56
+ expect(overview).toMatch(/populationCount === 0 \|\| displayUnmeasuredAsZero\s*\? "0"/);
57
+ expect(overview).toMatch(/unmeasuredZeroDescription\(\s*totalDurationMetric/);
57
58
  expect(overview).not.toContain("allPhaseMetrics.tokens ?? 0");
58
59
  expect(overview).not.toContain("executionMetrics.durationMs ?? 0");
59
60
  });
@@ -1,10 +1,21 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { createElement } from "react";
3
+ import { renderToStaticMarkup } from "react-dom/server";
2
4
  import { describe, expect, it } from "vitest";
3
5
  import { readAppCompositionSource } from "../../../../apps/frontend/src/__tests__/app-composition-support.js";
4
- import { resolveOperationsPeriodControl, shouldRenderOperationsContent } from "./OperationsPage.js";
6
+ import { resolveOperationsPeriodControl } from "./OperationsPage.js";
7
+ import { OperationsPageLoading } from "./OperationsPageOverview.js";
8
+ import { shouldRenderOperationsContent } from "./operations-content-visibility.js";
5
9
  import { workListTypeOnViewChange } from "./operations-page-contract.js";
6
10
 
7
11
  describe("overview period filter", () => {
12
+ it("renders independent KPI and panel skeletons without fake zeros or unavailable values", () => {
13
+ const html = renderToStaticMarkup(createElement(OperationsPageLoading, { locale: "pt-BR" }));
14
+ expect(html.match(/operations-skeleton-block/g)).toHaveLength(10);
15
+ expect(html).not.toContain("Indisponível");
16
+ expect(html).not.toContain(">0<");
17
+ expect(html).toContain('aria-busy="true"');
18
+ });
8
19
  it("passes activity-filtered entities and Runs to every overview metric", () => {
9
20
  const overview = readFileSync(new URL("OperationsPageOverview.tsx", import.meta.url), "utf8");
10
21
 
@@ -97,6 +97,7 @@ export interface OperationsPageProps {
97
97
  /** Timestamp of the immutable projection supplied by the composition root. */
98
98
  lastUpdatedAt?: string | null;
99
99
  loading: boolean;
100
+ costLoading?: boolean | undefined;
100
101
  onLoadingChange?: (loading: boolean) => void;
101
102
  currency: "BRL" | "USD";
102
103
  locale: OperationsLocale;