Finance
Revenue Streams Card - Revenue Sources
Monitor revenue streams and source distribution. Track revenue diversification and growth sources.
Finance Revenue Streams Card
The Finance Revenue Streams Card breaks down revenue by different sources, helping businesses understand their revenue composition and diversification.
Preview
Installation
npx shadcn@latest add https://vectormotion.vercel.app/registry/finance-revenue-streams-card.jsonFinance Revenue Streams Card
'use client'import React from 'react';import { Layers, TrendingUp, PieChart as PieIcon } from 'lucide-react';import { motion } from 'motion/react';import { clsx, type ClassValue } from "clsx"import { twMerge } from "tailwind-merge"import { ResponsiveContainer, PieChart, Pie, Cell, Tooltip } from 'recharts';function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs))}interface RevenueData { name: string; value: number; fill: string; [key: string]: any;}interface RevenueStreamsCardProps { isInteractive?: boolean; className?: string; title?: string; totalRevenue?: string; revenueGrowth?: string; recurringLabel?: string; oneTimeLabel?: string; data?: RevenueData[];}const DEFAULT_TITLE = "Revenue Streams";const DEFAULT_TOTAL_REVENUE = "$69k";const DEFAULT_REVENUE_GROWTH = "+12%";const DEFAULT_RECURRING_LABEL = "Recurring: 65%";const DEFAULT_ONE_TIME_LABEL = "One-time: 35%";const DEFAULT_DATA: RevenueData[] = [ { name: 'SaaS', value: 45000, fill: '#10b981' }, // Emerald { name: 'Services', value: 15000, fill: '#3b82f6' }, // Blue { name: 'Marketplace', value: 9000, fill: '#f59e0b' }, // Amber];export const RevenueStreamsCard: React.FC<RevenueStreamsCardProps> = ({ isInteractive = true, className = "", title = DEFAULT_TITLE, totalRevenue = DEFAULT_TOTAL_REVENUE, revenueGrowth = DEFAULT_REVENUE_GROWTH, recurringLabel = DEFAULT_RECURRING_LABEL, oneTimeLabel = DEFAULT_ONE_TIME_LABEL, data = DEFAULT_DATA,}) => { const index = 23; 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">{totalRevenue}</span> <span className="text-xs font-medium text-emerald-500 bg-emerald-500/10 px-1.5 py-0.5 rounded-full flex items-center gap-0.5"> <TrendingUp className="w-3 h-3" /> {revenueGrowth} </span> </div> </div> <div className="rounded-lg bg-emerald-500/10 p-2 text-emerald-500"> <PieIcon className="h-5 w-5" /> </div> </div> <div className="relative z-10 flex-1 min-h-[140px] flex items-center"> <div className="w-1/2 h-full relative"> <ResponsiveContainer width="100%" height="100%"> <PieChart> <Pie data={data} cx="50%" cy="50%" innerRadius={35} outerRadius={55} paddingAngle={4} dataKey="value" stroke="none" cornerRadius={4} > {data.map((entry, index) => ( <Cell key={`cell-${index}`} fill={entry.fill} /> ))} </Pie> <Tooltip cursor={false} 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"> <span className="font-bold" style={{ color: payload[0].payload.fill }}>{payload[0].name}</span>: ${payload[0].value.toLocaleString()} </div> ); } return null; }} /> </PieChart> </ResponsiveContainer> <div className="absolute inset-0 flex items-center justify-center pointer-events-none"> <Layers className="w-4 h-4 text-muted-foreground" /> </div> </div> <div className="w-1/2 pl-2 space-y-2"> {data.map((item) => ( <div key={item.name} className="flex flex-col"> <div className="flex items-center gap-1.5 text-xs text-muted-foreground"> <div className="w-2 h-2 rounded-full" style={{ backgroundColor: item.fill }} /> {item.name} </div> <div className="pl-3.5 text-sm font-semibold text-foreground"> ${(item.value / 1000).toFixed(0)}k <span className="text-[10px] text-zinc-400 font-normal">({((item.value / 69000) * 100).toFixed(0)}%)</span> </div> </div> ))} </div> </div> <div className="mt-2 pt-2 border-t border-border flex justify-between items-center text-xs text-muted-foreground"> <span>{recurringLabel}</span> <span>{oneTimeLabel}</span> </div> </motion.div> );};Props
Prop
Type
Usage
This component is a demo card displaying revenue streams data with animated visualizations and dark mode support.