@burdenoff/microfe-bigconsole 2026.924.2 → 2026.925.1

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 (134) hide show
  1. package/dist/bigconsole/BigConsoleRoot.js +94 -84
  2. package/dist/bigconsole/BigConsoleRoot.js.map +1 -1
  3. package/dist/bigconsole/BigConsoleRoutes.js +137 -138
  4. package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
  5. package/dist/bigconsole/assistant/previewController.js +7 -7
  6. package/dist/bigconsole/components/DrilldownDashboardRenderer.js +17 -17
  7. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +32 -32
  8. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +52 -52
  9. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
  10. package/dist/bigconsole/components/dashboard/DashboardCanvas.js +7 -7
  11. package/dist/bigconsole/components/dashboard/DashboardStatusPill.js +53 -0
  12. package/dist/bigconsole/components/dashboard/DashboardStatusPill.js.map +1 -0
  13. package/dist/bigconsole/components/dashboard/DashboardToolbar.js +60 -60
  14. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +21 -21
  15. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +30 -30
  16. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +36 -36
  17. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +34 -34
  18. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +38 -38
  19. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
  20. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +121 -121
  21. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
  22. package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +22 -22
  23. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +46 -46
  24. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +53 -53
  25. package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -1
  26. package/dist/bigconsole/components/dashboard/WidgetPalette.js +47 -47
  27. package/dist/bigconsole/components/dashboard/index.js +1 -0
  28. package/dist/bigconsole/components/datasink/CacheTTLSelector.js +35 -35
  29. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +18 -18
  30. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +20 -20
  31. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +30 -29
  32. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
  33. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +48 -48
  34. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +126 -114
  35. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
  36. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +35 -35
  37. package/dist/bigconsole/components/overview/DashboardGuide.js +214 -0
  38. package/dist/bigconsole/components/overview/DashboardGuide.js.map +1 -0
  39. package/dist/bigconsole/components/overview/DataHealthCard.js +97 -0
  40. package/dist/bigconsole/components/overview/DataHealthCard.js.map +1 -0
  41. package/dist/bigconsole/components/overview/OverviewQuickActions.js +32 -0
  42. package/dist/bigconsole/components/overview/OverviewQuickActions.js.map +1 -0
  43. package/dist/bigconsole/components/overview/OverviewStatCard.js +57 -0
  44. package/dist/bigconsole/components/overview/OverviewStatCard.js.map +1 -0
  45. package/dist/bigconsole/components/overview/RecentDashboardsPanel.js +107 -0
  46. package/dist/bigconsole/components/overview/RecentDashboardsPanel.js.map +1 -0
  47. package/dist/bigconsole/components/overview/dashboardGuideSteps.js +119 -0
  48. package/dist/bigconsole/components/overview/dashboardGuideSteps.js.map +1 -0
  49. package/dist/bigconsole/components/overview/index.js +8 -0
  50. package/dist/bigconsole/components/overview/overviewModel.js +119 -0
  51. package/dist/bigconsole/components/overview/overviewModel.js.map +1 -0
  52. package/dist/bigconsole/components/overview/useOverviewData.js +48 -0
  53. package/dist/bigconsole/components/overview/useOverviewData.js.map +1 -0
  54. package/dist/bigconsole/components/parser/DataSinkSelector.js +40 -40
  55. package/dist/bigconsole/components/parser/ImportParserDialog.js +127 -127
  56. package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
  57. package/dist/bigconsole/components/parser/ParserSelector.js +51 -51
  58. package/dist/bigconsole/components/preview/AiPreviewContent.js +5 -5
  59. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +32 -32
  60. package/dist/bigconsole/components/preview/AiPreviewPanel.js +37 -37
  61. package/dist/bigconsole/components/widgets/ActionConfigDialog.js +13 -13
  62. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +70 -70
  63. package/dist/bigconsole/components/widgets/PropertiesPanel.js +28 -28
  64. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +25 -25
  65. package/dist/bigconsole/components/widgets/chart/ChartConfig.js +48 -48
  66. package/dist/bigconsole/components/widgets/comments/WidgetComments.js +46 -46
  67. package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +61 -61
  68. package/dist/bigconsole/components/widgets/form/FormWidget.js +36 -36
  69. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +42 -42
  70. package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +25 -25
  71. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +21 -21
  72. package/dist/bigconsole/components/widgets/list/ListConfig.js +30 -30
  73. package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +38 -38
  74. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +9 -9
  75. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +9 -9
  76. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +49 -49
  77. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +39 -39
  78. package/dist/bigconsole/components/widgets/table/TableConfig.js +73 -73
  79. package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +64 -64
  80. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +10 -10
  81. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +68 -68
  82. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
  83. package/dist/bigconsole/hooks/useDataSinkOperations.js +92 -89
  84. package/dist/bigconsole/hooks/useDataSinkOperations.js.map +1 -1
  85. package/dist/bigconsole/hooks/useImportDataSink.js +96 -91
  86. package/dist/bigconsole/hooks/useImportDataSink.js.map +1 -1
  87. package/dist/bigconsole/pages/AdminPage.js +136 -136
  88. package/dist/bigconsole/pages/DashboardBuilderPage.js +10 -10
  89. package/dist/bigconsole/pages/DashboardViewPage.js +61 -61
  90. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  91. package/dist/bigconsole/pages/DashboardsPage.js +327 -348
  92. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  93. package/dist/bigconsole/pages/DataParsersPage.js +5 -5
  94. package/dist/bigconsole/pages/DataSinkBuilderPage.js +82 -80
  95. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  96. package/dist/bigconsole/pages/DataSinkViewPage.js +83 -83
  97. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  98. package/dist/bigconsole/pages/DataSinksPage.js +11 -11
  99. package/dist/bigconsole/pages/DataSourcesPage.js +5 -5
  100. package/dist/bigconsole/pages/InsightsPage.js +315 -315
  101. package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
  102. package/dist/bigconsole/pages/OverviewPage.js +244 -0
  103. package/dist/bigconsole/pages/OverviewPage.js.map +1 -0
  104. package/dist/bigconsole/pages/ParserBuilderPage.js +5 -5
  105. package/dist/bigconsole/pages/ParserViewPage.js +98 -98
  106. package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
  107. package/dist/bigconsole/pages/PipelineBuilderPage.js +8 -8
  108. package/dist/bigconsole/pages/PipelineViewPage.js +46 -46
  109. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  110. package/dist/bigconsole/pages/PipelinesPage.js +80 -80
  111. package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
  112. package/dist/bigconsole/pages/SettingsPage.js +70 -70
  113. package/dist/bigconsole/pages/WidgetDemoPage.js +42 -42
  114. package/dist/bigconsole/pages/WorkflowViewPage.js +200 -200
  115. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  116. package/dist/bigconsole/pages/WorkflowsPage.js +78 -78
  117. package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
  118. package/dist/bigconsole/providers/BigConsoleQuotaProvider.js +36 -35
  119. package/dist/bigconsole/providers/BigConsoleQuotaProvider.js.map +1 -1
  120. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +34 -34
  121. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +79 -79
  122. package/dist/bigconsole/renderers/adapters/CustomRenderer.js +46 -46
  123. package/dist/bigconsole/utils/createDialogParam.js +14 -0
  124. package/dist/bigconsole/utils/createDialogParam.js.map +1 -0
  125. package/dist/bigconsole/utils/quota.js +51 -23
  126. package/dist/bigconsole/utils/quota.js.map +1 -1
  127. package/dist/generated/global-operations.js +1 -1
  128. package/dist/generated/wspace-operations.js +285 -222
  129. package/dist/generated/wspace-operations.js.map +1 -1
  130. package/dist/shared/hooks/useTr.js +10 -7
  131. package/dist/shared/hooks/useTr.js.map +1 -1
  132. package/package.json +1 -1
  133. package/dist/bigconsole/pages/HomePage.js +0 -318
  134. package/dist/bigconsole/pages/HomePage.js.map +0 -1
@@ -0,0 +1,97 @@
1
+ import { Section as e } from "../common/Section.js";
2
+ import "../common/index.js";
3
+ import { useTr as t } from "../../../shared/hooks/useTr.js";
4
+ import n from "../datasink/DataSinkStatusBadge.js";
5
+ import { memo as r } from "react";
6
+ import { Activity as i, CheckCircle2 as a } from "lucide-react";
7
+ import { Button as o, EmptyState as s, Skeleton as c } from "@burdenoff/fe-libs/ui";
8
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
9
+ //#region src/bigconsole/components/overview/DataHealthCard.tsx
10
+ var d = r(function({ summary: r, loading: d, error: f, canCreate: p, onRetry: m, onNavigate: h, formatUpdated: g }) {
11
+ let _ = t(), v = _("bigconsole.overview.dataHealth.title", "Data health");
12
+ if (f && r.total === void 0) return /* @__PURE__ */ l(e, {
13
+ headingLevel: 2,
14
+ tone: "danger",
15
+ "data-testid": "overview-data-health-error",
16
+ title: _("bigconsole.overview.dataHealth.errorTitle", "Couldn’t load data sink health"),
17
+ description: f.message,
18
+ actions: /* @__PURE__ */ l(o, {
19
+ size: "sm",
20
+ onClick: m,
21
+ children: _("bigconsole.overview.retry", "Retry")
22
+ })
23
+ });
24
+ let y = /* @__PURE__ */ u(o, {
25
+ variant: "link",
26
+ size: "sm",
27
+ onClick: () => h("/datasinks"),
28
+ children: [_("bigconsole.overview.viewAll", "View all"), /* @__PURE__ */ u("span", {
29
+ className: "sr-only",
30
+ children: [" ", _("bigconsole.overview.dataHealth.viewAllContext", "data sinks")]
31
+ })]
32
+ });
33
+ return /* @__PURE__ */ l(e, {
34
+ headingLevel: 2,
35
+ "data-testid": "overview-data-health",
36
+ icon: /* @__PURE__ */ l(i, { className: "h-icon-sm w-icon-sm" }),
37
+ title: v,
38
+ actions: r.total ? y : void 0,
39
+ children: d && r.total === void 0 ? /* @__PURE__ */ u("div", {
40
+ className: "space-y-2",
41
+ "aria-hidden": "true",
42
+ children: [/* @__PURE__ */ l(c, { className: "h-10 w-full" }), /* @__PURE__ */ l(c, { className: "h-10 w-full" })]
43
+ }) : r.total ? r.rows.length === 0 ? /* @__PURE__ */ u("p", {
44
+ className: "flex items-center gap-2 text-sm text-status-success-text",
45
+ "data-testid": "overview-data-health-ok",
46
+ children: [/* @__PURE__ */ l(a, {
47
+ className: "size-4 shrink-0",
48
+ "aria-hidden": "true"
49
+ }), _("bigconsole.overview.dataHealth.allHealthy", "All data sinks are healthy.")]
50
+ }) : /* @__PURE__ */ u("div", {
51
+ className: "space-y-2",
52
+ children: [/* @__PURE__ */ l("ul", {
53
+ className: "flex flex-col gap-2",
54
+ children: r.rows.map((e) => {
55
+ let t = e.lastRefreshedAt ? g(e.lastRefreshedAt) : null;
56
+ return /* @__PURE__ */ l("li", { children: /* @__PURE__ */ u("button", {
57
+ type: "button",
58
+ onClick: () => h(`/datasinks/${encodeURIComponent(e.id)}`),
59
+ className: "flex min-h-11 w-full items-center justify-between gap-3 rounded-card border border-border-subtle bg-bg-surface px-3 py-2 text-left transition-colors duration-200 hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus motion-reduce:transition-none",
60
+ children: [/* @__PURE__ */ u("span", {
61
+ className: "min-w-0",
62
+ children: [/* @__PURE__ */ l("span", {
63
+ className: "block truncate text-sm font-medium text-text-primary",
64
+ children: e.name
65
+ }), t ? /* @__PURE__ */ l("span", {
66
+ className: "block text-xs text-text-muted",
67
+ children: _("bigconsole.overview.dataHealth.lastRefreshed", "Last refreshed {{time}}", { time: t })
68
+ }) : null]
69
+ }), /* @__PURE__ */ l(n, {
70
+ status: e.status,
71
+ size: "sm"
72
+ })]
73
+ }) }, e.id);
74
+ })
75
+ }), r.hiddenCount > 0 ? /* @__PURE__ */ l(o, {
76
+ variant: "link",
77
+ size: "sm",
78
+ className: "px-0",
79
+ onClick: () => h("/datasinks"),
80
+ children: _("bigconsole.overview.dataHealth.more", "+{{count}} more", { count: r.hiddenCount })
81
+ }) : null]
82
+ }) : /* @__PURE__ */ l(s, {
83
+ compact: !0,
84
+ title: _("bigconsole.overview.dataHealth.emptyTitle", "No data sinks yet"),
85
+ description: _("bigconsole.overview.dataHealth.emptyDescription", "Data sinks hold the data your dashboards display."),
86
+ action: p ? /* @__PURE__ */ l(o, {
87
+ size: "sm",
88
+ onClick: () => h("/datasinks/new"),
89
+ children: _("bigconsole.overview.actions.newDataSink", "New Data Sink")
90
+ }) : void 0
91
+ })
92
+ });
93
+ });
94
+ //#endregion
95
+ export { d as default };
96
+
97
+ //# sourceMappingURL=DataHealthCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataHealthCard.js","names":[],"sources":["../../../../src/bigconsole/components/overview/DataHealthCard.tsx"],"sourcesContent":["/**\n * DataHealthCard\n *\n * Which data sinks need attention — errored sinks first, then stale ones (a\n * page per status, each shown oldest refresh first), with a count of the rest.\n * Each row opens the sink, where the user can inspect the linked workflow or\n * import fresh data.\n */\n\nimport { type FC, memo } from 'react';\nimport { Button, EmptyState, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { Activity, CheckCircle2 } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { Section } from '../common';\nimport { DataSinkStatusBadge } from '../datasink/DataSinkStatusBadge';\nimport type { DataSinkSummary } from './overviewModel';\n\nexport interface DataHealthCardProps {\n summary: DataSinkSummary;\n /** First load in progress (no data yet). */\n loading: boolean;\n error: { message: string } | undefined;\n canCreate: boolean;\n onRetry: () => void;\n onNavigate: (path: string) => void;\n formatUpdated: (iso: string) => string | null;\n}\n\nexport const DataHealthCard: FC<DataHealthCardProps> = memo(function DataHealthCard({\n summary,\n loading,\n error,\n canCreate,\n onRetry,\n onNavigate,\n formatUpdated,\n}) {\n const tr = useTr();\n const title = tr('bigconsole.overview.dataHealth.title', 'Data health');\n\n if (error && summary.total === undefined) {\n return (\n <Section\n headingLevel={2}\n tone=\"danger\"\n data-testid=\"overview-data-health-error\"\n title={tr('bigconsole.overview.dataHealth.errorTitle', 'Couldn’t load data sink health')}\n description={error.message}\n actions={\n <Button size=\"sm\" onClick={onRetry}>\n {tr('bigconsole.overview.retry', 'Retry')}\n </Button>\n }\n />\n );\n }\n\n const viewAll = (\n <Button variant=\"link\" size=\"sm\" onClick={() => onNavigate('/datasinks')}>\n {tr('bigconsole.overview.viewAll', 'View all')}\n {/* Appended so the accessible name starts with the visible label (WCAG 2.5.3). */}\n <span className=\"sr-only\"> {tr('bigconsole.overview.dataHealth.viewAllContext', 'data sinks')}</span>\n </Button>\n );\n\n return (\n <Section\n headingLevel={2}\n data-testid=\"overview-data-health\"\n icon={<Activity className=\"h-icon-sm w-icon-sm\" />}\n title={title}\n actions={summary.total ? viewAll : undefined}\n >\n {loading && summary.total === undefined ? (\n <div className=\"space-y-2\" aria-hidden=\"true\">\n <Skeleton className=\"h-10 w-full\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n ) : !summary.total ? (\n <EmptyState\n compact\n title={tr('bigconsole.overview.dataHealth.emptyTitle', 'No data sinks yet')}\n description={tr(\n 'bigconsole.overview.dataHealth.emptyDescription',\n 'Data sinks hold the data your dashboards display.'\n )}\n action={\n canCreate ? (\n <Button size=\"sm\" onClick={() => onNavigate('/datasinks/new')}>\n {tr('bigconsole.overview.actions.newDataSink', 'New Data Sink')}\n </Button>\n ) : undefined\n }\n />\n ) : summary.rows.length === 0 ? (\n <p className=\"flex items-center gap-2 text-sm text-status-success-text\" data-testid=\"overview-data-health-ok\">\n <CheckCircle2 className=\"size-4 shrink-0\" aria-hidden=\"true\" />\n {tr('bigconsole.overview.dataHealth.allHealthy', 'All data sinks are healthy.')}\n </p>\n ) : (\n <div className=\"space-y-2\">\n <ul className=\"flex flex-col gap-2\">\n {summary.rows.map((row) => {\n const refreshed = row.lastRefreshedAt ? formatUpdated(row.lastRefreshedAt) : null;\n return (\n <li key={row.id}>\n <button\n type=\"button\"\n onClick={() => onNavigate(`/datasinks/${encodeURIComponent(row.id)}`)}\n className=\"flex min-h-11 w-full items-center justify-between gap-3 rounded-card border border-border-subtle bg-bg-surface px-3 py-2 text-left transition-colors duration-200 hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus motion-reduce:transition-none\"\n >\n <span className=\"min-w-0\">\n <span className=\"block truncate text-sm font-medium text-text-primary\">{row.name}</span>\n {refreshed ? (\n <span className=\"block text-xs text-text-muted\">\n {tr('bigconsole.overview.dataHealth.lastRefreshed', 'Last refreshed {{time}}', {\n time: refreshed,\n })}\n </span>\n ) : null}\n </span>\n <DataSinkStatusBadge status={row.status} size=\"sm\" />\n </button>\n </li>\n );\n })}\n </ul>\n {summary.hiddenCount > 0 ? (\n <Button variant=\"link\" size=\"sm\" className=\"px-0\" onClick={() => onNavigate('/datasinks')}>\n {tr('bigconsole.overview.dataHealth.more', '+{{count}} more', { count: summary.hiddenCount })}\n </Button>\n ) : null}\n </div>\n )}\n </Section>\n );\n});\n\nexport default DataHealthCard;\n"],"mappings":";;;;;;;;;AA4BA,IAAa,IAA0C,EAAK,SAAwB,EAClF,YACA,YACA,UACA,cACA,YACA,eACA,oBACC;CACD,IAAM,IAAK,GAAO,EACZ,IAAQ,EAAG,wCAAwC,cAAc;AAEvE,KAAI,KAAS,EAAQ,UAAU,KAAA,EAC7B,QACE,kBAAC,GAAD;EACE,cAAc;EACd,MAAK;EACL,eAAY;EACZ,OAAO,EAAG,6CAA6C,iCAAiC;EACxF,aAAa,EAAM;EACnB,SACE,kBAAC,GAAD;GAAQ,MAAK;GAAK,SAAS;aACxB,EAAG,6BAA6B,QAAQ;GAClC,CAAA;EAEX,CAAA;CAIN,IAAM,IACJ,kBAAC,GAAD;EAAQ,SAAQ;EAAO,MAAK;EAAK,eAAe,EAAW,aAAa;YAAxE,CACG,EAAG,+BAA+B,WAAW,EAE9C,kBAAC,QAAD;GAAM,WAAU;aAAhB,CAA0B,KAAE,EAAG,iDAAiD,aAAa,CAAQ;KAC9F;;AAGX,QACE,kBAAC,GAAD;EACE,cAAc;EACd,eAAY;EACZ,MAAM,kBAAC,GAAD,EAAU,WAAU,uBAAwB,CAAA;EAC3C;EACP,SAAS,EAAQ,QAAQ,IAAU,KAAA;YAElC,KAAW,EAAQ,UAAU,KAAA,IAC5B,kBAAC,OAAD;GAAK,WAAU;GAAY,eAAY;aAAvC,CACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EACpC,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,CAChC;OACH,EAAQ,QAgBT,EAAQ,KAAK,WAAW,IAC1B,kBAAC,KAAD;GAAG,WAAU;GAA2D,eAAY;aAApF,CACE,kBAAC,GAAD;IAAc,WAAU;IAAkB,eAAY;IAAS,CAAA,EAC9D,EAAG,6CAA6C,8BAA8B,CAC7E;OAEJ,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAQ,KAAK,KAAK,MAAQ;KACzB,IAAM,IAAY,EAAI,kBAAkB,EAAc,EAAI,gBAAgB,GAAG;AAC7E,YACE,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,cAAc,mBAAmB,EAAI,GAAG,GAAG;MACrE,WAAU;gBAHZ,CAKE,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAwD,EAAI;QAAY,CAAA,EACvF,IACC,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,gDAAgD,2BAA2B,EAC7E,MAAM,GACP,CAAC;QACG,CAAA,GACL,KACC;UACP,kBAAC,GAAD;OAAqB,QAAQ,EAAI;OAAQ,MAAK;OAAO,CAAA,CAC9C;SACN,EAlBI,EAAI,GAkBR;MAEP;IACC,CAAA,EACJ,EAAQ,cAAc,IACrB,kBAAC,GAAD;IAAQ,SAAQ;IAAO,MAAK;IAAK,WAAU;IAAO,eAAe,EAAW,aAAa;cACtF,EAAG,uCAAuC,mBAAmB,EAAE,OAAO,EAAQ,aAAa,CAAC;IACtF,CAAA,GACP,KACA;OArDN,kBAAC,GAAD;GACE,SAAA;GACA,OAAO,EAAG,6CAA6C,oBAAoB;GAC3E,aAAa,EACX,mDACA,oDACD;GACD,QACE,IACE,kBAAC,GAAD;IAAQ,MAAK;IAAK,eAAe,EAAW,iBAAiB;cAC1D,EAAG,2CAA2C,gBAAgB;IACxD,CAAA,GACP,KAAA;GAEN,CAAA;EAyCI,CAAA;EAEZ"}
@@ -0,0 +1,32 @@
1
+ import { Section as e } from "../common/Section.js";
2
+ import "../common/index.js";
3
+ import { useTr as t } from "../../../shared/hooks/useTr.js";
4
+ import { memo as n } from "react";
5
+ import { Zap as r } from "lucide-react";
6
+ import { Button as i } from "@burdenoff/fe-libs/ui";
7
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
8
+ //#region src/bigconsole/components/overview/OverviewQuickActions.tsx
9
+ var s = n(function({ actions: n, onNavigate: s }) {
10
+ let c = t();
11
+ return /* @__PURE__ */ a(e, {
12
+ headingLevel: 2,
13
+ "data-testid": "overview-quick-actions",
14
+ icon: /* @__PURE__ */ a(r, { className: "h-icon-sm w-icon-sm" }),
15
+ title: c("bigconsole.overview.quickActions.title", "Quick actions"),
16
+ children: /* @__PURE__ */ a("div", {
17
+ className: "flex flex-col gap-2",
18
+ children: n.map((e) => /* @__PURE__ */ o(i, {
19
+ type: "button",
20
+ variant: e.primary ? "default" : "outline",
21
+ onClick: () => s(e.path),
22
+ className: "min-h-11 w-full justify-start sm:min-h-10",
23
+ "data-testid": `overview-quick-action-${e.id}`,
24
+ children: [e.icon, e.label]
25
+ }, e.id))
26
+ })
27
+ });
28
+ });
29
+ //#endregion
30
+ export { s as default };
31
+
32
+ //# sourceMappingURL=OverviewQuickActions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OverviewQuickActions.js","names":[],"sources":["../../../../src/bigconsole/components/overview/OverviewQuickActions.tsx"],"sourcesContent":["/**\n * OverviewQuickActions\n *\n * Navigation shortcuts to the most common \"create\" flows. Actions the caller\n * is not permitted to perform are hidden, not disabled; the card is omitted by\n * the page when none remain.\n */\n\nimport { type FC, type ReactNode, memo } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Zap } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { Section } from '../common';\n\nexport interface OverviewQuickAction {\n id: string;\n label: string;\n icon: ReactNode;\n path: string;\n primary?: boolean;\n}\n\nexport interface OverviewQuickActionsProps {\n actions: OverviewQuickAction[];\n onNavigate: (path: string) => void;\n}\n\nexport const OverviewQuickActions: FC<OverviewQuickActionsProps> = memo(function OverviewQuickActions({\n actions,\n onNavigate,\n}) {\n const tr = useTr();\n\n return (\n <Section\n headingLevel={2}\n data-testid=\"overview-quick-actions\"\n icon={<Zap className=\"h-icon-sm w-icon-sm\" />}\n title={tr('bigconsole.overview.quickActions.title', 'Quick actions')}\n >\n <div className=\"flex flex-col gap-2\">\n {actions.map((action) => (\n <Button\n key={action.id}\n type=\"button\"\n variant={action.primary ? 'default' : 'outline'}\n onClick={() => onNavigate(action.path)}\n className=\"min-h-11 w-full justify-start sm:min-h-10\"\n data-testid={`overview-quick-action-${action.id}`}\n >\n {action.icon}\n {action.label}\n </Button>\n ))}\n </div>\n </Section>\n );\n});\n\nexport default OverviewQuickActions;\n"],"mappings":";;;;;;;;AA2BA,IAAa,IAAsD,EAAK,SAA8B,EACpG,YACA,iBACC;CACD,IAAM,IAAK,GAAO;AAElB,QACE,kBAAC,GAAD;EACE,cAAc;EACd,eAAY;EACZ,MAAM,kBAAC,GAAD,EAAK,WAAU,uBAAwB,CAAA;EAC7C,OAAO,EAAG,0CAA0C,gBAAgB;YAEpE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAQ,KAAK,MACZ,kBAAC,GAAD;IAEE,MAAK;IACL,SAAS,EAAO,UAAU,YAAY;IACtC,eAAe,EAAW,EAAO,KAAK;IACtC,WAAU;IACV,eAAa,yBAAyB,EAAO;cAN/C,CAQG,EAAO,MACP,EAAO,MACD;MATF,EAAO,GASL,CACT;GACE,CAAA;EACE,CAAA;EAEZ"}
@@ -0,0 +1,57 @@
1
+ import { memo as e } from "react";
2
+ import { GlassCard as t, Skeleton as n } from "@burdenoff/fe-libs/ui";
3
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
+ import { AnimatedCounter as a } from "@burdenoff/fe-libs/shared/motion";
5
+ //#region src/bigconsole/components/overview/OverviewStatCard.tsx
6
+ var o = {
7
+ default: "bg-[var(--color-accent-soft)] text-text-primary",
8
+ success: "bg-status-success-bg-subtle text-status-success-text",
9
+ warning: "bg-status-warning-bg-subtle text-[var(--color-status-warning-text)]"
10
+ }, s = e(function({ title: e, value: s, loading: c = !1, subtitle: l, icon: u, variant: d = "default", onClick: f, "data-testid": p }) {
11
+ let m = /* @__PURE__ */ i("div", {
12
+ className: "flex flex-col gap-2.5",
13
+ children: [
14
+ /* @__PURE__ */ i("div", {
15
+ className: "flex items-center justify-between gap-3",
16
+ children: [/* @__PURE__ */ r("p", {
17
+ className: "text-sm font-medium text-text-secondary",
18
+ children: e
19
+ }), /* @__PURE__ */ r("span", {
20
+ "aria-hidden": "true",
21
+ className: `flex size-9 shrink-0 items-center justify-center rounded-lg [&>svg]:size-4 ${o[d]}`,
22
+ children: u
23
+ })]
24
+ }),
25
+ s === void 0 ? c ? /* @__PURE__ */ r(n, { className: "h-8 w-16" }) : /* @__PURE__ */ r("div", {
26
+ "aria-hidden": "true",
27
+ className: "text-2xl font-semibold text-text-muted",
28
+ children: "—"
29
+ }) : /* @__PURE__ */ r("div", {
30
+ className: "text-2xl font-semibold tabular-nums text-text-primary",
31
+ children: /* @__PURE__ */ r(a, { value: s })
32
+ }),
33
+ l ? /* @__PURE__ */ r("p", {
34
+ className: "text-xs text-text-secondary",
35
+ children: l
36
+ }) : null
37
+ ]
38
+ });
39
+ return /* @__PURE__ */ r(t, {
40
+ treatment: "metric",
41
+ className: "h-full border-[var(--color-border-seam)] p-0",
42
+ "data-testid": p,
43
+ children: f ? /* @__PURE__ */ r("button", {
44
+ type: "button",
45
+ onClick: f,
46
+ className: "block h-full w-full rounded-card p-5 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
47
+ children: m
48
+ }) : /* @__PURE__ */ r("div", {
49
+ className: "p-5",
50
+ children: m
51
+ })
52
+ });
53
+ });
54
+ //#endregion
55
+ export { s as default };
56
+
57
+ //# sourceMappingURL=OverviewStatCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OverviewStatCard.js","names":[],"sources":["../../../../src/bigconsole/components/overview/OverviewStatCard.tsx"],"sourcesContent":["/**\n * OverviewStatCard\n *\n * KPI tile for the Overview: a label, a large number and an optional subtitle,\n * on the GlassCard \"metric\" surface. When `onClick` is set the whole tile is a\n * real `<button>` so it is keyboard- and screen-reader-navigable.\n */\n\nimport { type FC, type ReactNode, memo } from 'react';\nimport { GlassCard, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { AnimatedCounter } from '@burdenoff/fe-libs/shared/motion';\n\nexport type OverviewStatVariant = 'default' | 'success' | 'warning';\n\nexport interface OverviewStatCardProps {\n title: string;\n /** The count; `undefined` while loading or when the section failed to load. */\n value: number | undefined;\n /** The section is still loading — an undefined value renders a skeleton. */\n loading?: boolean;\n subtitle?: string;\n icon: ReactNode;\n variant?: OverviewStatVariant;\n onClick?: () => void;\n 'data-testid'?: string;\n}\n\n// Icon chip tint per variant. Warning text has no registered utility in the\n// shell theme, so it uses the token variable directly.\nconst ICON_CHIP_CLASSES: Record<OverviewStatVariant, string> = {\n default: 'bg-[var(--color-accent-soft)] text-text-primary',\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-[var(--color-status-warning-text)]',\n};\n\nexport const OverviewStatCard: FC<OverviewStatCardProps> = memo(function OverviewStatCard({\n title,\n value,\n loading = false,\n subtitle,\n icon,\n variant = 'default',\n onClick,\n 'data-testid': testId,\n}) {\n const body = (\n <div className=\"flex flex-col gap-2.5\">\n <div className=\"flex items-center justify-between gap-3\">\n <p className=\"text-sm font-medium text-text-secondary\">{title}</p>\n <span\n aria-hidden=\"true\"\n className={`flex size-9 shrink-0 items-center justify-center rounded-lg [&>svg]:size-4 ${ICON_CHIP_CLASSES[variant]}`}\n >\n {icon}\n </span>\n </div>\n {value !== undefined ? (\n <div className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n <AnimatedCounter value={value} />\n </div>\n ) : loading ? (\n <Skeleton className=\"h-8 w-16\" />\n ) : (\n // Failed to load: a neutral placeholder; the subtitle says why.\n <div aria-hidden=\"true\" className=\"text-2xl font-semibold text-text-muted\">\n —\n </div>\n )}\n {subtitle ? <p className=\"text-xs text-text-secondary\">{subtitle}</p> : null}\n </div>\n );\n\n return (\n <GlassCard treatment=\"metric\" className=\"h-full border-[var(--color-border-seam)] p-0\" data-testid={testId}>\n {onClick ? (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"block h-full w-full rounded-card p-5 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {body}\n </button>\n ) : (\n <div className=\"p-5\">{body}</div>\n )}\n </GlassCard>\n );\n});\n\nexport default OverviewStatCard;\n"],"mappings":";;;;;AA6BA,IAAM,IAAyD;CAC7D,SAAS;CACT,SAAS;CACT,SAAS;CACV,EAEY,IAA8C,EAAK,SAA0B,EACxF,UACA,UACA,aAAU,IACV,aACA,SACA,aAAU,WACV,YACA,eAAe,KACd;CACD,IAAM,IACJ,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAA2C;KAAU,CAAA,EAClE,kBAAC,QAAD;KACE,eAAY;KACZ,WAAW,8EAA8E,EAAkB;eAE1G;KACI,CAAA,CACH;;GACL,MAAU,KAAA,IAIP,IACF,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,GAGjC,kBAAC,OAAD;IAAK,eAAY;IAAO,WAAU;cAAyC;IAErE,CAAA,GATN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAwB,UAAS,CAAA;IAC7B,CAAA;GASP,IAAW,kBAAC,KAAD;IAAG,WAAU;cAA+B;IAAa,CAAA,GAAG;GACpE;;AAGR,QACE,kBAAC,GAAD;EAAW,WAAU;EAAS,WAAU;EAA+C,eAAa;YACjG,IACC,kBAAC,UAAD;GACE,MAAK;GACI;GACT,WAAU;aAET;GACM,CAAA,GAET,kBAAC,OAAD;GAAK,WAAU;aAAO;GAAW,CAAA;EAEzB,CAAA;EAEd"}
@@ -0,0 +1,107 @@
1
+ import { Section as e } from "../common/Section.js";
2
+ import "../common/index.js";
3
+ import { useTr as t } from "../../../shared/hooks/useTr.js";
4
+ import n, { useDashboardStatusLabel as r } from "../dashboard/DashboardStatusPill.js";
5
+ import { memo as i } from "react";
6
+ import { ArrowRight as a, LayoutDashboard as o } from "lucide-react";
7
+ import { Button as s, EmphasisPanel as c, IllustratedEmptyState as l, Skeleton as u } from "@burdenoff/fe-libs/ui";
8
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
9
+ //#region src/bigconsole/components/overview/RecentDashboardsPanel.tsx
10
+ var m = i(function({ dashboards: i, loading: m, error: h, canCreate: g, onRetry: _, onNavigate: v, formatUpdated: y, createDashboardPath: b }) {
11
+ let x = t(), S = r();
12
+ return h && i.length === 0 ? /* @__PURE__ */ f(e, {
13
+ headingLevel: 2,
14
+ tone: "danger",
15
+ className: "h-full",
16
+ "data-testid": "overview-recent-dashboards-error",
17
+ title: x("bigconsole.overview.recentDashboards.errorTitle", "Couldn’t load your dashboards"),
18
+ description: h.message,
19
+ actions: /* @__PURE__ */ f(s, {
20
+ size: "sm",
21
+ onClick: _,
22
+ children: x("bigconsole.overview.retry", "Retry")
23
+ })
24
+ }) : /* @__PURE__ */ p(c, {
25
+ "data-testid": "overview-recent-dashboards",
26
+ className: "flex h-full flex-col border-[color-mix(in_oklab,var(--color-action-primary-bg)_34%,transparent)] bg-bg-surface bg-[image:var(--gradient-accent-wash)] shadow-[var(--shadow-glow)]",
27
+ children: [/* @__PURE__ */ p("div", {
28
+ className: "mb-5 flex items-center justify-between gap-3",
29
+ children: [/* @__PURE__ */ p("h2", {
30
+ className: "flex items-center gap-2.5 text-lg font-semibold tracking-tight text-text-primary",
31
+ children: [/* @__PURE__ */ f("span", {
32
+ "aria-hidden": "true",
33
+ className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
34
+ children: /* @__PURE__ */ f(o, { className: "size-5" })
35
+ }), x("bigconsole.overview.recentDashboards.title", "Recent dashboards")]
36
+ }), /* @__PURE__ */ p(s, {
37
+ variant: "link",
38
+ size: "sm",
39
+ onClick: () => v("/dashboards"),
40
+ children: [x("bigconsole.overview.viewAll", "View all"), /* @__PURE__ */ p("span", {
41
+ className: "sr-only",
42
+ children: [" ", x("bigconsole.overview.recentDashboards.viewAllContext", "dashboards")]
43
+ })]
44
+ })]
45
+ }), m && i.length === 0 ? /* @__PURE__ */ p("div", {
46
+ className: "space-y-2",
47
+ "aria-hidden": "true",
48
+ children: [
49
+ /* @__PURE__ */ f(u, { className: "h-14 w-full" }),
50
+ /* @__PURE__ */ f(u, { className: "h-14 w-full" }),
51
+ /* @__PURE__ */ f(u, { className: "h-14 w-full" })
52
+ ]
53
+ }) : i.length === 0 ? /* @__PURE__ */ f(l, {
54
+ className: "flex-1 py-6",
55
+ illustration: "empty-data",
56
+ title: x("bigconsole.overview.recentDashboards.emptyTitle", "No dashboards yet"),
57
+ description: x("bigconsole.overview.recentDashboards.emptyDescription", "Follow the guide above to build your first dashboard."),
58
+ action: g ? /* @__PURE__ */ f(s, {
59
+ onClick: () => v(b),
60
+ children: x("bigconsole.overview.actions.createDashboard", "Create Dashboard")
61
+ }) : void 0
62
+ }) : /* @__PURE__ */ f("ul", {
63
+ className: "flex flex-col gap-2",
64
+ children: i.map((e) => {
65
+ let t = y(e.updatedAt);
66
+ return /* @__PURE__ */ f("li", { children: /* @__PURE__ */ p("button", {
67
+ type: "button",
68
+ onClick: () => v(`/dashboards/${encodeURIComponent(e.id)}`),
69
+ className: "flex min-h-14 w-full items-center gap-3 rounded-card border border-border-subtle bg-bg-surface px-3 py-2.5 text-left transition-colors duration-200 hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus motion-reduce:transition-none",
70
+ children: [/* @__PURE__ */ p("div", {
71
+ className: "min-w-0 flex-1",
72
+ children: [
73
+ /* @__PURE__ */ p("div", {
74
+ className: "flex flex-wrap items-center gap-2",
75
+ children: [/* @__PURE__ */ f("span", {
76
+ className: "truncate text-sm font-medium text-text-primary",
77
+ children: e.name
78
+ }), /* @__PURE__ */ f(n, {
79
+ status: e.status,
80
+ label: S(e.status)
81
+ })]
82
+ }),
83
+ e.description ? /* @__PURE__ */ f("p", {
84
+ className: "mt-0.5 line-clamp-1 text-xs text-text-muted",
85
+ children: e.description
86
+ }) : null,
87
+ /* @__PURE__ */ p("p", {
88
+ className: "mt-0.5 text-xs text-text-secondary",
89
+ children: [e.widgetCount === 1 ? x("bigconsole.overview.recentDashboards.widgetsOne", "1 widget") : x("bigconsole.overview.recentDashboards.widgetsOther", "{{count}} widgets", { count: e.widgetCount }), t ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f("span", {
90
+ "aria-hidden": "true",
91
+ children: " · "
92
+ }), x("bigconsole.overview.recentDashboards.updated", "Updated {{time}}", { time: t })] }) : null]
93
+ })
94
+ ]
95
+ }), /* @__PURE__ */ f(a, {
96
+ className: "size-4 shrink-0 text-text-muted",
97
+ "aria-hidden": "true"
98
+ })]
99
+ }) }, e.id);
100
+ })
101
+ })]
102
+ });
103
+ });
104
+ //#endregion
105
+ export { m as default };
106
+
107
+ //# sourceMappingURL=RecentDashboardsPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecentDashboardsPanel.js","names":[],"sources":["../../../../src/bigconsole/components/overview/RecentDashboardsPanel.tsx"],"sourcesContent":["/**\n * RecentDashboardsPanel\n *\n * The Overview's single emphasis zone: the most recently updated dashboards,\n * each one click away, with their lifecycle status and widget count.\n */\n\nimport { type FC, memo } from 'react';\nimport { Button, EmphasisPanel, IllustratedEmptyState, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { ArrowRight, LayoutDashboard } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { Section } from '../common';\nimport { DashboardStatusPill, useDashboardStatusLabel } from '../dashboard/DashboardStatusPill';\nimport type { RecentDashboard } from './overviewModel';\n\nexport interface RecentDashboardsPanelProps {\n dashboards: RecentDashboard[];\n /** First load in progress (no data yet). */\n loading: boolean;\n error: { message: string } | undefined;\n canCreate: boolean;\n onRetry: () => void;\n onNavigate: (path: string) => void;\n formatUpdated: (iso: string) => string | null;\n createDashboardPath: string;\n}\n\nexport const RecentDashboardsPanel: FC<RecentDashboardsPanelProps> = memo(function RecentDashboardsPanel({\n dashboards,\n loading,\n error,\n canCreate,\n onRetry,\n onNavigate,\n formatUpdated,\n createDashboardPath,\n}) {\n const tr = useTr();\n const statusLabel = useDashboardStatusLabel();\n\n if (error && dashboards.length === 0) {\n return (\n <Section\n headingLevel={2}\n tone=\"danger\"\n className=\"h-full\"\n data-testid=\"overview-recent-dashboards-error\"\n title={tr('bigconsole.overview.recentDashboards.errorTitle', 'Couldn’t load your dashboards')}\n description={error.message}\n actions={\n <Button size=\"sm\" onClick={onRetry}>\n {tr('bigconsole.overview.retry', 'Retry')}\n </Button>\n }\n />\n );\n }\n\n return (\n <EmphasisPanel\n data-testid=\"overview-recent-dashboards\"\n // EmphasisPanel's own `bg-accent-wash` / `border-border-seam` utilities are\n // not registered in the shell theme, so the surface, wash and border are\n // set explicitly from the same tokens.\n className=\"flex h-full flex-col border-[color-mix(in_oklab,var(--color-action-primary-bg)_34%,transparent)] bg-bg-surface bg-[image:var(--gradient-accent-wash)] shadow-[var(--shadow-glow)]\"\n >\n <div className=\"mb-5 flex items-center justify-between gap-3\">\n <h2 className=\"flex items-center gap-2.5 text-lg font-semibold tracking-tight text-text-primary\">\n <span\n aria-hidden=\"true\"\n className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\"\n >\n <LayoutDashboard className=\"size-5\" />\n </span>\n {tr('bigconsole.overview.recentDashboards.title', 'Recent dashboards')}\n </h2>\n <Button variant=\"link\" size=\"sm\" onClick={() => onNavigate('/dashboards')}>\n {tr('bigconsole.overview.viewAll', 'View all')}\n {/* Context for screen readers, appended so the accessible name still\n starts with the visible label in every locale (WCAG 2.5.3). */}\n <span className=\"sr-only\"> {tr('bigconsole.overview.recentDashboards.viewAllContext', 'dashboards')}</span>\n </Button>\n </div>\n\n {loading && dashboards.length === 0 ? (\n <div className=\"space-y-2\" aria-hidden=\"true\">\n <Skeleton className=\"h-14 w-full\" />\n <Skeleton className=\"h-14 w-full\" />\n <Skeleton className=\"h-14 w-full\" />\n </div>\n ) : dashboards.length === 0 ? (\n <IllustratedEmptyState\n className=\"flex-1 py-6\"\n illustration=\"empty-data\"\n title={tr('bigconsole.overview.recentDashboards.emptyTitle', 'No dashboards yet')}\n description={tr(\n 'bigconsole.overview.recentDashboards.emptyDescription',\n 'Follow the guide above to build your first dashboard.'\n )}\n action={\n canCreate ? (\n <Button onClick={() => onNavigate(createDashboardPath)}>\n {tr('bigconsole.overview.actions.createDashboard', 'Create Dashboard')}\n </Button>\n ) : undefined\n }\n />\n ) : (\n <ul className=\"flex flex-col gap-2\">\n {dashboards.map((dashboard) => {\n const updated = formatUpdated(dashboard.updatedAt);\n return (\n <li key={dashboard.id}>\n <button\n type=\"button\"\n onClick={() => onNavigate(`/dashboards/${encodeURIComponent(dashboard.id)}`)}\n className=\"flex min-h-14 w-full items-center gap-3 rounded-card border border-border-subtle bg-bg-surface px-3 py-2.5 text-left transition-colors duration-200 hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus motion-reduce:transition-none\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">{dashboard.name}</span>\n <DashboardStatusPill status={dashboard.status} label={statusLabel(dashboard.status)} />\n </div>\n {dashboard.description ? (\n <p className=\"mt-0.5 line-clamp-1 text-xs text-text-muted\">{dashboard.description}</p>\n ) : null}\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {dashboard.widgetCount === 1\n ? tr('bigconsole.overview.recentDashboards.widgetsOne', '1 widget')\n : tr('bigconsole.overview.recentDashboards.widgetsOther', '{{count}} widgets', {\n count: dashboard.widgetCount,\n })}\n {updated ? (\n <>\n <span aria-hidden=\"true\"> · </span>\n {tr('bigconsole.overview.recentDashboards.updated', 'Updated {{time}}', { time: updated })}\n </>\n ) : null}\n </p>\n </div>\n <ArrowRight className=\"size-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </EmphasisPanel>\n );\n});\n\nexport default RecentDashboardsPanel;\n"],"mappings":";;;;;;;;;AA2BA,IAAa,IAAwD,EAAK,SAA+B,EACvG,eACA,YACA,UACA,cACA,YACA,eACA,kBACA,0BACC;CACD,IAAM,IAAK,GAAO,EACZ,IAAc,GAAyB;AAoB7C,QAlBI,KAAS,EAAW,WAAW,IAE/B,kBAAC,GAAD;EACE,cAAc;EACd,MAAK;EACL,WAAU;EACV,eAAY;EACZ,OAAO,EAAG,mDAAmD,gCAAgC;EAC7F,aAAa,EAAM;EACnB,SACE,kBAAC,GAAD;GAAQ,MAAK;GAAK,SAAS;aACxB,EAAG,6BAA6B,QAAQ;GAClC,CAAA;EAEX,CAAA,GAKJ,kBAAC,GAAD;EACE,eAAY;EAIZ,WAAU;YALZ,CAOE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAAd,CACE,kBAAC,QAAD;KACE,eAAY;KACZ,WAAU;eAEV,kBAAC,GAAD,EAAiB,WAAU,UAAW,CAAA;KACjC,CAAA,EACN,EAAG,8CAA8C,oBAAoB,CACnE;OACL,kBAAC,GAAD;IAAQ,SAAQ;IAAO,MAAK;IAAK,eAAe,EAAW,cAAc;cAAzE,CACG,EAAG,+BAA+B,WAAW,EAG9C,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAA0B,KAAE,EAAG,uDAAuD,aAAa,CAAQ;OACpG;MACL;MAEL,KAAW,EAAW,WAAW,IAChC,kBAAC,OAAD;GAAK,WAAU;GAAY,eAAY;aAAvC;IACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA;IACpC,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA;IACpC,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA;IAChC;OACJ,EAAW,WAAW,IACxB,kBAAC,GAAD;GACE,WAAU;GACV,cAAa;GACb,OAAO,EAAG,mDAAmD,oBAAoB;GACjF,aAAa,EACX,yDACA,wDACD;GACD,QACE,IACE,kBAAC,GAAD;IAAQ,eAAe,EAAW,EAAoB;cACnD,EAAG,+CAA+C,mBAAmB;IAC/D,CAAA,GACP,KAAA;GAEN,CAAA,GAEF,kBAAC,MAAD;GAAI,WAAU;aACX,EAAW,KAAK,MAAc;IAC7B,IAAM,IAAU,EAAc,EAAU,UAAU;AAClD,WACE,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,eAAe,mBAAmB,EAAU,GAAG,GAAG;KAC5E,WAAU;eAHZ,CAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAkD,EAAU;SAAY,CAAA,EACxF,kBAAC,GAAD;SAAqB,QAAQ,EAAU;SAAQ,OAAO,EAAY,EAAU,OAAO;SAAI,CAAA,CACnF;;OACL,EAAU,cACT,kBAAC,KAAD;QAAG,WAAU;kBAA+C,EAAU;QAAgB,CAAA,GACpF;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb,CACG,EAAU,gBAAgB,IACvB,EAAG,mDAAmD,WAAW,GACjE,EAAG,qDAAqD,qBAAqB,EAC3E,OAAO,EAAU,aAClB,CAAC,EACL,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,eAAY;mBAAO;SAAU,CAAA,EAClC,EAAG,gDAAgD,oBAAoB,EAAE,MAAM,GAAS,CAAC,CACzF,EAAA,CAAA,GACD,KACF;;OACA;SACN,kBAAC,GAAD;MAAY,WAAU;MAAkC,eAAY;MAAS,CAAA,CACtE;QACN,EA9BI,EAAU,GA8Bd;KAEP;GACC,CAAA,CAEO;;EAElB"}
@@ -0,0 +1,119 @@
1
+ //#region src/bigconsole/components/overview/dashboardGuideSteps.ts
2
+ var e = "/dashboards?create=1";
3
+ function t(e) {
4
+ return e("bigconsole.overview.guide.noPermission", "You don't have permission for this step yet. Ask a workspace admin for access.");
5
+ }
6
+ function n({ tr: n, permissions: r, latestDashboard: i }) {
7
+ let a = i ?? null, o = t(n), s = {
8
+ id: "createDataSink",
9
+ title: n("bigconsole.overview.guide.steps.createDataSink.title", "Create a Data Sink"),
10
+ body: n("bigconsole.overview.guide.steps.createDataSink.body", "A Data Sink stores the data your widgets display. Give it a name and its **Unique Key** fills in automatically — you can edit the key now, but not after the sink is created. Click **Create DataSink** to save."),
11
+ tips: [n("bigconsole.overview.guide.steps.createDataSink.tip1", "Paste sample JSON into **Data Payload** to start with data right away.")],
12
+ optional: !1,
13
+ action: r.canCreateDataSink ? {
14
+ label: n("bigconsole.overview.guide.steps.createDataSink.action", "Create Data Sink"),
15
+ path: "/datasinks/new"
16
+ } : void 0,
17
+ noPermissionMessage: r.canCreateDataSink ? void 0 : o
18
+ }, c = {
19
+ id: "loadData",
20
+ title: n("bigconsole.overview.guide.steps.loadData.title", "Load your data"),
21
+ body: n("bigconsole.overview.guide.steps.loadData.body", "Open your sink from the Data Sinks list and click **Import Data**, then **Upload a CSV file**. The first row is used as column names. Or build a workflow that writes to the sink’s key to keep it up to date."),
22
+ tips: [n("bigconsole.overview.guide.steps.loadData.tip1", "A CSV import replaces the data already in the sink.")],
23
+ optional: !1,
24
+ action: r.canReadDataSinks ? {
25
+ label: n("bigconsole.overview.guide.steps.loadData.action", "Open Data Sinks"),
26
+ path: "/datasinks"
27
+ } : void 0,
28
+ secondaryAction: r.canCreateWorkflow ? {
29
+ label: n("bigconsole.overview.guide.steps.loadData.secondary", "Build a workflow"),
30
+ path: "/workflows/new"
31
+ } : void 0,
32
+ noPermissionMessage: r.canReadDataSinks ? void 0 : o
33
+ }, l = {
34
+ id: "shapeWithParser",
35
+ title: n("bigconsole.overview.guide.steps.shapeWithParser.title", "Shape it with a Parser"),
36
+ body: n("bigconsole.overview.guide.steps.shapeWithParser.body", "A Parser reshapes a Data Sink’s data with a jq or JSONata expression. Pick the source Data Sink, write the expression, and click **Run Test** to preview the output."),
37
+ tips: [n("bigconsole.overview.guide.steps.shapeWithParser.tip1", "Leave **Dashboard Scope** as Global so any dashboard can use it."), n("bigconsole.overview.guide.steps.shapeWithParser.tip2", "Skip this step if your data already fits the widget. A widget only lists parsers that read its Data Sink.")],
38
+ optional: !0,
39
+ action: r.canCreateParser ? {
40
+ label: n("bigconsole.overview.guide.steps.shapeWithParser.action", "Create Parser"),
41
+ path: "/parsers/new"
42
+ } : void 0,
43
+ noPermissionMessage: r.canCreateParser ? void 0 : o
44
+ }, u = {
45
+ id: "createDashboard",
46
+ title: n("bigconsole.overview.guide.steps.createDashboard.title", "Create a dashboard"),
47
+ body: n("bigconsole.overview.guide.steps.createDashboard.body", "Click **Create Dashboard**, give it a name, and start with **Blank Dashboard** — it opens straight in the builder. You can also switch to **From Template** when templates are available."),
48
+ tips: [n("bigconsole.overview.guide.steps.createDashboard.tip1", "Your plan includes a limited number of dashboards. Deleting or archiving one frees a slot.")],
49
+ optional: !1,
50
+ action: r.canCreateDashboard ? {
51
+ label: n("bigconsole.overview.guide.steps.createDashboard.action", "Create Dashboard"),
52
+ path: e
53
+ } : void 0,
54
+ noPermissionMessage: r.canCreateDashboard ? void 0 : o
55
+ }, d = a ? n("bigconsole.overview.guide.latestDashboardHint", "Opens “{{name}}”, your most recently updated dashboard.", { name: a.name }) : void 0, f = a && r.canBuildDashboards ? {
56
+ label: n("bigconsole.overview.guide.steps.addWidgets.action", "Open the builder"),
57
+ path: `/dashboards/${encodeURIComponent(a.id)}/builder`,
58
+ hint: d
59
+ } : r.canReadDashboards ? {
60
+ label: n("bigconsole.overview.guide.goToDashboards", "Go to Dashboards"),
61
+ path: "/dashboards"
62
+ } : void 0, p = {
63
+ id: "addWidgets",
64
+ title: n("bigconsole.overview.guide.steps.addWidgets.title", "Add widgets and connect data"),
65
+ body: n("bigconsole.overview.guide.steps.addWidgets.body", "In the builder, click **Add Widget**, then click or drag a widget from the Widgets panel. Its settings open — switch to the **Data** tab, choose your DataSink (and Parser, if you made one), then click **Save Changes**."),
66
+ tips: [n("bigconsole.overview.guide.steps.addWidgets.tip1", "If the widget looks empty, set **Data Path** on the Data tab, or field names like **X-Axis Field** or **Value Field** on the **Style** tab, to match your data’s keys.")],
67
+ optional: !1,
68
+ action: f,
69
+ noPermissionMessage: f ? void 0 : o
70
+ }, m = a && r.canReadDashboards ? {
71
+ label: n("bigconsole.overview.guide.steps.saveAndShare.action", "Open dashboard"),
72
+ path: `/dashboards/${encodeURIComponent(a.id)}`,
73
+ hint: d
74
+ } : r.canReadDashboards ? {
75
+ label: n("bigconsole.overview.guide.goToDashboards", "Go to Dashboards"),
76
+ path: "/dashboards"
77
+ } : void 0;
78
+ return [
79
+ s,
80
+ c,
81
+ l,
82
+ u,
83
+ p,
84
+ {
85
+ id: "saveAndShare",
86
+ title: n("bigconsole.overview.guide.steps.saveAndShare.title", "Save and share"),
87
+ body: n("bigconsole.overview.guide.steps.saveAndShare.body", "Click **Save** in the toolbar, then **Share**. Invite people, or set **General access** to Workspace so everyone here can see it."),
88
+ tips: [],
89
+ optional: !1,
90
+ action: m,
91
+ noPermissionMessage: m ? void 0 : o
92
+ }
93
+ ];
94
+ }
95
+ function r(e, t) {
96
+ return t ? {
97
+ title: e("bigconsole.overview.guide.ai.title", "Prefer to describe it?"),
98
+ body: e("bigconsole.overview.guide.ai.body", "Use AI Insights: type what you want and click **Generate dashboard**. It uses AI commands from your plan, and you’ll still connect data to each widget."),
99
+ action: {
100
+ label: e("bigconsole.overview.guide.ai.action", "Open AI Insights"),
101
+ path: "/insights"
102
+ }
103
+ } : null;
104
+ }
105
+ function i(e) {
106
+ let t = e.split("**");
107
+ if (t.length % 2 == 0) {
108
+ let e = t.splice(t.length - 2, 2).join("**");
109
+ t.push(e);
110
+ }
111
+ return t.map((e, t) => ({
112
+ text: e,
113
+ emphasis: t % 2 == 1
114
+ })).filter((e) => e.text.length > 0);
115
+ }
116
+ //#endregion
117
+ export { e as CREATE_DASHBOARD_PATH, r as buildAiAlternative, n as buildDashboardGuideSteps, i as splitGuideEmphasis };
118
+
119
+ //# sourceMappingURL=dashboardGuideSteps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboardGuideSteps.js","names":[],"sources":["../../../../src/bigconsole/components/overview/dashboardGuideSteps.ts"],"sourcesContent":["/**\n * Dashboard guide — the \"How to create a complete dashboard, end to end\" steps.\n *\n * Pure: takes a translator, the caller's permissions and the latest dashboard,\n * and returns the ordered steps the Overview renders. The copy mirrors the\n * labels users see on each target page (e.g. the DataSink builder's\n * \"Create DataSink\" button, the widget inspector's \"Data\" tab and\n * \"Save Changes\" footer), so a user can follow it click-for-click.\n *\n * `**bold**` markers in the copy highlight on-screen labels; the guide renders\n * them with `renderGuideEmphasis`. Paths are relative to the BigConsole base\n * path — the page navigates with `useAppNavigate`, which prefixes it.\n *\n * The guide is deliberately static: it never claims a step is \"done\". Existing\n * queries cannot tell workspace-wide whether a widget is bound or a board is\n * shared, so completion is not tracked.\n */\n\nexport type GuideTranslate = (key: string, fallback: string, params?: Record<string, string | number>) => string;\n\nexport type GuideStepId =\n 'createDataSink' | 'loadData' | 'shapeWithParser' | 'createDashboard' | 'addWidgets' | 'saveAndShare';\n\nexport interface GuideAction {\n label: string;\n path: string;\n /** Extra context shown under the action (e.g. which dashboard it opens). */\n hint?: string;\n}\n\nexport interface GuideStep {\n id: GuideStepId;\n title: string;\n body: string;\n tips: string[];\n optional: boolean;\n /** Primary call to action; absent when the caller lacks the permission. */\n action?: GuideAction;\n secondaryAction?: GuideAction;\n /** Shown instead of the primary action when it is not permitted. */\n noPermissionMessage?: string;\n}\n\nexport interface GuideAlternative {\n title: string;\n body: string;\n action: GuideAction;\n}\n\n/** Permission facts the guide needs, resolved by the page from BIGCONSOLE_PERMISSIONS. */\nexport interface GuidePermissions {\n canCreateDataSink: boolean;\n canReadDataSinks: boolean;\n canCreateWorkflow: boolean;\n canCreateParser: boolean;\n canCreateDashboard: boolean;\n canReadDashboards: boolean;\n /** Dashboard update AND widget create — needed to add widgets in the builder. */\n canBuildDashboards: boolean;\n canGenerateWithAi: boolean;\n}\n\nexport interface LatestDashboardRef {\n id: string;\n name: string;\n}\n\nexport interface BuildGuideStepsInput {\n tr: GuideTranslate;\n permissions: GuidePermissions;\n latestDashboard?: LatestDashboardRef | null;\n}\n\n/** Query param that makes the Dashboards page open its Create Dashboard dialog. */\nexport const CREATE_DASHBOARD_PATH = '/dashboards?create=1';\n\nfunction noPermission(tr: GuideTranslate): string {\n return tr(\n 'bigconsole.overview.guide.noPermission',\n \"You don't have permission for this step yet. Ask a workspace admin for access.\"\n );\n}\n\nexport function buildDashboardGuideSteps({ tr, permissions, latestDashboard }: BuildGuideStepsInput): GuideStep[] {\n const latest = latestDashboard ?? null;\n const denied = noPermission(tr);\n\n const createDataSink: GuideStep = {\n id: 'createDataSink',\n title: tr('bigconsole.overview.guide.steps.createDataSink.title', 'Create a Data Sink'),\n body: tr(\n 'bigconsole.overview.guide.steps.createDataSink.body',\n 'A Data Sink stores the data your widgets display. Give it a name and its **Unique Key** fills in automatically — you can edit the key now, but not after the sink is created. Click **Create DataSink** to save.'\n ),\n tips: [\n tr(\n 'bigconsole.overview.guide.steps.createDataSink.tip1',\n 'Paste sample JSON into **Data Payload** to start with data right away.'\n ),\n ],\n optional: false,\n action: permissions.canCreateDataSink\n ? {\n label: tr('bigconsole.overview.guide.steps.createDataSink.action', 'Create Data Sink'),\n path: '/datasinks/new',\n }\n : undefined,\n noPermissionMessage: permissions.canCreateDataSink ? undefined : denied,\n };\n\n const loadData: GuideStep = {\n id: 'loadData',\n title: tr('bigconsole.overview.guide.steps.loadData.title', 'Load your data'),\n body: tr(\n 'bigconsole.overview.guide.steps.loadData.body',\n 'Open your sink from the Data Sinks list and click **Import Data**, then **Upload a CSV file**. The first row is used as column names. Or build a workflow that writes to the sink’s key to keep it up to date.'\n ),\n tips: [tr('bigconsole.overview.guide.steps.loadData.tip1', 'A CSV import replaces the data already in the sink.')],\n optional: false,\n action: permissions.canReadDataSinks\n ? {\n label: tr('bigconsole.overview.guide.steps.loadData.action', 'Open Data Sinks'),\n path: '/datasinks',\n }\n : undefined,\n secondaryAction: permissions.canCreateWorkflow\n ? {\n label: tr('bigconsole.overview.guide.steps.loadData.secondary', 'Build a workflow'),\n path: '/workflows/new',\n }\n : undefined,\n noPermissionMessage: permissions.canReadDataSinks ? undefined : denied,\n };\n\n const shapeWithParser: GuideStep = {\n id: 'shapeWithParser',\n title: tr('bigconsole.overview.guide.steps.shapeWithParser.title', 'Shape it with a Parser'),\n body: tr(\n 'bigconsole.overview.guide.steps.shapeWithParser.body',\n 'A Parser reshapes a Data Sink’s data with a jq or JSONata expression. Pick the source Data Sink, write the expression, and click **Run Test** to preview the output.'\n ),\n tips: [\n tr(\n 'bigconsole.overview.guide.steps.shapeWithParser.tip1',\n 'Leave **Dashboard Scope** as Global so any dashboard can use it.'\n ),\n tr(\n 'bigconsole.overview.guide.steps.shapeWithParser.tip2',\n 'Skip this step if your data already fits the widget. A widget only lists parsers that read its Data Sink.'\n ),\n ],\n optional: true,\n action: permissions.canCreateParser\n ? {\n label: tr('bigconsole.overview.guide.steps.shapeWithParser.action', 'Create Parser'),\n path: '/parsers/new',\n }\n : undefined,\n noPermissionMessage: permissions.canCreateParser ? undefined : denied,\n };\n\n const createDashboard: GuideStep = {\n id: 'createDashboard',\n title: tr('bigconsole.overview.guide.steps.createDashboard.title', 'Create a dashboard'),\n body: tr(\n 'bigconsole.overview.guide.steps.createDashboard.body',\n 'Click **Create Dashboard**, give it a name, and start with **Blank Dashboard** — it opens straight in the builder. You can also switch to **From Template** when templates are available.'\n ),\n tips: [\n tr(\n 'bigconsole.overview.guide.steps.createDashboard.tip1',\n 'Your plan includes a limited number of dashboards. Deleting or archiving one frees a slot.'\n ),\n ],\n optional: false,\n action: permissions.canCreateDashboard\n ? {\n label: tr('bigconsole.overview.guide.steps.createDashboard.action', 'Create Dashboard'),\n path: CREATE_DASHBOARD_PATH,\n }\n : undefined,\n noPermissionMessage: permissions.canCreateDashboard ? undefined : denied,\n };\n\n const latestHint = latest\n ? tr('bigconsole.overview.guide.latestDashboardHint', 'Opens “{{name}}”, your most recently updated dashboard.', {\n name: latest.name,\n })\n : undefined;\n\n const addWidgetsAction: GuideAction | undefined =\n latest && permissions.canBuildDashboards\n ? {\n label: tr('bigconsole.overview.guide.steps.addWidgets.action', 'Open the builder'),\n path: `/dashboards/${encodeURIComponent(latest.id)}/builder`,\n hint: latestHint,\n }\n : permissions.canReadDashboards\n ? {\n label: tr('bigconsole.overview.guide.goToDashboards', 'Go to Dashboards'),\n path: '/dashboards',\n }\n : undefined;\n\n const addWidgets: GuideStep = {\n id: 'addWidgets',\n title: tr('bigconsole.overview.guide.steps.addWidgets.title', 'Add widgets and connect data'),\n body: tr(\n 'bigconsole.overview.guide.steps.addWidgets.body',\n 'In the builder, click **Add Widget**, then click or drag a widget from the Widgets panel. Its settings open — switch to the **Data** tab, choose your DataSink (and Parser, if you made one), then click **Save Changes**.'\n ),\n tips: [\n tr(\n 'bigconsole.overview.guide.steps.addWidgets.tip1',\n 'If the widget looks empty, set **Data Path** on the Data tab, or field names like **X-Axis Field** or **Value Field** on the **Style** tab, to match your data’s keys.'\n ),\n ],\n optional: false,\n action: addWidgetsAction,\n noPermissionMessage: addWidgetsAction ? undefined : denied,\n };\n\n const saveAndShareAction: GuideAction | undefined =\n latest && permissions.canReadDashboards\n ? {\n label: tr('bigconsole.overview.guide.steps.saveAndShare.action', 'Open dashboard'),\n path: `/dashboards/${encodeURIComponent(latest.id)}`,\n hint: latestHint,\n }\n : permissions.canReadDashboards\n ? {\n label: tr('bigconsole.overview.guide.goToDashboards', 'Go to Dashboards'),\n path: '/dashboards',\n }\n : undefined;\n\n const saveAndShare: GuideStep = {\n id: 'saveAndShare',\n title: tr('bigconsole.overview.guide.steps.saveAndShare.title', 'Save and share'),\n body: tr(\n 'bigconsole.overview.guide.steps.saveAndShare.body',\n 'Click **Save** in the toolbar, then **Share**. Invite people, or set **General access** to Workspace so everyone here can see it.'\n ),\n tips: [],\n optional: false,\n action: saveAndShareAction,\n noPermissionMessage: saveAndShareAction ? undefined : denied,\n };\n\n return [createDataSink, loadData, shapeWithParser, createDashboard, addWidgets, saveAndShare];\n}\n\n/** The AI Insights shortcut shown under the steps; `null` without prompt create permission. */\nexport function buildAiAlternative(tr: GuideTranslate, canGenerateWithAi: boolean): GuideAlternative | null {\n if (!canGenerateWithAi) return null;\n return {\n title: tr('bigconsole.overview.guide.ai.title', 'Prefer to describe it?'),\n body: tr(\n 'bigconsole.overview.guide.ai.body',\n 'Use AI Insights: type what you want and click **Generate dashboard**. It uses AI commands from your plan, and you’ll still connect data to each widget.'\n ),\n action: {\n label: tr('bigconsole.overview.guide.ai.action', 'Open AI Insights'),\n path: '/insights',\n },\n };\n}\n\nexport type GuideTextSegment = { text: string; emphasis: boolean };\n\n/**\n * Split copy on `**` markers into plain / emphasised segments. An unmatched\n * trailing marker is treated as literal text so a translation typo never\n * swallows the rest of the sentence.\n */\nexport function splitGuideEmphasis(text: string): GuideTextSegment[] {\n const parts = text.split('**');\n if (parts.length % 2 === 0) {\n // Odd number of markers: re-join the dangling tail as plain text.\n const tail = parts.splice(parts.length - 2, 2).join('**');\n parts.push(tail);\n }\n return parts\n .map((part, index) => ({ text: part, emphasis: index % 2 === 1 }))\n .filter((segment) => segment.text.length > 0);\n}\n"],"mappings":";AA0EA,IAAa,IAAwB;AAErC,SAAS,EAAa,GAA4B;AAChD,QAAO,EACL,0CACA,iFACD;;AAGH,SAAgB,EAAyB,EAAE,OAAI,gBAAa,sBAAsD;CAChH,IAAM,IAAS,KAAmB,MAC5B,IAAS,EAAa,EAAG,EAEzB,IAA4B;EAChC,IAAI;EACJ,OAAO,EAAG,wDAAwD,qBAAqB;EACvF,MAAM,EACJ,uDACA,mNACD;EACD,MAAM,CACJ,EACE,uDACA,yEACD,CACF;EACD,UAAU;EACV,QAAQ,EAAY,oBAChB;GACE,OAAO,EAAG,yDAAyD,mBAAmB;GACtF,MAAM;GACP,GACD,KAAA;EACJ,qBAAqB,EAAY,oBAAoB,KAAA,IAAY;EAClE,EAEK,IAAsB;EAC1B,IAAI;EACJ,OAAO,EAAG,kDAAkD,iBAAiB;EAC7E,MAAM,EACJ,iDACA,iNACD;EACD,MAAM,CAAC,EAAG,iDAAiD,sDAAsD,CAAC;EAClH,UAAU;EACV,QAAQ,EAAY,mBAChB;GACE,OAAO,EAAG,mDAAmD,kBAAkB;GAC/E,MAAM;GACP,GACD,KAAA;EACJ,iBAAiB,EAAY,oBACzB;GACE,OAAO,EAAG,sDAAsD,mBAAmB;GACnF,MAAM;GACP,GACD,KAAA;EACJ,qBAAqB,EAAY,mBAAmB,KAAA,IAAY;EACjE,EAEK,IAA6B;EACjC,IAAI;EACJ,OAAO,EAAG,yDAAyD,yBAAyB;EAC5F,MAAM,EACJ,wDACA,uKACD;EACD,MAAM,CACJ,EACE,wDACA,mEACD,EACD,EACE,wDACA,4GACD,CACF;EACD,UAAU;EACV,QAAQ,EAAY,kBAChB;GACE,OAAO,EAAG,0DAA0D,gBAAgB;GACpF,MAAM;GACP,GACD,KAAA;EACJ,qBAAqB,EAAY,kBAAkB,KAAA,IAAY;EAChE,EAEK,IAA6B;EACjC,IAAI;EACJ,OAAO,EAAG,yDAAyD,qBAAqB;EACxF,MAAM,EACJ,wDACA,4LACD;EACD,MAAM,CACJ,EACE,wDACA,6FACD,CACF;EACD,UAAU;EACV,QAAQ,EAAY,qBAChB;GACE,OAAO,EAAG,0DAA0D,mBAAmB;GACvF,MAAM;GACP,GACD,KAAA;EACJ,qBAAqB,EAAY,qBAAqB,KAAA,IAAY;EACnE,EAEK,IAAa,IACf,EAAG,iDAAiD,2DAA2D,EAC7G,MAAM,EAAO,MACd,CAAC,GACF,KAAA,GAEE,IACJ,KAAU,EAAY,qBAClB;EACE,OAAO,EAAG,qDAAqD,mBAAmB;EAClF,MAAM,eAAe,mBAAmB,EAAO,GAAG,CAAC;EACnD,MAAM;EACP,GACD,EAAY,oBACV;EACE,OAAO,EAAG,4CAA4C,mBAAmB;EACzE,MAAM;EACP,GACD,KAAA,GAEF,IAAwB;EAC5B,IAAI;EACJ,OAAO,EAAG,oDAAoD,+BAA+B;EAC7F,MAAM,EACJ,mDACA,6NACD;EACD,MAAM,CACJ,EACE,mDACA,yKACD,CACF;EACD,UAAU;EACV,QAAQ;EACR,qBAAqB,IAAmB,KAAA,IAAY;EACrD,EAEK,IACJ,KAAU,EAAY,oBAClB;EACE,OAAO,EAAG,uDAAuD,iBAAiB;EAClF,MAAM,eAAe,mBAAmB,EAAO,GAAG;EAClD,MAAM;EACP,GACD,EAAY,oBACV;EACE,OAAO,EAAG,4CAA4C,mBAAmB;EACzE,MAAM;EACP,GACD,KAAA;AAeR,QAAO;EAAC;EAAgB;EAAU;EAAiB;EAAiB;EAbpC;GAC9B,IAAI;GACJ,OAAO,EAAG,sDAAsD,iBAAiB;GACjF,MAAM,EACJ,qDACA,oIACD;GACD,MAAM,EAAE;GACR,UAAU;GACV,QAAQ;GACR,qBAAqB,IAAqB,KAAA,IAAY;GACvD;EAE4F;;AAI/F,SAAgB,EAAmB,GAAoB,GAAqD;AAE1G,QADK,IACE;EACL,OAAO,EAAG,sCAAsC,yBAAyB;EACzE,MAAM,EACJ,qCACA,0JACD;EACD,QAAQ;GACN,OAAO,EAAG,uCAAuC,mBAAmB;GACpE,MAAM;GACP;EACF,GAX8B;;AAqBjC,SAAgB,EAAmB,GAAkC;CACnE,IAAM,IAAQ,EAAK,MAAM,KAAK;AAC9B,KAAI,EAAM,SAAS,KAAM,GAAG;EAE1B,IAAM,IAAO,EAAM,OAAO,EAAM,SAAS,GAAG,EAAE,CAAC,KAAK,KAAK;AACzD,IAAM,KAAK,EAAK;;AAElB,QAAO,EACJ,KAAK,GAAM,OAAW;EAAE,MAAM;EAAM,UAAU,IAAQ,KAAM;EAAG,EAAE,CACjE,QAAQ,MAAY,EAAQ,KAAK,SAAS,EAAE"}
@@ -0,0 +1,8 @@
1
+ import "./dashboardGuideSteps.js";
2
+ import "./DashboardGuide.js";
3
+ import "./DataHealthCard.js";
4
+ import "./OverviewQuickActions.js";
5
+ import "./OverviewStatCard.js";
6
+ import "./RecentDashboardsPanel.js";
7
+ import "./overviewModel.js";
8
+ import "./useOverviewData.js";
@@ -0,0 +1,119 @@
1
+ function e(e) {
2
+ switch (e) {
3
+ case "PUBLISHED": return "PUBLISHED";
4
+ case "ARCHIVED": return "ARCHIVED";
5
+ default: return "DRAFT";
6
+ }
7
+ }
8
+ function t(t) {
9
+ if (!t) return {
10
+ total: void 0,
11
+ recent: [],
12
+ latest: null
13
+ };
14
+ let n = t.listDashboards.edges.slice(0, 5).map(({ node: t }) => ({
15
+ id: t.id,
16
+ name: t.name,
17
+ description: t.description?.trim() ? t.description.trim() : null,
18
+ status: e(t.status),
19
+ widgetCount: Math.max(0, t.widgetCount ?? 0),
20
+ updatedAt: t.updatedAt
21
+ }));
22
+ return {
23
+ total: t.listDashboards.totalCount,
24
+ recent: n,
25
+ latest: n[0] ?? null
26
+ };
27
+ }
28
+ function n(e) {
29
+ if (!e) return -Infinity;
30
+ let t = Date.parse(e);
31
+ return Number.isNaN(t) ? -Infinity : t;
32
+ }
33
+ function r(e, t) {
34
+ return e.edges.map(({ node: e }) => ({
35
+ id: e.id,
36
+ name: e.name?.trim() ? e.name.trim() : e.key,
37
+ status: t,
38
+ lastRefreshedAt: e.lastRefreshedAt ?? null
39
+ })).sort((e, t) => n(e.lastRefreshedAt) - n(t.lastRefreshedAt));
40
+ }
41
+ function i(e, t = 5) {
42
+ if (!e) return {
43
+ total: void 0,
44
+ ready: void 0,
45
+ attentionCount: void 0,
46
+ rows: [],
47
+ hiddenCount: 0
48
+ };
49
+ let n = e.errored.totalCount + e.stale.totalCount, i = /* @__PURE__ */ new Set(), a = [...r(e.errored, "error"), ...r(e.stale, "stale")].filter((e) => i.has(e.id) ? !1 : (i.add(e.id), !0)).slice(0, Math.max(0, t));
50
+ return {
51
+ total: e.all.totalCount,
52
+ ready: e.ready.totalCount,
53
+ attentionCount: n,
54
+ rows: a,
55
+ hiddenCount: Math.max(0, n - a.length)
56
+ };
57
+ }
58
+ function a(e) {
59
+ return e.permissionsLoading ? null : !e.dashboardsAllowed || e.dashboardsFailed ? !0 : e.dashboardsTotal === void 0 ? e.dashboardsLoading ? null : !0 : e.dashboardsTotal === 0;
60
+ }
61
+ var o = 6e4, s = 60 * o, c = 24 * s;
62
+ function l(e, t) {
63
+ let n = Date.parse(e);
64
+ if (Number.isNaN(n)) return null;
65
+ let r = t.getTime() - n;
66
+ return r < o ? { unit: "justNow" } : r < s ? {
67
+ unit: "minutes",
68
+ count: Math.floor(r / o)
69
+ } : r < c ? {
70
+ unit: "hours",
71
+ count: Math.floor(r / s)
72
+ } : r < 7 * c ? {
73
+ unit: "days",
74
+ count: Math.floor(r / c)
75
+ } : {
76
+ unit: "date",
77
+ date: new Date(n)
78
+ };
79
+ }
80
+ function u(e) {
81
+ try {
82
+ return new Intl.RelativeTimeFormat(e, {
83
+ numeric: "always",
84
+ style: "short"
85
+ });
86
+ } catch {
87
+ return new Intl.RelativeTimeFormat(void 0, {
88
+ numeric: "always",
89
+ style: "short"
90
+ });
91
+ }
92
+ }
93
+ function d(e, t) {
94
+ let n = {
95
+ month: "short",
96
+ day: "numeric",
97
+ year: t ? "numeric" : void 0
98
+ };
99
+ try {
100
+ return new Intl.DateTimeFormat(e, n);
101
+ } catch {
102
+ return new Intl.DateTimeFormat(void 0, n);
103
+ }
104
+ }
105
+ function f(e, t, n, r) {
106
+ let i = l(e, t);
107
+ if (!i) return null;
108
+ switch (i.unit) {
109
+ case "justNow": return n("bigconsole.overview.time.justNow", "just now");
110
+ case "minutes": return u(r).format(-i.count, "minute");
111
+ case "hours": return u(r).format(-i.count, "hour");
112
+ case "days": return u(r).format(-i.count, "day");
113
+ case "date": return d(r, i.date.getFullYear() !== t.getFullYear()).format(i.date);
114
+ }
115
+ }
116
+ //#endregion
117
+ export { f as formatRelativeTime, a as resolveGuideDefaultExpanded, t as summarizeDashboards, i as summarizeDataSinks, e as toDashboardListStatus, l as toRelativeTime };
118
+
119
+ //# sourceMappingURL=overviewModel.js.map