@burdenoff/microfe-bigconsole 2026.1004.8 → 2026.1005.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.
@@ -1 +1 @@
1
- {"version":3,"file":"DataSinkViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinkViewPage.tsx"],"sourcesContent":["/**\n * DataSink View Page\n *\n * Page for viewing DataSink details, data preview, and actions.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router';\nimport { useAppNavigate } from '../contexts';\nimport { newWorkflowPath } from '../BigConsoleRoutes';\nimport { isNative, nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { useDataSinkSubscription } from '../hooks/useDataSinkSubscription';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport { formatByteSize } from '../utils';\nimport { DataSinkStatusBadge } from '../components/datasink/DataSinkStatusBadge';\nimport { DataSinkDataViewer } from '../components/datasink/DataSinkDataViewer';\nimport { DataSinkTableViewer } from '../components/datasink/DataSinkTableViewer';\nimport { LinkedWorkflowsPanel } from '../components/datasink/LinkedWorkflowsPanel';\nimport { LinkedParsersPanel } from '../components/datasink/LinkedParsersPanel';\nimport { WorkflowStatusPanel } from '../components/datasink/WorkflowStatusPanel';\nimport { ExportDataSinkDialog } from '../components/datasink/ExportDataSinkDialog';\nimport { ImportDataGuideDialog } from '../components/datasink/ImportDataGuideDialog';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\n\n// Section-emphasis primitives (BOFF-5632)\nimport { Section, SectionStack, SECTION_FOCUS_RING } from '../components/common';\n\n// RBAC\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AccessDenied, PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\n\n// Lucide icons\nimport {\n ArrowLeft,\n Edit,\n RefreshCw,\n Trash2,\n Database,\n Loader2,\n AlertCircle,\n Eye,\n EyeOff,\n Download,\n Upload,\n Grid3X3,\n Code,\n Bell,\n} from 'lucide-react';\n\n/**\n * The preview-format segmented control clips its children (`overflow-hidden`\n * preserves the group's rounded corners), which would swallow an offset focus\n * ring entirely — and `SECTION_FOCUS_RING` also suppresses the native outline,\n * so the buttons would have no visible focus at all. Painting the ring inside\n * the button keeps it visible without dropping the group's rounding.\n */\nconst SEGMENT_RING_FIX = 'focus-visible:ring-inset focus-visible:ring-offset-0' as const;\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkViewPage = memo(function DataSinkViewPage() {\n const { dataSinkId } = useParams<{ dataSinkId: string; consoleId: string }>();\n const navigate = useAppNavigate();\n const { t } = useI18n();\n\n // RBAC — PermissionButton gates the action buttons directly; here we only need\n // the read gate that blocks the whole page.\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.DATASINK_READ);\n\n // Local state\n const [showData, setShowData] = useState(false);\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n const [isImportDialogOpen, setIsImportDialogOpen] = useState(false);\n const [dataPreview, setDataPreview] = useState<Record<string, unknown> | null>(null);\n const [dataLoading, setDataLoading] = useState(false);\n const [dataViewMode, setDataViewMode] = useState<'table' | 'json'>('table');\n // v3.0: server-side pagination for array payloads (avoids loading millions of rows).\n const [pageSize, setPageSize] = useState(100);\n const [pageOffset, setPageOffset] = useState(0);\n const [dataTotalCount, setDataTotalCount] = useState<number | null>(null);\n const [dataHasMore, setDataHasMore] = useState(false);\n\n // Store\n const dataSink = useDataSinkStore((state) => (dataSinkId ? state.dataSinks.get(dataSinkId) : undefined));\n\n // Operations\n const { loading, error, fetchDataSink, getDataSinkData, refreshDataSink, updateDataSink, deleteDataSink } =\n useDataSinkOperations({\n skipFetch: true,\n });\n\n // Subscribe to updates\n useDataSinkSubscription({\n dataSinkId,\n enabled: !!dataSinkId,\n });\n\n // Load DataSink on mount - always fetch full data to get workflowLinks\n useEffect(() => {\n if (dataSinkId) {\n fetchDataSink(dataSinkId);\n }\n }, [dataSinkId, fetchDataSink]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n navigate(`/datasinks`);\n }, [navigate]);\n\n // Handle edit\n const handleEdit = useCallback(() => {\n navigate(`/datasinks/${dataSinkId}/edit`);\n }, [navigate, dataSinkId]);\n\n // Handle refresh\n const handleRefresh = useCallback(async () => {\n if (!dataSinkId) return;\n setIsRefreshing(true);\n try {\n await refreshDataSink({ id: dataSinkId });\n } catch (err) {\n } finally {\n setIsRefreshing(false);\n }\n }, [dataSinkId, refreshDataSink]);\n\n // Handle delete\n const handleDelete = useCallback(async () => {\n if (!dataSinkId) return;\n const native = await nativeConfirm({\n title: t('bigconsole.dataSinks.view.deleteConfirmTitle', 'Delete DataSink?'),\n message: t('bigconsole.dataSinks.view.deleteConfirmMessage', 'Are you sure you want to delete this DataSink?'),\n okButtonTitle: t('bigconsole.dataSinks.view.delete', 'Delete'),\n cancelButtonTitle: t('bigconsole.dataSinks.view.cancel', 'Cancel'),\n });\n const ok =\n native === null\n ? window.confirm(\n t('bigconsole.dataSinks.view.deleteConfirmMessage', 'Are you sure you want to delete this DataSink?')\n )\n : native;\n if (!ok) return;\n nativeImpact('medium');\n setIsDeleting(true);\n try {\n await deleteDataSink(dataSinkId);\n if (isNative()) void nativeNotify('success');\n navigate(`/datasinks`);\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n }, [dataSinkId, deleteDataSink, navigate, t]);\n\n // Load one page of the sink's data (v3.0: server-side pagination).\n const loadDataPage = useCallback(\n async (offset: number, size: number) => {\n if (!dataSinkId) return;\n setDataLoading(true);\n try {\n const result = await getDataSinkData({ id: dataSinkId }, { limit: size, offset });\n if (result) {\n setDataPreview((result.raw as Record<string, unknown>) || {});\n // totalCount is non-null only for array payloads being paginated.\n setDataTotalCount(typeof result.totalCount === 'number' ? result.totalCount : null);\n setDataHasMore(Boolean(result.hasMore));\n setPageOffset(offset);\n }\n } catch {\n // handled by the hook's error state\n } finally {\n setDataLoading(false);\n }\n },\n [dataSinkId, getDataSinkData]\n );\n\n // Handle show data (toggles the preview; loads the first page).\n const handleShowData = useCallback(async () => {\n if (!dataSinkId) return;\n if (showData) {\n setShowData(false);\n return;\n }\n await loadDataPage(0, pageSize);\n setShowData(true);\n }, [dataSinkId, showData, loadDataPage, pageSize]);\n\n const handlePageSizeChange = useCallback(\n (size: number) => {\n setPageSize(size);\n void loadDataPage(0, size);\n },\n [loadDataPage]\n );\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // Handle import — opens a guide explaining that data is imported via a workflow.\n const handleImport = useCallback(() => {\n setIsImportDialogOpen(true);\n }, []);\n\n const handleImportClose = useCallback(() => {\n setIsImportDialogOpen(false);\n }, []);\n\n const handleGoToWorkflows = useCallback(() => {\n setIsImportDialogOpen(false);\n // Navigate straight to the builder path (basePath-aware via useAppNavigate).\n // Going via /workflows/new would hit a <Navigate> redirect to an absolute\n // path that drops the MFE base path — see WorkflowsPage for the same pattern.\n navigate(newWorkflowPath());\n }, [navigate]);\n\n // Direct CSV import — write the parsed rows straight to the sink (no workflow).\n const handleImportCsv = useCallback(\n async (rows: Record<string, unknown>[]): Promise<boolean> => {\n if (!dataSink) return false;\n // The DataSink `data` payload is a JSON scalar that accepts any JSON value;\n // tabular imports are stored as an array of row objects — the shape the\n // table viewer and parsers read. The generated input type narrows JSON to\n // Record<string, unknown>, so cast the row array to satisfy it.\n const updated = await updateDataSink({\n id: dataSink.id,\n version: dataSink.version,\n data: rows as unknown as Record<string, unknown>,\n });\n if (!updated) return false;\n // The import already succeeded. Revealing the data is best-effort — a\n // failure here must not turn a persisted import into a reported failure\n // (which would prompt a retry and hit a version conflict).\n try {\n await loadDataPage(0, pageSize);\n setShowData(true);\n } catch {\n // Data is saved; the user can open the preview manually.\n }\n return true;\n },\n [dataSink, updateDataSink, loadDataPage, pageSize]\n );\n\n // Loading state\n if (loading && !dataSink) {\n return (\n <div className=\"flex min-h-[240px] items-center justify-center p-card-padding\">\n <Loader2 className=\"h-icon-lg w-icon-lg animate-spin text-text-secondary\" />\n </div>\n );\n }\n\n // Error state\n // Only treat the shared hook error as fatal when the sink never loaded. Once\n // it is loaded, transient action errors (e.g. a failed CSV import) surface\n // inline in their own UI and must not replace the whole page.\n if (error && !dataSink) {\n return (\n <div className=\"p-card-padding\">\n <Section\n tone=\"danger\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.view.loadError', 'Failed to load DataSink')}\n description={error.message}\n icon={<AlertCircle className=\"h-icon-sm w-icon-sm\" />}\n actions={\n <Button onClick={handleBack} variant=\"secondary\" size=\"sm\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.goBack', 'Go back')}\n </Button>\n }\n />\n </div>\n );\n }\n\n // Read gate — once permissions have loaded, block actors without read access.\n if (!permissionsLoading && !canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.dataSinks.view.accessDeniedTitle', 'Access Denied')}\n description={t(\n 'bigconsole.dataSinks.view.accessDeniedDescription',\n \"You don't have permission to view this data sink.\"\n )}\n />\n );\n }\n\n // Not found state\n if (!dataSink) {\n return (\n <div className=\"flex min-h-[240px] items-center justify-center p-card-padding\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('bigconsole.dataSinks.view.notFoundTitle', 'DataSink not found')}\n description={t(\n 'bigconsole.dataSinks.view.notFoundDescription',\n \"The DataSink you're looking for doesn't exist or has been deleted.\"\n )}\n action={\n <Button onClick={handleBack} variant=\"default\" size=\"default\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.goBackToDataSinks', 'Go back to DataSinks')}\n </Button>\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-w-0 p-card-padding\">\n {/* Page header strip — structural only, no surface of its own. */}\n <header className=\"mb-section-gap flex flex-col gap-form-gap sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"min-w-0 flex-1\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-4\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.backToDataSinks', 'Back to DataSinks')}\n </Button>\n <div className=\"flex flex-wrap items-center gap-2\">\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {dataSink.name || t('bigconsole.dataSinks.view.untitled', 'Untitled')}\n </h1>\n <DataSinkStatusBadge status={dataSink.status} />\n <span className=\"rounded-pill border border-border-subtle px-2 py-1 text-xs text-text-secondary\">\n v{dataSink.version}\n </span>\n </div>\n <p className=\"mt-1 break-all font-mono text-sm text-text-muted\">{dataSink.key}</p>\n {dataSink.description && <p className=\"mt-2 text-sm text-text-secondary\">{dataSink.description}</p>}\n </div>\n\n {/* Actions. Refresh is the primary act on this route, so the destructive\n button is no longer the loudest thing under the title. */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DATASINK_EXECUTE} deniedBehavior=\"disable\">\n <Button onClick={handleRefresh} disabled={isRefreshing} variant=\"default\" size=\"default\">\n <RefreshCw className={`w-4 h-4 mr-2 ${isRefreshing ? 'animate-spin' : ''}`} />\n {isRefreshing\n ? t('bigconsole.dataSinks.view.refreshing', 'Refreshing...')\n : t('bigconsole.dataSinks.view.refresh', 'Refresh')}\n </Button>\n </PermissionButton>\n <Button onClick={handleImport} variant=\"secondary\" size=\"default\">\n <Upload className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.importData', 'Import Data')}\n </Button>\n <Button onClick={handleExport} variant=\"secondary\" size=\"default\">\n <Download className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.export', 'Export')}\n </Button>\n <Button onClick={() => navigate(`/datasinks/${dataSinkId}/alerts`)} variant=\"secondary\" size=\"default\">\n <Bell className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.alerts.title', 'Alerts')}\n </Button>\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DATASINK_UPDATE} deniedBehavior=\"disable\">\n <Button onClick={handleEdit} variant=\"secondary\" size=\"default\">\n <Edit className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.edit', 'Edit')}\n </Button>\n </PermissionButton>\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DATASINK_DELETE} deniedBehavior=\"hide\">\n <Button onClick={handleDelete} disabled={isDeleting} variant=\"destructive\" size=\"default\">\n <Trash2 className=\"w-4 h-4 mr-2\" />\n {isDeleting\n ? t('bigconsole.dataSinks.view.deleting', 'Deleting...')\n : t('bigconsole.dataSinks.view.delete', 'Delete')}\n </Button>\n </PermissionButton>\n </div>\n </header>\n\n <PagePurpose className=\"mb-section-gap\">\n {t(\n 'bigconsole.dataSinks.view.pagePurpose',\n 'A data sink is a structured store that holds your processed data and serves it to dashboards. From here you can inspect its schema and stats, preview the cached rows, refresh it from its linked workflow, or export it.'\n )}\n </PagePurpose>\n\n <SectionStack className=\"mt-section-gap\">\n {/* Supporting context: four peer panels, one container idiom. */}\n <div className=\"grid min-w-0 gap-section-gap lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]\">\n {/* Reference metadata. `standard`, matching the three sibling panels in\n this grid — one recessed cell beside three raised ones read as a\n rendering fault rather than as hierarchy. The `primary` preview\n below still carries the elevation that separates it from all four. */}\n <Section\n headingLevel={2}\n title={t('bigconsole.dataSinks.view.details', 'Details')}\n description=\"Cache, refresh and storage settings for this sink.\"\n >\n <dl className=\"divide-y divide-[var(--color-border-subtle)] text-sm\">\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2 first:pt-0\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.status', 'Status')}</dt>\n <dd>\n <DataSinkStatusBadge status={dataSink.status} size=\"sm\" />\n </dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.cacheTtl', 'Cache TTL')}</dt>\n <dd className=\"font-medium text-text-primary\">\n {dataSink.ttl ? `${dataSink.ttl}s` : t('bigconsole.dataSinks.view.none', 'None')}\n </dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">\n {t('bigconsole.dataSinks.view.refreshType', 'Refresh Type')}\n </dt>\n <dd className=\"font-medium capitalize text-text-primary\">\n {dataSink.refresh?.type || t('bigconsole.dataSinks.view.manual', 'Manual')}\n </dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.size', 'Size')}</dt>\n <dd className=\"font-medium text-text-primary\">{formatByteSize(dataSink.sizeBytes || 0)}</dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.version', 'Version')}</dt>\n <dd className=\"font-medium text-text-primary\">{dataSink.version || 1}</dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">\n {t('bigconsole.dataSinks.view.lastRefreshed', 'Last Refreshed')}\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {dataSink.lastRefreshedAt\n ? formatDistanceToNow(new Date(dataSink.lastRefreshedAt), { addSuffix: true })\n : t('bigconsole.dataSinks.view.never', 'Never')}\n </dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2 last:pb-0\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.created', 'Created')}</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDistanceToNow(new Date(dataSink.createdAt), { addSuffix: true })}\n </dd>\n </div>\n </dl>\n </Section>\n\n {/* The point of this route: the cached rows themselves. */}\n <Section\n emphasis=\"primary\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.view.dataPreview', 'Data Preview')}\n description=\"The rows this sink is currently serving to dashboards.\"\n icon={<Database className=\"h-icon-sm w-icon-sm\" />}\n actions={\n <>\n {showData && (\n <div\n role=\"group\"\n aria-label={t('bigconsole.dataSinks.view.dataPreview', 'Data Preview')}\n className=\"flex items-center overflow-hidden rounded-button border border-[var(--color-border-default)]\"\n >\n <button\n type=\"button\"\n onClick={() => setDataViewMode('table')}\n aria-pressed={dataViewMode === 'table'}\n className={`flex h-control-md w-[var(--size-controlHeight-md)] items-center justify-center ${SECTION_FOCUS_RING} ${SEGMENT_RING_FIX} ${\n dataViewMode === 'table'\n ? 'bg-bg-sunken text-text-primary'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title={t('bigconsole.dataSinks.view.tableView', 'Table view')}\n >\n <Grid3X3 className=\"w-4 h-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setDataViewMode('json')}\n aria-pressed={dataViewMode === 'json'}\n className={`flex h-control-md w-[var(--size-controlHeight-md)] items-center justify-center ${SECTION_FOCUS_RING} ${SEGMENT_RING_FIX} ${\n dataViewMode === 'json'\n ? 'bg-bg-sunken text-text-primary'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title={t('bigconsole.dataSinks.view.jsonView', 'JSON view')}\n >\n <Code className=\"w-4 h-4\" />\n </button>\n </div>\n )}\n <Button\n onClick={handleShowData}\n disabled={dataLoading}\n variant={showData ? 'secondary' : 'default'}\n size=\"sm\"\n >\n {dataLoading ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n {t('bigconsole.dataSinks.view.loading', 'Loading...')}\n </>\n ) : showData ? (\n <>\n <EyeOff className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.hideData', 'Hide Data')}\n </>\n ) : (\n <>\n <Eye className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.showData', 'Show Data')}\n </>\n )}\n </Button>\n </>\n }\n >\n {showData && dataPreview && (\n <>\n {dataViewMode === 'table' ? (\n <DataSinkTableViewer data={dataPreview} maxHeight=\"400px\" />\n ) : (\n <DataSinkDataViewer data={dataPreview} maxHeight=\"400px\" />\n )}\n {/* v3.0: pagination controls — shown only for array payloads. */}\n {dataTotalCount !== null && (\n <div className=\"mt-form-gap flex flex-wrap items-center justify-between gap-form-gap border-t border-[var(--color-border-subtle)] pt-form-gap\">\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span>{t('bigconsole.dataSinks.view.rowsPerPage', 'Rows per page')}</span>\n <select\n aria-label={t('bigconsole.dataSinks.view.rowsPerPage', 'Rows per page')}\n className={`h-control-md rounded-input border border-[var(--color-border-default)] bg-bg-canvas px-2 text-text-primary ${SECTION_FOCUS_RING}`}\n value={pageSize}\n onChange={(e) => handlePageSizeChange(Number(e.target.value))}\n >\n {[100, 500, 1000].map((n) => (\n <option key={n} value={n}>\n {n}\n </option>\n ))}\n </select>\n </label>\n <div className=\"flex flex-wrap items-center gap-control-gap text-sm text-text-secondary\">\n <span>\n {dataTotalCount === 0\n ? t('bigconsole.dataSinks.view.zeroRows', '0 rows')\n : t('bigconsole.dataSinks.view.rowRange', '{{start}}–{{end}} of {{total}}', {\n start: pageOffset + 1,\n end: Math.min(pageOffset + pageSize, dataTotalCount),\n total: dataTotalCount,\n })}\n </span>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={pageOffset === 0 || dataLoading}\n onClick={() => void loadDataPage(Math.max(0, pageOffset - pageSize), pageSize)}\n >\n {t('bigconsole.dataSinks.view.previous', 'Previous')}\n </Button>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={!dataHasMore || dataLoading}\n onClick={() => void loadDataPage(pageOffset + pageSize, pageSize)}\n >\n {t('bigconsole.dataSinks.view.next', 'Next')}\n </Button>\n </div>\n </div>\n )}\n </>\n )}\n\n {!showData &&\n (dataSink.sizeBytes > 0 || (dataSink.data && Object.keys(dataSink.data).length > 0) ? (\n <p className=\"flex items-center gap-icon-gap text-sm text-text-secondary\">\n <Database aria-hidden=\"true\" className=\"h-icon-sm w-icon-sm shrink-0 text-text-muted\" />\n {t('bigconsole.dataSinks.view.showDataHint', 'Click \"Show Data\" to preview the cached data.')}\n </p>\n ) : (\n // No data yet — surface the Import Data flow so a new user knows the next step.\n <div className=\"flex flex-col items-center gap-form-gap py-form-gap text-center\">\n <Database aria-hidden=\"true\" className=\"h-icon-lg w-icon-lg text-text-muted\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'bigconsole.dataSinks.view.noDataYet',\n 'This data sink has no data yet. Upload a CSV or build a workflow to import data.'\n )}\n </p>\n <Button onClick={handleImport} variant=\"default\" size=\"sm\">\n <Upload className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.importData', 'Import Data')}\n </Button>\n </div>\n ))}\n </Section>\n </div>\n <div className=\"grid min-w-0 gap-section-gap md:grid-cols-2\">\n {/* Workflow Status Panel */}\n {dataSink.fluidGridsWorkflowId && (\n <WorkflowStatusPanel\n workflowId={dataSink.fluidGridsWorkflowId}\n executionId={dataSink.fluidGridsWorkflowExecutionId || undefined}\n lastRefreshedAt={dataSink.lastRefreshedAt || undefined}\n setupState={dataSink.setupState}\n status={dataSink.status}\n onRerunWorkflow={handleRefresh}\n disabled={isRefreshing}\n isRerunning={isRefreshing}\n />\n )}\n\n {/* Linked Workflows */}\n <LinkedWorkflowsPanel workflowLinks={dataSink.workflowLinks} />\n\n {/* Linked Parsers */}\n <LinkedParsersPanel dataSinkKey={dataSink.key} />\n </div>\n </SectionStack>\n <NextSteps\n storageKey=\"bigconsole-datasink-view\"\n steps={[\n {\n id: 'preview',\n label: t('bigconsole.dataSinks.view.nextSteps.previewLabel', 'Preview the data'),\n description: t(\n 'bigconsole.dataSinks.view.nextSteps.previewDescription',\n 'Open the cached rows to confirm the sink holds what you expect.'\n ),\n onClick: handleShowData,\n },\n {\n id: 'refresh',\n label: t('bigconsole.dataSinks.view.nextSteps.refreshLabel', 'Refresh from source'),\n description: t(\n 'bigconsole.dataSinks.view.nextSteps.refreshDescription',\n 'Re-run the linked workflow to pull the latest data.'\n ),\n onClick: handleRefresh,\n },\n {\n id: 'build-dashboard',\n label: t('bigconsole.dataSinks.view.nextSteps.dashboardLabel', 'Visualize it on a dashboard'),\n description: t(\n 'bigconsole.dataSinks.view.nextSteps.dashboardDescription',\n 'Add widgets that query this sink to see your data live.'\n ),\n onClick: () => navigate('/dashboards'),\n },\n {\n id: 'export',\n label: t('bigconsole.dataSinks.view.nextSteps.exportLabel', 'Export the data'),\n description: t(\n 'bigconsole.dataSinks.view.nextSteps.exportDescription',\n 'Download the contents for use elsewhere.'\n ),\n onClick: handleExport,\n },\n ]}\n />\n\n {/* Export DataSink Dialog */}\n <ExportDataSinkDialog\n isOpen={isExportDialogOpen}\n dataSinkId={dataSinkId || null}\n dataSinkName={dataSink.name || dataSink.key}\n onClose={handleExportClose}\n />\n\n {/* Import Data guide Dialog */}\n <ImportDataGuideDialog\n isOpen={isImportDialogOpen}\n dataSinkName={dataSink.name || dataSink.key}\n hasExistingData={dataSink.sizeBytes > 0 || Boolean(dataSink.data && Object.keys(dataSink.data).length > 0)}\n onClose={handleImportClose}\n onGoToWorkflows={handleGoToWorkflows}\n onImportCsv={handleImportCsv}\n />\n </div>\n );\n});\n\nexport default DataSinkViewPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataSinkViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinkViewPage.tsx"],"sourcesContent":["/**\n * DataSink View Page\n *\n * Page for viewing DataSink details, data preview, and actions.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router';\nimport { useAppNavigate } from '../contexts';\nimport { newWorkflowPath } from '../BigConsoleRoutes';\nimport { NEW_WORKFLOW_NAV } from '../workflowOpen';\nimport { isNative, nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { useDataSinkSubscription } from '../hooks/useDataSinkSubscription';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport { formatByteSize } from '../utils';\nimport { DataSinkStatusBadge } from '../components/datasink/DataSinkStatusBadge';\nimport { DataSinkDataViewer } from '../components/datasink/DataSinkDataViewer';\nimport { DataSinkTableViewer } from '../components/datasink/DataSinkTableViewer';\nimport { LinkedWorkflowsPanel } from '../components/datasink/LinkedWorkflowsPanel';\nimport { LinkedParsersPanel } from '../components/datasink/LinkedParsersPanel';\nimport { WorkflowStatusPanel } from '../components/datasink/WorkflowStatusPanel';\nimport { ExportDataSinkDialog } from '../components/datasink/ExportDataSinkDialog';\nimport { ImportDataGuideDialog } from '../components/datasink/ImportDataGuideDialog';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\n\n// Section-emphasis primitives (BOFF-5632)\nimport { Section, SectionStack, SECTION_FOCUS_RING } from '../components/common';\n\n// RBAC\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AccessDenied, PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\n\n// Lucide icons\nimport {\n ArrowLeft,\n Edit,\n RefreshCw,\n Trash2,\n Database,\n Loader2,\n AlertCircle,\n Eye,\n EyeOff,\n Download,\n Upload,\n Grid3X3,\n Code,\n Bell,\n} from 'lucide-react';\n\n/**\n * The preview-format segmented control clips its children (`overflow-hidden`\n * preserves the group's rounded corners), which would swallow an offset focus\n * ring entirely — and `SECTION_FOCUS_RING` also suppresses the native outline,\n * so the buttons would have no visible focus at all. Painting the ring inside\n * the button keeps it visible without dropping the group's rounding.\n */\nconst SEGMENT_RING_FIX = 'focus-visible:ring-inset focus-visible:ring-offset-0' as const;\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkViewPage = memo(function DataSinkViewPage() {\n const { dataSinkId } = useParams<{ dataSinkId: string; consoleId: string }>();\n const navigate = useAppNavigate();\n const { t } = useI18n();\n\n // RBAC — PermissionButton gates the action buttons directly; here we only need\n // the read gate that blocks the whole page.\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.DATASINK_READ);\n\n // Local state\n const [showData, setShowData] = useState(false);\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n const [isImportDialogOpen, setIsImportDialogOpen] = useState(false);\n const [dataPreview, setDataPreview] = useState<Record<string, unknown> | null>(null);\n const [dataLoading, setDataLoading] = useState(false);\n const [dataViewMode, setDataViewMode] = useState<'table' | 'json'>('table');\n // v3.0: server-side pagination for array payloads (avoids loading millions of rows).\n const [pageSize, setPageSize] = useState(100);\n const [pageOffset, setPageOffset] = useState(0);\n const [dataTotalCount, setDataTotalCount] = useState<number | null>(null);\n const [dataHasMore, setDataHasMore] = useState(false);\n\n // Store\n const dataSink = useDataSinkStore((state) => (dataSinkId ? state.dataSinks.get(dataSinkId) : undefined));\n\n // Operations\n const { loading, error, fetchDataSink, getDataSinkData, refreshDataSink, updateDataSink, deleteDataSink } =\n useDataSinkOperations({\n skipFetch: true,\n });\n\n // Subscribe to updates\n useDataSinkSubscription({\n dataSinkId,\n enabled: !!dataSinkId,\n });\n\n // Load DataSink on mount - always fetch full data to get workflowLinks\n useEffect(() => {\n if (dataSinkId) {\n fetchDataSink(dataSinkId);\n }\n }, [dataSinkId, fetchDataSink]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n navigate(`/datasinks`);\n }, [navigate]);\n\n // Handle edit\n const handleEdit = useCallback(() => {\n navigate(`/datasinks/${dataSinkId}/edit`);\n }, [navigate, dataSinkId]);\n\n // Handle refresh\n const handleRefresh = useCallback(async () => {\n if (!dataSinkId) return;\n setIsRefreshing(true);\n try {\n await refreshDataSink({ id: dataSinkId });\n } catch (err) {\n } finally {\n setIsRefreshing(false);\n }\n }, [dataSinkId, refreshDataSink]);\n\n // Handle delete\n const handleDelete = useCallback(async () => {\n if (!dataSinkId) return;\n const native = await nativeConfirm({\n title: t('bigconsole.dataSinks.view.deleteConfirmTitle', 'Delete DataSink?'),\n message: t('bigconsole.dataSinks.view.deleteConfirmMessage', 'Are you sure you want to delete this DataSink?'),\n okButtonTitle: t('bigconsole.dataSinks.view.delete', 'Delete'),\n cancelButtonTitle: t('bigconsole.dataSinks.view.cancel', 'Cancel'),\n });\n const ok =\n native === null\n ? window.confirm(\n t('bigconsole.dataSinks.view.deleteConfirmMessage', 'Are you sure you want to delete this DataSink?')\n )\n : native;\n if (!ok) return;\n nativeImpact('medium');\n setIsDeleting(true);\n try {\n await deleteDataSink(dataSinkId);\n if (isNative()) void nativeNotify('success');\n navigate(`/datasinks`);\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n }, [dataSinkId, deleteDataSink, navigate, t]);\n\n // Load one page of the sink's data (v3.0: server-side pagination).\n const loadDataPage = useCallback(\n async (offset: number, size: number) => {\n if (!dataSinkId) return;\n setDataLoading(true);\n try {\n const result = await getDataSinkData({ id: dataSinkId }, { limit: size, offset });\n if (result) {\n setDataPreview((result.raw as Record<string, unknown>) || {});\n // totalCount is non-null only for array payloads being paginated.\n setDataTotalCount(typeof result.totalCount === 'number' ? result.totalCount : null);\n setDataHasMore(Boolean(result.hasMore));\n setPageOffset(offset);\n }\n } catch {\n // handled by the hook's error state\n } finally {\n setDataLoading(false);\n }\n },\n [dataSinkId, getDataSinkData]\n );\n\n // Handle show data (toggles the preview; loads the first page).\n const handleShowData = useCallback(async () => {\n if (!dataSinkId) return;\n if (showData) {\n setShowData(false);\n return;\n }\n await loadDataPage(0, pageSize);\n setShowData(true);\n }, [dataSinkId, showData, loadDataPage, pageSize]);\n\n const handlePageSizeChange = useCallback(\n (size: number) => {\n setPageSize(size);\n void loadDataPage(0, size);\n },\n [loadDataPage]\n );\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // Handle import — opens a guide explaining that data is imported via a workflow.\n const handleImport = useCallback(() => {\n setIsImportDialogOpen(true);\n }, []);\n\n const handleImportClose = useCallback(() => {\n setIsImportDialogOpen(false);\n }, []);\n\n const handleGoToWorkflows = useCallback(() => {\n setIsImportDialogOpen(false);\n // Navigate straight to the builder path (basePath-aware via useAppNavigate).\n // Going via /workflows/new would hit a <Navigate> redirect to an absolute\n // path that drops the MFE base path — see WorkflowsPage for the same pattern.\n navigate(newWorkflowPath(), NEW_WORKFLOW_NAV);\n }, [navigate]);\n\n // Direct CSV import — write the parsed rows straight to the sink (no workflow).\n const handleImportCsv = useCallback(\n async (rows: Record<string, unknown>[]): Promise<boolean> => {\n if (!dataSink) return false;\n // The DataSink `data` payload is a JSON scalar that accepts any JSON value;\n // tabular imports are stored as an array of row objects — the shape the\n // table viewer and parsers read. The generated input type narrows JSON to\n // Record<string, unknown>, so cast the row array to satisfy it.\n const updated = await updateDataSink({\n id: dataSink.id,\n version: dataSink.version,\n data: rows as unknown as Record<string, unknown>,\n });\n if (!updated) return false;\n // The import already succeeded. Revealing the data is best-effort — a\n // failure here must not turn a persisted import into a reported failure\n // (which would prompt a retry and hit a version conflict).\n try {\n await loadDataPage(0, pageSize);\n setShowData(true);\n } catch {\n // Data is saved; the user can open the preview manually.\n }\n return true;\n },\n [dataSink, updateDataSink, loadDataPage, pageSize]\n );\n\n // Loading state\n if (loading && !dataSink) {\n return (\n <div className=\"flex min-h-[240px] items-center justify-center p-card-padding\">\n <Loader2 className=\"h-icon-lg w-icon-lg animate-spin text-text-secondary\" />\n </div>\n );\n }\n\n // Error state\n // Only treat the shared hook error as fatal when the sink never loaded. Once\n // it is loaded, transient action errors (e.g. a failed CSV import) surface\n // inline in their own UI and must not replace the whole page.\n if (error && !dataSink) {\n return (\n <div className=\"p-card-padding\">\n <Section\n tone=\"danger\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.view.loadError', 'Failed to load DataSink')}\n description={error.message}\n icon={<AlertCircle className=\"h-icon-sm w-icon-sm\" />}\n actions={\n <Button onClick={handleBack} variant=\"secondary\" size=\"sm\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.goBack', 'Go back')}\n </Button>\n }\n />\n </div>\n );\n }\n\n // Read gate — once permissions have loaded, block actors without read access.\n if (!permissionsLoading && !canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.dataSinks.view.accessDeniedTitle', 'Access Denied')}\n description={t(\n 'bigconsole.dataSinks.view.accessDeniedDescription',\n \"You don't have permission to view this data sink.\"\n )}\n />\n );\n }\n\n // Not found state\n if (!dataSink) {\n return (\n <div className=\"flex min-h-[240px] items-center justify-center p-card-padding\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('bigconsole.dataSinks.view.notFoundTitle', 'DataSink not found')}\n description={t(\n 'bigconsole.dataSinks.view.notFoundDescription',\n \"The DataSink you're looking for doesn't exist or has been deleted.\"\n )}\n action={\n <Button onClick={handleBack} variant=\"default\" size=\"default\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.goBackToDataSinks', 'Go back to DataSinks')}\n </Button>\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-w-0 p-card-padding\">\n {/* Page header strip — structural only, no surface of its own. */}\n <header className=\"mb-section-gap flex flex-col gap-form-gap sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"min-w-0 flex-1\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-4\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.backToDataSinks', 'Back to DataSinks')}\n </Button>\n <div className=\"flex flex-wrap items-center gap-2\">\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {dataSink.name || t('bigconsole.dataSinks.view.untitled', 'Untitled')}\n </h1>\n <DataSinkStatusBadge status={dataSink.status} />\n <span className=\"rounded-pill border border-border-subtle px-2 py-1 text-xs text-text-secondary\">\n v{dataSink.version}\n </span>\n </div>\n <p className=\"mt-1 break-all font-mono text-sm text-text-muted\">{dataSink.key}</p>\n {dataSink.description && <p className=\"mt-2 text-sm text-text-secondary\">{dataSink.description}</p>}\n </div>\n\n {/* Actions. Refresh is the primary act on this route, so the destructive\n button is no longer the loudest thing under the title. */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DATASINK_EXECUTE} deniedBehavior=\"disable\">\n <Button onClick={handleRefresh} disabled={isRefreshing} variant=\"default\" size=\"default\">\n <RefreshCw className={`w-4 h-4 mr-2 ${isRefreshing ? 'animate-spin' : ''}`} />\n {isRefreshing\n ? t('bigconsole.dataSinks.view.refreshing', 'Refreshing...')\n : t('bigconsole.dataSinks.view.refresh', 'Refresh')}\n </Button>\n </PermissionButton>\n <Button onClick={handleImport} variant=\"secondary\" size=\"default\">\n <Upload className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.importData', 'Import Data')}\n </Button>\n <Button onClick={handleExport} variant=\"secondary\" size=\"default\">\n <Download className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.export', 'Export')}\n </Button>\n <Button onClick={() => navigate(`/datasinks/${dataSinkId}/alerts`)} variant=\"secondary\" size=\"default\">\n <Bell className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.alerts.title', 'Alerts')}\n </Button>\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DATASINK_UPDATE} deniedBehavior=\"disable\">\n <Button onClick={handleEdit} variant=\"secondary\" size=\"default\">\n <Edit className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.edit', 'Edit')}\n </Button>\n </PermissionButton>\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DATASINK_DELETE} deniedBehavior=\"hide\">\n <Button onClick={handleDelete} disabled={isDeleting} variant=\"destructive\" size=\"default\">\n <Trash2 className=\"w-4 h-4 mr-2\" />\n {isDeleting\n ? t('bigconsole.dataSinks.view.deleting', 'Deleting...')\n : t('bigconsole.dataSinks.view.delete', 'Delete')}\n </Button>\n </PermissionButton>\n </div>\n </header>\n\n <PagePurpose className=\"mb-section-gap\">\n {t(\n 'bigconsole.dataSinks.view.pagePurpose',\n 'A data sink is a structured store that holds your processed data and serves it to dashboards. From here you can inspect its schema and stats, preview the cached rows, refresh it from its linked workflow, or export it.'\n )}\n </PagePurpose>\n\n <SectionStack className=\"mt-section-gap\">\n {/* Supporting context: four peer panels, one container idiom. */}\n <div className=\"grid min-w-0 gap-section-gap lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]\">\n {/* Reference metadata. `standard`, matching the three sibling panels in\n this grid — one recessed cell beside three raised ones read as a\n rendering fault rather than as hierarchy. The `primary` preview\n below still carries the elevation that separates it from all four. */}\n <Section\n headingLevel={2}\n title={t('bigconsole.dataSinks.view.details', 'Details')}\n description=\"Cache, refresh and storage settings for this sink.\"\n >\n <dl className=\"divide-y divide-[var(--color-border-subtle)] text-sm\">\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2 first:pt-0\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.status', 'Status')}</dt>\n <dd>\n <DataSinkStatusBadge status={dataSink.status} size=\"sm\" />\n </dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.cacheTtl', 'Cache TTL')}</dt>\n <dd className=\"font-medium text-text-primary\">\n {dataSink.ttl ? `${dataSink.ttl}s` : t('bigconsole.dataSinks.view.none', 'None')}\n </dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">\n {t('bigconsole.dataSinks.view.refreshType', 'Refresh Type')}\n </dt>\n <dd className=\"font-medium capitalize text-text-primary\">\n {dataSink.refresh?.type || t('bigconsole.dataSinks.view.manual', 'Manual')}\n </dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.size', 'Size')}</dt>\n <dd className=\"font-medium text-text-primary\">{formatByteSize(dataSink.sizeBytes || 0)}</dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.version', 'Version')}</dt>\n <dd className=\"font-medium text-text-primary\">{dataSink.version || 1}</dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2\">\n <dt className=\"min-w-0 text-text-secondary\">\n {t('bigconsole.dataSinks.view.lastRefreshed', 'Last Refreshed')}\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {dataSink.lastRefreshedAt\n ? formatDistanceToNow(new Date(dataSink.lastRefreshedAt), { addSuffix: true })\n : t('bigconsole.dataSinks.view.never', 'Never')}\n </dd>\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-form-gap gap-y-1 py-2 last:pb-0\">\n <dt className=\"min-w-0 text-text-secondary\">{t('bigconsole.dataSinks.view.created', 'Created')}</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDistanceToNow(new Date(dataSink.createdAt), { addSuffix: true })}\n </dd>\n </div>\n </dl>\n </Section>\n\n {/* The point of this route: the cached rows themselves. */}\n <Section\n emphasis=\"primary\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.view.dataPreview', 'Data Preview')}\n description=\"The rows this sink is currently serving to dashboards.\"\n icon={<Database className=\"h-icon-sm w-icon-sm\" />}\n actions={\n <>\n {showData && (\n <div\n role=\"group\"\n aria-label={t('bigconsole.dataSinks.view.dataPreview', 'Data Preview')}\n className=\"flex items-center overflow-hidden rounded-button border border-[var(--color-border-default)]\"\n >\n <button\n type=\"button\"\n onClick={() => setDataViewMode('table')}\n aria-pressed={dataViewMode === 'table'}\n className={`flex h-control-md w-[var(--size-controlHeight-md)] items-center justify-center ${SECTION_FOCUS_RING} ${SEGMENT_RING_FIX} ${\n dataViewMode === 'table'\n ? 'bg-bg-sunken text-text-primary'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title={t('bigconsole.dataSinks.view.tableView', 'Table view')}\n >\n <Grid3X3 className=\"w-4 h-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setDataViewMode('json')}\n aria-pressed={dataViewMode === 'json'}\n className={`flex h-control-md w-[var(--size-controlHeight-md)] items-center justify-center ${SECTION_FOCUS_RING} ${SEGMENT_RING_FIX} ${\n dataViewMode === 'json'\n ? 'bg-bg-sunken text-text-primary'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title={t('bigconsole.dataSinks.view.jsonView', 'JSON view')}\n >\n <Code className=\"w-4 h-4\" />\n </button>\n </div>\n )}\n <Button\n onClick={handleShowData}\n disabled={dataLoading}\n variant={showData ? 'secondary' : 'default'}\n size=\"sm\"\n >\n {dataLoading ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n {t('bigconsole.dataSinks.view.loading', 'Loading...')}\n </>\n ) : showData ? (\n <>\n <EyeOff className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.hideData', 'Hide Data')}\n </>\n ) : (\n <>\n <Eye className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.showData', 'Show Data')}\n </>\n )}\n </Button>\n </>\n }\n >\n {showData && dataPreview && (\n <>\n {dataViewMode === 'table' ? (\n <DataSinkTableViewer data={dataPreview} maxHeight=\"400px\" />\n ) : (\n <DataSinkDataViewer data={dataPreview} maxHeight=\"400px\" />\n )}\n {/* v3.0: pagination controls — shown only for array payloads. */}\n {dataTotalCount !== null && (\n <div className=\"mt-form-gap flex flex-wrap items-center justify-between gap-form-gap border-t border-[var(--color-border-subtle)] pt-form-gap\">\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span>{t('bigconsole.dataSinks.view.rowsPerPage', 'Rows per page')}</span>\n <select\n aria-label={t('bigconsole.dataSinks.view.rowsPerPage', 'Rows per page')}\n className={`h-control-md rounded-input border border-[var(--color-border-default)] bg-bg-canvas px-2 text-text-primary ${SECTION_FOCUS_RING}`}\n value={pageSize}\n onChange={(e) => handlePageSizeChange(Number(e.target.value))}\n >\n {[100, 500, 1000].map((n) => (\n <option key={n} value={n}>\n {n}\n </option>\n ))}\n </select>\n </label>\n <div className=\"flex flex-wrap items-center gap-control-gap text-sm text-text-secondary\">\n <span>\n {dataTotalCount === 0\n ? t('bigconsole.dataSinks.view.zeroRows', '0 rows')\n : t('bigconsole.dataSinks.view.rowRange', '{{start}}–{{end}} of {{total}}', {\n start: pageOffset + 1,\n end: Math.min(pageOffset + pageSize, dataTotalCount),\n total: dataTotalCount,\n })}\n </span>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={pageOffset === 0 || dataLoading}\n onClick={() => void loadDataPage(Math.max(0, pageOffset - pageSize), pageSize)}\n >\n {t('bigconsole.dataSinks.view.previous', 'Previous')}\n </Button>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={!dataHasMore || dataLoading}\n onClick={() => void loadDataPage(pageOffset + pageSize, pageSize)}\n >\n {t('bigconsole.dataSinks.view.next', 'Next')}\n </Button>\n </div>\n </div>\n )}\n </>\n )}\n\n {!showData &&\n (dataSink.sizeBytes > 0 || (dataSink.data && Object.keys(dataSink.data).length > 0) ? (\n <p className=\"flex items-center gap-icon-gap text-sm text-text-secondary\">\n <Database aria-hidden=\"true\" className=\"h-icon-sm w-icon-sm shrink-0 text-text-muted\" />\n {t('bigconsole.dataSinks.view.showDataHint', 'Click \"Show Data\" to preview the cached data.')}\n </p>\n ) : (\n // No data yet — surface the Import Data flow so a new user knows the next step.\n <div className=\"flex flex-col items-center gap-form-gap py-form-gap text-center\">\n <Database aria-hidden=\"true\" className=\"h-icon-lg w-icon-lg text-text-muted\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'bigconsole.dataSinks.view.noDataYet',\n 'This data sink has no data yet. Upload a CSV or build a workflow to import data.'\n )}\n </p>\n <Button onClick={handleImport} variant=\"default\" size=\"sm\">\n <Upload className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.view.importData', 'Import Data')}\n </Button>\n </div>\n ))}\n </Section>\n </div>\n <div className=\"grid min-w-0 gap-section-gap md:grid-cols-2\">\n {/* Workflow Status Panel */}\n {dataSink.fluidGridsWorkflowId && (\n <WorkflowStatusPanel\n workflowId={dataSink.fluidGridsWorkflowId}\n executionId={dataSink.fluidGridsWorkflowExecutionId || undefined}\n lastRefreshedAt={dataSink.lastRefreshedAt || undefined}\n setupState={dataSink.setupState}\n status={dataSink.status}\n onRerunWorkflow={handleRefresh}\n disabled={isRefreshing}\n isRerunning={isRefreshing}\n />\n )}\n\n {/* Linked Workflows */}\n <LinkedWorkflowsPanel workflowLinks={dataSink.workflowLinks} />\n\n {/* Linked Parsers */}\n <LinkedParsersPanel dataSinkKey={dataSink.key} />\n </div>\n </SectionStack>\n <NextSteps\n storageKey=\"bigconsole-datasink-view\"\n steps={[\n {\n id: 'preview',\n label: t('bigconsole.dataSinks.view.nextSteps.previewLabel', 'Preview the data'),\n description: t(\n 'bigconsole.dataSinks.view.nextSteps.previewDescription',\n 'Open the cached rows to confirm the sink holds what you expect.'\n ),\n onClick: handleShowData,\n },\n {\n id: 'refresh',\n label: t('bigconsole.dataSinks.view.nextSteps.refreshLabel', 'Refresh from source'),\n description: t(\n 'bigconsole.dataSinks.view.nextSteps.refreshDescription',\n 'Re-run the linked workflow to pull the latest data.'\n ),\n onClick: handleRefresh,\n },\n {\n id: 'build-dashboard',\n label: t('bigconsole.dataSinks.view.nextSteps.dashboardLabel', 'Visualize it on a dashboard'),\n description: t(\n 'bigconsole.dataSinks.view.nextSteps.dashboardDescription',\n 'Add widgets that query this sink to see your data live.'\n ),\n onClick: () => navigate('/dashboards'),\n },\n {\n id: 'export',\n label: t('bigconsole.dataSinks.view.nextSteps.exportLabel', 'Export the data'),\n description: t(\n 'bigconsole.dataSinks.view.nextSteps.exportDescription',\n 'Download the contents for use elsewhere.'\n ),\n onClick: handleExport,\n },\n ]}\n />\n\n {/* Export DataSink Dialog */}\n <ExportDataSinkDialog\n isOpen={isExportDialogOpen}\n dataSinkId={dataSinkId || null}\n dataSinkName={dataSink.name || dataSink.key}\n onClose={handleExportClose}\n />\n\n {/* Import Data guide Dialog */}\n <ImportDataGuideDialog\n isOpen={isImportDialogOpen}\n dataSinkName={dataSink.name || dataSink.key}\n hasExistingData={dataSink.sizeBytes > 0 || Boolean(dataSink.data && Object.keys(dataSink.data).length > 0)}\n onClose={handleImportClose}\n onGoToWorkflows={handleGoToWorkflows}\n onImportCsv={handleImportCsv}\n />\n </div>\n );\n});\n\nexport default DataSinkViewPage;\n"],"mappings":"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"}
@@ -17,8 +17,8 @@ import { AccessDenied as ce, PermissionButton as p } from "@burdenoff/fe-libs/sh
17
17
  import { usePermissions as le } from "@burdenoff/fe-libs/shared/providers/shell";
18
18
  import { Button as m, IllustratedEmptyState as h, NextSteps as ue, PagePurpose as de, Spinner as g } from "@burdenoff/fe-libs/ui";
19
19
  import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
20
- import { useLazyWorkflowDefinition as fe } from "@burdenoff/fluidgrids-fe-sdk";
21
- import { useI18n as pe } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
20
+ import { useI18n as fe } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
21
+ import { useLazyWorkflowDefinition as pe } from "@burdenoff/fluidgrids-fe-sdk";
22
22
  import { formatDistanceToNow as b } from "date-fns";
23
23
  //#region src/bigconsole/pages/WorkflowViewPage.tsx
24
24
  var me = ({ className: e }) => /* @__PURE__ */ v("svg", {
@@ -115,9 +115,9 @@ var me = ({ className: e }) => /* @__PURE__ */ v("svg", {
115
115
  d: "M19 9l-7 7-7-7"
116
116
  })
117
117
  }), x = u(function() {
118
- let u = e(), { t: x } = pe(), { workflowId: S } = se(), { hasPermission: C, isLoading: Se } = le(), Ce = C(s.WORKFLOW_READ), w = C(s.WORKFLOW_UPDATE), T = C(s.DATASINK_READ);
118
+ let u = e(), { t: x } = fe(), { workflowId: S } = se(), { hasPermission: C, isLoading: Se } = le(), Ce = C(s.WORKFLOW_READ), w = C(s.WORKFLOW_UPDATE), T = C(s.DATASINK_READ);
119
119
  C(s.WORKFLOW_DELETE);
120
- let we = C(s.WORKFLOW_EXECUTE), [E, D] = f(null), [Te, O] = f(!1), [, k] = f(!1), [A, j] = f(!1), [Ee, M] = f(!1), [N, P] = f(!1), [F, I] = f(null), [De, L] = f(null), { unlinkDataSink: Oe } = t({ skipFetch: !0 }), [R, z] = f(!1), { getWorkflow: ke, workflow: B, loading: Ae, error: je } = fe(), [Me, V] = f(!1), [H, U] = f(null), { loading: Ne, error: W, fetchWorkflow: G, deleteWorkflow: K, executeWorkflow: q, pollWorkflowExecution: J } = t({ skipFetch: !0 });
120
+ let we = C(s.WORKFLOW_EXECUTE), [E, D] = f(null), [Te, O] = f(!1), [, k] = f(!1), [A, j] = f(!1), [Ee, M] = f(!1), [N, P] = f(!1), [F, I] = f(null), [De, L] = f(null), { unlinkDataSink: Oe } = t({ skipFetch: !0 }), [R, z] = f(!1), { getWorkflow: ke, workflow: B, loading: Ae, error: je } = pe(), [Me, V] = f(!1), [H, U] = f(null), { loading: Ne, error: W, fetchWorkflow: G, deleteWorkflow: K, executeWorkflow: q, pollWorkflowExecution: J } = t({ skipFetch: !0 });
121
121
  oe(() => {
122
122
  if (!S) {
123
123
  V(!0);
@@ -1 +1 @@
1
- {"version":3,"file":"WorkflowsPage.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/pages/WorkflowsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA2CH,eAAO,MAAM,aAAa,8CAqZxB,CAAC;AAEH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"WorkflowsPage.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/pages/WorkflowsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA4CH,eAAO,MAAM,aAAa,8CAqZxB,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -2,24 +2,25 @@ import { useWorkflowStore as e } from "../store/workflowStore.js";
2
2
  import { useAppNavigate as t } from "../contexts/NavigationContext.js";
3
3
  import "../contexts/index.js";
4
4
  import n from "../hooks/useWorkflowOperations.js";
5
- import { newWorkflowPath as r } from "../BigConsoleRoutes.js";
5
+ import { NEW_WORKFLOW_NAV as r } from "../workflowOpen.js";
6
+ import { newWorkflowPath as ee } from "../BigConsoleRoutes.js";
6
7
  import { SECTION_FOCUS_RING as i } from "../components/common/sectionStyles.js";
7
- import { Section as a, SectionStack as ee } from "../components/common/Section.js";
8
+ import { Section as a, SectionStack as o } from "../components/common/Section.js";
8
9
  import "../components/common/index.js";
9
- import { BIGCONSOLE_PERMISSIONS as o } from "../../constants/permissions.js";
10
- import { isNative as s, nativeNotify as c } from "../../utils/nativeBridge.js";
11
- import l from "../components/workflow/WorkflowDeleteDialog.js";
12
- import u from "../components/workflow/WorkflowCard.js";
13
- import { memo as d, useCallback as f, useMemo as te, useState as p } from "react";
14
- import { AccessDenied as m, PermissionButton as h } from "@burdenoff/fe-libs/shared/components";
15
- import { usePermissions as g } from "@burdenoff/fe-libs/shared/providers/shell";
16
- import { AlertCircle as _, Plus as v, Search as y, Workflow as b } from "lucide-react";
17
- import { AuroraBackground as ne, Button as x, GradientText as S, IllustratedEmptyState as C, Input as w, PagePurpose as T, Spinner as re } from "@burdenoff/fe-libs/ui";
18
- import { Fragment as ie, jsx as E, jsxs as D } from "react/jsx-runtime";
10
+ import { BIGCONSOLE_PERMISSIONS as s } from "../../constants/permissions.js";
11
+ import { isNative as c, nativeNotify as l } from "../../utils/nativeBridge.js";
12
+ import u from "../components/workflow/WorkflowDeleteDialog.js";
13
+ import d from "../components/workflow/WorkflowCard.js";
14
+ import { memo as f, useCallback as p, useMemo as m, useState as h } from "react";
15
+ import { AccessDenied as g, PermissionButton as _ } from "@burdenoff/fe-libs/shared/components";
16
+ import { usePermissions as v } from "@burdenoff/fe-libs/shared/providers/shell";
17
+ import { AlertCircle as y, Plus as b, Search as te, Workflow as x } from "lucide-react";
18
+ import { AuroraBackground as S, Button as C, GradientText as w, IllustratedEmptyState as T, Input as ne, PagePurpose as re, Spinner as ie } from "@burdenoff/fe-libs/ui";
19
+ import { Fragment as ae, jsx as E, jsxs as D } from "react/jsx-runtime";
19
20
  import { useI18n as O } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
20
21
  //#region src/bigconsole/pages/WorkflowsPage.tsx
21
- var k = d(function() {
22
- let d = t(), { t: k } = O(), { hasPermission: A, isLoading: j } = g(), M = A(o.WORKFLOW_READ), N = A(o.WORKFLOW_UPDATE), P = A(o.WORKFLOW_DELETE), F = A(o.WORKFLOW_EXECUTE), [I, L] = p("all"), [R, z] = p(null), B = e((e) => e.searchQuery), V = e((e) => e.setSearchQuery), H = e((e) => e.deleteDialogOpen), U = e((e) => e.currentWorkflow), W = e((e) => e.openDeleteDialog), G = e((e) => e.closeDeleteDialog), { loading: K, error: q, workflows: J, deleteWorkflow: Y, refetch: ae } = n(), { executeWorkflow: X } = n({ skipFetch: !0 }), oe = e((e) => e.workflowUIStates), Z = te(() => {
22
+ var k = f(function() {
23
+ let f = t(), { t: k } = O(), { hasPermission: A, isLoading: j } = v(), M = A(s.WORKFLOW_READ), N = A(s.WORKFLOW_UPDATE), P = A(s.WORKFLOW_DELETE), F = A(s.WORKFLOW_EXECUTE), [I, L] = h("all"), [R, z] = h(null), B = e((e) => e.searchQuery), V = e((e) => e.setSearchQuery), H = e((e) => e.deleteDialogOpen), U = e((e) => e.currentWorkflow), W = e((e) => e.openDeleteDialog), G = e((e) => e.closeDeleteDialog), { loading: K, error: q, workflows: J, deleteWorkflow: Y, refetch: oe } = n(), { executeWorkflow: X } = n({ skipFetch: !0 }), se = e((e) => e.workflowUIStates), Z = m(() => {
23
24
  let e = [...J];
24
25
  if (I !== "all" && (e = e.filter((e) => e.status === I)), B.trim()) {
25
26
  let t = B.toLowerCase();
@@ -30,28 +31,28 @@ var k = d(function() {
30
31
  J,
31
32
  I,
32
33
  B
33
- ]), Q = f(() => {
34
- d(r());
35
- }, [d]), se = f((e) => {
36
- d(`/workflows/details/${e}`);
37
- }, [d]), ce = f((e) => {
34
+ ]), Q = p(() => {
35
+ f(ee(), r);
36
+ }, [f]), ce = p((e) => {
37
+ f(`/workflows/details/${e}`);
38
+ }, [f]), le = p((e) => {
38
39
  let t = J.find((t) => t.id === e);
39
- t?.key && d(`/workflows/workflow/${encodeURIComponent(t.key)}/v1`);
40
- }, [d, J]), le = f((e) => {
40
+ t?.key && f(`/workflows/workflow/${encodeURIComponent(t.key)}/v1`);
41
+ }, [f, J]), $ = p((e) => {
41
42
  let t = J.find((t) => t.id === e);
42
43
  t && W(t);
43
- }, [J, W]), $ = f(async (e) => {
44
+ }, [J, W]), ue = p(async (e) => {
44
45
  let t = J.find((t) => t.id === e);
45
46
  if (t) {
46
47
  if (z(null), await X(e)) {
47
- s() && c("success"), z({
48
+ c() && l("success"), z({
48
49
  workflowName: t.name,
49
50
  ok: !0,
50
51
  message: k("bigconsole.workflows.run.triggered", "Run triggered. The execution count and last-run time update as the run progresses.")
51
52
  });
52
53
  return;
53
54
  }
54
- s() && c("error"), z({
55
+ c() && l("error"), z({
55
56
  workflowName: t.name,
56
57
  ok: !1,
57
58
  message: k("bigconsole.workflows.run.failed", "The workflow could not be started. Nothing has been changed.")
@@ -61,12 +62,12 @@ var k = d(function() {
61
62
  J,
62
63
  X,
63
64
  k
64
- ]), ue = f(async () => {
65
+ ]), de = p(async () => {
65
66
  if (U) {
66
67
  try {
67
- await Y(U.id), s() && c("success");
68
+ await Y(U.id), c() && l("success");
68
69
  } catch (e) {
69
- throw s() && c("error"), e;
70
+ throw c() && l("error"), e;
70
71
  }
71
72
  G();
72
73
  }
@@ -74,7 +75,7 @@ var k = d(function() {
74
75
  U,
75
76
  Y,
76
77
  G
77
- ]), de = [
78
+ ]), fe = [
78
79
  {
79
80
  value: "all",
80
81
  label: k("bigconsole.workflows.filters.all", "All")
@@ -100,11 +101,11 @@ var k = d(function() {
100
101
  label: k("bigconsole.workflows.status.archived", "Archived")
101
102
  }
102
103
  ];
103
- if (!j && !M) return /* @__PURE__ */ E(m, {
104
+ if (!j && !M) return /* @__PURE__ */ E(g, {
104
105
  title: k("bigconsole.workflows.errors.accessDeniedTitle", "Access Denied"),
105
106
  description: k("bigconsole.workflows.errors.accessDeniedListDescription", "You don't have permission to view workflows.")
106
107
  });
107
- let fe = !K && J.length === 0, pe = !K && Z.length === 0 && J.length > 0;
108
+ let pe = !K && J.length === 0, me = !K && Z.length === 0 && J.length > 0;
108
109
  return /* @__PURE__ */ D("div", {
109
110
  "data-tour": "bigconsole-workflows-list",
110
111
  className: "min-h-dvh bg-bg-canvas",
@@ -112,19 +113,19 @@ var k = d(function() {
112
113
  /* @__PURE__ */ D("header", {
113
114
  "data-section-emphasis": "bare",
114
115
  className: "relative min-w-0 overflow-hidden border-b border-[var(--color-border-seam)] bg-[image:var(--gradient-gloss-card)]",
115
- children: [/* @__PURE__ */ E(ne, { intensity: "subtle" }), /* @__PURE__ */ D("div", {
116
+ children: [/* @__PURE__ */ E(S, { intensity: "subtle" }), /* @__PURE__ */ D("div", {
116
117
  className: "relative flex flex-col gap-form-gap p-[var(--space-pagePadding)] sm:flex-row sm:items-center sm:justify-between",
117
118
  children: [/* @__PURE__ */ D("div", {
118
119
  className: "flex min-w-0 items-center gap-form-gap",
119
120
  children: [/* @__PURE__ */ E("span", {
120
121
  "aria-hidden": "true",
121
122
  className: "flex h-control-lg w-[var(--size-controlHeight-lg)] shrink-0 items-center justify-center rounded-card bg-action-primary-bg",
122
- children: /* @__PURE__ */ E(b, { className: "h-icon-lg w-icon-lg text-action-primary-text" })
123
+ children: /* @__PURE__ */ E(x, { className: "h-icon-lg w-icon-lg text-action-primary-text" })
123
124
  }), /* @__PURE__ */ D("div", {
124
125
  className: "min-w-0",
125
126
  children: [/* @__PURE__ */ E("h1", {
126
127
  className: "text-2xl font-bold text-text-primary",
127
- children: /* @__PURE__ */ E(S, { children: k("bigconsole.workflows.list.title", "Workflows") })
128
+ children: /* @__PURE__ */ E(w, { children: k("bigconsole.workflows.list.title", "Workflows") })
128
129
  }), /* @__PURE__ */ E("p", {
129
130
  className: "text-sm text-text-secondary",
130
131
  children: k("bigconsole.workflows.list.subtitle", "Design ETL workflows with FluidGrids and link them to DataSinks")
@@ -132,12 +133,12 @@ var k = d(function() {
132
133
  })]
133
134
  }), /* @__PURE__ */ E("div", {
134
135
  className: "flex flex-wrap items-center gap-control-gap",
135
- children: /* @__PURE__ */ E(h, {
136
- permission: o.WORKFLOW_CREATE,
136
+ children: /* @__PURE__ */ E(_, {
137
+ permission: s.WORKFLOW_CREATE,
137
138
  deniedBehavior: "disable",
138
- children: /* @__PURE__ */ D(x, {
139
+ children: /* @__PURE__ */ D(C, {
139
140
  onClick: Q,
140
- children: [/* @__PURE__ */ E(v, { className: "h-icon-sm w-icon-sm" }), k("bigconsole.workflows.actions.designWorkflow", "Design Workflow")]
141
+ children: [/* @__PURE__ */ E(b, { className: "h-icon-sm w-icon-sm" }), k("bigconsole.workflows.actions.designWorkflow", "Design Workflow")]
141
142
  })
142
143
  })
143
144
  })]
@@ -145,15 +146,15 @@ var k = d(function() {
145
146
  }),
146
147
  /* @__PURE__ */ E("div", {
147
148
  className: "p-[var(--space-pagePadding)]",
148
- children: /* @__PURE__ */ D(ee, { children: [/* @__PURE__ */ E(T, { children: k("bigconsole.workflows.list.purpose", "Workflows are visual ETL jobs built with the FluidGrids builder — they fetch data from external systems, transform it, and load it into your BigConsole DataSinks on a schedule or on demand. Use them to keep dashboards fed with fresh, shaped data without writing glue code.") }), q ? /* @__PURE__ */ E(a, {
149
+ children: /* @__PURE__ */ D(o, { children: [/* @__PURE__ */ E(re, { children: k("bigconsole.workflows.list.purpose", "Workflows are visual ETL jobs built with the FluidGrids builder — they fetch data from external systems, transform it, and load it into your BigConsole DataSinks on a schedule or on demand. Use them to keep dashboards fed with fresh, shaped data without writing glue code.") }), q ? /* @__PURE__ */ E(a, {
149
150
  headingLevel: 2,
150
151
  tone: "danger",
151
152
  title: k("bigconsole.workflows.errors.loadFailedTitle", "Failed to load Workflows"),
152
153
  description: "The workflow service did not respond. Nothing has been changed — retry when you are ready.",
153
- icon: /* @__PURE__ */ E(_, { className: "h-icon-md w-icon-md" }),
154
- actions: /* @__PURE__ */ E(x, {
154
+ icon: /* @__PURE__ */ E(y, { className: "h-icon-md w-icon-md" }),
155
+ actions: /* @__PURE__ */ E(C, {
155
156
  size: "sm",
156
- onClick: () => ae(),
157
+ onClick: () => oe(),
157
158
  children: k("bigconsole.workflows.actions.tryAgain", "Try Again")
158
159
  }),
159
160
  "data-testid": "workflows-error",
@@ -161,7 +162,7 @@ var k = d(function() {
161
162
  className: "break-words text-sm text-[var(--color-status-error-text)]",
162
163
  children: q.message
163
164
  })
164
- }) : /* @__PURE__ */ D(ie, { children: [/* @__PURE__ */ E(a, {
165
+ }) : /* @__PURE__ */ D(ae, { children: [/* @__PURE__ */ E(a, {
165
166
  headingLevel: 2,
166
167
  title: "Filters",
167
168
  description: "Narrow the list by status, or search by name, key or description.",
@@ -173,7 +174,7 @@ var k = d(function() {
173
174
  role: "group",
174
175
  "aria-label": "Filter workflows by status",
175
176
  className: "flex flex-wrap items-center gap-1 rounded-card bg-bg-sunken p-1",
176
- children: de.map((e) => /* @__PURE__ */ E("button", {
177
+ children: fe.map((e) => /* @__PURE__ */ E("button", {
177
178
  type: "button",
178
179
  "aria-pressed": I === e.value,
179
180
  onClick: () => L(e.value),
@@ -182,10 +183,10 @@ var k = d(function() {
182
183
  }, e.value))
183
184
  }), /* @__PURE__ */ D("div", {
184
185
  className: "relative w-full lg:max-w-[360px]",
185
- children: [/* @__PURE__ */ E(y, {
186
+ children: [/* @__PURE__ */ E(te, {
186
187
  "aria-hidden": "true",
187
188
  className: "pointer-events-none absolute left-3 top-1/2 h-icon-sm w-icon-sm -translate-y-1/2 text-text-muted"
188
- }), /* @__PURE__ */ E(w, {
189
+ }), /* @__PURE__ */ E(ne, {
189
190
  type: "text",
190
191
  "aria-label": k("bigconsole.workflows.filters.searchPlaceholder", "Search workflows..."),
191
192
  placeholder: k("bigconsole.workflows.filters.searchPlaceholder", "Search workflows..."),
@@ -210,7 +211,7 @@ var k = d(function() {
210
211
  className: "flex min-h-[240px] items-center justify-center",
211
212
  children: /* @__PURE__ */ D("div", {
212
213
  className: "text-center",
213
- children: [/* @__PURE__ */ E(re, {
214
+ children: [/* @__PURE__ */ E(ie, {
214
215
  size: "lg",
215
216
  className: "mb-form-gap"
216
217
  }), /* @__PURE__ */ E("p", {
@@ -219,20 +220,20 @@ var k = d(function() {
219
220
  })]
220
221
  })
221
222
  }),
222
- fe && /* @__PURE__ */ E(C, {
223
+ pe && /* @__PURE__ */ E(T, {
223
224
  illustration: "empty-data",
224
225
  title: k("bigconsole.workflows.list.emptyTitle", "No Workflows yet"),
225
226
  description: k("bigconsole.workflows.list.emptyDescription", "Design ETL workflows with the FluidGrids visual builder to fetch, transform, and deliver data into BigConsole DataSinks."),
226
- action: /* @__PURE__ */ E(h, {
227
- permission: o.WORKFLOW_CREATE,
227
+ action: /* @__PURE__ */ E(_, {
228
+ permission: s.WORKFLOW_CREATE,
228
229
  deniedBehavior: "disable",
229
- children: /* @__PURE__ */ D(x, {
230
+ children: /* @__PURE__ */ D(C, {
230
231
  onClick: Q,
231
- children: [/* @__PURE__ */ E(v, { className: "h-icon-sm w-icon-sm" }), k("bigconsole.workflows.actions.designWorkflow", "Design Workflow")]
232
+ children: [/* @__PURE__ */ E(b, { className: "h-icon-sm w-icon-sm" }), k("bigconsole.workflows.actions.designWorkflow", "Design Workflow")]
232
233
  })
233
234
  })
234
235
  }),
235
- pe && /* @__PURE__ */ E(C, {
236
+ me && /* @__PURE__ */ E(T, {
236
237
  illustration: "empty-search",
237
238
  title: k("bigconsole.workflows.list.noResultsTitle", "No matching workflows"),
238
239
  description: k("bigconsole.workflows.list.noResultsDescription", "No workflows match your filters")
@@ -261,23 +262,23 @@ var k = d(function() {
261
262
  }),
262
263
  Z.length > 0 && /* @__PURE__ */ E("div", {
263
264
  className: "grid grid-cols-1 gap-form-gap sm:grid-cols-2 xl:grid-cols-3",
264
- children: Z.map((e) => /* @__PURE__ */ E(u, {
265
+ children: Z.map((e) => /* @__PURE__ */ E(d, {
265
266
  workflow: e,
266
- onView: se,
267
- onEdit: N ? ce : void 0,
268
- onDelete: P ? le : void 0,
269
- onRun: F ? $ : void 0,
270
- isRunning: oe.get(e.id)?.isExecuting ?? !1
267
+ onView: ce,
268
+ onEdit: N ? le : void 0,
269
+ onDelete: P ? $ : void 0,
270
+ onRun: F ? ue : void 0,
271
+ isRunning: se.get(e.id)?.isExecuting ?? !1
271
272
  }, e.id))
272
273
  })
273
274
  ]
274
275
  })] })] })
275
276
  }),
276
- U && /* @__PURE__ */ E(l, {
277
+ U && /* @__PURE__ */ E(u, {
277
278
  workflow: U,
278
279
  isOpen: H,
279
280
  onClose: G,
280
- onConfirm: ue
281
+ onConfirm: de
281
282
  })
282
283
  ]
283
284
  });