This is an automated email from the ASF dual-hosted git repository. kgabryje pushed a commit to branch what-if in repository https://gitbox.apache.org/repos/asf/superset.git
commit f7bb0c8ed369b125728ec0c67fe80e59fd33efa1 Author: Kamil Gabryjelski <[email protected]> AuthorDate: Thu Dec 18 23:46:31 2025 +0100 Insights collapsible sections --- .../components/WhatIfDrawer/WhatIfAIInsights.tsx | 131 +++++++++++++++++++-- .../src/dashboard/components/WhatIfDrawer/types.ts | 9 +- 2 files changed, 129 insertions(+), 11 deletions(-) diff --git a/superset-frontend/src/dashboard/components/WhatIfDrawer/WhatIfAIInsights.tsx b/superset-frontend/src/dashboard/components/WhatIfDrawer/WhatIfAIInsights.tsx index dda919da61..8778017172 100644 --- a/superset-frontend/src/dashboard/components/WhatIfDrawer/WhatIfAIInsights.tsx +++ b/superset-frontend/src/dashboard/components/WhatIfDrawer/WhatIfAIInsights.tsx @@ -17,19 +17,20 @@ * under the License. */ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; import { t } from '@superset-ui/core'; import { styled, Alert } from '@apache-superset/core/ui'; -import { Icons } from '@superset-ui/core/components/Icons'; -import { Skeleton } from '@superset-ui/core/components/'; +import { Icons, IconType } from '@superset-ui/core/components/Icons'; +import { Collapse, Skeleton } from '@superset-ui/core/components/'; import { RootState, WhatIfModification } from 'src/dashboard/types'; import { whatIfHighlightStyles } from 'src/dashboard/util/useWhatIfHighlightStyles'; import { fetchWhatIfInterpretation } from './whatIfApi'; import { useChartComparison, useAllChartsLoaded } from './useChartComparison'; import { + GroupedWhatIfInsights, WhatIfAIStatus, - WhatIfInsight, + WhatIfInsightType, WhatIfInterpretResponse, } from './types'; @@ -37,6 +38,26 @@ import { const SKELETON_PARAGRAPH_3 = { rows: 3 }; const SKELETON_PARAGRAPH_2 = { rows: 2 }; +// Configuration for each insight type +const INSIGHT_TYPE_CONFIG: Record< + WhatIfInsightType, + { label: string; icon: React.ComponentType<IconType> } +> = { + observation: { label: t('Observations'), icon: Icons.EyeOutlined }, + implication: { label: t('Implications'), icon: Icons.WarningOutlined }, + recommendation: { + label: t('Recommendations'), + icon: Icons.CheckCircleOutlined, + }, +}; + +// Order in which insight types should appear +const INSIGHT_TYPE_ORDER: WhatIfInsightType[] = [ + 'observation', + 'implication', + 'recommendation', +]; + /** * Create a stable key from modifications for comparison. * This allows us to detect when modifications have meaningfully changed. @@ -116,6 +137,54 @@ const Summary = styled.div` ${whatIfHighlightStyles} `; +const StyledCollapse = styled(Collapse)` + background: transparent; + border: none; + + .ant-collapse-item { + border: none; + margin-bottom: ${({ theme }) => theme.sizeUnit * 2}px; + background: ${({ theme }) => theme.colorBgElevated}; + border-radius: ${({ theme }) => theme.borderRadius}px !important; + overflow: hidden; + + .ant-collapse-header { + padding: ${({ theme }) => theme.sizeUnit * 3}px; + background: transparent; + } + + .ant-collapse-content { + border-top: 1px solid ${({ theme }) => theme.colorBorderSecondary}; + } + + .ant-collapse-content-box { + padding: ${({ theme }) => theme.sizeUnit * 2}px; + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.sizeUnit * 2}px; + } + } +`; + +const CollapsePanelHeader = styled.div<{ insightType: WhatIfInsightType }>` + display: flex; + align-items: center; + gap: ${({ theme }) => theme.sizeUnit * 2}px; + font-weight: ${({ theme }) => theme.fontWeightStrong}; + color: ${({ theme, insightType }) => { + switch (insightType) { + case 'observation': + return theme.colorInfo; + case 'implication': + return theme.colorWarning; + case 'recommendation': + return theme.colorSuccess; + default: + return theme.colorText; + } + }}; +`; + interface WhatIfAIInsightsProps { affectedChartIds: number[]; modifications: WhatIfModification[]; @@ -236,6 +305,48 @@ const WhatIfAIInsights = ({ } }, [modifications]); + // Group insights by type + const insights = response?.insights; + const groupedInsights = useMemo(() => { + if (!insights) return {} as GroupedWhatIfInsights; + return insights.reduce<GroupedWhatIfInsights>((acc, insight) => { + if (!acc[insight.type]) { + acc[insight.type] = []; + } + acc[insight.type].push(insight); + return acc; + }, {} as GroupedWhatIfInsights); + }, [insights]); + + // Build collapse items from grouped insights + const collapseItems = useMemo( + () => + INSIGHT_TYPE_ORDER.filter(type => groupedInsights[type]?.length > 0).map( + type => { + const typeInsights = groupedInsights[type]; + const config = INSIGHT_TYPE_CONFIG[type]; + const IconComponent = config.icon; + + return { + key: type, + label: ( + <CollapsePanelHeader insightType={type}> + <IconComponent iconSize="m" /> + {config.label} + </CollapsePanelHeader> + ), + children: typeInsights.map((insight, index) => ( + <InsightCard key={index} insightType={insight.type}> + <InsightTitle>{insight.title}</InsightTitle> + <InsightDescription>{insight.description}</InsightDescription> + </InsightCard> + )), + }; + }, + ), + [groupedInsights], + ); + if (modifications.length === 0) { return null; } @@ -264,12 +375,12 @@ const WhatIfAIInsights = ({ <> <Summary>{response.summary}</Summary> - {response.insights.map((insight: WhatIfInsight, index: number) => ( - <InsightCard key={index} insightType={insight.type}> - <InsightTitle>{insight.title}</InsightTitle> - <InsightDescription>{insight.description}</InsightDescription> - </InsightCard> - ))} + <StyledCollapse + defaultActiveKey={INSIGHT_TYPE_ORDER} + items={collapseItems} + ghost + bordered + /> </> )} diff --git a/superset-frontend/src/dashboard/components/WhatIfDrawer/types.ts b/superset-frontend/src/dashboard/components/WhatIfDrawer/types.ts index 838ad6770c..787b5b2d7f 100644 --- a/superset-frontend/src/dashboard/components/WhatIfDrawer/types.ts +++ b/superset-frontend/src/dashboard/components/WhatIfDrawer/types.ts @@ -53,10 +53,15 @@ export interface WhatIfInterpretRequest { dashboardName?: string; } +export type WhatIfInsightType = + | 'observation' + | 'implication' + | 'recommendation'; + export interface WhatIfInsight { title: string; description: string; - type: 'observation' | 'implication' | 'recommendation'; + type: WhatIfInsightType; } export interface WhatIfInterpretResponse { @@ -65,6 +70,8 @@ export interface WhatIfInterpretResponse { rawResponse?: string; } +export type GroupedWhatIfInsights = Record<WhatIfInsightType, WhatIfInsight[]>; + export type WhatIfAIStatus = 'idle' | 'loading' | 'success' | 'error'; // Types for /suggest_related API endpoint
