Vector Motion
Finance

Income Analytics Card - Revenue Analysis

Analyze income streams and revenue sources. Monitor income distribution and growth trends.

Finance Income Analytics Card

The Finance Income Analytics Card breaks down income by source and tracks trends over time, helping businesses understand revenue composition.

Preview

Installation

npx shadcn@latest add https://vectormotion.vercel.app/registry/finance-income-analytics-card.json
Finance Income Analytics Card
'use client'import React from 'react';import { BarChart as BarChartIcon, TrendingUp } from 'lucide-react';import { BarChart, Bar, ResponsiveContainer, Tooltip, XAxis, Cell, ReferenceLine } from 'recharts';import { motion } from 'motion/react';import { clsx, type ClassValue } from "clsx"import { twMerge } from "tailwind-merge"function cn(...inputs: ClassValue[]) {  return twMerge(clsx(inputs))}interface IncomeData {  month: string;  income: number;  target: number;}interface IncomeAnalyticsCardProps {  isInteractive?: boolean;  className?: string;  title?: string;  totalIncome?: string;  change?: string;  goalAchievement?: string;  data?: IncomeData[];}const DEFAULT_TITLE = "Income Analytics";const DEFAULT_TOTAL_INCOME = "$5.8k";const DEFAULT_CHANGE = "+15%";const DEFAULT_GOAL_ACHIEVEMENT = "92%";const DEFAULT_DATA: IncomeData[] = [  { month: 'Jun', income: 4200, target: 4000 },  { month: 'Jul', income: 4800, target: 4200 },  { month: 'Aug', income: 4100, target: 4400 }, // Miss  { month: 'Sep', income: 5200, target: 4600 },  { month: 'Oct', income: 4900, target: 4800 },  { month: 'Nov', income: 5800, target: 5000 },];export const IncomeAnalyticsCard: React.FC<IncomeAnalyticsCardProps> = ({  isInteractive = true,  className = "",  title = DEFAULT_TITLE,  totalIncome = DEFAULT_TOTAL_INCOME,  change = DEFAULT_CHANGE,  goalAchievement = DEFAULT_GOAL_ACHIEVEMENT,  data = DEFAULT_DATA,}) => {  const index = 41;  return (    <motion.div      layoutId={isInteractive ? `card-${index}-${title}` : undefined}      transition={{ duration: 0.4, ease: "easeOut" }}      className={cn(        "relative overflow-hidden rounded-xl border border-border bg-card text-card-foreground p-5 shadow-sm transition-all flex flex-col h-full group",        isInteractive ? "cursor-pointer hover:border-primary/50 hover:shadow-md" : "",        className      )}    >      <div className="mb-2 flex items-start justify-between relative z-10">        <div>          <h3 className="font-semibold text-lg text-foreground">            {title}          </h3>          <div className="flex items-center gap-2 mt-1">            <span className="text-2xl font-bold text-foreground">{totalIncome}</span>            <span className="text-xs font-medium text-emerald-600 bg-emerald-500/10 px-1.5 py-0.5 rounded-full flex items-center gap-0.5">              <TrendingUp className="w-3 h-3" /> {change}            </span>          </div>        </div>        <div className="rounded-lg bg-emerald-500/10 p-2 text-emerald-500">          <BarChartIcon className="h-5 w-5" />        </div>      </div>      <div className="relative z-10 flex-1 min-h-[140px]">        <div className="absolute inset-0">          <ResponsiveContainer width="100%" height="100%">            <BarChart data={data} barGap={0} barCategoryGap="20%">              <Tooltip                cursor={{ fill: 'transparent' }}                content={({ active, payload }) => {                  if (active && payload && payload.length) {                    return (                      <div className="rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-800 p-2 shadow-sm text-xs">                        <div className="font-bold text-foreground">${payload[0].value}</div>                        <div className="text-muted-foreground">Target: ${payload[0].payload.target}</div>                      </div>                    );                  }                  return null;                }}              />              <XAxis                dataKey="month"                axisLine={false}                tickLine={false}                tick={{ fill: '#71717a', fontSize: 10 }}                dy={10}              />              <Bar                dataKey="income"                radius={[4, 4, 0, 0]}                animationDuration={1500}              >                {data.map((entry, index) => (                  <Cell                    key={`cell-${index}`}                    fill={entry.income >= entry.target ? '#10b981' : '#f59e0b'}                    className={entry.income >= entry.target ? 'fill-emerald-500' : 'fill-amber-500'}                  />                ))}              </Bar>              <ReferenceLine y={0} stroke="#e4e4e7" />            </BarChart>          </ResponsiveContainer>        </div>      </div>      <div className="z-10 mt-2 flex items-center justify-between text-xs pt-2 border-t border-border">        <span className="text-muted-foreground">Goal Achievement</span>        <span className="font-bold text-foreground">{goalAchievement}</span>      </div>    </motion.div>  );};

Props

Prop

Type