Gross, Net Profit calculate

This commit is contained in:
Satyam Rastogi
2026-05-11 18:56:04 +05:30
parent e9c67d8dda
commit 0b6ee9d626
2 changed files with 122 additions and 9 deletions
+24
View File
@@ -2011,6 +2011,8 @@ const MOCK_PROJECTS = [
spent: 22500, spent: 22500,
variancePercent: -50.0, variancePercent: -50.0,
margin: 0.40, margin: 0.40,
commission: 4500,
additionalExpenses: 1200,
startDate: '2026-01-20', startDate: '2026-01-20',
endDate: '2026-03-05', endDate: '2026-03-05',
completionPercentage: 55, completionPercentage: 55,
@@ -2084,6 +2086,8 @@ const MOCK_PROJECTS = [
spent: 8500, spent: 8500,
variancePercent: 0, variancePercent: 0,
margin: 0.35, margin: 0.35,
commission: 850,
additionalExpenses: 350,
startDate: '2025-11-15', startDate: '2025-11-15',
endDate: '2025-12-10', endDate: '2025-12-10',
completionPercentage: 100, completionPercentage: 100,
@@ -2130,6 +2134,8 @@ const MOCK_PROJECTS = [
spent: 18400, spent: 18400,
variancePercent: -42.5, variancePercent: -42.5,
margin: 0.30, margin: 0.30,
commission: 3200,
additionalExpenses: 900,
startDate: '2026-01-06', startDate: '2026-01-06',
endDate: '2026-02-28', endDate: '2026-02-28',
completionPercentage: 42, completionPercentage: 42,
@@ -2202,6 +2208,8 @@ const MOCK_PROJECTS = [
spent: 62300, spent: 62300,
variancePercent: 13.3, variancePercent: 13.3,
margin: -0.13, margin: -0.13,
commission: 5500,
additionalExpenses: 3800,
startDate: '2025-12-01', startDate: '2025-12-01',
endDate: '2026-02-28', endDate: '2026-02-28',
completionPercentage: 78, completionPercentage: 78,
@@ -2285,6 +2293,8 @@ const MOCK_PROJECTS = [
spent: 14200, spent: 14200,
variancePercent: -49.3, variancePercent: -49.3,
margin: 0.25, margin: 0.25,
commission: 2800,
additionalExpenses: 600,
startDate: '2026-01-06', startDate: '2026-01-06',
endDate: '2026-02-20', endDate: '2026-02-20',
completionPercentage: 38, completionPercentage: 38,
@@ -2355,6 +2365,8 @@ const MOCK_PROJECTS = [
spent: 5600, spent: 5600,
variancePercent: -69.7, variancePercent: -69.7,
margin: 0.38, margin: 0.38,
commission: 1850,
additionalExpenses: 450,
startDate: '2026-02-03', startDate: '2026-02-03',
endDate: '2026-03-15', endDate: '2026-03-15',
completionPercentage: 22, completionPercentage: 22,
@@ -2404,6 +2416,8 @@ const MOCK_PROJECTS = [
spent: 48000, spent: 48000,
variancePercent: -61.6, variancePercent: -61.6,
margin: 0.35, margin: 0.35,
commission: 12500,
additionalExpenses: 2800,
startDate: '2026-01-27', startDate: '2026-01-27',
endDate: '2026-04-15', endDate: '2026-04-15',
completionPercentage: 32, completionPercentage: 32,
@@ -2495,6 +2509,8 @@ const MOCK_PROJECTS = [
spent: 91500, spent: 91500,
variancePercent: 22.0, variancePercent: 22.0,
margin: -0.22, margin: -0.22,
commission: 7500,
additionalExpenses: 4200,
startDate: '2025-11-18', startDate: '2025-11-18',
endDate: '2026-02-28', endDate: '2026-02-28',
completionPercentage: 88, completionPercentage: 88,
@@ -2542,6 +2558,8 @@ const MOCK_PROJECTS = [
budget: 42000, budget: 42000,
spent: 25800, spent: 25800,
margin: 0.28, margin: 0.28,
commission: 4200,
additionalExpenses: 1100,
startDate: '2026-01-13', startDate: '2026-01-13',
endDate: '2026-03-10', endDate: '2026-03-10',
completionPercentage: 50, completionPercentage: 50,
@@ -2587,6 +2605,8 @@ const MOCK_PROJECTS = [
budget: 35000, budget: 35000,
spent: 35000, spent: 35000,
margin: 0.30, margin: 0.30,
commission: 3500,
additionalExpenses: 800,
startDate: '2025-09-15', startDate: '2025-09-15',
endDate: '2025-10-28', endDate: '2025-10-28',
completionPercentage: 100, completionPercentage: 100,
@@ -2632,6 +2652,8 @@ const MOCK_PROJECTS = [
budget: 95000, budget: 95000,
spent: 68400, spent: 68400,
margin: 0.20, margin: 0.20,
commission: 9500,
additionalExpenses: 5200,
startDate: '2025-12-08', startDate: '2025-12-08',
endDate: '2026-03-20', endDate: '2026-03-20',
completionPercentage: 62, completionPercentage: 62,
@@ -2685,6 +2707,8 @@ const MOCK_PROJECTS = [
budget: 58000, budget: 58000,
spent: 22600, spent: 22600,
margin: 0.32, margin: 0.32,
commission: 5800,
additionalExpenses: 1500,
startDate: '2026-01-13', startDate: '2026-01-13',
endDate: '2026-04-01', endDate: '2026-04-01',
completionPercentage: 28, completionPercentage: 28,
+98 -9
View File
@@ -12,7 +12,7 @@ import {
ArrowLeft, CheckCircle, Clock, AlertTriangle, PauseCircle, ShieldAlert, ArrowLeft, CheckCircle, Clock, AlertTriangle, PauseCircle, ShieldAlert,
FileText, DollarSign, GitPullRequest, AlertCircle, Activity, Milestone, FileText, DollarSign, GitPullRequest, AlertCircle, Activity, Milestone,
Shield, ChevronRight, Phone, MessageSquare, Mail, AlertOctagon, TrendingUp, Key, Zap, Camera, Plus, Map, User, Crosshair, Image as ImageIcon, MapPin, Shield, ChevronRight, Phone, MessageSquare, Mail, AlertOctagon, TrendingUp, Key, Zap, Camera, Plus, Map, User, Crosshair, Image as ImageIcon, MapPin,
Upload, Trash2, Eye, Pencil, X as XIcon, FolderOpen, Download, File, StickyNote, Banknote, ChevronLeft, ChevronDown as ChevronDownIcon Upload, Trash2, Eye, Pencil, X as XIcon, FolderOpen, Download, File, StickyNote, Banknote, ChevronLeft, ChevronDown as ChevronDownIcon, Info
} from 'lucide-react'; } from 'lucide-react';
import ChangeOrderDrawer from '../../components/owner/ChangeOrderDrawer'; import ChangeOrderDrawer from '../../components/owner/ChangeOrderDrawer';
import InvoiceDetailModal from '../../components/owner/InvoiceDetailModal'; import InvoiceDetailModal from '../../components/owner/InvoiceDetailModal';
@@ -220,6 +220,18 @@ const OwnerProjectDetail = () => {
projects.find(p => p.id === projectId && p.ownerId === user?.id) projects.find(p => p.id === projectId && p.ownerId === user?.id)
, [projects, projectId, user]); , [projects, projectId, user]);
const profitMetrics = useMemo(() => {
if (!project) return { totalCosts: 0, commission: 0, additionalExp: 0, grossProfit: 0, netProfit: 0, grossMargin: 0, netMargin: 0 };
const totalCosts = Number(project.actualCost ?? project.spent) || 0;
const commission = Number(project.commission) || 0;
const additionalExp = Number(project.additionalExpenses) || 0;
const gross = totalReceived - totalCosts;
const net = gross - commission - additionalExp;
const grossMargin = totalReceived > 0 ? (gross / totalReceived) * 100 : 0;
const netMargin = totalReceived > 0 ? (net / totalReceived) * 100 : 0;
return { totalCosts, commission, additionalExp, grossProfit: gross, netProfit: net, grossMargin, netMargin };
}, [project, totalReceived]);
if (!project) { if (!project) {
return ( return (
<div className="min-h-full bg-zinc-50 dark:bg-[#09090b] flex items-center justify-center text-zinc-500"> <div className="min-h-full bg-zinc-50 dark:bg-[#09090b] flex items-center justify-center text-zinc-500">
@@ -905,22 +917,99 @@ const OwnerProjectDetail = () => {
{/* PAYMENTS TAB */} {/* PAYMENTS TAB */}
{activeTab === 'payments' && ( {activeTab === 'payments' && (
<div className="space-y-6 max-w-7xl mx-auto"> <div className="space-y-6 max-w-7xl mx-auto">
{/* Summary Row */} {/* Financial Summary Row */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4"> <div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
<SpotlightCard className="p-4"> <SpotlightCard className="p-4">
<div className={`text-xl font-extrabold ${NEON_GREEN}`}>{formatCurrency(totalReceived)}</div> <div className={`text-xl font-extrabold ${NEON_GREEN}`}>{formatCurrency(totalReceived)}</div>
<div className="text-[10px] uppercase font-bold tracking-wider text-zinc-500 mt-1">Total Received</div> <div className="text-[10px] uppercase font-bold tracking-wider text-zinc-500 mt-1">Total Received</div>
</SpotlightCard> </SpotlightCard>
<SpotlightCard className="p-4"> <SpotlightCard className="p-4">
<div className="text-xl font-extrabold text-zinc-900 dark:text-white">{payments.length}</div> <div className="text-xl font-extrabold text-zinc-900 dark:text-white">{formatCurrency(profitMetrics.totalCosts)}</div>
<div className="text-[10px] uppercase font-bold tracking-wider text-zinc-500 mt-1">Payments</div> <div className="text-[10px] uppercase font-bold tracking-wider text-zinc-500 mt-1">Total Costs</div>
</SpotlightCard> </SpotlightCard>
<SpotlightCard className="p-4"> <SpotlightCard className="p-4">
<div className="text-xl font-extrabold text-zinc-900 dark:text-white font-mono"> <div className="text-xl font-extrabold text-zinc-900 dark:text-white">{formatCurrency(profitMetrics.commission)}</div>
{payments.length > 0 ? [...payments].sort((a, b) => b.date.localeCompare(a.date))[0].date : '—'} <div className="text-[10px] uppercase font-bold tracking-wider text-zinc-500 mt-1">Commission</div>
</div>
<div className="text-[10px] uppercase font-bold tracking-wider text-zinc-500 mt-1">Latest Payment</div>
</SpotlightCard> </SpotlightCard>
<SpotlightCard className="p-4">
<div className="text-xl font-extrabold text-zinc-900 dark:text-white">{formatCurrency(profitMetrics.additionalExp)}</div>
<div className="text-[10px] uppercase font-bold tracking-wider text-zinc-500 mt-1">Addl. Expenses</div>
</SpotlightCard>
</div>
{/* Profit Summary Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{/* Gross Profit */}
<NeoCard
spotlightColor={profitMetrics.grossProfit >= 0 ? "rgba(57, 255, 20, 0.12)" : "rgba(255, 0, 60, 0.12)"}
innerClassName="!p-5"
>
<div className="flex items-start justify-between mb-3">
<div className="flex items-center gap-2">
<TrendingUp size={18} className={profitMetrics.grossProfit >= 0 ? NEON_GREEN : NEON_RED} />
<h4 className="text-xs font-mono font-bold text-zinc-500 dark:text-zinc-400 uppercase tracking-widest">Gross Profit</h4>
</div>
<div className="relative group">
<Info size={14} className="text-zinc-400 dark:text-zinc-500 cursor-help" />
<div className="absolute right-0 top-6 z-30 w-64 p-3 rounded-xl bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-white/10 shadow-xl text-xs text-zinc-600 dark:text-zinc-300 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all pointer-events-none">
<p className="font-bold text-zinc-900 dark:text-white mb-1">Gross Profit Formula</p>
<p className="font-mono text-[11px]">Total Received Total Actual Costs</p>
<div className="mt-2 pt-2 border-t border-zinc-100 dark:border-white/5 space-y-1">
<p>{formatCurrency(totalReceived)} {formatCurrency(profitMetrics.totalCosts)}</p>
<p className="font-bold">= {formatCurrency(profitMetrics.grossProfit)}</p>
</div>
</div>
</div>
</div>
<div className={`text-3xl sm:text-4xl font-black font-mono tracking-tight ${profitMetrics.grossProfit >= 0 ? NEON_GREEN : NEON_RED}`}>
{profitMetrics.grossProfit >= 0 ? '+' : ''}{formatCurrency(profitMetrics.grossProfit)}
</div>
<div className="flex items-center gap-2 mt-2">
<span className={`text-xs font-bold px-2 py-0.5 rounded-full ${
profitMetrics.grossMargin >= 0
? 'text-green-600 dark:text-[#39ff14] bg-green-50 dark:bg-[#39ff14]/10 border border-green-200 dark:border-[#39ff14]/20'
: 'text-red-600 dark:text-[#ff003c] bg-red-50 dark:bg-[#ff003c]/10 border border-red-200 dark:border-[#ff003c]/20'
}`}>
{profitMetrics.grossMargin >= 0 ? '+' : ''}{profitMetrics.grossMargin.toFixed(1)}% margin
</span>
</div>
</NeoCard>
{/* Net Profit */}
<NeoCard
spotlightColor={profitMetrics.netProfit >= 0 ? "rgba(57, 255, 20, 0.12)" : "rgba(255, 0, 60, 0.12)"}
innerClassName="!p-5"
>
<div className="flex items-start justify-between mb-3">
<div className="flex items-center gap-2">
<DollarSign size={18} className={profitMetrics.netProfit >= 0 ? NEON_GREEN : NEON_RED} />
<h4 className="text-xs font-mono font-bold text-zinc-500 dark:text-zinc-400 uppercase tracking-widest">Net Profit</h4>
</div>
<div className="relative group">
<Info size={14} className="text-zinc-400 dark:text-zinc-500 cursor-help" />
<div className="absolute right-0 top-6 z-50 w-64 p-3 rounded-xl bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-white/10 shadow-xl text-xs text-zinc-600 dark:text-zinc-300 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all pointer-events-none">
<p className="font-bold text-zinc-900 dark:text-white mb-1">Net Profit Formula</p>
<p className="font-mono text-[11px]">Gross Profit Commission Addl. Expenses</p>
<div className="mt-2 pt-2 border-t border-zinc-100 dark:border-white/5 space-y-1">
<p>{formatCurrency(profitMetrics.grossProfit)} {formatCurrency(profitMetrics.commission)} {formatCurrency(profitMetrics.additionalExp)}</p>
<p className="font-bold">= {formatCurrency(profitMetrics.netProfit)}</p>
</div>
</div>
</div>
</div>
<div className={`text-3xl sm:text-4xl font-black font-mono tracking-tight ${profitMetrics.netProfit >= 0 ? NEON_GREEN : NEON_RED}`}>
{profitMetrics.netProfit >= 0 ? '+' : ''}{formatCurrency(profitMetrics.netProfit)}
</div>
<div className="flex items-center gap-2 mt-2">
<span className={`text-xs font-bold px-2 py-0.5 rounded-full ${
profitMetrics.netMargin >= 0
? 'text-green-600 dark:text-[#39ff14] bg-green-50 dark:bg-[#39ff14]/10 border border-green-200 dark:border-[#39ff14]/20'
: 'text-red-600 dark:text-[#ff003c] bg-red-50 dark:bg-[#ff003c]/10 border border-red-200 dark:border-[#ff003c]/20'
}`}>
{profitMetrics.netMargin >= 0 ? '+' : ''}{profitMetrics.netMargin.toFixed(1)}% margin
</span>
</div>
</NeoCard>
</div> </div>
{/* Payments Table */} {/* Payments Table */}