Files
myeasycms-v2/apps/web/app/(dashboard)/home/[account]/_components/dashboard-demo.tsx
giancarlo cad729670f Update dependencies and import dynamic method
This commit updates multiple dependencies in the pnpm-lock.yaml file and imports the dynamic method from 'next/dynamic' in the site-header-account-section.tsx file. These updates are part of routine maintenance and improvement of the codebase. The import allows for dynamic imports, which can enhance performance.
2024-04-16 20:56:18 +08:00

388 lines
10 KiB
TypeScript

'use client';
import { useMemo } from 'react';
import { ArrowDown, ArrowUp, Menu } from 'lucide-react';
import { Line, LineChart, ResponsiveContainer, XAxis } from 'recharts';
import { Badge } from '@kit/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@kit/ui/table';
export default function DashboardDemo() {
const mrr = useMemo(() => generateDemoData(), []);
const visitors = useMemo(() => generateDemoData(), []);
const returningVisitors = useMemo(() => generateDemoData(), []);
const churn = useMemo(() => generateDemoData(), []);
const netRevenue = useMemo(() => generateDemoData(), []);
const fees = useMemo(() => generateDemoData(), []);
const newCustomers = useMemo(() => generateDemoData(), []);
const tickets = useMemo(() => generateDemoData(), []);
const activeUsers = useMemo(() => generateDemoData(), []);
return (
<div className={'flex flex-col space-y-6 pb-36'}>
<div
className={
'grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3' +
' 2xl:grid-cols-4'
}
>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>Monthly Recurring Revenue</span>
<Trend trend={'up'}>20%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'flex items-center justify-between'}>
<Figure>{`$${mrr[1]}`}</Figure>
</div>
<Chart data={mrr[0]} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>Revenue</span>
<Trend trend={'up'}>12%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'mb-4 flex items-center justify-between'}>
<Figure>{`$${netRevenue[1]}`}</Figure>
</div>
<Chart data={netRevenue[0]} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>Fees</span>
<Trend trend={'up'}>9%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'flex items-center justify-between'}>
<Figure>{`$${fees[1]}`}</Figure>
</div>
<Chart data={fees[0]} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>New Customers</span>
<Trend trend={'down'}>-25%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'flex items-center justify-between'}>
<Figure>{`${newCustomers[1]}`}</Figure>
</div>
<Chart data={newCustomers[0]} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>Visitors</span>
<Trend trend={'down'}>-4.3%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'flex items-center justify-between'}>
<Figure>{visitors[1]}</Figure>
</div>
<Chart data={visitors[0]} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>Returning Visitors</span>
<Trend trend={'stale'}>10%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'flex items-center justify-between'}>
<Figure>{returningVisitors[1]}</Figure>
</div>
<Chart data={returningVisitors[0]} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>Churn</span>
<Trend trend={'up'}>-10%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'flex items-center justify-between'}>
<Figure>{churn[1]}%</Figure>
</div>
<Chart data={churn[0]} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>Support Tickets</span>
<Trend trend={'up'}>-30%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'flex items-center justify-between'}>
<Figure>{tickets[1]}</Figure>
</div>
<Chart data={tickets[0]} />
</CardContent>
</Card>
</div>
<div>
<Card>
<CardHeader>
<CardTitle className={'flex items-center justify-between'}>
<span>Active Users</span>
<Trend trend={'up'}>10%</Trend>
</CardTitle>
</CardHeader>
<CardContent>
<div className={'flex items-center justify-between'}>
<Figure>{activeUsers[1]}</Figure>
</div>
<Chart data={activeUsers[0]} />
</CardContent>
</Card>
</div>
<div>
<Card>
<CardHeader>
<CardTitle>Customers</CardTitle>
</CardHeader>
<CardContent>
<CustomersTable />
</CardContent>
</Card>
</div>
</div>
);
}
function generateDemoData() {
const today = new Date();
const formatter = new Intl.DateTimeFormat('en-us', {
month: 'long',
year: '2-digit',
});
const data: { value: string; name: string }[] = [];
for (let n = 8; n > 0; n -= 1) {
const date = new Date(today.getFullYear(), today.getMonth() - n, 1);
data.push({
name: formatter.format(date),
value: (Math.random() * 10).toFixed(1),
});
}
const lastValue = data[data.length - 1]?.value;
return [data, lastValue] as [typeof data, string];
}
function Chart(
props: React.PropsWithChildren<{ data: { value: string; name: string }[] }>,
) {
return (
<div
className={
'h-36 py-2 duration-200 animate-in fade-in slide-in-from-left-4 slide-in-from-top-4'
}
>
<ResponsiveContainer width={'100%'} height={'100%'}>
<LineChart
width={400}
height={100}
data={props.data}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 20,
}}
>
<Line
className={'text-primary'}
type="monotone"
dataKey="value"
stroke="currentColor"
strokeWidth={2}
dot={false}
/>
<XAxis
style={{ fontSize: 9 }}
axisLine={false}
tickSize={0}
dataKey="name"
height={15}
dy={10}
/>
</LineChart>
</ResponsiveContainer>
</div>
);
}
function CustomersTable() {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Customer</TableHead>
<TableHead>Plan</TableHead>
<TableHead>MRR</TableHead>
<TableHead>Logins</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Pippin Oddo</TableCell>
<TableCell>Pro</TableCell>
<TableCell>$100.2</TableCell>
<TableCell>920</TableCell>
<TableCell>
<BadgeWithTrend trend={'up'}>Healthy</BadgeWithTrend>
</TableCell>
</TableRow>
<TableRow>
<TableCell>Väinö Pánfilo</TableCell>
<TableCell>Basic</TableCell>
<TableCell>$40.6</TableCell>
<TableCell>300</TableCell>
<TableCell>
<BadgeWithTrend trend={'stale'}>Possible Churn</BadgeWithTrend>
</TableCell>
</TableRow>
<TableRow>
<TableCell>Giorgos Quinten</TableCell>
<TableCell>Pro</TableCell>
<TableCell>$2004.3</TableCell>
<TableCell>1000</TableCell>
<TableCell>
<BadgeWithTrend trend={'up'}>Healthy</BadgeWithTrend>
</TableCell>
</TableRow>
<TableRow>
<TableCell>Adhelm Otis</TableCell>
<TableCell>Basic</TableCell>
<TableCell>$0</TableCell>
<TableCell>10</TableCell>
<TableCell>
<BadgeWithTrend trend={'down'}>Churned</BadgeWithTrend>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
function BadgeWithTrend(props: React.PropsWithChildren<{ trend: string }>) {
const className = useMemo(() => {
switch (props.trend) {
case 'up':
return 'text-green-500';
case 'down':
return 'text-destructive';
case 'stale':
return 'text-orange-500';
}
}, [props.trend]);
return (
<Badge variant={'outline'}>
<span className={className}>{props.children}</span>
</Badge>
);
}
function Figure(props: React.PropsWithChildren) {
return (
<div className={'font-heading text-4xl font-extrabold'}>
{props.children}
</div>
);
}
function Trend(
props: React.PropsWithChildren<{
trend: 'up' | 'down' | 'stale';
}>,
) {
const Icon = useMemo(() => {
switch (props.trend) {
case 'up':
return <ArrowUp className={'h-4 text-green-500'} />;
case 'down':
return <ArrowDown className={'h-4 text-destructive'} />;
case 'stale':
return <Menu className={'h-4 text-orange-500'} />;
}
}, [props.trend]);
return (
<div>
<BadgeWithTrend trend={props.trend}>
<span className={'flex items-center space-x-0.5'}>
{Icon}
<span>{props.children}</span>
</span>
</BadgeWithTrend>
</div>
);
}