Skip to content
This repository was archived by the owner on Jan 5, 2025. It is now read-only.

Commit 7f361c3

Browse files
authored
Merge pull request #586 from openchatai/ui/analytics-2
Real Analytics
2 parents 4f67ca5 + df56977 commit 7f361c3

File tree

11 files changed

+282
-113
lines changed

11 files changed

+282
-113
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use client';
2+
3+
import { SimpleCard } from "@/components/domain/simple-card";
4+
import { getMostCalledActions } from "@/data/analytics";
5+
import { EChart } from "@kbox-labs/react-echarts";
6+
import useSWR from "swr";
7+
8+
type Props = {
9+
copilot_id: string
10+
}
11+
12+
export function AnalyticsActions({ copilot_id }: Props) {
13+
const {
14+
data: mostCalledActions,
15+
} = useSWR([copilot_id, 'copilot-mostCalledActions'], () => getMostCalledActions(copilot_id));
16+
const noMostCalledActions = mostCalledActions?.length === 0 || !mostCalledActions;
17+
return (
18+
<SimpleCard title="Most Called Actions" className="relative overflow-hidden">
19+
{noMostCalledActions ? <div className="h-full flex-center">
20+
<p className="text-center text-xl">No actions called yet</p>
21+
</div> : <EChart
22+
renderer='svg'
23+
className='size-full'
24+
xAxis={{
25+
type: 'category',
26+
data: mostCalledActions?.map((action) => action.operation_id) ?? [],
27+
}}
28+
yAxis={{
29+
type: 'value',
30+
}}
31+
series={{
32+
data: mostCalledActions?.map((action) => (action.count)) ?? [],
33+
type: 'bar',
34+
}}
35+
/>}
36+
</SimpleCard>
37+
)
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"use client";
2+
import { SimpleCard } from "@/components/domain/simple-card";
3+
import useSWR from "swr";
4+
import { getAnalyticsData } from "@/data/analytics";
5+
import { Skeleton } from "@/components/ui/skeleton";
6+
import { Counter } from "@/components/ui/Counter";
7+
8+
export function SimpleAnalyticsCards({ copilot_id }: {
9+
copilot_id: string
10+
}) {
11+
const {
12+
data: analyticsData,
13+
isLoading
14+
} = useSWR([copilot_id, 'copilot-analytics'], () => getAnalyticsData(copilot_id))
15+
return (
16+
<div className="grid grid-cols-3 gap-4">
17+
<SimpleCard title="Number of api calls">
18+
<Skeleton isLoading={isLoading}>
19+
<h2 className="text-lg font-bold">
20+
<Counter value={analyticsData?.api_called_count} />
21+
</h2>
22+
</Skeleton>
23+
</SimpleCard>
24+
25+
<SimpleCard title="Knowledge Base Queries">
26+
<Skeleton isLoading={isLoading}>
27+
<h2 className="text-lg font-bold">
28+
<Counter value={analyticsData?.knowledgebase_called_count} />
29+
</h2>
30+
</Skeleton>
31+
</SimpleCard>
32+
33+
<SimpleCard title="Misc Queries">
34+
<Skeleton isLoading={isLoading}>
35+
<h2 className="text-lg font-bold">
36+
<Counter value={analyticsData?.other_count} />
37+
</h2>
38+
</Skeleton>
39+
</SimpleCard>
40+
41+
</div>
42+
);
43+
}

dashboard/app/(copilot)/copilot/[copilot_id]/analytics/page.tsx

+7-58
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ import { HeaderShell } from '@/components/domain/HeaderShell'
22
import { SimpleCard } from '@/components/domain/simple-card'
33
import { Stack } from '@/components/ui/Stack'
44
import React from 'react'
5-
import { EChart } from '@kbox-labs/react-echarts'
5+
import { SimpleAnalyticsCards } from './AnalyticsCards';
6+
import { AnalyticsActions } from './AnalyticsActions'
67

78
type Props = {
89
params: {
910
copilot_id: string
1011
}
1112
}
12-
export default async function AnalyticsPage(props: Props) {
13+
14+
export default function AnalyticsPage(props: Props) {
15+
1316
return (
1417
<Stack direction='column' fluid className='h-full'>
1518
<HeaderShell>
@@ -18,63 +21,9 @@ export default async function AnalyticsPage(props: Props) {
1821
</h1>
1922
</HeaderShell>
2023
<main className='flex-1 overflow-auto p-4 w-full flex flex-col gap-4'>
21-
<div className='grid grid-cols-3 gap-4'>
22-
<SimpleCard title="Knowledgebase Calls" description="number of knowledgebase calls">
23-
20
24-
</SimpleCard>
25-
<SimpleCard title="Api Calls" description="number of api calls">
26-
20
27-
</SimpleCard>
28-
</div>
24+
<SimpleAnalyticsCards copilot_id={props.params.copilot_id} />
2925
<div className='flex-1 w-full rounded-md grid grid-cols-2 overflow-hidden gap-5'>
30-
<SimpleCard title="Messages Per session">
31-
<EChart
32-
renderer='svg'
33-
className='size-full'
34-
xAxis={{
35-
type: "value",
36-
}}
37-
yAxis={{
38-
type: "value",
39-
}}
40-
series={[{
41-
type: "scatter",
42-
symbolSize: 20,
43-
data: [
44-
[10.0, 8.04],
45-
[8.0, 6.95],
46-
[13.0, 7.58],
47-
[9.0, 8.81],
48-
[11.0, 8.33],
49-
[14.0, 9.96],
50-
[6.0, 7.24],
51-
[4.0, 4.26],
52-
[12.0, 10.84],
53-
[7.0, 4.82],
54-
[5.0, 5.68],
55-
],
56-
}]}
57-
/>
58-
</SimpleCard>
59-
<SimpleCard title="Action Calls">
60-
<EChart
61-
renderer='svg'
62-
className='size-full'
63-
xAxis={{
64-
type: 'category'
65-
}}
66-
yAxis={{
67-
type: 'value',
68-
boundaryGap: [0, '30%']
69-
}}
70-
series={[
71-
{
72-
type: "line",
73-
data: [120, 200, 150, 80, 70, 110, 130],
74-
}
75-
]}
76-
/>
77-
</SimpleCard>
26+
<AnalyticsActions copilot_id={props.params.copilot_id} />
7827
</div>
7928
</main>
8029
</Stack>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Stack } from "@/components/ui/Stack";
2+
import { CopilotType } from "@/data/copilot";
3+
import { Link } from "@/lib/router-events";
4+
import { motion } from "framer-motion";
5+
import { GalleryHorizontalEnd } from "lucide-react";
6+
import { format } from "timeago.js";
7+
8+
const IsoMorphicAnimatedLink = (animated:boolean) => animated ? motion(Link) : Link;
9+
10+
export function CopilotCard(
11+
{
12+
copilot,
13+
index,
14+
animated = true
15+
}:{
16+
copilot: CopilotType,
17+
index: number
18+
animated?: boolean
19+
}
20+
) {
21+
const copilotUrl = `/copilot/${copilot.id}`;
22+
const AnimatedLink = IsoMorphicAnimatedLink(animated);
23+
return (
24+
<AnimatedLink
25+
key={copilot.id}
26+
href={copilotUrl}
27+
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
28+
initial={{
29+
opacity: 0, y: 50,
30+
filter: "blur(10px)"
31+
}}
32+
exit={{
33+
opacity: 0, y: 50,
34+
filter: "blur(10px)"
35+
}}
36+
transition={{ duration: 0.2, delay: 0.1 * index }}
37+
className="group col-span-full lg:col-span-6 xl:col-span-3 copilot"
38+
>
39+
<div
40+
style={{
41+
transitionDelay: `max(0.1s, ${0.1 * index}ms)`,
42+
}}
43+
className="group relative flex h-56 items-center justify-center rounded-lg border-2 transition-colors box">
44+
<div className="flex-center size-20 shadow-lg group-hover:scale-95 transition-transform rounded-lg bg-primary text-gray-100">
45+
<GalleryHorizontalEnd className="size-12" />
46+
</div>
47+
</div>
48+
<Stack className="mt-1.5 ps-1 justify-between" gap={10} fluid>
49+
<h2 className="flex-1 text-sm font-semibold line-clamp-1" title={copilot.name}>
50+
{copilot.name}
51+
</h2>
52+
<p className="text-xs text-black">
53+
Created{" "}
54+
<span className="font-semibold">{format(copilot.created_at)}</span>
55+
</p>
56+
</Stack>
57+
</AnimatedLink>
58+
)
59+
}

dashboard/app/(main)/_parts/CopilotsContainer.tsx

+7-40
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use client";
22
import React from "react";
3-
import { GalleryHorizontalEnd } from "lucide-react";
43
import { Link } from "@/lib/router-events";
54
import useSwr from "swr";
65
import { CopilotType, listCopilots } from "@/data/copilot";
@@ -10,15 +9,14 @@ import { EmptyBlock } from "@/components/domain/EmptyBlock";
109
import { filterAtom } from "./Search";
1110
import { useAtomValue } from "jotai";
1211
import { Button } from "@/components/ui/button";
13-
import { format } from "timeago.js";
14-
import { motion, AnimatePresence } from 'framer-motion';
15-
import { Stack } from "@/components/ui/Stack";
12+
import { AnimatePresence } from "framer-motion";
13+
import { CopilotCard } from "./CopilotCard";
14+
1615
function orderByCreatedAt(copilots: CopilotType[]) {
1716
return copilots.sort((a, b) => {
1817
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
1918
});
2019
}
21-
const AnimatedLink = motion(Link);
2220

2321
export function CopilotsContainer() {
2422
const { data: copilots, isLoading } = useSwr("copilotsList", listCopilots);
@@ -67,44 +65,13 @@ export function CopilotsContainer() {
6765
) : (
6866
<div className="grid gap-4 py-4 grid-cols-12 copilot__container">
6967
{orderByCreatedAt($copilots).map((copilot, index) => {
70-
const copilotUrl = "/copilot/" + copilot.id;
7168
return (
7269
<AnimatePresence
7370
key={copilot.id}>
74-
<AnimatedLink
75-
key={copilot.id}
76-
href={copilotUrl}
77-
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
78-
initial={{
79-
opacity: 0, y: 50,
80-
filter: "blur(10px)"
81-
}}
82-
exit={{
83-
opacity: 0, y: 50,
84-
filter: "blur(10px)"
85-
}}
86-
transition={{ duration: 0.2, delay: 0.1 * index }}
87-
className="group col-span-full lg:col-span-6 xl:col-span-3 copilot"
88-
>
89-
<div
90-
style={{
91-
transitionDelay: `max(0.1s, ${0.1 * index}ms)`,
92-
}}
93-
className="group relative flex h-56 items-center justify-center rounded-lg border-2 transition-colors box">
94-
<div className="flex-center size-20 shadow-lg group-hover:scale-95 transition-transform rounded-lg bg-primary text-gray-100">
95-
<GalleryHorizontalEnd className="size-12" />
96-
</div>
97-
</div>
98-
<Stack className="mt-1.5 ps-1 justify-between" gap={10} fluid>
99-
<h2 className="flex-1 text-sm font-semibold line-clamp-1" title={copilot.name}>
100-
{copilot.name}
101-
</h2>
102-
<p className="text-xs text-black">
103-
Created{" "}
104-
<span className="font-semibold">{format(copilot.created_at)}</span>
105-
</p>
106-
</Stack>
107-
</AnimatedLink>
71+
<CopilotCard
72+
copilot={copilot}
73+
index={index}
74+
/>
10875
</AnimatePresence>
10976
);
11077
})}

0 commit comments

Comments
 (0)