import React, { useState, useMemo } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, BarChart, Bar, ComposedChart, Area } from 'recharts'; export default function TikTokDashboard() { // Product & Cost Inputs const [sellingPrice, setSellingPrice] = useState(120); const [productCost, setProductCost] = useState(34); const [shippingCost, setShippingCost] = useState(6); const [packagingCost, setPackagingCost] = useState(5); // TikTok Fees const [platformCommission, setPlatformCommission] = useState(5); const [paymentProcessing, setPaymentProcessing] = useState(1); const [affiliateCommission, setAffiliateCommission] = useState(15); // Inventory const [initialStock, setInitialStock] = useState(130); const [initialSamples, setInitialSamples] = useState(40); // Creator Variables const [videosPerCreatorPerMonth, setVideosPerCreatorPerMonth] = useState(5); const [conversionRate, setConversionRate] = useState(0.5); const [creatorRetentionRate, setCreatorRetentionRate] = useState(80); // Video Views Distribution (realistic TikTok distribution) const [flopViews, setFlopViews] = useState(500); // 70% of videos const [flopPercent, setFlopPercent] = useState(80); const [averageViews, setAverageViews] = useState(5000); // 20% of videos const [averagePercent, setAveragePercent] = useState(15); const [viralViews, setViralViews] = useState(100000); // 10% of videos // viralPercent is calculated as 100 - flopPercent - averagePercent // Cash Flow & Reinvestment const [startingCash, setStartingCash] = useState(0); const [returnRate, setReturnRate] = useState(3); const [sampleSeedingPercent, setSampleSeedingPercent] = useState(20); const [sampleCost, setSampleCost] = useState(40); const [monthlyExpenses, setMonthlyExpenses] = useState(1000); // Calculations const calculations = useMemo(() => { const sellableStock = initialStock - initialSamples; const totalFeesPercent = (platformCommission + paymentProcessing + affiliateCommission) / 100; const totalCostPerUnit = productCost + shippingCost + packagingCost; const profitPerUnit = sellingPrice * (1 - returnRate/100) * (1 - totalFeesPercent) - totalCostPerUnit; // Calculate viral percent (remainder) const viralPercent = Math.max(0, 100 - flopPercent - averagePercent); // Weighted average views per video const avgViewsPerVideo = (flopViews * flopPercent + averageViews * averagePercent + viralViews * viralPercent) / 100; let months = []; let cumulativeProfit = 0; let cumulativeUnits = 0; let cumulativeRevenue = 0; for (let m = 1; m <= 12; m++) { const prevMonth = months[m - 2] || { stockEnd: sellableStock, cashEnd: startingCash, unitsOrdered: 0, netRevenue: 0, totalFees: 0, totalActiveCreators: 0, cashAfterSeeding: startingCash, cashAfterExpenses: startingCash, cashAfterRestock: startingCash }; // --- CREATOR CALCULATIONS (CUMULATIVE) --- // Month 1: Start with initial samples sent // Month 2+: Previous creators (with retention) + new creators from seeding let activeCreatorsFromPast = 0; let newCreatorsThisMonth = 0; if (m === 1) { newCreatorsThisMonth = initialSamples; } else { // Existing creators with retention decay activeCreatorsFromPast = Math.floor(prevMonth.totalActiveCreators * (creatorRetentionRate / 100)); // New creators from sample seeding (using cash available after expenses) const cashForSeeding = Math.max(0, prevMonth.cashAfterExpenses) * (sampleSeedingPercent / 100); newCreatorsThisMonth = Math.floor(cashForSeeding / sampleCost); } const totalActiveCreators = activeCreatorsFromPast + newCreatorsThisMonth; const seedingCost = newCreatorsThisMonth * sampleCost; // --- VIDEO & DEMAND CALCULATIONS --- const totalVideos = totalActiveCreators * videosPerCreatorPerMonth; // Calculate views with distribution (add slight randomness per month) const monthSeed = m * 0.1; // Deterministic "randomness" based on month const varianceFactor = 0.8 + (Math.sin(monthSeed * 7) * 0.4); // 0.6 to 1.2 variance const flopVideoCount = Math.floor(totalVideos * (flopPercent / 100)); const avgVideoCount = Math.floor(totalVideos * (averagePercent / 100)); const viralVideoCount = totalVideos - flopVideoCount - avgVideoCount; const totalViews = Math.round( (flopVideoCount * flopViews + avgVideoCount * averageViews + viralVideoCount * viralViews) * varianceFactor ); const potentialSales = Math.round(totalViews * (conversionRate / 100)); // --- CASH & STOCK (with settlement delay) --- // Cash from previous month's sales arrives this month const cashFromSales = m === 1 ? 0 : (prevMonth.netRevenue - prevMonth.totalFees); // Stock arriving from previous order (1 month production delay) const stockArriving = m <= 1 ? 0 : prevMonth.unitsOrdered; // Available stock at start const stockStart = m === 1 ? sellableStock : prevMonth.stockEnd + stockArriving; // Units sold (limited by stock) const unitsSold = Math.min(stockStart, potentialSales); const stockEnd = stockStart - unitsSold; // Cash available (before expenses, seeding and restock) const cashStart = m === 1 ? startingCash : prevMonth.cashAfterRestock + cashFromSales; // Deduct monthly personal expenses first const expensesTaken = Math.min(monthlyExpenses, Math.max(0, cashStart)); const cashAfterExpenses = cashStart - expensesTaken; // Cash after seeding new creators const cashAfterSeeding = cashAfterExpenses - seedingCost; // Order as many units as affordable with remaining cash const unitsAffordable = Math.floor(Math.max(0, cashAfterSeeding) / productCost); const unitsOrdered = unitsAffordable > 0 ? unitsAffordable : 0; const restockCost = unitsOrdered * productCost; const cashAfterRestock = cashAfterSeeding - restockCost; // --- REVENUE & COSTS --- const grossRevenue = unitsSold * sellingPrice; const returns = grossRevenue * (returnRate / 100); const netRevenue = grossRevenue - returns; const platformFee = netRevenue * (platformCommission / 100); const paymentFee = netRevenue * (paymentProcessing / 100); const affiliateFee = netRevenue * (affiliateCommission / 100); const totalFees = platformFee + paymentFee + affiliateFee; const cogs = unitsSold * productCost; const shippingTotal = unitsSold * (shippingCost + packagingCost); const totalCosts = cogs + shippingTotal; // --- PROFIT --- const monthlyProfit = netRevenue - totalFees - totalCosts; const margin = netRevenue > 0 ? (monthlyProfit / netRevenue) * 100 : 0; // --- CUMULATIVE --- cumulativeProfit += monthlyProfit; cumulativeUnits += unitsSold; cumulativeRevenue += grossRevenue; months.push({ month: `M${m}`, monthNum: m, // Creators activeCreatorsFromPast, newCreatorsThisMonth, totalActiveCreators, seedingCost, // Content totalVideos, flopVideoCount, avgVideoCount, viralVideoCount, totalViews, avgViewsThisMonth: totalVideos > 0 ? Math.round(totalViews / totalVideos) : 0, potentialSales, // Target views to sell available stock targetViews: stockStart > 0 ? Math.round(stockStart / (conversionRate / 100)) : 0, // Stock stockStart, stockArriving, unitsSold, stockEnd, unitsOrdered, restockCost, // Cash cashStart, expensesTaken, cashAfterExpenses, cashAfterSeeding, cashAfterRestock, // Revenue grossRevenue, netRevenue, totalFees, totalCosts, monthlyProfit, margin, // Cumulative cumulativeProfit, cumulativeUnits, cumulativeRevenue }); } return { months, sellableStock, profitPerUnit, totalProfit: cumulativeProfit, totalUnits: cumulativeUnits, totalRevenue: cumulativeRevenue, totalCreatorsRecruited: months.reduce((sum, m) => sum + m.newCreatorsThisMonth, 0), totalExpensesTaken: months.reduce((sum, m) => sum + m.expensesTaken, 0), viralPercent, avgViewsPerVideo }; }, [sellingPrice, productCost, shippingCost, packagingCost, platformCommission, paymentProcessing, affiliateCommission, initialStock, initialSamples, videosPerCreatorPerMonth, flopViews, flopPercent, averageViews, averagePercent, viralViews, conversionRate, creatorRetentionRate, startingCash, returnRate, sampleSeedingPercent, sampleCost, monthlyExpenses]); const formatCurrency = (val) => `$${val.toLocaleString('en-US', {maximumFractionDigits: 0})}`; const formatNumber = (val) => val.toLocaleString('en-US', {maximumFractionDigits: 0}); const Slider = ({ label, value, setValue, min, max, step = 1, format = 'number', suffix = '', hint = '' }) => (
{label} {format === 'currency' ? `$${value}` : value}{suffix}
setValue(parseFloat(e.target.value))} className="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-500" /> {hint &&

{hint}

}
); return (

TikTok Shop Profit Dashboard

Cumulative Creator Model with Reinvestment

{/* Summary Cards */}

12-Month Profit

{formatCurrency(calculations.totalProfit)}

Total Revenue

{formatCurrency(calculations.totalRevenue)}

Units Sold

{formatNumber(calculations.totalUnits)}

Total Creators

{formatNumber(calculations.totalCreatorsRecruited)}

Your Take-Home

{formatCurrency(calculations.totalExpensesTaken)}

Profit/Unit

{formatCurrency(calculations.profitPerUnit)}

{/* Controls Column 1 */}
{/* Product */}

πŸ’° Product & Costs

{/* Fees */}

πŸ“Š TikTok Fees

{/* Inventory */}

πŸ“¦ Initial Inventory

Sellable Stock: {calculations.sellableStock} units

{/* Controls Column 2 */}
{/* Creators */}

🎬 Creator Performance

{/* Video Views Distribution */}

πŸ“Š Video Views Distribution

Mimics real TikTok: most flop, some average, few go viral

πŸ’€ Flops ({flopPercent}%) πŸ“ˆ Average ({averagePercent}%) πŸš€ Viral ({calculations.viralPercent}%)
Weighted Avg Views/Video: {formatNumber(calculations.avgViewsPerVideo)}
{/* Reinvestment */}

🌱 Cash Allocation

{/* Monthly Demand Calc */}

πŸ“ˆ Month 12 Projection

Active Creators: {calculations.months[11]?.totalActiveCreators || 0}

Total Videos: {formatNumber(calculations.months[11]?.totalVideos || 0)}

πŸ’€{calculations.months[11]?.flopVideoCount || 0} πŸ“ˆ{calculations.months[11]?.avgVideoCount || 0} πŸš€{calculations.months[11]?.viralVideoCount || 0}

Total Views: {formatNumber(calculations.months[11]?.totalViews || 0)}

Avg Views/Video: {formatNumber(calculations.months[11]?.avgViewsThisMonth || 0)}

Demand: {formatNumber(calculations.months[11]?.potentialSales || 0)} units

{/* Charts */}
{/* Creator Growth Chart */}

Creator Network Growth

{/* Profit Chart */}

Monthly Profit

`$${(v/1000).toFixed(0)}k`} /> [formatCurrency(value), 'Profit']} />
{/* Units Chart */}

Demand vs Stock vs Sold

{/* Monthly Table */}

Monthly Breakdown

{calculations.months.map((m, i) => ( ))}
Month Creators Videos πŸš€Viral Stock Views Needed Actual Views Sold Profit Your Take Cash End
{m.month} {m.totalActiveCreators} {formatNumber(m.totalVideos)} {m.viralVideoCount} {formatNumber(m.stockStart)} = m.targetViews ? 'text-green-400' : 'text-red-400'}`}> {formatNumber(m.targetViews)} {formatNumber(m.totalViews)} {formatNumber(m.unitsSold)} 0 ? 'text-green-400' : 'text-red-400'}`}> {formatCurrency(m.monthlyProfit)} {formatCurrency(m.expensesTaken)} {formatCurrency(m.cashAfterRestock)}
{/* How it works */}

How the model works:

  • Month 1: You send {initialSamples} samples β†’ {initialSamples} creators start posting
  • Retention: Each month, {creatorRetentionRate}% of existing creators continue posting
  • Video Distribution: {flopPercent}% flop ({formatNumber(flopViews)} views) β€’ {averagePercent}% average ({formatNumber(averageViews)} views) β€’ {calculations.viralPercent}% go viral ({formatNumber(viralViews)} views)
  • Cash Priority: Your ${formatNumber(monthlyExpenses)}/month β†’ seeding β†’ restocking
  • Cash delay: Sales money arrives next month (settlement), stock takes 1 month to produce
); }