CoolFace
Apppublic

ModelMuse02/AI_Sales_Forecasting

sourceHugging Faceupdated 7mo agoView on Hugging Face
0likes
Charts.tsx545 linesDownload Raw Back to root
1/**2 * Charts Component3 * Interactive visualizations using Recharts4 */5 6import React from 'react';7import {8  XAxis,9  YAxis,10  CartesianGrid,11  Tooltip,12  Legend,13  ResponsiveContainer,14  AreaChart,15  Area,16  BarChart,17  Bar,18  PieChart,19  Pie,20  Cell,21  ComposedChart,22  Line,23} from 'recharts';24import {25  MonthlyAggregation,26  ForecastResult,27  ProductAggregation,28  KPIMetrics,29} from '../types';30import { formatCurrency } from '../modules/kpiCalculation';31 32const COLORS = [33  '#3B82F6', // blue34  '#10B981', // green35  '#F59E0B', // amber36  '#EF4444', // red37  '#8B5CF6', // purple38  '#EC4899', // pink39  '#06B6D4', // cyan40  '#84CC16', // lime41];42 43interface ChartContainerProps {44  title: string;45  subtitle?: string;46  children: React.ReactNode;47}48 49const ChartContainer: React.FC<ChartContainerProps> = ({ title, subtitle, children }) => (50  <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">51    <div className="mb-4">52      <h3 className="text-lg font-semibold text-gray-900">{title}</h3>53      {subtitle && <p className="text-sm text-gray-500 mt-1">{subtitle}</p>}54    </div>55    <div className="h-80">56      {children}57    </div>58  </div>59);60 61// eslint-disable-next-line @typescript-eslint/no-explicit-any62const currencyFormatter = (value: any) => {63  if (value === undefined || value === null) return '';64  const numVal = typeof value === 'number' ? value : parseFloat(value);65  return formatCurrency(numVal);66};67 68// eslint-disable-next-line @typescript-eslint/no-explicit-any69const percentFormatter = (value: any) => {70  if (value === undefined || value === null) return '';71  const numVal = typeof value === 'number' ? value : parseFloat(value);72  return `${numVal > 0 ? '+' : ''}${numVal.toFixed(1)}%`;73};74 75interface RevenueTrendChartProps {76  data: MonthlyAggregation[];77}78 79export const RevenueTrendChart: React.FC<RevenueTrendChartProps> = ({ data }) => {80  const chartData = data.map(d => ({81    period: d.period,82    revenue: d.totalRevenue,83    units: d.totalUnits,84  }));85 86  return (87    <ChartContainer 88      title="Revenue Trend" 89      subtitle="Monthly revenue over time"90    >91      <ResponsiveContainer width="100%" height="100%">92        <ComposedChart data={chartData}>93          <CartesianGrid strokeDasharray="3 3" stroke="#E5E7EB" />94          <XAxis 95            dataKey="period" 96            tick={{ fontSize: 12 }} 97            tickLine={false}98            axisLine={{ stroke: '#E5E7EB' }}99          />100          <YAxis 101            yAxisId="left"102            tick={{ fontSize: 12 }} 103            tickLine={false}104            axisLine={{ stroke: '#E5E7EB' }}105            tickFormatter={(value: number) => `$${(value / 1000).toFixed(0)}K`}106          />107          <YAxis 108            yAxisId="right"109            orientation="right"110            tick={{ fontSize: 12 }} 111            tickLine={false}112            axisLine={{ stroke: '#E5E7EB' }}113          />114          <Tooltip 115            formatter={currencyFormatter}116            contentStyle={{ 117              backgroundColor: 'white', 118              border: '1px solid #E5E7EB',119              borderRadius: '8px',120            }}121          />122          <Legend />123          <Bar 124            yAxisId="left"125            dataKey="revenue" 126            name="Revenue" 127            fill="#3B82F6" 128            radius={[4, 4, 0, 0]}129            opacity={0.8}130          />131          <Line 132            yAxisId="right"133            type="monotone" 134            dataKey="units" 135            name="Units Sold"136            stroke="#10B981" 137            strokeWidth={2}138            dot={false}139          />140        </ComposedChart>141      </ResponsiveContainer>142    </ChartContainer>143  );144};145 146interface ForecastChartProps {147  forecast: ForecastResult;148}149 150export const ForecastChart: React.FC<ForecastChartProps> = ({ forecast }) => {151  const chartData = forecast.combinedData.map(d => ({152    period: d.dateStr,153    actual: d.actual,154    predicted: Math.round(d.predicted),155    upperBound: Math.round(d.upperBound),156    lowerBound: Math.round(d.lowerBound),157    isHistorical: d.isHistorical,158  }));159 160  return (161    <ChartContainer 162      title="Revenue Forecast" 163      subtitle={`12-month forecast with ${(forecast.metrics.r2 * 100).toFixed(0)}% confidence`}164    >165      <ResponsiveContainer width="100%" height="100%">166        <AreaChart data={chartData}>167          <defs>168            <linearGradient id="colorConfidence" x1="0" y1="0" x2="0" y2="1">169              <stop offset="5%" stopColor="#3B82F6" stopOpacity={0.2}/>170              <stop offset="95%" stopColor="#3B82F6" stopOpacity={0.05}/>171            </linearGradient>172          </defs>173          <CartesianGrid strokeDasharray="3 3" stroke="#E5E7EB" />174          <XAxis 175            dataKey="period" 176            tick={{ fontSize: 11 }} 177            tickLine={false}178            axisLine={{ stroke: '#E5E7EB' }}179            interval="preserveStartEnd"180          />181          <YAxis 182            tick={{ fontSize: 12 }} 183            tickLine={false}184            axisLine={{ stroke: '#E5E7EB' }}185            tickFormatter={(value: number) => `$${(value / 1000).toFixed(0)}K`}186          />187          <Tooltip 188            formatter={currencyFormatter}189            contentStyle={{ 190              backgroundColor: 'white', 191              border: '1px solid #E5E7EB',192              borderRadius: '8px',193            }}194          />195          <Legend />196          <Area197            type="monotone"198            dataKey="upperBound"199            stroke="none"200            fill="url(#colorConfidence)"201            name="Confidence Range"202          />203          <Area204            type="monotone"205            dataKey="lowerBound"206            stroke="none"207            fill="white"208            legendType="none"209          />210          <Line 211            type="monotone" 212            dataKey="actual" 213            name="Actual Revenue"214            stroke="#10B981" 215            strokeWidth={2}216            dot={{ r: 3 }}217            connectNulls218          />219          <Line 220            type="monotone" 221            dataKey="predicted" 222            name="Forecast"223            stroke="#3B82F6" 224            strokeWidth={2}225            strokeDasharray="5 5"226            dot={false}227          />228        </AreaChart>229      </ResponsiveContainer>230    </ChartContainer>231  );232};233 234interface ProductChartProps {235  data: ProductAggregation[];236}237 238export const ProductPieChart: React.FC<ProductChartProps> = ({ data }) => {239  const chartData = data.slice(0, 6).map(d => ({240    name: d.product,241    value: d.totalRevenue,242    percentage: d.percentage,243  }));244 245  return (246    <ChartContainer 247      title="Revenue by Product" 248      subtitle="Product contribution to total revenue"249    >250      <ResponsiveContainer width="100%" height="100%">251        <PieChart>252          <Pie253            data={chartData}254            cx="50%"255            cy="50%"256            labelLine={false}257            outerRadius={120}258            fill="#8884d8"259            dataKey="value"260            label={({ percent }) => (percent ?? 0) > 0.05 ? `${((percent ?? 0) * 100).toFixed(0)}%` : ''}261          >262            {chartData.map((_, index) => (263              <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />264            ))}265          </Pie>266          <Tooltip 267            formatter={currencyFormatter}268            contentStyle={{ 269              backgroundColor: 'white', 270              border: '1px solid #E5E7EB',271              borderRadius: '8px',272            }}273          />274          <Legend 275            layout="vertical" 276            align="right" 277            verticalAlign="middle"278          />279        </PieChart>280      </ResponsiveContainer>281    </ChartContainer>282  );283};284 285interface GrowthChartProps {286  kpis: KPIMetrics;287  monthlyData: MonthlyAggregation[];288}289 290export const GrowthChart: React.FC<GrowthChartProps> = ({ kpis, monthlyData }) => {291  const chartData = monthlyData.slice(1).map((d, i) => ({292    period: d.period,293    growthRate: kpis.monthlyGrowthRates[i] || 0,294    revenue: d.totalRevenue,295  }));296 297  return (298    <ChartContainer 299      title="Monthly Growth Rate" 300      subtitle="Month-over-month revenue change"301    >302      <ResponsiveContainer width="100%" height="100%">303        <BarChart data={chartData}>304          <CartesianGrid strokeDasharray="3 3" stroke="#E5E7EB" />305          <XAxis 306            dataKey="period" 307            tick={{ fontSize: 11 }} 308            tickLine={false}309            axisLine={{ stroke: '#E5E7EB' }}310            interval="preserveStartEnd"311          />312          <YAxis 313            tick={{ fontSize: 12 }} 314            tickLine={false}315            axisLine={{ stroke: '#E5E7EB' }}316            tickFormatter={(value: number) => `${value.toFixed(0)}%`}317          />318          <Tooltip 319            formatter={percentFormatter}320            contentStyle={{ 321              backgroundColor: 'white', 322              border: '1px solid #E5E7EB',323              borderRadius: '8px',324            }}325          />326          <Legend />327          <Bar 328            dataKey="growthRate" 329            name="Growth Rate"330            radius={[4, 4, 0, 0]}331          >332            {chartData.map((entry, index) => (333              <Cell 334                key={`cell-${index}`} 335                fill={entry.growthRate >= 0 ? '#10B981' : '#EF4444'} 336              />337            ))}338          </Bar>339        </BarChart>340      </ResponsiveContainer>341    </ChartContainer>342  );343};344 345interface SeasonalityChartProps {346  forecast: ForecastResult;347}348 349export const SeasonalityChart: React.FC<SeasonalityChartProps> = ({ forecast }) => {350  const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];351  352  const chartData = monthNames.map((name, index) => ({353    month: name,354    factor: ((forecast.seasonality.pattern[index] || 1) - 1) * 100,355    baseline: 0,356  }));357 358  return (359    <ChartContainer 360      title="Seasonality Pattern" 361      subtitle="Monthly deviation from average"362    >363      <ResponsiveContainer width="100%" height="100%">364        <BarChart data={chartData}>365          <CartesianGrid strokeDasharray="3 3" stroke="#E5E7EB" />366          <XAxis 367            dataKey="month" 368            tick={{ fontSize: 12 }} 369            tickLine={false}370            axisLine={{ stroke: '#E5E7EB' }}371          />372          <YAxis 373            tick={{ fontSize: 12 }} 374            tickLine={false}375            axisLine={{ stroke: '#E5E7EB' }}376            tickFormatter={(value: number) => `${value > 0 ? '+' : ''}${value.toFixed(0)}%`}377          />378          <Tooltip 379            formatter={percentFormatter}380            contentStyle={{ 381              backgroundColor: 'white', 382              border: '1px solid #E5E7EB',383              borderRadius: '8px',384            }}385          />386          <Bar 387            dataKey="factor" 388            name="Seasonal Factor"389            radius={[4, 4, 0, 0]}390          >391            {chartData.map((entry, index) => (392              <Cell 393                key={`cell-${index}`} 394                fill={entry.factor >= 0 ? '#3B82F6' : '#F59E0B'} 395              />396            ))}397          </Bar>398        </BarChart>399      </ResponsiveContainer>400    </ChartContainer>401  );402};403 404interface ProductBarChartProps {405  data: ProductAggregation[];406}407 408export const ProductBarChart: React.FC<ProductBarChartProps> = ({ data }) => {409  const chartData = data.slice(0, 8).map(d => ({410    product: d.product.length > 15 ? d.product.slice(0, 15) + '...' : d.product,411    revenue: d.totalRevenue,412    units: d.totalUnits,413    aov: d.averageOrderValue,414  }));415 416  return (417    <ChartContainer 418      title="Product Performance" 419      subtitle="Revenue and units by product"420    >421      <ResponsiveContainer width="100%" height="100%">422        <BarChart data={chartData} layout="vertical">423          <CartesianGrid strokeDasharray="3 3" stroke="#E5E7EB" />424          <XAxis 425            type="number"426            tick={{ fontSize: 12 }} 427            tickLine={false}428            axisLine={{ stroke: '#E5E7EB' }}429            tickFormatter={(value: number) => `$${(value / 1000).toFixed(0)}K`}430          />431          <YAxis 432            type="category"433            dataKey="product" 434            tick={{ fontSize: 11 }} 435            tickLine={false}436            axisLine={{ stroke: '#E5E7EB' }}437            width={100}438          />439          <Tooltip 440            formatter={currencyFormatter}441            contentStyle={{ 442              backgroundColor: 'white', 443              border: '1px solid #E5E7EB',444              borderRadius: '8px',445            }}446          />447          <Legend />448          <Bar 449            dataKey="revenue" 450            name="Revenue" 451            fill="#3B82F6" 452            radius={[0, 4, 4, 0]}453          />454        </BarChart>455      </ResponsiveContainer>456    </ChartContainer>457  );458};459 460interface ProfitMarginChartProps {461  data: MonthlyAggregation[];462}463 464export const ProfitMarginChart: React.FC<ProfitMarginChartProps> = ({ data }) => {465  const chartData = data466    .filter(d => d.totalProfit !== null)467    .map(d => ({468      period: d.period,469      revenue: d.totalRevenue,470      profit: d.totalProfit || 0,471      margin: d.totalRevenue > 0 ? ((d.totalProfit || 0) / d.totalRevenue) * 100 : 0,472    }));473 474  if (chartData.length === 0) return null;475 476  return (477    <ChartContainer 478      title="Profit Analysis" 479      subtitle="Revenue, profit, and margin over time"480    >481      <ResponsiveContainer width="100%" height="100%">482        <ComposedChart data={chartData}>483          <CartesianGrid strokeDasharray="3 3" stroke="#E5E7EB" />484          <XAxis 485            dataKey="period" 486            tick={{ fontSize: 11 }} 487            tickLine={false}488            axisLine={{ stroke: '#E5E7EB' }}489            interval="preserveStartEnd"490          />491          <YAxis 492            yAxisId="left"493            tick={{ fontSize: 12 }} 494            tickLine={false}495            axisLine={{ stroke: '#E5E7EB' }}496            tickFormatter={(value: number) => `$${(value / 1000).toFixed(0)}K`}497          />498          <YAxis 499            yAxisId="right"500            orientation="right"501            tick={{ fontSize: 12 }} 502            tickLine={false}503            axisLine={{ stroke: '#E5E7EB' }}504            tickFormatter={(value: number) => `${value.toFixed(0)}%`}505          />506          <Tooltip 507            formatter={currencyFormatter}508            contentStyle={{ 509              backgroundColor: 'white', 510              border: '1px solid #E5E7EB',511              borderRadius: '8px',512            }}513          />514          <Legend />515          <Bar 516            yAxisId="left"517            dataKey="revenue" 518            name="Revenue" 519            fill="#3B82F6" 520            radius={[4, 4, 0, 0]}521            opacity={0.8}522          />523          <Bar 524            yAxisId="left"525            dataKey="profit" 526            name="Profit" 527            fill="#10B981" 528            radius={[4, 4, 0, 0]}529            opacity={0.8}530          />531          <Line 532            yAxisId="right"533            type="monotone" 534            dataKey="margin" 535            name="Margin %"536            stroke="#F59E0B" 537            strokeWidth={2}538            dot={false}539          />540        </ComposedChart>541      </ResponsiveContainer>542    </ChartContainer>543  );544};545