@burdenoff/microfe-bigconsole 2026.716.2 → 2026.716.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 { AuroraBackground, GradientText, GlassCard, EmphasisPanel, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useAppNavigate } from '../contexts';\nimport { useBigConsoleAnalytics } from '../hooks/useBigConsoleAnalytics';\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 emitBigConsoleEvent = useBigConsoleAnalytics();\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 emitBigConsoleEvent('bigconsole.ai.generate_clicked', { maxWidgets: 8 });\n const result = await generateDashboard({\n variables: {\n input: {\n prompt: promptText.trim(),\n styleType: 'MODERN',\n layoutType: 'GRID',\n maxWidgets: 8,\n },\n },\n });\n const generationId = result.data?.generateDashboardFromPrompt?.id;\n if (generationId) {\n emitBigConsoleEvent('bigconsole.ai.dashboard_generated', {\n generationId,\n maxWidgets: 8,\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 }, [emitBigConsoleEvent, promptText, isGenerating, generateDashboard, refetchGenerations]);\n\n const handleCreateDashboard = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n try {\n emitBigConsoleEvent('bigconsole.ai.create_dashboard_clicked', { generationId });\n const result = await createFromGeneration({\n variables: { input: { generationId } },\n });\n const dashboardId = result.data?.createDashboardFromGeneration?.id;\n if (dashboardId) {\n emitBigConsoleEvent('bigconsole.ai.dashboard_created', {\n generationId,\n dashboardId,\n });\n navigate(`/dashboards/${dashboardId}`);\n }\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to create dashboard');\n }\n },\n [createFromGeneration, emitBigConsoleEvent, 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 className=\"relative mb-[var(--space-sectionGap)] overflow-hidden rounded-card border border-border-seam bg-gloss-card p-6\">\n <AuroraBackground intensity=\"subtle\" />\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 <GradientText>AI Insights</GradientText>\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 — primary insight zone */}\n <EmphasisPanel className=\"mb-[var(--space-sectionGap)] p-[var(--space-cardPadding)]\">\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 </EmphasisPanel>\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 <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title=\"No generations yet\"\n description=\"Describe a dashboard above to get started\"\n />\n </GlassCard>\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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"}
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 { AuroraBackground, GradientText, GlassCard, EmphasisPanel, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useAppNavigate } from '../contexts';\nimport { useBigConsoleAnalytics } from '../hooks/useBigConsoleAnalytics';\nimport { reportBigConsoleQuotaError } from '../utils/quota';\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 emitBigConsoleEvent = useBigConsoleAnalytics();\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 emitBigConsoleEvent('bigconsole.ai.generate_clicked', { maxWidgets: 8 });\n const result = await generateDashboard({\n variables: {\n input: {\n prompt: promptText.trim(),\n styleType: 'MODERN',\n layoutType: 'GRID',\n maxWidgets: 8,\n },\n },\n });\n if (reportBigConsoleQuotaError(result, 'command')) {\n return;\n }\n const generationId = result.data?.generateDashboardFromPrompt?.id;\n if (generationId) {\n emitBigConsoleEvent('bigconsole.ai.dashboard_generated', {\n generationId,\n maxWidgets: 8,\n });\n }\n setPromptText('');\n refetchGenerations();\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'command')) {\n return;\n }\n setMutationError(err instanceof Error ? err.message : 'Failed to generate dashboard');\n } finally {\n setIsGenerating(false);\n }\n }, [emitBigConsoleEvent, promptText, isGenerating, generateDashboard, refetchGenerations]);\n\n const handleCreateDashboard = useCallback(\n async (generationId: string) => {\n setMutationError(null);\n try {\n emitBigConsoleEvent('bigconsole.ai.create_dashboard_clicked', { generationId });\n const result = await createFromGeneration({\n variables: { input: { generationId } },\n });\n if (reportBigConsoleQuotaError(result, 'project')) {\n return;\n }\n const dashboardId = result.data?.createDashboardFromGeneration?.id;\n if (dashboardId) {\n emitBigConsoleEvent('bigconsole.ai.dashboard_created', {\n generationId,\n dashboardId,\n });\n navigate(`/dashboards/${dashboardId}`);\n }\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'project')) {\n return;\n }\n setMutationError(err instanceof Error ? err.message : 'Failed to create dashboard');\n }\n },\n [createFromGeneration, emitBigConsoleEvent, 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 const result = await regenerateDashboard({ variables: { input: { generationId } } });\n if (reportBigConsoleQuotaError(result, 'command')) {\n return;\n }\n refetchGenerations();\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'command')) {\n return;\n }\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 let shouldClearRefine = false;\n try {\n const result = await refineGeneration({\n variables: { input: { generationId, refinementPrompt: refineText.trim() } },\n });\n if (reportBigConsoleQuotaError(result, 'command')) {\n // The user cannot retry quota failures without changing plan, so clear the completed refinement.\n shouldClearRefine = true;\n return;\n }\n shouldClearRefine = true;\n refetchGenerations();\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'command')) {\n // Keep the retry text only for non-quota errors; quota failures need a plan change first.\n shouldClearRefine = true;\n return;\n }\n setMutationError(err instanceof Error ? err.message : 'Failed to refine generation');\n } finally {\n if (shouldClearRefine) {\n setRefineGen(null);\n setRefineText('');\n }\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 className=\"relative mb-[var(--space-sectionGap)] overflow-hidden rounded-card border border-border-seam bg-gloss-card p-6\">\n <AuroraBackground intensity=\"subtle\" />\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 <GradientText>AI Insights</GradientText>\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 — primary insight zone */}\n <EmphasisPanel className=\"mb-[var(--space-sectionGap)] p-[var(--space-cardPadding)]\">\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 </EmphasisPanel>\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 <GlassCard treatment=\"insight\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title=\"No generations yet\"\n description=\"Describe a dashboard above to get started\"\n />\n </GlassCard>\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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"}
@@ -0,0 +1,70 @@
1
+ import { BIGCONSOLE_QUOTA_EXHAUSTED_EVENT as e, getBigConsoleQuotaDialogContent as t } from "../utils/quota.js";
2
+ import { useCallback as n, useEffect as r, useState as i } from "react";
3
+ import { AlertTriangle as a } from "lucide-react";
4
+ import { Button as o, ResponsiveDialog as s, ResponsiveDialogContent as c, ResponsiveDialogDescription as l, ResponsiveDialogFooter as u, ResponsiveDialogHeader as d, ResponsiveDialogTitle as f } from "@burdenoff/fe-libs/ui";
5
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
6
+ //#region src/bigconsole/providers/BigConsoleQuotaProvider.tsx
7
+ var g = {
8
+ open: !1,
9
+ kind: "generic"
10
+ };
11
+ function _({ children: _, onViewBilling: v }) {
12
+ let [y, b] = i(g);
13
+ r(() => {
14
+ let t = (e) => {
15
+ let t = e.detail;
16
+ b({
17
+ open: !0,
18
+ kind: t?.kind ?? "generic"
19
+ });
20
+ };
21
+ return window.addEventListener(e, t), () => {
22
+ window.removeEventListener(e, t);
23
+ };
24
+ }, []);
25
+ let x = n(() => {
26
+ b((e) => ({
27
+ ...e,
28
+ open: !1
29
+ }));
30
+ }, []), S = n(() => {
31
+ b((e) => ({
32
+ ...e,
33
+ open: !1
34
+ })), v();
35
+ }, [v]), C = t(y.kind);
36
+ return /* @__PURE__ */ h(p, { children: [_, /* @__PURE__ */ m(s, {
37
+ open: y.open,
38
+ onOpenChange: (e) => !e && x(),
39
+ children: /* @__PURE__ */ h(c, {
40
+ className: "sm:max-w-md",
41
+ showCloseButton: !1,
42
+ children: [/* @__PURE__ */ h("div", {
43
+ className: "flex items-start gap-3",
44
+ children: [/* @__PURE__ */ m("div", {
45
+ className: "flex size-10 shrink-0 items-center justify-center rounded-full bg-status-warning-bg-subtle text-status-warning-text",
46
+ children: /* @__PURE__ */ m(a, {
47
+ className: "size-5",
48
+ "aria-hidden": "true"
49
+ })
50
+ }), /* @__PURE__ */ h(d, {
51
+ className: "text-left",
52
+ children: [/* @__PURE__ */ m(f, { children: C.title }), /* @__PURE__ */ m(l, { children: C.message })]
53
+ })]
54
+ }), /* @__PURE__ */ h(u, { children: [/* @__PURE__ */ m(o, {
55
+ type: "button",
56
+ variant: "outline",
57
+ onClick: x,
58
+ children: "Dismiss"
59
+ }), /* @__PURE__ */ m(o, {
60
+ type: "button",
61
+ onClick: S,
62
+ children: "View Billing"
63
+ })] })]
64
+ })
65
+ })] });
66
+ }
67
+ //#endregion
68
+ export { _ as BigConsoleQuotaProvider };
69
+
70
+ //# sourceMappingURL=BigConsoleQuotaProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BigConsoleQuotaProvider.js","names":[],"sources":["../../../src/bigconsole/providers/BigConsoleQuotaProvider.tsx"],"sourcesContent":["import { useCallback, useEffect, useState, type ReactNode } from 'react';\nimport { AlertTriangle } from 'lucide-react';\nimport {\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport {\n BIGCONSOLE_QUOTA_EXHAUSTED_EVENT,\n getBigConsoleQuotaDialogContent,\n type BigConsoleQuotaExhaustedEventDetail,\n type BigConsoleQuotaKind,\n} from '../utils/quota';\n\ninterface BigConsoleQuotaProviderProps {\n children: ReactNode;\n onViewBilling: () => void;\n}\n\ninterface QuotaDialogState {\n open: boolean;\n kind: BigConsoleQuotaKind;\n}\n\nconst INITIAL_STATE: QuotaDialogState = { open: false, kind: 'generic' };\n\nexport function BigConsoleQuotaProvider({ children, onViewBilling }: BigConsoleQuotaProviderProps) {\n const [state, setState] = useState<QuotaDialogState>(INITIAL_STATE);\n\n useEffect(() => {\n const handleQuotaExhausted = (event: Event) => {\n const detail = (event as CustomEvent<BigConsoleQuotaExhaustedEventDetail>).detail;\n setState({ open: true, kind: detail?.kind ?? 'generic' });\n };\n\n window.addEventListener(BIGCONSOLE_QUOTA_EXHAUSTED_EVENT, handleQuotaExhausted);\n return () => {\n window.removeEventListener(BIGCONSOLE_QUOTA_EXHAUSTED_EVENT, handleQuotaExhausted);\n };\n }, []);\n\n const handleClose = useCallback(() => {\n setState((current) => ({ ...current, open: false }));\n }, []);\n\n const handleViewBilling = useCallback(() => {\n setState((current) => ({ ...current, open: false }));\n onViewBilling();\n }, [onViewBilling]);\n\n const content = getBigConsoleQuotaDialogContent(state.kind);\n\n return (\n <>\n {children}\n <ResponsiveDialog open={state.open} onOpenChange={(open) => !open && handleClose()}>\n <ResponsiveDialogContent className=\"sm:max-w-md\" showCloseButton={false}>\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-10 shrink-0 items-center justify-center rounded-full bg-status-warning-bg-subtle text-status-warning-text\">\n <AlertTriangle className=\"size-5\" aria-hidden=\"true\" />\n </div>\n <ResponsiveDialogHeader className=\"text-left\">\n <ResponsiveDialogTitle>{content.title}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>{content.message}</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={handleClose}>\n Dismiss\n </Button>\n <Button type=\"button\" onClick={handleViewBilling}>\n View Billing\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </>\n );\n}\n"],"mappings":";;;;;;AA4BA,IAAM,IAAkC;CAAE,MAAM;CAAO,MAAM;CAAW;AAExE,SAAgB,EAAwB,EAAE,aAAU,oBAA+C;CACjG,IAAM,CAAC,GAAO,KAAY,EAA2B,EAAc;AAEnE,SAAgB;EACd,IAAM,KAAwB,MAAiB;GAC7C,IAAM,IAAU,EAA2D;AAC3E,KAAS;IAAE,MAAM;IAAM,MAAM,GAAQ,QAAQ;IAAW,CAAC;;AAI3D,SADA,OAAO,iBAAiB,GAAkC,EAAqB,QAClE;AACX,UAAO,oBAAoB,GAAkC,EAAqB;;IAEnF,EAAE,CAAC;CAEN,IAAM,IAAc,QAAkB;AACpC,KAAU,OAAa;GAAE,GAAG;GAAS,MAAM;GAAO,EAAE;IACnD,EAAE,CAAC,EAEA,IAAoB,QAAkB;AAE1C,EADA,GAAU,OAAa;GAAE,GAAG;GAAS,MAAM;GAAO,EAAE,EACpD,GAAe;IACd,CAAC,EAAc,CAAC,EAEb,IAAU,EAAgC,EAAM,KAAK;AAE3D,QACE,kBAAA,GAAA,EAAA,UAAA,CACG,GACD,kBAAC,GAAD;EAAkB,MAAM,EAAM;EAAM,eAAe,MAAS,CAAC,KAAQ,GAAa;YAChF,kBAAC,GAAD;GAAyB,WAAU;GAAc,iBAAiB;aAAlE,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAe,WAAU;MAAS,eAAY;MAAS,CAAA;KACnD,CAAA,EACN,kBAAC,GAAD;KAAwB,WAAU;eAAlC,CACE,kBAAC,GAAD,EAAA,UAAwB,EAAQ,OAA8B,CAAA,EAC9D,kBAAC,GAAD,EAAA,UAA8B,EAAQ,SAAsC,CAAA,CACrD;OACrB;OACN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAQ;IAAU,SAAS;cAAa;IAErD,CAAA,EACT,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAS;cAAmB;IAEzC,CAAA,CACc,EAAA,CAAA,CACD;;EACT,CAAA,CAClB,EAAA,CAAA"}
@@ -0,0 +1,86 @@
1
+ import { extractQuotaExhaustedExtensions as e } from "@burdenoff/fe-libs/shared/utils";
2
+ //#region src/bigconsole/utils/quota.ts
3
+ var t = "bigconsole:quota-exhausted", n = new Set([
4
+ "createbigconsoledashboard",
5
+ "clonedashboard",
6
+ "importdashboard",
7
+ "importbigconsoledashboard",
8
+ "instantiatetemplate",
9
+ "createdashboardfromgeneration"
10
+ ]), r = new Set(["createdashboardview"]), i = new Set([
11
+ "generatedashboardfromprompt",
12
+ "refinegenerateddashboard",
13
+ "regeneratedashboard",
14
+ "conversationaldashboardedit",
15
+ "previewpromptanalysis"
16
+ ]), a = new Set(["bigconsole.projects.count"]), o = new Set(["bigconsole.sessions.count"]), s = new Set(["bigconsole.commands.count"]);
17
+ function c(e) {
18
+ return typeof e == "object" && !!e;
19
+ }
20
+ function l(t) {
21
+ if (Array.isArray(t)) return e({ graphQLErrors: t });
22
+ if (!c(t)) return null;
23
+ if ("quotaResult" in t) {
24
+ let e = l(t.quotaResult);
25
+ if (e) return e;
26
+ }
27
+ if ("graphQLErrors" in t || "networkError" in t) {
28
+ let n = e(t);
29
+ if (n) return n;
30
+ }
31
+ if ("error" in t) {
32
+ let e = l(t.error);
33
+ if (e) return e;
34
+ }
35
+ if ("errors" in t && Array.isArray(t.errors)) {
36
+ let n = e({ graphQLErrors: t.errors });
37
+ if (n) return n;
38
+ }
39
+ if ("extensions" in t && c(t.extensions) && t.extensions.code === "QUOTA_EXHAUSTED") {
40
+ let n = e({ graphQLErrors: [t] });
41
+ if (n) return n;
42
+ }
43
+ return null;
44
+ }
45
+ function u(e) {
46
+ let t = /* @__PURE__ */ Error("QUOTA_EXHAUSTED");
47
+ return t.name = "BigConsoleQuotaError", Object.assign(t, { quotaResult: e });
48
+ }
49
+ function d(e) {
50
+ return l(e) !== null;
51
+ }
52
+ function f(e, t = "generic") {
53
+ let c = e.quotaName?.toLowerCase(), l = e.field?.toLowerCase();
54
+ return l && n.has(l) || c && a.has(c) ? "project" : l && r.has(l) || c && o.has(c) ? "session" : l && i.has(l) || c && s.has(c) ? "command" : t;
55
+ }
56
+ function p(e, n = "generic") {
57
+ let r = l(e);
58
+ return !r || typeof window > "u" ? !1 : (window.dispatchEvent(new CustomEvent(t, { detail: {
59
+ kind: f(r, n),
60
+ extensions: r
61
+ } })), !0);
62
+ }
63
+ function m(e) {
64
+ switch (e) {
65
+ case "project": return {
66
+ title: "Dashboard limit reached",
67
+ message: "You have used all dashboards available on your current BigConsole plan. Upgrade to create, clone, import, or instantiate more dashboards."
68
+ };
69
+ case "session": return {
70
+ title: "Saved view limit reached",
71
+ message: "You have used all saved views available on your current BigConsole plan. Upgrade to save more dashboard views."
72
+ };
73
+ case "command": return {
74
+ title: "AI command limit reached",
75
+ message: "You have used all AI commands available on your current BigConsole plan. Upgrade to generate and refine more dashboards."
76
+ };
77
+ default: return {
78
+ title: "Quota reached",
79
+ message: "You have reached a BigConsole usage limit on your current plan. Upgrade to continue."
80
+ };
81
+ }
82
+ }
83
+ //#endregion
84
+ export { t as BIGCONSOLE_QUOTA_EXHAUSTED_EVENT, u as createBigConsoleQuotaError, m as getBigConsoleQuotaDialogContent, d as isBigConsoleQuotaError, p as reportBigConsoleQuotaError };
85
+
86
+ //# sourceMappingURL=quota.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota.js","names":[],"sources":["../../../src/bigconsole/utils/quota.ts"],"sourcesContent":["import { extractQuotaExhaustedExtensions, type QuotaExhaustedExtensions } from '@burdenoff/fe-libs/shared/utils';\n\nexport const BIGCONSOLE_QUOTA_EXHAUSTED_EVENT = 'bigconsole:quota-exhausted';\n\nexport type BigConsoleQuotaKind = 'project' | 'session' | 'command' | 'generic';\n\nexport interface BigConsoleQuotaExhaustedEventDetail {\n kind: BigConsoleQuotaKind;\n extensions: QuotaExhaustedExtensions;\n}\n\nconst PROJECT_FIELDS = new Set([\n 'createbigconsoledashboard',\n 'clonedashboard',\n 'importdashboard',\n 'importbigconsoledashboard',\n 'instantiatetemplate',\n 'createdashboardfromgeneration',\n]);\nconst SESSION_FIELDS = new Set(['createdashboardview']);\nconst COMMAND_FIELDS = new Set([\n 'generatedashboardfromprompt',\n 'refinegenerateddashboard',\n 'regeneratedashboard',\n 'conversationaldashboardedit',\n 'previewpromptanalysis',\n]);\nconst PROJECT_QUOTA_NAMES = new Set(['bigconsole.projects.count']);\nconst SESSION_QUOTA_NAMES = new Set(['bigconsole.sessions.count']);\nconst COMMAND_QUOTA_NAMES = new Set(['bigconsole.commands.count']);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object';\n}\n\nexport function extractBigConsoleQuotaExtensions(value: unknown): QuotaExhaustedExtensions | null {\n if (Array.isArray(value)) {\n return extractQuotaExhaustedExtensions({ graphQLErrors: value });\n }\n\n if (!isObject(value)) {\n return null;\n }\n\n if ('quotaResult' in value) {\n const fromQuotaResult = extractBigConsoleQuotaExtensions(value.quotaResult);\n if (fromQuotaResult) return fromQuotaResult;\n }\n\n if ('graphQLErrors' in value || 'networkError' in value) {\n const fromApolloError = extractQuotaExhaustedExtensions(value);\n if (fromApolloError) return fromApolloError;\n }\n\n if ('error' in value) {\n const fromError = extractBigConsoleQuotaExtensions(value.error);\n if (fromError) return fromError;\n }\n\n if ('errors' in value && Array.isArray(value.errors)) {\n const fromErrors = extractQuotaExhaustedExtensions({ graphQLErrors: value.errors });\n if (fromErrors) return fromErrors;\n }\n\n if ('extensions' in value && isObject(value.extensions) && value.extensions.code === 'QUOTA_EXHAUSTED') {\n const fromGraphQLError = extractQuotaExhaustedExtensions({ graphQLErrors: [value] });\n if (fromGraphQLError) return fromGraphQLError;\n }\n\n return null;\n}\n\nexport function createBigConsoleQuotaError(value: unknown): Error {\n const error = new Error('QUOTA_EXHAUSTED');\n error.name = 'BigConsoleQuotaError';\n return Object.assign(error, { quotaResult: value });\n}\n\nexport function isBigConsoleQuotaError(value: unknown): boolean {\n return extractBigConsoleQuotaExtensions(value) !== null;\n}\n\nexport function inferBigConsoleQuotaKind(\n extensions: QuotaExhaustedExtensions,\n fallback: BigConsoleQuotaKind = 'generic'\n): BigConsoleQuotaKind {\n const quotaName = extensions.quotaName?.toLowerCase();\n const field = extensions.field?.toLowerCase();\n\n if ((field && PROJECT_FIELDS.has(field)) || (quotaName && PROJECT_QUOTA_NAMES.has(quotaName))) {\n return 'project';\n }\n\n if ((field && SESSION_FIELDS.has(field)) || (quotaName && SESSION_QUOTA_NAMES.has(quotaName))) {\n return 'session';\n }\n\n if ((field && COMMAND_FIELDS.has(field)) || (quotaName && COMMAND_QUOTA_NAMES.has(quotaName))) {\n return 'command';\n }\n\n return fallback;\n}\n\nexport function reportBigConsoleQuotaError(value: unknown, fallbackKind: BigConsoleQuotaKind = 'generic'): boolean {\n const extensions = extractBigConsoleQuotaExtensions(value);\n if (!extensions) return false;\n if (typeof window === 'undefined') return false;\n\n window.dispatchEvent(\n new CustomEvent<BigConsoleQuotaExhaustedEventDetail>(BIGCONSOLE_QUOTA_EXHAUSTED_EVENT, {\n detail: {\n kind: inferBigConsoleQuotaKind(extensions, fallbackKind),\n extensions,\n },\n })\n );\n\n return true;\n}\n\nexport function getBigConsoleQuotaDialogContent(kind: BigConsoleQuotaKind): {\n title: string;\n message: string;\n} {\n switch (kind) {\n case 'project':\n return {\n title: 'Dashboard limit reached',\n message:\n 'You have used all dashboards available on your current BigConsole plan. Upgrade to create, clone, import, or instantiate more dashboards.',\n };\n case 'session':\n return {\n title: 'Saved view limit reached',\n message:\n 'You have used all saved views available on your current BigConsole plan. Upgrade to save more dashboard views.',\n };\n case 'command':\n return {\n title: 'AI command limit reached',\n message:\n 'You have used all AI commands available on your current BigConsole plan. Upgrade to generate and refine more dashboards.',\n };\n case 'generic':\n default:\n return {\n title: 'Quota reached',\n message: 'You have reached a BigConsole usage limit on your current plan. Upgrade to continue.',\n };\n }\n}\n"],"mappings":";;AAEA,IAAa,IAAmC,8BAS1C,IAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,EACI,IAAiB,IAAI,IAAI,CAAC,sBAAsB,CAAC,EACjD,IAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACD,CAAC,EACI,IAAsB,IAAI,IAAI,CAAC,4BAA4B,CAAC,EAC5D,IAAsB,IAAI,IAAI,CAAC,4BAA4B,CAAC,EAC5D,IAAsB,IAAI,IAAI,CAAC,4BAA4B,CAAC;AAElE,SAAS,EAAS,GAAkD;AAClE,QAAyB,OAAO,KAAU,cAAnC;;AAGT,SAAgB,EAAiC,GAAiD;AAChG,KAAI,MAAM,QAAQ,EAAM,CACtB,QAAO,EAAgC,EAAE,eAAe,GAAO,CAAC;AAGlE,KAAI,CAAC,EAAS,EAAM,CAClB,QAAO;AAGT,KAAI,iBAAiB,GAAO;EAC1B,IAAM,IAAkB,EAAiC,EAAM,YAAY;AAC3E,MAAI,EAAiB,QAAO;;AAG9B,KAAI,mBAAmB,KAAS,kBAAkB,GAAO;EACvD,IAAM,IAAkB,EAAgC,EAAM;AAC9D,MAAI,EAAiB,QAAO;;AAG9B,KAAI,WAAW,GAAO;EACpB,IAAM,IAAY,EAAiC,EAAM,MAAM;AAC/D,MAAI,EAAW,QAAO;;AAGxB,KAAI,YAAY,KAAS,MAAM,QAAQ,EAAM,OAAO,EAAE;EACpD,IAAM,IAAa,EAAgC,EAAE,eAAe,EAAM,QAAQ,CAAC;AACnF,MAAI,EAAY,QAAO;;AAGzB,KAAI,gBAAgB,KAAS,EAAS,EAAM,WAAW,IAAI,EAAM,WAAW,SAAS,mBAAmB;EACtG,IAAM,IAAmB,EAAgC,EAAE,eAAe,CAAC,EAAM,EAAE,CAAC;AACpF,MAAI,EAAkB,QAAO;;AAG/B,QAAO;;AAGT,SAAgB,EAA2B,GAAuB;CAChE,IAAM,IAAQ,gBAAI,MAAM,kBAAkB;AAE1C,QADA,EAAM,OAAO,wBACN,OAAO,OAAO,GAAO,EAAE,aAAa,GAAO,CAAC;;AAGrD,SAAgB,EAAuB,GAAyB;AAC9D,QAAO,EAAiC,EAAM,KAAK;;AAGrD,SAAgB,EACd,GACA,IAAgC,WACX;CACrB,IAAM,IAAY,EAAW,WAAW,aAAa,EAC/C,IAAQ,EAAW,OAAO,aAAa;AAc7C,QAZK,KAAS,EAAe,IAAI,EAAM,IAAM,KAAa,EAAoB,IAAI,EAAU,GACnF,YAGJ,KAAS,EAAe,IAAI,EAAM,IAAM,KAAa,EAAoB,IAAI,EAAU,GACnF,YAGJ,KAAS,EAAe,IAAI,EAAM,IAAM,KAAa,EAAoB,IAAI,EAAU,GACnF,YAGF;;AAGT,SAAgB,EAA2B,GAAgB,IAAoC,WAAoB;CACjH,IAAM,IAAa,EAAiC,EAAM;AAa1D,QAZI,CAAC,KACD,OAAO,SAAW,MAAoB,MAE1C,OAAO,cACL,IAAI,YAAiD,GAAkC,EACrF,QAAQ;EACN,MAAM,EAAyB,GAAY,EAAa;EACxD;EACD,EACF,CAAC,CACH,EAEM;;AAGT,SAAgB,EAAgC,GAG9C;AACA,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;GACL,OAAO;GACP,SACE;GACH;EACH,KAAK,UACH,QAAO;GACL,OAAO;GACP,SACE;GACH;EACH,KAAK,UACH,QAAO;GACL,OAAO;GACP,SACE;GACH;EAEH,QACE,QAAO;GACL,OAAO;GACP,SAAS;GACV"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-bigconsole",
3
- "version": "2026.716.2",
3
+ "version": "2026.716.3",
4
4
  "description": "BigConsole - AI-powered analytics and dashboard platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -42,11 +42,11 @@
42
42
  "prepare": "lefthook install || true"
43
43
  },
44
44
  "peerDependencies": {
45
- "@monaco-editor/react": "^4.7.0",
46
45
  "@apollo/client": "^4.0.11",
47
46
  "@burdenoff/fe-libs": "2026.713.1",
48
47
  "@burdenoff/fluidgrids-fe-sdk": "2026.709.1",
49
48
  "@iconify/react": "^6.0.2",
49
+ "@monaco-editor/react": "^4.7.0",
50
50
  "@radix-ui/react-icons": "^1.3.2",
51
51
  "@tanstack/react-query": "^5.62.13",
52
52
  "@xyflow/react": "^12.10.0",