@burdenoff/microfe-bigconsole 2026.625.1 → 2026.626.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"DataSinksPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinksPage.tsx"],"sourcesContent":["/**\n * DataSinksPage\n *\n * List and manage DataSinks - fetches data from backend.\n * Uses fe-libs shared UI components and semantic tokens for consistent styling.\n */\n\nimport { type FC, memo, useCallback, useMemo, useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getProfileScopedKey } from '@burdenoff/fe-libs/shared/utils';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeNotify } from '../../utils/nativeBridge';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { useExportDataSink } from '../hooks/useExportDataSink';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport type { DataSink, DataSinkSetupState } from '../types';\nimport { DataSinkCard } from '../components/datasink/DataSinkCard';\nimport { DataSinkDeleteDialog } from '../components/datasink/DataSinkDeleteDialog';\nimport { DataSinkRefreshDialog } from '../components/datasink/DataSinkRefreshDialog';\nimport { ImportDataSinkDialog } from '../components/datasink/ImportDataSinkDialog';\n\n// fe-libs components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport {\n AuroraBackground,\n GradientText,\n GlassCard,\n IllustratedEmptyState,\n CTAOverflowMenu,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport {\n Plus,\n Search,\n AlertCircle,\n Container,\n X,\n Upload,\n LayoutGrid,\n Table2,\n Eye,\n Pencil,\n RefreshCw,\n Download,\n Trash2,\n} from 'lucide-react';\nimport { DataSinkStatusBadge } from '../components/datasink/DataSinkStatusBadge';\nimport { SetupStateBadge } from '../components/datasink/SetupStateBadge';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype SetupStateFilter = 'all' | 'in_progress' | 'completed';\ntype ListViewMode = 'grid' | 'table';\n\nconst VIEW_MODE_KEY = 'bigconsole-datasinks-view-mode';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Determine setup state for a DataSink based on its properties.\n */\nconst getSetupState = (dataSink: DataSink): DataSinkSetupState => {\n if (dataSink.setupState === 'completed') {\n return 'completed';\n }\n if (dataSink.ttl !== null && dataSink.ttl !== undefined && dataSink.ttl > 0) {\n return 'completed';\n }\n if (dataSink.fluidGridsWorkflowId && dataSink.data && Object.keys(dataSink.data).length > 0) {\n return 'completed';\n }\n if (dataSink.fluidGridsWorkflowId) {\n return 'workflow_running';\n }\n return 'workflow_pending';\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DataSinksPage: FC = memo(function DataSinksPage() {\n const navigate = useAppNavigate();\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n\n // Local state\n const [setupStateFilter, setSetupStateFilter] = useState<SetupStateFilter>('all');\n const [isImportDialogOpen, setIsImportDialogOpen] = useState(false);\n const [viewMode, setViewMode] = useState<ListViewMode>(\n () => (localStorage.getItem(getProfileScopedKey(VIEW_MODE_KEY)) as ListViewMode) || 'grid'\n );\n\n const toggleViewMode = useCallback((mode: ListViewMode) => {\n setViewMode(mode);\n localStorage.setItem(getProfileScopedKey(VIEW_MODE_KEY), mode);\n }, []);\n\n // Store state\n const statusFilter = useDataSinkStore((state) => state.statusFilter);\n const activeFilter = useDataSinkStore((state) => state.activeFilter);\n const searchQuery = useDataSinkStore((state) => state.searchQuery);\n const setStatusFilter = useDataSinkStore((state) => state.setStatusFilter);\n const setActiveFilter = useDataSinkStore((state) => state.setActiveFilter);\n const setSearchQuery = useDataSinkStore((state) => state.setSearchQuery);\n const deleteDialogOpen = useDataSinkStore((state) => state.deleteDialogOpen);\n const refreshDialogOpen = useDataSinkStore((state) => state.refreshDialogOpen);\n const currentDataSink = useDataSinkStore((state) => state.currentDataSink);\n const openDeleteDialog = useDataSinkStore((state) => state.openDeleteDialog);\n const closeDeleteDialog = useDataSinkStore((state) => state.closeDeleteDialog);\n const openRefreshDialog = useDataSinkStore((state) => state.openRefreshDialog);\n const closeRefreshDialog = useDataSinkStore((state) => state.closeRefreshDialog);\n\n // Data operations\n const { loading, error, dataSinks, deleteDataSink, refreshDataSink, refetch } = useDataSinkOperations();\n\n // Export hook\n const { downloadDataSink } = useExportDataSink();\n\n // Compute filtered DataSinks with setup states\n const dataSinksWithSetupState = useMemo(() => {\n return dataSinks.map((ds) => ({\n dataSink: ds,\n setupState: getSetupState(ds),\n }));\n }, [dataSinks]);\n\n // Compute filtered DataSinks\n const filteredDataSinks = useMemo(() => {\n let filtered = [...dataSinksWithSetupState];\n\n if (setupStateFilter === 'in_progress') {\n filtered = filtered.filter((item) => item.setupState !== 'completed');\n } else if (setupStateFilter === 'completed') {\n filtered = filtered.filter((item) => item.setupState === 'completed');\n }\n\n if (statusFilter !== null) {\n filtered = filtered.filter((item) => item.dataSink.status === statusFilter);\n }\n\n if (activeFilter !== null) {\n filtered = filtered.filter((item) => item.dataSink.isActive === activeFilter);\n }\n\n if (searchQuery.trim()) {\n const query = searchQuery.toLowerCase();\n filtered = filtered.filter(\n (item) =>\n item.dataSink.name?.toLowerCase().includes(query) ||\n item.dataSink.key.toLowerCase().includes(query) ||\n item.dataSink.description?.toLowerCase().includes(query)\n );\n }\n\n return filtered.sort((a, b) => new Date(b.dataSink.updatedAt).getTime() - new Date(a.dataSink.updatedAt).getTime());\n }, [dataSinksWithSetupState, setupStateFilter, statusFilter, activeFilter, searchQuery]);\n\n // Handlers\n const handleCreateDataSink = useCallback(() => {\n navigate('/datasinks/new');\n }, [navigate]);\n\n const handleViewDataSink = useCallback(\n (id: string) => {\n navigate(`/datasinks/${id}`);\n },\n [navigate]\n );\n\n const handleEditDataSink = useCallback(\n (id: string) => {\n navigate(`/datasinks/${id}/edit`);\n },\n [navigate]\n );\n\n const handleRefreshClick = useCallback(\n (id: string) => {\n const dataSink = dataSinks.find((ds) => ds.id === id);\n if (dataSink) {\n openRefreshDialog(dataSink);\n }\n },\n [dataSinks, openRefreshDialog]\n );\n\n const handleRefreshConfirm = useCallback(\n async (forceRefresh: boolean) => {\n if (currentDataSink) {\n await refreshDataSink({\n id: currentDataSink.id,\n forceRefresh,\n });\n closeRefreshDialog();\n }\n },\n [currentDataSink, refreshDataSink, closeRefreshDialog]\n );\n\n const handleDeleteClick = useCallback(\n (id: string) => {\n const dataSink = dataSinks.find((ds) => ds.id === id);\n if (dataSink) {\n openDeleteDialog(dataSink);\n }\n },\n [dataSinks, openDeleteDialog]\n );\n\n const handleDeleteConfirm = useCallback(async () => {\n if (currentDataSink) {\n try {\n await deleteDataSink(currentDataSink.id);\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n closeDeleteDialog();\n }\n }, [currentDataSink, deleteDataSink, closeDeleteDialog]);\n\n const handleExportDataSink = useCallback(\n async (id: string) => {\n const dataSink = dataSinks.find((ds) => ds.id === id);\n if (dataSink) {\n await downloadDataSink(id, dataSink.name || dataSink.key);\n }\n },\n [dataSinks, downloadDataSink]\n );\n\n const handleImportDataSink = useCallback(() => {\n setIsImportDialogOpen(true);\n }, []);\n\n const handleImportSuccess = useCallback(\n (_dataSinkId: string, _dataSinkName: string | null, _wasUpdate: boolean) => {\n // Refetch DataSinks to show the newly imported one\n refetch();\n },\n [refetch]\n );\n\n const hasActiveFilters = useMemo(() => {\n return setupStateFilter !== 'all' || statusFilter !== null || activeFilter !== null || searchQuery.trim() !== '';\n }, [setupStateFilter, statusFilter, activeFilter, searchQuery]);\n\n const handleClearFilters = useCallback(() => {\n setSetupStateFilter('all');\n setStatusFilter(null);\n setActiveFilter(null);\n setSearchQuery('');\n }, [setStatusFilter, setActiveFilter, setSearchQuery]);\n\n // Setup state filter options\n const setupStateOptions: { value: SetupStateFilter; label: string }[] = [\n { value: 'all', label: tr('bigconsole.dataSinks.filters.setupState.all', 'All') },\n { value: 'in_progress', label: tr('bigconsole.dataSinks.filters.setupState.inProgress', 'In Progress') },\n { value: 'completed', label: tr('bigconsole.dataSinks.filters.setupState.completed', 'Completed') },\n ];\n\n // Error state\n if (error) {\n return (\n <div style={{ padding: 'var(--space-pagePadding)' }}>\n <Card\n style={{\n borderColor: 'var(--color-status-error-border)',\n backgroundColor: 'var(--color-status-error-bgSubtle)',\n }}\n >\n <CardContent style={{ padding: 'var(--space-pagePadding)' }}>\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>\n <div\n style={{\n padding: 'var(--space-controlGap)',\n borderRadius: '50%',\n backgroundColor: 'var(--color-status-error-bgSubtle)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <AlertCircle\n style={{\n height: 'var(--size-icon-xl)',\n width: 'var(--size-icon-xl)',\n color: 'var(--color-status-error-text)',\n }}\n />\n </div>\n <h3\n style={{\n fontSize: 'var(--font-size-body-lg)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-status-error-text)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n {tr('bigconsole.dataSinks.error.title', 'Failed to load Data Sinks')}\n </h3>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n {error.message}\n </p>\n <Button size=\"sm\" onClick={() => refetch()}>\n {tr('bigconsole.dataSinks.error.retry', 'Try Again')}\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n }\n\n return (\n <div style={{ minHeight: '100vh', backgroundColor: 'var(--color-bg-canvas)' }}>\n {/* Header Section */}\n <div className=\"relative overflow-hidden border-b border-border-seam bg-gloss-card\">\n <AuroraBackground intensity=\"subtle\" />\n <div style={{ padding: 'var(--space-pagePadding)' }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-formGap)' }}>\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-card)',\n backgroundColor: 'var(--color-action-primary-bg)',\n }}\n >\n <Container\n style={{\n height: 'var(--size-icon-lg)',\n width: 'var(--size-icon-lg)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n </div>\n <div>\n <h1\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n margin: 0,\n color: 'var(--color-text-primary)',\n }}\n >\n <GradientText>{tr('bigconsole.dataSinks.title', 'Data Sinks')}</GradientText>\n </h1>\n <p style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-secondary)', margin: 0 }}>\n {tr('bigconsole.dataSinks.subtitle', 'Manage pre-processed data from FluidGrids workflows')}\n </p>\n </div>\n </div>\n\n <CTAOverflowMenu\n primary={{\n label: tr('bigconsole.dataSinks.actions.create', 'Create Data Sink'),\n onSelect: handleCreateDataSink,\n icon: (\n <Plus\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n ),\n }}\n actions={[\n {\n label: tr('bigconsole.dataSinks.actions.import', 'Import'),\n onSelect: handleImportDataSink,\n icon: <Upload style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />,\n },\n ]}\n />\n </div>\n </div>\n </div>\n\n {/* Main Content */}\n <div style={{ padding: 'var(--space-pagePadding)' }}>\n <PagePurpose className=\"mb-[var(--space-sectionGap)]\">\n Data sinks are the structured destinations that hold your processed data — define a\n schema once and every pipeline, parser, and workflow can write into it. Dashboards then\n query sinks directly, so a sink is where your data lives and where your charts read from.\n </PagePurpose>\n\n {/* Setup State Filter Tabs */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '2px',\n padding: '4px',\n backgroundColor: 'var(--color-bg-sunken)',\n borderRadius: 'var(--radius-card)',\n width: 'fit-content',\n }}\n >\n {setupStateOptions.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => setSetupStateFilter(option.value)}\n style={{\n padding: 'var(--space-controlGap) var(--space-formGap)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n backgroundColor: setupStateFilter === option.value ? 'var(--color-bg-surface)' : 'transparent',\n color:\n setupStateFilter === option.value ? 'var(--color-text-primary)' : 'var(--color-text-secondary)',\n boxShadow: setupStateFilter === option.value ? '0 1px 2px rgba(0,0,0,0.05)' : 'none',\n }}\n >\n {option.label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Search and Filters Row */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n marginBottom: 'var(--space-sectionGap)',\n flexWrap: 'wrap',\n }}\n >\n {/* Search Bar */}\n <div style={{ position: 'relative', maxWidth: '400px', flex: 1 }}>\n <Search\n style={{\n position: 'absolute',\n left: 'var(--space-controlGap)',\n top: '50%',\n transform: 'translateY(-50%)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-text-muted)',\n }}\n />\n <Input\n type=\"text\"\n placeholder={tr('bigconsole.dataSinks.search.placeholder', 'Search data sinks...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={{ paddingLeft: 'var(--size-controlHeight-md)', height: 'var(--size-controlHeight-md)' }}\n />\n </div>\n\n {/* Status Filter */}\n <Select\n value={statusFilter || 'all'}\n onValueChange={(value) =>\n setStatusFilter(value === 'all' ? null : (value as 'ready' | 'error' | 'stale' | 'refreshing'))\n }\n >\n <SelectTrigger style={{ width: '150px' }}>\n <SelectValue placeholder={tr('bigconsole.dataSinks.filters.status.placeholder', 'All Statuses')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{tr('bigconsole.dataSinks.filters.status.all', 'All Statuses')}</SelectItem>\n <SelectItem value=\"ready\">{tr('bigconsole.dataSinks.filters.status.ready', 'Ready')}</SelectItem>\n <SelectItem value=\"error\">{tr('bigconsole.dataSinks.filters.status.error', 'Error')}</SelectItem>\n <SelectItem value=\"stale\">{tr('bigconsole.dataSinks.filters.status.stale', 'Stale')}</SelectItem>\n <SelectItem value=\"refreshing\">\n {tr('bigconsole.dataSinks.filters.status.refreshing', 'Refreshing')}\n </SelectItem>\n </SelectContent>\n </Select>\n\n {/* Active Filter */}\n <Select\n value={activeFilter === null ? 'all' : activeFilter ? 'true' : 'false'}\n onValueChange={(value) => setActiveFilter(value === 'all' ? null : value === 'true')}\n >\n <SelectTrigger style={{ width: '130px' }}>\n <SelectValue placeholder={tr('bigconsole.dataSinks.filters.active.placeholder', 'All')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{tr('bigconsole.dataSinks.filters.active.all', 'All')}</SelectItem>\n <SelectItem value=\"true\">{tr('bigconsole.dataSinks.filters.active.active', 'Active')}</SelectItem>\n <SelectItem value=\"false\">{tr('bigconsole.dataSinks.filters.active.inactive', 'Inactive')}</SelectItem>\n </SelectContent>\n </Select>\n\n {/* Clear Filters */}\n {hasActiveFilters && (\n <Button variant=\"ghost\" size=\"sm\" onClick={handleClearFilters}>\n <X\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n {tr('bigconsole.dataSinks.filters.clear', 'Clear Filters')}\n </Button>\n )}\n\n {/* Results Count + View Toggle */}\n <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 'var(--space-controlGap)' }}>\n <Badge variant=\"secondary\">\n {filteredDataSinks.length}{' '}\n {filteredDataSinks.length === 1\n ? tr('bigconsole.dataSinks.count.single', 'Data Sink')\n : tr('bigconsole.dataSinks.count.plural', 'Data Sinks')}\n </Badge>\n <div\n style={{\n display: 'flex',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n overflow: 'hidden',\n }}\n >\n <button\n type=\"button\"\n onClick={() => toggleViewMode('grid')}\n title={tr('bigconsole.dataSinks.view.grid', 'Grid view')}\n style={{\n padding: '6px 8px',\n border: 'none',\n cursor: 'pointer',\n backgroundColor: viewMode === 'grid' ? 'var(--color-bg-sunken)' : 'transparent',\n color: viewMode === 'grid' ? 'var(--color-text-primary)' : 'var(--color-text-muted)',\n }}\n >\n <LayoutGrid size={16} />\n </button>\n <button\n type=\"button\"\n onClick={() => toggleViewMode('table')}\n title={tr('bigconsole.dataSinks.view.table', 'Table view')}\n style={{\n padding: '6px 8px',\n border: 'none',\n borderLeft: '1px solid var(--color-border)',\n cursor: 'pointer',\n backgroundColor: viewMode === 'table' ? 'var(--color-bg-sunken)' : 'transparent',\n color: viewMode === 'table' ? 'var(--color-text-primary)' : 'var(--color-text-muted)',\n }}\n >\n <Table2 size={16} />\n </button>\n </div>\n </div>\n </div>\n\n {/* Loading state */}\n {loading && dataSinks.length === 0 && (\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '80px 0' }}>\n <div style={{ textAlign: 'center' }}>\n <Spinner\n style={{\n height: 'var(--size-icon-xl)',\n width: 'var(--size-icon-xl)',\n margin: '0 auto var(--space-formGap)',\n }}\n />\n <p style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dataSinks.loading', 'Loading data sinks...')}\n </p>\n </div>\n </div>\n )}\n\n {/* Empty state */}\n {!loading && dataSinks.length === 0 && (\n <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('bigconsole.dataSinks.empty.title', 'No Data Sinks yet')}\n description={tr(\n 'bigconsole.dataSinks.empty.description',\n 'Data Sinks store pre-processed data from FluidGrids workflows for fast access in widgets.'\n )}\n action={\n <Button onClick={handleCreateDataSink}>\n <Plus\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n {tr('bigconsole.dataSinks.actions.create', 'Create Data Sink')}\n </Button>\n }\n />\n </GlassCard>\n )}\n\n {/* No results state */}\n {!loading && filteredDataSinks.length === 0 && dataSinks.length > 0 && (\n <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr('bigconsole.dataSinks.empty.filteredTitle', 'No matching data sinks')}\n description={tr('bigconsole.dataSinks.empty.filtered', 'No data sinks match your filters')}\n action={\n <Button variant=\"ghost\" size=\"sm\" onClick={handleClearFilters}>\n {tr('bigconsole.dataSinks.filters.clear', 'Clear Filters')}\n </Button>\n }\n />\n </GlassCard>\n )}\n\n {/* DataSink Grid / Table */}\n {filteredDataSinks.length > 0 && viewMode === 'grid' && (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))',\n gap: 'var(--space-formGap)',\n }}\n >\n {filteredDataSinks.map((item) => (\n <DataSinkCard\n key={item.dataSink.id}\n dataSink={item.dataSink}\n setupState={item.setupState}\n onView={handleViewDataSink}\n onEdit={handleEditDataSink}\n onRefresh={handleRefreshClick}\n onExport={handleExportDataSink}\n onDelete={handleDeleteClick}\n />\n ))}\n </div>\n )}\n\n {/* Table View */}\n {filteredDataSinks.length > 0 && viewMode === 'table' && (\n <div\n style={{\n border: '1px solid var(--color-border)',\n borderRadius: 'var(--radius-card)',\n overflow: 'hidden',\n }}\n >\n <div style={{ overflowX: 'auto' }}>\n <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 'var(--font-size-body-sm)' }}>\n <thead>\n <tr\n style={{\n backgroundColor: 'var(--color-bg-sunken)',\n borderBottom: '1px solid var(--color-border)',\n }}\n >\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.name', 'Name')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.key', 'Key')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.status', 'Status')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.setup', 'Setup')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'center',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.active', 'Active')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.version', 'Version')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.updated', 'Updated')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'right',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {filteredDataSinks.map((item) => {\n const ds = item.dataSink;\n return (\n <tr\n key={ds.id}\n onClick={() => handleViewDataSink(ds.id)}\n style={{\n borderBottom: '1px solid var(--color-border)',\n cursor: 'pointer',\n transition: 'background-color 0.15s',\n }}\n onMouseEnter={(e) => {\n (e.currentTarget as HTMLElement).style.backgroundColor = 'var(--color-bg-sunken)';\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLElement).style.backgroundColor = 'transparent';\n }}\n >\n <td\n style={{\n padding: '10px 12px',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n maxWidth: 200,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {ds.name || ds.key}\n </td>\n <td\n style={{\n padding: '10px 12px',\n color: 'var(--color-text-muted)',\n fontFamily: 'monospace',\n fontSize: '12px',\n maxWidth: 160,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {ds.key}\n </td>\n <td style={{ padding: '10px 12px' }}>\n <DataSinkStatusBadge status={ds.status} />\n </td>\n <td style={{ padding: '10px 12px' }}>\n <SetupStateBadge state={item.setupState} />\n </td>\n <td style={{ padding: '10px 12px', textAlign: 'center' }}>\n <span\n style={{\n display: 'inline-block',\n width: 8,\n height: 8,\n borderRadius: '50%',\n backgroundColor: ds.isActive ? '#22c55e' : 'var(--color-text-muted)',\n }}\n title={\n ds.isActive\n ? tr('bigconsole.dataSinks.badges.active', 'Active')\n : tr('bigconsole.dataSinks.badges.inactive', 'Inactive')\n }\n />\n </td>\n <td style={{ padding: '10px 12px', color: 'var(--color-text-muted)' }}>v{ds.version ?? 0}</td>\n <td style={{ padding: '10px 12px', color: 'var(--color-text-muted)', whiteSpace: 'nowrap' }}>\n {new Date(ds.updatedAt).toLocaleDateString()}\n </td>\n <td style={{ padding: '10px 12px', textAlign: 'right' }}>\n <div\n style={{ display: 'flex', gap: 4, justifyContent: 'flex-end' }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.view', 'View')}\n onClick={() => handleViewDataSink(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <Eye size={14} />\n </button>\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.edit', 'Edit')}\n onClick={() => handleEditDataSink(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <Pencil size={14} />\n </button>\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.refresh', 'Refresh')}\n onClick={() => handleRefreshClick(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <RefreshCw size={14} />\n </button>\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.export', 'Export')}\n onClick={() => handleExportDataSink(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <Download size={14} />\n </button>\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.delete', 'Delete')}\n onClick={() => handleDeleteClick(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <Trash2 size={14} />\n </button>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n )}\n </div>\n\n {/* Dialogs */}\n {currentDataSink && (\n <>\n <DataSinkDeleteDialog\n dataSink={currentDataSink}\n isOpen={deleteDialogOpen}\n onClose={closeDeleteDialog}\n onConfirm={handleDeleteConfirm}\n />\n\n <DataSinkRefreshDialog\n dataSink={currentDataSink}\n isOpen={refreshDialogOpen}\n onClose={closeRefreshDialog}\n onConfirm={handleRefreshConfirm}\n />\n </>\n )}\n\n {/* Import DataSink Dialog */}\n <ImportDataSinkDialog\n isOpen={isImportDialogOpen}\n onClose={() => setIsImportDialogOpen(false)}\n onSuccess={handleImportSuccess}\n />\n </div>\n );\n});\n\nexport default DataSinksPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataSinksPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinksPage.tsx"],"sourcesContent":["/**\n * DataSinksPage\n *\n * List and manage DataSinks - fetches data from backend.\n * Uses fe-libs shared UI components and semantic tokens for consistent styling.\n */\n\nimport { type FC, memo, useCallback, useMemo, useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getProfileScopedKey } from '@burdenoff/fe-libs/shared/utils';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeNotify } from '../../utils/nativeBridge';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { useExportDataSink } from '../hooks/useExportDataSink';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport type { DataSink, DataSinkSetupState } from '../types';\nimport { DataSinkCard } from '../components/datasink/DataSinkCard';\nimport { DataSinkDeleteDialog } from '../components/datasink/DataSinkDeleteDialog';\nimport { DataSinkRefreshDialog } from '../components/datasink/DataSinkRefreshDialog';\nimport { ImportDataSinkDialog } from '../components/datasink/ImportDataSinkDialog';\n\n// fe-libs components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport {\n AuroraBackground,\n GradientText,\n GlassCard,\n IllustratedEmptyState,\n CTAOverflowMenu,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport {\n Plus,\n Search,\n AlertCircle,\n Container,\n X,\n Upload,\n LayoutGrid,\n Table2,\n Eye,\n Pencil,\n RefreshCw,\n Download,\n Trash2,\n} from 'lucide-react';\nimport { DataSinkStatusBadge } from '../components/datasink/DataSinkStatusBadge';\nimport { SetupStateBadge } from '../components/datasink/SetupStateBadge';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype SetupStateFilter = 'all' | 'in_progress' | 'completed';\ntype ListViewMode = 'grid' | 'table';\n\nconst VIEW_MODE_KEY = 'bigconsole-datasinks-view-mode';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Determine setup state for a DataSink based on its properties.\n */\nconst getSetupState = (dataSink: DataSink): DataSinkSetupState => {\n if (dataSink.setupState === 'completed') {\n return 'completed';\n }\n if (dataSink.ttl !== null && dataSink.ttl !== undefined && dataSink.ttl > 0) {\n return 'completed';\n }\n if (dataSink.fluidGridsWorkflowId && dataSink.data && Object.keys(dataSink.data).length > 0) {\n return 'completed';\n }\n if (dataSink.fluidGridsWorkflowId) {\n return 'workflow_running';\n }\n return 'workflow_pending';\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DataSinksPage: FC = memo(function DataSinksPage() {\n const navigate = useAppNavigate();\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n\n // Local state\n const [setupStateFilter, setSetupStateFilter] = useState<SetupStateFilter>('all');\n const [isImportDialogOpen, setIsImportDialogOpen] = useState(false);\n const [viewMode, setViewMode] = useState<ListViewMode>(\n () => (localStorage.getItem(getProfileScopedKey(VIEW_MODE_KEY)) as ListViewMode) || 'grid'\n );\n\n const toggleViewMode = useCallback((mode: ListViewMode) => {\n setViewMode(mode);\n localStorage.setItem(getProfileScopedKey(VIEW_MODE_KEY), mode);\n }, []);\n\n // Store state\n const statusFilter = useDataSinkStore((state) => state.statusFilter);\n const activeFilter = useDataSinkStore((state) => state.activeFilter);\n const searchQuery = useDataSinkStore((state) => state.searchQuery);\n const setStatusFilter = useDataSinkStore((state) => state.setStatusFilter);\n const setActiveFilter = useDataSinkStore((state) => state.setActiveFilter);\n const setSearchQuery = useDataSinkStore((state) => state.setSearchQuery);\n const deleteDialogOpen = useDataSinkStore((state) => state.deleteDialogOpen);\n const refreshDialogOpen = useDataSinkStore((state) => state.refreshDialogOpen);\n const currentDataSink = useDataSinkStore((state) => state.currentDataSink);\n const openDeleteDialog = useDataSinkStore((state) => state.openDeleteDialog);\n const closeDeleteDialog = useDataSinkStore((state) => state.closeDeleteDialog);\n const openRefreshDialog = useDataSinkStore((state) => state.openRefreshDialog);\n const closeRefreshDialog = useDataSinkStore((state) => state.closeRefreshDialog);\n\n // Data operations\n const { loading, error, dataSinks, deleteDataSink, refreshDataSink, refetch } = useDataSinkOperations();\n\n // Export hook\n const { downloadDataSink } = useExportDataSink();\n\n // Compute filtered DataSinks with setup states\n const dataSinksWithSetupState = useMemo(() => {\n return dataSinks.map((ds) => ({\n dataSink: ds,\n setupState: getSetupState(ds),\n }));\n }, [dataSinks]);\n\n // Compute filtered DataSinks\n const filteredDataSinks = useMemo(() => {\n let filtered = [...dataSinksWithSetupState];\n\n if (setupStateFilter === 'in_progress') {\n filtered = filtered.filter((item) => item.setupState !== 'completed');\n } else if (setupStateFilter === 'completed') {\n filtered = filtered.filter((item) => item.setupState === 'completed');\n }\n\n if (statusFilter !== null) {\n filtered = filtered.filter((item) => item.dataSink.status === statusFilter);\n }\n\n if (activeFilter !== null) {\n filtered = filtered.filter((item) => item.dataSink.isActive === activeFilter);\n }\n\n if (searchQuery.trim()) {\n const query = searchQuery.toLowerCase();\n filtered = filtered.filter(\n (item) =>\n item.dataSink.name?.toLowerCase().includes(query) ||\n item.dataSink.key.toLowerCase().includes(query) ||\n item.dataSink.description?.toLowerCase().includes(query)\n );\n }\n\n return filtered.sort((a, b) => new Date(b.dataSink.updatedAt).getTime() - new Date(a.dataSink.updatedAt).getTime());\n }, [dataSinksWithSetupState, setupStateFilter, statusFilter, activeFilter, searchQuery]);\n\n // Handlers\n const handleCreateDataSink = useCallback(() => {\n navigate('/datasinks/new');\n }, [navigate]);\n\n const handleViewDataSink = useCallback(\n (id: string) => {\n navigate(`/datasinks/${id}`);\n },\n [navigate]\n );\n\n const handleEditDataSink = useCallback(\n (id: string) => {\n navigate(`/datasinks/${id}/edit`);\n },\n [navigate]\n );\n\n const handleRefreshClick = useCallback(\n (id: string) => {\n const dataSink = dataSinks.find((ds) => ds.id === id);\n if (dataSink) {\n openRefreshDialog(dataSink);\n }\n },\n [dataSinks, openRefreshDialog]\n );\n\n const handleRefreshConfirm = useCallback(\n async (forceRefresh: boolean) => {\n if (currentDataSink) {\n await refreshDataSink({\n id: currentDataSink.id,\n forceRefresh,\n });\n closeRefreshDialog();\n }\n },\n [currentDataSink, refreshDataSink, closeRefreshDialog]\n );\n\n const handleDeleteClick = useCallback(\n (id: string) => {\n const dataSink = dataSinks.find((ds) => ds.id === id);\n if (dataSink) {\n openDeleteDialog(dataSink);\n }\n },\n [dataSinks, openDeleteDialog]\n );\n\n const handleDeleteConfirm = useCallback(async () => {\n if (currentDataSink) {\n try {\n await deleteDataSink(currentDataSink.id);\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n closeDeleteDialog();\n }\n }, [currentDataSink, deleteDataSink, closeDeleteDialog]);\n\n const handleExportDataSink = useCallback(\n async (id: string) => {\n const dataSink = dataSinks.find((ds) => ds.id === id);\n if (dataSink) {\n await downloadDataSink(id, dataSink.name || dataSink.key);\n }\n },\n [dataSinks, downloadDataSink]\n );\n\n const handleImportDataSink = useCallback(() => {\n setIsImportDialogOpen(true);\n }, []);\n\n const handleImportSuccess = useCallback(\n (_dataSinkId: string, _dataSinkName: string | null, _wasUpdate: boolean) => {\n // Refetch DataSinks to show the newly imported one\n refetch();\n },\n [refetch]\n );\n\n const hasActiveFilters = useMemo(() => {\n return setupStateFilter !== 'all' || statusFilter !== null || activeFilter !== null || searchQuery.trim() !== '';\n }, [setupStateFilter, statusFilter, activeFilter, searchQuery]);\n\n const handleClearFilters = useCallback(() => {\n setSetupStateFilter('all');\n setStatusFilter(null);\n setActiveFilter(null);\n setSearchQuery('');\n }, [setStatusFilter, setActiveFilter, setSearchQuery]);\n\n // Setup state filter options\n const setupStateOptions: { value: SetupStateFilter; label: string }[] = [\n { value: 'all', label: tr('bigconsole.dataSinks.filters.setupState.all', 'All') },\n { value: 'in_progress', label: tr('bigconsole.dataSinks.filters.setupState.inProgress', 'In Progress') },\n { value: 'completed', label: tr('bigconsole.dataSinks.filters.setupState.completed', 'Completed') },\n ];\n\n // Error state\n if (error) {\n return (\n <div style={{ padding: 'var(--space-pagePadding)' }}>\n <Card\n style={{\n borderColor: 'var(--color-status-error-border)',\n backgroundColor: 'var(--color-status-error-bgSubtle)',\n }}\n >\n <CardContent style={{ padding: 'var(--space-pagePadding)' }}>\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>\n <div\n style={{\n padding: 'var(--space-controlGap)',\n borderRadius: '50%',\n backgroundColor: 'var(--color-status-error-bgSubtle)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <AlertCircle\n style={{\n height: 'var(--size-icon-xl)',\n width: 'var(--size-icon-xl)',\n color: 'var(--color-status-error-text)',\n }}\n />\n </div>\n <h3\n style={{\n fontSize: 'var(--font-size-body-lg)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-status-error-text)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n {tr('bigconsole.dataSinks.error.title', 'Failed to load Data Sinks')}\n </h3>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n {error.message}\n </p>\n <Button size=\"sm\" onClick={() => refetch()}>\n {tr('bigconsole.dataSinks.error.retry', 'Try Again')}\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n }\n\n return (\n <div style={{ minHeight: '100vh', backgroundColor: 'var(--color-bg-canvas)' }}>\n {/* Header Section */}\n <div className=\"relative overflow-hidden border-b border-border-seam bg-gloss-card\">\n <AuroraBackground intensity=\"subtle\" />\n <div style={{ padding: 'var(--space-pagePadding)' }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-formGap)' }}>\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-card)',\n backgroundColor: 'var(--color-action-primary-bg)',\n }}\n >\n <Container\n style={{\n height: 'var(--size-icon-lg)',\n width: 'var(--size-icon-lg)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n </div>\n <div>\n <h1\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n margin: 0,\n color: 'var(--color-text-primary)',\n }}\n >\n <GradientText>{tr('bigconsole.dataSinks.title', 'Data Sinks')}</GradientText>\n </h1>\n <p style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-secondary)', margin: 0 }}>\n {tr('bigconsole.dataSinks.subtitle', 'Manage pre-processed data from FluidGrids workflows')}\n </p>\n </div>\n </div>\n\n <CTAOverflowMenu\n primary={{\n label: tr('bigconsole.dataSinks.actions.create', 'Create Data Sink'),\n onSelect: handleCreateDataSink,\n icon: (\n <Plus\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n ),\n }}\n actions={[\n {\n label: tr('bigconsole.dataSinks.actions.import', 'Import'),\n onSelect: handleImportDataSink,\n icon: <Upload style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />,\n },\n ]}\n />\n </div>\n </div>\n </div>\n\n {/* Main Content */}\n <div style={{ padding: 'var(--space-pagePadding)' }}>\n <PagePurpose className=\"mb-[var(--space-sectionGap)]\">\n Data sinks are the structured destinations that hold your processed data — define a schema once and every\n pipeline, parser, and workflow can write into it. Dashboards then query sinks directly, so a sink is where\n your data lives and where your charts read from.\n </PagePurpose>\n\n {/* Setup State Filter Tabs */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '2px',\n padding: '4px',\n backgroundColor: 'var(--color-bg-sunken)',\n borderRadius: 'var(--radius-card)',\n width: 'fit-content',\n }}\n >\n {setupStateOptions.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => setSetupStateFilter(option.value)}\n style={{\n padding: 'var(--space-controlGap) var(--space-formGap)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n backgroundColor: setupStateFilter === option.value ? 'var(--color-bg-surface)' : 'transparent',\n color:\n setupStateFilter === option.value ? 'var(--color-text-primary)' : 'var(--color-text-secondary)',\n boxShadow: setupStateFilter === option.value ? '0 1px 2px rgba(0,0,0,0.05)' : 'none',\n }}\n >\n {option.label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Search and Filters Row */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n marginBottom: 'var(--space-sectionGap)',\n flexWrap: 'wrap',\n }}\n >\n {/* Search Bar */}\n <div style={{ position: 'relative', maxWidth: '400px', flex: 1 }}>\n <Search\n style={{\n position: 'absolute',\n left: 'var(--space-controlGap)',\n top: '50%',\n transform: 'translateY(-50%)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-text-muted)',\n }}\n />\n <Input\n type=\"text\"\n placeholder={tr('bigconsole.dataSinks.search.placeholder', 'Search data sinks...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={{ paddingLeft: 'var(--size-controlHeight-md)', height: 'var(--size-controlHeight-md)' }}\n />\n </div>\n\n {/* Status Filter */}\n <Select\n value={statusFilter || 'all'}\n onValueChange={(value) =>\n setStatusFilter(value === 'all' ? null : (value as 'ready' | 'error' | 'stale' | 'refreshing'))\n }\n >\n <SelectTrigger style={{ width: '150px' }}>\n <SelectValue placeholder={tr('bigconsole.dataSinks.filters.status.placeholder', 'All Statuses')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{tr('bigconsole.dataSinks.filters.status.all', 'All Statuses')}</SelectItem>\n <SelectItem value=\"ready\">{tr('bigconsole.dataSinks.filters.status.ready', 'Ready')}</SelectItem>\n <SelectItem value=\"error\">{tr('bigconsole.dataSinks.filters.status.error', 'Error')}</SelectItem>\n <SelectItem value=\"stale\">{tr('bigconsole.dataSinks.filters.status.stale', 'Stale')}</SelectItem>\n <SelectItem value=\"refreshing\">\n {tr('bigconsole.dataSinks.filters.status.refreshing', 'Refreshing')}\n </SelectItem>\n </SelectContent>\n </Select>\n\n {/* Active Filter */}\n <Select\n value={activeFilter === null ? 'all' : activeFilter ? 'true' : 'false'}\n onValueChange={(value) => setActiveFilter(value === 'all' ? null : value === 'true')}\n >\n <SelectTrigger style={{ width: '130px' }}>\n <SelectValue placeholder={tr('bigconsole.dataSinks.filters.active.placeholder', 'All')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{tr('bigconsole.dataSinks.filters.active.all', 'All')}</SelectItem>\n <SelectItem value=\"true\">{tr('bigconsole.dataSinks.filters.active.active', 'Active')}</SelectItem>\n <SelectItem value=\"false\">{tr('bigconsole.dataSinks.filters.active.inactive', 'Inactive')}</SelectItem>\n </SelectContent>\n </Select>\n\n {/* Clear Filters */}\n {hasActiveFilters && (\n <Button variant=\"ghost\" size=\"sm\" onClick={handleClearFilters}>\n <X\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n {tr('bigconsole.dataSinks.filters.clear', 'Clear Filters')}\n </Button>\n )}\n\n {/* Results Count + View Toggle */}\n <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 'var(--space-controlGap)' }}>\n <Badge variant=\"secondary\">\n {filteredDataSinks.length}{' '}\n {filteredDataSinks.length === 1\n ? tr('bigconsole.dataSinks.count.single', 'Data Sink')\n : tr('bigconsole.dataSinks.count.plural', 'Data Sinks')}\n </Badge>\n <div\n style={{\n display: 'flex',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n overflow: 'hidden',\n }}\n >\n <button\n type=\"button\"\n onClick={() => toggleViewMode('grid')}\n title={tr('bigconsole.dataSinks.view.grid', 'Grid view')}\n style={{\n padding: '6px 8px',\n border: 'none',\n cursor: 'pointer',\n backgroundColor: viewMode === 'grid' ? 'var(--color-bg-sunken)' : 'transparent',\n color: viewMode === 'grid' ? 'var(--color-text-primary)' : 'var(--color-text-muted)',\n }}\n >\n <LayoutGrid size={16} />\n </button>\n <button\n type=\"button\"\n onClick={() => toggleViewMode('table')}\n title={tr('bigconsole.dataSinks.view.table', 'Table view')}\n style={{\n padding: '6px 8px',\n border: 'none',\n borderLeft: '1px solid var(--color-border)',\n cursor: 'pointer',\n backgroundColor: viewMode === 'table' ? 'var(--color-bg-sunken)' : 'transparent',\n color: viewMode === 'table' ? 'var(--color-text-primary)' : 'var(--color-text-muted)',\n }}\n >\n <Table2 size={16} />\n </button>\n </div>\n </div>\n </div>\n\n {/* Loading state */}\n {loading && dataSinks.length === 0 && (\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '80px 0' }}>\n <div style={{ textAlign: 'center' }}>\n <Spinner\n style={{\n height: 'var(--size-icon-xl)',\n width: 'var(--size-icon-xl)',\n margin: '0 auto var(--space-formGap)',\n }}\n />\n <p style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dataSinks.loading', 'Loading data sinks...')}\n </p>\n </div>\n </div>\n )}\n\n {/* Empty state */}\n {!loading && dataSinks.length === 0 && (\n <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('bigconsole.dataSinks.empty.title', 'No Data Sinks yet')}\n description={tr(\n 'bigconsole.dataSinks.empty.description',\n 'Data Sinks store pre-processed data from FluidGrids workflows for fast access in widgets.'\n )}\n action={\n <Button onClick={handleCreateDataSink}>\n <Plus\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n {tr('bigconsole.dataSinks.actions.create', 'Create Data Sink')}\n </Button>\n }\n />\n </GlassCard>\n )}\n\n {/* No results state */}\n {!loading && filteredDataSinks.length === 0 && dataSinks.length > 0 && (\n <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr('bigconsole.dataSinks.empty.filteredTitle', 'No matching data sinks')}\n description={tr('bigconsole.dataSinks.empty.filtered', 'No data sinks match your filters')}\n action={\n <Button variant=\"ghost\" size=\"sm\" onClick={handleClearFilters}>\n {tr('bigconsole.dataSinks.filters.clear', 'Clear Filters')}\n </Button>\n }\n />\n </GlassCard>\n )}\n\n {/* DataSink Grid / Table */}\n {filteredDataSinks.length > 0 && viewMode === 'grid' && (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))',\n gap: 'var(--space-formGap)',\n }}\n >\n {filteredDataSinks.map((item) => (\n <DataSinkCard\n key={item.dataSink.id}\n dataSink={item.dataSink}\n setupState={item.setupState}\n onView={handleViewDataSink}\n onEdit={handleEditDataSink}\n onRefresh={handleRefreshClick}\n onExport={handleExportDataSink}\n onDelete={handleDeleteClick}\n />\n ))}\n </div>\n )}\n\n {/* Table View */}\n {filteredDataSinks.length > 0 && viewMode === 'table' && (\n <div\n style={{\n border: '1px solid var(--color-border)',\n borderRadius: 'var(--radius-card)',\n overflow: 'hidden',\n }}\n >\n <div style={{ overflowX: 'auto' }}>\n <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 'var(--font-size-body-sm)' }}>\n <thead>\n <tr\n style={{\n backgroundColor: 'var(--color-bg-sunken)',\n borderBottom: '1px solid var(--color-border)',\n }}\n >\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.name', 'Name')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.key', 'Key')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.status', 'Status')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.setup', 'Setup')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'center',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.active', 'Active')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.version', 'Version')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'left',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.updated', 'Updated')}\n </th>\n <th\n style={{\n padding: '10px 12px',\n textAlign: 'right',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n whiteSpace: 'nowrap',\n }}\n >\n {tr('bigconsole.dataSinks.table.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {filteredDataSinks.map((item) => {\n const ds = item.dataSink;\n return (\n <tr\n key={ds.id}\n onClick={() => handleViewDataSink(ds.id)}\n style={{\n borderBottom: '1px solid var(--color-border)',\n cursor: 'pointer',\n transition: 'background-color 0.15s',\n }}\n onMouseEnter={(e) => {\n (e.currentTarget as HTMLElement).style.backgroundColor = 'var(--color-bg-sunken)';\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLElement).style.backgroundColor = 'transparent';\n }}\n >\n <td\n style={{\n padding: '10px 12px',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n maxWidth: 200,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {ds.name || ds.key}\n </td>\n <td\n style={{\n padding: '10px 12px',\n color: 'var(--color-text-muted)',\n fontFamily: 'monospace',\n fontSize: '12px',\n maxWidth: 160,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {ds.key}\n </td>\n <td style={{ padding: '10px 12px' }}>\n <DataSinkStatusBadge status={ds.status} />\n </td>\n <td style={{ padding: '10px 12px' }}>\n <SetupStateBadge state={item.setupState} />\n </td>\n <td style={{ padding: '10px 12px', textAlign: 'center' }}>\n <span\n style={{\n display: 'inline-block',\n width: 8,\n height: 8,\n borderRadius: '50%',\n backgroundColor: ds.isActive ? '#22c55e' : 'var(--color-text-muted)',\n }}\n title={\n ds.isActive\n ? tr('bigconsole.dataSinks.badges.active', 'Active')\n : tr('bigconsole.dataSinks.badges.inactive', 'Inactive')\n }\n />\n </td>\n <td style={{ padding: '10px 12px', color: 'var(--color-text-muted)' }}>v{ds.version ?? 0}</td>\n <td style={{ padding: '10px 12px', color: 'var(--color-text-muted)', whiteSpace: 'nowrap' }}>\n {new Date(ds.updatedAt).toLocaleDateString()}\n </td>\n <td style={{ padding: '10px 12px', textAlign: 'right' }}>\n <div\n style={{ display: 'flex', gap: 4, justifyContent: 'flex-end' }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.view', 'View')}\n onClick={() => handleViewDataSink(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <Eye size={14} />\n </button>\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.edit', 'Edit')}\n onClick={() => handleEditDataSink(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <Pencil size={14} />\n </button>\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.refresh', 'Refresh')}\n onClick={() => handleRefreshClick(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <RefreshCw size={14} />\n </button>\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.export', 'Export')}\n onClick={() => handleExportDataSink(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <Download size={14} />\n </button>\n <button\n type=\"button\"\n title={tr('bigconsole.dataSinks.actions.delete', 'Delete')}\n onClick={() => handleDeleteClick(ds.id)}\n style={{\n padding: 4,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'var(--color-text-muted)',\n borderRadius: 4,\n }}\n >\n <Trash2 size={14} />\n </button>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n )}\n </div>\n\n {/* Dialogs */}\n {currentDataSink && (\n <>\n <DataSinkDeleteDialog\n dataSink={currentDataSink}\n isOpen={deleteDialogOpen}\n onClose={closeDeleteDialog}\n onConfirm={handleDeleteConfirm}\n />\n\n <DataSinkRefreshDialog\n dataSink={currentDataSink}\n isOpen={refreshDialogOpen}\n onClose={closeRefreshDialog}\n onConfirm={handleRefreshConfirm}\n />\n </>\n )}\n\n {/* Import DataSink Dialog */}\n <ImportDataSinkDialog\n isOpen={isImportDialogOpen}\n onClose={() => setIsImportDialogOpen(false)}\n onSuccess={handleImportSuccess}\n />\n </div>\n );\n});\n\nexport default DataSinksPage;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/bigconsole/pages/HomePage.tsx"],"sourcesContent":["/**\n * HomePage\n *\n * BigConsole landing page showing workspace statistics and quick actions.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuroraBackground,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Button,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useAppNavigate } from '../contexts';\nimport { LayoutDashboard, Blocks, FileCode2, GitBranch, ArrowRight, Loader2 } from 'lucide-react';\n\n// ============================================================================\n// GRAPHQL RESPONSE TYPES\n// ============================================================================\n\ninterface HomepageStatsData {\n listDashboards: { totalCount: number };\n listDataSinks: { totalCount: number };\n // TODO: listParsers and listPipelines don't support totalCount pagination yet;\n // using array length as a fallback until the schema is updated.\n listParsers: Array<{ id: string }>;\n listPipelines: Array<{ id: string }>;\n}\n\ninterface RecentDashboardNode {\n id: string;\n name: string;\n description?: string;\n updatedAt: string;\n}\n\ninterface RecentDashboardsData {\n listDashboards: {\n edges: Array<{ node: RecentDashboardNode }>;\n };\n}\n\nconst HOMEPAGE_STATS = gql`\n query HomepageStats {\n listDashboards(first: 0) {\n totalCount\n }\n listDataSinks(first: 0) {\n totalCount\n }\n listParsers {\n id\n }\n listPipelines {\n id\n }\n }\n`;\n\nconst RECENT_DASHBOARDS = gql`\n query RecentDashboards {\n listDashboards(first: 5, sortBy: \"updatedAt\", sortOrder: \"desc\") {\n edges {\n node {\n id\n name\n description\n updatedAt\n }\n }\n }\n }\n`;\n\ninterface StatCardProps {\n icon: React.ReactNode;\n label: string;\n count: number | undefined;\n loading: boolean;\n path: string;\n onNavigate: (path: string) => void;\n}\n\nconst StatCard: FC<StatCardProps & { hero?: boolean }> = memo(function StatCard({\n icon,\n label,\n count,\n loading,\n path,\n onNavigate,\n hero = false,\n}) {\n return (\n <GlassCard\n treatment={hero ? 'glass' : 'metric'}\n glow={hero}\n className=\"border-border-seam p-0\"\n >\n <button\n type=\"button\"\n onClick={() => onNavigate(path)}\n className=\"flex w-full flex-col gap-2 rounded-card p-[var(--space-cardPadding)] text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-center gap-2\">\n <div style={{ color: 'var(--color-action-primary-bg)' }}>{icon}</div>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-secondary)' }}>{label}</span>\n </div>\n <div\n className=\"tabular-nums\"\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {loading ? <Loader2 size={20} className=\"animate-spin\" /> : (count ?? 0)}\n </div>\n </button>\n </GlassCard>\n );\n});\n\nexport const HomePage: FC = memo(function HomePage() {\n const navigate = useAppNavigate();\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n const { data: stats, loading: statsLoading } = useQuery<HomepageStatsData>(HOMEPAGE_STATS, {\n fetchPolicy: 'cache-and-network',\n });\n const { data: recentData, loading: recentLoading } = useQuery<RecentDashboardsData>(RECENT_DASHBOARDS, {\n fetchPolicy: 'cache-and-network',\n });\n\n const recentDashboards = recentData?.listDashboards?.edges?.map((e) => e.node) || [];\n\n return (\n <div style={{ minHeight: '100vh', backgroundColor: 'var(--color-bg-canvas)' }}>\n <div style={{ padding: 'var(--space-pagePadding)', maxWidth: 960, margin: '0 auto' }}>\n {/* Hero Header */}\n <div\n className=\"relative mb-[var(--space-sectionGap)] overflow-hidden rounded-card border border-border-seam bg-gloss-card p-6\"\n >\n <AuroraBackground intensity=\"subtle\" />\n <h1\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n color: 'var(--color-text-primary)',\n }}\n >\n <GradientText>BigConsole</GradientText>\n </h1>\n <p style={{ fontSize: 'var(--font-size-body-md)', color: 'var(--color-text-secondary)', marginTop: 4 }}>\n {tr('bigconsole.home.subtitle', 'AI-powered analytics platform')}\n </p>\n </div>\n\n <PagePurpose className=\"mb-[var(--space-sectionGap)]\">\n {tr(\n 'bigconsole.home.purpose',\n 'BigConsole is your analytics command center — build live dashboards, wire up data sinks and parsers, then automate it all with pipelines and workflows. Start from the quick actions below or jump back into a recent dashboard.'\n )}\n </PagePurpose>\n\n {/* Stats Grid */}\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))',\n gap: 12,\n marginBottom: 'var(--space-sectionGap)',\n }}\n >\n <StatCard\n hero\n icon={<LayoutDashboard size={20} />}\n label={tr('bigconsole.home.stats.dashboards', 'Dashboards')}\n count={stats?.listDashboards?.totalCount}\n loading={statsLoading}\n path=\"/dashboards\"\n onNavigate={navigate}\n />\n <StatCard\n icon={<Blocks size={20} />}\n label={tr('bigconsole.home.stats.dataSinks', 'Data Sinks')}\n count={stats?.listDataSinks?.totalCount}\n loading={statsLoading}\n path=\"/datasinks\"\n onNavigate={navigate}\n />\n <StatCard\n icon={<FileCode2 size={20} />}\n label={tr('bigconsole.home.stats.parsers', 'Parsers')}\n count={stats?.listParsers?.length}\n loading={statsLoading}\n path=\"/parsers\"\n onNavigate={navigate}\n />\n <StatCard\n icon={<GitBranch size={20} />}\n label={tr('bigconsole.home.stats.pipelines', 'Pipelines')}\n count={stats?.listPipelines?.length}\n loading={statsLoading}\n path=\"/pipelines\"\n onNavigate={navigate}\n />\n </div>\n\n {/* Quick Actions */}\n <div style={{ marginBottom: 'var(--space-sectionGap)' }}>\n <h2\n style={{\n fontSize: 'var(--font-size-body-md)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 12,\n }}\n >\n {tr('bigconsole.home.quickActions.title', 'Quick Actions')}\n </h2>\n <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>\n {[\n {\n label: tr('bigconsole.home.quickActions.newDashboard', 'New Dashboard'),\n path: '/dashboards',\n icon: <LayoutDashboard size={14} />,\n },\n {\n label: tr('bigconsole.home.quickActions.newDataSink', 'New Data Sink'),\n path: '/datasinks/new',\n icon: <Blocks size={14} />,\n },\n {\n label: tr('bigconsole.home.quickActions.newParser', 'New Parser'),\n path: '/parsers/new',\n icon: <FileCode2 size={14} />,\n },\n {\n label: tr('bigconsole.home.quickActions.newPipeline', 'New Pipeline'),\n path: '/pipelines/new',\n icon: <GitBranch size={14} />,\n },\n ].map((action) => (\n <button\n key={action.path}\n onClick={() => navigate(action.path)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '8px 16px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-surface)',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n {action.icon} {action.label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Recent Dashboards */}\n <div>\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>\n <h2\n style={{\n fontSize: 'var(--font-size-body-md)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {tr('bigconsole.home.recentDashboards.title', 'Recent Dashboards')}\n </h2>\n <button\n onClick={() => navigate('/dashboards')}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '4px 8px',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: 'transparent',\n color: 'var(--color-action-primary-bg)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n {tr('bigconsole.home.recentDashboards.viewAll', 'View all')} <ArrowRight size={14} />\n </button>\n </div>\n\n {recentLoading ? (\n <div style={{ textAlign: 'center', padding: 24, color: 'var(--color-text-muted)' }}>\n <Loader2 size={20} className=\"animate-spin\" style={{ margin: '0 auto' }} />\n </div>\n ) : recentDashboards.length === 0 ? (\n <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('bigconsole.home.recentDashboards.emptyTitle', 'No dashboards yet')}\n description={tr(\n 'bigconsole.home.recentDashboards.emptyDescription',\n 'Create your first dashboard to get started'\n )}\n action={\n <Button onClick={() => navigate('/dashboards')}>\n {tr('bigconsole.home.quickActions.newDashboard', 'New Dashboard')}\n </Button>\n }\n />\n </GlassCard>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>\n {recentDashboards.map((d) => (\n <button\n key={d.id}\n onClick={() => navigate(`/dashboards/${d.id}`)}\n style={{\n textAlign: 'left',\n padding: 12,\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-surface)',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <div>\n <div\n style={{\n fontWeight: 'var(--font-weight-medium)',\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-primary)',\n }}\n >\n {d.name}\n </div>\n {d.description && (\n <div style={{ fontSize: 11, color: 'var(--color-text-muted)', marginTop: 2 }}>\n {d.description.slice(0, 80)}\n </div>\n )}\n </div>\n <ArrowRight size={14} style={{ color: 'var(--color-text-muted)' }} />\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n});\n\nexport default HomePage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/bigconsole/pages/HomePage.tsx"],"sourcesContent":["/**\n * HomePage\n *\n * BigConsole landing page showing workspace statistics and quick actions.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuroraBackground,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Button,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useAppNavigate } from '../contexts';\nimport { LayoutDashboard, Blocks, FileCode2, GitBranch, ArrowRight, Loader2 } from 'lucide-react';\n\n// ============================================================================\n// GRAPHQL RESPONSE TYPES\n// ============================================================================\n\ninterface HomepageStatsData {\n listDashboards: { totalCount: number };\n listDataSinks: { totalCount: number };\n // TODO: listParsers and listPipelines don't support totalCount pagination yet;\n // using array length as a fallback until the schema is updated.\n listParsers: Array<{ id: string }>;\n listPipelines: Array<{ id: string }>;\n}\n\ninterface RecentDashboardNode {\n id: string;\n name: string;\n description?: string;\n updatedAt: string;\n}\n\ninterface RecentDashboardsData {\n listDashboards: {\n edges: Array<{ node: RecentDashboardNode }>;\n };\n}\n\nconst HOMEPAGE_STATS = gql`\n query HomepageStats {\n listDashboards(first: 0) {\n totalCount\n }\n listDataSinks(first: 0) {\n totalCount\n }\n listParsers {\n id\n }\n listPipelines {\n id\n }\n }\n`;\n\nconst RECENT_DASHBOARDS = gql`\n query RecentDashboards {\n listDashboards(first: 5, sortBy: \"updatedAt\", sortOrder: \"desc\") {\n edges {\n node {\n id\n name\n description\n updatedAt\n }\n }\n }\n }\n`;\n\ninterface StatCardProps {\n icon: React.ReactNode;\n label: string;\n count: number | undefined;\n loading: boolean;\n path: string;\n onNavigate: (path: string) => void;\n}\n\nconst StatCard: FC<StatCardProps & { hero?: boolean }> = memo(function StatCard({\n icon,\n label,\n count,\n loading,\n path,\n onNavigate,\n hero = false,\n}) {\n return (\n <GlassCard treatment={hero ? 'glass' : 'metric'} glow={hero} className=\"border-border-seam p-0\">\n <button\n type=\"button\"\n onClick={() => onNavigate(path)}\n className=\"flex w-full flex-col gap-2 rounded-card p-[var(--space-cardPadding)] text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-center gap-2\">\n <div style={{ color: 'var(--color-action-primary-bg)' }}>{icon}</div>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-secondary)' }}>{label}</span>\n </div>\n <div\n className=\"tabular-nums\"\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {loading ? <Loader2 size={20} className=\"animate-spin\" /> : (count ?? 0)}\n </div>\n </button>\n </GlassCard>\n );\n});\n\nexport const HomePage: FC = memo(function HomePage() {\n const navigate = useAppNavigate();\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n const { data: stats, loading: statsLoading } = useQuery<HomepageStatsData>(HOMEPAGE_STATS, {\n fetchPolicy: 'cache-and-network',\n });\n const { data: recentData, loading: recentLoading } = useQuery<RecentDashboardsData>(RECENT_DASHBOARDS, {\n fetchPolicy: 'cache-and-network',\n });\n\n const recentDashboards = recentData?.listDashboards?.edges?.map((e) => e.node) || [];\n\n return (\n <div style={{ minHeight: '100vh', backgroundColor: 'var(--color-bg-canvas)' }}>\n <div style={{ padding: 'var(--space-pagePadding)', maxWidth: 960, margin: '0 auto' }}>\n {/* Hero Header */}\n <div className=\"relative mb-[var(--space-sectionGap)] overflow-hidden rounded-card border border-border-seam bg-gloss-card p-6\">\n <AuroraBackground intensity=\"subtle\" />\n <h1\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n color: 'var(--color-text-primary)',\n }}\n >\n <GradientText>BigConsole</GradientText>\n </h1>\n <p style={{ fontSize: 'var(--font-size-body-md)', color: 'var(--color-text-secondary)', marginTop: 4 }}>\n {tr('bigconsole.home.subtitle', 'AI-powered analytics platform')}\n </p>\n </div>\n\n <PagePurpose className=\"mb-[var(--space-sectionGap)]\">\n {tr(\n 'bigconsole.home.purpose',\n 'BigConsole is your analytics command center — build live dashboards, wire up data sinks and parsers, then automate it all with pipelines and workflows. Start from the quick actions below or jump back into a recent dashboard.'\n )}\n </PagePurpose>\n\n {/* Stats Grid */}\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))',\n gap: 12,\n marginBottom: 'var(--space-sectionGap)',\n }}\n >\n <StatCard\n hero\n icon={<LayoutDashboard size={20} />}\n label={tr('bigconsole.home.stats.dashboards', 'Dashboards')}\n count={stats?.listDashboards?.totalCount}\n loading={statsLoading}\n path=\"/dashboards\"\n onNavigate={navigate}\n />\n <StatCard\n icon={<Blocks size={20} />}\n label={tr('bigconsole.home.stats.dataSinks', 'Data Sinks')}\n count={stats?.listDataSinks?.totalCount}\n loading={statsLoading}\n path=\"/datasinks\"\n onNavigate={navigate}\n />\n <StatCard\n icon={<FileCode2 size={20} />}\n label={tr('bigconsole.home.stats.parsers', 'Parsers')}\n count={stats?.listParsers?.length}\n loading={statsLoading}\n path=\"/parsers\"\n onNavigate={navigate}\n />\n <StatCard\n icon={<GitBranch size={20} />}\n label={tr('bigconsole.home.stats.pipelines', 'Pipelines')}\n count={stats?.listPipelines?.length}\n loading={statsLoading}\n path=\"/pipelines\"\n onNavigate={navigate}\n />\n </div>\n\n {/* Quick Actions */}\n <div style={{ marginBottom: 'var(--space-sectionGap)' }}>\n <h2\n style={{\n fontSize: 'var(--font-size-body-md)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 12,\n }}\n >\n {tr('bigconsole.home.quickActions.title', 'Quick Actions')}\n </h2>\n <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>\n {[\n {\n label: tr('bigconsole.home.quickActions.newDashboard', 'New Dashboard'),\n path: '/dashboards',\n icon: <LayoutDashboard size={14} />,\n },\n {\n label: tr('bigconsole.home.quickActions.newDataSink', 'New Data Sink'),\n path: '/datasinks/new',\n icon: <Blocks size={14} />,\n },\n {\n label: tr('bigconsole.home.quickActions.newParser', 'New Parser'),\n path: '/parsers/new',\n icon: <FileCode2 size={14} />,\n },\n {\n label: tr('bigconsole.home.quickActions.newPipeline', 'New Pipeline'),\n path: '/pipelines/new',\n icon: <GitBranch size={14} />,\n },\n ].map((action) => (\n <button\n key={action.path}\n onClick={() => navigate(action.path)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '8px 16px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-surface)',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n {action.icon} {action.label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Recent Dashboards */}\n <div>\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>\n <h2\n style={{\n fontSize: 'var(--font-size-body-md)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {tr('bigconsole.home.recentDashboards.title', 'Recent Dashboards')}\n </h2>\n <button\n onClick={() => navigate('/dashboards')}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '4px 8px',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: 'transparent',\n color: 'var(--color-action-primary-bg)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n {tr('bigconsole.home.recentDashboards.viewAll', 'View all')} <ArrowRight size={14} />\n </button>\n </div>\n\n {recentLoading ? (\n <div style={{ textAlign: 'center', padding: 24, color: 'var(--color-text-muted)' }}>\n <Loader2 size={20} className=\"animate-spin\" style={{ margin: '0 auto' }} />\n </div>\n ) : recentDashboards.length === 0 ? (\n <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('bigconsole.home.recentDashboards.emptyTitle', 'No dashboards yet')}\n description={tr(\n 'bigconsole.home.recentDashboards.emptyDescription',\n 'Create your first dashboard to get started'\n )}\n action={\n <Button onClick={() => navigate('/dashboards')}>\n {tr('bigconsole.home.quickActions.newDashboard', 'New Dashboard')}\n </Button>\n }\n />\n </GlassCard>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>\n {recentDashboards.map((d) => (\n <button\n key={d.id}\n onClick={() => navigate(`/dashboards/${d.id}`)}\n style={{\n textAlign: 'left',\n padding: 12,\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-surface)',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <div>\n <div\n style={{\n fontWeight: 'var(--font-weight-medium)',\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-primary)',\n }}\n >\n {d.name}\n </div>\n {d.description && (\n <div style={{ fontSize: 11, color: 'var(--color-text-muted)', marginTop: 2 }}>\n {d.description.slice(0, 80)}\n </div>\n )}\n </div>\n <ArrowRight size={14} style={{ color: 'var(--color-text-muted)' }} />\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n});\n\nexport default HomePage;\n"],"mappings":";;;;;;;;;;AA+CA,IAAM,IAAiB,CAAG;;;;;;;;;;;;;;;GAiBpB,IAAoB,CAAG;;;;;;;;;;;;;GAwBvB,IAAmD,EAAK,SAAkB,EAC9E,SACA,UACA,UACA,YACA,SACA,eACA,UAAO,MACN;AACD,QACE,kBAAC,GAAD;EAAW,WAAW,IAAO,UAAU;EAAU,MAAM;EAAM,WAAU;YACrE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,EAAK;GAC/B,WAAU;aAHZ,CAKE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,OAAO,EAAE,OAAO,kCAAkC;eAAG;KAAW,CAAA,EACrE,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAA4B,OAAO;MAA+B;eAAG;KAAa,CAAA,CACvG;OACN,kBAAC,OAAD;IACE,WAAU;IACV,OAAO;KACL,UAAU;KACV,YAAY;KACZ,OAAO;KACR;cAEA,IAAU,kBAAC,GAAD;KAAS,MAAM;KAAI,WAAU;KAAiB,CAAA,GAAI,KAAS;IAClE,CAAA,CACC;;EACC,CAAA;EAEd,EAEW,IAAe,EAAK,WAAoB;CACnD,IAAM,IAAW,GAAgB,EAC3B,EAAE,SAAM,GAAS,EACjB,IAAK,GACR,GAAa,MAAqB;EACjC,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEzC,CAAC,EAAE,CACJ,EACK,EAAE,MAAM,GAAO,SAAS,MAAiB,EAA4B,GAAgB,EACzF,aAAa,qBACd,CAAC,EACI,EAAE,MAAM,GAAY,SAAS,MAAkB,EAA+B,GAAmB,EACrG,aAAa,qBACd,CAAC,EAEI,IAAmB,GAAY,gBAAgB,OAAO,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE;AAEpF,QACE,kBAAC,OAAD;EAAK,OAAO;GAAE,WAAW;GAAS,iBAAiB;GAA0B;YAC3E,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAA4B,UAAU;IAAK,QAAQ;IAAU;aAApF;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAkB,WAAU,UAAW,CAAA;MACvC,kBAAC,MAAD;OACE,OAAO;QACL,UAAU;QACV,YAAY;QACZ,OAAO;QACR;iBAED,kBAAC,GAAD,EAAA,UAAc,cAAyB,CAAA;OACpC,CAAA;MACL,kBAAC,KAAD;OAAG,OAAO;QAAE,UAAU;QAA4B,OAAO;QAA+B,WAAW;QAAG;iBACnG,EAAG,4BAA4B,gCAAgC;OAC9D,CAAA;MACA;;IAEN,kBAAC,GAAD;KAAa,WAAU;eACpB,EACC,2BACA,mOACD;KACW,CAAA;IAGd,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,qBAAqB;MACrB,KAAK;MACL,cAAc;MACf;eANH;MAQE,kBAAC,GAAD;OACE,MAAA;OACA,MAAM,kBAAC,GAAD,EAAiB,MAAM,IAAM,CAAA;OACnC,OAAO,EAAG,oCAAoC,aAAa;OAC3D,OAAO,GAAO,gBAAgB;OAC9B,SAAS;OACT,MAAK;OACL,YAAY;OACZ,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;OAC1B,OAAO,EAAG,mCAAmC,aAAa;OAC1D,OAAO,GAAO,eAAe;OAC7B,SAAS;OACT,MAAK;OACL,YAAY;OACZ,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;OAC7B,OAAO,EAAG,iCAAiC,UAAU;OACrD,OAAO,GAAO,aAAa;OAC3B,SAAS;OACT,MAAK;OACL,YAAY;OACZ,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;OAC7B,OAAO,EAAG,mCAAmC,YAAY;OACzD,OAAO,GAAO,eAAe;OAC7B,SAAS;OACT,MAAK;OACL,YAAY;OACZ,CAAA;MACE;;IAGN,kBAAC,OAAD;KAAK,OAAO,EAAE,cAAc,2BAA2B;eAAvD,CACE,kBAAC,MAAD;MACE,OAAO;OACL,UAAU;OACV,YAAY;OACZ,OAAO;OACP,cAAc;OACf;gBAEA,EAAG,sCAAsC,gBAAgB;MACvD,CAAA,EACL,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,KAAK;OAAI,UAAU;OAAQ;gBACvD;OACC;QACE,OAAO,EAAG,6CAA6C,gBAAgB;QACvE,MAAM;QACN,MAAM,kBAAC,GAAD,EAAiB,MAAM,IAAM,CAAA;QACpC;OACD;QACE,OAAO,EAAG,4CAA4C,gBAAgB;QACtE,MAAM;QACN,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;QAC3B;OACD;QACE,OAAO,EAAG,0CAA0C,aAAa;QACjE,MAAM;QACN,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;QAC9B;OACD;QACE,OAAO,EAAG,4CAA4C,eAAe;QACrE,MAAM;QACN,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;QAC9B;OACF,CAAC,KAAK,MACL,kBAAC,UAAD;OAEE,eAAe,EAAS,EAAO,KAAK;OACpC,OAAO;QACL,SAAS;QACT,YAAY;QACZ,KAAK;QACL,SAAS;QACT,cAAc;QACd,QAAQ;QACR,iBAAiB;QACjB,OAAO;QACP,UAAU;QACV,QAAQ;QACT;iBAdH;QAgBG,EAAO;QAAK;QAAE,EAAO;QACf;SAhBF,EAAO,KAgBL,CACT;MACE,CAAA,CACF;;IAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,gBAAgB;MAAiB,YAAY;MAAU,cAAc;MAAI;eAAxG,CACE,kBAAC,MAAD;MACE,OAAO;OACL,UAAU;OACV,YAAY;OACZ,OAAO;OACR;gBAEA,EAAG,0CAA0C,oBAAoB;MAC/D,CAAA,EACL,kBAAC,UAAD;MACE,eAAe,EAAS,cAAc;MACtC,OAAO;OACL,SAAS;OACT,YAAY;OACZ,KAAK;OACL,SAAS;OACT,cAAc;OACd,QAAQ;OACR,iBAAiB;OACjB,OAAO;OACP,UAAU;OACV,QAAQ;OACT;gBAbH;OAeG,EAAG,4CAA4C,WAAW;OAAC;OAAC,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;OAC9E;QACL;QAEL,IACC,kBAAC,OAAD;KAAK,OAAO;MAAE,WAAW;MAAU,SAAS;MAAI,OAAO;MAA2B;eAChF,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAe,OAAO,EAAE,QAAQ,UAAU;MAAI,CAAA;KACvE,CAAA,GACJ,EAAiB,WAAW,IAC9B,kBAAC,GAAD;KAAW,WAAU;KAAU,WAAU;eACvC,kBAAC,GAAD;MACE,cAAa;MACb,OAAO,EAAG,+CAA+C,oBAAoB;MAC7E,aAAa,EACX,qDACA,6CACD;MACD,QACE,kBAAC,GAAD;OAAQ,eAAe,EAAS,cAAc;iBAC3C,EAAG,6CAA6C,gBAAgB;OAC1D,CAAA;MAEX,CAAA;KACQ,CAAA,GAEZ,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,eAAe;MAAU,KAAK;MAAG;eAC7D,EAAiB,KAAK,MACrB,kBAAC,UAAD;MAEE,eAAe,EAAS,eAAe,EAAE,KAAK;MAC9C,OAAO;OACL,WAAW;OACX,SAAS;OACT,cAAc;OACd,QAAQ;OACR,iBAAiB;OACjB,QAAQ;OACR,SAAS;OACT,gBAAgB;OAChB,YAAY;OACb;gBAbH,CAeE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OACE,OAAO;QACL,YAAY;QACZ,UAAU;QACV,OAAO;QACR;iBAEA,EAAE;OACC,CAAA,EACL,EAAE,eACD,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAI,OAAO;QAA2B,WAAW;QAAG;iBACzE,EAAE,YAAY,MAAM,GAAG,GAAG;OACvB,CAAA,CAEJ,EAAA,CAAA,EACN,kBAAC,GAAD;OAAY,MAAM;OAAI,OAAO,EAAE,OAAO,2BAA2B;OAAI,CAAA,CAC9D;QA/BF,EAAE,GA+BA,CACT;KACE,CAAA,CAEJ,EAAA,CAAA;IACF;;EACF,CAAA;EAER"}