@burdenoff/microfe-bigconsole 2026.1004.7 → 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.
- package/dist/bigconsole/BigConsoleRoutes.d.ts +7 -1
- package/dist/bigconsole/BigConsoleRoutes.d.ts.map +1 -1
- package/dist/bigconsole/BigConsoleRoutes.js +154 -144
- package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
- package/dist/bigconsole/components/datasink/AlertExecutionHistory.d.ts +11 -0
- package/dist/bigconsole/components/datasink/AlertExecutionHistory.d.ts.map +1 -0
- package/dist/bigconsole/components/datasink/AlertExecutionHistory.js +248 -0
- package/dist/bigconsole/components/datasink/AlertExecutionHistory.js.map +1 -0
- package/dist/bigconsole/components/datasink/AlertRuleEditor.d.ts +23 -0
- package/dist/bigconsole/components/datasink/AlertRuleEditor.d.ts.map +1 -0
- package/dist/bigconsole/components/datasink/AlertRuleEditor.js +357 -0
- package/dist/bigconsole/components/datasink/AlertRuleEditor.js.map +1 -0
- package/dist/bigconsole/components/workflow/WorkflowOpenGuard.d.ts +25 -0
- package/dist/bigconsole/components/workflow/WorkflowOpenGuard.d.ts.map +1 -0
- package/dist/bigconsole/components/workflow/WorkflowOpenGuard.js +89 -0
- package/dist/bigconsole/components/workflow/WorkflowOpenGuard.js.map +1 -0
- package/dist/bigconsole/hooks/useDataSinkAlerts.d.ts +146 -0
- package/dist/bigconsole/hooks/useDataSinkAlerts.d.ts.map +1 -0
- package/dist/bigconsole/hooks/useDataSinkAlerts.js +221 -0
- package/dist/bigconsole/hooks/useDataSinkAlerts.js.map +1 -0
- package/dist/bigconsole/pages/DataSinkAlertsPage.d.ts +2 -0
- package/dist/bigconsole/pages/DataSinkAlertsPage.d.ts.map +1 -0
- package/dist/bigconsole/pages/DataSinkAlertsPage.js +422 -0
- package/dist/bigconsole/pages/DataSinkAlertsPage.js.map +1 -0
- package/dist/bigconsole/pages/DataSinkViewPage.d.ts.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +151 -144
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowViewPage.js +4 -4
- package/dist/bigconsole/pages/WorkflowsPage.d.ts.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +61 -60
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/dist/bigconsole/utils/alertRules.d.ts +40 -0
- package/dist/bigconsole/utils/alertRules.d.ts.map +1 -0
- package/dist/bigconsole/utils/alertRules.js +83 -0
- package/dist/bigconsole/utils/alertRules.js.map +1 -0
- package/dist/bigconsole/workflowOpen.d.ts +78 -0
- package/dist/bigconsole/workflowOpen.d.ts.map +1 -0
- package/dist/bigconsole/workflowOpen.js +33 -0
- package/dist/bigconsole/workflowOpen.js.map +1 -0
- package/dist/generated/wspace-operations.d.ts +418 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/generated/wspace-operations.js +329 -200
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.d.ts +122 -0
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/generated/wspace-types.js +7 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/translations/en.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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} 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 <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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAM,IAAmB,wDAMZ,IAAmB,EAAK,WAA4B;CAC/D,IAAM,EAAE,kBAAe,IAAsD,EACvE,IAAW,GAAgB,EAC3B,EAAE,SAAM,IAAS,EAIjB,EAAE,mBAAe,WAAW,OAAuB,IAAgB,EACnE,KAAU,GAAc,EAAuB,cAAc,EAG7D,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAoB,KAAyB,EAAS,GAAM,EAC7D,CAAC,IAAoB,KAAyB,EAAS,GAAM,EAC7D,CAAC,GAAa,MAAkB,EAAyC,KAAK,EAC9E,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAc,KAAmB,EAA2B,QAAQ,EAErE,CAAC,GAAU,MAAe,EAAS,IAAI,EACvC,CAAC,GAAY,MAAiB,EAAS,EAAE,EACzC,CAAC,GAAgB,MAAqB,EAAwB,KAAK,EACnE,CAAC,IAAa,MAAkB,EAAS,GAAM,EAG/C,IAAW,GAAkB,MAAW,IAAa,EAAM,UAAU,IAAI,EAAW,GAAG,KAAA,EAAW,EAGlG,EAAE,aAAS,UAAO,kBAAe,oBAAiB,oBAAiB,mBAAgB,sBACvF,GAAsB,EACpB,WAAW,IACZ,CAAC;AASJ,CANA,GAAwB;EACtB;EACA,SAAS,CAAC,CAAC;EACZ,CAAC,EAGF,SAAgB;AACd,EAAI,KACF,EAAc,EAAW;IAE1B,CAAC,GAAY,EAAc,CAAC;CAG/B,IAAM,IAAa,QAAkB;AACnC,IAAS,aAAa;IACrB,CAAC,EAAS,CAAC,EAGR,KAAa,QAAkB;AACnC,IAAS,cAAc,EAAW,OAAO;IACxC,CAAC,GAAU,EAAW,CAAC,EAGpB,IAAgB,EAAY,YAAY;AACvC,SACL;KAAgB,GAAK;AACrB,OAAI;AACF,UAAM,EAAgB,EAAE,IAAI,GAAY,CAAC;WAC7B,WACJ;AACR,MAAgB,GAAM;;;IAEvB,CAAC,GAAY,EAAgB,CAAC,EAG3B,KAAe,EAAY,YAAY;AAC3C,MAAI,CAAC,EAAY;EACjB,IAAM,IAAS,MAAM,GAAc;GACjC,OAAO,EAAE,gDAAgD,mBAAmB;GAC5E,SAAS,EAAE,kDAAkD,iDAAiD;GAC9G,eAAe,EAAE,oCAAoC,SAAS;GAC9D,mBAAmB,EAAE,oCAAoC,SAAS;GACnE,CAAC;AAEA,YAAW,OACP,OAAO,QACL,EAAE,kDAAkD,iDAAiD,CACtG,GACD,GAGN;GADA,GAAa,SAAS,EACtB,EAAc,GAAK;AACnB,OAAI;AAGF,IAFA,MAAM,EAAe,EAAW,EAC5B,GAAU,IAAO,EAAa,UAAU,EAC5C,EAAS,aAAa;WACV;AACZ,IAAI,GAAU,IAAO,EAAa,QAAQ;aAClC;AACR,MAAc,GAAM;;;IAErB;EAAC;EAAY;EAAgB;EAAU;EAAE,CAAC,EAGvC,IAAe,EACnB,OAAO,GAAgB,MAAiB;AACjC,SACL;KAAe,GAAK;AACpB,OAAI;IACF,IAAM,IAAS,MAAM,EAAgB,EAAE,IAAI,GAAY,EAAE;KAAE,OAAO;KAAM;KAAQ,CAAC;AACjF,IAAI,MACF,GAAgB,EAAO,OAAmC,EAAE,CAAC,EAE7D,GAAkB,OAAO,EAAO,cAAe,WAAW,EAAO,aAAa,KAAK,EACnF,GAAe,EAAQ,EAAO,QAAS,EACvC,GAAc,EAAO;WAEjB,WAEE;AACR,MAAe,GAAM;;;IAGzB,CAAC,GAAY,EAAgB,CAC9B,EAGK,IAAiB,EAAY,YAAY;AACxC,SACL;OAAI,GAAU;AACZ,MAAY,GAAM;AAClB;;AAGF,GADA,MAAM,EAAa,GAAG,EAAS,EAC/B,EAAY,GAAK;;IAChB;EAAC;EAAY;EAAU;EAAc;EAAS,CAAC,EAE5C,KAAuB,GAC1B,MAAiB;AAEX,EADL,GAAY,EAAK,EACZ,EAAa,GAAG,EAAK;IAE5B,CAAC,EAAa,CACf,EAGK,IAAe,QAAkB;AACrC,IAAsB,GAAK;IAC1B,EAAE,CAAC,EAEA,KAAoB,QAAkB;AAC1C,IAAsB,GAAM;IAC3B,EAAE,CAAC,EAGA,IAAe,QAAkB;AACrC,IAAsB,GAAK;IAC1B,EAAE,CAAC,EAEA,KAAoB,QAAkB;AAC1C,IAAsB,GAAM;IAC3B,EAAE,CAAC,EAEA,KAAsB,QAAkB;AAK5C,EAJA,EAAsB,GAAM,EAI5B,EAAS,GAAiB,CAAC;IAC1B,CAAC,EAAS,CAAC,EAGR,KAAkB,EACtB,OAAO,MAAsD;AAW3D,MAVI,CAAC,KAUD,CALY,MAAM,EAAe;GACnC,IAAI,EAAS;GACb,SAAS,EAAS;GAClB,MAAM;GACP,CAAC,CACY,QAAO;AAIrB,MAAI;AAEF,GADA,MAAM,EAAa,GAAG,EAAS,EAC/B,EAAY,GAAK;UACX;AAGR,SAAO;IAET;EAAC;EAAU;EAAgB;EAAc;EAAS,CACnD;AAsED,QAnEI,MAAW,CAAC,IAEZ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,wDAAyD,CAAA;EACxE,CAAA,GAQN,KAAS,CAAC,IAEV,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,MAAK;GACL,cAAc;GACd,OAAO,EAAE,uCAAuC,0BAA0B;GAC1E,aAAa,EAAM;GACnB,MAAM,kBAAC,IAAD,EAAa,WAAU,uBAAwB,CAAA;GACrD,SACE,kBAAC,GAAD;IAAQ,SAAS;IAAY,SAAQ;IAAY,MAAK;cAAtD,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EACrC,EAAE,oCAAoC,UAAU,CAC1C;;GAEX,CAAA;EACE,CAAA,GAKN,CAAC,MAAsB,CAAC,KAExB,kBAAC,IAAD;EACE,OAAO,EAAE,+CAA+C,gBAAgB;EACxE,aAAa,EACX,qDACA,oDACD;EACD,CAAA,GAKD,IAsBH,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAQ,SAAS;OAAY,SAAQ;OAAQ,MAAK;OAAK,WAAU;iBAAjE,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EACrC,EAAE,6CAA6C,oBAAoB,CAC7D;;MACT,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAS,QAAQ,EAAE,sCAAsC,WAAW;SAClE,CAAA;QACL,kBAAC,GAAD,EAAqB,QAAQ,EAAS,QAAU,CAAA;QAChD,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAAiG,KAC7F,EAAS,QACN;;QACH;;MACN,kBAAC,KAAD;OAAG,WAAU;iBAAoD,EAAS;OAAQ,CAAA;MACjF,EAAS,eAAe,kBAAC,KAAD;OAAG,WAAU;iBAAoC,EAAS;OAAgB,CAAA;MAC/F;QAIN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAkB,YAAY,EAAuB;OAAkB,gBAAe;iBACpF,kBAAC,GAAD;QAAQ,SAAS;QAAe,UAAU;QAAc,SAAQ;QAAU,MAAK;kBAA/E,CACE,kBAAC,IAAD,EAAW,WAAW,gBAAgB,IAAe,iBAAiB,MAAQ,CAAA,EAC7E,IACG,EAAE,wCAAwC,gBAAgB,GAC1D,EAAE,qCAAqC,UAAU,CAC9C;;OACQ,CAAA;MACnB,kBAAC,GAAD;OAAQ,SAAS;OAAc,SAAQ;OAAY,MAAK;iBAAxD,CACE,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAClC,EAAE,wCAAwC,cAAc,CAClD;;MACT,kBAAC,GAAD;OAAQ,SAAS;OAAc,SAAQ;OAAY,MAAK;iBAAxD,CACE,kBAAC,IAAD,EAAU,WAAU,gBAAiB,CAAA,EACpC,EAAE,oCAAoC,SAAS,CACzC;;MACT,kBAAC,GAAD;OAAkB,YAAY,EAAuB;OAAiB,gBAAe;iBACnF,kBAAC,GAAD;QAAQ,SAAS;QAAY,SAAQ;QAAY,MAAK;kBAAtD,CACE,kBAAC,IAAD,EAAM,WAAU,gBAAiB,CAAA,EAChC,EAAE,kCAAkC,OAAO,CACrC;;OACQ,CAAA;MACnB,kBAAC,GAAD;OAAkB,YAAY,EAAuB;OAAiB,gBAAe;iBACnF,kBAAC,GAAD;QAAQ,SAAS;QAAc,UAAU;QAAY,SAAQ;QAAc,MAAK;kBAAhF,CACE,kBAAC,IAAD,EAAQ,WAAU,gBAAiB,CAAA,EAClC,IACG,EAAE,sCAAsC,cAAc,GACtD,EAAE,oCAAoC,SAAS,CAC5C;;OACQ,CAAA;MACf;OACC;;GAET,kBAAC,IAAD;IAAa,WAAU;cACpB,EACC,yCACA,4NACD;IACW,CAAA;GAEd,kBAAC,IAAD;IAAc,WAAU;cAAxB,CAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CAKE,kBAAC,GAAD;MACE,cAAc;MACd,OAAO,EAAE,qCAAqC,UAAU;MACxD,aAAY;gBAEZ,kBAAC,MAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAA+B,EAAE,oCAAoC,SAAS;UAAM,CAAA,EAClG,kBAAC,MAAD,EAAA,UACE,kBAAC,GAAD;UAAqB,QAAQ,EAAS;UAAQ,MAAK;UAAO,CAAA,EACvD,CAAA,CACD;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAA+B,EAAE,sCAAsC,YAAY;UAAM,CAAA,EACvG,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAS,MAAM,GAAG,EAAS,IAAI,KAAK,EAAE,kCAAkC,OAAO;UAC7E,CAAA,CACD;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAE,yCAAyC,eAAe;UACxD,CAAA,EACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAS,SAAS,QAAQ,EAAE,oCAAoC,SAAS;UACvE,CAAA,CACD;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAA+B,EAAE,kCAAkC,OAAO;UAAM,CAAA,EAC9F,kBAAC,MAAD;UAAI,WAAU;oBAAiC,GAAe,EAAS,aAAa,EAAE;UAAM,CAAA,CACxF;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAA+B,EAAE,qCAAqC,UAAU;UAAM,CAAA,EACpG,kBAAC,MAAD;UAAI,WAAU;oBAAiC,EAAS,WAAW;UAAO,CAAA,CACtE;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAE,2CAA2C,iBAAiB;UAC5D,CAAA,EACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAS,kBACN,EAAoB,IAAI,KAAK,EAAS,gBAAgB,EAAE,EAAE,WAAW,IAAM,CAAC,GAC5E,EAAE,mCAAmC,QAAQ;UAC9C,CAAA,CACD;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAA+B,EAAE,qCAAqC,UAAU;UAAM,CAAA,EACpG,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAoB,IAAI,KAAK,EAAS,UAAU,EAAE,EAAE,WAAW,IAAM,CAAC;UACpE,CAAA,CACD;;QACH;;MACG,CAAA,EAGV,kBAAC,GAAD;MACE,UAAS;MACT,cAAc;MACd,OAAO,EAAE,yCAAyC,eAAe;MACjE,aAAY;MACZ,MAAM,kBAAC,GAAD,EAAU,WAAU,uBAAwB,CAAA;MAClD,SACE,kBAAA,GAAA,EAAA,UAAA,CACG,KACC,kBAAC,OAAD;OACE,MAAK;OACL,cAAY,EAAE,yCAAyC,eAAe;OACtE,WAAU;iBAHZ,CAKE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAgB,QAAQ;QACvC,gBAAc,MAAiB;QAC/B,WAAW,2NAAwG,EAAiB,GAClI,MAAiB,UACb,mCACA;QAEN,OAAO,EAAE,uCAAuC,aAAa;kBAE7D,kBAAC,IAAD,EAAS,WAAU,WAAY,CAAA;QACxB,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAgB,OAAO;QACtC,gBAAc,MAAiB;QAC/B,WAAW,2NAAwG,EAAiB,GAClI,MAAiB,SACb,mCACA;QAEN,OAAO,EAAE,sCAAsC,YAAY;kBAE3D,kBAAC,IAAD,EAAM,WAAU,WAAY,CAAA;QACrB,CAAA,CACL;UAER,kBAAC,GAAD;OACE,SAAS;OACT,UAAU;OACV,SAAS,IAAW,cAAc;OAClC,MAAK;iBAEJ,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EAChD,EAAE,qCAAqC,aAAa,CACpD,EAAA,CAAA,GACD,IACF,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,IAAD,EAAQ,WAAU,gBAAiB,CAAA,EAClC,EAAE,sCAAsC,YAAY,CACpD,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,IAAD,EAAK,WAAU,gBAAiB,CAAA,EAC/B,EAAE,sCAAsC,YAAY,CACpD,EAAA,CAAA;OAEE,CAAA,CACR,EAAA,CAAA;gBAjEP,CAoEG,KAAY,KACX,kBAAA,GAAA,EAAA,UAAA,CAEI,EADD,MAAiB,UACf,KAEA,IAFD;OAAqB,MAAM;OAAa,WAAU;OAAU,CAED,EAG5D,MAAmB,QAClB,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACE,kBAAC,QAAD,EAAA,UAAO,EAAE,yCAAyC,gBAAgB,EAAQ,CAAA,EAC1E,kBAAC,UAAD;SACE,cAAY,EAAE,yCAAyC,gBAAgB;SACvE,WAAW;SACX,OAAO;SACP,WAAW,MAAM,GAAqB,OAAO,EAAE,OAAO,MAAM,CAAC;mBAE5D;UAAC;UAAK;UAAK;UAAK,CAAC,KAAK,MACrB,kBAAC,UAAD;UAAgB,OAAO;oBACpB;UACM,EAFI,EAEJ,CACT;SACK,CAAA,CACH;WACR,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,QAAD,EAAA,UACG,MAAmB,IAChB,EAAE,sCAAsC,SAAS,GACjD,EAAE,sCAAsC,kCAAkC;UACxE,OAAO,IAAa;UACpB,KAAK,KAAK,IAAI,IAAa,GAAU,EAAe;UACpD,OAAO;UACR,CAAC,EACD,CAAA;SACP,kBAAC,GAAD;UACE,SAAQ;UACR,MAAK;UACL,UAAU,MAAe,KAAK;UAC9B,eAAe,KAAK,EAAa,KAAK,IAAI,GAAG,IAAa,EAAS,EAAE,EAAS;oBAE7E,EAAE,sCAAsC,WAAW;UAC7C,CAAA;SACT,kBAAC,GAAD;UACE,SAAQ;UACR,MAAK;UACL,UAAU,CAAC,MAAe;UAC1B,eAAe,KAAK,EAAa,IAAa,GAAU,EAAS;oBAEhE,EAAE,kCAAkC,OAAO;UACrC,CAAA;SACL;UACF;SAEP,EAAA,CAAA,EAGJ,CAAC,MACC,EAAS,YAAY,KAAM,EAAS,QAAQ,OAAO,KAAK,EAAS,KAAK,CAAC,SAAS,IAC/E,kBAAC,KAAD;OAAG,WAAU;iBAAb,CACE,kBAAC,GAAD;QAAU,eAAY;QAAO,WAAU;QAAiD,CAAA,EACvF,EAAE,0CAA0C,kDAAgD,CAC3F;WAGJ,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SAAU,eAAY;SAAO,WAAU;SAAwC,CAAA;QAC/E,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,uCACA,mFACD;SACC,CAAA;QACJ,kBAAC,GAAD;SAAQ,SAAS;SAAc,SAAQ;SAAU,MAAK;mBAAtD,CACE,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAClC,EAAE,wCAAwC,cAAc,CAClD;;QACL;UAEF;QACN;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,EAAS,wBACR,kBAAC,IAAD;OACE,YAAY,EAAS;OACrB,aAAa,EAAS,iCAAiC,KAAA;OACvD,iBAAiB,EAAS,mBAAmB,KAAA;OAC7C,YAAY,EAAS;OACrB,QAAQ,EAAS;OACjB,iBAAiB;OACjB,UAAU;OACV,aAAa;OACb,CAAA;MAIJ,kBAAC,IAAD,EAAsB,eAAe,EAAS,eAAiB,CAAA;MAG/D,kBAAC,IAAD,EAAoB,aAAa,EAAS,KAAO,CAAA;MAC7C;OACO;;GACf,kBAAC,IAAD;IACE,YAAW;IACX,OAAO;KACL;MACE,IAAI;MACJ,OAAO,EAAE,oDAAoD,mBAAmB;MAChF,aAAa,EACX,0DACA,kEACD;MACD,SAAS;MACV;KACD;MACE,IAAI;MACJ,OAAO,EAAE,oDAAoD,sBAAsB;MACnF,aAAa,EACX,0DACA,sDACD;MACD,SAAS;MACV;KACD;MACE,IAAI;MACJ,OAAO,EAAE,sDAAsD,8BAA8B;MAC7F,aAAa,EACX,4DACA,0DACD;MACD,eAAe,EAAS,cAAc;MACvC;KACD;MACE,IAAI;MACJ,OAAO,EAAE,mDAAmD,kBAAkB;MAC9E,aAAa,EACX,yDACA,2CACD;MACD,SAAS;MACV;KACF;IACD,CAAA;GAGF,kBAAC,IAAD;IACE,QAAQ;IACR,YAAY,KAAc;IAC1B,cAAc,EAAS,QAAQ,EAAS;IACxC,SAAS;IACT,CAAA;GAGF,kBAAC,IAAD;IACE,QAAQ;IACR,cAAc,EAAS,QAAQ,EAAS;IACxC,iBAAiB,EAAS,YAAY,KAAK,GAAQ,EAAS,QAAQ,OAAO,KAAK,EAAS,KAAK,CAAC,SAAS;IACxG,SAAS;IACT,iBAAiB;IACjB,aAAa;IACb,CAAA;GACE;MAvXJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,IAAD;GACE,cAAa;GACb,OAAO,EAAE,2CAA2C,qBAAqB;GACzE,aAAa,EACX,iDACA,qEACD;GACD,QACE,kBAAC,GAAD;IAAQ,SAAS;IAAY,SAAQ;IAAU,MAAK;cAApD,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EACrC,EAAE,+CAA+C,uBAAuB,CAClE;;GAEX,CAAA;EACE,CAAA;EA0WV"}
|
|
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 {
|
|
21
|
-
import {
|
|
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 } =
|
|
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 } =
|
|
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;
|
|
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 {
|
|
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
|
|
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
|
|
10
|
-
import { isNative as
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import { memo as
|
|
14
|
-
import { AccessDenied as
|
|
15
|
-
import { usePermissions as
|
|
16
|
-
import { AlertCircle as
|
|
17
|
-
import { AuroraBackground as
|
|
18
|
-
import { Fragment as
|
|
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 =
|
|
22
|
-
let
|
|
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 =
|
|
34
|
-
|
|
35
|
-
}, [
|
|
36
|
-
|
|
37
|
-
}, [
|
|
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 &&
|
|
40
|
-
}, [
|
|
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]),
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
]),
|
|
65
|
+
]), de = p(async () => {
|
|
65
66
|
if (U) {
|
|
66
67
|
try {
|
|
67
|
-
await Y(U.id),
|
|
68
|
+
await Y(U.id), c() && l("success");
|
|
68
69
|
} catch (e) {
|
|
69
|
-
throw
|
|
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
|
-
]),
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
136
|
-
permission:
|
|
136
|
+
children: /* @__PURE__ */ E(_, {
|
|
137
|
+
permission: s.WORKFLOW_CREATE,
|
|
137
138
|
deniedBehavior: "disable",
|
|
138
|
-
children: /* @__PURE__ */ D(
|
|
139
|
+
children: /* @__PURE__ */ D(C, {
|
|
139
140
|
onClick: Q,
|
|
140
|
-
children: [/* @__PURE__ */ E(
|
|
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(
|
|
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(
|
|
154
|
-
actions: /* @__PURE__ */ E(
|
|
154
|
+
icon: /* @__PURE__ */ E(y, { className: "h-icon-md w-icon-md" }),
|
|
155
|
+
actions: /* @__PURE__ */ E(C, {
|
|
155
156
|
size: "sm",
|
|
156
|
-
onClick: () =>
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
227
|
-
permission:
|
|
227
|
+
action: /* @__PURE__ */ E(_, {
|
|
228
|
+
permission: s.WORKFLOW_CREATE,
|
|
228
229
|
deniedBehavior: "disable",
|
|
229
|
-
children: /* @__PURE__ */ D(
|
|
230
|
+
children: /* @__PURE__ */ D(C, {
|
|
230
231
|
onClick: Q,
|
|
231
|
-
children: [/* @__PURE__ */ E(
|
|
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
|
-
|
|
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(
|
|
265
|
+
children: Z.map((e) => /* @__PURE__ */ E(d, {
|
|
265
266
|
workflow: e,
|
|
266
|
-
onView:
|
|
267
|
-
onEdit: N ?
|
|
268
|
-
onDelete: P ?
|
|
269
|
-
onRun: F ?
|
|
270
|
-
isRunning:
|
|
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(
|
|
277
|
+
U && /* @__PURE__ */ E(u, {
|
|
277
278
|
workflow: U,
|
|
278
279
|
isOpen: H,
|
|
279
280
|
onClose: G,
|
|
280
|
-
onConfirm:
|
|
281
|
+
onConfirm: de
|
|
281
282
|
})
|
|
282
283
|
]
|
|
283
284
|
});
|