@burdenoff/microfe-bigconsole 2026.730.6 → 2026.730.7
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":"DashboardsPage.js","names":[],"sources":["../../../src/bigconsole/pages/DashboardsPage.tsx"],"sourcesContent":["/**\n * DashboardsPage\n *\n * List and manage dashboards - fetches data from real backend.\n * Uses fe-libs shared UI components and semantic tokens for consistent styling.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useBigConsoleNavigate } from '../context';\nimport { isNative, nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } 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';\nimport {\n Plus,\n Upload,\n Search,\n LayoutDashboard,\n Calendar,\n Layers,\n FileText,\n Pencil,\n Trash2,\n Copy,\n LayoutGrid,\n List,\n ChevronLeft,\n ChevronRight,\n} from 'lucide-react';\nimport { useDashboardOperations } from '../hooks';\nimport { CreateDashboardDialog } from '../components/dashboard/CreateDashboardDialog';\nimport { ImportDashboardDialog } from '../components/dashboard/ImportDashboardDialog';\nimport { reportBigConsoleQuotaError } from '../utils/quota';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst VIEW_MODE_STORAGE_KEY = 'bf-bigconsole-dashboards-view';\n\n/** Number of dashboards shown per page in the list/grid views. */\nconst PAGE_SIZE = 12;\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardsPage: FC = memo(function DashboardsPage() {\n const navigate = useBigConsoleNavigate();\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 [searchQuery, setSearchQuery] = useState('');\n const [currentPage, setCurrentPage] = useState(1);\n const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);\n const [isCreating, setIsCreating] = useState(false);\n const [isImportDialogOpen, setIsImportDialogOpen] = useState(false);\n\n // View mode (grid/list) persisted to localStorage so it survives reloads\n const [viewMode, setViewMode] = useState<'grid' | 'list'>(() => {\n try {\n const stored = window.localStorage.getItem(VIEW_MODE_STORAGE_KEY);\n return stored === 'list' ? 'list' : 'grid';\n } catch {\n return 'grid';\n }\n });\n\n const handleSetViewMode = useCallback((mode: 'grid' | 'list') => {\n setViewMode(mode);\n try {\n window.localStorage.setItem(VIEW_MODE_STORAGE_KEY, mode);\n } catch {\n // Ignore storage errors (e.g. private mode) - view mode still works in-session\n }\n }, []);\n\n const { loading, error, dashboards, createDashboard, deleteDashboard, cloneDashboard, refetch } =\n useDashboardOperations();\n\n // Filter dashboards by search query\n const filteredDashboards = dashboards.filter(\n (d) =>\n d.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n d.description?.toLowerCase().includes(searchQuery.toLowerCase())\n );\n\n // Client-side pagination over the filtered result set\n const totalPages = Math.max(1, Math.ceil(filteredDashboards.length / PAGE_SIZE));\n const pageStartIndex = (currentPage - 1) * PAGE_SIZE;\n const paginatedDashboards = filteredDashboards.slice(pageStartIndex, pageStartIndex + PAGE_SIZE);\n\n // Reset to the first page when the search query or view changes so the\n // visible slice always starts from the top of the (possibly new) result set.\n useEffect(() => {\n setCurrentPage(1);\n }, [searchQuery, viewMode]);\n\n // Guard against the current page exceeding the available pages (e.g. after a\n // delete removes the last item on the final page).\n useEffect(() => {\n if (currentPage > totalPages) {\n setCurrentPage(totalPages);\n }\n }, [currentPage, totalPages]);\n\n const handlePrevPage = useCallback(() => {\n setCurrentPage((prev) => Math.max(1, prev - 1));\n }, []);\n\n const handleNextPage = useCallback(() => {\n setCurrentPage((prev) => Math.min(totalPages, prev + 1));\n }, [totalPages]);\n\n const handleGoToPage = useCallback((page: number) => {\n setCurrentPage(page);\n }, []);\n\n const handleOpenCreateDialog = useCallback(() => {\n setIsCreateDialogOpen(true);\n }, []);\n\n const handleCloseCreateDialog = useCallback(() => {\n if (!isCreating) {\n setIsCreateDialogOpen(false);\n }\n }, [isCreating]);\n\n const handleCreateDashboard = useCallback(\n async (data: { name: string; description: string; category: string; tags?: string[] }) => {\n setIsCreating(true);\n try {\n const newDashboard = await createDashboard({\n name: data.name,\n description: data.description,\n category: data.category,\n tags: data.tags,\n });\n\n if (newDashboard) {\n setIsCreateDialogOpen(false);\n navigate(`/dashboards/${newDashboard.id}/builder`);\n }\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'project')) {\n setIsCreateDialogOpen(false);\n return;\n }\n // Surface the failure instead of swallowing it: log for debugging,\n // notify on native, and rethrow so CreateDashboardDialog displays the error.\n console.error('Failed to create dashboard:', err);\n if (isNative()) void nativeNotify('error');\n throw err;\n } finally {\n setIsCreating(false);\n }\n },\n [createDashboard, navigate]\n );\n\n const handleOpenDashboard = useCallback(\n (dashboardId: string) => {\n navigate(`/dashboards/${dashboardId}`);\n },\n [navigate]\n );\n\n const handleEditDashboard = useCallback(\n (dashboardId: string, e: React.MouseEvent) => {\n e.stopPropagation();\n navigate(`/dashboards/${dashboardId}/builder`);\n },\n [navigate]\n );\n\n const handleDeleteDashboard = useCallback(\n async (dashboardId: string, e: React.MouseEvent) => {\n e.stopPropagation();\n const msg = tr('bigconsole.dashboards.confirmDelete', 'Are you sure you want to delete this dashboard?');\n const native = await nativeConfirm({\n title: 'Delete dashboard?',\n message: msg,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok = native === null ? window.confirm(msg) : native;\n if (ok) {\n nativeImpact('medium');\n try {\n await deleteDashboard(dashboardId);\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n }\n },\n [deleteDashboard, tr]\n );\n\n const handleCloneDashboard = useCallback(\n async (dashboardId: string, sourceName: string, e: React.MouseEvent) => {\n e.stopPropagation();\n const defaultName = tr('bigconsole.dashboards.clone.defaultSuffix', `${sourceName} (Copy)`);\n const name = window.prompt(\n tr('bigconsole.dashboards.clone.namePrompt', 'Name for the cloned dashboard:'),\n defaultName\n );\n if (!name || !name.trim()) return;\n nativeImpact('medium');\n try {\n const cloned = await cloneDashboard(dashboardId, name.trim());\n if (cloned) {\n if (isNative()) void nativeNotify('success');\n navigate(`/dashboards/${cloned.id}`);\n }\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'project')) {\n return;\n }\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n },\n [cloneDashboard, navigate, tr]\n );\n\n const handleOpenImportDialog = useCallback(() => {\n setIsImportDialogOpen(true);\n }, []);\n\n const handleCloseImportDialog = useCallback(() => {\n setIsImportDialogOpen(false);\n }, []);\n\n const handleImportSuccess = useCallback(\n (dashboardId: string, dashboardName: string) => {\n refetch();\n },\n [refetch]\n );\n\n // Format relative date\n const formatRelativeDate = (dateStr: string) => {\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n\n if (diffMins < 1) return tr('bigconsole.dashboards.time.justNow', 'Just now');\n if (diffMins < 60) return `${diffMins}${tr('bigconsole.dashboards.time.minutesAgoSuffix', 'm ago')}`;\n if (diffHours < 24) return `${diffHours}${tr('bigconsole.dashboards.time.hoursAgoSuffix', 'h ago')}`;\n if (diffDays < 7) return `${diffDays}${tr('bigconsole.dashboards.time.daysAgoSuffix', 'd ago')}`;\n\n return new Intl.DateTimeFormat('en-US', {\n month: 'short',\n day: 'numeric',\n year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined,\n }).format(date);\n };\n\n // Get widget count - uses widgetCount field from backend\n const getWidgetCount = (dashboard: (typeof dashboards)[0]) => {\n return dashboard.widgetCount ?? 0;\n };\n\n // Get page count\n const getPageCount = (dashboard: (typeof dashboards)[0]) => {\n return dashboard.pageCount || 0;\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 <h3\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-status-error-text)',\n }}\n >\n {tr('bigconsole.dashboards.error.title', 'Error loading dashboards')}\n </h3>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n marginTop: 'var(--space-controlGap)',\n }}\n >\n {error.message}\n </p>\n <Button size=\"sm\" onClick={() => refetch()} style={{ marginTop: 'var(--space-formGap)' }}>\n {tr('bigconsole.dashboards.error.retry', 'Retry')}\n </Button>\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\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexWrap: 'wrap',\n gap: 'var(--space-formGap)',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-formGap)', minWidth: 0 }}>\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 <LayoutDashboard\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.dashboards.title', 'Dashboards')}</GradientText>\n </h1>\n <p style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-secondary)', margin: 0 }}>\n {tr('bigconsole.dashboards.subtitle', 'Create and manage your analytics dashboards')}\n </p>\n </div>\n </div>\n\n <CTAOverflowMenu\n primary={{\n label: tr('bigconsole.dashboards.actions.create', 'Create Dashboard'),\n onSelect: handleOpenCreateDialog,\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.dashboards.actions.import', 'Import'),\n onSelect: handleOpenImportDialog,\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 Dashboards are live, shareable views built from widgets that query your data sinks — charts, tables, metrics,\n and drill-downs in one place. Compose a board to monitor a system, share KPIs with your team, or explore data\n interactively.\n </PagePurpose>\n\n {/* Search Bar + View Toggle */}\n <div\n style={{\n marginBottom: 'var(--space-sectionGap)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexWrap: 'wrap',\n gap: 'var(--space-formGap)',\n }}\n >\n <div style={{ position: 'relative', flex: 1, minWidth: 0, maxWidth: '400px' }}>\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.dashboards.search.placeholder', 'Search dashboards...')}\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 {/* Grid / List view toggle */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n padding: 'var(--space-iconGap)',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-bg-sunken)',\n }}\n >\n <Button\n variant={viewMode === 'grid' ? 'default' : 'ghost'}\n size=\"sm\"\n onClick={() => handleSetViewMode('grid')}\n aria-label={tr('bigconsole.dashboards.view.grid', 'Grid view')}\n aria-pressed={viewMode === 'grid'}\n title={tr('bigconsole.dashboards.view.grid', 'Grid view')}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n >\n <LayoutGrid style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'default' : 'ghost'}\n size=\"sm\"\n onClick={() => handleSetViewMode('list')}\n aria-label={tr('bigconsole.dashboards.view.list', 'List view')}\n aria-pressed={viewMode === 'list'}\n title={tr('bigconsole.dashboards.view.list', 'List view')}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n >\n <List style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n </div>\n </div>\n\n {/* Loading state */}\n {loading && dashboards.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.dashboards.loading', 'Loading dashboards...')}\n </p>\n </div>\n </div>\n )}\n\n {/* Dashboard Grid */}\n {!loading && viewMode === 'grid' && filteredDashboards.length > 0 && (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(min(320px, 100%), 1fr))',\n gap: 'var(--space-formGap)',\n }}\n >\n {paginatedDashboards.map((dashboard) => (\n <Card\n key={dashboard.id}\n onClick={() => handleOpenDashboard(dashboard.id)}\n style={{\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n }}\n className=\"hover:shadow-md hover:border-primary/50\"\n >\n <CardContent style={{ padding: 'var(--space-cardPadding)' }}>\n {/* Card Header with Icon and Name */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n }}\n >\n <LayoutDashboard\n style={{\n height: 'var(--size-icon-md)',\n width: 'var(--size-icon-md)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n </div>\n <div style={{ flex: 1, minWidth: 0 }}>\n <h3\n style={{\n fontWeight: 'var(--font-weight-bold)',\n fontSize: 'var(--font-size-body-md)',\n margin: 0,\n color: 'var(--color-text-primary)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {dashboard.name}\n </h3>\n {dashboard.category && (\n <Badge\n variant=\"secondary\"\n style={{\n marginTop: 'var(--space-controlGap)',\n fontSize: '10px',\n fontWeight: 'var(--font-weight-semibold)',\n textTransform: 'uppercase',\n }}\n >\n {dashboard.category}\n </Badge>\n )}\n </div>\n </div>\n\n {/* Description */}\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n lineHeight: 'var(--font-lineHeight-normal)',\n color: 'var(--color-text-secondary)',\n margin: '0 0 var(--space-formGap) 0',\n minHeight: '40px',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n }}\n >\n {dashboard.description || tr('bigconsole.dashboards.card.noDescription', 'No description provided')}\n </p>\n\n {/* Stats Row */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-formGap)',\n marginBottom: 'var(--space-formGap)',\n padding: 'var(--space-controlGap) var(--space-cardPadding)',\n backgroundColor: 'var(--color-bg-sunken)',\n borderRadius: 'var(--radius-button)',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Layers\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-action-primary-bg)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {getPageCount(dashboard)}\n </span>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dashboards.card.pages', 'pages')}\n </span>\n </div>\n <div\n style={{ width: '1px', height: 'var(--size-icon-sm)', backgroundColor: 'var(--color-border)' }}\n />\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <FileText\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-action-primary-bg)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {getWidgetCount(dashboard)}\n </span>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dashboards.card.widgets', 'widgets')}\n </span>\n </div>\n </div>\n\n {/* Footer with Date and Actions */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n paddingTop: 'var(--space-controlGap)',\n borderTop: '1px solid var(--color-border)',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n }}\n >\n <Calendar\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-text-muted)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-muted)',\n }}\n >\n {formatRelativeDate(dashboard.updatedAt)}\n </span>\n </div>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-controlGap)' }}>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleEditDashboard(dashboard.id, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n title={tr('bigconsole.dashboards.actions.editTitle', 'Edit Dashboard')}\n >\n <Pencil style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleCloneDashboard(dashboard.id, dashboard.name, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n title={tr('bigconsole.dashboards.actions.cloneTitle', 'Clone Dashboard')}\n >\n <Copy style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleDeleteDashboard(dashboard.id, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n className=\"hover:text-status-error-text hover:bg-status-error-bg/10\"\n title={tr('bigconsole.dashboards.actions.deleteTitle', 'Delete Dashboard')}\n >\n <Trash2 style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Dashboard List */}\n {!loading && viewMode === 'list' && filteredDashboards.length > 0 && (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: 'var(--space-controlGap)',\n }}\n >\n {paginatedDashboards.map((dashboard) => (\n <Card\n key={dashboard.id}\n onClick={() => handleOpenDashboard(dashboard.id)}\n style={{\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n }}\n className=\"hover:shadow-md hover:border-primary/50\"\n >\n <CardContent style={{ padding: 'var(--space-cardPadding)' }}>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-formGap)',\n flexWrap: 'wrap',\n }}\n >\n {/* Icon */}\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n }}\n >\n <LayoutDashboard\n style={{\n height: 'var(--size-icon-md)',\n width: 'var(--size-icon-md)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n </div>\n\n {/* Name + Description */}\n <div style={{ flex: 1, minWidth: '180px' }}>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n flexWrap: 'wrap',\n }}\n >\n <h3\n style={{\n fontWeight: 'var(--font-weight-bold)',\n fontSize: 'var(--font-size-body-md)',\n margin: 0,\n color: 'var(--color-text-primary)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n maxWidth: '100%',\n }}\n >\n {dashboard.name}\n </h3>\n {dashboard.category && (\n <Badge\n variant=\"secondary\"\n style={{\n fontSize: '10px',\n fontWeight: 'var(--font-weight-semibold)',\n textTransform: 'uppercase',\n }}\n >\n {dashboard.category}\n </Badge>\n )}\n </div>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n lineHeight: 'var(--font-lineHeight-normal)',\n color: 'var(--color-text-secondary)',\n margin: 'var(--space-controlGap) 0 0 0',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {dashboard.description ||\n tr('bigconsole.dashboards.card.noDescription', 'No description provided')}\n </p>\n </div>\n\n {/* Stats + Date */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-formGap)',\n flexWrap: 'wrap',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Layers\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-action-primary-bg)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {getPageCount(dashboard)}\n </span>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dashboards.card.pages', 'pages')}\n </span>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <FileText\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-action-primary-bg)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {getWidgetCount(dashboard)}\n </span>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dashboards.card.widgets', 'widgets')}\n </span>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Calendar\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-text-muted)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-muted)',\n }}\n >\n {formatRelativeDate(dashboard.updatedAt)}\n </span>\n </div>\n </div>\n\n {/* Actions */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-controlGap)' }}>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleEditDashboard(dashboard.id, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n title={tr('bigconsole.dashboards.actions.editTitle', 'Edit Dashboard')}\n >\n <Pencil style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleCloneDashboard(dashboard.id, dashboard.name, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n title={tr('bigconsole.dashboards.actions.cloneTitle', 'Clone Dashboard')}\n >\n <Copy style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleDeleteDashboard(dashboard.id, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n className=\"hover:text-status-error-text hover:bg-status-error-bg/10\"\n title={tr('bigconsole.dashboards.actions.deleteTitle', 'Delete Dashboard')}\n >\n <Trash2 style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Pagination controls */}\n {!loading && filteredDashboards.length > PAGE_SIZE && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--space-controlGap)',\n flexWrap: 'wrap',\n marginTop: 'var(--space-sectionGap)',\n }}\n >\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handlePrevPage}\n disabled={currentPage <= 1}\n aria-label={tr('bigconsole.dashboards.pagination.previous', 'Previous')}\n >\n <ChevronLeft\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n {tr('bigconsole.dashboards.pagination.previous', 'Previous')}\n </Button>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)', flexWrap: 'wrap' }}>\n {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (\n <Button\n key={page}\n variant={page === currentPage ? 'default' : 'ghost'}\n size=\"sm\"\n onClick={() => handleGoToPage(page)}\n aria-current={page === currentPage ? 'page' : undefined}\n style={{\n minWidth: 'var(--size-controlHeight-sm)',\n height: 'var(--size-controlHeight-sm)',\n padding: '0 var(--space-controlGap)',\n borderRadius: 'var(--radius-button)',\n }}\n >\n {page}\n </Button>\n ))}\n </div>\n\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleNextPage}\n disabled={currentPage >= totalPages}\n aria-label={tr('bigconsole.dashboards.pagination.next', 'Next')}\n >\n {tr('bigconsole.dashboards.pagination.next', 'Next')}\n <ChevronRight\n style={{\n marginLeft: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n </Button>\n\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n marginLeft: 'var(--space-controlGap)',\n }}\n >\n {tr('bigconsole.dashboards.pagination.pageOf', `Page ${currentPage} of ${totalPages}`)}\n </span>\n </div>\n )}\n\n {/* Empty state */}\n {!loading && filteredDashboards.length === 0 && (\n <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration={searchQuery ? 'empty-search' : 'empty-data'}\n title={\n searchQuery\n ? tr('bigconsole.dashboards.empty.filteredTitle', 'No dashboards found')\n : tr('bigconsole.dashboards.empty.title', 'No dashboards yet')\n }\n description={\n searchQuery\n ? tr('bigconsole.dashboards.empty.filteredDescription', 'Try adjusting your search terms')\n : tr(\n 'bigconsole.dashboards.empty.description',\n 'Get started by creating your first dashboard to visualize your data'\n )\n }\n action={\n !searchQuery ? (\n <div\n style={{\n display: 'flex',\n gap: 'var(--space-controlGap)',\n flexWrap: 'wrap',\n justifyContent: 'center',\n }}\n >\n <Button onClick={handleOpenCreateDialog}>\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.dashboards.actions.create', 'Create Dashboard')}\n </Button>\n <Button variant=\"outline\" onClick={handleOpenImportDialog}>\n <Upload\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n {tr('bigconsole.dashboards.actions.importDashboard', 'Import Dashboard')}\n </Button>\n </div>\n ) : undefined\n }\n />\n </GlassCard>\n )}\n </div>\n\n {/* Create Dashboard Dialog */}\n <CreateDashboardDialog\n isOpen={isCreateDialogOpen}\n isCreating={isCreating}\n onClose={handleCloseCreateDialog}\n onCreate={handleCreateDashboard}\n />\n\n {/* Import Dashboard Dialog */}\n <ImportDashboardDialog\n isOpen={isImportDialogOpen}\n onClose={handleCloseImportDialog}\n onSuccess={handleImportSuccess}\n />\n </div>\n );\n});\n\nexport default DashboardsPage;\n"],"mappings":";;;;;;;;;;;;;;AAiDA,IAAM,IAAwB,iCAGxB,IAAY,IAML,IAAqB,EAAK,WAA0B;CAC/D,IAAM,IAAW,GAAuB,EAClC,EAAE,SAAM,IAAS,EACjB,IAAK,GACR,GAAa,MAAqB;EACjC,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEzC,CAAC,EAAE,CACJ,EACK,CAAC,GAAa,MAAkB,EAAS,GAAG,EAC5C,CAAC,GAAa,KAAkB,EAAS,EAAE,EAC3C,CAAC,IAAoB,KAAyB,EAAS,GAAM,EAC7D,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,IAAoB,MAAyB,EAAS,GAAM,EAG7D,CAAC,GAAU,MAAe,QAAgC;AAC9D,MAAI;AAEF,UADe,OAAO,aAAa,QAAQ,EAAsB,KAC/C,SAAS,SAAS;UAC9B;AACN,UAAO;;GAET,EAEI,IAAoB,GAAa,MAA0B;AAC/D,KAAY,EAAK;AACjB,MAAI;AACF,UAAO,aAAa,QAAQ,GAAuB,EAAK;UAClD;IAGP,EAAE,CAAC,EAEA,EAAE,YAAS,UAAO,eAAY,oBAAiB,oBAAiB,mBAAgB,eACpF,GAAwB,EAGpB,IAAqB,EAAW,QACnC,MACC,EAAE,KAAK,aAAa,CAAC,SAAS,EAAY,aAAa,CAAC,IACxD,EAAE,aAAa,aAAa,CAAC,SAAS,EAAY,aAAa,CAAC,CACnE,EAGK,IAAa,KAAK,IAAI,GAAG,KAAK,KAAK,EAAmB,SAAS,EAAU,CAAC,EAC1E,KAAkB,IAAc,KAAK,GACrC,IAAsB,EAAmB,MAAM,GAAgB,IAAiB,EAAU;AAUhG,CANA,QAAgB;AACd,IAAe,EAAE;IAChB,CAAC,GAAa,EAAS,CAAC,EAI3B,QAAgB;AACd,EAAI,IAAc,KAChB,EAAe,EAAW;IAE3B,CAAC,GAAa,EAAW,CAAC;CAE7B,IAAM,KAAiB,QAAkB;AACvC,KAAgB,MAAS,KAAK,IAAI,GAAG,IAAO,EAAE,CAAC;IAC9C,EAAE,CAAC,EAEA,KAAiB,QAAkB;AACvC,KAAgB,MAAS,KAAK,IAAI,GAAY,IAAO,EAAE,CAAC;IACvD,CAAC,EAAW,CAAC,EAEV,KAAiB,GAAa,MAAiB;AACnD,IAAe,EAAK;IACnB,EAAE,CAAC,EAEA,IAAyB,QAAkB;AAC/C,IAAsB,GAAK;IAC1B,EAAE,CAAC,EAEA,KAA0B,QAAkB;AAChD,EAAK,KACH,EAAsB,GAAM;IAE7B,CAAC,EAAW,CAAC,EAEV,KAAwB,EAC5B,OAAO,MAAmF;AACxF,IAAc,GAAK;AACnB,MAAI;GACF,IAAM,IAAe,MAAM,EAAgB;IACzC,MAAM,EAAK;IACX,aAAa,EAAK;IAClB,UAAU,EAAK;IACf,MAAM,EAAK;IACZ,CAAC;AAEF,GAAI,MACF,EAAsB,GAAM,EAC5B,EAAS,eAAe,EAAa,GAAG,UAAU;WAE7C,GAAK;AACZ,OAAI,EAA2B,GAAK,UAAU,EAAE;AAC9C,MAAsB,GAAM;AAC5B;;AAMF,SAFA,QAAQ,MAAM,+BAA+B,EAAI,EAC7C,GAAU,IAAO,EAAa,QAAQ,EACpC;YACE;AACR,KAAc,GAAM;;IAGxB,CAAC,GAAiB,EAAS,CAC5B,EAEK,IAAsB,GACzB,MAAwB;AACvB,IAAS,eAAe,IAAc;IAExC,CAAC,EAAS,CACX,EAEK,IAAsB,GACzB,GAAqB,MAAwB;AAE5C,EADA,EAAE,iBAAiB,EACnB,EAAS,eAAe,EAAY,UAAU;IAEhD,CAAC,EAAS,CACX,EAEK,KAAwB,EAC5B,OAAO,GAAqB,MAAwB;AAClD,IAAE,iBAAiB;EACnB,IAAM,IAAM,EAAG,uCAAuC,kDAAkD,EAClG,IAAS,MAAM,EAAc;GACjC,OAAO;GACP,SAAS;GACT,eAAe;GACf,mBAAmB;GACpB,CAAC;AAEF,MADW,MAAW,OAAO,OAAO,QAAQ,EAAI,GAAG,GAC3C;AACN,KAAa,SAAS;AACtB,OAAI;AAEF,IADA,MAAM,EAAgB,EAAY,EAC9B,GAAU,IAAO,EAAa,UAAU;YACrC,GAAK;AAEZ,UADI,GAAU,IAAO,EAAa,QAAQ,EACpC;;;IAIZ,CAAC,GAAiB,EAAG,CACtB,EAEK,KAAuB,EAC3B,OAAO,GAAqB,GAAoB,MAAwB;AACtE,IAAE,iBAAiB;EACnB,IAAM,IAAc,EAAG,6CAA6C,GAAG,EAAW,SAAS,EACrF,IAAO,OAAO,OAClB,EAAG,0CAA0C,iCAAiC,EAC9E,EACD;AACG,SAAC,KAAQ,CAAC,EAAK,MAAM,GACzB;KAAa,SAAS;AACtB,OAAI;IACF,IAAM,IAAS,MAAM,EAAe,GAAa,EAAK,MAAM,CAAC;AAC7D,IAAI,MACE,GAAU,IAAO,EAAa,UAAU,EAC5C,EAAS,eAAe,EAAO,KAAK;YAE/B,GAAK;AACZ,QAAI,EAA2B,GAAK,UAAU,CAC5C;AAGF,UADI,GAAU,IAAO,EAAa,QAAQ,EACpC;;;IAGV;EAAC;EAAgB;EAAU;EAAG,CAC/B,EAEK,KAAyB,QAAkB;AAC/C,KAAsB,GAAK;IAC1B,EAAE,CAAC,EAEA,KAA0B,QAAkB;AAChD,KAAsB,GAAM;IAC3B,EAAE,CAAC,EAEA,KAAsB,GACzB,GAAqB,MAA0B;AAC9C,KAAS;IAEX,CAAC,EAAQ,CACV,EAGK,MAAsB,MAAoB;EAC9C,IAAM,IAAO,IAAI,KAAK,EAAQ,EACxB,oBAAM,IAAI,MAAM,EAChB,IAAS,EAAI,SAAS,GAAG,EAAK,SAAS,EACvC,IAAW,KAAK,MAAM,IAAS,IAAM,EACrC,IAAY,KAAK,MAAM,IAAS,KAAQ,EACxC,IAAW,KAAK,MAAM,IAAS,MAAS;AAO9C,SALI,IAAW,IAAU,EAAG,sCAAsC,WAAW,GACzE,IAAW,KAAW,GAAG,IAAW,EAAG,+CAA+C,QAAQ,KAC9F,IAAY,KAAW,GAAG,IAAY,EAAG,6CAA6C,QAAQ,KAC9F,IAAW,IAAU,GAAG,IAAW,EAAG,4CAA4C,QAAQ,KAEvF,IAAI,KAAK,eAAe,SAAS;GACtC,OAAO;GACP,KAAK;GACL,MAAM,EAAK,aAAa,KAAK,EAAI,aAAa,GAAe,KAAA,IAAZ;GAClD,CAAC,CAAC,OAAO,EAAK;IAIX,MAAkB,MACf,EAAU,eAAe,GAI5B,MAAgB,MACb,EAAU,aAAa;AAyChC,QArCI,IAEA,kBAAC,OAAD;EAAK,OAAO,EAAE,SAAS,4BAA4B;YACjD,kBAAC,GAAD;GACE,OAAO;IACL,aAAa;IACb,iBAAiB;IAClB;aAED,kBAAC,GAAD;IAAa,OAAO,EAAE,SAAS,4BAA4B;cAA3D;KACE,kBAAC,MAAD;MACE,OAAO;OACL,UAAU;OACV,YAAY;OACZ,OAAO;OACR;gBAEA,EAAG,qCAAqC,2BAA2B;MACjE,CAAA;KACL,kBAAC,KAAD;MACE,OAAO;OACL,UAAU;OACV,OAAO;OACP,WAAW;OACZ;gBAEA,EAAM;MACL,CAAA;KACJ,kBAAC,GAAD;MAAQ,MAAK;MAAK,eAAe,GAAS;MAAE,OAAO,EAAE,WAAW,wBAAwB;gBACrF,EAAG,qCAAqC,QAAQ;MAC1C,CAAA;KACG;;GACT,CAAA;EACH,CAAA,GAKR,kBAAC,OAAD;EAAK,OAAO;GAAE,WAAW;GAAS,iBAAiB;GAA0B;YAA7E;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,IAAD,EAAkB,WAAU,UAAW,CAAA,EACvC,kBAAC,OAAD;KAAK,OAAO,EAAE,SAAS,4BAA4B;eACjD,kBAAC,OAAD;MACE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,UAAU;OACV,KAAK;OACN;gBAPH,CASE,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAwB,UAAU;QAAG;iBAA/F,CACE,kBAAC,OAAD;QACE,OAAO;SACL,SAAS;SACT,QAAQ;SACR,OAAO;SACP,YAAY;SACZ,gBAAgB;SAChB,cAAc;SACd,iBAAiB;SAClB;kBAED,kBAAC,GAAD,EACE,OAAO;SACL,QAAQ;SACR,OAAO;SACP,OAAO;SACR,EACD,CAAA;QACE,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;QACE,OAAO;SACL,UAAU;SACV,YAAY;SACZ,QAAQ;SACR,OAAO;SACR;kBAED,kBAAC,IAAD,EAAA,UAAe,EAAG,+BAA+B,aAAa,EAAgB,CAAA;QAC3E,CAAA,EACL,kBAAC,KAAD;QAAG,OAAO;SAAE,UAAU;SAA4B,OAAO;SAA+B,QAAQ;SAAG;kBAChG,EAAG,kCAAkC,8CAA8C;QAClF,CAAA,CACA,EAAA,CAAA,CACF;UAEN,kBAAC,IAAD;OACE,SAAS;QACP,OAAO,EAAG,wCAAwC,mBAAmB;QACrE,UAAU;QACV,MACE,kBAAC,GAAD,EACE,OAAO;SACL,aAAa;SACb,QAAQ;SACR,OAAO;SACR,EACD,CAAA;QAEL;OACD,SAAS,CACP;QACE,OAAO,EAAG,wCAAwC,SAAS;QAC3D,UAAU;QACV,MAAM,kBAAC,GAAD,EAAQ,OAAO;SAAE,QAAQ;SAAuB,OAAO;SAAuB,EAAI,CAAA;QACzF,CACF;OACD,CAAA,CACE;;KACF,CAAA,CACF;;GAGN,kBAAC,OAAD;IAAK,OAAO,EAAE,SAAS,4BAA4B;cAAnD;KACE,kBAAC,IAAD;MAAa,WAAU;gBAA+B;MAIxC,CAAA;KAGd,kBAAC,OAAD;MACE,OAAO;OACL,cAAc;OACd,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,UAAU;OACV,KAAK;OACN;gBARH,CAUE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAY,MAAM;QAAG,UAAU;QAAG,UAAU;QAAS;iBAA7E,CACE,kBAAC,IAAD,EACE,OAAO;QACL,UAAU;QACV,MAAM;QACN,KAAK;QACL,WAAW;QACX,QAAQ;QACR,OAAO;QACP,OAAO;QACR,EACD,CAAA,EACF,kBAAC,IAAD;QACE,MAAK;QACL,aAAa,EAAG,4CAA4C,uBAAuB;QACnF,OAAO;QACP,WAAW,MAAM,GAAe,EAAE,OAAO,MAAM;QAC/C,OAAO;SAAE,aAAa;SAAgC,QAAQ;SAAgC;QAC9F,CAAA,CACE;UAGN,kBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,YAAY;QACZ,KAAK;QACL,SAAS;QACT,cAAc;QACd,iBAAiB;QAClB;iBARH,CAUE,kBAAC,GAAD;QACE,SAAS,MAAa,SAAS,YAAY;QAC3C,MAAK;QACL,eAAe,EAAkB,OAAO;QACxC,cAAY,EAAG,mCAAmC,YAAY;QAC9D,gBAAc,MAAa;QAC3B,OAAO,EAAG,mCAAmC,YAAY;QACzD,OAAO;SACL,QAAQ;SACR,OAAO;SACP,SAAS;SACT,cAAc;SACf;kBAED,kBAAC,IAAD,EAAY,OAAO;SAAE,QAAQ;SAAuB,OAAO;SAAuB,EAAI,CAAA;QAC/E,CAAA,EACT,kBAAC,GAAD;QACE,SAAS,MAAa,SAAS,YAAY;QAC3C,MAAK;QACL,eAAe,EAAkB,OAAO;QACxC,cAAY,EAAG,mCAAmC,YAAY;QAC9D,gBAAc,MAAa;QAC3B,OAAO,EAAG,mCAAmC,YAAY;QACzD,OAAO;SACL,QAAQ;SACR,OAAO;SACP,SAAS;SACT,cAAc;SACf;kBAED,kBAAC,IAAD,EAAM,OAAO;SAAE,QAAQ;SAAuB,OAAO;SAAuB,EAAI,CAAA;QACzE,CAAA,CACL;SACF;;KAGL,KAAW,EAAW,WAAW,KAChC,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,gBAAgB;OAAU,SAAS;OAAU;gBAChG,kBAAC,OAAD;OAAK,OAAO,EAAE,WAAW,UAAU;iBAAnC,CACE,kBAAC,IAAD,EACE,OAAO;QACL,QAAQ;QACR,OAAO;QACP,QAAQ;QACT,EACD,CAAA,EACF,kBAAC,KAAD;QAAG,OAAO;SAAE,UAAU;SAA4B,OAAO;SAA2B;kBACjF,EAAG,iCAAiC,wBAAwB;QAC3D,CAAA,CACA;;MACF,CAAA;KAIP,CAAC,KAAW,MAAa,UAAU,EAAmB,SAAS,KAC9D,kBAAC,OAAD;MACE,OAAO;OACL,SAAS;OACT,qBAAqB;OACrB,KAAK;OACN;gBAEA,EAAoB,KAAK,MACxB,kBAAC,GAAD;OAEE,eAAe,EAAoB,EAAU,GAAG;OAChD,OAAO;QACL,QAAQ;QACR,YAAY;QACb;OACD,WAAU;iBAEV,kBAAC,GAAD;QAAa,OAAO,EAAE,SAAS,4BAA4B;kBAA3D;SAEE,kBAAC,OAAD;UACE,OAAO;WACL,SAAS;WACT,YAAY;WACZ,KAAK;WACL,cAAc;WACf;oBANH,CAQE,kBAAC,OAAD;WACE,OAAO;YACL,SAAS;YACT,QAAQ;YACR,OAAO;YACP,YAAY;YACZ,YAAY;YACZ,gBAAgB;YAChB,cAAc;YACd,iBAAiB;YAClB;qBAED,kBAAC,GAAD,EACE,OAAO;YACL,QAAQ;YACR,OAAO;YACP,OAAO;YACR,EACD,CAAA;WACE,CAAA,EACN,kBAAC,OAAD;WAAK,OAAO;YAAE,MAAM;YAAG,UAAU;YAAG;qBAApC,CACE,kBAAC,MAAD;YACE,OAAO;aACL,YAAY;aACZ,UAAU;aACV,QAAQ;aACR,OAAO;aACP,UAAU;aACV,cAAc;aACd,YAAY;aACb;sBAEA,EAAU;YACR,CAAA,EACJ,EAAU,YACT,kBAAC,GAAD;YACE,SAAQ;YACR,OAAO;aACL,WAAW;aACX,UAAU;aACV,YAAY;aACZ,eAAe;aAChB;sBAEA,EAAU;YACL,CAAA,CAEN;aACF;;SAGN,kBAAC,KAAD;UACE,OAAO;WACL,UAAU;WACV,YAAY;WACZ,OAAO;WACP,QAAQ;WACR,WAAW;WACX,SAAS;WACT,iBAAiB;WACjB,iBAAiB;WACjB,UAAU;WACX;oBAEA,EAAU,eAAe,EAAG,4CAA4C,0BAA0B;UACjG,CAAA;SAGJ,kBAAC,OAAD;UACE,OAAO;WACL,SAAS;WACT,YAAY;WACZ,KAAK;WACL,cAAc;WACd,SAAS;WACT,iBAAiB;WACjB,cAAc;WACf;oBATH;WAWE,kBAAC,OAAD;YAAK,OAAO;aAAE,SAAS;aAAQ,YAAY;aAAU,KAAK;aAAwB;sBAAlF;aACE,kBAAC,GAAD,EACE,OAAO;cACL,QAAQ;cACR,OAAO;cACP,OAAO;cACR,EACD,CAAA;aACF,kBAAC,QAAD;cACE,OAAO;eACL,UAAU;eACV,YAAY;eACZ,OAAO;eACR;wBAEA,GAAa,EAAU;cACnB,CAAA;aACP,kBAAC,QAAD;cAAM,OAAO;eAAE,UAAU;eAA4B,OAAO;eAA2B;wBACpF,EAAG,oCAAoC,QAAQ;cAC3C,CAAA;aACH;;WACN,kBAAC,OAAD,EACE,OAAO;YAAE,OAAO;YAAO,QAAQ;YAAuB,iBAAiB;YAAuB,EAC9F,CAAA;WACF,kBAAC,OAAD;YAAK,OAAO;aAAE,SAAS;aAAQ,YAAY;aAAU,KAAK;aAAwB;sBAAlF;aACE,kBAAC,GAAD,EACE,OAAO;cACL,QAAQ;cACR,OAAO;cACP,OAAO;cACR,EACD,CAAA;aACF,kBAAC,QAAD;cACE,OAAO;eACL,UAAU;eACV,YAAY;eACZ,OAAO;eACR;wBAEA,GAAe,EAAU;cACrB,CAAA;aACP,kBAAC,QAAD;cAAM,OAAO;eAAE,UAAU;eAA4B,OAAO;eAA2B;wBACpF,EAAG,sCAAsC,UAAU;cAC/C,CAAA;aACH;;WACF;;SAGN,kBAAC,OAAD;UACE,OAAO;WACL,SAAS;WACT,YAAY;WACZ,gBAAgB;WAChB,YAAY;WACZ,WAAW;WACZ;oBAPH,CASE,kBAAC,OAAD;WACE,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACN;qBALH,CAOE,kBAAC,GAAD,EACE,OAAO;YACL,QAAQ;YACR,OAAO;YACP,OAAO;YACR,EACD,CAAA,EACF,kBAAC,QAAD;YACE,OAAO;aACL,UAAU;aACV,YAAY;aACZ,OAAO;aACR;sBAEA,GAAmB,EAAU,UAAU;YACnC,CAAA,CACH;cAEN,kBAAC,OAAD;WAAK,OAAO;YAAE,SAAS;YAAQ,YAAY;YAAU,KAAK;YAA2B;qBAArF;YACE,kBAAC,GAAD;aACE,SAAQ;aACR,MAAK;aACL,UAAU,MAAM,EAAoB,EAAU,IAAI,EAAE;aACpD,OAAO;cACL,QAAQ;cACR,OAAO;cACP,SAAS;cACT,cAAc;cACf;aACD,OAAO,EAAG,2CAA2C,iBAAiB;uBAEtE,kBAAC,GAAD,EAAQ,OAAO;cAAE,QAAQ;cAAuB,OAAO;cAAuB,EAAI,CAAA;aAC3E,CAAA;YACT,kBAAC,GAAD;aACE,SAAQ;aACR,MAAK;aACL,UAAU,MAAM,GAAqB,EAAU,IAAI,EAAU,MAAM,EAAE;aACrE,OAAO;cACL,QAAQ;cACR,OAAO;cACP,SAAS;cACT,cAAc;cACf;aACD,OAAO,EAAG,4CAA4C,kBAAkB;uBAExE,kBAAC,GAAD,EAAM,OAAO;cAAE,QAAQ;cAAuB,OAAO;cAAuB,EAAI,CAAA;aACzE,CAAA;YACT,kBAAC,GAAD;aACE,SAAQ;aACR,MAAK;aACL,UAAU,MAAM,GAAsB,EAAU,IAAI,EAAE;aACtD,OAAO;cACL,QAAQ;cACR,OAAO;cACP,SAAS;cACT,cAAc;cACf;aACD,WAAU;aACV,OAAO,EAAG,6CAA6C,mBAAmB;uBAE1E,kBAAC,GAAD,EAAQ,OAAO;cAAE,QAAQ;cAAuB,OAAO;cAAuB,EAAI,CAAA;aAC3E,CAAA;YACL;aACF;;SACM;;OACT,EAlOA,EAAU,GAkOV,CACP;MACE,CAAA;KAIP,CAAC,KAAW,MAAa,UAAU,EAAmB,SAAS,KAC9D,kBAAC,OAAD;MACE,OAAO;OACL,SAAS;OACT,eAAe;OACf,KAAK;OACN;gBAEA,EAAoB,KAAK,MACxB,kBAAC,GAAD;OAEE,eAAe,EAAoB,EAAU,GAAG;OAChD,OAAO;QACL,QAAQ;QACR,YAAY;QACb;OACD,WAAU;iBAEV,kBAAC,GAAD;QAAa,OAAO,EAAE,SAAS,4BAA4B;kBACzD,kBAAC,OAAD;SACE,OAAO;UACL,SAAS;UACT,YAAY;UACZ,KAAK;UACL,UAAU;UACX;mBANH;UASE,kBAAC,OAAD;WACE,OAAO;YACL,SAAS;YACT,QAAQ;YACR,OAAO;YACP,YAAY;YACZ,YAAY;YACZ,gBAAgB;YAChB,cAAc;YACd,iBAAiB;YAClB;qBAED,kBAAC,GAAD,EACE,OAAO;YACL,QAAQ;YACR,OAAO;YACP,OAAO;YACR,EACD,CAAA;WACE,CAAA;UAGN,kBAAC,OAAD;WAAK,OAAO;YAAE,MAAM;YAAG,UAAU;YAAS;qBAA1C,CACE,kBAAC,OAAD;YACE,OAAO;aACL,SAAS;aACT,YAAY;aACZ,KAAK;aACL,UAAU;aACX;sBANH,CAQE,kBAAC,MAAD;aACE,OAAO;cACL,YAAY;cACZ,UAAU;cACV,QAAQ;cACR,OAAO;cACP,UAAU;cACV,cAAc;cACd,YAAY;cACZ,UAAU;cACX;uBAEA,EAAU;aACR,CAAA,EACJ,EAAU,YACT,kBAAC,GAAD;aACE,SAAQ;aACR,OAAO;cACL,UAAU;cACV,YAAY;cACZ,eAAe;cAChB;uBAEA,EAAU;aACL,CAAA,CAEN;eACN,kBAAC,KAAD;YACE,OAAO;aACL,UAAU;aACV,YAAY;aACZ,OAAO;aACP,QAAQ;aACR,UAAU;aACV,cAAc;aACd,YAAY;aACb;sBAEA,EAAU,eACT,EAAG,4CAA4C,0BAA0B;YACzE,CAAA,CACA;;UAGN,kBAAC,OAAD;WACE,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,UAAU;YACX;qBANH;YAQE,kBAAC,OAAD;aAAK,OAAO;cAAE,SAAS;cAAQ,YAAY;cAAU,KAAK;cAAwB;uBAAlF;cACE,kBAAC,GAAD,EACE,OAAO;eACL,QAAQ;eACR,OAAO;eACP,OAAO;eACR,EACD,CAAA;cACF,kBAAC,QAAD;eACE,OAAO;gBACL,UAAU;gBACV,YAAY;gBACZ,OAAO;gBACR;yBAEA,GAAa,EAAU;eACnB,CAAA;cACP,kBAAC,QAAD;eAAM,OAAO;gBAAE,UAAU;gBAA4B,OAAO;gBAA2B;yBACpF,EAAG,oCAAoC,QAAQ;eAC3C,CAAA;cACH;;YACN,kBAAC,OAAD;aAAK,OAAO;cAAE,SAAS;cAAQ,YAAY;cAAU,KAAK;cAAwB;uBAAlF;cACE,kBAAC,GAAD,EACE,OAAO;eACL,QAAQ;eACR,OAAO;eACP,OAAO;eACR,EACD,CAAA;cACF,kBAAC,QAAD;eACE,OAAO;gBACL,UAAU;gBACV,YAAY;gBACZ,OAAO;gBACR;yBAEA,GAAe,EAAU;eACrB,CAAA;cACP,kBAAC,QAAD;eAAM,OAAO;gBAAE,UAAU;gBAA4B,OAAO;gBAA2B;yBACpF,EAAG,sCAAsC,UAAU;eAC/C,CAAA;cACH;;YACN,kBAAC,OAAD;aAAK,OAAO;cAAE,SAAS;cAAQ,YAAY;cAAU,KAAK;cAAwB;uBAAlF,CACE,kBAAC,GAAD,EACE,OAAO;cACL,QAAQ;cACR,OAAO;cACP,OAAO;cACR,EACD,CAAA,EACF,kBAAC,QAAD;cACE,OAAO;eACL,UAAU;eACV,YAAY;eACZ,OAAO;eACR;wBAEA,GAAmB,EAAU,UAAU;cACnC,CAAA,CACH;;YACF;;UAGN,kBAAC,OAAD;WAAK,OAAO;YAAE,SAAS;YAAQ,YAAY;YAAU,KAAK;YAA2B;qBAArF;YACE,kBAAC,GAAD;aACE,SAAQ;aACR,MAAK;aACL,UAAU,MAAM,EAAoB,EAAU,IAAI,EAAE;aACpD,OAAO;cACL,QAAQ;cACR,OAAO;cACP,SAAS;cACT,cAAc;cACf;aACD,OAAO,EAAG,2CAA2C,iBAAiB;uBAEtE,kBAAC,GAAD,EAAQ,OAAO;cAAE,QAAQ;cAAuB,OAAO;cAAuB,EAAI,CAAA;aAC3E,CAAA;YACT,kBAAC,GAAD;aACE,SAAQ;aACR,MAAK;aACL,UAAU,MAAM,GAAqB,EAAU,IAAI,EAAU,MAAM,EAAE;aACrE,OAAO;cACL,QAAQ;cACR,OAAO;cACP,SAAS;cACT,cAAc;cACf;aACD,OAAO,EAAG,4CAA4C,kBAAkB;uBAExE,kBAAC,GAAD,EAAM,OAAO;cAAE,QAAQ;cAAuB,OAAO;cAAuB,EAAI,CAAA;aACzE,CAAA;YACT,kBAAC,GAAD;aACE,SAAQ;aACR,MAAK;aACL,UAAU,MAAM,GAAsB,EAAU,IAAI,EAAE;aACtD,OAAO;cACL,QAAQ;cACR,OAAO;cACP,SAAS;cACT,cAAc;cACf;aACD,WAAU;aACV,OAAO,EAAG,6CAA6C,mBAAmB;uBAE1E,kBAAC,GAAD,EAAQ,OAAO;cAAE,QAAQ;cAAuB,OAAO;cAAuB,EAAI,CAAA;aAC3E,CAAA;YACL;;UACF;;QACM,CAAA;OACT,EAnNA,EAAU,GAmNV,CACP;MACE,CAAA;KAIP,CAAC,KAAW,EAAmB,SAAS,KACvC,kBAAC,OAAD;MACE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,KAAK;OACL,UAAU;OACV,WAAW;OACZ;gBARH;OAUE,kBAAC,GAAD;QACE,SAAQ;QACR,MAAK;QACL,SAAS;QACT,UAAU,KAAe;QACzB,cAAY,EAAG,6CAA6C,WAAW;kBALzE,CAOE,kBAAC,IAAD,EACE,OAAO;SACL,aAAa;SACb,QAAQ;SACR,OAAO;SACR,EACD,CAAA,EACD,EAAG,6CAA6C,WAAW,CACrD;;OAET,kBAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,YAAY;SAAU,KAAK;SAAwB,UAAU;SAAQ;kBACjG,MAAM,KAAK,EAAE,QAAQ,GAAY,GAAG,GAAG,MAAM,IAAI,EAAE,CAAC,KAAK,MACxD,kBAAC,GAAD;SAEE,SAAS,MAAS,IAAc,YAAY;SAC5C,MAAK;SACL,eAAe,GAAe,EAAK;SACnC,gBAAc,MAAS,IAAc,SAAS,KAAA;SAC9C,OAAO;UACL,UAAU;UACV,QAAQ;UACR,SAAS;UACT,cAAc;UACf;mBAEA;SACM,EAbF,EAaE,CACT;QACE,CAAA;OAEN,kBAAC,GAAD;QACE,SAAQ;QACR,MAAK;QACL,SAAS;QACT,UAAU,KAAe;QACzB,cAAY,EAAG,yCAAyC,OAAO;kBALjE,CAOG,EAAG,yCAAyC,OAAO,EACpD,kBAAC,IAAD,EACE,OAAO;SACL,YAAY;SACZ,QAAQ;SACR,OAAO;SACR,EACD,CAAA,CACK;;OAET,kBAAC,QAAD;QACE,OAAO;SACL,UAAU;SACV,OAAO;SACP,YAAY;SACb;kBAEA,EAAG,2CAA2C,QAAQ,EAAY,MAAM,IAAa;QACjF,CAAA;OACH;;KAIP,CAAC,KAAW,EAAmB,WAAW,KACzC,kBAAC,IAAD;MAAW,WAAU;MAAU,WAAU;gBACvC,kBAAC,IAAD;OACE,cAAc,IAAc,iBAAiB;OAC7C,OACE,IACI,EAAG,6CAA6C,sBAAsB,GACtE,EAAG,qCAAqC,oBAAoB;OAElE,aACE,IACI,EAAG,mDAAmD,kCAAkC,GACxF,EACE,2CACA,sEACD;OAEP,QACG,IA8BG,KAAA,IA7BF,kBAAC,OAAD;QACE,OAAO;SACL,SAAS;SACT,KAAK;SACL,UAAU;SACV,gBAAgB;SACjB;kBANH,CAQE,kBAAC,GAAD;SAAQ,SAAS;mBAAjB,CACE,kBAAC,GAAD,EACE,OAAO;UACL,aAAa;UACb,QAAQ;UACR,OAAO;UACR,EACD,CAAA,EACD,EAAG,wCAAwC,mBAAmB,CACxD;YACT,kBAAC,GAAD;SAAQ,SAAQ;SAAU,SAAS;mBAAnC,CACE,kBAAC,GAAD,EACE,OAAO;UACL,aAAa;UACb,QAAQ;UACR,OAAO;UACR,EACD,CAAA,EACD,EAAG,iDAAiD,mBAAmB,CACjE;WACL;;OAGV,CAAA;MACQ,CAAA;KAEV;;GAGN,kBAAC,GAAD;IACE,QAAQ;IACI;IACZ,SAAS;IACT,UAAU;IACV,CAAA;GAGF,kBAAC,GAAD;IACE,QAAQ;IACR,SAAS;IACT,WAAW;IACX,CAAA;GACE;;EAER"}
|
|
1
|
+
{"version":3,"file":"DashboardsPage.js","names":[],"sources":["../../../src/bigconsole/pages/DashboardsPage.tsx"],"sourcesContent":["/**\n * DashboardsPage\n *\n * List and manage dashboards - fetches data from real backend.\n * Uses fe-libs shared UI components and semantic tokens for consistent styling.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useBigConsoleNavigate } from '../context';\nimport { isNative, nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } 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';\nimport {\n Plus,\n Upload,\n Search,\n LayoutDashboard,\n Calendar,\n Layers,\n FileText,\n Pencil,\n Trash2,\n Copy,\n LayoutGrid,\n List,\n ChevronLeft,\n ChevronRight,\n} from 'lucide-react';\nimport { useDashboardOperations } from '../hooks';\nimport { CreateDashboardDialog } from '../components/dashboard/CreateDashboardDialog';\nimport { ImportDashboardDialog } from '../components/dashboard/ImportDashboardDialog';\nimport { reportBigConsoleQuotaError } from '../utils/quota';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst VIEW_MODE_STORAGE_KEY = 'bf-bigconsole-dashboards-view';\n\n/** Number of dashboards shown per page in the list/grid views. */\nconst PAGE_SIZE = 12;\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardsPage: FC = memo(function DashboardsPage() {\n const navigate = useBigConsoleNavigate();\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 [searchQuery, setSearchQuery] = useState('');\n const [currentPage, setCurrentPage] = useState(1);\n const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);\n const [isCreating, setIsCreating] = useState(false);\n const [isImportDialogOpen, setIsImportDialogOpen] = useState(false);\n\n // View mode (grid/list) persisted to localStorage so it survives reloads\n const [viewMode, setViewMode] = useState<'grid' | 'list'>(() => {\n try {\n const stored = window.localStorage.getItem(VIEW_MODE_STORAGE_KEY);\n return stored === 'list' ? 'list' : 'grid';\n } catch {\n return 'grid';\n }\n });\n\n const handleSetViewMode = useCallback((mode: 'grid' | 'list') => {\n setViewMode(mode);\n try {\n window.localStorage.setItem(VIEW_MODE_STORAGE_KEY, mode);\n } catch {\n // Ignore storage errors (e.g. private mode) - view mode still works in-session\n }\n }, []);\n\n const { loading, error, dashboards, createDashboard, deleteDashboard, cloneDashboard, refetch } =\n useDashboardOperations();\n\n // Filter dashboards by search query\n const filteredDashboards = dashboards.filter(\n (d) =>\n d.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n d.description?.toLowerCase().includes(searchQuery.toLowerCase())\n );\n\n // Client-side pagination over the filtered result set\n const totalPages = Math.max(1, Math.ceil(filteredDashboards.length / PAGE_SIZE));\n const pageStartIndex = (currentPage - 1) * PAGE_SIZE;\n const paginatedDashboards = filteredDashboards.slice(pageStartIndex, pageStartIndex + PAGE_SIZE);\n\n // Reset to the first page when the search query or view changes so the\n // visible slice always starts from the top of the (possibly new) result set.\n useEffect(() => {\n setCurrentPage(1);\n }, [searchQuery, viewMode]);\n\n // Guard against the current page exceeding the available pages (e.g. after a\n // delete removes the last item on the final page).\n useEffect(() => {\n if (currentPage > totalPages) {\n setCurrentPage(totalPages);\n }\n }, [currentPage, totalPages]);\n\n const handlePrevPage = useCallback(() => {\n setCurrentPage((prev) => Math.max(1, prev - 1));\n }, []);\n\n const handleNextPage = useCallback(() => {\n setCurrentPage((prev) => Math.min(totalPages, prev + 1));\n }, [totalPages]);\n\n const handleGoToPage = useCallback((page: number) => {\n setCurrentPage(page);\n }, []);\n\n const handleOpenCreateDialog = useCallback(() => {\n setIsCreateDialogOpen(true);\n }, []);\n\n /**\n * A template install creates the dashboard inside the dialog rather than\n * through onCreate, so the list has no idea it happened. Refetch and open the\n * new board — landing on it is what makes the \"connect your data\" prompts\n * discoverable.\n */\n const handleTemplateInstalled = useCallback(\n ({ dashboardId }: { dashboardId: string; widgetsNeedingData: number }) => {\n refetch();\n navigate(`/dashboards/${dashboardId}`);\n },\n [refetch, navigate]\n );\n\n const handleCloseCreateDialog = useCallback(() => {\n if (!isCreating) {\n setIsCreateDialogOpen(false);\n }\n }, [isCreating]);\n\n const handleCreateDashboard = useCallback(\n async (data: { name: string; description: string; category: string; tags?: string[] }) => {\n setIsCreating(true);\n try {\n const newDashboard = await createDashboard({\n name: data.name,\n description: data.description,\n category: data.category,\n tags: data.tags,\n });\n\n if (newDashboard) {\n setIsCreateDialogOpen(false);\n navigate(`/dashboards/${newDashboard.id}/builder`);\n }\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'project')) {\n setIsCreateDialogOpen(false);\n return;\n }\n // Surface the failure instead of swallowing it: log for debugging,\n // notify on native, and rethrow so CreateDashboardDialog displays the error.\n console.error('Failed to create dashboard:', err);\n if (isNative()) void nativeNotify('error');\n throw err;\n } finally {\n setIsCreating(false);\n }\n },\n [createDashboard, navigate]\n );\n\n const handleOpenDashboard = useCallback(\n (dashboardId: string) => {\n navigate(`/dashboards/${dashboardId}`);\n },\n [navigate]\n );\n\n const handleEditDashboard = useCallback(\n (dashboardId: string, e: React.MouseEvent) => {\n e.stopPropagation();\n navigate(`/dashboards/${dashboardId}/builder`);\n },\n [navigate]\n );\n\n const handleDeleteDashboard = useCallback(\n async (dashboardId: string, e: React.MouseEvent) => {\n e.stopPropagation();\n const msg = tr('bigconsole.dashboards.confirmDelete', 'Are you sure you want to delete this dashboard?');\n const native = await nativeConfirm({\n title: 'Delete dashboard?',\n message: msg,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok = native === null ? window.confirm(msg) : native;\n if (ok) {\n nativeImpact('medium');\n try {\n await deleteDashboard(dashboardId);\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n }\n },\n [deleteDashboard, tr]\n );\n\n const handleCloneDashboard = useCallback(\n async (dashboardId: string, sourceName: string, e: React.MouseEvent) => {\n e.stopPropagation();\n const defaultName = tr('bigconsole.dashboards.clone.defaultSuffix', `${sourceName} (Copy)`);\n const name = window.prompt(\n tr('bigconsole.dashboards.clone.namePrompt', 'Name for the cloned dashboard:'),\n defaultName\n );\n if (!name || !name.trim()) return;\n nativeImpact('medium');\n try {\n const cloned = await cloneDashboard(dashboardId, name.trim());\n if (cloned) {\n if (isNative()) void nativeNotify('success');\n navigate(`/dashboards/${cloned.id}`);\n }\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'project')) {\n return;\n }\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n },\n [cloneDashboard, navigate, tr]\n );\n\n const handleOpenImportDialog = useCallback(() => {\n setIsImportDialogOpen(true);\n }, []);\n\n const handleCloseImportDialog = useCallback(() => {\n setIsImportDialogOpen(false);\n }, []);\n\n const handleImportSuccess = useCallback(\n (dashboardId: string, dashboardName: string) => {\n refetch();\n },\n [refetch]\n );\n\n // Format relative date\n const formatRelativeDate = (dateStr: string) => {\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n\n if (diffMins < 1) return tr('bigconsole.dashboards.time.justNow', 'Just now');\n if (diffMins < 60) return `${diffMins}${tr('bigconsole.dashboards.time.minutesAgoSuffix', 'm ago')}`;\n if (diffHours < 24) return `${diffHours}${tr('bigconsole.dashboards.time.hoursAgoSuffix', 'h ago')}`;\n if (diffDays < 7) return `${diffDays}${tr('bigconsole.dashboards.time.daysAgoSuffix', 'd ago')}`;\n\n return new Intl.DateTimeFormat('en-US', {\n month: 'short',\n day: 'numeric',\n year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined,\n }).format(date);\n };\n\n // Get widget count - uses widgetCount field from backend\n const getWidgetCount = (dashboard: (typeof dashboards)[0]) => {\n return dashboard.widgetCount ?? 0;\n };\n\n // Get page count\n const getPageCount = (dashboard: (typeof dashboards)[0]) => {\n return dashboard.pageCount || 0;\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 <h3\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-status-error-text)',\n }}\n >\n {tr('bigconsole.dashboards.error.title', 'Error loading dashboards')}\n </h3>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n marginTop: 'var(--space-controlGap)',\n }}\n >\n {error.message}\n </p>\n <Button size=\"sm\" onClick={() => refetch()} style={{ marginTop: 'var(--space-formGap)' }}>\n {tr('bigconsole.dashboards.error.retry', 'Retry')}\n </Button>\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\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexWrap: 'wrap',\n gap: 'var(--space-formGap)',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-formGap)', minWidth: 0 }}>\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 <LayoutDashboard\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.dashboards.title', 'Dashboards')}</GradientText>\n </h1>\n <p style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-secondary)', margin: 0 }}>\n {tr('bigconsole.dashboards.subtitle', 'Create and manage your analytics dashboards')}\n </p>\n </div>\n </div>\n\n <CTAOverflowMenu\n primary={{\n label: tr('bigconsole.dashboards.actions.create', 'Create Dashboard'),\n onSelect: handleOpenCreateDialog,\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.dashboards.actions.import', 'Import'),\n onSelect: handleOpenImportDialog,\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 Dashboards are live, shareable views built from widgets that query your data sinks — charts, tables, metrics,\n and drill-downs in one place. Compose a board to monitor a system, share KPIs with your team, or explore data\n interactively.\n </PagePurpose>\n\n {/* Search Bar + View Toggle */}\n <div\n style={{\n marginBottom: 'var(--space-sectionGap)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexWrap: 'wrap',\n gap: 'var(--space-formGap)',\n }}\n >\n <div style={{ position: 'relative', flex: 1, minWidth: 0, maxWidth: '400px' }}>\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.dashboards.search.placeholder', 'Search dashboards...')}\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 {/* Grid / List view toggle */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n padding: 'var(--space-iconGap)',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-bg-sunken)',\n }}\n >\n <Button\n variant={viewMode === 'grid' ? 'default' : 'ghost'}\n size=\"sm\"\n onClick={() => handleSetViewMode('grid')}\n aria-label={tr('bigconsole.dashboards.view.grid', 'Grid view')}\n aria-pressed={viewMode === 'grid'}\n title={tr('bigconsole.dashboards.view.grid', 'Grid view')}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n >\n <LayoutGrid style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'default' : 'ghost'}\n size=\"sm\"\n onClick={() => handleSetViewMode('list')}\n aria-label={tr('bigconsole.dashboards.view.list', 'List view')}\n aria-pressed={viewMode === 'list'}\n title={tr('bigconsole.dashboards.view.list', 'List view')}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n >\n <List style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n </div>\n </div>\n\n {/* Loading state */}\n {loading && dashboards.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.dashboards.loading', 'Loading dashboards...')}\n </p>\n </div>\n </div>\n )}\n\n {/* Dashboard Grid */}\n {!loading && viewMode === 'grid' && filteredDashboards.length > 0 && (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(min(320px, 100%), 1fr))',\n gap: 'var(--space-formGap)',\n }}\n >\n {paginatedDashboards.map((dashboard) => (\n <Card\n key={dashboard.id}\n onClick={() => handleOpenDashboard(dashboard.id)}\n style={{\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n }}\n className=\"hover:shadow-md hover:border-primary/50\"\n >\n <CardContent style={{ padding: 'var(--space-cardPadding)' }}>\n {/* Card Header with Icon and Name */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n }}\n >\n <LayoutDashboard\n style={{\n height: 'var(--size-icon-md)',\n width: 'var(--size-icon-md)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n </div>\n <div style={{ flex: 1, minWidth: 0 }}>\n <h3\n style={{\n fontWeight: 'var(--font-weight-bold)',\n fontSize: 'var(--font-size-body-md)',\n margin: 0,\n color: 'var(--color-text-primary)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {dashboard.name}\n </h3>\n {dashboard.category && (\n <Badge\n variant=\"secondary\"\n style={{\n marginTop: 'var(--space-controlGap)',\n fontSize: '10px',\n fontWeight: 'var(--font-weight-semibold)',\n textTransform: 'uppercase',\n }}\n >\n {dashboard.category}\n </Badge>\n )}\n </div>\n </div>\n\n {/* Description */}\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n lineHeight: 'var(--font-lineHeight-normal)',\n color: 'var(--color-text-secondary)',\n margin: '0 0 var(--space-formGap) 0',\n minHeight: '40px',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n }}\n >\n {dashboard.description || tr('bigconsole.dashboards.card.noDescription', 'No description provided')}\n </p>\n\n {/* Stats Row */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-formGap)',\n marginBottom: 'var(--space-formGap)',\n padding: 'var(--space-controlGap) var(--space-cardPadding)',\n backgroundColor: 'var(--color-bg-sunken)',\n borderRadius: 'var(--radius-button)',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Layers\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-action-primary-bg)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {getPageCount(dashboard)}\n </span>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dashboards.card.pages', 'pages')}\n </span>\n </div>\n <div\n style={{ width: '1px', height: 'var(--size-icon-sm)', backgroundColor: 'var(--color-border)' }}\n />\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <FileText\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-action-primary-bg)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {getWidgetCount(dashboard)}\n </span>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dashboards.card.widgets', 'widgets')}\n </span>\n </div>\n </div>\n\n {/* Footer with Date and Actions */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n paddingTop: 'var(--space-controlGap)',\n borderTop: '1px solid var(--color-border)',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n }}\n >\n <Calendar\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-text-muted)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-muted)',\n }}\n >\n {formatRelativeDate(dashboard.updatedAt)}\n </span>\n </div>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-controlGap)' }}>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleEditDashboard(dashboard.id, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n title={tr('bigconsole.dashboards.actions.editTitle', 'Edit Dashboard')}\n >\n <Pencil style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleCloneDashboard(dashboard.id, dashboard.name, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n title={tr('bigconsole.dashboards.actions.cloneTitle', 'Clone Dashboard')}\n >\n <Copy style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleDeleteDashboard(dashboard.id, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n className=\"hover:text-status-error-text hover:bg-status-error-bg/10\"\n title={tr('bigconsole.dashboards.actions.deleteTitle', 'Delete Dashboard')}\n >\n <Trash2 style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Dashboard List */}\n {!loading && viewMode === 'list' && filteredDashboards.length > 0 && (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: 'var(--space-controlGap)',\n }}\n >\n {paginatedDashboards.map((dashboard) => (\n <Card\n key={dashboard.id}\n onClick={() => handleOpenDashboard(dashboard.id)}\n style={{\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n }}\n className=\"hover:shadow-md hover:border-primary/50\"\n >\n <CardContent style={{ padding: 'var(--space-cardPadding)' }}>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-formGap)',\n flexWrap: 'wrap',\n }}\n >\n {/* Icon */}\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n flexShrink: 0,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n }}\n >\n <LayoutDashboard\n style={{\n height: 'var(--size-icon-md)',\n width: 'var(--size-icon-md)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n </div>\n\n {/* Name + Description */}\n <div style={{ flex: 1, minWidth: '180px' }}>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n flexWrap: 'wrap',\n }}\n >\n <h3\n style={{\n fontWeight: 'var(--font-weight-bold)',\n fontSize: 'var(--font-size-body-md)',\n margin: 0,\n color: 'var(--color-text-primary)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n maxWidth: '100%',\n }}\n >\n {dashboard.name}\n </h3>\n {dashboard.category && (\n <Badge\n variant=\"secondary\"\n style={{\n fontSize: '10px',\n fontWeight: 'var(--font-weight-semibold)',\n textTransform: 'uppercase',\n }}\n >\n {dashboard.category}\n </Badge>\n )}\n </div>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n lineHeight: 'var(--font-lineHeight-normal)',\n color: 'var(--color-text-secondary)',\n margin: 'var(--space-controlGap) 0 0 0',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {dashboard.description ||\n tr('bigconsole.dashboards.card.noDescription', 'No description provided')}\n </p>\n </div>\n\n {/* Stats + Date */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-formGap)',\n flexWrap: 'wrap',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Layers\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-action-primary-bg)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {getPageCount(dashboard)}\n </span>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dashboards.card.pages', 'pages')}\n </span>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <FileText\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-action-primary-bg)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {getWidgetCount(dashboard)}\n </span>\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n {tr('bigconsole.dashboards.card.widgets', 'widgets')}\n </span>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Calendar\n style={{\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n color: 'var(--color-text-muted)',\n }}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-muted)',\n }}\n >\n {formatRelativeDate(dashboard.updatedAt)}\n </span>\n </div>\n </div>\n\n {/* Actions */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-controlGap)' }}>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleEditDashboard(dashboard.id, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n title={tr('bigconsole.dashboards.actions.editTitle', 'Edit Dashboard')}\n >\n <Pencil style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleCloneDashboard(dashboard.id, dashboard.name, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n title={tr('bigconsole.dashboards.actions.cloneTitle', 'Clone Dashboard')}\n >\n <Copy style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => handleDeleteDashboard(dashboard.id, e)}\n style={{\n height: 'var(--size-controlHeight-sm)',\n width: 'var(--size-controlHeight-sm)',\n padding: 0,\n borderRadius: 'var(--radius-button)',\n }}\n className=\"hover:text-status-error-text hover:bg-status-error-bg/10\"\n title={tr('bigconsole.dashboards.actions.deleteTitle', 'Delete Dashboard')}\n >\n <Trash2 style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Pagination controls */}\n {!loading && filteredDashboards.length > PAGE_SIZE && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--space-controlGap)',\n flexWrap: 'wrap',\n marginTop: 'var(--space-sectionGap)',\n }}\n >\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handlePrevPage}\n disabled={currentPage <= 1}\n aria-label={tr('bigconsole.dashboards.pagination.previous', 'Previous')}\n >\n <ChevronLeft\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n {tr('bigconsole.dashboards.pagination.previous', 'Previous')}\n </Button>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)', flexWrap: 'wrap' }}>\n {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (\n <Button\n key={page}\n variant={page === currentPage ? 'default' : 'ghost'}\n size=\"sm\"\n onClick={() => handleGoToPage(page)}\n aria-current={page === currentPage ? 'page' : undefined}\n style={{\n minWidth: 'var(--size-controlHeight-sm)',\n height: 'var(--size-controlHeight-sm)',\n padding: '0 var(--space-controlGap)',\n borderRadius: 'var(--radius-button)',\n }}\n >\n {page}\n </Button>\n ))}\n </div>\n\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleNextPage}\n disabled={currentPage >= totalPages}\n aria-label={tr('bigconsole.dashboards.pagination.next', 'Next')}\n >\n {tr('bigconsole.dashboards.pagination.next', 'Next')}\n <ChevronRight\n style={{\n marginLeft: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n </Button>\n\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n marginLeft: 'var(--space-controlGap)',\n }}\n >\n {tr('bigconsole.dashboards.pagination.pageOf', `Page ${currentPage} of ${totalPages}`)}\n </span>\n </div>\n )}\n\n {/* Empty state */}\n {!loading && filteredDashboards.length === 0 && (\n <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration={searchQuery ? 'empty-search' : 'empty-data'}\n title={\n searchQuery\n ? tr('bigconsole.dashboards.empty.filteredTitle', 'No dashboards found')\n : tr('bigconsole.dashboards.empty.title', 'No dashboards yet')\n }\n description={\n searchQuery\n ? tr('bigconsole.dashboards.empty.filteredDescription', 'Try adjusting your search terms')\n : tr(\n 'bigconsole.dashboards.empty.description',\n 'Get started by creating your first dashboard to visualize your data'\n )\n }\n action={\n !searchQuery ? (\n <div\n style={{\n display: 'flex',\n gap: 'var(--space-controlGap)',\n flexWrap: 'wrap',\n justifyContent: 'center',\n }}\n >\n <Button onClick={handleOpenCreateDialog}>\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.dashboards.actions.create', 'Create Dashboard')}\n </Button>\n <Button variant=\"outline\" onClick={handleOpenImportDialog}>\n <Upload\n style={{\n marginRight: 'var(--space-iconGap)',\n height: 'var(--size-icon-sm)',\n width: 'var(--size-icon-sm)',\n }}\n />\n {tr('bigconsole.dashboards.actions.importDashboard', 'Import Dashboard')}\n </Button>\n </div>\n ) : undefined\n }\n />\n </GlassCard>\n )}\n </div>\n\n {/* Create Dashboard Dialog */}\n <CreateDashboardDialog\n isOpen={isCreateDialogOpen}\n isCreating={isCreating}\n onClose={handleCloseCreateDialog}\n onCreate={handleCreateDashboard}\n onTemplateInstalled={handleTemplateInstalled}\n />\n\n {/* Import Dashboard Dialog */}\n <ImportDashboardDialog\n isOpen={isImportDialogOpen}\n onClose={handleCloseImportDialog}\n onSuccess={handleImportSuccess}\n />\n </div>\n );\n});\n\nexport default DashboardsPage;\n"],"mappings":"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"}
|