Vector Motion
E-Commerce

Revenue Forecast Card - Revenue Projections

Forecast future revenue based on historical trends. Plan budgets and set sales targets.

Revenue Forecast Card

The Revenue Forecast Card Project future revenue with predictive analytics.

Preview

Installation

ash npx shadcn@latest add https://vectormotion.vercel.app/registry/revenue-forecast-card.json

Revenue Forecast Card
'use client';import React from "react";import { TrendingUp, Calendar } from "lucide-react";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 ForecastItem {  month: string;  actual: number | null;  forecast: number | null;  [key: string]: any;}interface RevenueForecastCardProps {  className?: string;  title?: string;  description?: string;  projectedRevenue?: string;  growthRate?: string;  forecast?: ForecastItem[];}const DEFAULT_TITLE = "Revenue Forecast";const DEFAULT_DESCRIPTION = "Next 3 Months";const DEFAULT_PROJECTED_REVENUE = "$102K";const DEFAULT_GROWTH_RATE = "+15.9%";const DEFAULT_FORECAST: ForecastItem[] = [  { month: "Jan", actual: 85000, forecast: null },  { month: "Feb", actual: 92000, forecast: null },  { month: "Mar", actual: 88000, forecast: 95000 },  { month: "Apr", actual: null, forecast: 98000 },  { month: "May", actual: null, forecast: 102000 },];export const RevenueForecastCard: React.FC<RevenueForecastCardProps> = ({  className = "",  title = DEFAULT_TITLE,  description = DEFAULT_DESCRIPTION,  projectedRevenue = DEFAULT_PROJECTED_REVENUE,  growthRate = DEFAULT_GROWTH_RATE,  forecast = DEFAULT_FORECAST,}) => {  const isInteractive = true;  const index = 6;  const maxValue = 110000;  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-6 shadow-sm transition-all flex flex-col group",        isInteractive          ? "cursor-pointer hover:border-zinc-300 dark:hover:border-zinc-700"          : "",        className,      )}    >      <div className="mb-4 flex items-start justify-between relative z-10">        <div>          <h3 className="font-semibold text-lg tracking-tight text-foreground">            {title}          </h3>          {description && (            <p className="text-sm text-muted-foreground mt-1">{description}</p>          )}        </div>        <div className="rounded-full bg-zinc-100 dark:bg-zinc-800 p-2 text-foreground flex items-center justify-center">          <TrendingUp className="h-5 w-5 text-blue-500" />        </div>      </div>      <div className="relative z-10 flex-1">        <div className="grid grid-cols-2 gap-4 mb-4">          <div>            <div className="text-xs text-muted-foreground mb-1">              Projected (May)            </div>            <div className="text-2xl font-bold text-foreground">              {projectedRevenue}            </div>          </div>          <div>            <div className="text-xs text-muted-foreground mb-1">              Growth Rate            </div>            <div className="text-2xl font-bold text-emerald-500">              {growthRate}            </div>          </div>        </div>        <div className="flex items-end justify-between h-32 gap-2">          {forecast.map((item, idx) => {            const value = item.actual || item.forecast || 0;            const height = (value / maxValue) * 100;            const isForecast = item.forecast && !item.actual;            return (              <div                key={idx}                className="flex-1 flex flex-col items-center gap-2"              >                <motion.div                  initial={{ height: 0 }}                  animate={{ height: `${height}%` }}                  transition={{ duration: 0.8, delay: idx * 0.1 }}                  className={`w-full rounded-t-lg relative ${isForecast                      ? "bg-gradient-to-t from-blue-300 to-blue-200 dark:from-blue-600 dark:to-blue-500 opacity-60 border-2 border-dashed border-blue-400"                      : "bg-gradient-to-t from-blue-500 to-blue-400"                    }`}                >                  <div className="absolute -top-6 left-1/2 -translate-x-1/2 text-xs font-medium text-foreground">                    ${(value / 1000).toFixed(0)}K                  </div>                </motion.div>                <span className="text-xs text-muted-foreground">                  {item.month}                </span>              </div>            );          })}        </div>        <div className="mt-4 flex items-center gap-4 text-xs">          <div className="flex items-center gap-2">            <div className="w-3 h-3 bg-blue-500 rounded"></div>            <span className="text-muted-foreground">Actual</span>          </div>          <div className="flex items-center gap-2">            <div className="w-3 h-3 bg-blue-300 dark:bg-blue-600 opacity-60 border-2 border-dashed border-blue-400 rounded"></div>            <span className="text-muted-foreground">Forecast</span>          </div>        </div>      </div>    </motion.div>  );};

Usage

This component is a data-rich dashboard card displaying e-commerce metrics with animated visualizations and dark mode support. Perfect for dashboards, landing pages, and analytics interfaces.

Prop

Type