@burdenoff/microfe-bigconsole 2026.924.1 → 2026.924.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/bigconsole/BigConsoleRoutes.js +137 -138
  2. package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
  3. package/dist/bigconsole/assistant/previewController.js +10 -10
  4. package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js +25 -0
  5. package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js.map +1 -0
  6. package/dist/bigconsole/components/DrilldownDashboardRenderer.js +17 -17
  7. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +32 -32
  8. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +52 -52
  9. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
  10. package/dist/bigconsole/components/dashboard/DashboardCanvas.js +7 -7
  11. package/dist/bigconsole/components/dashboard/DashboardStatusPill.js +53 -0
  12. package/dist/bigconsole/components/dashboard/DashboardStatusPill.js.map +1 -0
  13. package/dist/bigconsole/components/dashboard/DashboardToolbar.js +60 -60
  14. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +21 -21
  15. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +30 -30
  16. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +36 -36
  17. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +34 -34
  18. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +38 -38
  19. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
  20. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +121 -121
  21. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
  22. package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +22 -22
  23. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +46 -46
  24. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +53 -53
  25. package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -1
  26. package/dist/bigconsole/components/dashboard/WidgetPalette.js +47 -47
  27. package/dist/bigconsole/components/dashboard/index.js +1 -0
  28. package/dist/bigconsole/components/datasink/CacheTTLSelector.js +35 -35
  29. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +18 -18
  30. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +20 -20
  31. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +30 -29
  32. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
  33. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +48 -48
  34. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +103 -103
  35. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
  36. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +35 -35
  37. package/dist/bigconsole/components/overview/DashboardGuide.js +214 -0
  38. package/dist/bigconsole/components/overview/DashboardGuide.js.map +1 -0
  39. package/dist/bigconsole/components/overview/DataHealthCard.js +97 -0
  40. package/dist/bigconsole/components/overview/DataHealthCard.js.map +1 -0
  41. package/dist/bigconsole/components/overview/OverviewQuickActions.js +32 -0
  42. package/dist/bigconsole/components/overview/OverviewQuickActions.js.map +1 -0
  43. package/dist/bigconsole/components/overview/OverviewStatCard.js +57 -0
  44. package/dist/bigconsole/components/overview/OverviewStatCard.js.map +1 -0
  45. package/dist/bigconsole/components/overview/RecentDashboardsPanel.js +107 -0
  46. package/dist/bigconsole/components/overview/RecentDashboardsPanel.js.map +1 -0
  47. package/dist/bigconsole/components/overview/dashboardGuideSteps.js +119 -0
  48. package/dist/bigconsole/components/overview/dashboardGuideSteps.js.map +1 -0
  49. package/dist/bigconsole/components/overview/index.js +8 -0
  50. package/dist/bigconsole/components/overview/overviewModel.js +119 -0
  51. package/dist/bigconsole/components/overview/overviewModel.js.map +1 -0
  52. package/dist/bigconsole/components/overview/useOverviewData.js +48 -0
  53. package/dist/bigconsole/components/overview/useOverviewData.js.map +1 -0
  54. package/dist/bigconsole/components/parser/DataSinkSelector.js +40 -40
  55. package/dist/bigconsole/components/parser/ImportParserDialog.js +127 -127
  56. package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
  57. package/dist/bigconsole/components/parser/ParserSelector.js +51 -51
  58. package/dist/bigconsole/components/preview/AiPreviewContent.js +5 -5
  59. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +62 -61
  60. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
  61. package/dist/bigconsole/components/preview/AiPreviewPanel.js +82 -81
  62. package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
  63. package/dist/bigconsole/components/widgets/ActionConfigDialog.js +13 -13
  64. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +70 -70
  65. package/dist/bigconsole/components/widgets/PropertiesPanel.js +28 -28
  66. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +25 -25
  67. package/dist/bigconsole/components/widgets/chart/ChartConfig.js +48 -48
  68. package/dist/bigconsole/components/widgets/comments/WidgetComments.js +46 -46
  69. package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +61 -61
  70. package/dist/bigconsole/components/widgets/form/FormWidget.js +36 -36
  71. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +42 -42
  72. package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +25 -25
  73. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +21 -21
  74. package/dist/bigconsole/components/widgets/list/ListConfig.js +30 -30
  75. package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +38 -38
  76. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +9 -9
  77. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +9 -9
  78. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +49 -49
  79. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +39 -39
  80. package/dist/bigconsole/components/widgets/table/TableConfig.js +73 -73
  81. package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +64 -64
  82. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +10 -10
  83. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +68 -68
  84. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
  85. package/dist/bigconsole/pages/AdminPage.js +136 -136
  86. package/dist/bigconsole/pages/DashboardBuilderPage.js +10 -10
  87. package/dist/bigconsole/pages/DashboardViewPage.js +61 -61
  88. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  89. package/dist/bigconsole/pages/DashboardsPage.js +327 -348
  90. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  91. package/dist/bigconsole/pages/DataParsersPage.js +5 -5
  92. package/dist/bigconsole/pages/DataSinkBuilderPage.js +10 -10
  93. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  94. package/dist/bigconsole/pages/DataSinkViewPage.js +83 -83
  95. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  96. package/dist/bigconsole/pages/DataSinksPage.js +11 -11
  97. package/dist/bigconsole/pages/DataSourcesPage.js +5 -5
  98. package/dist/bigconsole/pages/InsightsPage.js +315 -315
  99. package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
  100. package/dist/bigconsole/pages/OverviewPage.js +244 -0
  101. package/dist/bigconsole/pages/OverviewPage.js.map +1 -0
  102. package/dist/bigconsole/pages/ParserBuilderPage.js +5 -5
  103. package/dist/bigconsole/pages/ParserViewPage.js +98 -98
  104. package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
  105. package/dist/bigconsole/pages/PipelineBuilderPage.js +8 -8
  106. package/dist/bigconsole/pages/PipelineViewPage.js +46 -46
  107. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  108. package/dist/bigconsole/pages/PipelinesPage.js +80 -80
  109. package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
  110. package/dist/bigconsole/pages/SettingsPage.js +70 -70
  111. package/dist/bigconsole/pages/WidgetDemoPage.js +42 -42
  112. package/dist/bigconsole/pages/WorkflowViewPage.js +200 -200
  113. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  114. package/dist/bigconsole/pages/WorkflowsPage.js +78 -78
  115. package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
  116. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +34 -34
  117. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +79 -79
  118. package/dist/bigconsole/renderers/adapters/CustomRenderer.js +46 -46
  119. package/dist/bigconsole/utils/createDialogParam.js +14 -0
  120. package/dist/bigconsole/utils/createDialogParam.js.map +1 -0
  121. package/dist/generated/global-operations.js +1 -1
  122. package/dist/generated/wspace-operations.js +285 -222
  123. package/dist/generated/wspace-operations.js.map +1 -1
  124. package/dist/shared/hooks/useTr.js +10 -7
  125. package/dist/shared/hooks/useTr.js.map +1 -1
  126. package/package.json +1 -1
  127. package/dist/bigconsole/pages/HomePage.js +0 -318
  128. package/dist/bigconsole/pages/HomePage.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DataSinkBuilderPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinkBuilderPage.tsx"],"sourcesContent":["/**\n * DataSink Builder Page\n *\n * Simplified page for creating and editing DataSinks.\n * DataSinks are now standalone - workflows push data to them via unique keys.\n * Required fields: name, key\n */\n\nimport { memo, useState, useMemo, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeToast } from '../../utils/nativeBridge';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { CacheTTLSelector } from '../components/datasink/CacheTTLSelector';\nimport { RefreshTypeSelector } from '../components/datasink/RefreshTypeSelector';\nimport type { CreateDataSinkInput, UpdateDataSinkInput, DataSinkRefreshType } from '../types';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Section-emphasis primitives (BOFF-5632)\nimport { Section, SectionStack } from '../components/common';\n\n// Lucide icons\nimport { ArrowLeft, Save, Loader2, AlertCircle, Database, Settings, Eye, Copy, Check, ListChecks } from 'lucide-react';\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\ntype TranslateFn = (key: string, fallback: string, params?: Record<string, string | number>) => string;\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface RefreshConfig {\n type: 'manual' | 'scheduled';\n schedule?: {\n cron: string;\n timezone: string;\n };\n}\n\ninterface FormState {\n name: string;\n key: string;\n description: string;\n ttl: number;\n refresh: RefreshConfig;\n data: string; // JSON string for data payload\n}\n\ninterface FormErrors {\n name?: string;\n key?: string;\n data?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Generate a key from name (slug format)\n */\nfunction generateKey(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .substring(0, 64);\n}\n\n/**\n * Validate form data\n */\nfunction validateForm(form: FormState, t: TranslateFn): FormErrors {\n const errors: FormErrors = {};\n\n if (!form.name.trim()) {\n errors.name = t('bigconsole.dataSinks.builder.validation.nameRequired', 'Name is required');\n } else if (form.name.length < 2) {\n errors.name = t('bigconsole.dataSinks.builder.validation.nameMinLength', 'Name must be at least 2 characters');\n } else if (form.name.length > 100) {\n errors.name = t('bigconsole.dataSinks.builder.validation.nameMaxLength', 'Name must be less than 100 characters');\n }\n\n if (!form.key.trim()) {\n errors.key = t('bigconsole.dataSinks.builder.validation.keyRequired', 'Key is required');\n } else if (!/^[a-z0-9_]+$/.test(form.key)) {\n errors.key = t(\n 'bigconsole.dataSinks.builder.validation.keyFormat',\n 'Key can only contain lowercase letters, numbers, and underscores'\n );\n } else if (form.key.length > 64) {\n errors.key = t('bigconsole.dataSinks.builder.validation.keyMaxLength', 'Key must be less than 64 characters');\n }\n\n // Validate JSON data if provided\n if (form.data && form.data.trim()) {\n try {\n JSON.parse(form.data);\n } catch {\n errors.data = t('bigconsole.dataSinks.builder.validation.invalidJson', 'Invalid JSON format');\n }\n }\n\n return errors;\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkBuilderPage = memo(function DataSinkBuilderPage() {\n const { dataSinkId } = useParams<{ dataSinkId?: string }>();\n const navigate = useAppNavigate();\n const { t } = useI18n();\n\n const isEdit = dataSinkId !== 'new' && !!dataSinkId;\n\n // RBAC — Save requires UPDATE when editing an existing sink, CREATE when\n // creating a new one. In edit mode the page also requires READ to be viewed.\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.DATASINK_READ);\n const savePermission = isEdit ? BIGCONSOLE_PERMISSIONS.DATASINK_UPDATE : BIGCONSOLE_PERMISSIONS.DATASINK_CREATE;\n\n // Form state\n const [form, setForm] = useState<FormState>({\n name: '',\n key: '',\n description: '',\n ttl: 300, // 5 minutes default\n refresh: {\n type: 'manual',\n },\n data: '{}', // Default empty JSON object\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [keyManuallyEdited, setKeyManuallyEdited] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [keyCopied, setKeyCopied] = useState(false);\n\n // Data preview state\n const [isLoadingPreview, setIsLoadingPreview] = useState(false);\n const [previewData, setPreviewData] = useState<unknown[] | null>(null);\n const [previewError, setPreviewError] = useState<string | null>(null);\n\n // Operations\n const { loading, error, dataSinks, fetchDataSink, createDataSink, updateDataSink, getDataSinkData } =\n useDataSinkOperations({\n skipFetch: true,\n });\n\n // Get existing data sink if editing\n const existingDataSink = isEdit ? dataSinks.find((ds) => ds.id === dataSinkId) : null;\n\n // Load existing data sink for editing\n useEffect(() => {\n if (isEdit && dataSinkId && !existingDataSink) {\n fetchDataSink(dataSinkId);\n }\n }, [isEdit, dataSinkId, existingDataSink, fetchDataSink]);\n\n // Track if form has been initialized\n const [formInitialized, setFormInitialized] = useState(false);\n\n // Populate form with existing data when editing (only once)\n useEffect(() => {\n if (existingDataSink && !formInitialized) {\n // Format existing data as pretty JSON string\n let dataString = '';\n if (existingDataSink.data) {\n try {\n // Check if data is already a string (JSON string from API)\n if (typeof existingDataSink.data === 'string') {\n // Parse and re-stringify to ensure proper formatting\n const parsed = JSON.parse(existingDataSink.data);\n dataString = JSON.stringify(parsed, null, 2);\n } else {\n // It's an object, just stringify it\n dataString = JSON.stringify(existingDataSink.data, null, 2);\n }\n } catch {\n // If parsing fails, use as-is if string, otherwise empty\n dataString = typeof existingDataSink.data === 'string' ? existingDataSink.data : '';\n }\n }\n\n setForm({\n name: existingDataSink.name || '',\n key: existingDataSink.key || '',\n description: existingDataSink.description || '',\n ttl: existingDataSink.ttl || 300,\n refresh: existingDataSink.refresh\n ? {\n type: existingDataSink.refresh.type === 'scheduled' ? 'scheduled' : 'manual',\n schedule: existingDataSink.refresh.schedule\n ? {\n cron: existingDataSink.refresh.schedule.cron,\n timezone: existingDataSink.refresh.schedule.timezone,\n }\n : undefined,\n }\n : { type: 'manual' },\n data: dataString,\n });\n setKeyManuallyEdited(true); // Don't auto-generate key when editing\n setFormInitialized(true); // Mark as initialized\n setErrors({}); // Clear any errors\n }\n }, [existingDataSink, formInitialized]);\n\n // Auto-generate key from name\n const handleNameChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n key: keyManuallyEdited ? prev.key : generateKey(name),\n }));\n setErrors((prev) => ({ ...prev, name: undefined }));\n },\n [keyManuallyEdited]\n );\n\n // Handle key change\n const handleKeyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const key = e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '');\n setForm((prev) => ({ ...prev, key }));\n setKeyManuallyEdited(true);\n setErrors((prev) => ({ ...prev, key: undefined }));\n }, []);\n\n // Handle description change\n const handleDescriptionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, description: e.target.value }));\n }, []);\n\n // Handle TTL change\n const handleTTLChange = useCallback((ttl: number | null) => {\n setForm((prev) => ({ ...prev, ttl: ttl || 0 }));\n }, []);\n\n // Handle refresh config change\n const handleRefreshChange = useCallback((config: RefreshConfig) => {\n setForm((prev) => ({ ...prev, refresh: config }));\n }, []);\n\n // Handle data change\n const handleDataChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, data: e.target.value }));\n setErrors((prev) => ({ ...prev, data: undefined }));\n }, []);\n\n // Format JSON data\n const handleFormatData = useCallback(() => {\n try {\n const parsed = JSON.parse(form.data);\n setForm((prev) => ({ ...prev, data: JSON.stringify(parsed, null, 2) }));\n setErrors((prev) => ({ ...prev, data: undefined }));\n } catch {\n setErrors((prev) => ({\n ...prev,\n data: t('bigconsole.dataSinks.builder.validation.invalidJson', 'Invalid JSON format'),\n }));\n }\n }, [form.data, t]);\n\n // Copy key to clipboard\n const handleCopyKey = useCallback(async () => {\n if (form.key) {\n await nativeCopyText(form.key);\n setKeyCopied(true);\n if (isNative()) void nativeToast(t('bigconsole.dataSinks.builder.toolbar.keyCopied', 'Key copied'));\n setTimeout(() => setKeyCopied(false), 2000);\n }\n }, [form.key, t]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n navigate('/datasinks');\n }, [navigate]);\n\n // Load data preview\n const handleShowData = useCallback(async () => {\n if (!isEdit || !existingDataSink) return;\n\n setIsLoadingPreview(true);\n setPreviewError(null);\n\n try {\n const result = await getDataSinkData({ key: existingDataSink.key });\n if (result?.raw) {\n // Get first 20 items if it's an array, otherwise wrap in array\n const data = Array.isArray(result.raw) ? result.raw.slice(0, 20) : [result.raw];\n setPreviewData(data);\n } else {\n setPreviewData([]);\n }\n } catch (err) {\n setPreviewError(\n err instanceof Error\n ? err.message\n : t('bigconsole.dataSinks.builder.errors.loadDataFailed', 'Failed to load data')\n );\n } finally {\n setIsLoadingPreview(false);\n }\n }, [isEdit, existingDataSink, getDataSinkData, t]);\n\n // Handle form submission\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError(null);\n\n // Validate form\n const validationErrors = validateForm(form, t);\n if (Object.keys(validationErrors).length > 0) {\n setErrors(validationErrors);\n return;\n }\n\n setIsSaving(true);\n try {\n // Parse data JSON\n let parsedData: Record<string, unknown> = {};\n if (form.data && form.data.trim()) {\n try {\n parsedData = JSON.parse(form.data) as Record<string, unknown>;\n } catch {\n setErrors((prev) => ({\n ...prev,\n data: t('bigconsole.dataSinks.builder.validation.invalidJson', 'Invalid JSON format'),\n }));\n setIsSaving(false);\n return;\n }\n }\n\n if (isEdit && existingDataSink) {\n // Update existing\n const input: UpdateDataSinkInput = {\n id: existingDataSink.id,\n version: existingDataSink.version,\n name: form.name,\n description: form.description || undefined,\n ttl: form.ttl,\n refresh: {\n type: form.refresh.type as DataSinkRefreshType,\n schedule: form.refresh.schedule,\n },\n data: parsedData, // Include data in update\n };\n const result = await updateDataSink(input);\n if (!result) {\n throw new Error(t('bigconsole.dataSinks.builder.errors.updateFailed', 'Failed to update DataSink'));\n }\n } else {\n // Create new - simplified flow (standalone DataSinks)\n const input: CreateDataSinkInput = {\n key: form.key,\n name: form.name,\n description: form.description || undefined,\n ttl: form.ttl,\n refresh: {\n type: form.refresh.type as DataSinkRefreshType,\n schedule: form.refresh.schedule,\n },\n data: parsedData, // Include data in create\n };\n const result = await createDataSink(input);\n if (!result) {\n throw new Error(t('bigconsole.dataSinks.builder.errors.createFailed', 'Failed to create DataSink'));\n }\n }\n navigate('/datasinks');\n } catch (err) {\n setSubmitError(\n err instanceof Error\n ? err.message\n : t('bigconsole.dataSinks.builder.errors.saveFailed', 'Failed to save DataSink')\n );\n } finally {\n setIsSaving(false);\n }\n },\n [form, isEdit, existingDataSink, createDataSink, updateDataSink, navigate, t]\n );\n\n // Check if form is valid\n const isFormValid = useMemo(() => {\n const nameValid = form.name.trim().length >= 2;\n const keyValid = form.key.trim().length > 0;\n\n // Check JSON validity if data is provided\n let dataValid = true;\n if (form.data && form.data.trim()) {\n try {\n JSON.parse(form.data);\n } catch {\n dataValid = false;\n }\n }\n\n return nameValid && keyValid && dataValid;\n }, [form.name, form.key, form.data]);\n\n // Loading state\n if (loading && isEdit) {\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 if (error) {\n return (\n <div className=\"mx-auto w-full max-w-3xl p-card-padding\">\n <Section\n tone=\"danger\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.errors.loadFailed', '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.builder.header.backToDataSinks', 'Back to DataSinks')}\n </Button>\n }\n />\n </div>\n );\n }\n\n // Read gate — the edit surface requires read access to the existing sink.\n if (isEdit && !permissionsLoading && !canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.dataSinks.builder.accessDenied.title', 'Access Denied')}\n description={t(\n 'bigconsole.dataSinks.builder.accessDenied.description',\n \"You don't have permission to view this data sink.\"\n )}\n />\n );\n }\n\n return (\n <div className=\"mx-auto w-full min-w-0 max-w-3xl p-card-padding\">\n {/* Page header strip */}\n <header className=\"mb-form-gap\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-4\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.builder.header.backToDataSinks', 'Back to DataSinks')}\n </Button>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {isEdit\n ? t('bigconsole.dataSinks.builder.header.editTitle', 'Edit DataSink')\n : t('bigconsole.dataSinks.builder.header.createTitle', 'Create DataSink')}\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {isEdit\n ? t('bigconsole.dataSinks.builder.header.editSubtitle', 'Update the DataSink configuration.')\n : t(\n 'bigconsole.dataSinks.builder.header.createSubtitle',\n 'Create a new DataSink to store and cache data. Use the unique key to connect workflows.'\n )}\n </p>\n </header>\n\n <PagePurpose className=\"mb-section-gap\">\n {t(\n 'bigconsole.dataSinks.builder.header.purpose',\n 'A data sink is the named, structured store your dashboards read from. Give it a unique key, configure how long data is cached and how it refreshes, then point a workflow at that key to push data in. Get the basics right here and everything downstream connects to it.'\n )}\n </PagePurpose>\n\n {/* Submit failure — a status section, not a hand-rolled tinted div. */}\n {submitError && (\n <Section\n tone=\"danger\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.errors.saveFailed', 'Failed to save DataSink')}\n description={submitError}\n icon={<AlertCircle className=\"h-icon-sm w-icon-sm\" />}\n className=\"mb-section-gap\"\n />\n )}\n\n {/* Form */}\n <form onSubmit={handleSubmit}>\n <SectionStack>\n {/* The point of this route: the sink's identity. */}\n <Section\n emphasis=\"primary\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.basicInfo.title', 'Basic Information')}\n description={t('bigconsole.dataSinks.builder.basicInfo.subtitle', 'Essential details for your DataSink')}\n icon={<Database className=\"h-icon-sm w-icon-sm\" />}\n bodyClassName=\"space-y-form-gap\"\n >\n <>\n {/* Name field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"name\">\n {t('bigconsole.dataSinks.builder.basicInfo.nameLabel', 'Name')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </Label>\n <Input\n id=\"name\"\n type=\"text\"\n value={form.name}\n onChange={handleNameChange}\n placeholder={t('bigconsole.dataSinks.builder.basicInfo.namePlaceholder', 'e.g., Sales Metrics')}\n className={errors.name ? 'border-status-error-border' : ''}\n />\n {errors.name && <p className=\"text-xs text-status-error-text\">{errors.name}</p>}\n </div>\n\n {/* Key field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"key\">\n {t('bigconsole.dataSinks.builder.basicInfo.keyLabel', 'Unique Key')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </Label>\n <div className=\"flex gap-2\">\n <Input\n id=\"key\"\n type=\"text\"\n value={form.key}\n onChange={handleKeyChange}\n placeholder={t('bigconsole.dataSinks.builder.basicInfo.keyPlaceholder', 'sales_metrics')}\n disabled={isEdit}\n className={`font-mono flex-1 ${errors.key ? 'border-status-error-border' : ''}`}\n />\n {form.key && (\n <Button type=\"button\" onClick={handleCopyKey} variant=\"outline\" size=\"sm\" className=\"px-3\">\n {keyCopied ? (\n <Check className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <Copy className=\"w-4 h-4\" />\n )}\n </Button>\n )}\n </div>\n {errors.key && <p className=\"text-xs text-status-error-text\">{errors.key}</p>}\n <p className=\"text-xs text-text-muted\">\n {isEdit\n ? t(\n 'bigconsole.dataSinks.builder.basicInfo.keyHelpEdit',\n 'Key cannot be changed. Use this key in your workflows to push data.'\n )\n : t(\n 'bigconsole.dataSinks.builder.basicInfo.keyHelpCreate',\n 'Auto-generated from name. This key will be used by workflows to push data.'\n )}\n </p>\n </div>\n\n {/* Description field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"description\">\n {t('bigconsole.dataSinks.builder.basicInfo.descriptionLabel', 'Description')}\n </Label>\n <Textarea\n id=\"description\"\n value={form.description}\n onChange={handleDescriptionChange}\n placeholder={t(\n 'bigconsole.dataSinks.builder.basicInfo.descriptionPlaceholder',\n 'Describe what data this sink stores...'\n )}\n rows={2}\n className=\"resize-none\"\n />\n </div>\n </>\n </Section>\n\n {/* Configuration */}\n <Section\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.config.title', 'Configuration')}\n description={t('bigconsole.dataSinks.builder.config.subtitle', 'Cache and refresh settings')}\n icon={<Settings className=\"h-icon-sm w-icon-sm\" />}\n bodyClassName=\"space-y-form-gap\"\n >\n <>\n {/* Cache TTL — the field label lives here, so the selector's own\n heading is suppressed to avoid a duplicated label. */}\n <div className=\"space-y-2\">\n <Label>{t('bigconsole.dataSinks.builder.config.cacheDurationLabel', 'Cache Duration')}</Label>\n <CacheTTLSelector value={form.ttl} onChange={handleTTLChange} hideLabel />\n <p className=\"text-xs text-text-muted\">\n {t(\n 'bigconsole.dataSinks.builder.config.cacheDurationHelp',\n 'How long cached data remains valid before requiring refresh.'\n )}\n </p>\n </div>\n\n {/* Refresh Type */}\n <RefreshTypeSelector value={form.refresh} onChange={handleRefreshChange} />\n </>\n </Section>\n\n {/* Data Payload */}\n <Section\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.dataPayload.title', 'Data Payload')}\n description={\n isEdit\n ? t(\n 'bigconsole.dataSinks.builder.dataPayload.subtitleEdit',\n 'Edit the JSON data stored in this DataSink'\n )\n : t('bigconsole.dataSinks.builder.dataPayload.subtitleCreate', 'Optional initial data (JSON format)')\n }\n icon={<Database className=\"h-icon-sm w-icon-sm\" />}\n actions={\n <Button type=\"button\" onClick={handleFormatData} variant=\"outline\" size=\"sm\">\n {t('bigconsole.dataSinks.builder.dataPayload.formatJson', 'Format JSON')}\n </Button>\n }\n bodyClassName=\"space-y-2\"\n >\n <>\n <Textarea\n aria-label={t('bigconsole.dataSinks.builder.dataPayload.title', 'Data Payload')}\n value={form.data}\n onChange={handleDataChange}\n placeholder={t('bigconsole.dataSinks.builder.dataPayload.dataPlaceholder', '{\"key\": \"value\"}')}\n rows={10}\n className={`resize-y font-mono text-xs ${errors.data ? 'border-status-error-border' : ''}`}\n />\n {errors.data && <p className=\"text-xs text-status-error-text\">{errors.data}</p>}\n <p className=\"text-xs text-text-muted\">\n {t(\n 'bigconsole.dataSinks.builder.dataPayload.help',\n 'Enter valid JSON data. This data will be stored and can be accessed by widgets via parsers.'\n )}\n </p>\n </>\n </Section>\n\n {/* Data Preview — read-only inspector, so it sits below the editable\n sections at `muted`, not level with them. */}\n {isEdit && existingDataSink && (\n <Section\n emphasis=\"muted\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.preview.title', 'Data Preview')}\n description={t('bigconsole.dataSinks.builder.preview.subtitle', 'Preview the first 20 records')}\n icon={<Eye className=\"h-icon-sm w-icon-sm\" />}\n actions={\n <Button type=\"button\" onClick={handleShowData} variant=\"outline\" size=\"sm\" disabled={isLoadingPreview}>\n {isLoadingPreview ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n {t('bigconsole.dataSinks.builder.preview.loading', 'Loading...')}\n </>\n ) : (\n <>\n <Eye className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.builder.preview.showData', 'Show Data')}\n </>\n )}\n </Button>\n }\n >\n {previewError ? (\n <Section tone=\"danger\" padding=\"compact\" headingLevel={3} title=\"Could not load the preview\">\n <p className=\"text-sm text-status-error-text\">{previewError}</p>\n </Section>\n ) : previewData && previewData.length > 0 ? (\n <pre className=\"max-h-[300px] min-w-0 overflow-auto rounded-input bg-bg-surface p-3 font-mono text-xs\">\n {JSON.stringify(previewData, null, 2)}\n </pre>\n ) : previewData ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={t('bigconsole.dataSinks.builder.preview.emptyTitle', 'No data available yet')}\n description={t(\n 'bigconsole.dataSinks.builder.preview.emptyDescription',\n \"Connect a workflow to this sink's key to start pushing data.\"\n )}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Select &quot;Show data&quot; to load the records this sink currently holds.\n </p>\n )}\n </Section>\n )}\n\n {/* Guidance sits before the commit row — it is no longer advice that\n appears after the action which ends the task. */}\n {!isEdit && (\n <Section\n tone=\"info\"\n emphasis=\"muted\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.whatsNext.title', \"What's Next?\")}\n icon={<ListChecks className=\"h-icon-sm w-icon-sm\" />}\n >\n <ol className=\"list-inside list-decimal space-y-1 text-xs text-text-secondary\">\n <li>\n {t('bigconsole.dataSinks.builder.whatsNext.step1', 'Create this DataSink to get your unique key')}\n </li>\n <li>\n {t('bigconsole.dataSinks.builder.whatsNext.step2Prefix', 'Go to')}{' '}\n <strong>{t('bigconsole.dataSinks.builder.whatsNext.workflows', 'Workflows')}</strong>{' '}\n {t('bigconsole.dataSinks.builder.whatsNext.step2Suffix', 'and create a FluidGrids workflow')}\n </li>\n <li>\n {t(\n 'bigconsole.dataSinks.builder.whatsNext.step3',\n 'Use the unique key in your workflow to push data to this DataSink'\n )}\n </li>\n <li>\n {t('bigconsole.dataSinks.builder.whatsNext.step4Prefix', 'Create')}{' '}\n <strong>{t('bigconsole.dataSinks.builder.whatsNext.parsers', 'Parsers')}</strong>{' '}\n {t('bigconsole.dataSinks.builder.whatsNext.step4Suffix', 'to transform the data using JQ or JSONata')}\n </li>\n </ol>\n </Section>\n )}\n\n {/* Commit row — structural strip with a rule so the actions zone reads\n as the end of the form. */}\n <Section\n emphasis=\"bare\"\n padding=\"none\"\n aria-label=\"Save or discard this data sink\"\n className=\"border-t border-[var(--color-border-subtle)] pt-form-gap\"\n bodyClassName=\"flex flex-wrap items-center justify-end gap-control-gap\"\n >\n <Button type=\"button\" onClick={handleBack} variant=\"secondary\">\n {t('bigconsole.dataSinks.builder.actions.cancel', 'Cancel')}\n </Button>\n <PermissionButton permission={savePermission} deniedBehavior=\"disable\">\n <Button type=\"submit\" disabled={!isFormValid || isSaving} variant=\"default\">\n {isSaving ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n {t('bigconsole.dataSinks.builder.actions.saving', 'Saving...')}\n </>\n ) : (\n <>\n <Save className=\"w-4 h-4 mr-2\" />\n {isEdit\n ? t('bigconsole.dataSinks.builder.actions.saveChanges', 'Save Changes')\n : t('bigconsole.dataSinks.builder.actions.create', 'Create DataSink')}\n </>\n )}\n </Button>\n </PermissionButton>\n </Section>\n </SectionStack>\n </form>\n </div>\n );\n});\n\nexport default DataSinkBuilderPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataSinkBuilderPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinkBuilderPage.tsx"],"sourcesContent":["/**\n * DataSink Builder Page\n *\n * Simplified page for creating and editing DataSinks.\n * DataSinks are now standalone - workflows push data to them via unique keys.\n * Required fields: name, key\n */\n\nimport { memo, useState, useMemo, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeToast } from '../../utils/nativeBridge';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { CacheTTLSelector } from '../components/datasink/CacheTTLSelector';\nimport { RefreshTypeSelector } from '../components/datasink/RefreshTypeSelector';\nimport type { CreateDataSinkInput, UpdateDataSinkInput, DataSinkRefreshType } from '../types';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Section-emphasis primitives (BOFF-5632)\nimport { Section, SectionStack } from '../components/common';\n\n// Lucide icons\nimport { ArrowLeft, Save, Loader2, AlertCircle, Database, Settings, Eye, Copy, Check, ListChecks } from 'lucide-react';\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\ntype TranslateFn = (key: string, fallback: string, params?: Record<string, string | number>) => string;\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface RefreshConfig {\n type: 'manual' | 'scheduled';\n schedule?: {\n cron: string;\n timezone: string;\n };\n}\n\ninterface FormState {\n name: string;\n key: string;\n description: string;\n ttl: number;\n refresh: RefreshConfig;\n data: string; // JSON string for data payload\n}\n\ninterface FormErrors {\n name?: string;\n key?: string;\n data?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Generate a key from name (slug format)\n */\nfunction generateKey(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .substring(0, 64);\n}\n\n/**\n * Validate form data\n */\nfunction validateForm(form: FormState, t: TranslateFn): FormErrors {\n const errors: FormErrors = {};\n\n if (!form.name.trim()) {\n errors.name = t('bigconsole.dataSinks.builder.validation.nameRequired', 'Name is required');\n } else if (form.name.length < 2) {\n errors.name = t('bigconsole.dataSinks.builder.validation.nameMinLength', 'Name must be at least 2 characters');\n } else if (form.name.length > 100) {\n errors.name = t('bigconsole.dataSinks.builder.validation.nameMaxLength', 'Name must be less than 100 characters');\n }\n\n if (!form.key.trim()) {\n errors.key = t('bigconsole.dataSinks.builder.validation.keyRequired', 'Key is required');\n } else if (!/^[a-z0-9_]+$/.test(form.key)) {\n errors.key = t(\n 'bigconsole.dataSinks.builder.validation.keyFormat',\n 'Key can only contain lowercase letters, numbers, and underscores'\n );\n } else if (form.key.length > 64) {\n errors.key = t('bigconsole.dataSinks.builder.validation.keyMaxLength', 'Key must be less than 64 characters');\n }\n\n // Validate JSON data if provided\n if (form.data && form.data.trim()) {\n try {\n JSON.parse(form.data);\n } catch {\n errors.data = t('bigconsole.dataSinks.builder.validation.invalidJson', 'Invalid JSON format');\n }\n }\n\n return errors;\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkBuilderPage = memo(function DataSinkBuilderPage() {\n const { dataSinkId } = useParams<{ dataSinkId?: string }>();\n const navigate = useAppNavigate();\n const { t } = useI18n();\n\n const isEdit = dataSinkId !== 'new' && !!dataSinkId;\n\n // RBAC — Save requires UPDATE when editing an existing sink, CREATE when\n // creating a new one. In edit mode the page also requires READ to be viewed.\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.DATASINK_READ);\n const savePermission = isEdit ? BIGCONSOLE_PERMISSIONS.DATASINK_UPDATE : BIGCONSOLE_PERMISSIONS.DATASINK_CREATE;\n\n // Form state\n const [form, setForm] = useState<FormState>({\n name: '',\n key: '',\n description: '',\n ttl: 300, // 5 minutes default\n refresh: {\n type: 'manual',\n },\n data: '{}', // Default empty JSON object\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [keyManuallyEdited, setKeyManuallyEdited] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [keyCopied, setKeyCopied] = useState(false);\n\n // Data preview state\n const [isLoadingPreview, setIsLoadingPreview] = useState(false);\n const [previewData, setPreviewData] = useState<unknown[] | null>(null);\n const [previewError, setPreviewError] = useState<string | null>(null);\n\n // Operations\n const { loading, error, dataSinks, fetchDataSink, createDataSink, updateDataSink, getDataSinkData } =\n useDataSinkOperations({\n skipFetch: true,\n });\n\n // Get existing data sink if editing\n const existingDataSink = isEdit ? dataSinks.find((ds) => ds.id === dataSinkId) : null;\n\n // Load existing data sink for editing\n useEffect(() => {\n if (isEdit && dataSinkId && !existingDataSink) {\n fetchDataSink(dataSinkId);\n }\n }, [isEdit, dataSinkId, existingDataSink, fetchDataSink]);\n\n // Track if form has been initialized\n const [formInitialized, setFormInitialized] = useState(false);\n\n // Populate form with existing data when editing (only once)\n useEffect(() => {\n if (existingDataSink && !formInitialized) {\n // Format existing data as pretty JSON string\n let dataString = '';\n if (existingDataSink.data) {\n try {\n // Check if data is already a string (JSON string from API)\n if (typeof existingDataSink.data === 'string') {\n // Parse and re-stringify to ensure proper formatting\n const parsed = JSON.parse(existingDataSink.data);\n dataString = JSON.stringify(parsed, null, 2);\n } else {\n // It's an object, just stringify it\n dataString = JSON.stringify(existingDataSink.data, null, 2);\n }\n } catch {\n // If parsing fails, use as-is if string, otherwise empty\n dataString = typeof existingDataSink.data === 'string' ? existingDataSink.data : '';\n }\n }\n\n setForm({\n name: existingDataSink.name || '',\n key: existingDataSink.key || '',\n description: existingDataSink.description || '',\n ttl: existingDataSink.ttl || 300,\n refresh: existingDataSink.refresh\n ? {\n type: existingDataSink.refresh.type === 'scheduled' ? 'scheduled' : 'manual',\n schedule: existingDataSink.refresh.schedule\n ? {\n cron: existingDataSink.refresh.schedule.cron,\n timezone: existingDataSink.refresh.schedule.timezone,\n }\n : undefined,\n }\n : { type: 'manual' },\n data: dataString,\n });\n setKeyManuallyEdited(true); // Don't auto-generate key when editing\n setFormInitialized(true); // Mark as initialized\n setErrors({}); // Clear any errors\n }\n }, [existingDataSink, formInitialized]);\n\n // Auto-generate key from name\n const handleNameChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n key: keyManuallyEdited ? prev.key : generateKey(name),\n }));\n setErrors((prev) => ({ ...prev, name: undefined }));\n },\n [keyManuallyEdited]\n );\n\n // Handle key change\n const handleKeyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const key = e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '');\n setForm((prev) => ({ ...prev, key }));\n setKeyManuallyEdited(true);\n setErrors((prev) => ({ ...prev, key: undefined }));\n }, []);\n\n // Handle description change\n const handleDescriptionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, description: e.target.value }));\n }, []);\n\n // Handle TTL change\n const handleTTLChange = useCallback((ttl: number | null) => {\n setForm((prev) => ({ ...prev, ttl: ttl || 0 }));\n }, []);\n\n // Handle refresh config change\n const handleRefreshChange = useCallback((config: RefreshConfig) => {\n setForm((prev) => ({ ...prev, refresh: config }));\n }, []);\n\n // Handle data change\n const handleDataChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, data: e.target.value }));\n setErrors((prev) => ({ ...prev, data: undefined }));\n }, []);\n\n // Format JSON data\n const handleFormatData = useCallback(() => {\n try {\n const parsed = JSON.parse(form.data);\n setForm((prev) => ({ ...prev, data: JSON.stringify(parsed, null, 2) }));\n setErrors((prev) => ({ ...prev, data: undefined }));\n } catch {\n setErrors((prev) => ({\n ...prev,\n data: t('bigconsole.dataSinks.builder.validation.invalidJson', 'Invalid JSON format'),\n }));\n }\n }, [form.data, t]);\n\n // Copy key to clipboard\n const handleCopyKey = useCallback(async () => {\n if (form.key) {\n await nativeCopyText(form.key);\n setKeyCopied(true);\n if (isNative()) void nativeToast(t('bigconsole.dataSinks.builder.toolbar.keyCopied', 'Key copied'));\n setTimeout(() => setKeyCopied(false), 2000);\n }\n }, [form.key, t]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n navigate('/datasinks');\n }, [navigate]);\n\n // Load data preview\n const handleShowData = useCallback(async () => {\n if (!isEdit || !existingDataSink) return;\n\n setIsLoadingPreview(true);\n setPreviewError(null);\n\n try {\n const result = await getDataSinkData({ key: existingDataSink.key });\n if (result?.raw) {\n // Get first 20 items if it's an array, otherwise wrap in array\n const data = Array.isArray(result.raw) ? result.raw.slice(0, 20) : [result.raw];\n setPreviewData(data);\n } else {\n setPreviewData([]);\n }\n } catch (err) {\n setPreviewError(\n err instanceof Error\n ? err.message\n : t('bigconsole.dataSinks.builder.errors.loadDataFailed', 'Failed to load data')\n );\n } finally {\n setIsLoadingPreview(false);\n }\n }, [isEdit, existingDataSink, getDataSinkData, t]);\n\n // Handle form submission\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError(null);\n\n // Validate form\n const validationErrors = validateForm(form, t);\n if (Object.keys(validationErrors).length > 0) {\n setErrors(validationErrors);\n return;\n }\n\n setIsSaving(true);\n try {\n // Parse data JSON\n let parsedData: Record<string, unknown> = {};\n if (form.data && form.data.trim()) {\n try {\n parsedData = JSON.parse(form.data) as Record<string, unknown>;\n } catch {\n setErrors((prev) => ({\n ...prev,\n data: t('bigconsole.dataSinks.builder.validation.invalidJson', 'Invalid JSON format'),\n }));\n setIsSaving(false);\n return;\n }\n }\n\n if (isEdit && existingDataSink) {\n // Update existing\n const input: UpdateDataSinkInput = {\n id: existingDataSink.id,\n version: existingDataSink.version,\n name: form.name,\n description: form.description || undefined,\n ttl: form.ttl,\n refresh: {\n type: form.refresh.type as DataSinkRefreshType,\n schedule: form.refresh.schedule,\n },\n data: parsedData, // Include data in update\n };\n const result = await updateDataSink(input);\n if (!result) {\n throw new Error(t('bigconsole.dataSinks.builder.errors.updateFailed', 'Failed to update DataSink'));\n }\n } else {\n // Create new - simplified flow (standalone DataSinks)\n const input: CreateDataSinkInput = {\n key: form.key,\n name: form.name,\n description: form.description || undefined,\n ttl: form.ttl,\n refresh: {\n type: form.refresh.type as DataSinkRefreshType,\n schedule: form.refresh.schedule,\n },\n data: parsedData, // Include data in create\n };\n const result = await createDataSink(input);\n if (!result) {\n throw new Error(t('bigconsole.dataSinks.builder.errors.createFailed', 'Failed to create DataSink'));\n }\n }\n navigate('/datasinks');\n } catch (err) {\n setSubmitError(\n err instanceof Error\n ? err.message\n : t('bigconsole.dataSinks.builder.errors.saveFailed', 'Failed to save DataSink')\n );\n } finally {\n setIsSaving(false);\n }\n },\n [form, isEdit, existingDataSink, createDataSink, updateDataSink, navigate, t]\n );\n\n // Check if form is valid\n const isFormValid = useMemo(() => {\n const nameValid = form.name.trim().length >= 2;\n const keyValid = form.key.trim().length > 0;\n\n // Check JSON validity if data is provided\n let dataValid = true;\n if (form.data && form.data.trim()) {\n try {\n JSON.parse(form.data);\n } catch {\n dataValid = false;\n }\n }\n\n return nameValid && keyValid && dataValid;\n }, [form.name, form.key, form.data]);\n\n // Loading state\n if (loading && isEdit) {\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 if (error) {\n return (\n <div className=\"mx-auto w-full max-w-3xl p-card-padding\">\n <Section\n tone=\"danger\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.errors.loadFailed', '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.builder.header.backToDataSinks', 'Back to DataSinks')}\n </Button>\n }\n />\n </div>\n );\n }\n\n // Read gate — the edit surface requires read access to the existing sink.\n if (isEdit && !permissionsLoading && !canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.dataSinks.builder.accessDenied.title', 'Access Denied')}\n description={t(\n 'bigconsole.dataSinks.builder.accessDenied.description',\n \"You don't have permission to view this data sink.\"\n )}\n />\n );\n }\n\n return (\n <div className=\"mx-auto w-full min-w-0 max-w-3xl p-card-padding\">\n {/* Page header strip */}\n <header className=\"mb-form-gap\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-4\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.builder.header.backToDataSinks', 'Back to DataSinks')}\n </Button>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {isEdit\n ? t('bigconsole.dataSinks.builder.header.editTitle', 'Edit DataSink')\n : t('bigconsole.dataSinks.builder.header.createTitle', 'Create DataSink')}\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {isEdit\n ? t('bigconsole.dataSinks.builder.header.editSubtitle', 'Update the DataSink configuration.')\n : t(\n 'bigconsole.dataSinks.builder.header.createSubtitle',\n 'Create a new DataSink to store and cache data. Use the unique key to connect workflows.'\n )}\n </p>\n </header>\n\n <PagePurpose className=\"mb-section-gap\">\n {t(\n 'bigconsole.dataSinks.builder.header.purpose',\n 'A data sink is the named, structured store your dashboards read from. Give it a unique key, configure how long data is cached and how it refreshes, then point a workflow at that key to push data in. Get the basics right here and everything downstream connects to it.'\n )}\n </PagePurpose>\n\n {/* Submit failure — a status section, not a hand-rolled tinted div. */}\n {submitError && (\n <Section\n tone=\"danger\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.errors.saveFailed', 'Failed to save DataSink')}\n description={submitError}\n icon={<AlertCircle className=\"h-icon-sm w-icon-sm\" />}\n className=\"mb-section-gap\"\n />\n )}\n\n {/* Form */}\n <form onSubmit={handleSubmit}>\n <SectionStack>\n {/* The point of this route: the sink's identity. */}\n <Section\n emphasis=\"primary\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.basicInfo.title', 'Basic Information')}\n description={t('bigconsole.dataSinks.builder.basicInfo.subtitle', 'Essential details for your DataSink')}\n icon={<Database className=\"h-icon-sm w-icon-sm\" />}\n bodyClassName=\"space-y-form-gap\"\n >\n <>\n {/* Name field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"name\">\n {t('bigconsole.dataSinks.builder.basicInfo.nameLabel', 'Name')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </Label>\n <Input\n id=\"name\"\n type=\"text\"\n value={form.name}\n onChange={handleNameChange}\n placeholder={t('bigconsole.dataSinks.builder.basicInfo.namePlaceholder', 'e.g., Sales Metrics')}\n className={errors.name ? 'border-status-error-border' : ''}\n />\n {errors.name && <p className=\"text-xs text-status-error-text\">{errors.name}</p>}\n </div>\n\n {/* Key field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"key\">\n {t('bigconsole.dataSinks.builder.basicInfo.keyLabel', 'Unique Key')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </Label>\n <div className=\"flex gap-2\">\n <Input\n id=\"key\"\n type=\"text\"\n value={form.key}\n onChange={handleKeyChange}\n placeholder={t('bigconsole.dataSinks.builder.basicInfo.keyPlaceholder', 'sales_metrics')}\n disabled={isEdit}\n className={`font-mono flex-1 ${errors.key ? 'border-status-error-border' : ''}`}\n />\n {form.key && (\n <Button type=\"button\" onClick={handleCopyKey} variant=\"outline\" size=\"sm\" className=\"px-3\">\n {keyCopied ? (\n <Check className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <Copy className=\"w-4 h-4\" />\n )}\n </Button>\n )}\n </div>\n {errors.key && <p className=\"text-xs text-status-error-text\">{errors.key}</p>}\n <p className=\"text-xs text-text-muted\">\n {isEdit\n ? t(\n 'bigconsole.dataSinks.builder.basicInfo.keyHelpEdit',\n 'Key cannot be changed. Use this key in your workflows to push data.'\n )\n : t(\n 'bigconsole.dataSinks.builder.basicInfo.keyHelpCreate',\n 'Auto-generated from name. This key will be used by workflows to push data.'\n )}\n </p>\n </div>\n\n {/* Description field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"description\">\n {t('bigconsole.dataSinks.builder.basicInfo.descriptionLabel', 'Description')}\n </Label>\n <Textarea\n id=\"description\"\n value={form.description}\n onChange={handleDescriptionChange}\n placeholder={t(\n 'bigconsole.dataSinks.builder.basicInfo.descriptionPlaceholder',\n 'Describe what data this sink stores...'\n )}\n rows={2}\n className=\"resize-none\"\n />\n </div>\n </>\n </Section>\n\n {/* Configuration */}\n <Section\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.config.title', 'Configuration')}\n description={t('bigconsole.dataSinks.builder.config.subtitle', 'Cache and refresh settings')}\n icon={<Settings className=\"h-icon-sm w-icon-sm\" />}\n bodyClassName=\"space-y-form-gap\"\n >\n <>\n {/* Cache TTL — the field label lives here, so the selector's own\n heading is suppressed to avoid a duplicated label. */}\n <div className=\"space-y-2\">\n <Label>{t('bigconsole.dataSinks.builder.config.cacheDurationLabel', 'Cache Duration')}</Label>\n <CacheTTLSelector value={form.ttl} onChange={handleTTLChange} hideLabel />\n <p className=\"text-xs text-text-muted\">\n {t(\n 'bigconsole.dataSinks.builder.config.cacheDurationHelp',\n 'How long cached data remains valid before requiring refresh.'\n )}\n </p>\n </div>\n\n {/* Refresh Type */}\n <RefreshTypeSelector value={form.refresh} onChange={handleRefreshChange} />\n </>\n </Section>\n\n {/* Data Payload */}\n <Section\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.dataPayload.title', 'Data Payload')}\n description={\n isEdit\n ? t(\n 'bigconsole.dataSinks.builder.dataPayload.subtitleEdit',\n 'Edit the JSON data stored in this DataSink'\n )\n : t('bigconsole.dataSinks.builder.dataPayload.subtitleCreate', 'Optional initial data (JSON format)')\n }\n icon={<Database className=\"h-icon-sm w-icon-sm\" />}\n actions={\n <Button type=\"button\" onClick={handleFormatData} variant=\"outline\" size=\"sm\">\n {t('bigconsole.dataSinks.builder.dataPayload.formatJson', 'Format JSON')}\n </Button>\n }\n bodyClassName=\"space-y-2\"\n >\n <>\n <Textarea\n aria-label={t('bigconsole.dataSinks.builder.dataPayload.title', 'Data Payload')}\n value={form.data}\n onChange={handleDataChange}\n placeholder={t('bigconsole.dataSinks.builder.dataPayload.dataPlaceholder', '{\"key\": \"value\"}')}\n rows={10}\n className={`resize-y font-mono text-xs ${errors.data ? 'border-status-error-border' : ''}`}\n />\n {errors.data && <p className=\"text-xs text-status-error-text\">{errors.data}</p>}\n <p className=\"text-xs text-text-muted\">\n {t(\n 'bigconsole.dataSinks.builder.dataPayload.help',\n 'Enter valid JSON data. This data will be stored and can be accessed by widgets via parsers.'\n )}\n </p>\n </>\n </Section>\n\n {/* Data Preview — read-only inspector, so it sits below the editable\n sections at `muted`, not level with them. */}\n {isEdit && existingDataSink && (\n <Section\n emphasis=\"muted\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.preview.title', 'Data Preview')}\n description={t('bigconsole.dataSinks.builder.preview.subtitle', 'Preview the first 20 records')}\n icon={<Eye className=\"h-icon-sm w-icon-sm\" />}\n actions={\n <Button type=\"button\" onClick={handleShowData} variant=\"outline\" size=\"sm\" disabled={isLoadingPreview}>\n {isLoadingPreview ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n {t('bigconsole.dataSinks.builder.preview.loading', 'Loading...')}\n </>\n ) : (\n <>\n <Eye className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dataSinks.builder.preview.showData', 'Show Data')}\n </>\n )}\n </Button>\n }\n >\n {previewError ? (\n <Section tone=\"danger\" padding=\"compact\" headingLevel={3} title=\"Could not load the preview\">\n <p className=\"text-sm text-status-error-text\">{previewError}</p>\n </Section>\n ) : previewData && previewData.length > 0 ? (\n <pre className=\"max-h-[300px] min-w-0 overflow-auto rounded-input bg-bg-surface p-3 font-mono text-xs\">\n {JSON.stringify(previewData, null, 2)}\n </pre>\n ) : previewData ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={t('bigconsole.dataSinks.builder.preview.emptyTitle', 'No data available yet')}\n description={t(\n 'bigconsole.dataSinks.builder.preview.emptyDescription',\n \"Connect a workflow to this sink's key to start pushing data.\"\n )}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Select &quot;Show data&quot; to load the records this sink currently holds.\n </p>\n )}\n </Section>\n )}\n\n {/* Guidance sits before the commit row — it is no longer advice that\n appears after the action which ends the task. */}\n {!isEdit && (\n <Section\n tone=\"info\"\n emphasis=\"muted\"\n headingLevel={2}\n title={t('bigconsole.dataSinks.builder.whatsNext.title', \"What's Next?\")}\n icon={<ListChecks className=\"h-icon-sm w-icon-sm\" />}\n >\n <ol className=\"list-inside list-decimal space-y-1 text-xs text-text-secondary\">\n <li>\n {t('bigconsole.dataSinks.builder.whatsNext.step1', 'Create this DataSink to get your unique key')}\n </li>\n <li>\n {t('bigconsole.dataSinks.builder.whatsNext.step2Prefix', 'Go to')}{' '}\n <strong>{t('bigconsole.dataSinks.builder.whatsNext.workflows', 'Workflows')}</strong>{' '}\n {t('bigconsole.dataSinks.builder.whatsNext.step2Suffix', 'and create a FluidGrids workflow')}\n </li>\n <li>\n {t(\n 'bigconsole.dataSinks.builder.whatsNext.step3',\n 'Use the unique key in your workflow to push data to this DataSink'\n )}\n </li>\n <li>\n {t('bigconsole.dataSinks.builder.whatsNext.step4Prefix', 'Create')}{' '}\n <strong>{t('bigconsole.dataSinks.builder.whatsNext.parsers', 'Parsers')}</strong>{' '}\n {t('bigconsole.dataSinks.builder.whatsNext.step4Suffix', 'to transform the data using JQ or JSONata')}\n </li>\n </ol>\n </Section>\n )}\n\n {/* Commit row — structural strip with a rule so the actions zone reads\n as the end of the form. */}\n <Section\n emphasis=\"bare\"\n padding=\"none\"\n aria-label=\"Save or discard this data sink\"\n className=\"border-t border-[var(--color-border-subtle)] pt-form-gap\"\n bodyClassName=\"flex flex-wrap items-center justify-end gap-control-gap\"\n >\n <Button type=\"button\" onClick={handleBack} variant=\"secondary\">\n {t('bigconsole.dataSinks.builder.actions.cancel', 'Cancel')}\n </Button>\n <PermissionButton permission={savePermission} deniedBehavior=\"disable\">\n <Button type=\"submit\" disabled={!isFormValid || isSaving} variant=\"default\">\n {isSaving ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n {t('bigconsole.dataSinks.builder.actions.saving', 'Saving...')}\n </>\n ) : (\n <>\n <Save className=\"w-4 h-4 mr-2\" />\n {isEdit\n ? t('bigconsole.dataSinks.builder.actions.saveChanges', 'Save Changes')\n : t('bigconsole.dataSinks.builder.actions.create', 'Create DataSink')}\n </>\n )}\n </Button>\n </PermissionButton>\n </Section>\n </SectionStack>\n </form>\n </div>\n );\n});\n\nexport default DataSinkBuilderPage;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwEA,SAAS,GAAY,GAAsB;AACzC,QAAO,EACJ,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG,CACvB,UAAU,GAAG,GAAG;;AAMrB,SAAS,GAAa,GAAiB,GAA4B;CACjE,IAAM,IAAqB,EAAE;AAsB7B,KApBK,EAAK,KAAK,MAAM,GAEV,EAAK,KAAK,SAAS,IAC5B,EAAO,OAAO,EAAE,yDAAyD,qCAAqC,GACrG,EAAK,KAAK,SAAS,QAC5B,EAAO,OAAO,EAAE,yDAAyD,wCAAwC,IAJjH,EAAO,OAAO,EAAE,wDAAwD,mBAAmB,EAOxF,EAAK,IAAI,MAAM,GAER,eAAe,KAAK,EAAK,IAAI,GAK9B,EAAK,IAAI,SAAS,OAC3B,EAAO,MAAM,EAAE,wDAAwD,sCAAsC,IAL7G,EAAO,MAAM,EACX,qDACA,mEACD,GALD,EAAO,MAAM,EAAE,uDAAuD,kBAAkB,EAWtF,EAAK,QAAQ,EAAK,KAAK,MAAM,CAC/B,KAAI;AACF,OAAK,MAAM,EAAK,KAAK;SACf;AACN,IAAO,OAAO,EAAE,uDAAuD,sBAAsB;;AAIjG,QAAO;;AAOT,IAAa,IAAsB,EAAK,WAA+B;CACrE,IAAM,EAAE,kBAAe,IAAoC,EACrD,IAAW,GAAgB,EAC3B,EAAE,SAAM,IAAS,EAEjB,IAAS,MAAe,SAAS,CAAC,CAAC,GAInC,EAAE,mBAAe,WAAW,OAAuB,GAAgB,EACnE,KAAU,GAAc,EAAuB,cAAc,EAC7D,KAAiB,IAAS,EAAuB,kBAAkB,EAAuB,iBAG1F,CAAC,GAAM,KAAW,EAAoB;EAC1C,MAAM;EACN,KAAK;EACL,aAAa;EACb,KAAK;EACL,SAAS,EACP,MAAM,UACP;EACD,MAAM;EACP,CAAC,EACI,CAAC,GAAQ,KAAa,EAAqB,EAAE,CAAC,EAC9C,CAAC,GAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,IAAW,KAAgB,EAAS,GAAM,EAG3C,CAAC,GAAkB,KAAuB,EAAS,GAAM,EACzD,CAAC,GAAa,KAAkB,EAA2B,KAAK,EAChE,CAAC,GAAc,KAAmB,EAAwB,KAAK,EAG/D,EAAE,aAAS,UAAO,eAAW,kBAAe,mBAAgB,mBAAgB,uBAChF,GAAsB,EACpB,WAAW,IACZ,CAAC,EAGE,IAAmB,IAAS,GAAU,MAAM,MAAO,EAAG,OAAO,EAAW,GAAG;AAGjF,SAAgB;AACd,EAAI,KAAU,KAAc,CAAC,KAC3B,EAAc,EAAW;IAE1B;EAAC;EAAQ;EAAY;EAAkB;EAAc,CAAC;CAGzD,IAAM,CAAC,GAAiB,MAAsB,EAAS,GAAM;AAG7D,SAAgB;AACd,MAAI,KAAoB,CAAC,GAAiB;GAExC,IAAI,IAAa;AACjB,OAAI,EAAiB,KACnB,KAAI;AAEF,QAAI,OAAO,EAAiB,QAAS,UAAU;KAE7C,IAAM,IAAS,KAAK,MAAM,EAAiB,KAAK;AAChD,SAAa,KAAK,UAAU,GAAQ,MAAM,EAAE;UAG5C,KAAa,KAAK,UAAU,EAAiB,MAAM,MAAM,EAAE;WAEvD;AAEN,QAAa,OAAO,EAAiB,QAAS,WAAW,EAAiB,OAAO;;AAwBrF,GApBA,EAAQ;IACN,MAAM,EAAiB,QAAQ;IAC/B,KAAK,EAAiB,OAAO;IAC7B,aAAa,EAAiB,eAAe;IAC7C,KAAK,EAAiB,OAAO;IAC7B,SAAS,EAAiB,UACtB;KACE,MAAM,EAAiB,QAAQ,SAAS,cAAc,cAAc;KACpE,UAAU,EAAiB,QAAQ,WAC/B;MACE,MAAM,EAAiB,QAAQ,SAAS;MACxC,UAAU,EAAiB,QAAQ,SAAS;MAC7C,GACD,KAAA;KACL,GACD,EAAE,MAAM,UAAU;IACtB,MAAM;IACP,CAAC,EACF,EAAqB,GAAK,EAC1B,GAAmB,GAAK,EACxB,EAAU,EAAE,CAAC;;IAEd,CAAC,GAAkB,EAAgB,CAAC;CAGvC,IAAM,KAAmB,GACtB,MAA2C;EAC1C,IAAM,IAAO,EAAE,OAAO;AAMtB,EALA,GAAS,OAAU;GACjB,GAAG;GACH;GACA,KAAK,IAAoB,EAAK,MAAM,GAAY,EAAK;GACtD,EAAE,EACH,GAAW,OAAU;GAAE,GAAG;GAAM,MAAM,KAAA;GAAW,EAAE;IAErD,CAAC,EAAkB,CACpB,EAGK,KAAkB,GAAa,MAA2C;EAC9E,IAAM,IAAM,EAAE,OAAO,MAAM,aAAa,CAAC,QAAQ,eAAe,GAAG;AAGnE,EAFA,GAAS,OAAU;GAAE,GAAG;GAAM;GAAK,EAAE,EACrC,EAAqB,GAAK,EAC1B,GAAW,OAAU;GAAE,GAAG;GAAM,KAAK,KAAA;GAAW,EAAE;IACjD,EAAE,CAAC,EAGA,KAA0B,GAAa,MAA8C;AACzF,KAAS,OAAU;GAAE,GAAG;GAAM,aAAa,EAAE,OAAO;GAAO,EAAE;IAC5D,EAAE,CAAC,EAGA,KAAkB,GAAa,MAAuB;AAC1D,KAAS,OAAU;GAAE,GAAG;GAAM,KAAK,KAAO;GAAG,EAAE;IAC9C,EAAE,CAAC,EAGA,KAAsB,GAAa,MAA0B;AACjE,KAAS,OAAU;GAAE,GAAG;GAAM,SAAS;GAAQ,EAAE;IAChD,EAAE,CAAC,EAGA,IAAmB,GAAa,MAA8C;AAElF,EADA,GAAS,OAAU;GAAE,GAAG;GAAM,MAAM,EAAE,OAAO;GAAO,EAAE,EACtD,GAAW,OAAU;GAAE,GAAG;GAAM,MAAM,KAAA;GAAW,EAAE;IAClD,EAAE,CAAC,EAGA,KAAmB,QAAkB;AACzC,MAAI;GACF,IAAM,IAAS,KAAK,MAAM,EAAK,KAAK;AAEpC,GADA,GAAS,OAAU;IAAE,GAAG;IAAM,MAAM,KAAK,UAAU,GAAQ,MAAM,EAAE;IAAE,EAAE,EACvE,GAAW,OAAU;IAAE,GAAG;IAAM,MAAM,KAAA;IAAW,EAAE;UAC7C;AACN,MAAW,OAAU;IACnB,GAAG;IACH,MAAM,EAAE,uDAAuD,sBAAsB;IACtF,EAAE;;IAEJ,CAAC,EAAK,MAAM,EAAE,CAAC,EAGZ,KAAgB,EAAY,YAAY;AAC5C,EAAI,EAAK,QACP,MAAM,GAAe,EAAK,IAAI,EAC9B,EAAa,GAAK,EACd,IAAU,IAAO,EAAY,EAAE,kDAAkD,aAAa,CAAC,EACnG,iBAAiB,EAAa,GAAM,EAAE,IAAK;IAE5C,CAAC,EAAK,KAAK,EAAE,CAAC,EAGX,IAAa,QAAkB;AACnC,IAAS,aAAa;IACrB,CAAC,EAAS,CAAC,EAGR,KAAiB,EAAY,YAAY;AACzC,SAAC,KAAU,CAAC,IAGhB;GADA,EAAoB,GAAK,EACzB,EAAgB,KAAK;AAErB,OAAI;IACF,IAAM,IAAS,MAAM,EAAgB,EAAE,KAAK,EAAiB,KAAK,CAAC;AACnE,IAAI,GAAQ,MAGV,EADa,MAAM,QAAQ,EAAO,IAAI,GAAG,EAAO,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,EAAO,IAAI,CAC3D,GAEpB,EAAe,EAAE,CAAC;YAEb,GAAK;AACZ,MACE,aAAe,QACX,EAAI,UACJ,EAAE,sDAAsD,sBAAsB,CACnF;aACO;AACR,MAAoB,GAAM;;;IAE3B;EAAC;EAAQ;EAAkB;EAAiB;EAAE,CAAC,EAG5C,KAAe,EACnB,OAAO,MAAuB;AAE5B,EADA,EAAE,gBAAgB,EAClB,EAAe,KAAK;EAGpB,IAAM,IAAmB,GAAa,GAAM,EAAE;AAC9C,MAAI,OAAO,KAAK,EAAiB,CAAC,SAAS,GAAG;AAC5C,KAAU,EAAiB;AAC3B;;AAGF,IAAY,GAAK;AACjB,MAAI;GAEF,IAAI,IAAsC,EAAE;AAC5C,OAAI,EAAK,QAAQ,EAAK,KAAK,MAAM,CAC/B,KAAI;AACF,QAAa,KAAK,MAAM,EAAK,KAAK;WAC5B;AAKN,IAJA,GAAW,OAAU;KACnB,GAAG;KACH,MAAM,EAAE,uDAAuD,sBAAsB;KACtF,EAAE,EACH,EAAY,GAAM;AAClB;;AAIJ,OAAI,KAAU;QAeR,CADW,MAAM,EAZc;KACjC,IAAI,EAAiB;KACrB,SAAS,EAAiB;KAC1B,MAAM,EAAK;KACX,aAAa,EAAK,eAAe,KAAA;KACjC,KAAK,EAAK;KACV,SAAS;MACP,MAAM,EAAK,QAAQ;MACnB,UAAU,EAAK,QAAQ;MACxB;KACD,MAAM;KACP,CACyC,CAExC,OAAU,MAAM,EAAE,oDAAoD,4BAA4B,CAAC;cAgBjG,CADW,MAAM,EAXc;IACjC,KAAK,EAAK;IACV,MAAM,EAAK;IACX,aAAa,EAAK,eAAe,KAAA;IACjC,KAAK,EAAK;IACV,SAAS;KACP,MAAM,EAAK,QAAQ;KACnB,UAAU,EAAK,QAAQ;KACxB;IACD,MAAM;IACP,CACyC,CAExC,OAAU,MAAM,EAAE,oDAAoD,4BAA4B,CAAC;AAGvG,KAAS,aAAa;WACf,GAAK;AACZ,KACE,aAAe,QACX,EAAI,UACJ,EAAE,kDAAkD,0BAA0B,CACnF;YACO;AACR,KAAY,GAAM;;IAGtB;EAAC;EAAM;EAAQ;EAAkB;EAAgB;EAAgB;EAAU;EAAE,CAC9E,EAGK,KAAc,SAAc;EAChC,IAAM,IAAY,EAAK,KAAK,MAAM,CAAC,UAAU,GACvC,IAAW,EAAK,IAAI,MAAM,CAAC,SAAS,GAGtC,IAAY;AAChB,MAAI,EAAK,QAAQ,EAAK,KAAK,MAAM,CAC/B,KAAI;AACF,QAAK,MAAM,EAAK,KAAK;UACf;AACN,OAAY;;AAIhB,SAAO,KAAa,KAAY;IAC/B;EAAC,EAAK;EAAM,EAAK;EAAK,EAAK;EAAK,CAAC;AA6CpC,QA1CI,MAAW,IAEX,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,wDAAyD,CAAA;EACxE,CAAA,GAKN,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,MAAK;GACL,cAAc;GACd,OAAO,EAAE,kDAAkD,0BAA0B;GACrF,aAAa,EAAM;GACnB,MAAM,kBAAC,GAAD,EAAa,WAAU,uBAAwB,CAAA;GACrD,SACE,kBAAC,GAAD;IAAQ,SAAS;IAAY,SAAQ;IAAY,MAAK;cAAtD,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EACrC,EAAE,uDAAuD,oBAAoB,CACvE;;GAEX,CAAA;EACE,CAAA,GAKN,KAAU,CAAC,MAAsB,CAAC,KAElC,kBAAC,IAAD;EACE,OAAO,EAAE,mDAAmD,gBAAgB;EAC5E,aAAa,EACX,yDACA,oDACD;EACD,CAAA,GAKJ,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAAlB;KACE,kBAAC,GAAD;MAAQ,SAAS;MAAY,SAAQ;MAAQ,MAAK;MAAK,WAAU;gBAAjE,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EACrC,EAAE,uDAAuD,oBAAoB,CACvE;;KACT,kBAAC,MAAD;MAAI,WAAU;gBACX,IACG,EAAE,iDAAiD,gBAAgB,GACnE,EAAE,mDAAmD,kBAAkB;MACxE,CAAA;KACL,kBAAC,KAAD;MAAG,WAAU;gBACV,IACG,EAAE,oDAAoD,qCAAqC,GAC3F,EACE,sDACA,0FACD;MACH,CAAA;KACG;;GAET,kBAAC,IAAD;IAAa,WAAU;cACpB,EACC,+CACA,6QACD;IACW,CAAA;GAGb,KACC,kBAAC,GAAD;IACE,MAAK;IACL,cAAc;IACd,OAAO,EAAE,kDAAkD,0BAA0B;IACrF,aAAa;IACb,MAAM,kBAAC,GAAD,EAAa,WAAU,uBAAwB,CAAA;IACrD,WAAU;IACV,CAAA;GAIJ,kBAAC,QAAD;IAAM,UAAU;cACd,kBAAC,GAAD,EAAA,UAAA;KAEE,kBAAC,GAAD;MACE,UAAS;MACT,cAAc;MACd,OAAO,EAAE,gDAAgD,oBAAoB;MAC7E,aAAa,EAAE,mDAAmD,sCAAsC;MACxG,MAAM,kBAAC,GAAD,EAAU,WAAU,uBAAwB,CAAA;MAClD,eAAc;gBAEd,kBAAA,GAAA,EAAA,UAAA;OAEE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD;UAAO,SAAQ;oBAAf;WACG,EAAE,oDAAoD,OAAO;WAAE;WAChE,kBAAC,QAAD;YAAM,WAAU;sBAAyB;YAAQ,CAAA;WAC3C;;SACR,kBAAC,GAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,EAAK;UACZ,UAAU;UACV,aAAa,EAAE,0DAA0D,sBAAsB;UAC/F,WAAW,EAAO,OAAO,+BAA+B;UACxD,CAAA;SACD,EAAO,QAAQ,kBAAC,KAAD;UAAG,WAAU;oBAAkC,EAAO;UAAS,CAAA;SAC3E;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD;UAAO,SAAQ;oBAAf;WACG,EAAE,mDAAmD,aAAa;WAAE;WACrE,kBAAC,QAAD;YAAM,WAAU;sBAAyB;YAAQ,CAAA;WAC3C;;SACR,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WACE,IAAG;WACH,MAAK;WACL,OAAO,EAAK;WACZ,UAAU;WACV,aAAa,EAAE,yDAAyD,gBAAgB;WACxF,UAAU;WACV,WAAW,oBAAoB,EAAO,MAAM,+BAA+B;WAC3E,CAAA,EACD,EAAK,OACJ,kBAAC,GAAD;WAAQ,MAAK;WAAS,SAAS;WAAe,SAAQ;WAAU,MAAK;WAAK,WAAU;qBACjF,KACC,kBAAC,IAAD,EAAO,WAAU,oCAAqC,CAAA,GAEtD,kBAAC,IAAD,EAAM,WAAU,WAAY,CAAA;WAEvB,CAAA,CAEP;;SACL,EAAO,OAAO,kBAAC,KAAD;UAAG,WAAU;oBAAkC,EAAO;UAAQ,CAAA;SAC7E,kBAAC,KAAD;UAAG,WAAU;oBACV,IACG,EACE,sDACA,sEACD,GACD,EACE,wDACA,6EACD;UACH,CAAA;SACA;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBACZ,EAAE,2DAA2D,cAAc;SACtE,CAAA,EACR,kBAAC,GAAD;SACE,IAAG;SACH,OAAO,EAAK;SACZ,UAAU;SACV,aAAa,EACX,iEACA,yCACD;SACD,MAAM;SACN,WAAU;SACV,CAAA,CACE;;OACL,EAAA,CAAA;MACK,CAAA;KAGV,kBAAC,GAAD;MACE,cAAc;MACd,OAAO,EAAE,6CAA6C,gBAAgB;MACtE,aAAa,EAAE,gDAAgD,6BAA6B;MAC5F,MAAM,kBAAC,IAAD,EAAU,WAAU,uBAAwB,CAAA;MAClD,eAAc;gBAEd,kBAAA,GAAA,EAAA,UAAA,CAGE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAA,UAAQ,EAAE,0DAA0D,iBAAiB,EAAS,CAAA;QAC9F,kBAAC,IAAD;SAAkB,OAAO,EAAK;SAAK,UAAU;SAAiB,WAAA;SAAY,CAAA;QAC1E,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,yDACA,+DACD;SACC,CAAA;QACA;UAGN,kBAAC,IAAD;OAAqB,OAAO,EAAK;OAAS,UAAU;OAAuB,CAAA,CAC1E,EAAA,CAAA;MACK,CAAA;KAGV,kBAAC,GAAD;MACE,cAAc;MACd,OAAO,EAAE,kDAAkD,eAAe;MAC1E,aACE,IACI,EACE,yDACA,6CACD,GACD,EAAE,2DAA2D,sCAAsC;MAEzG,MAAM,kBAAC,GAAD,EAAU,WAAU,uBAAwB,CAAA;MAClD,SACE,kBAAC,GAAD;OAAQ,MAAK;OAAS,SAAS;OAAkB,SAAQ;OAAU,MAAK;iBACrE,EAAE,uDAAuD,cAAc;OACjE,CAAA;MAEX,eAAc;gBAEd,kBAAA,GAAA,EAAA,UAAA;OACE,kBAAC,GAAD;QACE,cAAY,EAAE,kDAAkD,eAAe;QAC/E,OAAO,EAAK;QACZ,UAAU;QACV,aAAa,EAAE,4DAA4D,uBAAmB;QAC9F,MAAM;QACN,WAAW,8BAA8B,EAAO,OAAO,+BAA+B;QACtF,CAAA;OACD,EAAO,QAAQ,kBAAC,KAAD;QAAG,WAAU;kBAAkC,EAAO;QAAS,CAAA;OAC/E,kBAAC,KAAD;QAAG,WAAU;kBACV,EACC,iDACA,8FACD;QACC,CAAA;OACH,EAAA,CAAA;MACK,CAAA;KAIT,KAAU,KACT,kBAAC,GAAD;MACE,UAAS;MACT,cAAc;MACd,OAAO,EAAE,8CAA8C,eAAe;MACtE,aAAa,EAAE,iDAAiD,+BAA+B;MAC/F,MAAM,kBAAC,GAAD,EAAK,WAAU,uBAAwB,CAAA;MAC7C,SACE,kBAAC,GAAD;OAAQ,MAAK;OAAS,SAAS;OAAgB,SAAQ;OAAU,MAAK;OAAK,UAAU;iBAClF,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EAChD,EAAE,gDAAgD,aAAa,CAC/D,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAK,WAAU,gBAAiB,CAAA,EAC/B,EAAE,iDAAiD,YAAY,CAC/D,EAAA,CAAA;OAEE,CAAA;gBAGV,IACC,kBAAC,GAAD;OAAS,MAAK;OAAS,SAAQ;OAAU,cAAc;OAAG,OAAM;iBAC9D,kBAAC,KAAD;QAAG,WAAU;kBAAkC;QAAiB,CAAA;OACxD,CAAA,GACR,KAAe,EAAY,SAAS,IACtC,kBAAC,OAAD;OAAK,WAAU;iBACZ,KAAK,UAAU,GAAa,MAAM,EAAE;OACjC,CAAA,GACJ,IACF,kBAAC,GAAD;OACE,cAAa;OACb,OAAO,EAAE,mDAAmD,wBAAwB;OACpF,aAAa,EACX,yDACA,+DACD;OACD,CAAA,GAEF,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAEvC,CAAA;MAEE,CAAA;KAKX,CAAC,KACA,kBAAC,GAAD;MACE,MAAK;MACL,UAAS;MACT,cAAc;MACd,OAAO,EAAE,gDAAgD,eAAe;MACxE,MAAM,kBAAC,GAAD,EAAY,WAAU,uBAAwB,CAAA;gBAEpD,kBAAC,MAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,MAAD,EAAA,UACG,EAAE,gDAAgD,8CAA8C,EAC9F,CAAA;QACL,kBAAC,MAAD,EAAA,UAAA;SACG,EAAE,sDAAsD,QAAQ;SAAE;SACnE,kBAAC,UAAD,EAAA,UAAS,EAAE,oDAAoD,YAAY,EAAU,CAAA;SAAC;SACrF,EAAE,sDAAsD,mCAAmC;SACzF,EAAA,CAAA;QACL,kBAAC,MAAD,EAAA,UACG,EACC,gDACA,oEACD,EACE,CAAA;QACL,kBAAC,MAAD,EAAA,UAAA;SACG,EAAE,sDAAsD,SAAS;SAAE;SACpE,kBAAC,UAAD,EAAA,UAAS,EAAE,kDAAkD,UAAU,EAAU,CAAA;SAAC;SACjF,EAAE,sDAAsD,4CAA4C;SAClG,EAAA,CAAA;QACF;;MACG,CAAA;KAKZ,kBAAC,GAAD;MACE,UAAS;MACT,SAAQ;MACR,cAAW;MACX,WAAU;MACV,eAAc;gBALhB,CAOE,kBAAC,GAAD;OAAQ,MAAK;OAAS,SAAS;OAAY,SAAQ;iBAChD,EAAE,+CAA+C,SAAS;OACpD,CAAA,EACT,kBAAC,GAAD;OAAkB,YAAY;OAAgB,gBAAe;iBAC3D,kBAAC,GAAD;QAAQ,MAAK;QAAS,UAAU,CAAC,MAAe;QAAU,SAAQ;kBAC/D,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EAChD,EAAE,+CAA+C,YAAY,CAC7D,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,IAAD,EAAM,WAAU,gBAAiB,CAAA,EAChC,IACG,EAAE,oDAAoD,eAAe,GACrE,EAAE,+CAA+C,kBAAkB,CACtE,EAAA,CAAA;QAEE,CAAA;OACQ,CAAA,CACX;;KACG,EAAA,CAAA;IACV,CAAA;GACH;;EAER"}
@@ -1,19 +1,19 @@
1
1
  import { useDataSinkStore as e } from "../store/dataSinkStore.js";
2
2
  import { useAppNavigate as t } from "../contexts/NavigationContext.js";
3
3
  import "../contexts/index.js";
4
+ import { newWorkflowPath as n } from "../BigConsoleRoutes.js";
4
5
  import "../components/common/sectionStyles.js";
5
- import { Section as n, SectionStack as ee } from "../components/common/Section.js";
6
+ import { Section as r, SectionStack as ee } from "../components/common/Section.js";
6
7
  import "../components/common/index.js";
7
- import { newWorkflowPath as te } from "../BigConsoleRoutes.js";
8
- import ne from "../components/datasink/DataSinkTableViewer.js";
9
- import re from "../hooks/useDataSinkOperations.js";
10
- import { BIGCONSOLE_PERMISSIONS as r } from "../../constants/permissions.js";
11
- import i from "../components/datasink/DataSinkStatusBadge.js";
12
- import { isNative as a, nativeConfirm as ie, nativeImpact as ae, nativeNotify as o } from "../../utils/nativeBridge.js";
13
- import oe from "../hooks/useDataSinkSubscription.js";
14
- import se from "../components/datasink/DataSinkDataViewer.js";
15
- import ce from "../components/datasink/LinkedWorkflowsPanel.js";
16
- import s from "../components/datasink/LinkedParsersPanel.js";
8
+ import te from "../components/datasink/DataSinkTableViewer.js";
9
+ import ne from "../hooks/useDataSinkOperations.js";
10
+ import { BIGCONSOLE_PERMISSIONS as i } from "../../constants/permissions.js";
11
+ import a from "../components/datasink/DataSinkStatusBadge.js";
12
+ import { isNative as o, nativeConfirm as re, nativeImpact as ie, nativeNotify as s } from "../../utils/nativeBridge.js";
13
+ import ae from "../hooks/useDataSinkSubscription.js";
14
+ import oe from "../components/datasink/DataSinkDataViewer.js";
15
+ import se from "../components/datasink/LinkedWorkflowsPanel.js";
16
+ import ce from "../components/datasink/LinkedParsersPanel.js";
17
17
  import le from "../components/datasink/WorkflowStatusPanel.js";
18
18
  import ue from "../components/datasink/ExportDataSinkDialog.js";
19
19
  import de from "../components/datasink/ImportDataGuideDialog.js";
@@ -22,14 +22,14 @@ import { useParams as pe } from "react-router";
22
22
  import { AccessDenied as me, PermissionButton as d } from "@burdenoff/fe-libs/shared/components";
23
23
  import { usePermissions as he } from "@burdenoff/fe-libs/shared/providers/shell";
24
24
  import { AlertCircle as ge, ArrowLeft as f, Code as _e, Database as p, Download as ve, Edit as ye, Eye as be, EyeOff as xe, Grid3X3 as Se, Loader2 as m, RefreshCw as Ce, Trash2 as we, Upload as h } from "lucide-react";
25
- import { useI18n as Te } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
26
- import { Button as g, IllustratedEmptyState as Ee, NextSteps as De, PagePurpose as Oe } from "@burdenoff/fe-libs/ui";
25
+ import { Button as g, IllustratedEmptyState as Te, NextSteps as Ee, PagePurpose as De } from "@burdenoff/fe-libs/ui";
27
26
  import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
27
+ import { useI18n as Oe } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
28
28
  import { formatDistanceToNow as b } from "date-fns";
29
29
  //#region src/bigconsole/pages/DataSinkViewPage.tsx
30
30
  var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function() {
31
- let { dataSinkId: c } = pe(), S = t(), { t: C } = Te(), { hasPermission: ke, isLoading: w } = he(), Ae = ke(r.DATASINK_READ), [T, E] = u(!1), [D, O] = u(!1), [k, A] = u(!1), [je, j] = u(!1), [Me, M] = u(!1), [N, Ne] = u(null), [P, F] = u(!1), [I, L] = u("table"), [R, Pe] = u(100), [z, Fe] = u(0), [B, Ie] = u(null), [Le, Re] = u(!1), V = e((e) => c ? e.dataSinks.get(c) : void 0), { loading: ze, error: H, fetchDataSink: U, getDataSinkData: W, refreshDataSink: G, updateDataSink: K, deleteDataSink: q } = re({ skipFetch: !0 });
32
- oe({
31
+ let { dataSinkId: c } = pe(), S = t(), { t: C } = Oe(), { hasPermission: ke, isLoading: Ae } = he(), je = ke(i.DATASINK_READ), [w, T] = u(!1), [E, D] = u(!1), [O, k] = u(!1), [Me, A] = u(!1), [Ne, j] = u(!1), [M, Pe] = u(null), [N, P] = u(!1), [F, I] = u("table"), [L, Fe] = u(100), [R, Ie] = u(0), [z, B] = u(null), [Le, Re] = u(!1), V = e((e) => c ? e.dataSinks.get(c) : void 0), { loading: ze, error: H, fetchDataSink: U, getDataSinkData: W, refreshDataSink: G, updateDataSink: K, deleteDataSink: q } = ne({ skipFetch: !0 });
32
+ ae({
33
33
  dataSinkId: c,
34
34
  enabled: !!c
35
35
  }), fe(() => {
@@ -41,29 +41,29 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
41
41
  S(`/datasinks/${c}/edit`);
42
42
  }, [S, c]), Y = l(async () => {
43
43
  if (c) {
44
- O(!0);
44
+ D(!0);
45
45
  try {
46
46
  await G({ id: c });
47
47
  } catch {} finally {
48
- O(!1);
48
+ D(!1);
49
49
  }
50
50
  }
51
51
  }, [c, G]), Ve = l(async () => {
52
52
  if (!c) return;
53
- let e = await ie({
53
+ let e = await re({
54
54
  title: C("bigconsole.dataSinks.view.deleteConfirmTitle", "Delete DataSink?"),
55
55
  message: C("bigconsole.dataSinks.view.deleteConfirmMessage", "Are you sure you want to delete this DataSink?"),
56
56
  okButtonTitle: C("bigconsole.dataSinks.view.delete", "Delete"),
57
57
  cancelButtonTitle: C("bigconsole.dataSinks.view.cancel", "Cancel")
58
58
  });
59
59
  if (e === null ? window.confirm(C("bigconsole.dataSinks.view.deleteConfirmMessage", "Are you sure you want to delete this DataSink?")) : e) {
60
- ae("medium"), A(!0);
60
+ ie("medium"), k(!0);
61
61
  try {
62
- await q(c), a() && o("success"), S("/datasinks");
62
+ await q(c), o() && s("success"), S("/datasinks");
63
63
  } catch {
64
- a() && o("error");
64
+ o() && s("error");
65
65
  } finally {
66
- A(!1);
66
+ k(!1);
67
67
  }
68
68
  }
69
69
  }, [
@@ -73,42 +73,42 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
73
73
  C
74
74
  ]), X = l(async (e, t) => {
75
75
  if (c) {
76
- F(!0);
76
+ P(!0);
77
77
  try {
78
78
  let n = await W({ id: c }, {
79
79
  limit: t,
80
80
  offset: e
81
81
  });
82
- n && (Ne(n.raw || {}), Ie(typeof n.totalCount == "number" ? n.totalCount : null), Re(!!n.hasMore), Fe(e));
82
+ n && (Pe(n.raw || {}), B(typeof n.totalCount == "number" ? n.totalCount : null), Re(!!n.hasMore), Ie(e));
83
83
  } catch {} finally {
84
- F(!1);
84
+ P(!1);
85
85
  }
86
86
  }
87
87
  }, [c, W]), Z = l(async () => {
88
88
  if (c) {
89
- if (T) {
90
- E(!1);
89
+ if (w) {
90
+ T(!1);
91
91
  return;
92
92
  }
93
- await X(0, R), E(!0);
93
+ await X(0, L), T(!0);
94
94
  }
95
95
  }, [
96
96
  c,
97
- T,
97
+ w,
98
98
  X,
99
- R
99
+ L
100
100
  ]), He = l((e) => {
101
- Pe(e), X(0, e);
101
+ Fe(e), X(0, e);
102
102
  }, [X]), Q = l(() => {
103
- j(!0);
103
+ A(!0);
104
104
  }, []), Ue = l(() => {
105
- j(!1);
105
+ A(!1);
106
106
  }, []), $ = l(() => {
107
- M(!0);
107
+ j(!0);
108
108
  }, []), We = l(() => {
109
- M(!1);
109
+ j(!1);
110
110
  }, []), Ge = l(() => {
111
- M(!1), S(te());
111
+ j(!1), S(n());
112
112
  }, [S]), Ke = l(async (e) => {
113
113
  if (!V || !await K({
114
114
  id: V.id,
@@ -116,21 +116,21 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
116
116
  data: e
117
117
  })) return !1;
118
118
  try {
119
- await X(0, R), E(!0);
119
+ await X(0, L), T(!0);
120
120
  } catch {}
121
121
  return !0;
122
122
  }, [
123
123
  V,
124
124
  K,
125
125
  X,
126
- R
126
+ L
127
127
  ]);
128
128
  return ze && !V ? /* @__PURE__ */ v("div", {
129
129
  className: "flex min-h-[240px] items-center justify-center p-card-padding",
130
130
  children: /* @__PURE__ */ v(m, { className: "h-icon-lg w-icon-lg animate-spin text-text-secondary" })
131
131
  }) : H && !V ? /* @__PURE__ */ v("div", {
132
132
  className: "p-card-padding",
133
- children: /* @__PURE__ */ v(n, {
133
+ children: /* @__PURE__ */ v(r, {
134
134
  tone: "danger",
135
135
  headingLevel: 2,
136
136
  title: C("bigconsole.dataSinks.view.loadError", "Failed to load DataSink"),
@@ -143,7 +143,7 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
143
143
  children: [/* @__PURE__ */ v(f, { className: "w-4 h-4 mr-2" }), C("bigconsole.dataSinks.view.goBack", "Go back")]
144
144
  })
145
145
  })
146
- }) : !w && !Ae ? /* @__PURE__ */ v(me, {
146
+ }) : !Ae && !je ? /* @__PURE__ */ v(me, {
147
147
  title: C("bigconsole.dataSinks.view.accessDeniedTitle", "Access Denied"),
148
148
  description: C("bigconsole.dataSinks.view.accessDeniedDescription", "You don't have permission to view this data sink.")
149
149
  }) : V ? /* @__PURE__ */ y("div", {
@@ -166,7 +166,7 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
166
166
  children: [/* @__PURE__ */ v("h1", {
167
167
  className: "text-2xl font-bold text-text-primary",
168
168
  children: V.name || C("bigconsole.dataSinks.view.untitled", "Untitled")
169
- }), /* @__PURE__ */ v(i, { status: V.status })]
169
+ }), /* @__PURE__ */ v(a, { status: V.status })]
170
170
  }),
171
171
  /* @__PURE__ */ v("p", {
172
172
  className: "mt-1 break-all font-mono text-sm text-text-muted",
@@ -181,14 +181,14 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
181
181
  className: "flex flex-wrap items-center gap-2",
182
182
  children: [
183
183
  /* @__PURE__ */ v(d, {
184
- permission: r.DATASINK_EXECUTE,
184
+ permission: i.DATASINK_EXECUTE,
185
185
  deniedBehavior: "disable",
186
186
  children: /* @__PURE__ */ y(g, {
187
187
  onClick: Y,
188
- disabled: D,
188
+ disabled: E,
189
189
  variant: "default",
190
190
  size: "default",
191
- children: [/* @__PURE__ */ v(Ce, { className: `w-4 h-4 mr-2 ${D ? "animate-spin" : ""}` }), D ? C("bigconsole.dataSinks.view.refreshing", "Refreshing...") : C("bigconsole.dataSinks.view.refresh", "Refresh")]
191
+ children: [/* @__PURE__ */ v(Ce, { className: `w-4 h-4 mr-2 ${E ? "animate-spin" : ""}` }), E ? C("bigconsole.dataSinks.view.refreshing", "Refreshing...") : C("bigconsole.dataSinks.view.refresh", "Refresh")]
192
192
  })
193
193
  }),
194
194
  /* @__PURE__ */ y(g, {
@@ -204,7 +204,7 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
204
204
  children: [/* @__PURE__ */ v(ve, { className: "w-4 h-4 mr-2" }), C("bigconsole.dataSinks.view.export", "Export")]
205
205
  }),
206
206
  /* @__PURE__ */ v(d, {
207
- permission: r.DATASINK_UPDATE,
207
+ permission: i.DATASINK_UPDATE,
208
208
  deniedBehavior: "disable",
209
209
  children: /* @__PURE__ */ y(g, {
210
210
  onClick: Be,
@@ -214,24 +214,24 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
214
214
  })
215
215
  }),
216
216
  /* @__PURE__ */ v(d, {
217
- permission: r.DATASINK_DELETE,
217
+ permission: i.DATASINK_DELETE,
218
218
  deniedBehavior: "hide",
219
219
  children: /* @__PURE__ */ y(g, {
220
220
  onClick: Ve,
221
- disabled: k,
221
+ disabled: O,
222
222
  variant: "destructive",
223
223
  size: "default",
224
- children: [/* @__PURE__ */ v(we, { className: "w-4 h-4 mr-2" }), k ? C("bigconsole.dataSinks.view.deleting", "Deleting...") : C("bigconsole.dataSinks.view.delete", "Delete")]
224
+ children: [/* @__PURE__ */ v(we, { className: "w-4 h-4 mr-2" }), O ? C("bigconsole.dataSinks.view.deleting", "Deleting...") : C("bigconsole.dataSinks.view.delete", "Delete")]
225
225
  })
226
226
  })
227
227
  ]
228
228
  })]
229
229
  }),
230
- /* @__PURE__ */ v(Oe, {
230
+ /* @__PURE__ */ v(De, {
231
231
  className: "mb-section-gap",
232
232
  children: C("bigconsole.dataSinks.view.pagePurpose", "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.")
233
233
  }),
234
- /* @__PURE__ */ v(De, {
234
+ /* @__PURE__ */ v(Ee, {
235
235
  storageKey: "bigconsole-datasink-view",
236
236
  steps: [
237
237
  {
@@ -265,7 +265,7 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
265
265
  children: [/* @__PURE__ */ y("div", {
266
266
  className: "grid min-w-0 gap-section-gap md:grid-cols-2",
267
267
  children: [
268
- /* @__PURE__ */ v(n, {
268
+ /* @__PURE__ */ v(r, {
269
269
  headingLevel: 2,
270
270
  title: C("bigconsole.dataSinks.view.details", "Details"),
271
271
  description: "Cache, refresh and storage settings for this sink.",
@@ -277,7 +277,7 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
277
277
  children: [/* @__PURE__ */ v("dt", {
278
278
  className: "min-w-0 text-text-secondary",
279
279
  children: C("bigconsole.dataSinks.view.status", "Status")
280
- }), /* @__PURE__ */ v("dd", { children: /* @__PURE__ */ v(i, {
280
+ }), /* @__PURE__ */ v("dd", { children: /* @__PURE__ */ v(a, {
281
281
  status: V.status,
282
282
  size: "sm"
283
283
  }) })]
@@ -352,55 +352,55 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
352
352
  setupState: V.setupState,
353
353
  status: V.status,
354
354
  onRerunWorkflow: Y,
355
- disabled: D,
356
- isRerunning: D
355
+ disabled: E,
356
+ isRerunning: E
357
357
  }),
358
- /* @__PURE__ */ v(ce, { workflowLinks: V.workflowLinks }),
359
- /* @__PURE__ */ v(s, { dataSinkKey: V.key })
358
+ /* @__PURE__ */ v(se, { workflowLinks: V.workflowLinks }),
359
+ /* @__PURE__ */ v(ce, { dataSinkKey: V.key })
360
360
  ]
361
- }), /* @__PURE__ */ y(n, {
361
+ }), /* @__PURE__ */ y(r, {
362
362
  emphasis: "primary",
363
363
  headingLevel: 2,
364
364
  title: C("bigconsole.dataSinks.view.dataPreview", "Data Preview"),
365
365
  description: "The rows this sink is currently serving to dashboards.",
366
366
  icon: /* @__PURE__ */ v(p, { className: "h-icon-sm w-icon-sm" }),
367
- actions: /* @__PURE__ */ y(_, { children: [T && /* @__PURE__ */ y("div", {
367
+ actions: /* @__PURE__ */ y(_, { children: [w && /* @__PURE__ */ y("div", {
368
368
  role: "group",
369
369
  "aria-label": C("bigconsole.dataSinks.view.dataPreview", "Data Preview"),
370
370
  className: "flex items-center overflow-hidden rounded-button border border-[var(--color-border-default)]",
371
371
  children: [/* @__PURE__ */ v("button", {
372
372
  type: "button",
373
- onClick: () => L("table"),
374
- "aria-pressed": I === "table",
375
- className: `flex h-control-md w-[var(--size-controlHeight-md)] items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface ${x} ${I === "table" ? "bg-bg-sunken text-text-primary" : "text-text-secondary hover:text-text-primary"}`,
373
+ onClick: () => I("table"),
374
+ "aria-pressed": F === "table",
375
+ className: `flex h-control-md w-[var(--size-controlHeight-md)] items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface ${x} ${F === "table" ? "bg-bg-sunken text-text-primary" : "text-text-secondary hover:text-text-primary"}`,
376
376
  title: C("bigconsole.dataSinks.view.tableView", "Table view"),
377
377
  children: /* @__PURE__ */ v(Se, { className: "w-4 h-4" })
378
378
  }), /* @__PURE__ */ v("button", {
379
379
  type: "button",
380
- onClick: () => L("json"),
381
- "aria-pressed": I === "json",
382
- className: `flex h-control-md w-[var(--size-controlHeight-md)] items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface ${x} ${I === "json" ? "bg-bg-sunken text-text-primary" : "text-text-secondary hover:text-text-primary"}`,
380
+ onClick: () => I("json"),
381
+ "aria-pressed": F === "json",
382
+ className: `flex h-control-md w-[var(--size-controlHeight-md)] items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface ${x} ${F === "json" ? "bg-bg-sunken text-text-primary" : "text-text-secondary hover:text-text-primary"}`,
383
383
  title: C("bigconsole.dataSinks.view.jsonView", "JSON view"),
384
384
  children: /* @__PURE__ */ v(_e, { className: "w-4 h-4" })
385
385
  })]
386
386
  }), /* @__PURE__ */ v(g, {
387
387
  onClick: Z,
388
- disabled: P,
389
- variant: T ? "secondary" : "default",
388
+ disabled: N,
389
+ variant: w ? "secondary" : "default",
390
390
  size: "sm",
391
- children: P ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(m, { className: "w-4 h-4 mr-2 animate-spin" }), C("bigconsole.dataSinks.view.loading", "Loading...")] }) : T ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(xe, { className: "w-4 h-4 mr-2" }), C("bigconsole.dataSinks.view.hideData", "Hide Data")] }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(be, { className: "w-4 h-4 mr-2" }), C("bigconsole.dataSinks.view.showData", "Show Data")] })
391
+ children: N ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(m, { className: "w-4 h-4 mr-2 animate-spin" }), C("bigconsole.dataSinks.view.loading", "Loading...")] }) : w ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(xe, { className: "w-4 h-4 mr-2" }), C("bigconsole.dataSinks.view.hideData", "Hide Data")] }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(be, { className: "w-4 h-4 mr-2" }), C("bigconsole.dataSinks.view.showData", "Show Data")] })
392
392
  })] }),
393
- children: [T && N && /* @__PURE__ */ y(_, { children: [v(I === "table" ? ne : se, {
394
- data: N,
393
+ children: [w && M && /* @__PURE__ */ y(_, { children: [v(F === "table" ? te : oe, {
394
+ data: M,
395
395
  maxHeight: "400px"
396
- }), B !== null && /* @__PURE__ */ y("div", {
396
+ }), z !== null && /* @__PURE__ */ y("div", {
397
397
  className: "mt-form-gap flex flex-wrap items-center justify-between gap-form-gap border-t border-[var(--color-border-subtle)] pt-form-gap",
398
398
  children: [/* @__PURE__ */ y("label", {
399
399
  className: "flex items-center gap-2 text-sm text-text-secondary",
400
400
  children: [/* @__PURE__ */ v("span", { children: C("bigconsole.dataSinks.view.rowsPerPage", "Rows per page") }), /* @__PURE__ */ v("select", {
401
401
  "aria-label": C("bigconsole.dataSinks.view.rowsPerPage", "Rows per page"),
402
402
  className: "h-control-md rounded-input border border-[var(--color-border-default)] bg-bg-canvas px-2 text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface",
403
- value: R,
403
+ value: L,
404
404
  onChange: (e) => He(Number(e.target.value)),
405
405
  children: [
406
406
  100,
@@ -414,28 +414,28 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
414
414
  }), /* @__PURE__ */ y("div", {
415
415
  className: "flex flex-wrap items-center gap-control-gap text-sm text-text-secondary",
416
416
  children: [
417
- /* @__PURE__ */ v("span", { children: B === 0 ? C("bigconsole.dataSinks.view.zeroRows", "0 rows") : C("bigconsole.dataSinks.view.rowRange", "{{start}}–{{end}} of {{total}}", {
418
- start: z + 1,
419
- end: Math.min(z + R, B),
420
- total: B
417
+ /* @__PURE__ */ v("span", { children: z === 0 ? C("bigconsole.dataSinks.view.zeroRows", "0 rows") : C("bigconsole.dataSinks.view.rowRange", "{{start}}–{{end}} of {{total}}", {
418
+ start: R + 1,
419
+ end: Math.min(R + L, z),
420
+ total: z
421
421
  }) }),
422
422
  /* @__PURE__ */ v(g, {
423
423
  variant: "secondary",
424
424
  size: "sm",
425
- disabled: z === 0 || P,
426
- onClick: () => void X(Math.max(0, z - R), R),
425
+ disabled: R === 0 || N,
426
+ onClick: () => void X(Math.max(0, R - L), L),
427
427
  children: C("bigconsole.dataSinks.view.previous", "Previous")
428
428
  }),
429
429
  /* @__PURE__ */ v(g, {
430
430
  variant: "secondary",
431
431
  size: "sm",
432
- disabled: !Le || P,
433
- onClick: () => void X(z + R, R),
432
+ disabled: !Le || N,
433
+ onClick: () => void X(R + L, L),
434
434
  children: C("bigconsole.dataSinks.view.next", "Next")
435
435
  })
436
436
  ]
437
437
  })]
438
- })] }), !T && (V.data && Object.keys(V.data).length > 0 ? /* @__PURE__ */ y("p", {
438
+ })] }), !w && (V.data && Object.keys(V.data).length > 0 ? /* @__PURE__ */ y("p", {
439
439
  className: "flex items-center gap-icon-gap text-sm text-text-secondary",
440
440
  children: [/* @__PURE__ */ v(p, {
441
441
  "aria-hidden": "true",
@@ -463,13 +463,13 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
463
463
  })]
464
464
  }),
465
465
  /* @__PURE__ */ v(ue, {
466
- isOpen: je,
466
+ isOpen: Me,
467
467
  dataSinkId: c || null,
468
468
  dataSinkName: V.name || V.key,
469
469
  onClose: Ue
470
470
  }),
471
471
  /* @__PURE__ */ v(de, {
472
- isOpen: Me,
472
+ isOpen: Ne,
473
473
  dataSinkName: V.name || V.key,
474
474
  hasExistingData: !!(V.data && Object.keys(V.data).length > 0),
475
475
  onClose: We,
@@ -479,7 +479,7 @@ var x = "focus-visible:ring-inset focus-visible:ring-offset-0", S = c(function()
479
479
  ]
480
480
  }) : /* @__PURE__ */ v("div", {
481
481
  className: "flex min-h-[240px] items-center justify-center p-card-padding",
482
- children: /* @__PURE__ */ v(Ee, {
482
+ children: /* @__PURE__ */ v(Te, {
483
483
  illustration: "empty-data",
484
484
  title: C("bigconsole.dataSinks.view.notFoundTitle", "DataSink not found"),
485
485
  description: C("bigconsole.dataSinks.view.notFoundDescription", "The DataSink you're looking for doesn't exist or has been deleted."),