1- import { Card , Box , Text } from "@shopify/polaris" ;
1+ import { Card , Box , Text , InlineGrid , BlockStack } from "@shopify/polaris" ;
22import { AnalyticsChart } from "./AnalyticsChart" ;
33import { useLoaderData } from "@remix-run/react" ;
44import { loader } from "app/routes/app._dashboard.experimentview.$experimentId" ;
55
6+ interface SingleEventChartData {
7+ categoryName : string ;
8+ noTrieveCount : number ;
9+ trieveCount : number ;
10+ }
11+
612const eventNameMap = {
713 "site-checkout_end" : "Checkout" ,
814 "site-add_to_cart" : "Add to Cart" ,
@@ -12,90 +18,140 @@ const eventNameMap = {
1218export const ExperimentAnalyticsChart = ( ) => {
1319 const { treatmentEventsResult, experiment } = useLoaderData < typeof loader > ( ) ;
1420
15- const chartData = treatmentEventsResult
16- . reduce (
17- ( acc , event ) => {
18- const existing = acc . find (
19- ( item ) => item . event_name === event . event_name ,
20- ) ;
21- const currentEventCount = Number ( event . event_count ) ;
21+ const individualChartsData = treatmentEventsResult . reduce (
22+ ( acc , event ) => {
23+ const eventNameKey =
24+ eventNameMap [ event . event_name as keyof typeof eventNameMap ] ;
25+ let chartObj = acc . find ( ( c ) => c . categoryName === eventNameKey ) ;
2226
23- if ( existing ) {
24- if ( event . treatment_name === experiment . control_name ) {
25- existing . control_count = currentEventCount ;
26- } else if ( event . treatment_name === experiment . t1_name ) {
27- existing . treatment_count = currentEventCount ;
28- }
29- } else {
30- acc . push ( {
31- event_name : event . event_name ,
32- control_count :
33- event . treatment_name === experiment . control_name
34- ? currentEventCount
35- : 0 ,
36- treatment_count :
37- event . treatment_name === experiment . t1_name
38- ? currentEventCount
39- : 0 ,
40- } ) ;
41- }
27+ if ( ! chartObj ) {
4228 return acc ;
29+ }
30+
31+ const currentEventCount = Number ( event . event_count ) ;
32+ if ( event . treatment_name === experiment . control_name ) {
33+ chartObj . noTrieveCount += currentEventCount ;
34+ } else if ( event . treatment_name === experiment . t1_name ) {
35+ chartObj . trieveCount += currentEventCount ;
36+ }
37+
38+ return acc ;
39+ } ,
40+ [
41+ {
42+ categoryName : "Clicked Product" ,
43+ noTrieveCount : 0 ,
44+ trieveCount : 0 ,
45+ } ,
46+ {
47+ categoryName : "Add to Cart" ,
48+ noTrieveCount : 0 ,
49+ trieveCount : 0 ,
50+ } ,
51+ {
52+ categoryName : "Checkout" ,
53+ noTrieveCount : 0 ,
54+ trieveCount : 0 ,
4355 } ,
44- [
45- {
46- event_name : "Click" ,
47- control_count : 0 ,
48- treatment_count : 0 ,
49- } ,
50- {
51- event_name : "site-add_to_cart" ,
52- control_count : 0 ,
53- treatment_count : 0 ,
54- } ,
55- {
56- event_name : "site-checkout_end" ,
57- control_count : 0 ,
58- treatment_count : 0 ,
59- } ,
60- ] ,
61- )
62- . map ( ( item ) => ( {
63- name : eventNameMap [ item . event_name as keyof typeof eventNameMap ] ,
64- NoTrieve : item . control_count ,
65- Trieve : item . treatment_count ,
66- } ) ) ;
56+ ] as SingleEventChartData [ ] ,
57+ ) ;
6758
68- const yAxes = [
59+ const yAxesConfig = [
6960 {
70- key : "NoTrieve " as const ,
71- label : "No Trieve" ,
61+ key : "noTrieveCount " as const ,
62+ label : "Without Trieve" ,
7263 color : "rgba(54, 162, 235, 0.8)" ,
73- } ,
64+ } , // Blue
7465 {
75- key : "Trieve " as const ,
76- label : "Trieve" ,
66+ key : "trieveCount " as const ,
67+ label : "With Trieve" ,
7768 color : "rgba(75, 192, 192, 0.8)" ,
78- } ,
69+ } , // Teal
7970 ] ;
8071
8172 return (
8273 < Card >
8374 < Box padding = "400" >
8475 < Text variant = "headingMd" as = "h2" >
85- Event Comparison
76+ Event Breakdown
8677 </ Text >
87- < Box minHeight = "300px" paddingBlockStart = "400" >
88- < AnalyticsChart
89- data = { chartData }
90- xAxis = "name"
91- yAxes = { yAxes }
92- chartType = "bar"
93- granularity = "day"
94- wholeUnits = { true }
95- yAxisLabel = "Event Count"
96- yAxisSuggestedMax = { 10 }
97- />
98- </ Box >
78+ { individualChartsData . length === 0 && (
79+ < Box paddingBlockStart = "400" >
80+ < Text as = "p" alignment = "center" tone = "subdued" >
81+ No event data available for this experiment.
82+ </ Text >
83+ </ Box >
84+ ) }
85+ < InlineGrid
86+ columns = { {
87+ xs : 1 ,
88+ sm : 1 ,
89+ md : 3 ,
90+ } }
91+ gap = "400"
92+ >
93+ { individualChartsData . map ( ( eventData ) => {
94+ const controlCount = eventData . noTrieveCount ;
95+ const treatmentCount = eventData . trieveCount ;
96+ const delta = treatmentCount - controlCount ;
97+
98+ let deltaDisplay = "" ;
99+ let percentageDisplay = "" ;
100+ let tone : "success" | "critical" | "subdued" = "subdued" ;
101+
102+ if ( controlCount === 0 && treatmentCount > 0 ) {
103+ deltaDisplay = `+ ${ treatmentCount } ` ;
104+ percentageDisplay = "(from 0)" ;
105+ tone = "success" ;
106+ } else if ( controlCount > 0 && treatmentCount === 0 ) {
107+ deltaDisplay = `-${ controlCount } ` ;
108+ percentageDisplay = "(-100%)" ;
109+ tone = "critical" ;
110+ } else if ( controlCount === 0 && treatmentCount === 0 ) {
111+ deltaDisplay = "+0" ;
112+ percentageDisplay = "(+0.0%)" ;
113+ tone = "subdued" ;
114+ } else if ( controlCount > 0 ) {
115+ const percentage = ( delta / controlCount ) * 100 ;
116+ deltaDisplay = `${ delta >= 0 ? "+" : "" } ${ delta } ` ;
117+ percentageDisplay = ` (${ percentage >= 0 ? "+" : "" } ${ percentage . toFixed ( 1 ) } %)` ;
118+ if ( delta > 0 ) tone = "success" ;
119+ else if ( delta < 0 ) tone = "critical" ;
120+ } else {
121+ // Fallback for any other scenarios, like controlCount being negative (not for counts)
122+ deltaDisplay = `${ delta >= 0 ? "+" : "" } ${ delta } ` ;
123+ if ( delta > 0 ) tone = "success" ;
124+ else if ( delta < 0 ) tone = "critical" ;
125+ }
126+ const fullDeltaText = `${ deltaDisplay } ${ percentageDisplay } ` . trim ( ) ;
127+
128+ return (
129+ < Box key = { eventData . categoryName } paddingBlockStart = "200" >
130+ < BlockStack gap = "100" align = "center" inlineAlign = "center" >
131+ < Text variant = "bodyLg" as = "p" alignment = "center" >
132+ { eventData . categoryName . charAt ( 0 ) . toUpperCase ( ) +
133+ eventData . categoryName . slice ( 1 ) }
134+ </ Text >
135+ < Text as = "p" variant = "bodyMd" tone = { tone } alignment = "center" >
136+ { fullDeltaText }
137+ </ Text >
138+ </ BlockStack >
139+ < Box >
140+ < AnalyticsChart
141+ data = { [ eventData ] }
142+ xAxis = "categoryName"
143+ yAxes = { yAxesConfig }
144+ chartType = "bar"
145+ granularity = "day"
146+ wholeUnits = { true }
147+ yAxisLabel = { `# of ${ eventData . categoryName } s` }
148+ yAxisSuggestedMax = { 10 }
149+ />
150+ </ Box >
151+ </ Box >
152+ ) ;
153+ } ) }
154+ </ InlineGrid >
99155 </ Box >
100156 </ Card >
101157 ) ;
0 commit comments