import React, { useMemo } from 'react'; import type { ReactHTML, ReactElement, ReactNode } from 'react'; import { InlineDefinition, KeylineCard, Subtitle, css, spacing, palette, useDarkMode, cx, IndexBadge, IndexIcon, Icon, SignalPopover, PerformanceSignals, } from '@mongodb-js/compass-components'; import { openCreateIndexModal, type SerializedExplainPlan, } from '../stores/explain-plan-modal-store'; import { connect } from 'react-redux'; import { usePreference } from 'compass-preferences-model'; const defaultFormatter = (_: unknown) => String(_); const statsStyles = css({ display: 'flex', alignItems: 'center', gap: spacing[2], }); const statsTextStyles = css({ lineHeight: '20px', }); const ExplainPlanSummaryStat = ({ as = 'li', glyph, value, formatter = defaultFormatter, label, definition, 'data-testid': dataTestId, }: { as?: keyof ReactHTML; glyph?: ReactElement; value?: T; formatter?: (val: T) => string; label: ReactNode; definition: React.ReactNode; ['data-testid']?: string; }): ReactElement | null => { return React.createElement( as, { className: statsStyles }, glyph ? React.cloneElement(glyph, { viewBox: `0 0 ${spacing[3]} ${spacing[3]}`, width: spacing[3], height: spacing[3], }) : null, {typeof value === 'undefined' ? ( label ) : ( <> {formatter(value)} {label} )} ); }; type ExplainPlanSummaryProps = { docsReturned: number; docsExamined: number; executionTimeMs: number; sortedInMemory: boolean; indexKeysExamined: number; indexType: SerializedExplainPlan['indexType']; indexKeys: [string, unknown][]; onCreateIndexInsightClick(): void; }; const summaryCardStyles = css({ display: 'grid', gridTemplateRows: 'auto 1fr', maxHeight: '100%', overflow: 'hidden', }); const summaryHeadingStyles = css({ paddingTop: spacing[3], paddingBottom: spacing[3], paddingLeft: spacing[4], paddingRight: spacing[4], backgroundColor: palette.gray.light3, boxShadow: `0 0 0 1px ${palette.gray.light2}`, }); const summaryHeadingDarkModeStyles = css({ backgroundColor: palette.gray.dark3, boxShadow: `0 0 0 1px ${palette.gray.dark2}`, }); const statsListStyles = css({ display: 'grid', gap: spacing[3], paddingTop: spacing[3], paddingBottom: spacing[4], paddingLeft: spacing[4], paddingRight: spacing[4], overflow: 'auto', }); const indexesSummaryStyles = css({ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: spacing[2], }); const indexIconDescriptionStyles = css({ verticalAlign: 'text-top', }); export const ExplainPlanSummary: React.FunctionComponent< ExplainPlanSummaryProps > = ({ docsReturned, docsExamined, executionTimeMs, sortedInMemory, indexKeysExamined, indexType, indexKeys, onCreateIndexInsightClick, }) => { const darkMode = useDarkMode(); const showInsights = usePreference('showInsights', React); const warningColor = darkMode ? palette.yellow.base : palette.yellow.dark2; const indexMessageText = useMemo(() => { const typeToMessage = { COLLSCAN: 'No index available for this query.', COVERED: 'Query covered by index:', MULTIPLE: 'Query used the following indexes (shard results differ):', INDEX: 'Query used the following index:', UNAVAILABLE: '', }; return typeToMessage[indexType]; }, [indexType]); const hasNoIndex = ['COLLSCAN', 'UNAVAILABLE'].includes(indexType); return ( Query Performance Summary ); }; export default connect(null, { onCreateIndexInsightClick: openCreateIndexModal, })(ExplainPlanSummary);