11'use client'
22
33import { useMemo } from 'react'
4- import { BarChart , ChartFrame , DashboardMetric , DonutChart , formatChartLatency } from '@sim/emcn'
4+ import {
5+ BarChart ,
6+ type BarChartSeries ,
7+ ChartFrame ,
8+ ChartLegend ,
9+ type ChartLegendItem ,
10+ cn ,
11+ DashboardMetric ,
12+ formatChartLatency ,
13+ } from '@sim/emcn'
514import type { OrganizationActivitySummary } from '@/lib/api/contracts/organization-activity'
615import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section'
16+ import {
17+ USAGE_CHAT_COLOR ,
18+ USAGE_OTHER_COLOR ,
19+ USAGE_PALETTE_CLASS ,
20+ } from '@/ee/organization-usage/constants'
21+ import { useLegendHighlight } from '@/ee/organization-usage/hooks/use-legend-highlight'
722import { useOrganizationActivitySummary } from '@/hooks/queries/organization-activity'
823import type { OrganizationUsageWindowKey } from '@/hooks/queries/utils/organization-usage-keys'
924
25+ const CHART_HEIGHT = 180
26+
27+ /**
28+ * Outcome layers, bottom-up. Failed is the status red and sits on the stack where a
29+ * spike reads at a glance; Other (cancelled, paused, unfinished) stays neutral, in a
30+ * gray whose lightness keeps it apart from the red for color-vision deficiency.
31+ */
32+ const OUTCOMES = [
33+ { id : 'completed' , label : 'Completed' , color : 'var(--brand-blue)' } ,
34+ { id : 'failed' , label : 'Failed' , color : 'var(--text-error)' } ,
35+ { id : 'other' , label : 'Other' , color : USAGE_OTHER_COLOR } ,
36+ ] as const
37+
38+ const OUTCOME_LEGEND : ChartLegendItem [ ] = [ ...OUTCOMES ]
39+ const OUTCOME_IDS = OUTCOMES . map ( ( outcome ) => outcome . id )
40+
41+ type ActivityPoint = OrganizationActivitySummary [ 'series' ] [ number ]
42+
43+ const OUTCOME_VALUE : Record < ( typeof OUTCOMES ) [ number ] [ 'id' ] , ( point : ActivityPoint ) => number > = {
44+ completed : ( point ) => point . completed ,
45+ failed : ( point ) => point . failed ,
46+ other : ( point ) => Math . max ( 0 , point . workflowRuns - point . completed - point . failed ) ,
47+ }
48+
1049interface ActivitySummaryProps {
1150 summary ?: OrganizationActivitySummary
1251 loading ?: boolean
@@ -24,30 +63,26 @@ export function formatFailureRate(rate: number | null): string {
2463}
2564
2665export function ActivitySummary ( { summary, loading, error, onRetry } : ActivitySummaryProps ) {
27- const workflowSeries = useMemo (
66+ const highlight = useLegendHighlight ( OUTCOME_IDS )
67+
68+ const outcomeSeries = useMemo < BarChartSeries [ ] > (
2869 ( ) =>
29- summary ?. series . map ( ( point ) => ( {
30- timestamp : point . timestamp ,
31- value : point . workflowRuns ,
32- } ) ) ?? [ ] ,
70+ OUTCOMES . map ( ( outcome ) => ( {
71+ ...outcome ,
72+ data : ( summary ?. series ?? [ ] ) . map ( ( point ) => ( {
73+ timestamp : point . timestamp ,
74+ value : OUTCOME_VALUE [ outcome . id ] ( point ) ,
75+ } ) ) ,
76+ } ) ) ,
3377 [ summary ?. series ]
3478 )
79+
3580 const chatSeries = useMemo (
3681 ( ) =>
37- summary ?. series . map ( ( point ) => ( {
38- timestamp : point . timestamp ,
39- value : point . chatRuns ,
40- } ) ) ?? [ ] ,
41- [ summary ?. series ]
42- )
43- const failureSeries = useMemo (
44- ( ) =>
45- summary ?. series . map ( ( point ) => ( {
46- timestamp : point . timestamp ,
47- value : point . failed ,
48- } ) ) ?? [ ] ,
82+ summary ?. series . map ( ( point ) => ( { timestamp : point . timestamp , value : point . chatRuns } ) ) ?? [ ] ,
4983 [ summary ?. series ]
5084 )
85+
5186 const totals = summary ?. totals
5287 const metrics = [
5388 {
@@ -81,18 +116,10 @@ export function ActivitySummary({ summary, loading, error, onRetry }: ActivitySu
81116 description : 'Completed and failed workflows with a recorded duration.' ,
82117 } ,
83118 ]
84- const outcomes = [
85- { label : 'Completed' , value : totals ?. completed ?? 0 , color : 'var(--indicator-seat-filled)' } ,
86- { label : 'Failed' , value : totals ?. failed ?? 0 , color : 'var(--text-error)' } ,
87- {
88- label : 'Other' ,
89- value : totals ? totals . workflowRuns - totals . completed - totals . failed : 0 ,
90- color : 'var(--text-muted)' ,
91- } ,
92- ]
93119 const chartState = { loading, error : error ? "Couldn't load activity." : undefined , onRetry }
120+
94121 return (
95- < div className = 'flex flex-col gap-5' >
122+ < div className = { cn ( 'flex flex-col gap-5' , USAGE_PALETTE_CLASS ) } >
96123 < div className = 'grid grid-cols-[repeat(auto-fit,minmax(min(120px,100%),1fr))] gap-4' >
97124 { metrics . map ( ( metric ) => (
98125 < DashboardMetric
@@ -104,41 +131,32 @@ export function ActivitySummary({ summary, loading, error, onRetry }: ActivitySu
104131 ) ) }
105132 </ div >
106133 < div className = 'grid grid-cols-[repeat(auto-fit,minmax(min(280px,100%),1fr))] gap-6' >
107- < ChartFrame title = 'Workflow runs' height = { 160 } { ...chartState } >
134+ < div className = 'flex min-w-0 flex-col gap-2' >
135+ < ChartFrame
136+ title = 'Workflow runs'
137+ description = 'Other includes cancelled, paused, and unfinished runs.'
138+ height = { CHART_HEIGHT }
139+ { ...chartState }
140+ >
141+ < BarChart
142+ label = ''
143+ xAxisFormat = 'date'
144+ height = { CHART_HEIGHT }
145+ series = { outcomeSeries }
146+ highlightedSeriesId = { highlight . highlightedId }
147+ />
148+ </ ChartFrame >
149+ < ChartLegend layout = 'row' items = { OUTCOME_LEGEND } { ...highlight . legendProps } />
150+ </ div >
151+ < ChartFrame title = 'Chat runs' height = { CHART_HEIGHT } { ...chartState } >
108152 < BarChart
109- xAxisFormat = 'date'
110- data = { workflowSeries }
111153 label = ''
112- color = 'var(--indicator-seat-filled)'
113- height = { 160 }
114- />
115- </ ChartFrame >
116- < ChartFrame title = 'Chat runs' height = { 160 } { ...chartState } >
117- < BarChart
118154 xAxisFormat = 'date'
155+ height = { CHART_HEIGHT }
119156 data = { chatSeries }
120- label = ''
121- color = 'var(--indicator-seat-filled)'
122- height = { 160 }
157+ color = { USAGE_CHAT_COLOR }
123158 />
124159 </ ChartFrame >
125- < ChartFrame title = 'Failed runs' height = { 160 } { ...chartState } >
126- < BarChart
127- xAxisFormat = 'date'
128- data = { failureSeries }
129- label = ''
130- color = 'var(--text-error)'
131- height = { 160 }
132- />
133- </ ChartFrame >
134- < ChartFrame
135- title = 'Workflow outcomes'
136- description = 'Other includes cancelled, paused, and unfinished runs.'
137- height = { 160 }
138- { ...chartState }
139- >
140- < DonutChart segments = { outcomes } label = 'Workflow outcomes' />
141- </ ChartFrame >
142160 </ div >
143161 </ div >
144162 )
0 commit comments