import React, { useState, useEffect, useMemo } from 'react'; import { Plus, Trash2, Calculator, TrendingUp, DollarSign, PieChart, Save } from 'lucide-react'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, ReferenceLine } from 'recharts'; const Card = ({ children, className = "" }) => (
{children}
); const SectionHeader = ({ icon: Icon, title }) => (

{title}

); const InputField = ({ label, value, onChange, type = "number", prefix = "", suffix = "", step = "0.01" }) => (
{prefix && (
{prefix}
)} onChange(e.target.value)} className={`focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-slate-300 rounded-md py-2 ${prefix ? 'pl-12' : 'pl-3'} ${suffix ? 'pr-12' : 'pr-3'} bg-slate-50`} /> {suffix && (
{suffix}
)}
); export default function FinancialModel() { // --- STATE --- // General & Volume const [currency, setCurrency] = useState('EGP'); const [fxRate, setFxRate] = useState(50); const [salesVolume, setSalesVolume] = useState(3000000); const [unitsPerBag, setUnitsPerBag] = useState(18); // Variable Costs (Unit Economics) const [cifUsd, setCifUsd] = useState(0.70); const [customsDuty, setCustomsDuty] = useState(20); // In percent const [otherImportFees, setOtherImportFees] = useState(5); // In percent of CIF const [inlandFreight, setInlandFreight] = useState(1.00); const [portClearance, setPortClearance] = useState(0.50); // Changed default from 0 to 0.5 as assumption correction const [wasteContingency, setWasteContingency] = useState(2.00); // NEW: Missing assumption added // Marketing & Sales const [variableMarketing, setVariableMarketing] = useState(10.00); const [salesComm, setSalesComm] = useState(2.5); // In percent of Net Sales // Pricing const [exFactoryPrice, setExFactoryPrice] = useState(65.00); const [tradeDiscount, setTradeDiscount] = useState(10.00); // In percent // Fixed Costs (Dynamic List) const [fixedCosts, setFixedCosts] = useState([ { id: 1, name: 'Fixed Salaries & Wages', value: 750000 }, { id: 2, name: 'Rent & Utilities', value: 240000 }, { id: 3, name: 'Office & Admin', value: 60000 }, { id: 4, name: 'Fixed Marketing (Campaigns)', value: 600000 }, { id: 5, name: 'Travel & Meetings', value: 50000 }, { id: 6, name: 'Professional Fees', value: 0 }, { id: 7, name: 'Other Overheads', value: 0 }, ]); // Tax const [taxRate, setTaxRate] = useState(22); // --- CALCULATIONS --- const metrics = useMemo(() => { // 1. Costing Per Bag const cifEgp = cifUsd * fxRate; const dutyVal = cifEgp * (customsDuty / 100); const otherImportVal = cifEgp * (otherImportFees / 100); // Note: Applying waste contingency on total landed cost structure const landedBase = cifEgp + dutyVal + otherImportVal + parseFloat(inlandFreight) + parseFloat(portClearance); const wasteVal = landedBase * (wasteContingency / 100); const landedCostPerBag = landedBase + wasteVal; // 2. Pricing Per Bag const discountVal = exFactoryPrice * (tradeDiscount / 100); const netRealizedPrice = exFactoryPrice - discountVal; // 3. Variable Selling Exp Per Bag const commissionVal = netRealizedPrice * (salesComm / 100); const totalVariableCost = landedCostPerBag + parseFloat(variableMarketing) + commissionVal; // 4. Margins Per Bag const grossMarginVal = netRealizedPrice - landedCostPerBag; // Accounting definition usually excludes marketing const contributionVal = netRealizedPrice - totalVariableCost; // 5. Totals (Year 1) const netSalesRevenue = salesVolume * netRealizedPrice; const totalCOGS = salesVolume * landedCostPerBag; const totalGrossProfit = netSalesRevenue - totalCOGS; const totalVariableMarketing = salesVolume * parseFloat(variableMarketing); const totalCommissions = salesVolume * commissionVal; const totalFixedCosts = fixedCosts.reduce((sum, item) => sum + parseFloat(item.value || 0), 0); const ebitda = totalGrossProfit - totalVariableMarketing - totalCommissions - totalFixedCosts; // Assuming 0 depreciation for now as per input file, but ready for logic const ebit = ebitda; const taxVal = ebit > 0 ? ebit * (taxRate / 100) : 0; const netIncome = ebit - taxVal; return { cifEgp, dutyVal, otherImportVal, wasteVal, landedCostPerBag, netRealizedPrice, commissionVal, totalVariableCost, grossMarginVal, contributionVal, netSalesRevenue, totalCOGS, totalGrossProfit, totalVariableMarketing, totalCommissions, totalFixedCosts, ebitda, taxVal, netIncome, grossMarginPct: (grossMarginVal / netRealizedPrice) * 100, contributionPct: (contributionVal / netRealizedPrice) * 100, netIncomePct: (netIncome / netSalesRevenue) * 100, breakEvenVolume: contributionVal > 0 ? totalFixedCosts / contributionVal : 0 }; }, [fxRate, cifUsd, customsDuty, otherImportFees, inlandFreight, portClearance, wasteContingency, variableMarketing, salesComm, exFactoryPrice, tradeDiscount, fixedCosts, salesVolume, taxRate]); // --- HANDLERS --- const handleAddFixedCost = () => { const newId = Math.max(...fixedCosts.map(i => i.id), 0) + 1; setFixedCosts([...fixedCosts, { id: newId, name: 'New Expense Category', value: 0 }]); }; const handleRemoveFixedCost = (id) => { setFixedCosts(fixedCosts.filter(item => item.id !== id)); }; const handleUpdateFixedCost = (id, field, val) => { setFixedCosts(fixedCosts.map(item => item.id === id ? { ...item, [field]: field === 'value' ? parseFloat(val) : val } : item )); }; const formatCurrency = (val) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'EGP', maximumFractionDigits: 0 }).format(val); }; const formatUnit = (val) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'EGP', minimumFractionDigits: 2 }).format(val); }; // Chart Data const waterfallData = [ { name: 'CIF', value: metrics.cifEgp }, { name: 'Duty', value: metrics.dutyVal }, { name: 'Fees', value: metrics.otherImportVal + parseFloat(portClearance) + parseFloat(inlandFreight) }, { name: 'Waste', value: metrics.wasteVal }, { name: 'Var Mkt', value: parseFloat(variableMarketing) }, { name: 'Comm', value: metrics.commissionVal }, ]; return (
{/* Header */}

IRIS Costing & P&L Model

Interactive Financial Planning Dashboard

Year 1 Projection
{/* LEFT COLUMN: INPUTS */}
{/* General Inputs */}
{/* Variable Cost Inputs */}
{/* Pricing Inputs */}
Net Realized Price: {formatUnit(metrics.netRealizedPrice)}
{/* MIDDLE COLUMN: FIXED COSTS & SUMMARY */}
{/* Top KPIs */}

Gross Margin

{metrics.grossMarginPct.toFixed(1)}%

{formatUnit(metrics.grossMarginVal)}/bag

EBITDA Margin

{((metrics.ebitda / metrics.netSalesRevenue) * 100).toFixed(1)}%

{formatCurrency(metrics.ebitda)}

Break Even Vol

{(metrics.breakEvenVolume / 1000).toFixed(1)}k

bags
{/* Dynamic Fixed Costs */}

Annual Fixed Expenses

{fixedCosts.map((item) => (
handleUpdateFixedCost(item.id, 'name', e.target.value)} className="flex-1 text-sm border-slate-200 rounded px-2 py-1 focus:ring-1 focus:ring-blue-500 outline-none" />
EGP handleUpdateFixedCost(item.id, 'value', e.target.value)} className="w-full text-right text-sm border-slate-200 rounded px-2 py-1 pl-8 focus:ring-1 focus:ring-blue-500 outline-none" />
))}
Total Fixed Costs {formatCurrency(metrics.totalFixedCosts)}
{/* Cost Breakdown Chart */}
formatUnit(val)} />
Total Variable Cost: {formatUnit(metrics.totalVariableCost)}
{/* P&L Table */}

Pro Forma P&L Statement (Year 1)

All figures in EGP
{/* Variable Selling */} {/* Fixed Costs Summary */} {/* Final Profit */}
Line Item Amount % Sales
Net Sales Revenue {formatCurrency(metrics.netSalesRevenue)} 100.0%
Total COGS (Landed) ({formatCurrency(metrics.totalCOGS)}) {(metrics.totalCOGS / metrics.netSalesRevenue * 100).toFixed(1)}%
Gross Profit {formatCurrency(metrics.totalGrossProfit)} {metrics.grossMarginPct.toFixed(1)}%
Variable Marketing ({formatCurrency(metrics.totalVariableMarketing)}) {(metrics.totalVariableMarketing / metrics.netSalesRevenue * 100).toFixed(1)}%
Sales Commissions ({formatCurrency(metrics.totalCommissions)}) {(metrics.totalCommissions / metrics.netSalesRevenue * 100).toFixed(1)}%
Contribution Margin {formatCurrency(metrics.netSalesRevenue - metrics.totalCOGS - metrics.totalVariableMarketing - metrics.totalCommissions)} {metrics.contributionPct.toFixed(1)}%
Fixed Operating Expenses ({formatCurrency(metrics.totalFixedCosts)}) {(metrics.totalFixedCosts / metrics.netSalesRevenue * 100).toFixed(1)}%
EBITDA {formatCurrency(metrics.ebitda)} {((metrics.ebitda / metrics.netSalesRevenue) * 100).toFixed(1)}%
Income Tax ({taxRate}%) ({formatCurrency(metrics.taxVal)}) {(metrics.taxVal / metrics.netSalesRevenue * 100).toFixed(1)}%
Net Income {formatCurrency(metrics.netIncome)} {metrics.netIncomePct.toFixed(1)}%
); }
Scroll to Top