/** * Visualization types for graph analytics results. * These types match the backend @thatworks/shared-frontend/graph-analytics-visualization types. */ /** * Trend indicator for metric card. * Shows direction and magnitude of change. */ export interface MetricCardTrend { /** Display value (e.g., "2%", "+$50K", "-15") */ displayValue: string; /** Direction of the trend (determines arrow direction) */ direction: 'up' | 'down' | 'neutral'; /** * Semantic meaning - determines color. * 'positive' = green (improvement), 'negative' = red (regression), 'neutral' = gray. * This is separate from direction because "down 2%" could be positive for cancellation rate. */ sentiment: 'positive' | 'negative' | 'neutral'; /** Optional comparison period label (e.g., "vs last week") */ comparisonLabel?: string; } /** * Sparkline configuration for mini time-series in metric card. */ export interface MetricCardSparkline { /** Data points for the sparkline */ data: number[]; /** Optional labels for data points (used in tooltips) */ labels?: string[]; /** Chart type */ chartType: 'line' | 'bar'; /** Optional color for the sparkline */ color?: string; /** Label shown at the start (left) of the sparkline (e.g., "25 Jan") */ startLabel?: string; /** Label shown at the end (right) of the sparkline (e.g., "25 Feb") */ endLabel?: string; } /** * Metric card (KPI card) visualization for displaying a single key metric * with optional trend indicator and sparkline chart. */ export interface MetricCardVisualization { type: 'metric-card'; /** Card title (e.g., "LEARNER CANCELLATION RATE") */ title: string; /** Primary metric value (e.g., "7.8%", "$1.2M", "42") */ primaryValue: string; /** Optional supporting context text (e.g., "42 learners cancelled") */ supportingText?: string; /** Optional trend indicator showing change from previous period */ trend?: MetricCardTrend; /** Optional sparkline for mini time-series visualization */ sparkline?: MetricCardSparkline; } /** A single pill item with label and optional background color. */ export interface PillBadgeItem { label: string; /** Optional CSS color for the pill background (e.g., '#E8F5E9') */ color?: string; } /** Pill/badge visualization for short labels rendered as rounded chips. */ export interface PillBadgeVisualization { type: 'pill-badge'; pills: PillBadgeItem[]; } /** A card shown inside a tooltip with a title and key-value detail lines. */ export interface TooltipCard { title: string; /** Optional URL to make the title a hyperlink */ url?: string; details: { label: string; value: string; }[]; } /** A named group of tooltip cards, rendered as a section with a header. */ export interface TooltipSection { title: string; cards: TooltipCard[]; } /** Text block with a primary markdown body and an optional muted subtitle. */ export interface AnnotatedTextVisualization { type: 'annotated-text'; /** Primary content rendered as markdown */ markdown: string; /** Optional secondary text rendered in muted style */ subtitle?: string; /** Optional tooltip shown via info icon */ tooltip?: TooltipSection[]; } /** Visualization types allowed inside table cells (no nested tables). */ export type CellVisualizationResult = MetricCardVisualization | PillBadgeVisualization | AnnotatedTextVisualization; /** Column definition for table visualization. */ export interface TableColumn { key: string; label: string; width?: string; } /** Cell in a table visualization. */ export interface TableCell { plainText?: string; /** Embedded visualization (e.g., pill-badge or annotated-text). Tables cannot be nested. */ visualization?: CellVisualizationResult; /** Rich text content as markdown */ markdown?: string; /** Number of rows this cell should span */ rowspan?: number; /** Truncate plain text to single line with tooltip on hover */ truncatePlainText?: boolean; /** Optional URL to wrap the plain text value in a hyperlink */ plainTextUrl?: string; } /** Row in a table visualization. */ export interface TableRow { id: string; cells: Record; /** If set, this row is rendered as a section header spanning all columns */ sectionHeader?: string; sectionHeaderColor?: string; backgroundColor?: string; } /** Table visualization for displaying structured data rows. */ export interface TableVisualization { type: 'table'; title?: string; descriptionMarkdown?: string; columns: TableColumn[]; rows: TableRow[]; hideHeader?: boolean; headerBackgroundColor?: string; headerBold?: boolean; /** Show only the first N rows initially, with a "Show more" button */ initialRowLimit?: number; } /** * Discriminated union of all visualization types. * Use the `type` field to determine which visualization to render. */ export type VisualizationResult = MetricCardVisualization | PillBadgeVisualization | AnnotatedTextVisualization | TableVisualization; /** * Supported relative time periods for analytics queries. * These provide a user-friendly way to specify date ranges without calculating dates. */ export type RelativeTimePeriod = 'this_quarter' | 'this_month' | 'rolling_30_days' | 'rolling_60_days' | 'rolling_90_days'; /** * Analytics configuration from GET /rest/analytics/config */ export interface AnalyticsConfig { /** Unique identifier for the analytics configuration */ id: string; /** Display name for the analytics */ name: string; /** Whether this analytics is available for the connection */ enabled: boolean; } /** * Analytics config response from GET /rest/analytics/config */ export interface AnalyticsConfigResponse { /** List of available analytics configurations */ analytics: AnalyticsConfig[]; /** Default time period for analytics queries */ timePeriod: RelativeTimePeriod; /** Whether to include delta comparison with the previous period by default */ includeDelta: boolean; } /** * Per-insight parameter overrides, keyed by analyticsId. * Each entry is a flat map of parameter key to value (number | boolean | string) matching * the parameter declarations on the corresponding insight. Values for an analyticsId are * only sent to that insight's `/analytics` call, so keys cannot leak across insights. * * @example * { * growth_opportunities: { lookAheadDays: 30, capacityThreshold: 85 }, * } */ export type AnalyticsParameters = Record>; /** * Analytics response from GET /rest/analytics */ export interface AnalyticsResponse { /** The analytics configuration ID that was executed */ analyticsId: string; /** Unique identifier for this analysis run (for caching/LLM context) */ analysisId: string; /** The visualization result data */ visualization: VisualizationResult; } /** * Analytics data with loading and error state */ export interface AnalyticsData { /** Analytics configuration */ config: AnalyticsConfig; /** Visualization result (null if loading or error) */ visualization: VisualizationResult | null; /** Analysis ID from the response */ analysisId: string | null; /** Loading state */ loading: boolean; /** Error if fetch failed */ error: Error | null; }