Files
stats/apps/public/src/app/(home)/_sections/collaboration-chart.tsx
Carl-Gerhard Lindesvärd ac4429d6d9 feat: new public website
2025-12-02 09:22:36 +01:00

172 lines
5.6 KiB
TypeScript

'use client';
import { FeatureCardContainer } from '@/components/feature-card';
import { MoreVerticalIcon } from 'lucide-react';
import { useMemo, useState } from 'react';
import {
Bar,
CartesianGrid,
Cell,
ComposedChart,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
// Sample data for the last 7 days
const data = [
{ day: 'Mon', visitors: 1200, revenue: 1250 },
{ day: 'Tue', visitors: 1450, revenue: 1890 },
{ day: 'Wed', visitors: 1320, revenue: 1520 },
{ day: 'Thu', visitors: 1580, revenue: 2100 },
{ day: 'Fri', visitors: 1420, revenue: 1750 },
{ day: 'Sat', visitors: 1180, revenue: 1100 },
{ day: 'Sun', visitors: 1250, revenue: 1380 },
];
// Custom tooltip component
const CustomTooltip = ({ active, payload, label }: any) => {
if (active && payload && payload.length) {
const visitors =
payload.find((p: any) => p.dataKey === 'visitors')?.value || 0;
const revenue =
payload.find((p: any) => p.dataKey === 'revenue')?.value || 0;
return (
<div className="bg-card border border-border rounded-lg p-3 shadow-lg min-w-[200px]">
<div className="text-sm font-semibold mb-2">{label}</div>
<div className="text-sm text-muted-foreground space-y-1 flex-1">
<div className="row gap-2 items-center flex-1">
<div className="h-6 bg-foreground w-1 rounded-full" />
<div className="font-medium row items-center gap-2 justify-between flex-1">
<span>Visitors</span> <span>{visitors.toLocaleString()}</span>
</div>
</div>
<div className="row gap-2 items-center flex-1">
<div className="h-6 bg-emerald-500 w-1 rounded-full" />
<div className="font-medium row items-center gap-2 justify-between flex-1">
<span>Revenue</span> <span>${revenue.toLocaleString()}</span>
</div>
</div>
</div>
</div>
);
}
return null;
};
export function CollaborationChart() {
const [activeIndex, setActiveIndex] = useState<number | null>(1); // Default to Tue (index 1)
// Calculate metrics from active point or default
const activeData = useMemo(() => {
return activeIndex !== null ? data[activeIndex] : data[1];
}, [activeIndex]);
const totalVisitors = activeData.visitors;
const totalRevenue = activeData.revenue;
return (
<FeatureCardContainer className="col gap-4 h-full">
{/* Header */}
<div className="row items-center justify-between">
<div>
<h3 className="font-semibold">Product page views</h3>
<p className="text-sm text-muted-foreground">Last 7 days</p>
</div>
<button
type="button"
className="text-muted-foreground hover:text-foreground transition-colors"
>
<MoreVerticalIcon className="size-4" />
</button>
</div>
{/* Chart */}
<div className="flex-1 min-h-[200px]">
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={data}
margin={{ top: 5, right: 5, left: 5, bottom: 5 }}
onMouseMove={(state) => {
if (state?.activeTooltipIndex !== undefined) {
setActiveIndex(state.activeTooltipIndex);
}
}}
onMouseLeave={() => setActiveIndex(null)}
>
<CartesianGrid
strokeDasharray="3 3"
stroke="hsl(var(--border))"
opacity={0.3}
/>
<XAxis
dataKey="day"
axisLine={false}
tickLine={false}
tick={{ fill: 'var(--muted-foreground)', fontSize: 10 }}
/>
<YAxis
yAxisId="left"
axisLine={false}
tickLine={false}
tick={{ fill: 'var(--muted-foreground)', fontSize: 10 }}
hide
/>
<YAxis
yAxisId="right"
orientation="right"
axisLine={false}
tickLine={false}
tick={{ fill: 'var(--muted-foreground)', fontSize: 10 }}
domain={[0, 2400]}
hide
/>
<Tooltip content={<CustomTooltip />} cursor={false} />
{/* Revenue bars */}
<Bar yAxisId="right" dataKey="revenue" radius={4}>
{data.map((entry, index) => (
<Cell
key={`cell-${entry.day}`}
className={
activeIndex === index
? 'fill-emerald-500' // Lighter green on hover
: 'fill-foreground/30' // Default green
}
style={{ transition: 'fill 0.2s ease' }}
/>
))}
</Bar>
<Line
yAxisId="left"
type="monotone"
dataKey="visitors"
strokeWidth={2}
stroke="var(--foreground)"
dot={false}
/>
</ComposedChart>
</ResponsiveContainer>
</div>
{/* Metrics */}
<div className="grid grid-cols-2 gap-4 center-center">
<div>
<div className="text-2xl font-semibold font-mono">
{totalVisitors.toLocaleString()}
</div>
<div className="text-xs text-muted-foreground">Visitors</div>
</div>
<div>
<div className="text-2xl font-semibold font-mono text-emerald-500">
${totalRevenue.toLocaleString()}
</div>
<div className="text-xs text-muted-foreground">Revenue</div>
</div>
</div>
</FeatureCardContainer>
);
}