@burdenoff/microfe-bigconsole 2026.924.2 → 2026.925.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/BigConsoleRoot.js +94 -84
- package/dist/bigconsole/BigConsoleRoot.js.map +1 -1
- package/dist/bigconsole/BigConsoleRoutes.js +137 -138
- package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
- package/dist/bigconsole/assistant/previewController.js +7 -7
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js +17 -17
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +32 -32
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +52 -52
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +7 -7
- package/dist/bigconsole/components/dashboard/DashboardStatusPill.js +53 -0
- package/dist/bigconsole/components/dashboard/DashboardStatusPill.js.map +1 -0
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +60 -60
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +21 -21
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +30 -30
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +36 -36
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +34 -34
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +38 -38
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +121 -121
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +22 -22
- package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +46 -46
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +53 -53
- package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -1
- package/dist/bigconsole/components/dashboard/WidgetPalette.js +47 -47
- package/dist/bigconsole/components/dashboard/index.js +1 -0
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js +35 -35
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +18 -18
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +20 -20
- package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +30 -29
- package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +48 -48
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +126 -114
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +35 -35
- package/dist/bigconsole/components/overview/DashboardGuide.js +214 -0
- package/dist/bigconsole/components/overview/DashboardGuide.js.map +1 -0
- package/dist/bigconsole/components/overview/DataHealthCard.js +97 -0
- package/dist/bigconsole/components/overview/DataHealthCard.js.map +1 -0
- package/dist/bigconsole/components/overview/OverviewQuickActions.js +32 -0
- package/dist/bigconsole/components/overview/OverviewQuickActions.js.map +1 -0
- package/dist/bigconsole/components/overview/OverviewStatCard.js +57 -0
- package/dist/bigconsole/components/overview/OverviewStatCard.js.map +1 -0
- package/dist/bigconsole/components/overview/RecentDashboardsPanel.js +107 -0
- package/dist/bigconsole/components/overview/RecentDashboardsPanel.js.map +1 -0
- package/dist/bigconsole/components/overview/dashboardGuideSteps.js +119 -0
- package/dist/bigconsole/components/overview/dashboardGuideSteps.js.map +1 -0
- package/dist/bigconsole/components/overview/index.js +8 -0
- package/dist/bigconsole/components/overview/overviewModel.js +119 -0
- package/dist/bigconsole/components/overview/overviewModel.js.map +1 -0
- package/dist/bigconsole/components/overview/useOverviewData.js +48 -0
- package/dist/bigconsole/components/overview/useOverviewData.js.map +1 -0
- package/dist/bigconsole/components/parser/DataSinkSelector.js +40 -40
- package/dist/bigconsole/components/parser/ImportParserDialog.js +127 -127
- package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +51 -51
- package/dist/bigconsole/components/preview/AiPreviewContent.js +5 -5
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +32 -32
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +37 -37
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js +13 -13
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +70 -70
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +28 -28
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +25 -25
- package/dist/bigconsole/components/widgets/chart/ChartConfig.js +48 -48
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js +46 -46
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +61 -61
- package/dist/bigconsole/components/widgets/form/FormWidget.js +36 -36
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +42 -42
- package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +25 -25
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +21 -21
- package/dist/bigconsole/components/widgets/list/ListConfig.js +30 -30
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +38 -38
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +9 -9
- package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +9 -9
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +49 -49
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +39 -39
- package/dist/bigconsole/components/widgets/table/TableConfig.js +73 -73
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +64 -64
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +10 -10
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +68 -68
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
- package/dist/bigconsole/hooks/useDataSinkOperations.js +92 -89
- package/dist/bigconsole/hooks/useDataSinkOperations.js.map +1 -1
- package/dist/bigconsole/hooks/useImportDataSink.js +96 -91
- package/dist/bigconsole/hooks/useImportDataSink.js.map +1 -1
- package/dist/bigconsole/pages/AdminPage.js +136 -136
- package/dist/bigconsole/pages/DashboardBuilderPage.js +10 -10
- package/dist/bigconsole/pages/DashboardViewPage.js +61 -61
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +327 -348
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataParsersPage.js +5 -5
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +82 -80
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +83 -83
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinksPage.js +11 -11
- package/dist/bigconsole/pages/DataSourcesPage.js +5 -5
- package/dist/bigconsole/pages/InsightsPage.js +315 -315
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/dist/bigconsole/pages/OverviewPage.js +244 -0
- package/dist/bigconsole/pages/OverviewPage.js.map +1 -0
- package/dist/bigconsole/pages/ParserBuilderPage.js +5 -5
- package/dist/bigconsole/pages/ParserViewPage.js +98 -98
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +8 -8
- package/dist/bigconsole/pages/PipelineViewPage.js +46 -46
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelinesPage.js +80 -80
- package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
- package/dist/bigconsole/pages/SettingsPage.js +70 -70
- package/dist/bigconsole/pages/WidgetDemoPage.js +42 -42
- package/dist/bigconsole/pages/WorkflowViewPage.js +200 -200
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +78 -78
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/dist/bigconsole/providers/BigConsoleQuotaProvider.js +36 -35
- package/dist/bigconsole/providers/BigConsoleQuotaProvider.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +34 -34
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +79 -79
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js +46 -46
- package/dist/bigconsole/utils/createDialogParam.js +14 -0
- package/dist/bigconsole/utils/createDialogParam.js.map +1 -0
- package/dist/bigconsole/utils/quota.js +51 -23
- package/dist/bigconsole/utils/quota.js.map +1 -1
- package/dist/generated/global-operations.js +1 -1
- package/dist/generated/wspace-operations.js +285 -222
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/shared/hooks/useTr.js +10 -7
- package/dist/shared/hooks/useTr.js.map +1 -1
- package/package.json +1 -1
- package/dist/bigconsole/pages/HomePage.js +0 -318
- 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 "Show data" 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';\nimport { reportBigConsoleQuotaError } from '../utils/quota';\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 // A quota refusal opens the billing dialog (for a new org: \"Set up\n // billing\", BOFF-7913) instead of a dead-end error banner. The form\n // keeps its values so the user can retry once billing is set up.\n if (reportBigConsoleQuotaError(err)) {\n return;\n }\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 "Show data" 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,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
|
|
6
|
+
import { Section as r, SectionStack as ee } from "../components/common/Section.js";
|
|
6
7
|
import "../components/common/index.js";
|
|
7
|
-
import
|
|
8
|
-
import ne from "../
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import oe from "../
|
|
14
|
-
import se from "../components/datasink/
|
|
15
|
-
import ce from "../components/datasink/
|
|
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 {
|
|
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 } =
|
|
32
|
-
|
|
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
|
-
|
|
44
|
+
D(!0);
|
|
45
45
|
try {
|
|
46
46
|
await G({ id: c });
|
|
47
47
|
} catch {} finally {
|
|
48
|
-
|
|
48
|
+
D(!1);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}, [c, G]), Ve = l(async () => {
|
|
52
52
|
if (!c) return;
|
|
53
|
-
let e = await
|
|
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
|
-
|
|
60
|
+
ie("medium"), k(!0);
|
|
61
61
|
try {
|
|
62
|
-
await q(c),
|
|
62
|
+
await q(c), o() && s("success"), S("/datasinks");
|
|
63
63
|
} catch {
|
|
64
|
-
|
|
64
|
+
o() && s("error");
|
|
65
65
|
} finally {
|
|
66
|
-
|
|
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
|
-
|
|
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 && (
|
|
82
|
+
n && (Pe(n.raw || {}), B(typeof n.totalCount == "number" ? n.totalCount : null), Re(!!n.hasMore), Ie(e));
|
|
83
83
|
} catch {} finally {
|
|
84
|
-
|
|
84
|
+
P(!1);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
}, [c, W]), Z = l(async () => {
|
|
88
88
|
if (c) {
|
|
89
|
-
if (
|
|
90
|
-
|
|
89
|
+
if (w) {
|
|
90
|
+
T(!1);
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
|
-
await X(0,
|
|
93
|
+
await X(0, L), T(!0);
|
|
94
94
|
}
|
|
95
95
|
}, [
|
|
96
96
|
c,
|
|
97
|
-
|
|
97
|
+
w,
|
|
98
98
|
X,
|
|
99
|
-
|
|
99
|
+
L
|
|
100
100
|
]), He = l((e) => {
|
|
101
|
-
|
|
101
|
+
Fe(e), X(0, e);
|
|
102
102
|
}, [X]), Q = l(() => {
|
|
103
|
-
|
|
103
|
+
A(!0);
|
|
104
104
|
}, []), Ue = l(() => {
|
|
105
|
-
|
|
105
|
+
A(!1);
|
|
106
106
|
}, []), $ = l(() => {
|
|
107
|
-
|
|
107
|
+
j(!0);
|
|
108
108
|
}, []), We = l(() => {
|
|
109
|
-
|
|
109
|
+
j(!1);
|
|
110
110
|
}, []), Ge = l(() => {
|
|
111
|
-
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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
|
-
}) : !
|
|
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(
|
|
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:
|
|
184
|
+
permission: i.DATASINK_EXECUTE,
|
|
185
185
|
deniedBehavior: "disable",
|
|
186
186
|
children: /* @__PURE__ */ y(g, {
|
|
187
187
|
onClick: Y,
|
|
188
|
-
disabled:
|
|
188
|
+
disabled: E,
|
|
189
189
|
variant: "default",
|
|
190
190
|
size: "default",
|
|
191
|
-
children: [/* @__PURE__ */ v(Ce, { className: `w-4 h-4 mr-2 ${
|
|
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:
|
|
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:
|
|
217
|
+
permission: i.DATASINK_DELETE,
|
|
218
218
|
deniedBehavior: "hide",
|
|
219
219
|
children: /* @__PURE__ */ y(g, {
|
|
220
220
|
onClick: Ve,
|
|
221
|
-
disabled:
|
|
221
|
+
disabled: O,
|
|
222
222
|
variant: "destructive",
|
|
223
223
|
size: "default",
|
|
224
|
-
children: [/* @__PURE__ */ v(we, { className: "w-4 h-4 mr-2" }),
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
356
|
-
isRerunning:
|
|
355
|
+
disabled: E,
|
|
356
|
+
isRerunning: E
|
|
357
357
|
}),
|
|
358
|
-
/* @__PURE__ */ v(
|
|
359
|
-
/* @__PURE__ */ v(
|
|
358
|
+
/* @__PURE__ */ v(se, { workflowLinks: V.workflowLinks }),
|
|
359
|
+
/* @__PURE__ */ v(ce, { dataSinkKey: V.key })
|
|
360
360
|
]
|
|
361
|
-
}), /* @__PURE__ */ y(
|
|
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: [
|
|
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: () =>
|
|
374
|
-
"aria-pressed":
|
|
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} ${
|
|
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: () =>
|
|
381
|
-
"aria-pressed":
|
|
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} ${
|
|
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:
|
|
389
|
-
variant:
|
|
388
|
+
disabled: N,
|
|
389
|
+
variant: w ? "secondary" : "default",
|
|
390
390
|
size: "sm",
|
|
391
|
-
children:
|
|
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: [
|
|
394
|
-
data:
|
|
393
|
+
children: [w && M && /* @__PURE__ */ y(_, { children: [v(F === "table" ? te : oe, {
|
|
394
|
+
data: M,
|
|
395
395
|
maxHeight: "400px"
|
|
396
|
-
}),
|
|
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:
|
|
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:
|
|
418
|
-
start:
|
|
419
|
-
end: Math.min(
|
|
420
|
-
total:
|
|
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:
|
|
426
|
-
onClick: () => void X(Math.max(0,
|
|
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 ||
|
|
433
|
-
onClick: () => void X(
|
|
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
|
-
})] }), !
|
|
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:
|
|
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:
|
|
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(
|
|
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."),
|