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 = "" }) => (
);
const InputField = ({ label, value, onChange, type = "number", prefix = "", suffix = "", step = "0.01" }) => (
);
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 (
{children}
);
const SectionHeader = ({ icon: Icon, title }) => (
{title}
{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}
)}
{/* Header */}
);
} IRIS Costing & P&L Model
Interactive Financial Planning Dashboard
Year 1 Projection
{/* LEFT COLUMN: INPUTS */}
{/* General Inputs */}
{/* Variable Cost Inputs */}
{/* Pricing Inputs */}
{/* MIDDLE COLUMN: FIXED COSTS & SUMMARY */}
Net Realized Price:
{formatUnit(metrics.netRealizedPrice)}
{/* Top KPIs */}
Gross Margin
{metrics.grossMarginPct.toFixed(1)}%
{formatUnit(metrics.grossMarginVal)}/bagEBITDA Margin
{((metrics.ebitda / metrics.netSalesRevenue) * 100).toFixed(1)}%
{formatCurrency(metrics.ebitda)}Break Even Vol
{(metrics.breakEvenVolume / 1000).toFixed(1)}k
bags
{/* Dynamic Fixed Costs */}
{/* Cost Breakdown Chart */}
formatUnit(val)} />
{/* P&L Table */}
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)}
Total Variable Cost: {formatUnit(metrics.totalVariableCost)}
Pro Forma P&L Statement (Year 1)
All figures in EGP
| 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)}% |
