Finance
Gross Profit Card - Profitability
Monitor gross profit and profit margins. Track cost of goods sold and gross profit trends.
Finance Gross Profit Card
The Finance Gross Profit Card displays gross profit and margin metrics, helping businesses assess profitability before operating expenses.
Preview
Installation
npx shadcn@latest add https://vectormotion.vercel.app/registry/finance-gross-profit-card.jsonFinance Gross Profit Card
'use client'import React from 'react';import { DollarSign, TrendingUp, ArrowRight } from 'lucide-react';import { ResponsiveContainer, AreaChart, Area, Tooltip, XAxis } 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 ProfitData { month: string; val: number;}interface GrossProfitCardProps { isInteractive?: boolean; className?: string; title?: string; amount?: string; perLabel?: string; change?: string; marginLabel?: string; data?: ProfitData[];}const DEFAULT_TITLE = "Gross Profit";const DEFAULT_AMOUNT = "$50k";const DEFAULT_PER_LABEL = "/mo";const DEFAULT_CHANGE = "+22% YoY";const DEFAULT_MARGIN_LABEL = "Margin: 68%";const DEFAULT_DATA: ProfitData[] = [ { month: 'Jan', val: 30 }, { month: 'Feb', val: 35 }, { month: 'Mar', val: 32 }, { month: 'Apr', val: 40 }, { month: 'May', val: 45 }, { month: 'Jun', val: 50 },];export const GrossProfitCard: React.FC<GrossProfitCardProps> = ({ isInteractive = true, className = "", title = DEFAULT_TITLE, amount = DEFAULT_AMOUNT, perLabel = DEFAULT_PER_LABEL, change = DEFAULT_CHANGE, marginLabel = DEFAULT_MARGIN_LABEL, data = DEFAULT_DATA,}) => { const index = 39; 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">{amount}</span> <span className="text-xs text-muted-foreground">{perLabel}</span> </div> </div> <div className="rounded-lg bg-emerald-500/10 p-2 text-emerald-500"> <DollarSign className="h-5 w-5" /> </div> </div> <div className="relative z-10 flex-1 min-h-[100px]"> <div className="h-full w-full absolute inset-0"> <ResponsiveContainer width="100%" height="100%"> <AreaChart data={data}> <defs> <linearGradient id="colorProfit" x1="0" y1="0" x2="0" y2="1"> <stop offset="5%" stopColor="#10b981" stopOpacity={0.3} /> <stop offset="95%" stopColor="#10b981" stopOpacity={0} /> </linearGradient> </defs> <Tooltip cursor={{ stroke: '#10b981', strokeWidth: 1, strokeDasharray: '3 3' }} contentStyle={{ backgroundColor: 'var(--tooltip-bg)', borderRadius: '8px', border: 'none', boxShadow: '0 4px 12px rgba(0,0,0,0.1)' }} formatter={(value) => [`$${value}k`, 'Profit']} labelStyle={{ display: 'none' }} /> <Area type="monotone" dataKey="val" stroke="#10b981" fill="url(#colorProfit)" strokeWidth={2} animationDuration={1500} /> </AreaChart> </ResponsiveContainer> </div> </div> <div className="relative z-10 mt-2 flex items-center justify-between text-xs pt-2 border-t border-border"> <div className="flex items-center gap-1.5 text-emerald-500 font-medium"> <TrendingUp className="w-3 h-3" /> <span>{change}</span> </div> <div className="flex items-center gap-1 text-muted-foreground"> <span>{marginLabel}</span> </div> </div> </motion.div> );};Props
Prop
Type