@burdenoff/microfe-bigconsole 2026.1001.2 → 2026.1001.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/components/overview/OverviewQuickActions.js +2 -2
- package/dist/bigconsole/components/overview/OverviewQuickActions.js.map +1 -1
- package/dist/bigconsole/components/overview/OverviewStatCard.d.ts +2 -0
- package/dist/bigconsole/components/overview/OverviewStatCard.d.ts.map +1 -1
- package/dist/bigconsole/components/overview/OverviewStatCard.js +13 -13
- package/dist/bigconsole/components/overview/OverviewStatCard.js.map +1 -1
- package/dist/bigconsole/components/overview/RecentDashboardsPanel.js +6 -7
- package/dist/bigconsole/components/overview/RecentDashboardsPanel.js.map +1 -1
- package/dist/bigconsole/components/overview/useOverviewData.d.ts +3 -1
- package/dist/bigconsole/components/overview/useOverviewData.d.ts.map +1 -1
- package/dist/bigconsole/components/overview/useOverviewData.js +61 -30
- package/dist/bigconsole/components/overview/useOverviewData.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanView.d.ts +4 -18
- package/dist/bigconsole/components/widgets/kanban/KanbanView.d.ts.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanView.js +145 -161
- package/dist/bigconsole/components/widgets/kanban/KanbanView.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.d.ts.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +13 -10
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/model.d.ts +42 -0
- package/dist/bigconsole/components/widgets/kanban/model.d.ts.map +1 -0
- package/dist/bigconsole/components/widgets/kanban/model.js +145 -0
- package/dist/bigconsole/components/widgets/kanban/model.js.map +1 -0
- package/dist/bigconsole/pages/OverviewPage.d.ts.map +1 -1
- package/dist/bigconsole/pages/OverviewPage.js +183 -163
- package/dist/bigconsole/pages/OverviewPage.js.map +1 -1
- package/dist/generated/wspace-operations.d.ts +68 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/generated/wspace-operations.js +87 -59
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/translations/en.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/kanban/model.ts"],"sourcesContent":["export interface KanbanColumn {\n id: string;\n title: string;\n color?: string;\n}\n\nexport interface KanbanTask {\n id: string;\n title: string;\n column: string;\n description?: string;\n stableId?: boolean;\n priority?: 'high' | 'medium' | 'low';\n assignee?: string;\n labels?: string[];\n dueDate?: string;\n}\n\nexport interface KanbanData {\n columns?: KanbanColumn[];\n tasks?: KanbanTask[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n// Field names we try, in order, when the widget config doesn't name one\n// explicitly. A DataSink/parser typically emits a flat array of rows (e.g. the\n// Linear \"Kanban board\" parser -> [{ id, title, status, assignee, ... }]), so\n// the board is derived from a status/column field on those rows.\nconst COLUMN_FIELD_CANDIDATES = ['status', 'state', 'column', 'stage', 'category', 'phase', 'group'];\nconst TITLE_FIELD_CANDIDATES = ['title', 'name', 'summary', 'label', 'identifier', 'id'];\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/** Coerce parser output into an array of object rows (handles common envelopes). */\nfunction asRows(data: unknown): Record<string, unknown>[] {\n const onlyObjects = (arr: unknown[]): Record<string, unknown>[] =>\n arr.filter((r): r is Record<string, unknown> => !!r && typeof r === 'object' && !Array.isArray(r));\n\n if (Array.isArray(data)) return onlyObjects(data);\n\n if (data && typeof data === 'object') {\n const obj = data as Record<string, unknown>;\n for (const key of ['tasks', 'rows', 'items', 'data', 'nodes', 'results', 'records']) {\n if (Array.isArray(obj[key])) return onlyObjects(obj[key] as unknown[]);\n }\n const firstArray = Object.values(obj).find((v) => Array.isArray(v));\n if (Array.isArray(firstArray)) return onlyObjects(firstArray);\n }\n return [];\n}\n\n/** First key that actually exists on the sample row. */\nfunction firstPresent(row: Record<string, unknown>, keys: (string | undefined)[]): string | undefined {\n for (const key of keys) {\n if (key && key in row) return key;\n }\n return undefined;\n}\n\nfunction normalizePriority(value: unknown): KanbanTask['priority'] {\n const v = asString(value)?.toLowerCase();\n if (!v) return undefined;\n if (v === 'urgent' || v === 'high') return 'high';\n if (v === 'medium' || v === 'normal') return 'medium';\n if (v === 'low') return 'low';\n return undefined;\n}\n\n/** Index fallbacks are local-only and cannot collide with source IDs. */\nfunction taskIdentities(values: unknown[]): { id: string; stableId: boolean }[] {\n const ids = values.map((value) => (typeof value === 'string' || typeof value === 'number' ? String(value) : ''));\n const counts = new Map<string, number>();\n ids.forEach((id) => {\n if (id) counts.set(id, (counts.get(id) ?? 0) + 1);\n });\n const used = new Set(ids);\n return ids.map((id, index) => {\n if (id && counts.get(id) === 1) return { id, stableId: true };\n let localId = `row-${index}`;\n while (used.has(localId)) localId += '-local';\n used.add(localId);\n return { id: localId, stableId: false };\n });\n}\n\n/**\n * Normalize widget data into the board's { columns, tasks } shape.\n *\n * KanbanWidget previously required the exact canonical shape and rendered\n * \"No board data\" for any DataSink/parser output that was a plain array of\n * rows (which is what parsers actually emit) — BOFF-4110. This adapts a flat\n * array into columns (distinct values of a status/column field, in first-seen\n * order) plus one card per row, honoring config field mappings with fallbacks.\n */\nexport function normalizeKanban(\n data: unknown,\n config?: Record<string, unknown>\n): { columns: KanbanColumn[]; tasks: KanbanTask[] } {\n // Canonical shape passes through untouched (back-compat).\n if (data && typeof data === 'object' && !Array.isArray(data) && Array.isArray((data as KanbanData).columns)) {\n const d = data as KanbanData;\n const identities = taskIdentities((d.tasks ?? []).map((task) => task.id));\n return {\n columns: d.columns ?? [],\n tasks: (d.tasks ?? []).map((task, index) => ({ ...task, ...identities[index] })),\n };\n }\n\n const rows = asRows(data);\n if (rows.length === 0) return { columns: [], tasks: [] };\n\n const sample = rows[0];\n const columnField = firstPresent(sample, [\n asString(config?.columnField),\n asString(config?.groupField),\n asString(config?.statusField),\n ...COLUMN_FIELD_CANDIDATES,\n asString(config?.xAxisField),\n asString(config?.labelField),\n ]);\n if (!columnField) return { columns: [], tasks: [] };\n\n const titleField = firstPresent(sample, [\n asString(config?.cardTitleField),\n asString(config?.titleField),\n ...TITLE_FIELD_CANDIDATES,\n asString(config?.labelField),\n ]);\n\n const identities = taskIdentities(rows.map((row) => row.id ?? row.identifier ?? row.key));\n const columnTitles = new Map<string, string>();\n const columnOrder: string[] = [];\n const tasks: KanbanTask[] = rows.map((row, i) => {\n const columnRaw = row[columnField];\n const column = columnRaw == null || columnRaw === '' ? 'Uncategorized' : String(columnRaw);\n if (!columnTitles.has(column)) {\n columnTitles.set(column, column);\n columnOrder.push(column);\n }\n const labels = Array.isArray(row.labels) ? row.labels.filter((l): l is string => typeof l === 'string') : undefined;\n return {\n ...identities[i],\n description: asString(row[asString(config?.cardDescriptionField) ?? 'description']),\n title: titleField && row[titleField] != null ? String(row[titleField]) : `Item ${i + 1}`,\n column,\n assignee: asString(row.assignee),\n priority: normalizePriority(row.priority ?? row.priorityLabel),\n labels: labels && labels.length > 0 ? labels : undefined,\n };\n });\n\n const columns: KanbanColumn[] = columnOrder.map((id) => ({ id, title: columnTitles.get(id) ?? id }));\n return { columns, tasks };\n}\n\nexport interface KanbanColumnMove {\n taskId: string;\n sourceColumn: string;\n targetColumn: string;\n}\n\nexport function validKanbanMoves(value: unknown): KanbanColumnMove[] {\n if (!Array.isArray(value)) return [];\n return value.filter((move): move is KanbanColumnMove => {\n if (!move || typeof move !== 'object') return false;\n const candidate = move as Partial<KanbanColumnMove>;\n return (\n typeof candidate.taskId === 'string' &&\n typeof candidate.sourceColumn === 'string' &&\n typeof candidate.targetColumn === 'string'\n );\n });\n}\n\n/** Overrides only affect a current card in its original source column. */\nexport function applyKanbanMoves(tasks: KanbanTask[], columns: KanbanColumn[], value: unknown): KanbanTask[] {\n const moves = validKanbanMoves(value);\n const targets = new Set(columns.map((column) => column.id));\n return tasks.map((task) => {\n const move = [...moves]\n .reverse()\n .find(\n (candidate) =>\n candidate.taskId === task.id && candidate.sourceColumn === task.column && targets.has(candidate.targetColumn)\n );\n return move ? { ...task, column: move.targetColumn } : task;\n });\n}\n"],"mappings":";AA+BA,IAAM,IAA0B;CAAC;CAAU;CAAS;CAAU;CAAS;CAAY;CAAS;CAAQ,EAC9F,IAAyB;CAAC;CAAS;CAAQ;CAAW;CAAS;CAAc;CAAK;AAExF,SAAS,EAAS,GAAoC;AACpD,QAAO,OAAO,KAAU,YAAY,EAAM,SAAS,IAAI,IAAQ,KAAA;;AAIjE,SAAS,EAAO,GAA0C;CACxD,IAAM,KAAe,MACnB,EAAI,QAAQ,MAAoC,CAAC,CAAC,KAAK,OAAO,KAAM,YAAY,CAAC,MAAM,QAAQ,EAAE,CAAC;AAEpG,KAAI,MAAM,QAAQ,EAAK,CAAE,QAAO,EAAY,EAAK;AAEjD,KAAI,KAAQ,OAAO,KAAS,UAAU;EACpC,IAAM,IAAM;AACZ,OAAK,IAAM,KAAO;GAAC;GAAS;GAAQ;GAAS;GAAQ;GAAS;GAAW;GAAU,CACjF,KAAI,MAAM,QAAQ,EAAI,GAAK,CAAE,QAAO,EAAY,EAAI,GAAkB;EAExE,IAAM,IAAa,OAAO,OAAO,EAAI,CAAC,MAAM,MAAM,MAAM,QAAQ,EAAE,CAAC;AACnE,MAAI,MAAM,QAAQ,EAAW,CAAE,QAAO,EAAY,EAAW;;AAE/D,QAAO,EAAE;;AAIX,SAAS,EAAa,GAA8B,GAAkD;AACpG,MAAK,IAAM,KAAO,EAChB,KAAI,KAAO,KAAO,EAAK,QAAO;;AAKlC,SAAS,EAAkB,GAAwC;CACjE,IAAM,IAAI,EAAS,EAAM,EAAE,aAAa;AACnC,QACL;MAAI,MAAM,YAAY,MAAM,OAAQ,QAAO;AAC3C,MAAI,MAAM,YAAY,MAAM,SAAU,QAAO;AAC7C,MAAI,MAAM,MAAO,QAAO;;;AAK1B,SAAS,EAAe,GAAwD;CAC9E,IAAM,IAAM,EAAO,KAAK,MAAW,OAAO,KAAU,YAAY,OAAO,KAAU,WAAW,OAAO,EAAM,GAAG,GAAI,EAC1G,oBAAS,IAAI,KAAqB;AACxC,GAAI,SAAS,MAAO;AAClB,EAAI,KAAI,EAAO,IAAI,IAAK,EAAO,IAAI,EAAG,IAAI,KAAK,EAAE;GACjD;CACF,IAAM,IAAO,IAAI,IAAI,EAAI;AACzB,QAAO,EAAI,KAAK,GAAI,MAAU;AAC5B,MAAI,KAAM,EAAO,IAAI,EAAG,KAAK,EAAG,QAAO;GAAE;GAAI,UAAU;GAAM;EAC7D,IAAI,IAAU,OAAO;AACrB,SAAO,EAAK,IAAI,EAAQ,EAAE,MAAW;AAErC,SADA,EAAK,IAAI,EAAQ,EACV;GAAE,IAAI;GAAS,UAAU;GAAO;GACvC;;AAYJ,SAAgB,EACd,GACA,GACkD;AAElD,KAAI,KAAQ,OAAO,KAAS,YAAY,CAAC,MAAM,QAAQ,EAAK,IAAI,MAAM,QAAS,EAAoB,QAAQ,EAAE;EAC3G,IAAM,IAAI,GACJ,IAAa,GAAgB,EAAE,SAAS,EAAE,EAAE,KAAK,MAAS,EAAK,GAAG,CAAC;AACzE,SAAO;GACL,SAAS,EAAE,WAAW,EAAE;GACxB,QAAQ,EAAE,SAAS,EAAE,EAAE,KAAK,GAAM,OAAW;IAAE,GAAG;IAAM,GAAG,EAAW;IAAQ,EAAE;GACjF;;CAGH,IAAM,IAAO,EAAO,EAAK;AACzB,KAAI,EAAK,WAAW,EAAG,QAAO;EAAE,SAAS,EAAE;EAAE,OAAO,EAAE;EAAE;CAExD,IAAM,IAAS,EAAK,IACd,IAAc,EAAa,GAAQ;EACvC,EAAS,GAAQ,YAAY;EAC7B,EAAS,GAAQ,WAAW;EAC5B,EAAS,GAAQ,YAAY;EAC7B,GAAG;EACH,EAAS,GAAQ,WAAW;EAC5B,EAAS,GAAQ,WAAW;EAC7B,CAAC;AACF,KAAI,CAAC,EAAa,QAAO;EAAE,SAAS,EAAE;EAAE,OAAO,EAAE;EAAE;CAEnD,IAAM,IAAa,EAAa,GAAQ;EACtC,EAAS,GAAQ,eAAe;EAChC,EAAS,GAAQ,WAAW;EAC5B,GAAG;EACH,EAAS,GAAQ,WAAW;EAC7B,CAAC,EAEI,IAAa,EAAe,EAAK,KAAK,MAAQ,EAAI,MAAM,EAAI,cAAc,EAAI,IAAI,CAAC,EACnF,oBAAe,IAAI,KAAqB,EACxC,IAAwB,EAAE,EAC1B,IAAsB,EAAK,KAAK,GAAK,MAAM;EAC/C,IAAM,IAAY,EAAI,IAChB,IAAS,KAAa,QAAQ,MAAc,KAAK,kBAAkB,OAAO,EAAU;AAC1F,EAAK,EAAa,IAAI,EAAO,KAC3B,EAAa,IAAI,GAAQ,EAAO,EAChC,EAAY,KAAK,EAAO;EAE1B,IAAM,IAAS,MAAM,QAAQ,EAAI,OAAO,GAAG,EAAI,OAAO,QAAQ,MAAmB,OAAO,KAAM,SAAS,GAAG,KAAA;AAC1G,SAAO;GACL,GAAG,EAAW;GACd,aAAa,EAAS,EAAI,EAAS,GAAQ,qBAAqB,IAAI,eAAe;GACnF,OAAO,KAAc,EAAI,MAAe,OAAO,OAAO,EAAI,GAAY,GAAG,QAAQ,IAAI;GACrF;GACA,UAAU,EAAS,EAAI,SAAS;GAChC,UAAU,EAAkB,EAAI,YAAY,EAAI,cAAc;GAC9D,QAAQ,KAAU,EAAO,SAAS,IAAI,IAAS,KAAA;GAChD;GACD;AAGF,QAAO;EAAE,SADuB,EAAY,KAAK,OAAQ;GAAE;GAAI,OAAO,EAAa,IAAI,EAAG,IAAI;GAAI,EAAE;EAClF;EAAO;;AAS3B,SAAgB,EAAiB,GAAoC;AAEnE,QADK,MAAM,QAAQ,EAAM,GAClB,EAAM,QAAQ,MAAmC;AACtD,MAAI,CAAC,KAAQ,OAAO,KAAS,SAAU,QAAO;EAC9C,IAAM,IAAY;AAClB,SACE,OAAO,EAAU,UAAW,YAC5B,OAAO,EAAU,gBAAiB,YAClC,OAAO,EAAU,gBAAiB;GAEpC,GATgC,EAAE;;AAatC,SAAgB,EAAiB,GAAqB,GAAyB,GAA8B;CAC3G,IAAM,IAAQ,EAAiB,EAAM,EAC/B,IAAU,IAAI,IAAI,EAAQ,KAAK,MAAW,EAAO,GAAG,CAAC;AAC3D,QAAO,EAAM,KAAK,MAAS;EACzB,IAAM,IAAO,CAAC,GAAG,EAAM,CACpB,SAAS,CACT,MACE,MACC,EAAU,WAAW,EAAK,MAAM,EAAU,iBAAiB,EAAK,UAAU,EAAQ,IAAI,EAAU,aAAa,CAChH;AACH,SAAO,IAAO;GAAE,GAAG;GAAM,QAAQ,EAAK;GAAc,GAAG;GACvD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverviewPage.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/pages/OverviewPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,EAA2D,MAAM,OAAO,CAAC;AA2BzF,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"OverviewPage.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/pages/OverviewPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,EAA2D,MAAM,OAAO,CAAC;AA2BzF,eAAO,MAAM,YAAY,EAAE,EA4UzB,CAAC;AAEH,eAAe,YAAY,CAAC"}
|
|
@@ -1,54 +1,54 @@
|
|
|
1
1
|
import { useAppNavigate as e } from "../contexts/NavigationContext.js";
|
|
2
2
|
import "../contexts/index.js";
|
|
3
3
|
import { BIGCONSOLE_PERMISSIONS as t } from "../../constants/permissions.js";
|
|
4
|
-
import { useTr as
|
|
5
|
-
import { CREATE_DASHBOARD_PATH as
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import { formatRelativeTime as
|
|
12
|
-
import { useOverviewData as
|
|
4
|
+
import { useTr as ee } from "../../shared/hooks/useTr.js";
|
|
5
|
+
import { CREATE_DASHBOARD_PATH as n, buildAiAlternative as r, buildDashboardGuideSteps as i } from "../components/overview/dashboardGuideSteps.js";
|
|
6
|
+
import a from "../components/overview/DashboardGuide.js";
|
|
7
|
+
import o from "../components/overview/DataHealthCard.js";
|
|
8
|
+
import s from "../components/overview/OverviewQuickActions.js";
|
|
9
|
+
import c from "../components/overview/OverviewStatCard.js";
|
|
10
|
+
import te from "../components/overview/RecentDashboardsPanel.js";
|
|
11
|
+
import { formatRelativeTime as ne, resolveGuideDefaultExpanded as re, summarizeDashboards as ie, summarizeDataSinks as l } from "../components/overview/overviewModel.js";
|
|
12
|
+
import { useOverviewData as u } from "../components/overview/useOverviewData.js";
|
|
13
13
|
import "../components/overview/index.js";
|
|
14
|
-
import { memo as
|
|
15
|
-
import { usePermissions as
|
|
16
|
-
import {
|
|
17
|
-
import { AuroraBackground as
|
|
18
|
-
import { Fragment as
|
|
19
|
-
import { useI18n as
|
|
20
|
-
import { PageHeader as
|
|
21
|
-
import { MotionItem as
|
|
14
|
+
import { memo as d, useCallback as f, useEffect as p, useMemo as m, useRef as h, useState as g } from "react";
|
|
15
|
+
import { usePermissions as _ } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
16
|
+
import { Database as v, FileCode2 as y, LayoutDashboard as b, Plus as x, RefreshCw as ae, Sparkles as oe, Workflow as S } from "lucide-react";
|
|
17
|
+
import { AuroraBackground as se, Button as ce, GradientText as le, PagePurpose as ue } from "@burdenoff/fe-libs/ui";
|
|
18
|
+
import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
19
|
+
import { useI18n as E } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
20
|
+
import { PageHeader as D } from "@burdenoff/fe-libs/chrome";
|
|
21
|
+
import { MotionItem as O, MotionList as k, MotionPage as A } from "@burdenoff/fe-libs/shared/motion";
|
|
22
22
|
//#region src/bigconsole/pages/OverviewPage.tsx
|
|
23
|
-
var
|
|
24
|
-
let
|
|
25
|
-
tr:
|
|
26
|
-
latestDashboard:
|
|
23
|
+
var j = d(function() {
|
|
24
|
+
let d = e(), j = ee(), { locale: M } = E(), { hasPermission: N } = _(), { permissionsLoading: P, dashboards: F, dataSinks: I, parsers: L, pipelines: R, refetchAll: z } = u(), B = f((e) => d(e), [d]), V = m(() => ie(F.data), [F.data]), H = m(() => l(I.data), [I.data]), U = f((e) => ne(e, /* @__PURE__ */ new Date(), j, M), [j, M]), W = f((e) => !P && N(e), [P, N]), de = m(() => i({
|
|
25
|
+
tr: j,
|
|
26
|
+
latestDashboard: V.latest,
|
|
27
27
|
permissions: {
|
|
28
|
-
canCreateDataSink:
|
|
29
|
-
canReadDataSinks:
|
|
30
|
-
canCreateWorkflow:
|
|
31
|
-
canCreateParser:
|
|
32
|
-
canCreateDashboard:
|
|
33
|
-
canReadDashboards:
|
|
34
|
-
canBuildDashboards:
|
|
35
|
-
canGenerateWithAi:
|
|
28
|
+
canCreateDataSink: W(t.DATASINK_CREATE),
|
|
29
|
+
canReadDataSinks: W(t.DATASINK_READ),
|
|
30
|
+
canCreateWorkflow: W(t.WORKFLOW_CREATE),
|
|
31
|
+
canCreateParser: W(t.PARSER_CREATE),
|
|
32
|
+
canCreateDashboard: W(t.DASHBOARD_CREATE),
|
|
33
|
+
canReadDashboards: W(t.DASHBOARD_READ),
|
|
34
|
+
canBuildDashboards: W(t.DASHBOARD_UPDATE) && W(t.WIDGET_CREATE),
|
|
35
|
+
canGenerateWithAi: W(t.PROMPT_CREATE)
|
|
36
36
|
}
|
|
37
37
|
}), [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
]), fe =
|
|
38
|
+
j,
|
|
39
|
+
W,
|
|
40
|
+
V.latest
|
|
41
|
+
]), fe = m(() => r(j, W(t.PROMPT_CREATE)), [j, W]), G = m(() => [
|
|
42
42
|
{
|
|
43
43
|
permission: t.DASHBOARD_CREATE,
|
|
44
44
|
action: {
|
|
45
45
|
id: "create-dashboard",
|
|
46
|
-
label:
|
|
47
|
-
icon: /* @__PURE__ */
|
|
46
|
+
label: j("bigconsole.overview.actions.createDashboard", "Create Dashboard"),
|
|
47
|
+
icon: /* @__PURE__ */ w(x, {
|
|
48
48
|
className: "size-4",
|
|
49
49
|
"aria-hidden": "true"
|
|
50
50
|
}),
|
|
51
|
-
path:
|
|
51
|
+
path: n,
|
|
52
52
|
primary: !0
|
|
53
53
|
}
|
|
54
54
|
},
|
|
@@ -56,8 +56,8 @@ var A = p(function() {
|
|
|
56
56
|
permission: t.DATASINK_CREATE,
|
|
57
57
|
action: {
|
|
58
58
|
id: "new-data-sink",
|
|
59
|
-
label:
|
|
60
|
-
icon: /* @__PURE__ */
|
|
59
|
+
label: j("bigconsole.overview.actions.newDataSink", "New Data Sink"),
|
|
60
|
+
icon: /* @__PURE__ */ w(v, {
|
|
61
61
|
className: "size-4",
|
|
62
62
|
"aria-hidden": "true"
|
|
63
63
|
}),
|
|
@@ -68,177 +68,197 @@ var A = p(function() {
|
|
|
68
68
|
permission: t.PARSER_CREATE,
|
|
69
69
|
action: {
|
|
70
70
|
id: "new-parser",
|
|
71
|
-
label:
|
|
72
|
-
icon: /* @__PURE__ */
|
|
71
|
+
label: j("bigconsole.overview.actions.newParser", "New Parser"),
|
|
72
|
+
icon: /* @__PURE__ */ w(y, {
|
|
73
73
|
className: "size-4",
|
|
74
74
|
"aria-hidden": "true"
|
|
75
75
|
}),
|
|
76
76
|
path: "/parsers/new"
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
|
+
{
|
|
80
|
+
permission: t.PIPELINE_CREATE,
|
|
81
|
+
action: {
|
|
82
|
+
id: "new-pipeline",
|
|
83
|
+
label: j("bigconsole.overview.actions.newPipeline", "New Pipeline"),
|
|
84
|
+
icon: /* @__PURE__ */ w(S, {
|
|
85
|
+
className: "size-4",
|
|
86
|
+
"aria-hidden": "true"
|
|
87
|
+
}),
|
|
88
|
+
path: "/pipelines/new"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
79
91
|
{
|
|
80
92
|
permission: t.PROMPT_CREATE,
|
|
81
93
|
action: {
|
|
82
94
|
id: "generate-with-ai",
|
|
83
|
-
label:
|
|
84
|
-
icon: /* @__PURE__ */
|
|
95
|
+
label: j("bigconsole.overview.actions.generateWithAi", "Generate with AI Insights"),
|
|
96
|
+
icon: /* @__PURE__ */ w(oe, {
|
|
85
97
|
className: "size-4",
|
|
86
98
|
"aria-hidden": "true"
|
|
87
99
|
}),
|
|
88
100
|
path: "/insights"
|
|
89
101
|
}
|
|
90
102
|
}
|
|
91
|
-
].filter(({ permission: e }) =>
|
|
92
|
-
permissionsLoading:
|
|
93
|
-
dashboardsAllowed:
|
|
94
|
-
dashboardsLoading:
|
|
95
|
-
dashboardsTotal:
|
|
96
|
-
dashboardsFailed: !!
|
|
103
|
+
].filter(({ permission: e }) => W(e)).map(({ action: e }) => e), [j, W]), [pe, K] = g(!1), q = h(!1), J = re({
|
|
104
|
+
permissionsLoading: P,
|
|
105
|
+
dashboardsAllowed: F.allowed,
|
|
106
|
+
dashboardsLoading: F.loading,
|
|
107
|
+
dashboardsTotal: V.total,
|
|
108
|
+
dashboardsFailed: !!F.error
|
|
97
109
|
});
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}, [
|
|
101
|
-
let me =
|
|
102
|
-
|
|
103
|
-
}, []), he =
|
|
104
|
-
title:
|
|
105
|
-
subtitle:
|
|
106
|
-
show:
|
|
107
|
-
hide:
|
|
108
|
-
optional:
|
|
110
|
+
p(() => {
|
|
111
|
+
q.current || J === null || (q.current = !0, K(J));
|
|
112
|
+
}, [J]);
|
|
113
|
+
let me = f(() => {
|
|
114
|
+
q.current = !0, K((e) => !e);
|
|
115
|
+
}, []), he = m(() => ({
|
|
116
|
+
title: j("bigconsole.overview.guide.title", "How to create a complete dashboard, end to end"),
|
|
117
|
+
subtitle: j("bigconsole.overview.guide.subtitle", "Follow these steps in order — each button takes you to the right place."),
|
|
118
|
+
show: j("bigconsole.overview.guide.show", "Show guide"),
|
|
119
|
+
hide: j("bigconsole.overview.guide.hide", "Hide guide"),
|
|
120
|
+
optional: j("bigconsole.overview.guide.optional", "Optional"),
|
|
109
121
|
flow: [
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
122
|
+
j("bigconsole.overview.guide.flow.dataSink", "Data Sink"),
|
|
123
|
+
j("bigconsole.overview.guide.flow.parser", "Parser (optional)"),
|
|
124
|
+
j("bigconsole.overview.guide.flow.dashboard", "Dashboard"),
|
|
125
|
+
j("bigconsole.overview.guide.flow.widgets", "Widgets"),
|
|
126
|
+
j("bigconsole.overview.guide.flow.share", "Share")
|
|
115
127
|
],
|
|
116
|
-
stepPrefix: (e) =>
|
|
117
|
-
}), [
|
|
118
|
-
actions:
|
|
119
|
-
onNavigate:
|
|
120
|
-
}) : null,
|
|
121
|
-
summary:
|
|
122
|
-
loading:
|
|
123
|
-
error:
|
|
124
|
-
canCreate:
|
|
125
|
-
onRetry:
|
|
126
|
-
onNavigate:
|
|
127
|
-
formatUpdated:
|
|
128
|
+
stepPrefix: (e) => j("bigconsole.overview.guide.stepPrefix", "Step {{number}}:", { number: e })
|
|
129
|
+
}), [j]), Y = F.loading && F.data !== void 0 || I.loading && I.data !== void 0 || L.loading && L.data !== void 0 || R.loading && R.data !== void 0, ge = P || F.allowed || I.allowed || L.allowed || R.allowed, X = !F.loading && !!F.error && V.total === void 0, Z = !I.loading && !!I.error && H.total === void 0, Q = j("bigconsole.overview.kpis.unavailable", "Currently unavailable"), $ = G.length > 0 ? /* @__PURE__ */ w(s, {
|
|
130
|
+
actions: G,
|
|
131
|
+
onNavigate: B
|
|
132
|
+
}) : null, _e = I.allowed ? /* @__PURE__ */ w(o, {
|
|
133
|
+
summary: H,
|
|
134
|
+
loading: I.loading,
|
|
135
|
+
error: I.error,
|
|
136
|
+
canCreate: W(t.DATASINK_CREATE),
|
|
137
|
+
onRetry: I.refetch,
|
|
138
|
+
onNavigate: B,
|
|
139
|
+
formatUpdated: U
|
|
128
140
|
}) : null;
|
|
129
|
-
return /* @__PURE__ */
|
|
141
|
+
return /* @__PURE__ */ w(A, {
|
|
130
142
|
className: "min-h-screen bg-bg-canvas p-[var(--space-pagePadding)]",
|
|
131
|
-
children: /* @__PURE__ */
|
|
143
|
+
children: /* @__PURE__ */ T("div", {
|
|
132
144
|
"data-testid": "overview-page",
|
|
133
145
|
className: "space-y-6",
|
|
134
146
|
children: [
|
|
135
|
-
/* @__PURE__ */
|
|
136
|
-
className: "relative overflow-hidden rounded-card px-
|
|
137
|
-
children: [/* @__PURE__ */
|
|
138
|
-
title: /* @__PURE__ */
|
|
139
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ T("div", {
|
|
148
|
+
className: "relative overflow-hidden rounded-card border border-border-subtle bg-bg-surface px-6 py-5",
|
|
149
|
+
children: [/* @__PURE__ */ w(se, { intensity: "subtle" }), /* @__PURE__ */ w(D, {
|
|
150
|
+
title: /* @__PURE__ */ T(C, { children: [
|
|
151
|
+
/* @__PURE__ */ w(le, { children: j("bigconsole.overview.titleNoun", "BigConsole") }),
|
|
140
152
|
" ",
|
|
141
|
-
|
|
153
|
+
j("bigconsole.overview.titleSuffix", "Overview")
|
|
142
154
|
] }),
|
|
143
|
-
description:
|
|
144
|
-
actions: /* @__PURE__ */
|
|
155
|
+
description: j("bigconsole.overview.description", "Your dashboards and data at a glance"),
|
|
156
|
+
actions: /* @__PURE__ */ T(ce, {
|
|
145
157
|
type: "button",
|
|
146
158
|
variant: "outline",
|
|
147
159
|
size: "sm",
|
|
148
|
-
onClick:
|
|
149
|
-
disabled:
|
|
160
|
+
onClick: z,
|
|
161
|
+
disabled: Y || P,
|
|
150
162
|
className: "min-h-11 sm:min-h-9",
|
|
151
163
|
"data-testid": "overview-refresh",
|
|
152
|
-
children: [/* @__PURE__ */
|
|
153
|
-
className: `size-4 ${
|
|
164
|
+
children: [/* @__PURE__ */ w(ae, {
|
|
165
|
+
className: `size-4 ${Y ? "motion-safe:animate-spin" : ""}`,
|
|
154
166
|
"aria-hidden": "true"
|
|
155
|
-
}),
|
|
167
|
+
}), j("bigconsole.overview.refresh", "Refresh")]
|
|
156
168
|
})
|
|
157
169
|
})]
|
|
158
170
|
}),
|
|
159
|
-
/* @__PURE__ */
|
|
160
|
-
|
|
171
|
+
ge ? /* @__PURE__ */ T(k, {
|
|
172
|
+
"data-tour": "bigconsole-overview-stats",
|
|
173
|
+
"data-testid": "overview-kpis",
|
|
174
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4",
|
|
175
|
+
stagger: .06,
|
|
176
|
+
children: [
|
|
177
|
+
P || F.allowed ? /* @__PURE__ */ w(O, {
|
|
178
|
+
variant: "fadeScale",
|
|
179
|
+
children: /* @__PURE__ */ w(c, {
|
|
180
|
+
title: j("bigconsole.overview.kpis.dashboards", "Dashboards"),
|
|
181
|
+
value: V.total,
|
|
182
|
+
loading: P || F.loading,
|
|
183
|
+
subtitle: X ? Q : j("bigconsole.overview.kpis.dashboardsSubtitle", "Across this workspace"),
|
|
184
|
+
icon: /* @__PURE__ */ w(b, {}),
|
|
185
|
+
variant: X ? "warning" : "default",
|
|
186
|
+
onClick: () => B("/dashboards"),
|
|
187
|
+
"data-testid": "overview-kpi-dashboards"
|
|
188
|
+
})
|
|
189
|
+
}) : null,
|
|
190
|
+
P || I.allowed ? /* @__PURE__ */ w(C, { children: /* @__PURE__ */ w(O, {
|
|
191
|
+
variant: "fadeScale",
|
|
192
|
+
children: /* @__PURE__ */ w(c, {
|
|
193
|
+
title: j("bigconsole.overview.kpis.dataSinks", "Data Sinks"),
|
|
194
|
+
value: H.total,
|
|
195
|
+
loading: P || I.loading,
|
|
196
|
+
subtitle: Z ? Q : H.ready === void 0 ? void 0 : j("bigconsole.overview.kpis.dataSinksReady", "{{count}} ready", { count: H.ready }),
|
|
197
|
+
icon: /* @__PURE__ */ w(v, {}),
|
|
198
|
+
variant: Z ? "warning" : "default",
|
|
199
|
+
onClick: () => B("/datasinks"),
|
|
200
|
+
"data-testid": "overview-kpi-data-sinks"
|
|
201
|
+
})
|
|
202
|
+
}) }) : null,
|
|
203
|
+
P || L.allowed ? /* @__PURE__ */ w(O, {
|
|
204
|
+
variant: "fadeScale",
|
|
205
|
+
children: /* @__PURE__ */ w(c, {
|
|
206
|
+
title: j("bigconsole.overview.kpis.parsers", "Parsers"),
|
|
207
|
+
value: L.data?.listParsers.totalCount,
|
|
208
|
+
loading: P || L.loading,
|
|
209
|
+
subtitle: L.error ? Q : j("bigconsole.overview.kpis.defaultConsole", "In the default console"),
|
|
210
|
+
icon: /* @__PURE__ */ w(y, {}),
|
|
211
|
+
variant: L.error ? "warning" : "default",
|
|
212
|
+
onClick: () => B("/parsers"),
|
|
213
|
+
"data-testid": "overview-kpi-parsers"
|
|
214
|
+
})
|
|
215
|
+
}) : null,
|
|
216
|
+
P || R.allowed ? /* @__PURE__ */ w(O, {
|
|
217
|
+
variant: "fadeScale",
|
|
218
|
+
children: /* @__PURE__ */ w(c, {
|
|
219
|
+
title: j("bigconsole.overview.kpis.pipelines", "Pipelines"),
|
|
220
|
+
value: R.data?.listPipelines.totalCount,
|
|
221
|
+
loading: P || R.loading,
|
|
222
|
+
valueSuffix: (R.data?.listPipelines.totalCount ?? 0) >= 25 ? "+" : void 0,
|
|
223
|
+
subtitle: R.error ? Q : j("bigconsole.overview.kpis.defaultConsole", "In the default console"),
|
|
224
|
+
icon: /* @__PURE__ */ w(S, {}),
|
|
225
|
+
variant: R.error ? "warning" : "default",
|
|
226
|
+
onClick: () => B("/pipelines"),
|
|
227
|
+
"data-testid": "overview-kpi-pipelines"
|
|
228
|
+
})
|
|
229
|
+
}) : null
|
|
230
|
+
]
|
|
231
|
+
}) : null,
|
|
232
|
+
$,
|
|
233
|
+
F.allowed ? /* @__PURE__ */ w(te, {
|
|
234
|
+
dashboards: V.recent,
|
|
235
|
+
loading: F.loading,
|
|
236
|
+
error: F.error,
|
|
237
|
+
canCreate: W(t.DASHBOARD_CREATE),
|
|
238
|
+
onRetry: F.refetch,
|
|
239
|
+
onNavigate: B,
|
|
240
|
+
formatUpdated: U,
|
|
241
|
+
createDashboardPath: n
|
|
242
|
+
}) : null,
|
|
243
|
+
/* @__PURE__ */ w(ue, {
|
|
244
|
+
icon: /* @__PURE__ */ w(b, { className: "size-4" }),
|
|
161
245
|
className: "max-w-[80ch]",
|
|
162
|
-
children:
|
|
246
|
+
children: j("bigconsole.overview.purpose", "Bring data into a Data Sink, optionally reshape it with a Parser, and turn it into live dashboards your team can share.")
|
|
163
247
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
-
steps:
|
|
248
|
+
/* @__PURE__ */ w(a, {
|
|
249
|
+
steps: de,
|
|
166
250
|
alternative: fe,
|
|
167
251
|
labels: he,
|
|
168
252
|
expanded: pe,
|
|
169
253
|
onToggle: me,
|
|
170
|
-
onNavigate:
|
|
254
|
+
onNavigate: B
|
|
171
255
|
}),
|
|
172
|
-
|
|
173
|
-
"data-tour": "bigconsole-overview-stats",
|
|
174
|
-
"data-testid": "overview-kpis",
|
|
175
|
-
className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
|
|
176
|
-
stagger: .06,
|
|
177
|
-
children: [N || P.allowed ? /* @__PURE__ */ C(D, {
|
|
178
|
-
variant: "fadeScale",
|
|
179
|
-
children: /* @__PURE__ */ C(o, {
|
|
180
|
-
title: A("bigconsole.overview.kpis.dashboards", "Dashboards"),
|
|
181
|
-
value: R.total,
|
|
182
|
-
loading: N || P.loading,
|
|
183
|
-
subtitle: Y ? Z : A("bigconsole.overview.kpis.dashboardsSubtitle", "Across this workspace"),
|
|
184
|
-
icon: /* @__PURE__ */ C(v, {}),
|
|
185
|
-
variant: Y ? "warning" : "default",
|
|
186
|
-
onClick: () => L("/dashboards"),
|
|
187
|
-
"data-testid": "overview-kpi-dashboards"
|
|
188
|
-
})
|
|
189
|
-
}) : null, N || F.allowed ? /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ C(D, {
|
|
190
|
-
variant: "fadeScale",
|
|
191
|
-
children: /* @__PURE__ */ C(o, {
|
|
192
|
-
title: A("bigconsole.overview.kpis.dataSinks", "Data Sinks"),
|
|
193
|
-
value: z.total,
|
|
194
|
-
loading: N || F.loading,
|
|
195
|
-
subtitle: X ? Z : z.ready === void 0 ? void 0 : A("bigconsole.overview.kpis.dataSinksReady", "{{count}} ready", { count: z.ready }),
|
|
196
|
-
icon: /* @__PURE__ */ C(g, {}),
|
|
197
|
-
variant: X ? "warning" : "default",
|
|
198
|
-
onClick: () => L("/datasinks"),
|
|
199
|
-
"data-testid": "overview-kpi-data-sinks"
|
|
200
|
-
})
|
|
201
|
-
}), /* @__PURE__ */ C(D, {
|
|
202
|
-
variant: "fadeScale",
|
|
203
|
-
children: /* @__PURE__ */ C(o, {
|
|
204
|
-
title: A("bigconsole.overview.kpis.needsAttention", "Needs attention"),
|
|
205
|
-
value: J,
|
|
206
|
-
loading: N || F.loading,
|
|
207
|
-
subtitle: X ? Z : A("bigconsole.overview.kpis.needsAttentionSubtitle", "Data sinks with errors or stale data"),
|
|
208
|
-
icon: /* @__PURE__ */ C(se, {}),
|
|
209
|
-
variant: X || (J ?? 0) > 0 ? "warning" : J === void 0 ? "default" : "success",
|
|
210
|
-
onClick: () => L("/datasinks"),
|
|
211
|
-
"data-testid": "overview-kpi-needs-attention"
|
|
212
|
-
})
|
|
213
|
-
})] }) : null]
|
|
214
|
-
}) : null,
|
|
215
|
-
P.allowed ? /* @__PURE__ */ w("div", {
|
|
216
|
-
className: "grid grid-cols-1 gap-6 xl:grid-cols-3",
|
|
217
|
-
children: [/* @__PURE__ */ C("div", {
|
|
218
|
-
className: "min-w-0 xl:col-span-2",
|
|
219
|
-
children: /* @__PURE__ */ C(s, {
|
|
220
|
-
dashboards: R.recent,
|
|
221
|
-
loading: P.loading,
|
|
222
|
-
error: P.error,
|
|
223
|
-
canCreate: V(t.DASHBOARD_CREATE),
|
|
224
|
-
onRetry: P.refetch,
|
|
225
|
-
onNavigate: L,
|
|
226
|
-
formatUpdated: B,
|
|
227
|
-
createDashboardPath: r
|
|
228
|
-
})
|
|
229
|
-
}), Q || $ ? /* @__PURE__ */ w("div", {
|
|
230
|
-
className: "flex min-w-0 flex-col gap-6",
|
|
231
|
-
children: [Q, $]
|
|
232
|
-
}) : null]
|
|
233
|
-
}) : Q || $ ? /* @__PURE__ */ w("div", {
|
|
234
|
-
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
235
|
-
children: [Q, $]
|
|
236
|
-
}) : null
|
|
256
|
+
_e
|
|
237
257
|
]
|
|
238
258
|
})
|
|
239
259
|
});
|
|
240
260
|
});
|
|
241
261
|
//#endregion
|
|
242
|
-
export {
|
|
262
|
+
export { j as OverviewPage, j as default };
|
|
243
263
|
|
|
244
264
|
//# sourceMappingURL=OverviewPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../src/bigconsole/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * OverviewPage\n *\n * BigConsole home (`/bigconsole`): the dashboards and data at a glance, plus a\n * step-by-step guide to building a complete dashboard. Mirrors the VibeControls\n * product Overview — aurora hero, purpose line, guidance, KPI tiles and a single\n * emphasis panel — while showing only data the backend reports accurately.\n *\n * Every section is gated on its own read permission and loaded by its own\n * operation (see `useOverviewData`), so a caller missing one permission still\n * gets the rest of the page.\n */\n\nimport { type FC, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PageHeader } from '@burdenoff/fe-libs/chrome';\nimport { MotionItem, MotionList, MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { AuroraBackground, Button, GradientText, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { AlertTriangle, Database, FileCode2, LayoutDashboard, Plus, RefreshCw, Sparkles } from 'lucide-react';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useAppNavigate } from '../contexts';\nimport {\n CREATE_DASHBOARD_PATH,\n DashboardGuide,\n DataHealthCard,\n OverviewQuickActions,\n OverviewStatCard,\n RecentDashboardsPanel,\n buildAiAlternative,\n buildDashboardGuideSteps,\n formatRelativeTime,\n resolveGuideDefaultExpanded,\n summarizeDashboards,\n summarizeDataSinks,\n useOverviewData,\n type OverviewQuickAction,\n} from '../components/overview';\n\nexport const OverviewPage: FC = memo(function OverviewPage() {\n const navigate = useAppNavigate();\n const tr = useTr();\n const { locale } = useI18n();\n const { hasPermission } = usePermissions();\n const { permissionsLoading, dashboards, dataSinks, refetchAll } = useOverviewData();\n\n const handleNavigate = useCallback((path: string) => navigate(path), [navigate]);\n\n // ---------------------------------------------------------------------------\n // Derived data\n // ---------------------------------------------------------------------------\n\n const dashboardSummary = useMemo(() => summarizeDashboards(dashboards.data), [dashboards.data]);\n const dataSinkSummary = useMemo(() => summarizeDataSinks(dataSinks.data), [dataSinks.data]);\n\n const formatUpdated = useCallback((iso: string) => formatRelativeTime(iso, new Date(), tr, locale), [tr, locale]);\n\n const can = useCallback(\n (permission: string) => !permissionsLoading && hasPermission(permission),\n [permissionsLoading, hasPermission]\n );\n\n const guideSteps = useMemo(\n () =>\n buildDashboardGuideSteps({\n tr,\n latestDashboard: dashboardSummary.latest,\n permissions: {\n canCreateDataSink: can(BIGCONSOLE_PERMISSIONS.DATASINK_CREATE),\n canReadDataSinks: can(BIGCONSOLE_PERMISSIONS.DATASINK_READ),\n canCreateWorkflow: can(BIGCONSOLE_PERMISSIONS.WORKFLOW_CREATE),\n canCreateParser: can(BIGCONSOLE_PERMISSIONS.PARSER_CREATE),\n canCreateDashboard: can(BIGCONSOLE_PERMISSIONS.DASHBOARD_CREATE),\n canReadDashboards: can(BIGCONSOLE_PERMISSIONS.DASHBOARD_READ),\n canBuildDashboards: can(BIGCONSOLE_PERMISSIONS.DASHBOARD_UPDATE) && can(BIGCONSOLE_PERMISSIONS.WIDGET_CREATE),\n canGenerateWithAi: can(BIGCONSOLE_PERMISSIONS.PROMPT_CREATE),\n },\n }),\n [tr, can, dashboardSummary.latest]\n );\n\n const aiAlternative = useMemo(() => buildAiAlternative(tr, can(BIGCONSOLE_PERMISSIONS.PROMPT_CREATE)), [tr, can]);\n\n const quickActions = useMemo<OverviewQuickAction[]>(() => {\n const candidates: Array<{ permission: string; action: OverviewQuickAction }> = [\n {\n permission: BIGCONSOLE_PERMISSIONS.DASHBOARD_CREATE,\n action: {\n id: 'create-dashboard',\n label: tr('bigconsole.overview.actions.createDashboard', 'Create Dashboard'),\n icon: <Plus className=\"size-4\" aria-hidden=\"true\" />,\n path: CREATE_DASHBOARD_PATH,\n primary: true,\n },\n },\n {\n permission: BIGCONSOLE_PERMISSIONS.DATASINK_CREATE,\n action: {\n id: 'new-data-sink',\n label: tr('bigconsole.overview.actions.newDataSink', 'New Data Sink'),\n icon: <Database className=\"size-4\" aria-hidden=\"true\" />,\n path: '/datasinks/new',\n },\n },\n {\n permission: BIGCONSOLE_PERMISSIONS.PARSER_CREATE,\n action: {\n id: 'new-parser',\n label: tr('bigconsole.overview.actions.newParser', 'New Parser'),\n icon: <FileCode2 className=\"size-4\" aria-hidden=\"true\" />,\n path: '/parsers/new',\n },\n },\n {\n permission: BIGCONSOLE_PERMISSIONS.PROMPT_CREATE,\n action: {\n id: 'generate-with-ai',\n label: tr('bigconsole.overview.actions.generateWithAi', 'Generate with AI Insights'),\n icon: <Sparkles className=\"size-4\" aria-hidden=\"true\" />,\n path: '/insights',\n },\n },\n ];\n return candidates.filter(({ permission }) => can(permission)).map(({ action }) => action);\n }, [tr, can]);\n\n // ---------------------------------------------------------------------------\n // Guide: expanded by default only until the workspace has dashboards. The\n // default is applied once, after the first answer, so it never fights a user\n // who has toggled it.\n // ---------------------------------------------------------------------------\n\n const [guideExpanded, setGuideExpanded] = useState(false);\n const guideDefaultApplied = useRef(false);\n const guideDefault = resolveGuideDefaultExpanded({\n permissionsLoading,\n dashboardsAllowed: dashboards.allowed,\n dashboardsLoading: dashboards.loading,\n dashboardsTotal: dashboardSummary.total,\n dashboardsFailed: Boolean(dashboards.error),\n });\n\n useEffect(() => {\n if (guideDefaultApplied.current || guideDefault === null) return;\n guideDefaultApplied.current = true;\n setGuideExpanded(guideDefault);\n }, [guideDefault]);\n\n const handleToggleGuide = useCallback(() => {\n guideDefaultApplied.current = true;\n setGuideExpanded((expanded) => !expanded);\n }, []);\n\n const guideLabels = useMemo(\n () => ({\n title: tr('bigconsole.overview.guide.title', 'How to create a complete dashboard, end to end'),\n subtitle: tr(\n 'bigconsole.overview.guide.subtitle',\n 'Follow these steps in order — each button takes you to the right place.'\n ),\n show: tr('bigconsole.overview.guide.show', 'Show guide'),\n hide: tr('bigconsole.overview.guide.hide', 'Hide guide'),\n optional: tr('bigconsole.overview.guide.optional', 'Optional'),\n flow: [\n tr('bigconsole.overview.guide.flow.dataSink', 'Data Sink'),\n tr('bigconsole.overview.guide.flow.parser', 'Parser (optional)'),\n tr('bigconsole.overview.guide.flow.dashboard', 'Dashboard'),\n tr('bigconsole.overview.guide.flow.widgets', 'Widgets'),\n tr('bigconsole.overview.guide.flow.share', 'Share'),\n ],\n stepPrefix: (stepNumber: number) =>\n tr('bigconsole.overview.guide.stepPrefix', 'Step {{number}}:', { number: stepNumber }),\n }),\n [tr]\n );\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n\n const refreshing =\n (dashboards.loading && dashboards.data !== undefined) || (dataSinks.loading && dataSinks.data !== undefined);\n const showKpis = permissionsLoading || dashboards.allowed || dataSinks.allowed;\n const attentionCount = dataSinkSummary.attentionCount;\n // A section whose first load failed has no numbers to show; its tiles say so\n // instead of shimmering forever (a Retry that is in flight shows the skeleton).\n // (Apollo reports a rejected operation as `data: null`, so check the summaries.)\n const dashboardsUnavailable =\n !dashboards.loading && Boolean(dashboards.error) && dashboardSummary.total === undefined;\n const dataSinksUnavailable = !dataSinks.loading && Boolean(dataSinks.error) && dataSinkSummary.total === undefined;\n const unavailableLabel = tr('bigconsole.overview.kpis.unavailable', 'Currently unavailable');\n\n const quickActionsCard =\n quickActions.length > 0 ? <OverviewQuickActions actions={quickActions} onNavigate={handleNavigate} /> : null;\n const dataHealthCard = dataSinks.allowed ? (\n <DataHealthCard\n summary={dataSinkSummary}\n loading={dataSinks.loading}\n error={dataSinks.error}\n canCreate={can(BIGCONSOLE_PERMISSIONS.DATASINK_CREATE)}\n onRetry={dataSinks.refetch}\n onNavigate={handleNavigate}\n formatUpdated={formatUpdated}\n />\n ) : null;\n\n return (\n <MotionPage className=\"min-h-screen bg-bg-canvas p-[var(--space-pagePadding)]\">\n <div data-testid=\"overview-page\" className=\"space-y-6\">\n {/* Hero header — the one aurora backdrop on this page. */}\n <div className=\"relative overflow-hidden rounded-card px-2 pt-1\">\n <AuroraBackground intensity=\"subtle\" />\n <PageHeader\n title={\n <>\n <GradientText>{tr('bigconsole.overview.titleNoun', 'BigConsole')}</GradientText>{' '}\n {tr('bigconsole.overview.titleSuffix', 'Overview')}\n </>\n }\n description={tr('bigconsole.overview.description', 'Your dashboards and data at a glance')}\n actions={\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={refetchAll}\n disabled={refreshing || permissionsLoading}\n className=\"min-h-11 sm:min-h-9\"\n data-testid=\"overview-refresh\"\n >\n <RefreshCw className={`size-4 ${refreshing ? 'motion-safe:animate-spin' : ''}`} aria-hidden=\"true\" />\n {tr('bigconsole.overview.refresh', 'Refresh')}\n </Button>\n }\n />\n </div>\n\n <PagePurpose icon={<LayoutDashboard className=\"size-4\" />} className=\"max-w-[80ch]\">\n {tr(\n 'bigconsole.overview.purpose',\n 'Bring data into a Data Sink, optionally reshape it with a Parser, and turn it into live dashboards your team can share.'\n )}\n </PagePurpose>\n\n <DashboardGuide\n steps={guideSteps}\n alternative={aiAlternative}\n labels={guideLabels}\n expanded={guideExpanded}\n onToggle={handleToggleGuide}\n onNavigate={handleNavigate}\n />\n\n {showKpis ? (\n <MotionList\n data-tour=\"bigconsole-overview-stats\"\n data-testid=\"overview-kpis\"\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\"\n stagger={0.06}\n >\n {permissionsLoading || dashboards.allowed ? (\n <MotionItem variant=\"fadeScale\">\n <OverviewStatCard\n title={tr('bigconsole.overview.kpis.dashboards', 'Dashboards')}\n value={dashboardSummary.total}\n loading={permissionsLoading || dashboards.loading}\n subtitle={\n dashboardsUnavailable\n ? unavailableLabel\n : tr('bigconsole.overview.kpis.dashboardsSubtitle', 'Across this workspace')\n }\n icon={<LayoutDashboard />}\n variant={dashboardsUnavailable ? 'warning' : 'default'}\n onClick={() => handleNavigate('/dashboards')}\n data-testid=\"overview-kpi-dashboards\"\n />\n </MotionItem>\n ) : null}\n {permissionsLoading || dataSinks.allowed ? (\n <>\n <MotionItem variant=\"fadeScale\">\n <OverviewStatCard\n title={tr('bigconsole.overview.kpis.dataSinks', 'Data Sinks')}\n value={dataSinkSummary.total}\n loading={permissionsLoading || dataSinks.loading}\n subtitle={\n dataSinksUnavailable\n ? unavailableLabel\n : dataSinkSummary.ready === undefined\n ? undefined\n : tr('bigconsole.overview.kpis.dataSinksReady', '{{count}} ready', {\n count: dataSinkSummary.ready,\n })\n }\n icon={<Database />}\n variant={dataSinksUnavailable ? 'warning' : 'default'}\n onClick={() => handleNavigate('/datasinks')}\n data-testid=\"overview-kpi-data-sinks\"\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <OverviewStatCard\n title={tr('bigconsole.overview.kpis.needsAttention', 'Needs attention')}\n value={attentionCount}\n loading={permissionsLoading || dataSinks.loading}\n subtitle={\n dataSinksUnavailable\n ? unavailableLabel\n : tr('bigconsole.overview.kpis.needsAttentionSubtitle', 'Data sinks with errors or stale data')\n }\n icon={<AlertTriangle />}\n variant={\n dataSinksUnavailable || (attentionCount ?? 0) > 0\n ? 'warning'\n : attentionCount === undefined\n ? 'default'\n : 'success'\n }\n onClick={() => handleNavigate('/datasinks')}\n data-testid=\"overview-kpi-needs-attention\"\n />\n </MotionItem>\n </>\n ) : null}\n </MotionList>\n ) : null}\n\n {dashboards.allowed ? (\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\">\n <div className=\"min-w-0 xl:col-span-2\">\n <RecentDashboardsPanel\n dashboards={dashboardSummary.recent}\n loading={dashboards.loading}\n error={dashboards.error}\n canCreate={can(BIGCONSOLE_PERMISSIONS.DASHBOARD_CREATE)}\n onRetry={dashboards.refetch}\n onNavigate={handleNavigate}\n formatUpdated={formatUpdated}\n createDashboardPath={CREATE_DASHBOARD_PATH}\n />\n </div>\n {quickActionsCard || dataHealthCard ? (\n <div className=\"flex min-w-0 flex-col gap-6\">\n {quickActionsCard}\n {dataHealthCard}\n </div>\n ) : null}\n </div>\n ) : quickActionsCard || dataHealthCard ? (\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {quickActionsCard}\n {dataHealthCard}\n </div>\n ) : null}\n </div>\n </MotionPage>\n );\n});\n\nexport default OverviewPage;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../src/bigconsole/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * OverviewPage\n *\n * BigConsole home (`/bigconsole`): the dashboards and data at a glance, plus a\n * step-by-step guide to building a complete dashboard. Mirrors the VibeControls\n * product Overview — aurora hero, purpose line, guidance, KPI tiles and a single\n * emphasis panel — while showing only data the backend reports accurately.\n *\n * Every section is gated on its own read permission and loaded by its own\n * operation (see `useOverviewData`), so a caller missing one permission still\n * gets the rest of the page.\n */\n\nimport { type FC, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PageHeader } from '@burdenoff/fe-libs/chrome';\nimport { MotionItem, MotionList, MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { AuroraBackground, Button, GradientText, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { Workflow, Database, FileCode2, LayoutDashboard, Plus, RefreshCw, Sparkles } from 'lucide-react';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useAppNavigate } from '../contexts';\nimport {\n CREATE_DASHBOARD_PATH,\n DashboardGuide,\n DataHealthCard,\n OverviewQuickActions,\n OverviewStatCard,\n RecentDashboardsPanel,\n buildAiAlternative,\n buildDashboardGuideSteps,\n formatRelativeTime,\n resolveGuideDefaultExpanded,\n summarizeDashboards,\n summarizeDataSinks,\n useOverviewData,\n type OverviewQuickAction,\n} from '../components/overview';\n\nexport const OverviewPage: FC = memo(function OverviewPage() {\n const navigate = useAppNavigate();\n const tr = useTr();\n const { locale } = useI18n();\n const { hasPermission } = usePermissions();\n const { permissionsLoading, dashboards, dataSinks, parsers, pipelines, refetchAll } = useOverviewData();\n\n const handleNavigate = useCallback((path: string) => navigate(path), [navigate]);\n\n // ---------------------------------------------------------------------------\n // Derived data\n // ---------------------------------------------------------------------------\n\n const dashboardSummary = useMemo(() => summarizeDashboards(dashboards.data), [dashboards.data]);\n const dataSinkSummary = useMemo(() => summarizeDataSinks(dataSinks.data), [dataSinks.data]);\n\n const formatUpdated = useCallback((iso: string) => formatRelativeTime(iso, new Date(), tr, locale), [tr, locale]);\n\n const can = useCallback(\n (permission: string) => !permissionsLoading && hasPermission(permission),\n [permissionsLoading, hasPermission]\n );\n\n const guideSteps = useMemo(\n () =>\n buildDashboardGuideSteps({\n tr,\n latestDashboard: dashboardSummary.latest,\n permissions: {\n canCreateDataSink: can(BIGCONSOLE_PERMISSIONS.DATASINK_CREATE),\n canReadDataSinks: can(BIGCONSOLE_PERMISSIONS.DATASINK_READ),\n canCreateWorkflow: can(BIGCONSOLE_PERMISSIONS.WORKFLOW_CREATE),\n canCreateParser: can(BIGCONSOLE_PERMISSIONS.PARSER_CREATE),\n canCreateDashboard: can(BIGCONSOLE_PERMISSIONS.DASHBOARD_CREATE),\n canReadDashboards: can(BIGCONSOLE_PERMISSIONS.DASHBOARD_READ),\n canBuildDashboards: can(BIGCONSOLE_PERMISSIONS.DASHBOARD_UPDATE) && can(BIGCONSOLE_PERMISSIONS.WIDGET_CREATE),\n canGenerateWithAi: can(BIGCONSOLE_PERMISSIONS.PROMPT_CREATE),\n },\n }),\n [tr, can, dashboardSummary.latest]\n );\n\n const aiAlternative = useMemo(() => buildAiAlternative(tr, can(BIGCONSOLE_PERMISSIONS.PROMPT_CREATE)), [tr, can]);\n\n const quickActions = useMemo<OverviewQuickAction[]>(() => {\n const candidates: Array<{ permission: string; action: OverviewQuickAction }> = [\n {\n permission: BIGCONSOLE_PERMISSIONS.DASHBOARD_CREATE,\n action: {\n id: 'create-dashboard',\n label: tr('bigconsole.overview.actions.createDashboard', 'Create Dashboard'),\n icon: <Plus className=\"size-4\" aria-hidden=\"true\" />,\n path: CREATE_DASHBOARD_PATH,\n primary: true,\n },\n },\n {\n permission: BIGCONSOLE_PERMISSIONS.DATASINK_CREATE,\n action: {\n id: 'new-data-sink',\n label: tr('bigconsole.overview.actions.newDataSink', 'New Data Sink'),\n icon: <Database className=\"size-4\" aria-hidden=\"true\" />,\n path: '/datasinks/new',\n },\n },\n {\n permission: BIGCONSOLE_PERMISSIONS.PARSER_CREATE,\n action: {\n id: 'new-parser',\n label: tr('bigconsole.overview.actions.newParser', 'New Parser'),\n icon: <FileCode2 className=\"size-4\" aria-hidden=\"true\" />,\n path: '/parsers/new',\n },\n },\n {\n permission: BIGCONSOLE_PERMISSIONS.PIPELINE_CREATE,\n action: {\n id: 'new-pipeline',\n label: tr('bigconsole.overview.actions.newPipeline', 'New Pipeline'),\n icon: <Workflow className=\"size-4\" aria-hidden=\"true\" />,\n path: '/pipelines/new',\n },\n },\n {\n permission: BIGCONSOLE_PERMISSIONS.PROMPT_CREATE,\n action: {\n id: 'generate-with-ai',\n label: tr('bigconsole.overview.actions.generateWithAi', 'Generate with AI Insights'),\n icon: <Sparkles className=\"size-4\" aria-hidden=\"true\" />,\n path: '/insights',\n },\n },\n ];\n return candidates.filter(({ permission }) => can(permission)).map(({ action }) => action);\n }, [tr, can]);\n\n // ---------------------------------------------------------------------------\n // Guide: expanded by default only until the workspace has dashboards. The\n // default is applied once, after the first answer, so it never fights a user\n // who has toggled it.\n // ---------------------------------------------------------------------------\n\n const [guideExpanded, setGuideExpanded] = useState(false);\n const guideDefaultApplied = useRef(false);\n const guideDefault = resolveGuideDefaultExpanded({\n permissionsLoading,\n dashboardsAllowed: dashboards.allowed,\n dashboardsLoading: dashboards.loading,\n dashboardsTotal: dashboardSummary.total,\n dashboardsFailed: Boolean(dashboards.error),\n });\n\n useEffect(() => {\n if (guideDefaultApplied.current || guideDefault === null) return;\n guideDefaultApplied.current = true;\n setGuideExpanded(guideDefault);\n }, [guideDefault]);\n\n const handleToggleGuide = useCallback(() => {\n guideDefaultApplied.current = true;\n setGuideExpanded((expanded) => !expanded);\n }, []);\n\n const guideLabels = useMemo(\n () => ({\n title: tr('bigconsole.overview.guide.title', 'How to create a complete dashboard, end to end'),\n subtitle: tr(\n 'bigconsole.overview.guide.subtitle',\n 'Follow these steps in order — each button takes you to the right place.'\n ),\n show: tr('bigconsole.overview.guide.show', 'Show guide'),\n hide: tr('bigconsole.overview.guide.hide', 'Hide guide'),\n optional: tr('bigconsole.overview.guide.optional', 'Optional'),\n flow: [\n tr('bigconsole.overview.guide.flow.dataSink', 'Data Sink'),\n tr('bigconsole.overview.guide.flow.parser', 'Parser (optional)'),\n tr('bigconsole.overview.guide.flow.dashboard', 'Dashboard'),\n tr('bigconsole.overview.guide.flow.widgets', 'Widgets'),\n tr('bigconsole.overview.guide.flow.share', 'Share'),\n ],\n stepPrefix: (stepNumber: number) =>\n tr('bigconsole.overview.guide.stepPrefix', 'Step {{number}}:', { number: stepNumber }),\n }),\n [tr]\n );\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n\n const refreshing =\n (dashboards.loading && dashboards.data !== undefined) ||\n (dataSinks.loading && dataSinks.data !== undefined) ||\n (parsers.loading && parsers.data !== undefined) ||\n (pipelines.loading && pipelines.data !== undefined);\n const showKpis =\n permissionsLoading || dashboards.allowed || dataSinks.allowed || parsers.allowed || pipelines.allowed;\n // A section whose first load failed has no numbers to show; its tiles say so\n // instead of shimmering forever (a Retry that is in flight shows the skeleton).\n // (Apollo reports a rejected operation as `data: null`, so check the summaries.)\n const dashboardsUnavailable =\n !dashboards.loading && Boolean(dashboards.error) && dashboardSummary.total === undefined;\n const dataSinksUnavailable = !dataSinks.loading && Boolean(dataSinks.error) && dataSinkSummary.total === undefined;\n const unavailableLabel = tr('bigconsole.overview.kpis.unavailable', 'Currently unavailable');\n\n const quickActionsCard =\n quickActions.length > 0 ? <OverviewQuickActions actions={quickActions} onNavigate={handleNavigate} /> : null;\n const dataHealthCard = dataSinks.allowed ? (\n <DataHealthCard\n summary={dataSinkSummary}\n loading={dataSinks.loading}\n error={dataSinks.error}\n canCreate={can(BIGCONSOLE_PERMISSIONS.DATASINK_CREATE)}\n onRetry={dataSinks.refetch}\n onNavigate={handleNavigate}\n formatUpdated={formatUpdated}\n />\n ) : null;\n\n return (\n <MotionPage className=\"min-h-screen bg-bg-canvas p-[var(--space-pagePadding)]\">\n <div data-testid=\"overview-page\" className=\"space-y-6\">\n {/* Hero header — the one aurora backdrop on this page. */}\n <div className=\"relative overflow-hidden rounded-card border border-border-subtle bg-bg-surface px-6 py-5\">\n <AuroraBackground intensity=\"subtle\" />\n <PageHeader\n title={\n <>\n <GradientText>{tr('bigconsole.overview.titleNoun', 'BigConsole')}</GradientText>{' '}\n {tr('bigconsole.overview.titleSuffix', 'Overview')}\n </>\n }\n description={tr('bigconsole.overview.description', 'Your dashboards and data at a glance')}\n actions={\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={refetchAll}\n disabled={refreshing || permissionsLoading}\n className=\"min-h-11 sm:min-h-9\"\n data-testid=\"overview-refresh\"\n >\n <RefreshCw className={`size-4 ${refreshing ? 'motion-safe:animate-spin' : ''}`} aria-hidden=\"true\" />\n {tr('bigconsole.overview.refresh', 'Refresh')}\n </Button>\n }\n />\n </div>\n\n {showKpis ? (\n <MotionList\n data-tour=\"bigconsole-overview-stats\"\n data-testid=\"overview-kpis\"\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\"\n stagger={0.06}\n >\n {permissionsLoading || dashboards.allowed ? (\n <MotionItem variant=\"fadeScale\">\n <OverviewStatCard\n title={tr('bigconsole.overview.kpis.dashboards', 'Dashboards')}\n value={dashboardSummary.total}\n loading={permissionsLoading || dashboards.loading}\n subtitle={\n dashboardsUnavailable\n ? unavailableLabel\n : tr('bigconsole.overview.kpis.dashboardsSubtitle', 'Across this workspace')\n }\n icon={<LayoutDashboard />}\n variant={dashboardsUnavailable ? 'warning' : 'default'}\n onClick={() => handleNavigate('/dashboards')}\n data-testid=\"overview-kpi-dashboards\"\n />\n </MotionItem>\n ) : null}\n {permissionsLoading || dataSinks.allowed ? (\n <>\n <MotionItem variant=\"fadeScale\">\n <OverviewStatCard\n title={tr('bigconsole.overview.kpis.dataSinks', 'Data Sinks')}\n value={dataSinkSummary.total}\n loading={permissionsLoading || dataSinks.loading}\n subtitle={\n dataSinksUnavailable\n ? unavailableLabel\n : dataSinkSummary.ready === undefined\n ? undefined\n : tr('bigconsole.overview.kpis.dataSinksReady', '{{count}} ready', {\n count: dataSinkSummary.ready,\n })\n }\n icon={<Database />}\n variant={dataSinksUnavailable ? 'warning' : 'default'}\n onClick={() => handleNavigate('/datasinks')}\n data-testid=\"overview-kpi-data-sinks\"\n />\n </MotionItem>\n </>\n ) : null}\n {permissionsLoading || parsers.allowed ? (\n <MotionItem variant=\"fadeScale\">\n <OverviewStatCard\n title={tr('bigconsole.overview.kpis.parsers', 'Parsers')}\n value={parsers.data?.listParsers.totalCount}\n loading={permissionsLoading || parsers.loading}\n subtitle={\n parsers.error\n ? unavailableLabel\n : tr('bigconsole.overview.kpis.defaultConsole', 'In the default console')\n }\n icon={<FileCode2 />}\n variant={parsers.error ? 'warning' : 'default'}\n onClick={() => handleNavigate('/parsers')}\n data-testid=\"overview-kpi-parsers\"\n />\n </MotionItem>\n ) : null}\n {permissionsLoading || pipelines.allowed ? (\n <MotionItem variant=\"fadeScale\">\n <OverviewStatCard\n title={tr('bigconsole.overview.kpis.pipelines', 'Pipelines')}\n value={pipelines.data?.listPipelines.totalCount}\n loading={permissionsLoading || pipelines.loading}\n valueSuffix={(pipelines.data?.listPipelines.totalCount ?? 0) >= 25 ? '+' : undefined}\n subtitle={\n pipelines.error\n ? unavailableLabel\n : tr('bigconsole.overview.kpis.defaultConsole', 'In the default console')\n }\n icon={<Workflow />}\n variant={pipelines.error ? 'warning' : 'default'}\n onClick={() => handleNavigate('/pipelines')}\n data-testid=\"overview-kpi-pipelines\"\n />\n </MotionItem>\n ) : null}\n </MotionList>\n ) : null}\n\n {quickActionsCard}\n {dashboards.allowed ? (\n <RecentDashboardsPanel\n dashboards={dashboardSummary.recent}\n loading={dashboards.loading}\n error={dashboards.error}\n canCreate={can(BIGCONSOLE_PERMISSIONS.DASHBOARD_CREATE)}\n onRetry={dashboards.refetch}\n onNavigate={handleNavigate}\n formatUpdated={formatUpdated}\n createDashboardPath={CREATE_DASHBOARD_PATH}\n />\n ) : null}\n <PagePurpose icon={<LayoutDashboard className=\"size-4\" />} className=\"max-w-[80ch]\">\n {tr(\n 'bigconsole.overview.purpose',\n 'Bring data into a Data Sink, optionally reshape it with a Parser, and turn it into live dashboards your team can share.'\n )}\n </PagePurpose>\n\n <DashboardGuide\n steps={guideSteps}\n alternative={aiAlternative}\n labels={guideLabels}\n expanded={guideExpanded}\n onToggle={handleToggleGuide}\n onNavigate={handleNavigate}\n />\n\n {dataHealthCard}\n </div>\n </MotionPage>\n );\n});\n\nexport default OverviewPage;\n"],"mappings":"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"}
|