Line Charts
9 free modern line charts designed to present key metrics and insights. Each chart features unique layouts, data visualizations, and styling options. Perfect for dashboards, admin panels, and analytics pages.
Loading...
charts/line-charts/line-chart-1.tsx
'use client';
import React from 'react';
import { Badge } from '@/registry/default/ui/badge';
import { Button } from '@/registry/default/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/registry/default/ui/dropdown-menu';
import { ArrowDown, ArrowUp, Calendar, Download, Filter, MoreHorizontal, RefreshCw, Share2 } from 'lucide-react';
import { Area, CartesianGrid, ComposedChart, Line, ReferenceLine, XAxis, YAxis } from 'recharts';
// Sales data for B2B Software Company (6 months)
const salesData = [
{ month: 'Jan 24', goals: 250000, sales: 280000, salesArea: 280000 },
{ month: 'Feb 24', goals: 420000, sales: 350000, salesArea: 350000 },
{ month: 'Mar 24', goals: 380000, sales: 480000, salesArea: 480000 },
{ month: 'Apr 24', goals: 520000, sales: 390000, salesArea: 390000 },
{ month: 'May 24', goals: 300000, sales: 520000, salesArea: 520000 },
{ month: 'Jun 24', goals: 550000, sales: 465000, salesArea: 465000 },
];
// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
goals: {
label: 'Goals',
color: 'var(--color-pink-500)',
},
sales: {
label: 'Sales',
color: 'var(--color-teal-500)',
},
} satisfies ChartConfig;
// Custom Tooltip
interface TooltipProps {
active?: boolean;
payload?: Array<{
dataKey: string;
value: number;
color: string;
}>;
label?: string;
}
const ChartLabel = ({ label, color = chartConfig.sales.color }: { label: string; color: string }) => {
return (
<div className="flex items-center gap-1.5">
<div className="size-3.5 border-4 rounded-full bg-background" style={{ borderColor: color }}></div>
<span className="text-muted-foreground">{label}</span>
</div>
);
};
const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
if (active && payload && payload.length) {
// Filter out salesArea from tooltip
const filteredPayload = payload.filter((entry) => entry.dataKey !== 'salesArea');
return (
<div className="rounded-lg border bg-popover p-3 shadow-sm shadow-black/5 min-w-[180px]">
<div className="text-xs font-medium text-muted-foreground tracking-wide mb-2.5">{label}</div>
<div className="space-y-2">
{filteredPayload.map((entry, index) => {
const config = chartConfig[entry.dataKey as keyof typeof chartConfig];
return (
<div key={index} className="flex items-center gap-2 text-xs">
<ChartLabel label={config?.label + ':'} color={entry.color} />
<span className="font-semibold text-popover-foreground">${(entry.value / 1000000).toFixed(1)}M</span>
{config?.label == 'Goals' && (
<Badge
variant={
((entry.value - filteredPayload[0].value) / filteredPayload[0].value) * 100 > 0
? 'success'
: 'destructive'
}
appearance="light"
className="text-xs flex items-center gap-1"
>
{((entry.value - filteredPayload[0].value) / filteredPayload[0].value) * 100 > 0 ? (
<ArrowUp className="size-3" />
) : (
<ArrowDown className="size-3" />
)}
{Math.abs(((entry.value - filteredPayload[0].value) / filteredPayload[0].value) * 100).toFixed(0)}%
</Badge>
)}
</div>
);
})}
</div>
</div>
);
}
return null;
};
export default function LineChart1() {
return (
<div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
<Card className="w-full lg:max-w-4xl">
<CardHeader className="border-0 min-h-auto pt-6 pb-6">
<CardTitle className="text-base font-semibold">Sales Overview</CardTitle>
<CardToolbar>
<div className="flex items-center gap-4 text-sm">
<ChartLabel label="Sales" color={chartConfig.sales.color} />
<ChartLabel label="Goals" color={chartConfig.goals.color} />
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="dim" size="sm" mode="icon" className="-me-1.5">
<MoreHorizontal className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" side="bottom">
<DropdownMenuItem>
<Download className="size-4" />
Export Data
</DropdownMenuItem>
<DropdownMenuItem>
<Calendar className="size-4" />
Change Period
</DropdownMenuItem>
<DropdownMenuItem>
<Filter className="size-4" />
Filter Data
</DropdownMenuItem>
<DropdownMenuItem>
<RefreshCw className="size-4" />
Refresh
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Share2 className="size-4" />
Share Report
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</CardToolbar>
</CardHeader>
<CardContent className="px-2.5 flex flex-col items-end">
<ChartContainer
config={chartConfig}
className="h-[350px] w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
>
<ComposedChart
data={salesData}
margin={{
top: 5,
right: 15,
left: 5,
bottom: 5,
}}
>
<defs>
<linearGradient id="salesGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={chartConfig.sales.color} stopOpacity={0.3} />
<stop offset="100%" stopColor={chartConfig.sales.color} stopOpacity={0.05} />
</linearGradient>
</defs>
<CartesianGrid
strokeDasharray="4 4"
stroke="var(--input)"
strokeOpacity={1}
horizontal={true}
vertical={false}
/>
<XAxis
dataKey="month"
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, className: 'text-muted-foreground' }}
dy={5}
tickMargin={12}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, className: 'text-muted-foreground' }}
tickFormatter={(value) => `$${(value / 1000000).toFixed(1)}M`}
domain={['dataMin - 50000', 'dataMax + 50000']}
tickMargin={12}
/>
{/* Current month reference line */}
<ReferenceLine x="Mar 24" stroke={chartConfig.sales.color} strokeWidth={1} />
{/* Tooltip */}
<ChartTooltip
content={<CustomTooltip />}
cursor={{
stroke: 'var(--input)',
strokeWidth: 1,
strokeDasharray: 'none',
}}
/>
{/* Sales area with gradient background */}
<Area
type="linear"
dataKey="salesArea"
stroke="transparent"
fill="url(#salesGradient)"
strokeWidth={0}
dot={false}
/>
{/* Sales line with dots */}
<Line
type="linear"
dataKey="sales"
stroke={chartConfig.sales.color}
strokeWidth={2}
dot={{
fill: 'var(--background)',
strokeWidth: 2,
r: 6,
stroke: chartConfig.sales.color,
}}
/>
{/* Goals line (dashed) */}
<Line
type="linear"
dataKey="goals"
stroke={chartConfig.goals.color}
strokeWidth={2}
strokeDasharray="4 4"
dot={{
fill: 'var(--background)',
strokeWidth: 2,
r: 6,
stroke: chartConfig.goals.color,
strokeDasharray: '0',
}}
/>
</ComposedChart>
</ChartContainer>
</CardContent>
</Card>
</div>
);
}
Loading...
charts/line-charts/line-chart-2.tsx
'use client';
import React, { useState } from 'react';
import { Badge } from '@/registry/default/ui/badge';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { Select, SelectContent, SelectItem, SelectTrigger } from '@/registry/default/ui/select';
import { TrendingUp } from 'lucide-react';
import { Area, CartesianGrid, ComposedChart, Line, XAxis, YAxis } from 'recharts';
// Cashflow data for 12 months
const cashflowData = [
{ month: 'JAN', value: 2100 },
{ month: 'FEB', value: 2300 },
{ month: 'MAR', value: 1900 },
{ month: 'APR', value: 4800 },
{ month: 'MAY', value: 5200 },
{ month: 'JUN', value: 8900 },
{ month: 'JUL', value: 6200 },
{ month: 'AUG', value: 7100 },
{ month: 'SEP', value: 9400 },
{ month: 'OCT', value: 10200 },
{ month: 'NOV', value: 11100 },
{ month: 'DEC', value: 11800 },
];
// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
value: {
label: 'Cashflow',
color: 'var(--color-violet-500)',
},
} satisfies ChartConfig;
// Custom Tooltip
interface TooltipProps {
active?: boolean;
payload?: Array<{
dataKey: string;
value: number;
color: string;
}>;
label?: string;
}
const CustomTooltip = ({ active, payload }: TooltipProps) => {
if (active && payload && payload.length) {
return (
<>
<div className="rounded-lg bg-zinc-900 text-white p-3 shadow-lg">
<div className="text-xs font-medium mb-1">Total:</div>
<div className="text-sm font-semibold">${payload[0].value.toLocaleString()}</div>
</div>
</>
);
}
return null;
};
// Period configuration
const PERIODS = {
'6m': {
key: '6m',
label: '6 months',
dateRange: 'Jul 01 - Dec 31, 2024',
},
'12m': {
key: '12m',
label: '12 months',
dateRange: 'Jan 01 - Dec 31, 2024',
},
'2y': {
key: '2y',
label: '2 years',
dateRange: 'Jan 01, 2023 - Dec 31, 2024',
},
} as const;
type PeriodKey = keyof typeof PERIODS;
export default function LineChart2() {
const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('12m');
// Filter data based on selected period
const getFilteredData = () => {
switch (selectedPeriod) {
case '6m':
return cashflowData.slice(-6);
case '12m':
return cashflowData;
case '2y':
// Simulate 2 years data by duplicating and modifying the current year
const previousYear = cashflowData.map((item) => ({
month: `${item.month} '23`,
value: Math.round(item.value * 0.85), // 15% lower for previous year
}));
const currentYear = cashflowData.map((item) => ({
month: `${item.month} '24`,
value: item.value,
}));
return [...previousYear, ...currentYear];
default:
return cashflowData;
}
};
const filteredData = getFilteredData();
// Get current period configuration
const currentPeriod = PERIODS[selectedPeriod];
// Calculate total and percentage based on filtered data
const totalCash = filteredData.reduce((sum, item) => sum + item.value, 0);
const lastValue = filteredData[filteredData.length - 1]?.value || 0;
const previousValue = filteredData[filteredData.length - 2]?.value || 0;
const percentageChange = previousValue > 0 ? ((lastValue - previousValue) / previousValue) * 100 : 0;
return (
<div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
<Card className="w-full lg:max-w-4xl ">
<CardHeader className="border-0 min-h-auto pt-6 pb-4">
<CardTitle className="text-lg font-semibold">Cashflow</CardTitle>
<CardToolbar>
<Select value={selectedPeriod} onValueChange={(value) => setSelectedPeriod(value as PeriodKey)}>
<SelectTrigger>{currentPeriod.label}</SelectTrigger>
<SelectContent align="end">
{Object.values(PERIODS).map((period) => (
<SelectItem key={period.key} value={period.key}>
{period.label}
</SelectItem>
))}
</SelectContent>
</Select>
</CardToolbar>
</CardHeader>
<CardContent className="px-0">
{/* Stats Section */}
<div className="px-5 mb-8">
<div className="text-xs font-medium text-muted-foreground tracking-wide mb-2">
{currentPeriod.dateRange}
</div>
<div className="flex items-center gap-3 mb-4">
<div className="text-3xl font-bold">${totalCash.toLocaleString()}</div>
<Badge variant="success" appearance="light">
<TrendingUp className="size-3" />
{Math.abs(percentageChange).toFixed(2)}%
</Badge>
</div>
</div>
{/* Chart */}
<div className="relative">
<ChartContainer
config={chartConfig}
className="h-[300px] w-full ps-1.5 pe-2.5 overflow-visible [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
>
<ComposedChart
data={filteredData}
margin={{
top: 25,
right: 25,
left: 0,
bottom: 25,
}}
style={{ overflow: 'visible' }}
>
{/* Gradient */}
<defs>
<linearGradient id="cashflowGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={chartConfig.value.color} stopOpacity={0.15} />
<stop offset="100%" stopColor={chartConfig.value.color} stopOpacity={0} />
</linearGradient>
<filter id="dotShadow" x="-50%" y="-50%" width="200%" height="200%">
<feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.5)" />
</filter>
</defs>
<CartesianGrid
strokeDasharray="4 12"
stroke="var(--input)"
strokeOpacity={1}
horizontal={true}
vertical={false}
/>
<XAxis
dataKey="month"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickMargin={12}
dy={10}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => `${value / 1000}K`}
domain={[0, 'dataMax + 1000']}
tickCount={6}
tickMargin={12}
/>
<ChartTooltip
content={<CustomTooltip />}
cursor={{
stroke: chartConfig.value.color,
strokeWidth: 1,
strokeDasharray: 'none',
}}
/>
{/* Gradient area */}
<Area
type="linear"
dataKey="value"
stroke="transparent"
fill="url(#cashflowGradient)"
strokeWidth={0}
dot={false}
/>
{/* Main cashflow line */}
<Line
type="linear"
dataKey="value"
stroke={chartConfig.value.color}
strokeWidth={3}
dot={(props) => {
const { cx, cy, payload } = props;
if (payload.month === 'JUN' || payload.month === 'NOV') {
return (
<circle
key={`dot-${cx}-${cy}`}
cx={cx}
cy={cy}
r={6}
fill={chartConfig.value.color}
stroke="white"
strokeWidth={2}
filter="url(#dotShadow)"
/>
);
}
return <g key={`dot-${cx}-${cy}`} />; // Return empty group for other points
}}
activeDot={{
r: 6,
fill: chartConfig.value.color,
stroke: 'white',
strokeWidth: 2,
filter: 'url(#dotShadow)',
}}
/>
</ComposedChart>
</ChartContainer>
</div>
</CardContent>
</Card>
</div>
);
}
Loading...
charts/line-charts/line-chart-3.tsx
'use client';
import React, { useState } from 'react';
import { Button } from '@/registry/default/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from '@/registry/default/ui/chart';
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/ui/toggle-group';
import { ChartNoAxesCombined, Info } from 'lucide-react';
import { CartesianGrid, Line, LineChart } from 'recharts';
// Revenue performance data for different periods (in thousands)
const revenueData = {
'5D': [
{ period: 'Mon', revenue: 10.2, fill: 'var(--color-revenue)' },
{ period: 'Tue', revenue: 62.8, fill: 'var(--color-revenue)' },
{ period: 'Wed', revenue: 38.1, fill: 'var(--color-revenue)' },
{ period: 'Thu', revenue: 71.4, fill: 'var(--color-revenue)' },
{ period: 'Fri', revenue: 54.7, fill: 'var(--color-revenue)' },
{ period: 'Sat', revenue: 29.3, fill: 'var(--color-revenue)' },
{ period: 'Sun', revenue: 40.6, fill: 'var(--color-revenue)' },
],
'2W': [
{ period: 'W1', revenue: 324.5, fill: 'var(--color-revenue)' },
{ period: 'W2', revenue: 398.7, fill: 'var(--color-revenue)' },
{ period: 'W3', revenue: 276.3, fill: 'var(--color-revenue)' },
{ period: 'W4', revenue: 445.9, fill: 'var(--color-revenue)' },
{ period: 'W5', revenue: 387.2, fill: 'var(--color-revenue)' },
{ period: 'W6', revenue: 512.8, fill: 'var(--color-revenue)' },
{ period: 'W7', revenue: 358.4, fill: 'var(--color-revenue)' },
{ period: 'W8', revenue: 478.6, fill: 'var(--color-revenue)' },
],
'1M': [
{ period: 'W1', revenue: 324.5, fill: 'var(--color-revenue)' },
{ period: 'W2', revenue: 398.7, fill: 'var(--color-revenue)' },
{ period: 'W3', revenue: 276.3, fill: 'var(--color-revenue)' },
{ period: 'W4', revenue: 445.9, fill: 'var(--color-revenue)' },
{ period: 'W5', revenue: 387.2, fill: 'var(--color-revenue)' },
{ period: 'W6', revenue: 512.8, fill: 'var(--color-revenue)' },
{ period: 'W7', revenue: 358.4, fill: 'var(--color-revenue)' },
{ period: 'W8', revenue: 478.6, fill: 'var(--color-revenue)' },
{ period: 'W9', revenue: 423.1, fill: 'var(--color-revenue)' },
{ period: 'W10', revenue: 567.3, fill: 'var(--color-revenue)' },
{ period: 'W11', revenue: 489.7, fill: 'var(--color-revenue)' },
{ period: 'W12', revenue: 534.2, fill: 'var(--color-revenue)' },
],
'6M': [
{ period: 'Jan', revenue: 1875.3, fill: 'var(--color-revenue)' },
{ period: 'Feb', revenue: 2234.7, fill: 'var(--color-revenue)' },
{ period: 'Mar', revenue: 1698.2, fill: 'var(--color-revenue)' },
{ period: 'Apr', revenue: 2567.8, fill: 'var(--color-revenue)' },
{ period: 'May', revenue: 2145.6, fill: 'var(--color-revenue)' },
{ period: 'Jun', revenue: 2789.4, fill: 'var(--color-revenue)' },
{ period: 'Jul', revenue: 2356.1, fill: 'var(--color-revenue)' },
{ period: 'Aug', revenue: 3012.5, fill: 'var(--color-revenue)' },
{ period: 'Sep', revenue: 2687.9, fill: 'var(--color-revenue)' },
{ period: 'Oct', revenue: 3234.8, fill: 'var(--color-revenue)' },
{ period: 'Nov', revenue: 2891.3, fill: 'var(--color-revenue)' },
{ period: 'Dec', revenue: 3456.7, fill: 'var(--color-revenue)' },
],
'1Y': [
{ period: 'Q1 2023', revenue: 5808.2, fill: 'var(--color-revenue)' },
{ period: 'Q2 2023', revenue: 7501.8, fill: 'var(--color-revenue)' },
{ period: 'Q3 2023', revenue: 6234.7, fill: 'var(--color-revenue)' },
{ period: 'Q4 2023', revenue: 8456.3, fill: 'var(--color-revenue)' },
{ period: 'Q1 2024', revenue: 7123.9, fill: 'var(--color-revenue)' },
{ period: 'Q2 2024', revenue: 9287.5, fill: 'var(--color-revenue)' },
{ period: 'Q3 2024', revenue: 8567.1, fill: 'var(--color-revenue)' },
{ period: 'Q4 2024', revenue: 10234.6, fill: 'var(--color-revenue)' },
],
};
// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
revenue: {
label: 'Revenue',
color: 'var(--color-violet-500)',
},
} satisfies ChartConfig;
// Period configuration
const PERIODS = {
'5D': { key: '5D', label: '5D' },
'2W': { key: '2W', label: '2W' },
'1M': { key: '1M', label: '1M' },
'6M': { key: '6M', label: '6M' },
'1Y': { key: '1Y', label: '1Y' },
} as const;
type PeriodKey = keyof typeof PERIODS;
export default function LineChart3() {
const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('5D');
// Get data for selected period
const currentData = revenueData[selectedPeriod];
// Calculate total revenue dynamically based on selected period
const totalRevenue = currentData.reduce((sum, item) => sum + item.revenue, 0);
// Format total revenue display
const formatRevenue = (amount: number) => {
if (amount >= 1000) {
return `${(amount / 1000).toFixed(1)}M`;
}
return `${amount.toFixed(0)}k`;
};
return (
<div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
<Card className="w-full max-w-md rounded-2xl shadow-sm p-6">
<CardHeader className="p-0 pb-6 mb-6">
<CardTitle className="text-lg font-semibold">Revenue Performance</CardTitle>
<CardToolbar>
<Button variant="outline">Export</Button>
</CardToolbar>
</CardHeader>
<CardContent className="p-0 space-y-6">
{/* Nav */}
<div className="space-y-6 mb-6">
{/* Stats Section */}
<div className="flex items-center gap-4">
<div className="size-12 bg-violet-100 border border-violet-200 dark:border-violet-800 dark:bg-violet-950 rounded-full flex items-center justify-center">
<ChartNoAxesCombined className="w-6 h-6 text-violet-600" />
</div>
<div>
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1">
Total Revenue
</div>
<div className="text-2xl font-bold">${formatRevenue(totalRevenue)}</div>
</div>
</div>
{/* Toggle Group */}
<ToggleGroup
type="single"
value={selectedPeriod}
variant="outline"
onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
className="w-full"
>
{Object.values(PERIODS).map((period) => (
<ToggleGroupItem
key={period.key}
value={period.key}
variant="outline"
className="flex-1 data-[state=on]:bg-gray-900 data-[state=on]:text-white data-[state=on]:border-gray-900"
>
{period.label}
</ToggleGroupItem>
))}
</ToggleGroup>
</div>
{/* Chart */}
<div className="h-40 relative w-full overflow-hidden">
<ChartContainer
config={chartConfig}
className="h-full w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
>
<LineChart
accessibilityLayer
data={currentData}
margin={{
top: 10,
left: 10,
right: 10,
bottom: 10,
}}
>
<CartesianGrid
strokeDasharray="4 8"
stroke="var(--input)"
strokeOpacity={1}
horizontal={false}
vertical={true}
/>
<ChartTooltip
cursor={{
stroke: chartConfig.revenue.color,
strokeWidth: 1,
strokeDasharray: '2 4',
}}
content={<ChartTooltipContent indicator="line" nameKey="revenue" hideLabel />}
/>
<Line
dataKey="revenue"
type="natural"
stroke="var(--color-revenue)"
strokeWidth={2}
dot={false}
activeDot={{
r: 4,
fill: 'var(--color-revenue)',
stroke: 'var(--color-revenue)',
strokeWidth: 0,
}}
/>
</LineChart>
</ChartContainer>
</div>
{/* Footer Note */}
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<Info className="size-3.5" />
<span>Revenue includes subscription and one-time payments.</span>
</div>
</CardContent>
</Card>
</div>
);
}
Loading...
charts/line-charts/line-chart-4.tsx
'use client';
import React from 'react';
import { Button } from '@/registry/default/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from '@/registry/default/ui/chart';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/registry/default/ui/dropdown-menu';
import { Calendar, Download, Filter, MoreHorizontal, RefreshCw, Share2 } from 'lucide-react';
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from 'recharts';
// Social media engagement data throughout the day (percentage)
const engagementData = [
{ time: '6AM', facebook: 2, instagram: 8, linkedin: 5 },
{ time: '7AM', facebook: 5, instagram: 12, linkedin: 8 },
{ time: '8AM', facebook: 8, instagram: 18, linkedin: 15 },
{ time: '9AM', facebook: 12, instagram: 25, linkedin: 22 },
{ time: '10AM', facebook: 15, instagram: 35, linkedin: 28 },
{ time: '11AM', facebook: 18, instagram: 42, linkedin: 32 },
{ time: '12PM', facebook: 22, instagram: 38, linkedin: 35 },
{ time: '1PM', facebook: 25, instagram: 45, linkedin: 30 },
{ time: '2PM', facebook: 28, instagram: 48, linkedin: 33 },
{ time: '3PM', facebook: 30, instagram: 52, linkedin: 38 },
{ time: '4PM', facebook: 26, instagram: 46, linkedin: 35 },
{ time: '5PM', facebook: 24, instagram: 44, linkedin: 32 },
{ time: '6PM', facebook: 22, instagram: 40, linkedin: 28 },
{ time: '7PM', facebook: 20, instagram: 38, linkedin: 25 },
{ time: '8PM', facebook: 18, instagram: 35, linkedin: 22 },
];
// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
facebook: {
label: 'Facebook',
color: 'var(--color-blue-600)',
},
instagram: {
label: 'Instagram',
color: 'var(--color-orange-500)',
},
linkedin: {
label: 'LinkedIn',
color: 'var(--color-slate-600)',
},
} satisfies ChartConfig;
// Custom Tooltip
interface TooltipProps {
active?: boolean;
payload?: Array<{
dataKey: string;
value: number;
color: string;
}>;
label?: string;
}
const ChartLabel = ({ label, color }: { label: string; color: string }) => {
return (
<div className="flex items-center gap-1.5">
<div className="size-3.5 border-4 rounded-full bg-background" style={{ borderColor: color }}></div>
<span className="text-muted-foreground">{label}</span>
</div>
);
};
const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
if (active && payload && payload.length) {
return (
<div className="rounded-lg border bg-popover p-3 shadow-sm shadow-black/5 min-w-[150px]">
<div className="text-xs font-medium text-muted-foreground tracking-wide mb-2.5">{label}</div>
<div className="space-y-2">
{payload.map((entry, index) => {
const config = chartConfig[entry.dataKey as keyof typeof chartConfig];
return (
<div key={index} className="flex items-center gap-2 text-xs">
<ChartLabel label={config?.label + ':'} color={entry.color} />
<span className="font-semibold text-popover-foreground">{entry.value}%</span>
</div>
);
})}
</div>
</div>
);
}
return null;
};
// Chart Legend Component
const ChartLegend = ({ label, color }: { label: string; color: string }) => {
return (
<div className="flex items-center gap-2">
<div
className="size-3.5 border-4 rounded-full bg-background border-border"
style={{ borderColor: `${color}` }}
></div>
<span className="text-sm text-muted-foreground">{label}</span>
</div>
);
};
export default function LineChart4() {
return (
<div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
<Card className="w-full max-w-2xl">
<CardHeader className="border-0 pt-6 pb-4">
<CardTitle className="text-lg font-semibold">Social Media Activity</CardTitle>
<CardToolbar>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>
<Download className="h-4 w-4" />
Export Data
</DropdownMenuItem>
<DropdownMenuItem>
<Calendar className="h-4 w-4" />
Change Date
</DropdownMenuItem>
<DropdownMenuItem>
<Filter className="h-4 w-4" />
Filter Platforms
</DropdownMenuItem>
<DropdownMenuItem>
<RefreshCw className="h-4 w-4" />
Refresh
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Share2 className="h-4 w-4" />
Share Report
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</CardToolbar>
</CardHeader>
<CardContent className="ps-0 pe-4.5 pb-6">
<ChartContainer
config={chartConfig}
className="h-[200px] w-full mb-6 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
>
<LineChart
data={engagementData}
margin={{
top: 5,
right: 5,
left: 5,
bottom: 5,
}}
>
<CartesianGrid
strokeDasharray="4 8"
stroke="var(--input)"
strokeOpacity={1}
horizontal={true}
vertical={false}
/>
<XAxis
dataKey="time"
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, fill: 'var(--text-muted-foreground)' }}
tickMargin={10}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, fill: 'var(--text-muted-foreground)' }}
tickFormatter={(value) => `${value}%`}
domain={[0, 60]}
tickMargin={10}
/>
<ChartTooltip content={<CustomTooltip />} cursor={{ strokeDasharray: '3 3', stroke: 'var(--input)' }} />
{/* Facebook Line */}
<Line dataKey="facebook" type="monotone" stroke="var(--color-facebook)" strokeWidth={2} dot={false} />
{/* Instagram Line */}
<Line dataKey="instagram" type="monotone" stroke="var(--color-instagram)" strokeWidth={2} dot={false} />
{/* LinkedIn Line */}
<Line dataKey="linkedin" type="monotone" stroke="var(--color-linkedin)" strokeWidth={2} dot={false} />
</LineChart>
</ChartContainer>
{/* Legend */}
<div className="flex items-center justify-center gap-6">
<ChartLegend label="Facebook" color={chartConfig.facebook.color} />
<ChartLegend label="Instagram" color={chartConfig.instagram.color} />
<ChartLegend label="LinkedIn" color={chartConfig.linkedin.color} />
</div>
</CardContent>
</Card>
</div>
);
}
Loading...
charts/line-charts/line-chart-5.tsx
'use client';
import React, { useState } from 'react';
import { Badge } from '@/registry/default/ui/badge';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/registry/default/ui/select';
import { TrendingDown, TrendingUp } from 'lucide-react';
import { Area, CartesianGrid, ComposedChart, Line, XAxis, YAxis } from 'recharts';
// E-commerce data for different periods with balanced patterns
const salesViewsData = {
'7d': [
{ period: 'Aug 1', sales: 850, views: 620 },
{ period: 'Aug 5', sales: 920, views: 780 },
{ period: 'Aug 7', sales: 780, views: 540 },
{ period: 'Aug 8', sales: 1240, views: 890 },
{ period: 'Aug 22', sales: 1100, views: 720 },
],
'30d': [
{ period: 'Week 1', sales: 5200, views: 4800 },
{ period: 'Week 2', sales: 6400, views: 5200 },
{ period: 'Week 3', sales: 5800, views: 4600 },
{ period: 'Week 4', sales: 7200, views: 5800 },
{ period: 'Week 5', sales: 6800, views: 5400 },
{ period: 'Week 6', sales: 6200, views: 4900 },
],
'90d': [
{ period: 'Jan', sales: 22000, views: 18500 },
{ period: 'Feb', sales: 24800, views: 20200 },
{ period: 'Mar', sales: 21400, views: 17800 },
{ period: 'Apr', sales: 26200, views: 21600 },
{ period: 'May', sales: 25600, views: 20800 },
{ period: 'Jun', sales: 27400, views: 22400 },
{ period: 'Jul', sales: 24800, views: 19600 },
{ period: 'Aug', sales: 23600, views: 18800 },
{ period: 'Sep', sales: 28200, views: 23200 },
],
'12m': [
{ period: 'Q1 23', sales: 85000, views: 72000 },
{ period: 'Q2 23', sales: 92000, views: 78000 },
{ period: 'Q3 23', sales: 88000, views: 74000 },
{ period: 'Q4 23', sales: 98000, views: 82000 },
{ period: 'Q1 24', sales: 94000, views: 79000 },
{ period: 'Q2 24', sales: 102000, views: 86000 },
{ period: 'Q3 24', sales: 96000, views: 81000 },
{ period: 'Q4 24', sales: 108000, views: 90000 },
],
};
// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
sales: {
label: 'Sales',
color: 'var(--color-amber-500)',
},
views: {
label: 'Views',
color: 'var(--color-purple-500)',
},
} satisfies ChartConfig;
// Custom Tooltip
interface TooltipProps {
active?: boolean;
payload?: Array<{
dataKey: string;
value: number;
color: string;
}>;
label?: string;
}
const ChartLabel = ({ label, color }: { label: string; color: string }) => {
return (
<div className="flex items-center gap-1.5">
<div className="w-1 h-3 rounded-full" style={{ backgroundColor: color }}></div>
<span className="text-muted-foreground">{label}</span>
</div>
);
};
const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
if (active && payload && payload.length) {
return (
<div className="rounded-lg border bg-popover p-3 shadow-sm shadow-black/5 min-w-[150px]">
<div className="text-xs font-medium text-muted-foreground tracking-wide mb-2.5">{label}</div>
<div className="space-y-2">
{payload.map((entry, index) => {
const config = chartConfig[entry.dataKey as keyof typeof chartConfig];
return (
<div key={index} className="flex items-center gap-2 text-xs">
<ChartLabel label={config?.label + ':'} color={entry.color} />
<span className="font-semibold text-popover-foreground">
{entry.dataKey === 'sales'
? `£${entry.value.toLocaleString()}`
: entry.value >= 0
? `+${entry.value.toLocaleString()}`
: entry.value.toLocaleString()}
</span>
</div>
);
})}
</div>
</div>
);
}
return null;
};
// Period configuration
const PERIODS = {
'7d': { key: '7d', label: 'Last 7 days' },
'30d': { key: '30d', label: 'Last 30 days' },
'90d': { key: '90d', label: 'Last 90 days' },
'12m': { key: '12m', label: 'Last 12 months' },
} as const;
type PeriodKey = keyof typeof PERIODS;
export default function LineChart5() {
const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('30d');
// Get data for selected period
const currentData = salesViewsData[selectedPeriod];
// Calculate totals and percentages
const totalSales = currentData.reduce((sum, item) => sum + item.sales, 0);
const totalViews = currentData.reduce((sum, item) => sum + item.views, 0);
// Calculate percentage changes (simulated)
const salesChange = selectedPeriod === '7d' ? 12 : selectedPeriod === '30d' ? 8 : selectedPeriod === '90d' ? -3 : 15;
const viewsChange = selectedPeriod === '7d' ? -3 : selectedPeriod === '30d' ? 5 : selectedPeriod === '90d' ? -8 : 12;
return (
<div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
<Card className="w-full max-w-3xl">
<CardHeader className="border-0 min-h-auto pt-6 pb-4">
<CardTitle className="text-lg font-semibold">E-commerce Sales</CardTitle>
<CardToolbar>
{/* Period Selector */}
<Select value={selectedPeriod} onValueChange={(value) => setSelectedPeriod(value as PeriodKey)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent align="end">
{Object.values(PERIODS).map((period) => (
<SelectItem key={period.key} value={period.key}>
{period.label}
</SelectItem>
))}
</SelectContent>
</Select>
</CardToolbar>
</CardHeader>
<CardContent className="px-2 pb-6">
{/* Stats Section */}
<div className="flex items-center flex-wrap gap-3.5 md:gap-10 px-5 mb-8 text-sm">
<div className="flex items-center gap-3.5">
<ChartLabel label="Sales" color={chartConfig.sales.color} />
<div className="flex items-center gap-2">
<span className="text-2xl font-bold">£{totalSales.toLocaleString()}</span>
<Badge variant={salesChange >= 0 ? 'success' : 'destructive'} appearance="light">
{salesChange >= 0 ? <TrendingUp className="size-3" /> : <TrendingDown className="size-3" />}
{Math.abs(salesChange)}%
</Badge>
</div>
</div>
<div className="flex items-center gap-3.5">
<ChartLabel label="Views" color={chartConfig.views.color} />
<div className="flex items-center gap-2">
<span className="text-2xl font-bold">{totalViews.toLocaleString()}</span>
<Badge variant={salesChange <= 0 ? 'success' : 'destructive'} appearance="light">
{viewsChange <= 0 ? <TrendingUp className="size-3" /> : <TrendingDown className="size-3" />}
{Math.abs(viewsChange)}%
</Badge>
</div>
</div>
</div>
{/* Chart */}
<ChartContainer
config={chartConfig}
className="h-[300px] w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
>
<ComposedChart
data={currentData}
margin={{
top: 30,
right: 5,
left: 5,
bottom: 10,
}}
>
{/* Background pattern for chart area only */}
<defs>
<linearGradient id="salesGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={chartConfig.sales.color} stopOpacity={0.3} />
<stop offset="100%" stopColor={chartConfig.sales.color} stopOpacity={0.05} />
</linearGradient>
<linearGradient id="viewsGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={chartConfig.views.color} stopOpacity={0.3} />
<stop offset="100%" stopColor={chartConfig.views.color} stopOpacity={0.05} />
</linearGradient>
<filter id="glow">
<feGaussianBlur stdDeviation="3" result="coloredBlur" />
<feMerge>
<feMergeNode in="coloredBlur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<CartesianGrid
strokeDasharray="4 12"
stroke="var(--input)"
strokeOpacity={1}
horizontal={true}
vertical={false}
/>
{/* X Axis */}
<XAxis
dataKey="period"
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
tickMargin={10}
/>
{/* Left Y Axis for Sales */}
<YAxis
yAxisId="sales"
orientation="left"
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
tickFormatter={(value) => (selectedPeriod === '7d' ? `£${value}` : `£${(value / 1000).toFixed(0)}k`)}
tickMargin={10}
/>
{/* Right Y Axis for Views */}
<YAxis
yAxisId="views"
orientation="right"
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
tickFormatter={(value) => {
if (selectedPeriod === '7d') {
return value >= 0 ? `+${value}` : value.toString();
}
return value >= 0 ? `+${(value / 1000).toFixed(0)}k` : `${(value / 1000).toFixed(0)}k`;
}}
tickMargin={8}
domain={['dataMin - 100', 'dataMax + 100']}
/>
<ChartTooltip
content={<CustomTooltip />}
cursor={{ strokeDasharray: '3 3', stroke: 'var(--muted-foreground)', strokeOpacity: 0.5 }}
/>
{/* Sales Line (Linear) */}
<Line
yAxisId="sales"
type="linear"
dataKey="sales"
stroke={chartConfig.sales.color}
strokeWidth={1}
dot={false}
activeDot={{
r: 5,
fill: chartConfig.sales.color,
strokeWidth: 0,
}}
/>
{/* Views Line (Linear Dashed) */}
<Line
yAxisId="views"
type="linear"
dataKey="views"
stroke={chartConfig.views.color}
strokeWidth={1}
strokeDasharray="8 4"
dot={false}
activeDot={{
r: 5,
fill: chartConfig.views.color,
strokeWidth: 0,
}}
/>
</ComposedChart>
</ChartContainer>
</CardContent>
</Card>
</div>
);
}
Loading...
charts/line-charts/line-chart-6.tsx
'use client';
import React, { useState } from 'react';
import { Badge } from '@/registry/default/ui/badge';
import { Card, CardContent, CardHeader } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { ArrowDown, ArrowUp } from 'lucide-react';
import { Line, LineChart, XAxis, YAxis } from 'recharts';
import { cn } from '@/lib/utils';
// E-commerce platform metrics data (adapted from interactive chart example)
const platformData = [
{ date: '2024-04-01', orders: 222, response: 150, revenue: 8.2, customers: 420 },
{ date: '2024-04-02', orders: 97, response: 180, revenue: 4.5, customers: 290 },
{ date: '2024-04-03', orders: 167, response: 120, revenue: 6.8, customers: 380 },
{ date: '2024-04-04', orders: 242, response: 260, revenue: 9.1, customers: 520 },
{ date: '2024-04-05', orders: 301, response: 340, revenue: 11.2, customers: 620 },
{ date: '2024-04-06', orders: 59, response: 110, revenue: 2.8, customers: 180 },
{ date: '2024-04-07', orders: 261, response: 190, revenue: 9.8, customers: 510 },
{ date: '2024-04-08', orders: 327, response: 350, revenue: 12.1, customers: 650 },
{ date: '2024-04-09', orders: 89, response: 150, revenue: 3.8, customers: 220 },
{ date: '2024-04-10', orders: 195, response: 165, revenue: 7.2, customers: 390 },
{ date: '2024-04-11', orders: 224, response: 170, revenue: 8.5, customers: 450 },
{ date: '2024-04-12', orders: 387, response: 290, revenue: 13.8, customers: 710 },
{ date: '2024-04-13', orders: 215, response: 250, revenue: 8.2, customers: 430 },
{ date: '2024-04-14', orders: 75, response: 130, revenue: 3.1, customers: 190 },
{ date: '2024-04-15', orders: 122, response: 180, revenue: 5.1, customers: 300 },
{ date: '2024-04-16', orders: 197, response: 160, revenue: 7.5, customers: 390 },
{ date: '2024-04-17', orders: 473, response: 380, revenue: 17.2, customers: 890 },
{ date: '2024-04-18', orders: 338, response: 400, revenue: 12.9, customers: 670 },
];
// Metric configurations
const metrics = [
{
key: 'orders',
label: 'Orders',
value: 2865,
previousValue: 2420,
format: (val: number) => val.toLocaleString(),
},
{
key: 'response',
label: 'Response Time',
value: 135,
previousValue: 118,
format: (val: number) => `${val}ms`,
isNegative: true, // Lower response time is better
},
{
key: 'revenue',
label: 'Revenue',
value: 8.67,
previousValue: 7.54,
format: (val: number) => `$${val.toFixed(2)}k`,
},
{
key: 'customers',
label: 'Active Users',
value: 1425,
previousValue: 1240,
format: (val: number) => val.toLocaleString(),
},
];
// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
orders: {
label: 'Orders',
color: 'var(--color-teal-500)',
},
response: {
label: 'Response Time',
color: 'var(--color-violet-500)',
},
revenue: {
label: 'Revenue',
color: 'var(--color-lime-500)',
},
customers: {
label: 'Active Users',
color: 'var(--color-sky-500)',
},
} satisfies ChartConfig;
// Custom Tooltip
interface TooltipProps {
active?: boolean;
payload?: Array<{
dataKey: string;
value: number;
color: string;
}>;
label?: string;
}
const CustomTooltip = ({ active, payload }: TooltipProps) => {
if (active && payload && payload.length) {
const entry = payload[0];
const metric = metrics.find((m) => m.key === entry.dataKey);
if (metric) {
return (
<div className="rounded-lg border bg-popover p-3 shadow-sm shadow-black/5 min-w-[120px]">
<div className="flex items-center gap-2 text-sm">
<div className="size-1.5 rounded-full" style={{ backgroundColor: entry.color }}></div>
<span className="text-muted-foreground">{metric.label}:</span>
<span className="font-semibold text-popover-foreground">{metric.format(entry.value)}</span>
</div>
</div>
);
}
}
return null;
};
export default function LineChart6() {
const [selectedMetric, setSelectedMetric] = useState<string>('response');
return (
<div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
<Card className="@container w-full max-w-4xl">
<CardHeader className="p-0 mb-5">
{/* Metrics Grid */}
<div className="grid @2xl:grid-cols-2 @3xl:grid-cols-4 grow">
{metrics.map((metric) => {
const change = ((metric.value - metric.previousValue) / metric.previousValue) * 100;
const isPositive = metric.isNegative ? change < 0 : change > 0;
return (
<button
key={metric.key}
onClick={() => setSelectedMetric(metric.key)}
className={cn(
'cursor-pointer flex-1 text-start p-4 last:border-b-0 border-b @2xl:border-b @2xl:even:border-e @3xl:border-b-0 @3xl:border-e @3xl:last:border-e-0 transition-all',
selectedMetric === metric.key && 'bg-muted/50',
)}
>
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-muted-foreground">{metric.label}</span>
<Badge variant={isPositive ? 'success' : 'destructive'} appearance="outline">
{isPositive ? <ArrowUp className="size-3" /> : <ArrowDown className="size-3" />}
{Math.abs(change).toFixed(1)}%
</Badge>
</div>
<div className="text-2xl font-bold">{metric.format(metric.value)}</div>
<div className="text-xs text-muted-foreground mt-1">from {metric.format(metric.previousValue)}</div>
</button>
);
})}
</div>
</CardHeader>
<CardContent className="px-2.5 py-6">
<ChartContainer
config={chartConfig}
className="h-96 w-full overflow-visible [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
>
<LineChart
data={platformData}
margin={{
top: 20,
right: 20,
left: 5,
bottom: 20,
}}
style={{ overflow: 'visible' }}
>
{/* Background pattern for chart area only */}
<defs>
<pattern id="dotGrid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="10" cy="10" r="1" fill="var(--input)" fillOpacity="1" />
</pattern>
<filter id="lineShadow" x="-100%" y="-100%" width="300%" height="300%">
<feDropShadow
dx="4"
dy="6"
stdDeviation="25"
floodColor={`${chartConfig[selectedMetric as keyof typeof chartConfig]?.color}60`}
/>
</filter>
<filter id="dotShadow" x="-50%" y="-50%" width="200%" height="200%">
<feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.5)" />
</filter>
</defs>
<XAxis
dataKey="date"
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
tickMargin={10}
tickFormatter={(value) => {
const date = new Date(value);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
});
}}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
tickMargin={10}
tickCount={6}
tickFormatter={(value) => {
const metric = metrics.find((m) => m.key === selectedMetric);
return metric ? metric.format(value) : value.toString();
}}
/>
<ChartTooltip content={<CustomTooltip />} cursor={{ strokeDasharray: '3 3', stroke: '#9ca3af' }} />
{/* Background pattern for chart area only */}
<rect
x="60px"
y="-20px"
width="calc(100% - 75px)"
height="calc(100% - 10px)"
fill="url(#dotGrid)"
style={{ pointerEvents: 'none' }}
/>
<Line
type="monotone"
dataKey={selectedMetric}
stroke={chartConfig[selectedMetric as keyof typeof chartConfig]?.color}
strokeWidth={2}
filter="url(#lineShadow)"
dot={false}
activeDot={{
r: 6,
fill: chartConfig[selectedMetric as keyof typeof chartConfig]?.color,
stroke: 'white',
strokeWidth: 2,
filter: 'url(#dotShadow)',
}}
/>
</LineChart>
</ChartContainer>
</CardContent>
</Card>
</div>
);
}
Loading...
charts/line-charts/line-chart-7.tsx
'use client';
import React, { useState } from 'react';
import { Button } from '@/registry/default/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/ui/toggle-group';
import { ArrowDownRight, ArrowUpRight, Settings } from 'lucide-react';
import { Line, LineChart, XAxis, YAxis } from 'recharts';
import { cn } from '@/lib/utils';
// NFT Collection analytics data for different time periods
const nftData = {
week: [
{ period: 'Mon', value: 2.1 },
{ period: 'Tue', value: 4.2 },
{ period: 'Wed', value: 2.8 },
{ period: 'Thu', value: 5.1 },
{ period: 'Fri', value: 3.3 },
{ period: 'Sat', value: 6.2 },
{ period: 'Sun', value: 4.9 },
],
month: [
{ period: 'Jan', value: 1.2 },
{ period: 'Feb', value: 2.8 },
{ period: 'Mar', value: 1.9 },
{ period: 'Apr', value: 3.4 },
{ period: 'May', value: 2.7 },
{ period: 'Jun', value: 4.1 },
{ period: 'Jul', value: 3.2 },
{ period: 'Aug', value: 5.4 },
{ period: 'Sep', value: 4.8 },
{ period: 'Oct', value: 6.1 },
{ period: 'Nov', value: 5.2 },
{ period: 'Dec', value: 6.8 },
],
max: [
{ period: '2018', value: 0.1 },
{ period: '2019', value: 0.4 },
{ period: '2020', value: 0.7 },
{ period: '2021', value: 2.1 },
{ period: '2022', value: 3.2 },
{ period: '2023', value: 4.2 },
{ period: '2024', value: 6.4 },
],
};
type PeriodKey = keyof typeof nftData;
const PERIODS = {
week: { key: 'week', label: 'Week' },
month: { key: 'month', label: 'Month' },
max: { key: 'max', label: 'Max' },
} as const;
// Chart configuration with emerald theme
const chartConfig = {
value: {
label: 'NFT Floor Price',
color: 'var(--color-emerald-500)',
},
} satisfies ChartConfig;
// Custom Tooltip
interface TooltipProps {
active?: boolean;
payload?: Array<{
dataKey: string;
value: number;
color: string;
}>;
label?: string;
}
const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
if (active && payload && payload.length) {
return (
<div className="rounded-lg border border-slate-700 bg-slate-800 p-3 shadow-md shadow-slate-100/5 min-w-[120px]">
<div className="text-xs font-medium text-white/60 tracking-wide mb-2">{label}</div>
<div className="text-sm font-semibold text-white">{payload[0].value} USD</div>
</div>
);
}
return null;
};
export default function LineChart7() {
const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('max');
const currentData = nftData[selectedPeriod];
const currentValue = currentData[currentData.length - 1]?.value || 0;
const previousValue = currentData[currentData.length - 2]?.value || 0;
const growth = previousValue > 0 ? ((currentValue - previousValue) / previousValue) * 100 : 0;
return (
<div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
<Card className="w-full rounded-3xl lg:max-w-lg bg-gradient-to-br from-slate-900 via-slate-900 to-emerald-950/90 border-slate-700 text-white shadow-2xl shadow-emerald-500/10">
<CardHeader className="border-0 min-h-auto pt-6 pb-6">
<CardTitle className="text-lg font-semibold">Sales Analytics</CardTitle>
<CardToolbar>
<Button variant="dim" size="sm" mode="icon">
<Settings className="size-4 text-slate-200" />
</Button>
</CardToolbar>
</CardHeader>
<CardContent className="px-6 pb-6 space-y-6">
{/* Toggle Period Selector */}
<ToggleGroup
type="single"
value={selectedPeriod}
onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
className="w-full rounded-2xl border border-slate-700/80 p-1.5"
>
{Object.values(PERIODS).map((period) => (
<ToggleGroupItem
key={period.key}
value={period.key}
className="h-10 shadow-none! rounded-xl flex-1 border-0 data-[state=on]:bg-slate-800 data-[state=on]:text-emerald-400 text-zinc-400 hover:bg-transparent hover:text-emerald-300"
>
{period.label}
</ToggleGroupItem>
))}
</ToggleGroup>
{/* Chart Container */}
<div className="relative">
<ChartContainer
config={chartConfig}
className="h-[300px] w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
>
<LineChart
data={currentData}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 10,
}}
>
<XAxis
dataKey="period"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, strokeWidth: 0.5, stroke: 'rgb(255, 255, 255)', opacity: 0.4 }}
tickMargin={20}
interval="preserveStartEnd"
/>
<YAxis hide domain={['dataMin - 0.5', 'dataMax + 0.5']} />
<ChartTooltip
content={<CustomTooltip />}
cursor={{ strokeDasharray: '2 2', stroke: 'rgb(16 185 129)', strokeOpacity: 0.6 }}
/>
{/* Background pattern for chart area only */}
<defs>
<pattern id="dotGridDark" x="0" y="0" width="36" height="36" patternUnits="userSpaceOnUse">
<circle cx="15" cy="15" r="1.5" fill="#ffffff" fillOpacity="0.1" />
</pattern>
</defs>
<rect
x="10px"
y="-30px"
width="100%"
height="100%"
fill="url(#dotGridDark)"
style={{ pointerEvents: 'none' }}
/>
{/* Main line with sharp angles */}
<Line
type="linear"
dataKey="value"
stroke="rgb(16 185 129)"
strokeWidth={3}
dot={{
r: 4,
fill: 'rgb(16 185 129)',
stroke: 'rgb(16 185 129)',
strokeWidth: 2,
filter: 'drop-shadow(0 0 6px rgb(16 185 129))',
}}
activeDot={{
r: 6,
stroke: 'rgb(16 185 129)',
strokeWidth: 3,
fill: 'rgb(16 185 129)',
filter: 'drop-shadow(0 0 8px rgb(16 185 129))',
}}
/>
{/* Endpoint dot */}
<Line
type="linear"
dataKey="value"
stroke="transparent"
strokeWidth={0}
dot={false}
activeDot={{
r: 7,
stroke: 'rgb(16 185 129)',
strokeWidth: 4,
fill: 'rgb(16 185 129)',
filter: 'drop-shadow(0 0 10px rgb(16 185 129))',
}}
/>
</LineChart>
</ChartContainer>
</div>
{/* Statistics */}
<div className="flex items-center gap-2.5 justify-between">
{/* Floor Price */}
<div className="flex flex-col gap-0.5">
<div className="text-sm text-slate-300">Floor Price (USD)</div>
<div className="flex items-start gap-2">
<span className="text-4xl font-bold text-white">{currentValue.toFixed(1)}</span>
<span className="text-sm text-emerald-400 font-medium pt-0.75">+{(currentValue - 2.5).toFixed(1)}</span>
</div>
</div>
{/* Growth */}
<div className={cn('flex items-center gap-1.5', growth > 0 ? 'text-emerald-400' : 'text-destructive')}>
{growth > 0 ? <ArrowUpRight className="size-6" /> : <ArrowDownRight className="size-6" />}
<span className="text-2xl font-semibold">{(growth * 2.1).toFixed(1)}%</span>
</div>
</div>
</CardContent>
</Card>
</div>
);
}
Loading...
charts/line-charts/line-chart-8.tsx
'use client';
import React from 'react';
import { Card, CardContent } from '@/registry/default/ui/card';
import { Line, LineChart, ReferenceLine, ResponsiveContainer, Tooltip, YAxis } from 'recharts';
// Business Case 1: SaaS Revenue Tracking (Detailed wavy pattern with micro-fluctuations)
const revenueData = [
{ value: 15000 },
{ value: 18000 },
{ value: 25000 },
{ value: 32000 },
{ value: 35000 },
{ value: 28000 },
{ value: 20000 },
{ value: 12000 },
{ value: 5000 },
{ value: -2000 },
{ value: -10000 },
{ value: -18000 },
{ value: -25000 },
{ value: -22000 },
{ value: -15000 },
{ value: -8000 },
{ value: 0 },
{ value: 8000 },
{ value: 20000 },
{ value: 28000 },
{ value: 40000 },
{ value: 48000 },
{ value: 50000 },
{ value: 45000 },
{ value: 35000 },
{ value: 25000 },
{ value: 15000 },
{ value: 2000 },
{ value: -5000 },
{ value: -12000 },
{ value: -20000 },
{ value: -28000 },
{ value: -30000 },
{ value: -25000 },
{ value: -15000 },
{ value: -5000 },
{ value: 10000 },
{ value: 22000 },
{ value: 35000 },
{ value: 45000 },
{ value: 55000 },
{ value: 52000 },
{ value: 45000 },
{ value: 35000 },
{ value: 25000 },
{ value: 12000 },
{ value: 5000 },
{ value: -8000 },
{ value: -15000 },
{ value: -12000 },
{ value: -5000 },
{ value: 3000 },
{ value: 15000 },
{ value: 25000 },
{ value: 20000 },
{ value: 10000 },
{ value: -2000 },
{ value: -15000 },
{ value: -20000 },
{ value: -15000 },
];
// Business Case 2: E-commerce Conversion Rate (Detailed sine wave with micro-variations)
const conversionData = [
{ value: 0 },
{ value: 0.8 },
{ value: 1.5 },
{ value: 2.2 },
{ value: 2.8 },
{ value: 3.2 },
{ value: 3.5 },
{ value: 3.4 },
{ value: 3.2 },
{ value: 2.6 },
{ value: 2.0 },
{ value: 1.2 },
{ value: 0.5 },
{ value: -0.2 },
{ value: -1.2 },
{ value: -1.8 },
{ value: -2.5 },
{ value: -2.8 },
{ value: -3.0 },
{ value: -2.9 },
{ value: -2.8 },
{ value: -2.2 },
{ value: -1.5 },
{ value: -0.8 },
{ value: 0.2 },
{ value: 1.0 },
{ value: 2.0 },
{ value: 2.8 },
{ value: 3.5 },
{ value: 3.9 },
{ value: 4.2 },
{ value: 4.1 },
{ value: 3.8 },
{ value: 3.2 },
{ value: 2.5 },
{ value: 1.5 },
{ value: 0.8 },
{ value: 0.2 },
{ value: -1.0 },
{ value: -1.6 },
{ value: -2.5 },
{ value: -2.9 },
{ value: -3.2 },
{ value: -3.0 },
{ value: -2.0 },
{ value: -1.2 },
{ value: 0 },
{ value: 1.2 },
{ value: 2.5 },
{ value: 3.5 },
{ value: 4.0 },
{ value: 3.8 },
{ value: 2.8 },
{ value: 1.5 },
{ value: 0.5 },
{ value: -0.8 },
{ value: -2.0 },
{ value: -2.8 },
{ value: -2.5 },
{ value: -1.0 },
];
// Business Case 3: Server Performance Monitoring (Detailed oscillating decline with volatility)
const performanceData = [
{ value: 5 },
{ value: 8 },
{ value: 10 },
{ value: 12 },
{ value: 8 },
{ value: 5 },
{ value: 3 },
{ value: 0 },
{ value: -2 },
{ value: -5 },
{ value: -8 },
{ value: -10 },
{ value: -12 },
{ value: -10 },
{ value: -8 },
{ value: -5 },
{ value: -3 },
{ value: 0 },
{ value: 2 },
{ value: 4 },
{ value: 6 },
{ value: 7 },
{ value: 4 },
{ value: 1 },
{ value: -1 },
{ value: -4 },
{ value: -6 },
{ value: -8 },
{ value: -10 },
{ value: -11 },
{ value: -12 },
{ value: -10 },
{ value: -8 },
{ value: -6 },
{ value: -4 },
{ value: -2 },
{ value: 1 },
{ value: 3 },
{ value: 5 },
{ value: 6 },
{ value: 3 },
{ value: 0 },
{ value: -2 },
{ value: -5 },
{ value: -7 },
{ value: -9 },
{ value: -11 },
{ value: -13 },
{ value: -15 },
{ value: -13 },
{ value: -11 },
{ value: -8 },
{ value: -5 },
{ value: -2 },
{ value: 0 },
{ value: -3 },
{ value: -6 },
{ value: -9 },
{ value: -12 },
{ value: -15 },
];
// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const businessCards = [
{
title: 'Revenue Variance',
metric: 'Monthly change from baseline',
baseValue: '+15K',
baseCurrency: 'Start',
targetValue: '-15K',
targetCurrency: 'End',
data: revenueData,
change: 'Volatile',
isPositive: false,
color: 'var(--color-blue-500)',
},
{
title: 'Conversion Change',
metric: 'Rate variance from zero',
baseValue: '50%',
baseCurrency: 'Baseline',
targetValue: '+60.5%',
targetCurrency: 'Current',
data: conversionData,
change: 'Cyclical',
isPositive: true,
color: 'var(--color-emerald-500)',
},
{
title: 'Performance Drift',
metric: 'System variance tracking',
baseValue: '+5%',
baseCurrency: 'Peak',
targetValue: '-15%',
targetCurrency: 'Low',
data: performanceData,
change: 'Declining',
isPositive: false,
color: 'var(--color-amber-500)',
},
];
export default function LineChart8() {
return (
<div className="min-h-screen flex items-center justify-center p-6 lg:p-12">
{/* Container */}
<div className="@container grow w-full max-w-7xl">
{/* Grid */}
<div className="grid grid-cols-1 @3xl:grid-cols-3 gap-6">
{/* Business Cards */}
{businessCards.map((card, i) => (
<Card key={i}>
<CardContent className="flex flex-col gap-6">
{/* Header */}
<div className="flex flex-col">
<h3 className="text-base font-semibold text-foreground m-0">{card.title}</h3>
<p className="text-sm text-muted-foreground m-0">{card.metric}</p>
</div>
{/* Chart Section */}
<div className="flex items-center justify-between">
{/* Left side - Base Currency */}
<div className="text-center">
<div className="text-lg font-semibold text-foreground">{card.baseValue}</div>
<div className="text-xs text-muted-foreground font-medium">{card.baseCurrency}</div>
</div>
{/* Center - Mini Chart */}
<div className="flex-1 h-14 mx-6 relative">
<ResponsiveContainer width="100%" height="100%">
<LineChart
data={card.data}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 10,
}}
>
<YAxis domain={['dataMin', 'dataMax']} hide={true} />
<ReferenceLine y={0} stroke="var(--input)" strokeWidth={1} strokeDasharray="3 3" />
<Tooltip
cursor={{ stroke: card.color, strokeWidth: 1, strokeDasharray: '2 2' }}
position={{ x: undefined, y: undefined }}
offset={10}
allowEscapeViewBox={{ x: true, y: true }}
content={({ active, payload, coordinate }) => {
if (active && payload && payload.length && coordinate) {
const value = payload[0].value;
const formatValue = (val: number) => {
if (card.title === 'Revenue Variance') {
return val >= 0 ? `+$${val.toLocaleString()}` : `-$${Math.abs(val).toLocaleString()}`;
} else if (card.title === 'Conversion Change') {
return val >= 0 ? `+${val.toFixed(1)}%` : `${val.toFixed(1)}%`;
} else {
return val >= 0 ? `+${val}%` : `${val}%`;
}
};
// Smart positioning logic
const tooltipStyle: React.CSSProperties = {
transform:
coordinate.x && coordinate.x > 120 ? 'translateX(-100%)' : 'translateX(10px)',
marginTop: coordinate.y && coordinate.y > 30 ? '-40px' : '10px',
};
return (
<div
className="bg-background/95 backdrop-blur-sm border border-border shadow-xl rounded-lg p-2.5 pointer-events-none z-50"
style={tooltipStyle}
>
<p className="text-sm font-semibold text-foreground leading-tight mb-1.5">
{formatValue(value as number)}
</p>
<p className="text-xs text-muted-foreground leading-tight">{card.title}</p>
</div>
);
}
return null;
}}
/>
<Line
type="monotone"
dataKey="value"
stroke={card.color}
strokeWidth={2}
dot={{
r: 0,
strokeWidth: 0,
}}
activeDot={{
r: 5,
fill: card.color,
stroke: 'white',
strokeWidth: 2,
filter: `drop-shadow(0 0 6px ${card.color})`,
}}
/>
</LineChart>
</ResponsiveContainer>
</div>
{/* Right side - Target Currency */}
<div className="text-center">
<div className="text-lg font-semibold text-foreground">{card.targetValue}</div>
<div className="text-xs text-muted-foreground font-medium">{card.targetCurrency}</div>
</div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
</div>
);
}
Loading...
charts/line-charts/line-chart-9.tsx
'use client';
import React from 'react';
import { Card, CardContent } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { TrendingUp } from 'lucide-react';
import { CartesianGrid, ComposedChart, Line, ReferenceLine, XAxis, YAxis } from 'recharts';
// E-commerce revenue tracking with seasonal fluctuations
const portfolioData = [
{ date: 'Jan 1', value: 850, time: '20:00' },
{ date: 'Jan 2', value: 1100, time: '00:00' },
{ date: 'Jan 3', value: 1680, time: '04:00' },
{ date: 'Jan 4', value: 1490, time: '08:00' },
{ date: 'Jan 5', value: 2020, time: '12:00' },
{ date: 'Jan 6', value: 2080, time: '16:00' },
{ date: 'Jan 7', value: 2180, time: '20:00' },
{ date: 'Jan 8', value: 2250, time: '00:00' },
{ date: 'Jan 9', value: 2480, time: '04:00' },
{ date: 'Jan 10', value: 2290, time: '08:00' },
{ date: 'Jan 11', value: 2450, time: '12:00' },
{ date: 'Jan 12', value: 2380, time: '16:00' },
{ date: 'Jan 13', value: 2220, time: '20:00' },
{ date: 'Jan 14', value: 1980, time: '00:00' },
{ date: 'Jan 15', value: 1750, time: '04:00' },
{ date: 'Jan 16', value: 1620, time: '08:00' },
{ date: 'Jan 17', value: 1480, time: '12:00' },
{ date: 'Jan 18', value: 1580, time: '16:00' },
{ date: 'Jan 19', value: 1820, time: '20:00' },
{ date: 'Jan 20', value: 1950, time: '00:00' },
{ date: 'Jan 21', value: 2080, time: '04:00' },
{ date: 'Jan 22', value: 2220, time: '08:00' },
{ date: 'Jan 23', value: 2380, time: '12:00' },
{ date: 'Jan 24', value: 2550, time: '16:00' },
{ date: 'Jan 25', value: 2480, time: '20:00' },
{ date: 'Jan 26', value: 2720, time: '00:00' },
{ date: 'Jan 27', value: 2900, time: '04:00' },
{ date: 'Jan 28', value: 2550, time: '08:00' },
{ date: 'Jan 29', value: 2320, time: '12:00' },
{ date: 'Feb 15', value: 2250, time: '14:00' },
{ date: 'Mar 24', value: 1900, time: '16:00' },
];
// Chart configuration
const chartConfig = {
value: {
label: 'Balance',
color: 'var(--color-purple-500)',
},
} satisfies ChartConfig;
// Calculate portfolio metrics
const currentBalance = 24847.83;
const todaysPnL = 1249.0;
const pnlPercentage = 8;
const highValue = Math.max(...portfolioData.map((d) => d.value));
const lowValue = Math.min(...portfolioData.map((d) => d.value));
const change = -0.082;
// Custom Tooltip
interface TooltipProps {
active?: boolean;
payload?: Array<{
payload: {
date: string;
value: number;
};
}>;
label?: string;
}
const CustomTooltip = ({ active, payload }: TooltipProps) => {
if (active && payload && payload.length) {
const data = payload[0].payload;
return (
<div className="bg-popover border border-border rounded-lg p-3 shadow-lg">
<div className="text-sm text-muted-foreground mb-1">{data.date}</div>
<div className="flex items-center gap-2">
<div className="text-base font-bold">${(data.value * 10).toLocaleString()}.00</div>
<div className="text-[11px] text-emerald-600">+12.7%</div>
</div>
</div>
);
}
return null;
};
export default function LineChart9() {
return (
<div className="min-h-screen flex items-center justify-center p-6">
<Card className="w-full max-w-5xl">
<CardContent className="flex flex-col items-stretch gap-5">
{/* Header */}
<div className="mb-5">
<h1 className="text-base text-muted-foreground font-medium mb-1">Current Balance</h1>
<div className="flex flex-wrap items-baseline gap-1.5 sm:gap-3.5">
<span className="text-4xl font-bold">${currentBalance.toLocaleString()}</span>
<div className="flex items-center gap-1 text-emerald-600">
<TrendingUp className="w-4 h-4" />
<span className="font-medium">+12.7%</span>
<span className="text-muted-foreground font-normal">Last 24 hours</span>
</div>
</div>
</div>
<div className="grow">
{/* Stats Row */}
<div className="flex items-center justify-between flex-wrap gap-2.5 text-sm mb-2.5">
{/* Today's Sales */}
<div className="flex items-center gap-6">
<div className="flex items-center gap-2">
<span className="text-muted-foreground">Today's Sales:</span>
<span className="font-semibold">${todaysPnL.toLocaleString()}</span>
<div className="flex items-center gap-1 text-emerald-600">
<TrendingUp className="w-3 h-3" />
<span>(+{pnlPercentage}%)</span>
</div>
</div>
</div>
{/* Stats Row */}
<div className="flex items-center gap-6 text-muted-foreground">
<span>
High: <span className="text-sky-600 font-medium">{highValue.toLocaleString()}.08</span>
</span>
<span>
Low: <span className="text-yellow-600 font-medium">{lowValue.toLocaleString()}.42</span>
</span>
<span>
Change: <span className="text-red-600 font-medium">{change}%</span>
</span>
</div>
</div>
{/* Chart */}
<ChartContainer
config={chartConfig}
className="h-96 w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
>
<ComposedChart
data={portfolioData}
margin={{
top: 20,
right: 10,
left: 5,
bottom: 20,
}}
>
<defs>
<linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={chartConfig.value.color} stopOpacity={0.1} />
<stop offset="100%" stopColor={chartConfig.value.color} stopOpacity={0} />
</linearGradient>
<pattern id="dotGrid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="10" cy="10" r="1" fill="var(--input)" fillOpacity="0.3" />
</pattern>
<filter id="dotShadow" x="-50%" y="-50%" width="200%" height="200%">
<feDropShadow dx="2" dy="3" stdDeviation="3" floodColor="rgba(0,0,0,0.8)" />
</filter>
<filter id="lineShadow" x="-100%" y="-100%" width="300%" height="300%">
<feDropShadow dx="4" dy="6" stdDeviation="25" floodColor="rgba(59, 130, 246, 0.9)" />
</filter>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="url(#dotGrid)" style={{ pointerEvents: 'none' }} />
<CartesianGrid
strokeDasharray="4 8"
stroke="var(--input)"
strokeOpacity={1}
horizontal={true}
vertical={false}
/>
{/* Active tick reference line */}
<ReferenceLine x="Jan 17" stroke={chartConfig.value.color} strokeDasharray="4 4" strokeWidth={1} />
<XAxis
dataKey="date"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: chartConfig.value.color }}
tickMargin={15}
interval="preserveStartEnd"
tickCount={5}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: chartConfig.value.color }}
tickFormatter={(value) => `$${(value * 2).toLocaleString()}`}
tickMargin={15}
/>
<ChartTooltip
content={<CustomTooltip />}
cursor={{ strokeDasharray: '3 3', stroke: 'var(--muted-foreground)', strokeOpacity: 0.5 }}
/>
<Line
type="monotone"
dataKey="value"
stroke={chartConfig.value.color}
strokeWidth={2}
filter="url(#lineShadow)"
dot={(props) => {
const { cx, cy, payload } = props;
if (payload.date === 'Jan 17' || payload.value > 2800 || payload.value < 1000) {
return (
<circle
key={`dot-${payload.date}`}
cx={cx}
cy={cy}
r={6}
fill={chartConfig.value.color}
stroke="white"
strokeWidth={2}
filter="url(#dotShadow)"
/>
);
}
return <g key={`dot-${payload.date}`} />; // Return empty group for other points
}}
activeDot={{
r: 6,
fill: chartConfig.value.color,
stroke: 'white',
strokeWidth: 2,
filter: 'url(#dotShadow)',
}}
/>
</ComposedChart>
</ChartContainer>
</div>
</CardContent>
</Card>
</div>
);
}
Didn't find what you were looking for?
Suggest block