area-chart-4

PreviousNext
Docs
reuiblock

Preview

Loading preview…
registry/default/blocks/charts/area-charts/area-chart-4.tsx
'use client';

import React, { Fragment, useState } from 'react';
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 { CheckCircle, Clock, TrendingUp } from 'lucide-react';
import { Area, AreaChart, XAxis, YAxis } from 'recharts';

// Subscription revenue data for different periods
const revenueData = {
  day: [
    { period: '00:00', revenue: 1200 },
    { period: '04:00', revenue: 800 },
    { period: '08:00', revenue: 2100 },
    { period: '12:00', revenue: 3200 },
    { period: '16:00', revenue: 2800 },
    { period: '20:00', revenue: 1900 },
  ],
  week: [
    { period: 'Mon', revenue: 2400 },
    { period: 'Tue', revenue: 2800 },
    { period: 'Wed', revenue: 2200 },
    { period: 'Thu', revenue: 3200 },
    { period: 'Fri', revenue: 2900 },
    { period: 'Sat', revenue: 1800 },
    { period: 'Sun', revenue: 2600 },
  ],
  month: [
    { period: 'Week 1', revenue: 16800 },
    { period: 'Week 2', revenue: 18200 },
    { period: 'Week 3', revenue: 15600 },
    { period: 'Week 4', revenue: 19400 },
  ],
  year: [
    { period: 'Q1', revenue: 198000 },
    { period: 'Q2', revenue: 225000 },
    { period: 'Q3', revenue: 189000 },
    { period: 'Q4', revenue: 267000 },
  ],
};

const chartConfig = {
  revenue: {
    label: 'Revenue',
    color: 'var(--color-slate-600)',
  },
} 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">Revenue:</div>
        <div className="text-sm font-semibold">${payload[0].value.toLocaleString()}</div>
      </div>
    );
  }
  return null;
};

// Period configuration
const PERIODS = {
  day: { key: 'day', label: 'Day' },
  week: { key: 'week', label: 'Week' },
  month: { key: 'month', label: 'Month' },
  year: { key: 'year', label: 'Year' },
} as const;

type PeriodKey = keyof typeof PERIODS;

// Statistics data
const statisticsData = [
  {
    id: 'finished',
    label: 'Finished',
    value: '18',
    change: '+4 tasks',
    changeType: 'positive',
    icon: CheckCircle,
  },
  {
    id: 'tracked',
    label: 'Tracked',
    value: '31h',
    change: '-6 hours',
    changeType: 'negative',
    icon: Clock,
  },
  {
    id: 'efficiency',
    label: 'Efficiency',
    value: '93%',
    change: '+12%',
    changeType: 'positive',
    icon: TrendingUp,
  },
] as const;

export default function AreaChart4() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('day');

  // Get data for selected period
  const currentData = revenueData[selectedPeriod];

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full lg:max-w-4xl rounded-2xl">
        <CardHeader className="min-h-auto py-6 border-0">
          <CardTitle className="text-xl font-semibold">Orders Overview</CardTitle>
          <CardToolbar>
            <ToggleGroup
              type="single"
              value={selectedPeriod}
              variant="outline"
              onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
              className=""
            >
              {Object.values(PERIODS).map((period) => (
                <ToggleGroupItem
                  key={period.key}
                  value={period.key}
                  className="px-3.5 first:rounded-s-full! last:rounded-e-full!"
                >
                  {period.label}
                </ToggleGroupItem>
              ))}
            </ToggleGroup>
          </CardToolbar>
        </CardHeader>

        <CardContent className="px-0">
          {/* Statistics Blocks */}
          <div className="flex items-center flex-wrap px-6 gap-10 mb-10">
            {statisticsData.map((stat) => {
              const IconComponent = stat.icon;
              return (
                <Fragment key={stat.id}>
                  <div className="h-10 w-px bg-border hidden lg:block first:hidden" />
                  <div key={stat.id} className="flex items-center gap-3">
                    <div className="flex items-center">
                      <div className="flex items-center gap-3">
                        <div className="flex items-center justify-center w-10 h-10 rounded-full bg-muted/60 border border-muted-foreground/10">
                          <IconComponent className="w-4.5 text-muted-foreground" />
                        </div>
                        <div>
                          <div className="text-sm text-muted-foreground mb-0.5">{stat.label}</div>
                          <div className="flex items-center gap-2">
                            <span className="text-2xl font-bold">{stat.value}</span>
                            <span
                              className={`text-sm font-medium ${
                                stat.changeType === 'positive' ? 'text-emerald-600' : 'text-red-600'
                              }`}
                            >
                              {stat.change}
                            </span>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </Fragment>
              );
            })}
          </div>

          {/* Chart */}
          <div className="px-3.5 h-[300px] w-full">
            <ChartContainer
              config={chartConfig}
              className="h-full w-full overflow-visible [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
            >
              <AreaChart
                data={currentData}
                margin={{
                  top: 15,
                  right: 10,
                  left: 10,
                  bottom: 15,
                }}
                style={{ overflow: 'visible' }}
              >
                {/* SVG Pattern for chart area */}
                <defs>
                  {/* Grid pattern */}
                  <pattern id="gridPattern" x="0" y="0" width="20" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 20 0 L 0 0 0 20" fill="none" stroke="var(--input)" strokeWidth="0.5" strokeOpacity="1" />
                  </pattern>

                  {/* Area gradient fill */}
                  <linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={chartConfig.revenue.color} stopOpacity={0.3} />
                    <stop offset="100%" stopColor={chartConfig.revenue.color} stopOpacity={0.05} />
                  </linearGradient>

                  {/* Shadow filters for dots */}
                  <filter id="dotShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.4)" />
                  </filter>
                  <filter id="activeDotShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="3" dy="3" stdDeviation="4" floodColor="rgba(0,0,0,0.5)" />
                  </filter>
                </defs>

                {/* Background pattern for chart area only */}
                <rect
                  x="60px"
                  y="-20px"
                  width="calc(100% - 75px)"
                  height="calc(100% - 10px)"
                  fill="url(#gridPattern)"
                  style={{ pointerEvents: 'none' }}
                />

                <XAxis
                  dataKey="period"
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, fill: 'var(--muted-foreground)' }}
                  tickMargin={8}
                  interval={0}
                  includeHidden={true}
                />

                <YAxis
                  hide={true}
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
                  tickFormatter={(value) => `$${value >= 1000 ? `${(value / 1000).toFixed(0)}K` : value}`}
                  tickMargin={8}
                  domain={[0, 'dataMax']}
                  ticks={[0]}
                />

                <ChartTooltip
                  content={<CustomTooltip />}
                  cursor={{
                    stroke: chartConfig.revenue.color,
                    strokeWidth: 1,
                    strokeDasharray: 'none',
                  }}
                />

                <Area
                  type="monotone"
                  dataKey="revenue"
                  stroke={chartConfig.revenue.color}
                  strokeWidth={2}
                  fill="url(#areaGradient)"
                  dot={(props) => {
                    const { cx, cy, payload } = props;
                    // Show dots only for specific periods based on selected time range
                    const showDot =
                      (selectedPeriod === 'day' && (payload.period === '08:00' || payload.period === '16:00')) ||
                      (selectedPeriod === 'week' && (payload.period === 'Thu' || payload.period === 'Sat')) ||
                      (selectedPeriod === 'month' && payload.period === 'Week 2') ||
                      (selectedPeriod === 'year' && payload.period === 'Q2');

                    if (showDot) {
                      return (
                        <circle
                          key={`dot-${cx}-${cy}`}
                          cx={cx}
                          cy={cy}
                          r={4}
                          fill={chartConfig.revenue.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.revenue.color,
                    stroke: 'white',
                    strokeWidth: 2,
                    filter: 'url(#dotShadow)',
                  }}
                />
              </AreaChart>
            </ChartContainer>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}

Installation

npx shadcn@latest add @reui/area-chart-4

Usage

import { AreaChart4 } from "@/components/area-chart-4"
<AreaChart4 />