@burdenoff/microfe-bigconsole 2026.615.2 → 2026.616.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"InsightsPage.js","names":[],"sources":["../../../src/bigconsole/pages/InsightsPage.tsx"],"sourcesContent":["/**\n * InsightsPage\n *\n * AI-powered dashboard generation using the Prompt module backend.\n * Users enter a natural language prompt to generate dashboards with\n * suggested widgets, layouts, and configurations.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport {\n Sparkles,\n Loader2,\n Trash2,\n ArrowRight,\n Clock,\n CheckCircle,\n XCircle,\n AlertCircle,\n RefreshCw,\n Eye,\n Ban,\n Wand2,\n X,\n} from 'lucide-react';\n\n// ============================================================================\n// GRAPHQL OPERATIONS (inline until codegen is configured for prompt module)\n// TODO(BOFF-2055): Replace inline gql with codegen-generated hooks\n// ============================================================================\n\nconst LIST_PROMPT_GENERATIONS = gql`\n query ListPromptGenerations($status: PromptGenerationStatus, $first: Int) {\n listPromptGenerations(status: $status, first: $first) {\n edges {\n node {\n id\n prompt\n status\n dashboardId\n errorMessage\n createdAt\n completedAt\n generatedSpec {\n name\n description\n overallConfidence\n pages {\n name\n widgets {\n title\n type\n }\n }\n }\n }\n }\n totalCount\n }\n }\n`;\n\nconst GET_PROMPT_SUGGESTIONS = gql`\n query GetPromptSuggestions($category: String, $limit: Int) {\n getPromptSuggestions(category: $category, limit: $limit) {\n id\n category\n title\n prompt\n exampleDescription\n tags\n }\n }\n`;\n\nconst GENERATE_DASHBOARD = gql`\n mutation GenerateDashboardFromPrompt($input: GenerateDashboardFromPromptInput!) {\n generateDashboardFromPrompt(input: $input) {\n id\n status\n prompt\n }\n }\n`;\n\nconst CREATE_FROM_GENERATION = gql`\n mutation CreateDashboardFromGeneration($input: CreateDashboardFromGenerationInput!) {\n createDashboardFromGeneration(input: $input) {\n id\n }\n }\n`;\n\nconst DELETE_GENERATION = gql`\n mutation DeletePromptGeneration($generationId: ID!) {\n deletePromptGeneration(generationId: $generationId)\n }\n`;\n\nconst CANCEL_GENERATION = gql`\n mutation CancelPromptGeneration($generationId: ID!) {\n cancelPromptGeneration(generationId: $generationId) {\n id\n status\n }\n }\n`;\n\nconst REGENERATE_DASHBOARD = gql`\n mutation RegenerateDashboard($input: RegenerateDashboardInput!) {\n regenerateDashboard(input: $input) {\n id\n status\n prompt\n }\n }\n`;\n\nconst REFINE_GENERATION = gql`\n mutation RefineGeneratedDashboard($input: RefineGeneratedDashboardInput!) {\n refineGeneratedDashboard(input: $input) {\n id\n status\n prompt\n }\n }\n`;\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface ListPromptGenerationsData {\n listPromptGenerations: {\n edges: Array<{ node: PromptGeneration }>;\n totalCount: number;\n };\n}\n\ninterface GetPromptSuggestionsData {\n getPromptSuggestions: PromptSuggestion[];\n}\n\ninterface GenerateDashboardData {\n generateDashboardFromPrompt: {\n id: string;\n status: string;\n prompt: string;\n };\n}\n\ninterface CreateFromGenerationData {\n createDashboardFromGeneration: {\n id: string;\n };\n}\n\nconst NON_TERMINAL_STATUSES = ['PENDING', 'PROCESSING', 'GENERATING'];\n\ninterface PromptSuggestion {\n id: string;\n category: string;\n title: string;\n prompt: string;\n exampleDescription?: string;\n tags: string[];\n}\n\ninterface PromptGeneration {\n id: string;\n prompt: string;\n status: string;\n dashboardId?: string;\n errorMessage?: string;\n createdAt: string;\n completedAt?: string;\n generatedSpec?: {\n name: string;\n description?: string;\n overallConfidence: number;\n pages: Array<{\n name: string;\n widgets: Array<{ title: string; type: string }>;\n }>;\n };\n}\n\n// ============================================================================\n// STATUS HELPERS\n// ============================================================================\n\nfunction getStatusIcon(status: string) {\n switch (status) {\n case 'COMPLETED':\n return <CheckCircle size={16} className=\"text-green-500\" />;\n case 'FAILED':\n return <XCircle size={16} className=\"text-red-500\" />;\n case 'CANCELLED':\n return <XCircle size={16} className=\"text-gray-400\" />;\n case 'PENDING':\n case 'PROCESSING':\n case 'GENERATING':\n return <Loader2 size={16} className=\"text-blue-500 animate-spin\" />;\n default:\n return <Clock size={16} className=\"text-gray-400\" />;\n }\n}\n\nfunction getStatusLabel(status: string) {\n switch (status) {\n case 'COMPLETED':\n return 'Completed';\n case 'FAILED':\n return 'Failed';\n case 'CANCELLED':\n return 'Cancelled';\n case 'PENDING':\n return 'Pending';\n case 'PROCESSING':\n return 'Processing';\n case 'GENERATING':\n return 'Generating';\n default:\n return status;\n }\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const InsightsPage: FC = memo(function InsightsPage() {\n const navigate = useAppNavigate();\n const [promptText, setPromptText] = useState('');\n const [isGenerating, setIsGenerating] = useState(false);\n const [mutationError, setMutationError] = useState<string | null>(null);\n // Track per-generation in-flight mutations independently so cancelling A\n // does not visually \"busy\" B's regenerate (and vice-versa).\n const [busyIds, setBusyIds] = useState<Set<string>>(() => new Set());\n const [previewGen, setPreviewGen] = useState<PromptGeneration | null>(null);\n const [refineGen, setRefineGen] = useState<PromptGeneration | null>(null);\n const [refineText, setRefineText] = useState('');\n\n const markBusy = useCallback((id: string) => {\n setBusyIds((prev) => new Set(prev).add(id));\n }, []);\n const clearBusy = useCallback((id: string) => {\n setBusyIds((prev) => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n // Fetch existing generations\n const {\n data: generationsData,\n loading: generationsLoading,\n refetch: refetchGenerations,\n } = useQuery<ListPromptGenerationsData>(LIST_PROMPT_GENERATIONS, {\n variables: { first: 20 },\n fetchPolicy: 'cache-and-network',\n });\n\n // Fetch suggestions\n const { data: suggestionsData } = useQuery<GetPromptSuggestionsData>(GET_PROMPT_SUGGESTIONS, {\n variables: { limit: 6 },\n });\n\n // Mutations\n const [generateDashboard] = useMutation<GenerateDashboardData>(GENERATE_DASHBOARD);\n const [createFromGeneration] = useMutation<CreateFromGenerationData>(CREATE_FROM_GENERATION);\n const [deleteGeneration] = useMutation(DELETE_GENERATION);\n const [cancelGeneration] = useMutation(CANCEL_GENERATION);\n const [regenerateDashboard] = useMutation(REGENERATE_DASHBOARD);\n const [refineGeneration] = useMutation(REFINE_GENERATION);\n\n const generations: PromptGeneration[] = generationsData?.listPromptGenerations?.edges?.map((edge) => edge.node) || [];\n const suggestions: PromptSuggestion[] = suggestionsData?.getPromptSuggestions || [];\n\n // Poll for in-progress generations every 3 seconds\n useEffect(() => {\n const hasActiveGenerations = generations.some((gen) => NON_TERMINAL_STATUSES.includes(gen.status));\n if (!hasActiveGenerations) return;\n\n const interval = setInterval(() => {\n refetchGenerations();\n }, 3_000);\n\n return () => clearInterval(interval);\n }, [generations, refetchGenerations]);\n\n // Close the Preview / Refine modals on Escape for keyboard accessibility.\n useEffect(() => {\n if (!previewGen && !refineGen) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setPreviewGen(null);\n setRefineGen(null);\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [previewGen, refineGen]);\n\n // Handlers\n const handleGenerate = useCallback(async () => {\n if (!promptText.trim() || isGenerating) return;\n if (promptText.length > 2000) {\n setMutationError('Prompt is too long (max 2000 characters).');\n return;\n }\n setMutationError(null);\n setIsGenerating(true);\n try {\n await generateDashboard({\n variables: {\n input: {\n prompt: promptText.trim(),\n styleType: 'MODERN',\n layoutType: 'GRID',\n maxWidgets: 8,\n },\n },\n });\n setPromptText('');\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to generate dashboard');\n } finally {\n setIsGenerating(false);\n }\n }, [promptText, isGenerating, generateDashboard, refetchGenerations]);\n\n const handleCreateDashboard = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n try {\n const result = await createFromGeneration({\n variables: { input: { generationId } },\n });\n const dashboardId = result.data?.createDashboardFromGeneration?.id;\n if (dashboardId) {\n navigate(`/dashboards/${dashboardId}`);\n }\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to create dashboard');\n }\n },\n [createFromGeneration, navigate]\n );\n\n const handleDelete = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n try {\n await deleteGeneration({ variables: { generationId } });\n if (isNative()) void nativeNotify('success');\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to delete generation');\n if (isNative()) void nativeNotify('error');\n }\n },\n [deleteGeneration, refetchGenerations]\n );\n\n // Cancel an in-flight (PENDING/PROCESSING/GENERATING) generation.\n const handleCancel = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n markBusy(generationId);\n try {\n await cancelGeneration({ variables: { generationId } });\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to cancel generation');\n } finally {\n clearBusy(generationId);\n }\n },\n [cancelGeneration, refetchGenerations]\n );\n\n // Regenerate from an existing generation, reusing its original prompt.\n const handleRegenerate = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n markBusy(generationId);\n try {\n await regenerateDashboard({ variables: { input: { generationId } } });\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to regenerate dashboard');\n } finally {\n clearBusy(generationId);\n }\n },\n [regenerateDashboard, refetchGenerations]\n );\n\n // Submit refinement instructions for a completed generation.\n const handleRefineSubmit = useCallback(async () => {\n if (!refineGen || !refineText.trim()) return;\n const generationId = refineGen.id;\n setMutationError(null);\n markBusy(generationId);\n try {\n await refineGeneration({\n variables: { input: { generationId, refinementPrompt: refineText.trim() } },\n });\n setRefineGen(null);\n setRefineText('');\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to refine generation');\n } finally {\n clearBusy(generationId);\n }\n }, [refineGen, refineText, refineGeneration, refetchGenerations]);\n\n const handleSuggestionClick = useCallback((prompt: string) => {\n setPromptText(prompt);\n }, []);\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 {/* Header */}\n <div style={{ marginBottom: 'var(--space-sectionGap)' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Sparkles size={24} style={{ color: 'var(--color-action-primary-bg)' }} />\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 AI Insights\n </h1>\n </div>\n <p style={{ fontSize: 'var(--font-size-body-md)', color: 'var(--color-text-secondary)', marginTop: 4 }}>\n Generate dashboards from natural language prompts\n </p>\n </div>\n\n {/* Prompt Input */}\n <div\n style={{\n backgroundColor: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border)',\n borderRadius: 'var(--radius-card)',\n padding: 'var(--space-cardPadding)',\n marginBottom: 'var(--space-sectionGap)',\n }}\n >\n <label\n htmlFor=\"prompt-input\"\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n marginBottom: 8,\n }}\n >\n Describe the dashboard you want to create\n </label>\n <textarea\n id=\"prompt-input\"\n value={promptText}\n onChange={(e) => setPromptText(e.target.value)}\n placeholder=\"e.g., Create a sales dashboard showing revenue trends, top products, and regional performance\"\n rows={3}\n maxLength={2000}\n style={{\n width: '100%',\n padding: '8px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-canvas)',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-md)',\n resize: 'vertical',\n outline: 'none',\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) handleGenerate();\n }}\n />\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginTop: 8,\n flexWrap: 'wrap',\n gap: 8,\n }}\n >\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n Press Ctrl+Enter to generate\n </span>\n <button\n onClick={handleGenerate}\n disabled={!promptText.trim() || isGenerating}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '8px 16px',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n color: 'var(--color-action-primary-text)',\n fontWeight: 'var(--font-weight-medium)',\n fontSize: 'var(--font-size-body-sm)',\n border: 'none',\n cursor: !promptText.trim() || isGenerating ? 'not-allowed' : 'pointer',\n opacity: !promptText.trim() || isGenerating ? 0.5 : 1,\n }}\n >\n {isGenerating ? <Loader2 size={16} className=\"animate-spin\" /> : <Sparkles size={16} />}\n Generate Dashboard\n </button>\n </div>\n </div>\n\n {/* Mutation Error Banner */}\n {mutationError && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n padding: '10px 16px',\n marginBottom: 'var(--space-sectionGap)',\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-status-error-text)',\n backgroundColor: 'var(--color-bg-surface)',\n color: 'var(--color-status-error-text)',\n fontSize: 'var(--font-size-body-sm)',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <AlertCircle size={16} />\n {mutationError}\n </div>\n <button\n onClick={() => setMutationError(null)}\n style={{\n background: 'none',\n border: 'none',\n color: 'var(--color-status-error-text)',\n cursor: 'pointer',\n fontSize: 'var(--font-size-body-md)',\n padding: '0 4px',\n }}\n aria-label=\"Dismiss error\"\n >\n &times;\n </button>\n </div>\n )}\n\n {/* Suggestions */}\n {suggestions.length > 0 && (\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 Suggestions\n </h2>\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(min(280px, 100%), 1fr))',\n gap: 12,\n }}\n >\n {suggestions.map((s) => (\n <button\n key={s.id}\n onClick={() => handleSuggestionClick(s.prompt)}\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 }}\n >\n <div\n style={{\n fontWeight: 'var(--font-weight-medium)',\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-primary)',\n marginBottom: 4,\n }}\n >\n {s.title}\n </div>\n <div\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n lineHeight: 1.4,\n }}\n >\n {s.exampleDescription || s.prompt.slice(0, 80)}\n </div>\n <div style={{ display: 'flex', gap: 4, marginTop: 8, flexWrap: 'wrap' }}>\n {s.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n style={{\n fontSize: 11,\n padding: '2px 6px',\n borderRadius: 4,\n backgroundColor: 'var(--color-bg-sunken)',\n color: 'var(--color-text-muted)',\n }}\n >\n {tag}\n </span>\n ))}\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Generations History */}\n <div>\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 Recent Generations\n </h2>\n\n {generationsLoading && generations.length === 0 ? (\n <div style={{ textAlign: 'center', padding: 40, color: 'var(--color-text-muted)' }}>\n <Loader2 size={24} className=\"animate-spin\" style={{ margin: '0 auto 8px' }} />\n <p>Loading generations...</p>\n </div>\n ) : generations.length === 0 ? (\n <div\n style={{\n textAlign: 'center',\n padding: 40,\n backgroundColor: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border)',\n borderRadius: 'var(--radius-card)',\n }}\n >\n <Sparkles size={32} style={{ color: 'var(--color-text-muted)', margin: '0 auto 8px' }} />\n <p style={{ color: 'var(--color-text-secondary)', fontWeight: 'var(--font-weight-medium)' }}>\n No generations yet\n </p>\n <p style={{ color: 'var(--color-text-muted)', fontSize: 'var(--font-size-body-sm)', marginTop: 4 }}>\n Describe a dashboard above to get started\n </p>\n </div>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n {generations.map((gen) => (\n <div\n key={gen.id}\n style={{\n padding: 16,\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-surface)',\n }}\n >\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'flex-start',\n flexWrap: 'wrap',\n gap: 8,\n }}\n >\n <div style={{ flex: '1 1 200px', minWidth: 0 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4, minWidth: 0 }}>\n {getStatusIcon(gen.status)}\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {gen.generatedSpec?.name || 'Generating...'}\n </span>\n <span\n style={{\n fontSize: 11,\n color: 'var(--color-text-muted)',\n padding: '2px 6px',\n borderRadius: 4,\n backgroundColor: 'var(--color-bg-sunken)',\n }}\n >\n {getStatusLabel(gen.status)}\n </span>\n </div>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n marginBottom: 4,\n }}\n >\n {gen.prompt.length > 120 ? gen.prompt.slice(0, 120) + '...' : gen.prompt}\n </p>\n {gen.generatedSpec && (\n <div style={{ fontSize: 11, color: 'var(--color-text-muted)' }}>\n {gen.generatedSpec.pages.reduce((acc, p) => acc + p.widgets.length, 0)} widgets across{' '}\n {gen.generatedSpec.pages.length} page(s) &middot;{' '}\n {Math.round(gen.generatedSpec.overallConfidence * 100)}% confidence\n </div>\n )}\n {gen.errorMessage && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n marginTop: 4,\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-status-error-text)',\n }}\n >\n <AlertCircle size={14} />\n {gen.errorMessage}\n </div>\n )}\n </div>\n <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>\n {NON_TERMINAL_STATUSES.includes(gen.status) && (\n <button\n onClick={() => {\n nativeImpact('medium');\n void handleCancel(gen.id);\n }}\n disabled={busyIds.has(gen.id)}\n title=\"Cancel generation\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-status-warning-text)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: busyIds.has(gen.id) ? 'wait' : 'pointer',\n }}\n >\n <Ban size={14} /> Cancel\n </button>\n )}\n {gen.status === 'COMPLETED' && gen.generatedSpec && (\n <button\n onClick={() => setPreviewGen(gen)}\n title=\"Preview proposed layout\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n <Eye size={14} /> Preview\n </button>\n )}\n {gen.status === 'COMPLETED' && gen.generatedSpec && (\n <button\n onClick={() => {\n setRefineGen(gen);\n setRefineText('');\n }}\n title=\"Refine with instructions\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n <Wand2 size={14} /> Refine\n </button>\n )}\n {(gen.status === 'COMPLETED' || gen.status === 'FAILED' || gen.status === 'CANCELLED') && (\n <button\n onClick={() => {\n nativeImpact('medium');\n void handleRegenerate(gen.id);\n }}\n disabled={busyIds.has(gen.id)}\n title=\"Regenerate with the same prompt\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: busyIds.has(gen.id) ? 'wait' : 'pointer',\n }}\n >\n <RefreshCw size={14} className={busyIds.has(gen.id) ? 'animate-spin' : ''} /> Regenerate\n </button>\n )}\n {gen.status === 'COMPLETED' && !gen.dashboardId && (\n <button\n onClick={() => handleCreateDashboard(gen.id)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n color: 'var(--color-action-primary-text)',\n border: 'none',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n Create <ArrowRight size={14} />\n </button>\n )}\n {gen.dashboardId && (\n <button\n onClick={() => navigate(`/dashboards/${gen.dashboardId}`)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n View Dashboard <ArrowRight size={14} />\n </button>\n )}\n <button\n onClick={() => {\n nativeImpact('medium');\n void handleDelete(gen.id);\n }}\n title=\"Delete generation\"\n style={{\n display: 'flex',\n alignItems: 'center',\n padding: 6,\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-muted)',\n cursor: 'pointer',\n }}\n >\n <Trash2 size={14} />\n </button>\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Preview modal — shows the AI-proposed page/widget structure before creation */}\n {previewGen?.generatedSpec && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Generation preview\"\n onClick={() => setPreviewGen(null)}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n backgroundColor: 'var(--color-overlay-scrim)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 'var(--space-pagePadding)',\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n style={{\n width: 'min(640px, 100%)',\n maxHeight: '80vh',\n overflowY: 'auto',\n backgroundColor: 'var(--color-bg-surface)',\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n boxShadow: 'var(--shadow-elevation-3)',\n padding: 'var(--space-cardPadding)',\n }}\n >\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'flex-start',\n gap: 8,\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n <div style={{ minWidth: 0 }}>\n <h3\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-semibold)',\n }}\n >\n {previewGen.generatedSpec.name}\n </h3>\n {previewGen.generatedSpec.description && (\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n marginTop: 2,\n }}\n >\n {previewGen.generatedSpec.description}\n </p>\n )}\n </div>\n <button\n onClick={() => setPreviewGen(null)}\n aria-label=\"Close preview\"\n style={{\n display: 'flex',\n padding: 4,\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: 'transparent',\n color: 'var(--color-text-muted)',\n cursor: 'pointer',\n }}\n >\n <X size={18} />\n </button>\n </div>\n <div\n style={{\n fontSize: 11,\n color: 'var(--color-text-muted)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n {previewGen.generatedSpec.pages.reduce((acc, p) => acc + p.widgets.length, 0)} widgets across{' '}\n {previewGen.generatedSpec.pages.length} page(s) &middot;{' '}\n {Math.round(previewGen.generatedSpec.overallConfidence * 100)}% confidence\n </div>\n {previewGen.generatedSpec.pages.map((page, pi) => (\n <div key={pi} style={{ marginBottom: 'var(--space-controlGap)' }}>\n <div\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n marginBottom: 4,\n }}\n >\n {page.name}\n </div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>\n {page.widgets.map((w, wi) => (\n <span\n key={wi}\n style={{\n fontSize: 11,\n padding: '3px 8px',\n borderRadius: 'var(--radius-pill)',\n backgroundColor: 'var(--color-bg-sunken)',\n color: 'var(--color-text-secondary)',\n }}\n >\n {w.title} · {w.type}\n </span>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Refine modal — collects refinement instructions for refineGeneratedDashboard */}\n {refineGen && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Refine generation\"\n onClick={() => setRefineGen(null)}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n backgroundColor: 'var(--color-overlay-scrim)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 'var(--space-pagePadding)',\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n style={{\n width: 'min(520px, 100%)',\n backgroundColor: 'var(--color-bg-surface)',\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n boxShadow: 'var(--shadow-elevation-3)',\n padding: 'var(--space-cardPadding)',\n }}\n >\n <h3\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-semibold)',\n marginBottom: 4,\n }}\n >\n Refine dashboard\n </h3>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Describe what to change — the AI keeps the existing spec and applies your instructions.\n </p>\n <textarea\n value={refineText}\n onChange={(e) => setRefineText(e.target.value)}\n maxLength={2000}\n rows={4}\n placeholder=\"e.g., Add a revenue-by-region map and remove the duplicate KPI cards\"\n style={{\n width: '100%',\n resize: 'vertical',\n padding: 'var(--space-inputPadding, 8px)',\n borderRadius: 'var(--radius-input)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-canvas)',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n marginBottom: 'var(--space-controlGap)',\n }}\n />\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, flexWrap: 'wrap' }}>\n <button\n onClick={() => setRefineGen(null)}\n style={{\n padding: '8px 16px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n Cancel\n </button>\n <button\n onClick={() => void handleRefineSubmit()}\n disabled={!refineText.trim() || busyIds.has(refineGen.id)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '8px 16px',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: 'var(--color-action-primary-bg)',\n color: 'var(--color-action-primary-text)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: !refineText.trim() || busyIds.has(refineGen.id) ? 'not-allowed' : 'pointer',\n opacity: !refineText.trim() ? 0.6 : 1,\n }}\n >\n {busyIds.has(refineGen.id) ? <Loader2 size={14} className=\"animate-spin\" /> : <Wand2 size={14} />}\n Apply refinement\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n});\n\nexport default InsightsPage;\n"],"mappings":";;;;;;;;;AAkCA,IAAM,IAA0B,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+B7B,KAAyB,CAAG;;;;;;;;;;;GAa5B,KAAqB,CAAG;;;;;;;;GAUxB,IAAyB,CAAG;;;;;;GAQ5B,IAAoB,CAAG;;;;GAMvB,KAAoB,CAAG;;;;;;;GASvB,IAAuB,CAAG;;;;;;;;GAU1B,KAAoB,CAAG;;;;;;;;GAuCvB,IAAwB;CAAC;CAAW;CAAc;CAAa;AAkCrE,SAAS,GAAc,GAAgB;AACrC,SAAQ,GAAR;EACE,KAAK,YACH,QAAO,kBAAC,GAAD;GAAa,MAAM;GAAI,WAAU;GAAmB,CAAA;EAC7D,KAAK,SACH,QAAO,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAAiB,CAAA;EACvD,KAAK,YACH,QAAO,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAAkB,CAAA;EACxD,KAAK;EACL,KAAK;EACL,KAAK,aACH,QAAO,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAA+B,CAAA;EACrE,QACE,QAAO,kBAAC,GAAD;GAAO,MAAM;GAAI,WAAU;GAAkB,CAAA;;;AAI1D,SAAS,EAAe,GAAgB;AACtC,SAAQ,GAAR;EACE,KAAK,YACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,aACH,QAAO;EACT,KAAK,aACH,QAAO;EACT,QACE,QAAO;;;AAQb,IAAa,IAAmB,EAAK,WAAwB;CAC3D,IAAM,IAAW,GAAgB,EAC3B,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,GAAe,KAAoB,EAAwB,KAAK,EAGjE,CAAC,GAAS,KAAc,wBAA4B,IAAI,KAAK,CAAC,EAC9D,CAAC,GAAY,KAAiB,EAAkC,KAAK,EACrE,CAAC,GAAW,KAAgB,EAAkC,KAAK,EACnE,CAAC,GAAY,KAAiB,EAAS,GAAG,EAE1C,IAAW,GAAa,MAAe;AAC3C,KAAY,MAAS,IAAI,IAAI,EAAK,CAAC,IAAI,EAAG,CAAC;IAC1C,EAAE,CAAC,EACA,IAAY,GAAa,MAAe;AAC5C,KAAY,MAAS;GACnB,IAAM,IAAO,IAAI,IAAI,EAAK;AAE1B,UADA,EAAK,OAAO,EAAG,EACR;IACP;IACD,EAAE,CAAC,EAGA,EACJ,MAAM,GACN,SAAS,GACT,SAAS,MACP,EAAoC,GAAyB;EAC/D,WAAW,EAAE,OAAO,IAAI;EACxB,aAAa;EACd,CAAC,EAGI,EAAE,MAAM,MAAoB,EAAmC,IAAwB,EAC3F,WAAW,EAAE,OAAO,GAAG,EACxB,CAAC,EAGI,CAAC,KAAqB,EAAmC,GAAmB,EAC5E,CAAC,KAAwB,EAAsC,EAAuB,EACtF,CAAC,KAAoB,EAAY,EAAkB,EACnD,CAAC,KAAoB,EAAY,GAAkB,EACnD,CAAC,KAAuB,EAAY,EAAqB,EACzD,CAAC,KAAoB,EAAY,GAAkB,EAEnD,IAAkC,GAAiB,uBAAuB,OAAO,KAAK,MAAS,EAAK,KAAK,IAAI,EAAE,EAC/G,IAAkC,GAAiB,wBAAwB,EAAE;AAenF,CAZA,QAAgB;AAEd,MAAI,CADyB,EAAY,MAAM,MAAQ,EAAsB,SAAS,EAAI,OAAO,CAAC,CACvE;EAE3B,IAAM,IAAW,kBAAkB;AACjC,MAAoB;KACnB,IAAM;AAET,eAAa,cAAc,EAAS;IACnC,CAAC,GAAa,EAAmB,CAAC,EAGrC,QAAgB;AACd,MAAI,CAAC,KAAc,CAAC,EAAW;EAC/B,IAAM,KAAS,MAAqB;AAClC,GAAI,EAAE,QAAQ,aACZ,EAAc,KAAK,EACnB,EAAa,KAAK;;AAItB,SADA,OAAO,iBAAiB,WAAW,EAAM,QAC5B,OAAO,oBAAoB,WAAW,EAAM;IACxD,CAAC,GAAY,EAAU,CAAC;CAG3B,IAAM,IAAiB,EAAY,YAAY;AACzC,SAAC,EAAW,MAAM,IAAI,IAC1B;OAAI,EAAW,SAAS,KAAM;AAC5B,MAAiB,4CAA4C;AAC7D;;AAGF,GADA,EAAiB,KAAK,EACtB,EAAgB,GAAK;AACrB,OAAI;AAYF,IAXA,MAAM,EAAkB,EACtB,WAAW,EACT,OAAO;KACL,QAAQ,EAAW,MAAM;KACzB,WAAW;KACX,YAAY;KACZ,YAAY;KACb,EACF,EACF,CAAC,EACF,EAAc,GAAG,EACjB,GAAoB;YACb,GAAK;AACZ,MAAiB,aAAe,QAAQ,EAAI,UAAU,+BAA+B;aAC7E;AACR,MAAgB,GAAM;;;IAEvB;EAAC;EAAY;EAAc;EAAmB;EAAmB,CAAC,EAE/D,KAAwB,EAC5B,OAAO,MAAyB;AAC9B,IAAiB,KAAK;AACtB,MAAI;GAIF,IAAM,KAHS,MAAM,EAAqB,EACxC,WAAW,EAAE,OAAO,EAAE,iBAAc,EAAE,EACvC,CAAC,EACyB,MAAM,+BAA+B;AAChE,GAAI,KACF,EAAS,eAAe,IAAc;WAEjC,GAAK;AACZ,KAAiB,aAAe,QAAQ,EAAI,UAAU,6BAA6B;;IAGvF,CAAC,GAAsB,EAAS,CACjC,EAEK,KAAe,EACnB,OAAO,MAAyB;AAC9B,IAAiB,KAAK;AACtB,MAAI;AAGF,GAFA,MAAM,EAAiB,EAAE,WAAW,EAAE,iBAAc,EAAE,CAAC,EACnD,GAAU,IAAO,EAAa,UAAU,EAC5C,GAAoB;WACb,GAAK;AAEZ,GADA,EAAiB,aAAe,QAAQ,EAAI,UAAU,8BAA8B,EAChF,GAAU,IAAO,EAAa,QAAQ;;IAG9C,CAAC,GAAkB,EAAmB,CACvC,EAGK,KAAe,EACnB,OAAO,MAAyB;AAE9B,EADA,EAAiB,KAAK,EACtB,EAAS,EAAa;AACtB,MAAI;AAEF,GADA,MAAM,EAAiB,EAAE,WAAW,EAAE,iBAAc,EAAE,CAAC,EACvD,GAAoB;WACb,GAAK;AACZ,KAAiB,aAAe,QAAQ,EAAI,UAAU,8BAA8B;YAC5E;AACR,KAAU,EAAa;;IAG3B,CAAC,GAAkB,EAAmB,CACvC,EAGK,KAAmB,EACvB,OAAO,MAAyB;AAE9B,EADA,EAAiB,KAAK,EACtB,EAAS,EAAa;AACtB,MAAI;AAEF,GADA,MAAM,EAAoB,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAc,EAAE,EAAE,CAAC,EACrE,GAAoB;WACb,GAAK;AACZ,KAAiB,aAAe,QAAQ,EAAI,UAAU,iCAAiC;YAC/E;AACR,KAAU,EAAa;;IAG3B,CAAC,GAAqB,EAAmB,CAC1C,EAGK,KAAqB,EAAY,YAAY;AACjD,MAAI,CAAC,KAAa,CAAC,EAAW,MAAM,CAAE;EACtC,IAAM,IAAe,EAAU;AAE/B,EADA,EAAiB,KAAK,EACtB,EAAS,EAAa;AACtB,MAAI;AAMF,GALA,MAAM,EAAiB,EACrB,WAAW,EAAE,OAAO;IAAE;IAAc,kBAAkB,EAAW,MAAM;IAAE,EAAE,EAC5E,CAAC,EACF,EAAa,KAAK,EAClB,EAAc,GAAG,EACjB,GAAoB;WACb,GAAK;AACZ,KAAiB,aAAe,QAAQ,EAAI,UAAU,8BAA8B;YAC5E;AACR,KAAU,EAAa;;IAExB;EAAC;EAAW;EAAY;EAAkB;EAAmB,CAAC,EAE3D,KAAwB,GAAa,MAAmB;AAC5D,IAAc,EAAO;IACpB,EAAE,CAAC;AAEN,QACE,kBAAC,OAAD;EAAK,OAAO;GAAE,WAAW;GAAS,iBAAiB;GAA0B;YAA7E;GACE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAA4B,UAAU;KAAK,QAAQ;KAAU;cAApF;KAEE,kBAAC,OAAD;MAAK,OAAO,EAAE,cAAc,2BAA2B;gBAAvD,CACE,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAwB;iBAAlF,CACE,kBAAC,GAAD;QAAU,MAAM;QAAI,OAAO,EAAE,OAAO,kCAAkC;QAAI,CAAA,EAC1E,kBAAC,MAAD;QACE,OAAO;SACL,UAAU;SACV,YAAY;SACZ,OAAO;SACR;kBACF;QAEI,CAAA,CACD;UACN,kBAAC,KAAD;OAAG,OAAO;QAAE,UAAU;QAA4B,OAAO;QAA+B,WAAW;QAAG;iBAAE;OAEpG,CAAA,CACA;;KAGN,kBAAC,OAAD;MACE,OAAO;OACL,iBAAiB;OACjB,QAAQ;OACR,cAAc;OACd,SAAS;OACT,cAAc;OACf;gBAPH;OASE,kBAAC,SAAD;QACE,SAAQ;QACR,OAAO;SACL,SAAS;SACT,UAAU;SACV,YAAY;SACZ,OAAO;SACP,cAAc;SACf;kBACF;QAEO,CAAA;OACR,kBAAC,YAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;QAC9C,aAAY;QACZ,MAAM;QACN,WAAW;QACX,OAAO;SACL,OAAO;SACP,SAAS;SACT,cAAc;SACd,QAAQ;SACR,iBAAiB;SACjB,OAAO;SACP,UAAU;SACV,QAAQ;SACR,SAAS;SACV;QACD,YAAY,MAAM;AAChB,SAAI,EAAE,QAAQ,YAAY,EAAE,WAAW,EAAE,YAAU,GAAgB;;QAErE,CAAA;OACF,kBAAC,OAAD;QACE,OAAO;SACL,SAAS;SACT,gBAAgB;SAChB,YAAY;SACZ,WAAW;SACX,UAAU;SACV,KAAK;SACN;kBARH,CAUE,kBAAC,QAAD;SAAM,OAAO;UAAE,UAAU;UAA4B,OAAO;UAA2B;mBAAE;SAElF,CAAA,EACP,kBAAC,UAAD;SACE,SAAS;SACT,UAAU,CAAC,EAAW,MAAM,IAAI;SAChC,OAAO;UACL,SAAS;UACT,YAAY;UACZ,KAAK;UACL,SAAS;UACT,cAAc;UACd,iBAAiB;UACjB,OAAO;UACP,YAAY;UACZ,UAAU;UACV,QAAQ;UACR,QAAQ,CAAC,EAAW,MAAM,IAAI,IAAe,gBAAgB;UAC7D,SAAS,CAAC,EAAW,MAAM,IAAI,IAAe,KAAM;UACrD;mBAhBH,CAkBG,IAAe,kBAAC,GAAD;UAAS,MAAM;UAAI,WAAU;UAAiB,CAAA,GAAG,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EAAC,qBAEjF;WACL;;OACF;;KAGL,KACC,kBAAC,OAAD;MACE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,KAAK;OACL,SAAS;OACT,cAAc;OACd,cAAc;OACd,QAAQ;OACR,iBAAiB;OACjB,OAAO;OACP,UAAU;OACX;gBAbH,CAeE,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAG;iBAA7D,CACE,kBAAC,GAAD,EAAa,MAAM,IAAM,CAAA,EACxB,EACG;UACN,kBAAC,UAAD;OACE,eAAe,EAAiB,KAAK;OACrC,OAAO;QACL,YAAY;QACZ,QAAQ;QACR,OAAO;QACP,QAAQ;QACR,UAAU;QACV,SAAS;QACV;OACD,cAAW;iBACZ;OAEQ,CAAA,CACL;;KAIP,EAAY,SAAS,KACpB,kBAAC,OAAD;MAAK,OAAO,EAAE,cAAc,2BAA2B;gBAAvD,CACE,kBAAC,MAAD;OACE,OAAO;QACL,UAAU;QACV,YAAY;QACZ,OAAO;QACP,cAAc;QACf;iBACF;OAEI,CAAA,EACL,kBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,qBAAqB;QACrB,KAAK;QACN;iBAEA,EAAY,KAAK,MAChB,kBAAC,UAAD;QAEE,eAAe,GAAsB,EAAE,OAAO;QAC9C,OAAO;SACL,WAAW;SACX,SAAS;SACT,cAAc;SACd,QAAQ;SACR,iBAAiB;SACjB,QAAQ;SACT;kBAVH;SAYE,kBAAC,OAAD;UACE,OAAO;WACL,YAAY;WACZ,UAAU;WACV,OAAO;WACP,cAAc;WACf;oBAEA,EAAE;UACC,CAAA;SACN,kBAAC,OAAD;UACE,OAAO;WACL,UAAU;WACV,OAAO;WACP,YAAY;WACb;oBAEA,EAAE,sBAAsB,EAAE,OAAO,MAAM,GAAG,GAAG;UAC1C,CAAA;SACN,kBAAC,OAAD;UAAK,OAAO;WAAE,SAAS;WAAQ,KAAK;WAAG,WAAW;WAAG,UAAU;WAAQ;oBACpE,EAAE,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MACvB,kBAAC,QAAD;WAEE,OAAO;YACL,UAAU;YACV,SAAS;YACT,cAAc;YACd,iBAAiB;YACjB,OAAO;YACR;qBAEA;WACI,EAVA,EAUA,CACP;UACE,CAAA;SACC;UA9CF,EAAE,GA8CA,CACT;OACE,CAAA,CACF;;KAIR,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MACE,OAAO;OACL,UAAU;OACV,YAAY;OACZ,OAAO;OACP,cAAc;OACf;gBACF;MAEI,CAAA,EAEJ,KAAsB,EAAY,WAAW,IAC5C,kBAAC,OAAD;MAAK,OAAO;OAAE,WAAW;OAAU,SAAS;OAAI,OAAO;OAA2B;gBAAlF,CACE,kBAAC,GAAD;OAAS,MAAM;OAAI,WAAU;OAAe,OAAO,EAAE,QAAQ,cAAc;OAAI,CAAA,EAC/E,kBAAC,KAAD,EAAA,UAAG,0BAA0B,CAAA,CACzB;UACJ,EAAY,WAAW,IACzB,kBAAC,OAAD;MACE,OAAO;OACL,WAAW;OACX,SAAS;OACT,iBAAiB;OACjB,QAAQ;OACR,cAAc;OACf;gBAPH;OASE,kBAAC,GAAD;QAAU,MAAM;QAAI,OAAO;SAAE,OAAO;SAA2B,QAAQ;SAAc;QAAI,CAAA;OACzF,kBAAC,KAAD;QAAG,OAAO;SAAE,OAAO;SAA+B,YAAY;SAA6B;kBAAE;QAEzF,CAAA;OACJ,kBAAC,KAAD;QAAG,OAAO;SAAE,OAAO;SAA2B,UAAU;SAA4B,WAAW;SAAG;kBAAE;QAEhG,CAAA;OACA;UAEN,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,eAAe;OAAU,KAAK;OAAI;gBAC9D,EAAY,KAAK,MAChB,kBAAC,OAAD;OAEE,OAAO;QACL,SAAS;QACT,cAAc;QACd,QAAQ;QACR,iBAAiB;QAClB;iBAED,kBAAC,OAAD;QACE,OAAO;SACL,SAAS;SACT,gBAAgB;SAChB,YAAY;SACZ,UAAU;SACV,KAAK;SACN;kBAPH,CASE,kBAAC,OAAD;SAAK,OAAO;UAAE,MAAM;UAAa,UAAU;UAAG;mBAA9C;UACE,kBAAC,OAAD;WAAK,OAAO;YAAE,SAAS;YAAQ,YAAY;YAAU,KAAK;YAAG,cAAc;YAAG,UAAU;YAAG;qBAA3F;YACG,GAAc,EAAI,OAAO;YAC1B,kBAAC,QAAD;aACE,OAAO;cACL,UAAU;cACV,YAAY;cACZ,OAAO;cACP,UAAU;cACV,UAAU;cACV,cAAc;cACd,YAAY;cACb;uBAEA,EAAI,eAAe,QAAQ;aACvB,CAAA;YACP,kBAAC,QAAD;aACE,OAAO;cACL,UAAU;cACV,OAAO;cACP,SAAS;cACT,cAAc;cACd,iBAAiB;cAClB;uBAEA,EAAe,EAAI,OAAO;aACtB,CAAA;YACH;;UACN,kBAAC,KAAD;WACE,OAAO;YACL,UAAU;YACV,OAAO;YACP,cAAc;YACf;qBAEA,EAAI,OAAO,SAAS,MAAM,EAAI,OAAO,MAAM,GAAG,IAAI,GAAG,QAAQ,EAAI;WAChE,CAAA;UACH,EAAI,iBACH,kBAAC,OAAD;WAAK,OAAO;YAAE,UAAU;YAAI,OAAO;YAA2B;qBAA9D;YACG,EAAI,cAAc,MAAM,QAAQ,GAAK,MAAM,IAAM,EAAE,QAAQ,QAAQ,EAAE;YAAC;YAAgB;YACtF,EAAI,cAAc,MAAM;YAAO;YAAkB;YACjD,KAAK,MAAM,EAAI,cAAc,oBAAoB,IAAI;YAAC;YACnD;;UAEP,EAAI,gBACH,kBAAC,OAAD;WACE,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,WAAW;YACX,UAAU;YACV,OAAO;YACR;qBARH,CAUE,kBAAC,GAAD,EAAa,MAAM,IAAM,CAAA,EACxB,EAAI,aACD;;UAEJ;YACN,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,KAAK;UAAG,UAAU;UAAQ;mBAAzD;UACG,EAAsB,SAAS,EAAI,OAAO,IACzC,kBAAC,UAAD;WACE,eAAe;AAER,YADL,EAAa,SAAS,EACjB,GAAa,EAAI,GAAG;;WAE3B,UAAU,EAAQ,IAAI,EAAI,GAAG;WAC7B,OAAM;WACN,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,SAAS;YACT,cAAc;YACd,QAAQ;YACR,iBAAiB;YACjB,OAAO;YACP,UAAU;YACV,QAAQ,EAAQ,IAAI,EAAI,GAAG,GAAG,SAAS;YACxC;qBAlBH,CAoBE,kBAAC,IAAD,EAAK,MAAM,IAAM,CAAA,EAAA,UACV;;UAEV,EAAI,WAAW,eAAe,EAAI,iBACjC,kBAAC,UAAD;WACE,eAAe,EAAc,EAAI;WACjC,OAAM;WACN,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,SAAS;YACT,cAAc;YACd,QAAQ;YACR,iBAAiB;YACjB,OAAO;YACP,UAAU;YACV,QAAQ;YACT;qBAdH,CAgBE,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA,EAAA,WACV;;UAEV,EAAI,WAAW,eAAe,EAAI,iBACjC,kBAAC,UAAD;WACE,eAAe;AAEb,YADA,EAAa,EAAI,EACjB,EAAc,GAAG;;WAEnB,OAAM;WACN,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,SAAS;YACT,cAAc;YACd,QAAQ;YACR,iBAAiB;YACjB,OAAO;YACP,UAAU;YACV,QAAQ;YACT;qBAjBH,CAmBE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,EAAA,UACZ;;WAET,EAAI,WAAW,eAAe,EAAI,WAAW,YAAY,EAAI,WAAW,gBACxE,kBAAC,UAAD;WACE,eAAe;AAER,YADL,EAAa,SAAS,EACjB,GAAiB,EAAI,GAAG;;WAE/B,UAAU,EAAQ,IAAI,EAAI,GAAG;WAC7B,OAAM;WACN,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,SAAS;YACT,cAAc;YACd,QAAQ;YACR,iBAAiB;YACjB,OAAO;YACP,UAAU;YACV,QAAQ,EAAQ,IAAI,EAAI,GAAG,GAAG,SAAS;YACxC;qBAlBH,CAoBE,kBAAC,GAAD;YAAW,MAAM;YAAI,WAAW,EAAQ,IAAI,EAAI,GAAG,GAAG,iBAAiB;YAAM,CAAA,EAAA,cACtE;;UAEV,EAAI,WAAW,eAAe,CAAC,EAAI,eAClC,kBAAC,UAAD;WACE,eAAe,GAAsB,EAAI,GAAG;WAC5C,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,SAAS;YACT,cAAc;YACd,iBAAiB;YACjB,OAAO;YACP,QAAQ;YACR,UAAU;YACV,QAAQ;YACT;qBAbH,CAcC,WACQ,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CACxB;;UAEV,EAAI,eACH,kBAAC,UAAD;WACE,eAAe,EAAS,eAAe,EAAI,cAAc;WACzD,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,SAAS;YACT,cAAc;YACd,QAAQ;YACR,iBAAiB;YACjB,OAAO;YACP,UAAU;YACV,QAAQ;YACT;qBAbH,CAcC,mBACgB,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CAChC;;UAEX,kBAAC,UAAD;WACE,eAAe;AAER,YADL,EAAa,SAAS,EACjB,GAAa,EAAI,GAAG;;WAE3B,OAAM;WACN,OAAO;YACL,SAAS;YACT,YAAY;YACZ,SAAS;YACT,cAAc;YACd,QAAQ;YACR,iBAAiB;YACjB,OAAO;YACP,QAAQ;YACT;qBAED,kBAAC,IAAD,EAAQ,MAAM,IAAM,CAAA;WACb,CAAA;UACL;WACF;;OACF,EApOC,EAAI,GAoOL,CACN;MACE,CAAA,CAEJ,EAAA,CAAA;KACF;;GAGL,GAAY,iBACX,kBAAC,OAAD;IACE,MAAK;IACL,cAAW;IACX,cAAW;IACX,eAAe,EAAc,KAAK;IAClC,OAAO;KACL,UAAU;KACV,OAAO;KACP,QAAQ;KACR,iBAAiB;KACjB,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,SAAS;KACV;cAED,kBAAC,OAAD;KACE,UAAU,MAAM,EAAE,iBAAiB;KACnC,OAAO;MACL,OAAO;MACP,WAAW;MACX,WAAW;MACX,iBAAiB;MACjB,cAAc;MACd,QAAQ;MACR,WAAW;MACX,SAAS;MACV;eAXH;MAaE,kBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,gBAAgB;QAChB,YAAY;QACZ,KAAK;QACL,cAAc;QACf;iBAPH,CASE,kBAAC,OAAD;QAAK,OAAO,EAAE,UAAU,GAAG;kBAA3B,CACE,kBAAC,MAAD;SACE,OAAO;UACL,UAAU;UACV,YAAY;UACb;mBAEA,EAAW,cAAc;SACvB,CAAA,EACJ,EAAW,cAAc,eACxB,kBAAC,KAAD;SACE,OAAO;UACL,UAAU;UACV,OAAO;UACP,WAAW;UACZ;mBAEA,EAAW,cAAc;SACxB,CAAA,CAEF;WACN,kBAAC,UAAD;QACE,eAAe,EAAc,KAAK;QAClC,cAAW;QACX,OAAO;SACL,SAAS;SACT,SAAS;SACT,cAAc;SACd,QAAQ;SACR,iBAAiB;SACjB,OAAO;SACP,QAAQ;SACT;kBAED,kBAAC,IAAD,EAAG,MAAM,IAAM,CAAA;QACR,CAAA,CACL;;MACN,kBAAC,OAAD;OACE,OAAO;QACL,UAAU;QACV,OAAO;QACP,cAAc;QACf;iBALH;QAOG,EAAW,cAAc,MAAM,QAAQ,GAAK,MAAM,IAAM,EAAE,QAAQ,QAAQ,EAAE;QAAC;QAAgB;QAC7F,EAAW,cAAc,MAAM;QAAO;QAAkB;QACxD,KAAK,MAAM,EAAW,cAAc,oBAAoB,IAAI;QAAC;QAC1D;;MACL,EAAW,cAAc,MAAM,KAAK,GAAM,MACzC,kBAAC,OAAD;OAAc,OAAO,EAAE,cAAc,2BAA2B;iBAAhE,CACE,kBAAC,OAAD;QACE,OAAO;SACL,UAAU;SACV,YAAY;SACZ,OAAO;SACP,cAAc;SACf;kBAEA,EAAK;QACF,CAAA,EACN,kBAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,UAAU;SAAQ,KAAK;SAAG;kBACtD,EAAK,QAAQ,KAAK,GAAG,MACpB,kBAAC,QAAD;SAEE,OAAO;UACL,UAAU;UACV,SAAS;UACT,cAAc;UACd,iBAAiB;UACjB,OAAO;UACR;mBARH;UAUG,EAAE;UAAM;UAAI,EAAE;UACV;WAVA,EAUA,CACP;QACE,CAAA,CACF;SA3BI,EA2BJ,CACN;MACE;;IACF,CAAA;GAIP,KACC,kBAAC,OAAD;IACE,MAAK;IACL,cAAW;IACX,cAAW;IACX,eAAe,EAAa,KAAK;IACjC,OAAO;KACL,UAAU;KACV,OAAO;KACP,QAAQ;KACR,iBAAiB;KACjB,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,SAAS;KACV;cAED,kBAAC,OAAD;KACE,UAAU,MAAM,EAAE,iBAAiB;KACnC,OAAO;MACL,OAAO;MACP,iBAAiB;MACjB,cAAc;MACd,QAAQ;MACR,WAAW;MACX,SAAS;MACV;eATH;MAWE,kBAAC,MAAD;OACE,OAAO;QACL,UAAU;QACV,YAAY;QACZ,cAAc;QACf;iBACF;OAEI,CAAA;MACL,kBAAC,KAAD;OACE,OAAO;QACL,UAAU;QACV,OAAO;QACP,cAAc;QACf;iBACF;OAEG,CAAA;MACJ,kBAAC,YAAD;OACE,OAAO;OACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;OAC9C,WAAW;OACX,MAAM;OACN,aAAY;OACZ,OAAO;QACL,OAAO;QACP,QAAQ;QACR,SAAS;QACT,cAAc;QACd,QAAQ;QACR,iBAAiB;QACjB,OAAO;QACP,UAAU;QACV,cAAc;QACf;OACD,CAAA;MACF,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,gBAAgB;QAAY,KAAK;QAAG,UAAU;QAAQ;iBAArF,CACE,kBAAC,UAAD;QACE,eAAe,EAAa,KAAK;QACjC,OAAO;SACL,SAAS;SACT,cAAc;SACd,QAAQ;SACR,iBAAiB;SACjB,OAAO;SACP,UAAU;SACV,QAAQ;SACT;kBACF;QAEQ,CAAA,EACT,kBAAC,UAAD;QACE,eAAe,KAAK,IAAoB;QACxC,UAAU,CAAC,EAAW,MAAM,IAAI,EAAQ,IAAI,EAAU,GAAG;QACzD,OAAO;SACL,SAAS;SACT,YAAY;SACZ,KAAK;SACL,SAAS;SACT,cAAc;SACd,QAAQ;SACR,iBAAiB;SACjB,OAAO;SACP,UAAU;SACV,QAAQ,CAAC,EAAW,MAAM,IAAI,EAAQ,IAAI,EAAU,GAAG,GAAG,gBAAgB;SAC1E,SAAU,EAAW,MAAM,GAAS,IAAN;SAC/B;kBAfH,CAiBG,EAAQ,IAAI,EAAU,GAAG,GAAG,kBAAC,GAAD;SAAS,MAAM;SAAI,WAAU;SAAiB,CAAA,GAAG,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,EAAC,mBAE3F;UACL;;MACF;;IACF,CAAA;GAEJ;;EAER"}
1
+ {"version":3,"file":"InsightsPage.js","names":[],"sources":["../../../src/bigconsole/pages/InsightsPage.tsx"],"sourcesContent":["/**\n * InsightsPage\n *\n * AI-powered dashboard generation using the Prompt module backend.\n * Users enter a natural language prompt to generate dashboards with\n * suggested widgets, layouts, and configurations.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport {\n Sparkles,\n Loader2,\n Trash2,\n ArrowRight,\n Clock,\n CheckCircle,\n XCircle,\n AlertCircle,\n RefreshCw,\n Eye,\n Ban,\n Wand2,\n X,\n} from 'lucide-react';\n\n// ============================================================================\n// GRAPHQL OPERATIONS (inline until codegen is configured for prompt module)\n// TODO(BOFF-2055): Replace inline gql with codegen-generated hooks\n// ============================================================================\n\nconst LIST_PROMPT_GENERATIONS = gql`\n query ListPromptGenerations($status: PromptGenerationStatus, $first: Int) {\n listPromptGenerations(status: $status, first: $first) {\n edges {\n node {\n id\n prompt\n status\n dashboardId\n errorMessage\n createdAt\n completedAt\n generatedSpec {\n name\n description\n overallConfidence\n pages {\n name\n widgets {\n title\n type\n }\n }\n }\n }\n }\n totalCount\n }\n }\n`;\n\nconst GET_PROMPT_SUGGESTIONS = gql`\n query GetPromptSuggestions($category: String, $limit: Int) {\n getPromptSuggestions(category: $category, limit: $limit) {\n id\n category\n title\n prompt\n exampleDescription\n tags\n }\n }\n`;\n\nconst GENERATE_DASHBOARD = gql`\n mutation GenerateDashboardFromPrompt($input: GenerateDashboardFromPromptInput!) {\n generateDashboardFromPrompt(input: $input) {\n id\n status\n prompt\n }\n }\n`;\n\nconst CREATE_FROM_GENERATION = gql`\n mutation CreateDashboardFromGeneration($input: CreateDashboardFromGenerationInput!) {\n createDashboardFromGeneration(input: $input) {\n id\n }\n }\n`;\n\nconst DELETE_GENERATION = gql`\n mutation DeletePromptGeneration($generationId: ID!) {\n deletePromptGeneration(generationId: $generationId)\n }\n`;\n\nconst CANCEL_GENERATION = gql`\n mutation CancelPromptGeneration($generationId: ID!) {\n cancelPromptGeneration(generationId: $generationId) {\n id\n status\n }\n }\n`;\n\nconst REGENERATE_DASHBOARD = gql`\n mutation RegenerateDashboard($input: RegenerateDashboardInput!) {\n regenerateDashboard(input: $input) {\n id\n status\n prompt\n }\n }\n`;\n\nconst REFINE_GENERATION = gql`\n mutation RefineGeneratedDashboard($input: RefineGeneratedDashboardInput!) {\n refineGeneratedDashboard(input: $input) {\n id\n status\n prompt\n }\n }\n`;\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface ListPromptGenerationsData {\n listPromptGenerations: {\n edges: Array<{ node: PromptGeneration }>;\n totalCount: number;\n };\n}\n\ninterface GetPromptSuggestionsData {\n getPromptSuggestions: PromptSuggestion[];\n}\n\ninterface GenerateDashboardData {\n generateDashboardFromPrompt: {\n id: string;\n status: string;\n prompt: string;\n };\n}\n\ninterface CreateFromGenerationData {\n createDashboardFromGeneration: {\n id: string;\n };\n}\n\nconst NON_TERMINAL_STATUSES = ['PENDING', 'PROCESSING', 'GENERATING'];\n\ninterface PromptSuggestion {\n id: string;\n category: string;\n title: string;\n prompt: string;\n exampleDescription?: string;\n tags: string[];\n}\n\ninterface PromptGeneration {\n id: string;\n prompt: string;\n status: string;\n dashboardId?: string;\n errorMessage?: string;\n createdAt: string;\n completedAt?: string;\n generatedSpec?: {\n name: string;\n description?: string;\n overallConfidence: number;\n pages: Array<{\n name: string;\n widgets: Array<{ title: string; type: string }>;\n }>;\n };\n}\n\n// ============================================================================\n// STATUS HELPERS\n// ============================================================================\n\nfunction getStatusIcon(status: string) {\n switch (status) {\n case 'COMPLETED':\n return <CheckCircle size={16} className=\"text-green-500\" />;\n case 'FAILED':\n return <XCircle size={16} className=\"text-red-500\" />;\n case 'CANCELLED':\n return <XCircle size={16} className=\"text-gray-400\" />;\n case 'PENDING':\n case 'PROCESSING':\n case 'GENERATING':\n return <Loader2 size={16} className=\"text-blue-500 animate-spin\" />;\n default:\n return <Clock size={16} className=\"text-gray-400\" />;\n }\n}\n\nfunction getStatusLabel(status: string) {\n switch (status) {\n case 'COMPLETED':\n return 'Completed';\n case 'FAILED':\n return 'Failed';\n case 'CANCELLED':\n return 'Cancelled';\n case 'PENDING':\n return 'Pending';\n case 'PROCESSING':\n return 'Processing';\n case 'GENERATING':\n return 'Generating';\n default:\n return status;\n }\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const InsightsPage: FC = memo(function InsightsPage() {\n const navigate = useAppNavigate();\n const [promptText, setPromptText] = useState('');\n const [isGenerating, setIsGenerating] = useState(false);\n const [mutationError, setMutationError] = useState<string | null>(null);\n // Track per-generation in-flight mutations independently so cancelling A\n // does not visually \"busy\" B's regenerate (and vice-versa).\n const [busyIds, setBusyIds] = useState<Set<string>>(() => new Set());\n const [previewGen, setPreviewGen] = useState<PromptGeneration | null>(null);\n const [refineGen, setRefineGen] = useState<PromptGeneration | null>(null);\n const [refineText, setRefineText] = useState('');\n\n const markBusy = useCallback((id: string) => {\n setBusyIds((prev) => new Set(prev).add(id));\n }, []);\n const clearBusy = useCallback((id: string) => {\n setBusyIds((prev) => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n // Fetch existing generations.\n const {\n data: generationsData,\n loading: generationsLoading,\n refetch: refetchGenerations,\n startPolling,\n stopPolling,\n } = useQuery<ListPromptGenerationsData>(LIST_PROMPT_GENERATIONS, {\n variables: { first: 20 },\n fetchPolicy: 'cache-and-network',\n });\n\n // Whether any generation is still in a non-terminal state. Derived from the\n // latest query data so polling stops automatically once everything settles.\n const hasActiveGenerations =\n generationsData?.listPromptGenerations?.edges?.some((edge) => NON_TERMINAL_STATUSES.includes(edge.node.status)) ??\n false;\n\n // Fetch suggestions\n const { data: suggestionsData } = useQuery<GetPromptSuggestionsData>(GET_PROMPT_SUGGESTIONS, {\n variables: { limit: 6 },\n });\n\n // Mutations\n const [generateDashboard] = useMutation<GenerateDashboardData>(GENERATE_DASHBOARD);\n const [createFromGeneration] = useMutation<CreateFromGenerationData>(CREATE_FROM_GENERATION);\n const [deleteGeneration] = useMutation(DELETE_GENERATION);\n const [cancelGeneration] = useMutation(CANCEL_GENERATION);\n const [regenerateDashboard] = useMutation(REGENERATE_DASHBOARD);\n const [refineGeneration] = useMutation(REFINE_GENERATION);\n\n const generations: PromptGeneration[] = generationsData?.listPromptGenerations?.edges?.map((edge) => edge.node) || [];\n const suggestions: PromptSuggestion[] = suggestionsData?.getPromptSuggestions || [];\n\n // Poll every 2s while a generation is non-terminal (or a generate request is\n // in flight) so cards advance (e.g. PROCESSING → COMPLETED) without a manual\n // refresh; polling is stopped once everything settles.\n useEffect(() => {\n if (hasActiveGenerations || isGenerating) {\n startPolling(2_000);\n } else {\n stopPolling();\n }\n return () => stopPolling();\n }, [hasActiveGenerations, isGenerating, startPolling, stopPolling]);\n\n // Close the Preview / Refine modals on Escape for keyboard accessibility.\n useEffect(() => {\n if (!previewGen && !refineGen) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setPreviewGen(null);\n setRefineGen(null);\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [previewGen, refineGen]);\n\n // Handlers\n const handleGenerate = useCallback(async () => {\n if (!promptText.trim() || isGenerating) return;\n if (promptText.length > 2000) {\n setMutationError('Prompt is too long (max 2000 characters).');\n return;\n }\n setMutationError(null);\n setIsGenerating(true);\n try {\n await generateDashboard({\n variables: {\n input: {\n prompt: promptText.trim(),\n styleType: 'MODERN',\n layoutType: 'GRID',\n maxWidgets: 8,\n },\n },\n });\n setPromptText('');\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to generate dashboard');\n } finally {\n setIsGenerating(false);\n }\n }, [promptText, isGenerating, generateDashboard, refetchGenerations]);\n\n const handleCreateDashboard = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n try {\n const result = await createFromGeneration({\n variables: { input: { generationId } },\n });\n const dashboardId = result.data?.createDashboardFromGeneration?.id;\n if (dashboardId) {\n navigate(`/dashboards/${dashboardId}`);\n }\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to create dashboard');\n }\n },\n [createFromGeneration, navigate]\n );\n\n const handleDelete = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n try {\n await deleteGeneration({ variables: { generationId } });\n if (isNative()) void nativeNotify('success');\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to delete generation');\n if (isNative()) void nativeNotify('error');\n }\n },\n [deleteGeneration, refetchGenerations]\n );\n\n // Cancel an in-flight (PENDING/PROCESSING/GENERATING) generation.\n const handleCancel = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n markBusy(generationId);\n try {\n await cancelGeneration({ variables: { generationId } });\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to cancel generation');\n } finally {\n clearBusy(generationId);\n }\n },\n [cancelGeneration, refetchGenerations]\n );\n\n // Regenerate from an existing generation, reusing its original prompt.\n const handleRegenerate = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n markBusy(generationId);\n try {\n await regenerateDashboard({ variables: { input: { generationId } } });\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to regenerate dashboard');\n } finally {\n clearBusy(generationId);\n }\n },\n [regenerateDashboard, refetchGenerations]\n );\n\n // Submit refinement instructions for a completed generation.\n const handleRefineSubmit = useCallback(async () => {\n if (!refineGen || !refineText.trim()) return;\n const generationId = refineGen.id;\n setMutationError(null);\n markBusy(generationId);\n try {\n await refineGeneration({\n variables: { input: { generationId, refinementPrompt: refineText.trim() } },\n });\n setRefineGen(null);\n setRefineText('');\n refetchGenerations();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to refine generation');\n } finally {\n clearBusy(generationId);\n }\n }, [refineGen, refineText, refineGeneration, refetchGenerations]);\n\n const handleSuggestionClick = useCallback((prompt: string) => {\n setPromptText(prompt);\n }, []);\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 {/* Header */}\n <div style={{ marginBottom: 'var(--space-sectionGap)' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Sparkles size={24} style={{ color: 'var(--color-action-primary-bg)' }} />\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 AI Insights\n </h1>\n </div>\n <p style={{ fontSize: 'var(--font-size-body-md)', color: 'var(--color-text-secondary)', marginTop: 4 }}>\n Generate dashboards from natural language prompts\n </p>\n </div>\n\n {/* Prompt Input */}\n <div\n style={{\n backgroundColor: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border)',\n borderRadius: 'var(--radius-card)',\n padding: 'var(--space-cardPadding)',\n marginBottom: 'var(--space-sectionGap)',\n }}\n >\n <label\n htmlFor=\"prompt-input\"\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n marginBottom: 8,\n }}\n >\n Describe the dashboard you want to create\n </label>\n <textarea\n id=\"prompt-input\"\n value={promptText}\n onChange={(e) => setPromptText(e.target.value)}\n placeholder=\"e.g., Create a sales dashboard showing revenue trends, top products, and regional performance\"\n rows={3}\n maxLength={2000}\n style={{\n width: '100%',\n padding: '8px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-canvas)',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-md)',\n resize: 'vertical',\n outline: 'none',\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) handleGenerate();\n }}\n />\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginTop: 8,\n flexWrap: 'wrap',\n gap: 8,\n }}\n >\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>\n Press Ctrl+Enter to generate\n </span>\n <button\n onClick={handleGenerate}\n disabled={!promptText.trim() || isGenerating}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '8px 16px',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n color: 'var(--color-action-primary-text)',\n fontWeight: 'var(--font-weight-medium)',\n fontSize: 'var(--font-size-body-sm)',\n border: 'none',\n cursor: !promptText.trim() || isGenerating ? 'not-allowed' : 'pointer',\n opacity: !promptText.trim() || isGenerating ? 0.5 : 1,\n }}\n >\n {isGenerating ? <Loader2 size={16} className=\"animate-spin\" /> : <Sparkles size={16} />}\n Generate Dashboard\n </button>\n </div>\n </div>\n\n {/* Mutation Error Banner */}\n {mutationError && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n padding: '10px 16px',\n marginBottom: 'var(--space-sectionGap)',\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-status-error-text)',\n backgroundColor: 'var(--color-bg-surface)',\n color: 'var(--color-status-error-text)',\n fontSize: 'var(--font-size-body-sm)',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <AlertCircle size={16} />\n {mutationError}\n </div>\n <button\n onClick={() => setMutationError(null)}\n style={{\n background: 'none',\n border: 'none',\n color: 'var(--color-status-error-text)',\n cursor: 'pointer',\n fontSize: 'var(--font-size-body-md)',\n padding: '0 4px',\n }}\n aria-label=\"Dismiss error\"\n >\n &times;\n </button>\n </div>\n )}\n\n {/* Suggestions */}\n {suggestions.length > 0 && (\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 Suggestions\n </h2>\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(min(280px, 100%), 1fr))',\n gap: 12,\n }}\n >\n {suggestions.map((s) => (\n <button\n key={s.id}\n onClick={() => handleSuggestionClick(s.prompt)}\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 }}\n >\n <div\n style={{\n fontWeight: 'var(--font-weight-medium)',\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-primary)',\n marginBottom: 4,\n }}\n >\n {s.title}\n </div>\n <div\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n lineHeight: 1.4,\n }}\n >\n {s.exampleDescription || s.prompt.slice(0, 80)}\n </div>\n <div style={{ display: 'flex', gap: 4, marginTop: 8, flexWrap: 'wrap' }}>\n {s.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n style={{\n fontSize: 11,\n padding: '2px 6px',\n borderRadius: 4,\n backgroundColor: 'var(--color-bg-sunken)',\n color: 'var(--color-text-muted)',\n }}\n >\n {tag}\n </span>\n ))}\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Generations History */}\n <div>\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 Recent Generations\n </h2>\n\n {generationsLoading && generations.length === 0 ? (\n <div style={{ textAlign: 'center', padding: 40, color: 'var(--color-text-muted)' }}>\n <Loader2 size={24} className=\"animate-spin\" style={{ margin: '0 auto 8px' }} />\n <p>Loading generations...</p>\n </div>\n ) : generations.length === 0 && !isGenerating ? (\n <div\n style={{\n textAlign: 'center',\n padding: 40,\n backgroundColor: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border)',\n borderRadius: 'var(--radius-card)',\n }}\n >\n <Sparkles size={32} style={{ color: 'var(--color-text-muted)', margin: '0 auto 8px' }} />\n <p style={{ color: 'var(--color-text-secondary)', fontWeight: 'var(--font-weight-medium)' }}>\n No generations yet\n </p>\n <p style={{ color: 'var(--color-text-muted)', fontSize: 'var(--font-size-body-sm)', marginTop: 4 }}>\n Describe a dashboard above to get started\n </p>\n </div>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n {/* Transient placeholder while the generate request is in flight and\n the backend has not yet returned the new PENDING generation. */}\n {isGenerating && !hasActiveGenerations && (\n <div\n style={{\n padding: 16,\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-surface)',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>\n {getStatusIcon('PROCESSING')}\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n }}\n >\n Generating...\n </span>\n <span\n style={{\n fontSize: 11,\n color: 'var(--color-text-muted)',\n padding: '2px 6px',\n borderRadius: 4,\n backgroundColor: 'var(--color-bg-sunken)',\n }}\n >\n {getStatusLabel('PROCESSING')}\n </span>\n </div>\n </div>\n )}\n {generations.map((gen) => (\n <div\n key={gen.id}\n style={{\n padding: 16,\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-surface)',\n }}\n >\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'flex-start',\n flexWrap: 'wrap',\n gap: 8,\n }}\n >\n <div style={{ flex: '1 1 200px', minWidth: 0 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4, minWidth: 0 }}>\n {getStatusIcon(gen.status)}\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {gen.generatedSpec?.name || 'Generating...'}\n </span>\n <span\n style={{\n fontSize: 11,\n color: 'var(--color-text-muted)',\n padding: '2px 6px',\n borderRadius: 4,\n backgroundColor: 'var(--color-bg-sunken)',\n }}\n >\n {getStatusLabel(gen.status)}\n </span>\n </div>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n marginBottom: 4,\n }}\n >\n {gen.prompt.length > 120 ? gen.prompt.slice(0, 120) + '...' : gen.prompt}\n </p>\n {gen.generatedSpec && (\n <div style={{ fontSize: 11, color: 'var(--color-text-muted)' }}>\n {gen.generatedSpec.pages.reduce((acc, p) => acc + p.widgets.length, 0)} widgets across{' '}\n {gen.generatedSpec.pages.length} page(s) &middot;{' '}\n {Math.round(gen.generatedSpec.overallConfidence * 100)}% confidence\n </div>\n )}\n {gen.errorMessage && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n marginTop: 4,\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-status-error-text)',\n }}\n >\n <AlertCircle size={14} />\n {gen.errorMessage}\n </div>\n )}\n </div>\n <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>\n {NON_TERMINAL_STATUSES.includes(gen.status) && (\n <button\n onClick={() => {\n nativeImpact('medium');\n void handleCancel(gen.id);\n }}\n disabled={busyIds.has(gen.id)}\n title=\"Cancel generation\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-status-warning-text)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: busyIds.has(gen.id) ? 'wait' : 'pointer',\n }}\n >\n <Ban size={14} /> Cancel\n </button>\n )}\n {gen.status === 'COMPLETED' && gen.generatedSpec && (\n <button\n onClick={() => setPreviewGen(gen)}\n title=\"Preview proposed layout\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n <Eye size={14} /> Preview\n </button>\n )}\n {gen.status === 'COMPLETED' && gen.generatedSpec && (\n <button\n onClick={() => {\n setRefineGen(gen);\n setRefineText('');\n }}\n title=\"Refine with instructions\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n <Wand2 size={14} /> Refine\n </button>\n )}\n {(gen.status === 'COMPLETED' || gen.status === 'FAILED' || gen.status === 'CANCELLED') && (\n <button\n onClick={() => {\n nativeImpact('medium');\n void handleRegenerate(gen.id);\n }}\n disabled={busyIds.has(gen.id)}\n title=\"Regenerate with the same prompt\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: busyIds.has(gen.id) ? 'wait' : 'pointer',\n }}\n >\n <RefreshCw size={14} className={busyIds.has(gen.id) ? 'animate-spin' : ''} /> Regenerate\n </button>\n )}\n {gen.status === 'COMPLETED' && !gen.dashboardId && (\n <button\n onClick={() => handleCreateDashboard(gen.id)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n color: 'var(--color-action-primary-text)',\n border: 'none',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n Create <ArrowRight size={14} />\n </button>\n )}\n {gen.dashboardId && (\n <button\n onClick={() => navigate(`/dashboards/${gen.dashboardId}`)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '6px 12px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n View Dashboard <ArrowRight size={14} />\n </button>\n )}\n <button\n onClick={() => {\n nativeImpact('medium');\n void handleDelete(gen.id);\n }}\n title=\"Delete generation\"\n style={{\n display: 'flex',\n alignItems: 'center',\n padding: 6,\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-muted)',\n cursor: 'pointer',\n }}\n >\n <Trash2 size={14} />\n </button>\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Preview modal — shows the AI-proposed page/widget structure before creation */}\n {previewGen?.generatedSpec && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Generation preview\"\n onClick={() => setPreviewGen(null)}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n backgroundColor: 'var(--color-overlay-scrim)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 'var(--space-pagePadding)',\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n style={{\n width: 'min(640px, 100%)',\n maxHeight: '80vh',\n overflowY: 'auto',\n backgroundColor: 'var(--color-bg-surface)',\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n boxShadow: 'var(--shadow-elevation-3)',\n padding: 'var(--space-cardPadding)',\n }}\n >\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'flex-start',\n gap: 8,\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n <div style={{ minWidth: 0 }}>\n <h3\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-semibold)',\n }}\n >\n {previewGen.generatedSpec.name}\n </h3>\n {previewGen.generatedSpec.description && (\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n marginTop: 2,\n }}\n >\n {previewGen.generatedSpec.description}\n </p>\n )}\n </div>\n <button\n onClick={() => setPreviewGen(null)}\n aria-label=\"Close preview\"\n style={{\n display: 'flex',\n padding: 4,\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: 'transparent',\n color: 'var(--color-text-muted)',\n cursor: 'pointer',\n }}\n >\n <X size={18} />\n </button>\n </div>\n <div\n style={{\n fontSize: 11,\n color: 'var(--color-text-muted)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n {previewGen.generatedSpec.pages.reduce((acc, p) => acc + p.widgets.length, 0)} widgets across{' '}\n {previewGen.generatedSpec.pages.length} page(s) &middot;{' '}\n {Math.round(previewGen.generatedSpec.overallConfidence * 100)}% confidence\n </div>\n {previewGen.generatedSpec.pages.map((page, pi) => (\n <div key={pi} style={{ marginBottom: 'var(--space-controlGap)' }}>\n <div\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-primary)',\n marginBottom: 4,\n }}\n >\n {page.name}\n </div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>\n {page.widgets.map((w, wi) => (\n <span\n key={wi}\n style={{\n fontSize: 11,\n padding: '3px 8px',\n borderRadius: 'var(--radius-pill)',\n backgroundColor: 'var(--color-bg-sunken)',\n color: 'var(--color-text-secondary)',\n }}\n >\n {w.title} · {w.type}\n </span>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Refine modal — collects refinement instructions for refineGeneratedDashboard */}\n {refineGen && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Refine generation\"\n onClick={() => setRefineGen(null)}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n backgroundColor: 'var(--color-overlay-scrim)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 'var(--space-pagePadding)',\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n style={{\n width: 'min(520px, 100%)',\n backgroundColor: 'var(--color-bg-surface)',\n borderRadius: 'var(--radius-card)',\n border: '1px solid var(--color-border)',\n boxShadow: 'var(--shadow-elevation-3)',\n padding: 'var(--space-cardPadding)',\n }}\n >\n <h3\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-semibold)',\n marginBottom: 4,\n }}\n >\n Refine dashboard\n </h3>\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Describe what to change — the AI keeps the existing spec and applies your instructions.\n </p>\n <textarea\n value={refineText}\n onChange={(e) => setRefineText(e.target.value)}\n maxLength={2000}\n rows={4}\n placeholder=\"e.g., Add a revenue-by-region map and remove the duplicate KPI cards\"\n style={{\n width: '100%',\n resize: 'vertical',\n padding: 'var(--space-inputPadding, 8px)',\n borderRadius: 'var(--radius-input)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-canvas)',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n marginBottom: 'var(--space-controlGap)',\n }}\n />\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, flexWrap: 'wrap' }}>\n <button\n onClick={() => setRefineGen(null)}\n style={{\n padding: '8px 16px',\n borderRadius: 'var(--radius-button)',\n border: '1px solid var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-primary)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n }}\n >\n Cancel\n </button>\n <button\n onClick={() => void handleRefineSubmit()}\n disabled={!refineText.trim() || busyIds.has(refineGen.id)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '8px 16px',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: 'var(--color-action-primary-bg)',\n color: 'var(--color-action-primary-text)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: !refineText.trim() || busyIds.has(refineGen.id) ? 'not-allowed' : 'pointer',\n opacity: !refineText.trim() ? 0.6 : 1,\n }}\n >\n {busyIds.has(refineGen.id) ? <Loader2 size={14} className=\"animate-spin\" /> : <Wand2 size={14} />}\n Apply refinement\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n});\n\nexport default InsightsPage;\n"],"mappings":"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"}