Skip to content

Commit 7526327

Browse files
committed
feature: segment the user journey into search,chat,and reccomendations
1 parent 554c5a4 commit 7526327

38 files changed

Lines changed: 908 additions & 211 deletions

clients/trieve-shopify-extension/app/components/analytics/AnalyticsChart.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ import Crosshair from "chartjs-plugin-crosshair";
88

99
Chart.register(Crosshair);
1010

11-
interface AnalyticsChartProps<T> {
11+
interface AnalyticsChartProps<T extends Record<string, any>> {
1212
data: T[] | null | undefined;
1313
granularity: Granularity;
14-
date_range?: SearchAnalyticsFilter["date_range"];
14+
dateRange?: SearchAnalyticsFilter["date_range"];
1515
yAxis: keyof T;
1616
xAxis: keyof T;
1717
label: string;
1818
wholeUnits?: boolean;
1919
dataType?: "number" | "percentage" | "currency" | "time";
2020
}
2121

22-
export const AnalyticsChart = <T,>(props: AnalyticsChartProps<T>) => {
22+
export const AnalyticsChart = <T extends Record<string, any>>(props: AnalyticsChartProps<T>) => {
2323
const canvasRef = useRef<HTMLCanvasElement>(null);
2424
const chartInstanceRef = useRef<Chart | null>(null);
2525

@@ -205,9 +205,9 @@ export const AnalyticsChart = <T,>(props: AnalyticsChartProps<T>) => {
205205
chartInstance.options.scales["x"].time.round = undefined;
206206
}
207207

208-
const info = fillDate({
208+
const info = fillDate<T>({
209209
data,
210-
date_range: props.date_range,
210+
dateRange: props.dateRange,
211211
granularity: props.granularity,
212212
dataKey: props.yAxis,
213213
timestampKey: props.xAxis,
@@ -234,7 +234,7 @@ export const AnalyticsChart = <T,>(props: AnalyticsChartProps<T>) => {
234234
}, [
235235
props.data,
236236
props.granularity,
237-
props.date_range,
237+
props.dateRange,
238238
props.yAxis,
239239
props.xAxis,
240240
props.label,
Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
import { KnownEventNames } from "app/utils/formatting";
22

33
export const chatEvents: KnownEventNames[] = [
4-
"trieve-modal_load",
5-
"trieve-modal_click",
6-
"start_conversation",
7-
"send_message",
4+
"component_load",
5+
"component_click",
6+
"conversation_started",
87
"site-add_to_cart",
98
"site-checkout",
109
];
1110

1211
export const searchEvents: KnownEventNames[] = [
13-
"trieve-modal_load",
14-
"trieve-modal_click",
12+
"component_load",
13+
"component_click",
14+
"searched",
1515
"site-add_to_cart",
1616
"site-checkout",
1717
];
18+
19+
export const recommendationEvents: KnownEventNames[] = [
20+
"component_load",
21+
"component_click",
22+
"recommendation_created",
23+
"site-add_to_cart",
24+
"site-checkout",
25+
];

clients/trieve-shopify-extension/app/components/analytics/GraphComponent.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface GraphComponentProps<T extends Record<string, any>> {
1919
xAxis: keyof T;
2020
yAxis: keyof T;
2121
label: string;
22-
date_range: ComponentAnalyticsFilter["date_range"];
22+
dateRange: ComponentAnalyticsFilter["date_range"];
2323
tooltipContent: string;
2424
dataType?: "number" | "percentage" | "currency" | "time";
2525
}
@@ -32,7 +32,7 @@ export const GraphComponent = <T extends Record<string, any>>({
3232
xAxis,
3333
yAxis,
3434
label,
35-
date_range,
35+
dateRange,
3636
tooltipContent,
3737
dataType = "number",
3838
}: GraphComponentProps<T>) => {
@@ -80,7 +80,7 @@ export const GraphComponent = <T extends Record<string, any>>({
8080
yAxis={yAxis}
8181
granularity={granularity}
8282
label={label}
83-
date_range={date_range}
83+
dateRange={dateRange}
8484
/>
8585
)}
8686
</Box>

clients/trieve-shopify-extension/app/components/analytics/chat/ChatAverageRating.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const ChatAverageRating = ({
2323
topLevelMetric={data?.avg_chat_rating}
2424
graphData={data?.points}
2525
granularity={granularity}
26-
date_range={filters.date_range}
26+
dateRange={filters.date_range}
2727
xAxis={"time_stamp"}
2828
yAxis={"point"}
2929
label="Average Chat Rating"

clients/trieve-shopify-extension/app/components/analytics/chat/ChatConversionRate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const ChatConversionRate = ({
2323
topLevelMetric={data?.conversion_rate}
2424
graphData={data?.points}
2525
granularity={granularity}
26-
date_range={filters.date_range}
26+
dateRange={filters.date_range}
2727
dataType="percentage"
2828
xAxis={"time_stamp"}
2929
yAxis={"point"}

clients/trieve-shopify-extension/app/components/analytics/chat/ChatRevenue.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ export const ChatRevenue = ({
2020
return (
2121
<GraphComponent
2222
loading={isLoading}
23-
topLevelMetric={data?.avg_chat_revenue}
23+
topLevelMetric={data?.avg_revenue}
2424
graphData={data?.points}
2525
granularity={granularity}
26-
date_range={filters.date_range}
26+
dateRange={filters.date_range}
2727
dataType="currency"
2828
xAxis={"time_stamp"}
2929
yAxis={"point"}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import {
2+
Box,
3+
Card,
4+
SkeletonBodyText,
5+
Tooltip,
6+
Text,
7+
ColumnContentType,
8+
Select,
9+
} from "@shopify/polaris";
10+
import { useQuery } from "@tanstack/react-query";
11+
import { useTrieve } from "app/context/trieveContext";
12+
import { formatEventName, KnownEventNames } from "app/utils/formatting";
13+
import { Chart, ChartConfiguration } from "chart.js";
14+
import ChartDataLabels from "chartjs-plugin-datalabels";
15+
import { useEffect, useMemo, useRef } from "react";
16+
import { ComponentAnalyticsFilter, EventNameAndCounts } from "trieve-ts-sdk";
17+
import { BasicTableComponent } from "../BasicTableComponent";
18+
import { chatEvents } from "../EventPathSelector";
19+
import { chatEventFunnelQuery } from "app/queries/analytics/chat";
20+
21+
export const ChatUserJourneyFunnel = ({
22+
filters,
23+
}: {
24+
filters: ComponentAnalyticsFilter;
25+
}) => {
26+
const { trieve } = useTrieve();
27+
const events = chatEvents;
28+
29+
const { data, status } = useQuery(chatEventFunnelQuery(trieve, filters));
30+
31+
const canvasRef = useRef<HTMLCanvasElement>(null);
32+
const chartInstanceRef = useRef<Chart | null>(null);
33+
34+
35+
const filteredData = useMemo(() => {
36+
if (!data) return [];
37+
const selected = events.map((event) => {
38+
return (
39+
data.event_names.find((e: EventNameAndCounts) => e.event_name === event) || {
40+
event_name: event,
41+
event_count: 0,
42+
}
43+
);
44+
});
45+
46+
// Return in the same order of selectedEvents
47+
return selected.sort((a, b) => {
48+
const indexA = events.indexOf(a.event_name as KnownEventNames);
49+
const indexB = events.indexOf(b.event_name as KnownEventNames);
50+
if (indexA === -1 || indexB === -1) {
51+
return 0;
52+
} else {
53+
return indexA - indexB;
54+
}
55+
});
56+
}, [data, events]);
57+
58+
useEffect(() => {
59+
const canvas = canvasRef.current;
60+
61+
if (!canvas || !filteredData) return;
62+
63+
if (!chartInstanceRef.current) {
64+
chartInstanceRef.current = new Chart(canvas, {
65+
type: "funnel",
66+
data: {
67+
labels: filteredData.map((t) => t.event_name),
68+
datasets: [
69+
{
70+
data: filteredData.map((t) => t.event_count),
71+
},
72+
],
73+
},
74+
options: {
75+
animation: false,
76+
indexAxis: "x",
77+
responsive: true,
78+
elements: {
79+
trapezoid: {
80+
backgroundColor: "rgba(128, 0, 128, 0.26)",
81+
},
82+
},
83+
aspectRatio: 1,
84+
interaction: {
85+
mode: "nearest",
86+
axis: "x",
87+
intersect: false,
88+
},
89+
plugins: {
90+
legend: { display: false },
91+
datalabels: {
92+
formatter(_, context) {
93+
const thisOne = filteredData[context.dataIndex];
94+
return formatEventName(thisOne.event_name);
95+
},
96+
color(context) {
97+
const thisOne = filteredData[context.dataIndex];
98+
if (thisOne.event_count === 0) {
99+
return "rgba(128, 0, 128, 0.26)";
100+
}
101+
return "rgba(58, 0, 58, 0.76)";
102+
},
103+
font: {
104+
size: 12,
105+
},
106+
},
107+
tooltip: {
108+
callbacks: {
109+
title(tooltipItem) {
110+
const index = tooltipItem[0].dataIndex;
111+
return formatEventName(filteredData[index].event_name);
112+
},
113+
},
114+
backgroundColor: "rgba(128, 0, 128, 0.9)",
115+
titleColor: "white",
116+
bodyColor: "white",
117+
padding: 4,
118+
displayColors: false,
119+
position: "nearest",
120+
titleFont: { size: 11 },
121+
bodyFont: { size: 11 },
122+
},
123+
// @ts-expect-error library types not updated
124+
crosshair: {
125+
line: {
126+
color: "rgba(128, 0, 128, 0)",
127+
width: 1,
128+
dashPattern: [6, 6],
129+
},
130+
sync: {
131+
enabled: true,
132+
group: 1,
133+
},
134+
snap: {
135+
enabled: true,
136+
},
137+
zoom: {
138+
enabled: false,
139+
},
140+
},
141+
},
142+
},
143+
plugins: [ChartDataLabels],
144+
} satisfies ChartConfiguration<"funnel">);
145+
}
146+
147+
const chartInstance = chartInstanceRef.current;
148+
149+
// Update the chart data
150+
chartInstance.data.labels = filteredData.map((t) => t.event_name);
151+
chartInstance.data.datasets[0].data = filteredData.map(
152+
(t) => t.event_count,
153+
);
154+
chartInstance.update();
155+
156+
// Cleanup function
157+
return () => {
158+
if (chartInstanceRef.current) {
159+
chartInstanceRef.current.destroy();
160+
chartInstanceRef.current = null;
161+
}
162+
};
163+
}, [filteredData]);
164+
165+
const tableData = filteredData
166+
? filteredData.map((item) => [
167+
formatEventName(item.event_name),
168+
item.event_count.toString(),
169+
])
170+
: [];
171+
const tableHeadings = ["Event Name", "Unique Users"];
172+
const tableContentTypes: ColumnContentType[] = ["text", "numeric"];
173+
const hasNext = false;
174+
175+
return (
176+
<Card>
177+
<div className="pb-2 w-full flex justify-between">
178+
<div>
179+
<Tooltip
180+
content={"Stages of the purchase journey by unique users"}
181+
hasUnderline
182+
>
183+
<Text as="span" variant="bodyLg" fontWeight="bold">
184+
User Journey
185+
</Text>
186+
</Tooltip>
187+
</div>
188+
</div>
189+
{events.length > 0 ? (
190+
<>
191+
<Box paddingBlockStart="800" minHeight="150px">
192+
{status === "pending" ? (
193+
<div className="pl-2">
194+
<SkeletonBodyText lines={10} />
195+
</div>
196+
) : (
197+
<canvas ref={canvasRef} className="max-h-[200px] w-full" />
198+
)}
199+
</Box>
200+
<div className="py-2"></div>
201+
<BasicTableComponent
202+
noCard
203+
hidePagination
204+
data={tableData}
205+
page={1}
206+
setPage={() => { }}
207+
tableContentTypes={tableContentTypes}
208+
tableHeadings={tableHeadings}
209+
hasNext={hasNext}
210+
/>
211+
</>
212+
) : null}
213+
</Card>
214+
);
215+
};

clients/trieve-shopify-extension/app/components/analytics/chat/MessagesPerUser.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const MessagesPerUser = ({
2323
topLevelMetric={data?.avg_messages_per_user}
2424
graphData={data?.points}
2525
granularity={granularity}
26-
date_range={filters.date_range}
26+
dateRange={filters.date_range}
2727
xAxis={"time_stamp"}
2828
yAxis={"point"}
2929
label="Messages Per User"

clients/trieve-shopify-extension/app/components/analytics/chat/TopicCTRRate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const TopicCTRRate = ({
2323
topLevelMetric={data?.total_ctr}
2424
graphData={data?.points}
2525
granularity={granularity}
26-
date_range={filters.date_range}
26+
dateRange={filters.date_range}
2727
dataType="percentage"
2828
xAxis={"time_stamp"}
2929
yAxis={"point"}

clients/trieve-shopify-extension/app/components/analytics/chat/TopicsGraph.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const TopicsUsage = ({
2323
topLevelMetric={data?.total_topics}
2424
graphData={data?.points}
2525
granularity={granularity}
26-
date_range={filters.date_range}
26+
dateRange={filters.date_range}
2727
xAxis={"time_stamp"}
2828
yAxis={"point"}
2929
label="Chat Sessions Created"

0 commit comments

Comments
 (0)