diff --git a/src/data/mockStore.jsx b/src/data/mockStore.jsx
index a1b0a5d..cf3fa90 100644
--- a/src/data/mockStore.jsx
+++ b/src/data/mockStore.jsx
@@ -2014,7 +2014,6 @@ const MOCK_PROJECTS = [
commissionType: 'percent_gross',
commissionRate: 10,
commission: 4500,
- additionalExpenses: 1200,
startDate: '2026-01-20',
endDate: '2026-03-05',
completionPercentage: 55,
@@ -2091,7 +2090,6 @@ const MOCK_PROJECTS = [
commissionType: 'flat',
commissionRate: 850,
commission: 850,
- additionalExpenses: 350,
startDate: '2025-11-15',
endDate: '2025-12-10',
completionPercentage: 100,
@@ -2141,7 +2139,6 @@ const MOCK_PROJECTS = [
commissionType: 'percent_net',
commissionRate: 15,
commission: 3200,
- additionalExpenses: 900,
startDate: '2026-01-06',
endDate: '2026-02-28',
completionPercentage: 42,
@@ -2217,7 +2214,6 @@ const MOCK_PROJECTS = [
commissionType: 'custom',
commissionRate: 5500,
commission: 5500,
- additionalExpenses: 3800,
startDate: '2025-12-01',
endDate: '2026-02-28',
completionPercentage: 78,
@@ -2304,7 +2300,6 @@ const MOCK_PROJECTS = [
commissionType: 'percent_gross',
commissionRate: 10,
commission: 2800,
- additionalExpenses: 600,
startDate: '2026-01-06',
endDate: '2026-02-20',
completionPercentage: 38,
@@ -2378,7 +2373,6 @@ const MOCK_PROJECTS = [
commissionType: 'flat',
commissionRate: 1850,
commission: 1850,
- additionalExpenses: 450,
startDate: '2026-02-03',
endDate: '2026-03-15',
completionPercentage: 22,
@@ -2431,7 +2425,6 @@ const MOCK_PROJECTS = [
commissionType: 'percent_gross',
commissionRate: 10,
commission: 12500,
- additionalExpenses: 2800,
startDate: '2026-01-27',
endDate: '2026-04-15',
completionPercentage: 32,
@@ -2526,7 +2519,6 @@ const MOCK_PROJECTS = [
commissionType: 'percent_net',
commissionRate: 12,
commission: 7500,
- additionalExpenses: 4200,
startDate: '2025-11-18',
endDate: '2026-02-28',
completionPercentage: 88,
@@ -2577,7 +2569,6 @@ const MOCK_PROJECTS = [
commissionType: 'percent_gross',
commissionRate: 10,
commission: 4200,
- additionalExpenses: 1100,
startDate: '2026-01-13',
endDate: '2026-03-10',
completionPercentage: 50,
@@ -2626,7 +2617,6 @@ const MOCK_PROJECTS = [
commissionType: 'custom',
commissionRate: 3500,
commission: 3500,
- additionalExpenses: 800,
startDate: '2025-09-15',
endDate: '2025-10-28',
completionPercentage: 100,
@@ -2675,7 +2665,6 @@ const MOCK_PROJECTS = [
commissionType: 'flat',
commissionRate: 9500,
commission: 9500,
- additionalExpenses: 5200,
startDate: '2025-12-08',
endDate: '2026-03-20',
completionPercentage: 62,
@@ -2732,7 +2721,6 @@ const MOCK_PROJECTS = [
commissionType: 'percent_gross',
commissionRate: 10,
commission: 5800,
- additionalExpenses: 1500,
startDate: '2026-01-13',
endDate: '2026-04-01',
completionPercentage: 28,
diff --git a/src/pages/owner/OwnerProjectDetail.jsx b/src/pages/owner/OwnerProjectDetail.jsx
index 8ae540a..23a8842 100644
--- a/src/pages/owner/OwnerProjectDetail.jsx
+++ b/src/pages/owner/OwnerProjectDetail.jsx
@@ -68,7 +68,6 @@ const tabs = [
{ id: 'rfis', label: 'RFIs', icon: FileText },
{ id: 'milestones', label: 'Milestones', icon: Milestone },
{ id: 'invoices', label: 'Invoices', icon: DollarSign },
- { id: 'payments', label: 'Payments', icon: Banknote },
{ id: 'risks', label: 'Risk & Issues', icon: AlertCircle },
{ id: 'activity', label: 'Activity', icon: Clock },
{ id: 'docs', label: 'Docs', icon: FolderOpen },
@@ -265,9 +264,8 @@ const OwnerProjectDetail = () => {
const activeCommissionConfig = resolvedCommission;
const profitMetrics = useMemo(() => {
- if (!project) return { totalCosts: 0, commission: 0, commissionFormula: '', additionalExp: 0, grossProfit: 0, netProfit: 0, grossMargin: 0, netMargin: 0 };
+ if (!project) return { totalCosts: 0, commission: 0, commissionFormula: '', grossProfit: 0, netProfit: 0, grossMargin: 0, netMargin: 0 };
const totalCosts = Number(project.actualCost ?? project.spent) || 0;
- const additionalExp = Number(project.additionalExpenses) || 0;
const gross = totalReceived - totalCosts;
const { type, rate } = activeCommissionConfig;
@@ -297,10 +295,10 @@ const OwnerProjectDetail = () => {
}
commission = Number.isFinite(commission) ? commission : 0;
- const net = gross - commission - additionalExp;
+ const net = gross - commission;
const grossMargin = totalReceived > 0 ? (gross / totalReceived) * 100 : 0;
const netMargin = totalReceived > 0 ? (net / totalReceived) * 100 : 0;
- return { totalCosts, commission, commissionFormula, additionalExp, grossProfit: gross, netProfit: net, grossMargin, netMargin };
+ return { totalCosts, commission, commissionFormula, grossProfit: gross, netProfit: net, grossMargin, netMargin };
}, [project, totalReceived, activeCommissionConfig]);
if (!project) {
@@ -736,6 +734,23 @@ const OwnerProjectDetail = () => {
);
})}
+
+
+ |
+ Totals
+ |
+ {formatCurrency(totalReceived)} |
+ {formatCurrency(profitMetrics.totalCosts)} |
+ {(() => {
+ const totalVariance = profitMetrics.totalCosts - totalReceived;
+ return (
+ 0 ? 'text-[#ff4500]' : 'text-[#39ff14]'}`}>
+ {totalVariance > 0 ? '+' : ''}{formatCurrency(totalVariance)}
+ |
+ );
+ })()}
+
+
@@ -747,6 +762,329 @@ const OwnerProjectDetail = () => {
Budget: {formatCurrency(project.approvedBudget || project.budget)} · Spent: {formatCurrency(project.actualCost || project.spent)}
)}
+
+ {/* Commission Configuration Card */}
+
+ openTooltip && setOpenTooltip(null)}>
+
+
+
Commission
+ {/* Source Badge */}
+ {(() => {
+ const src = resolvedCommission.source;
+ const badgeCfg = {
+ org: { icon: Building2, label: 'Org Default', color: 'text-sky-500 dark:text-[#00f0ff]', bg: 'bg-sky-50 dark:bg-[#00f0ff]/10', border: 'border-sky-200 dark:border-[#00f0ff]/20' },
+ user: { icon: UserIcon, label: 'User Override', color: 'text-purple-500 dark:text-purple-400', bg: 'bg-purple-50 dark:bg-purple-500/10', border: 'border-purple-200 dark:border-purple-500/20' },
+ job: { icon: Briefcase, label: 'Job Override', color: 'text-amber-600 dark:text-[#fda913]', bg: 'bg-amber-50 dark:bg-[#fda913]/10', border: 'border-amber-200 dark:border-[#fda913]/20' },
+ };
+ const b = badgeCfg[src];
+ const BadgeIcon = b.icon;
+ return (
+
+
+ {b.label}
+
+ );
+ })()}
+
+
+
+
+
+ {openTooltip === 'commission' && (
+
+
Commission Hierarchy
+
+
Job Override: Highest priority — set per job
+
User Override: Per-user commission settings
+
Org Default: Organization-wide fallback
+
+
+
Priority: Job → User → Org
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {activeCommissionConfig.type === 'percent_gross' || activeCommissionConfig.type === 'percent_net' ? '%' : '$'}
+
+ setCommissionConfig(prev => ({ ...(prev || { type: activeCommissionConfig.type, rate: activeCommissionConfig.rate }), type: activeCommissionConfig.type, rate: e.target.value }))}
+ placeholder={activeCommissionConfig.type === 'percent_gross' || activeCommissionConfig.type === 'percent_net' ? 'e.g. 10' : 'e.g. 5000'}
+ className="w-full bg-zinc-50 dark:bg-[#18181b] border border-zinc-200 dark:border-white/10 rounded-xl pl-8 pr-4 py-2.5 text-sm text-zinc-900 dark:text-white placeholder-zinc-400 dark:placeholder-zinc-600 outline-none focus:border-amber-500 dark:focus:border-amber-500 focus:ring-1 focus:ring-amber-500/30 transition-all"
+ />
+
+
+
+
+
+ {formatCurrency(profitMetrics.commission)}
+
+
+
+
+
+
+ Formula:
+ {profitMetrics.commissionFormula}
+
+
+
+
+ {/* Profit Summary Cards */}
+
+
= 0 ? "rgba(57, 255, 20, 0.12)" : "rgba(255, 0, 60, 0.12)"}
+ innerClassName="!p-5"
+ >
+
+
+ = 0 ? NEON_GREEN : NEON_RED} />
+ Gross Profit
+
+
+
+ {openTooltip === 'gross' && (
+
+
Gross Profit Formula
+
Total Received − Total Actual Costs
+
+
{formatCurrency(totalReceived)} − {formatCurrency(profitMetrics.totalCosts)} = {formatCurrency(profitMetrics.grossProfit)}
+
+
+ )}
+
+
+ = 0 ? NEON_GREEN : NEON_RED}`}>
+ {profitMetrics.grossProfit >= 0 ? '+' : ''}{formatCurrency(profitMetrics.grossProfit)}
+
+
+ = 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
+
+
+
+
+
= 0 ? "rgba(57, 255, 20, 0.12)" : "rgba(255, 0, 60, 0.12)"}
+ innerClassName="!p-5"
+ >
+
+
+ = 0 ? NEON_GREEN : NEON_RED} />
+ Net Profit
+
+
+
+ {openTooltip === 'net' && (
+
+
Net Profit Formula
+
Gross Profit − Commission
+
+
{formatCurrency(profitMetrics.grossProfit)} − {formatCurrency(profitMetrics.commission)} = {formatCurrency(profitMetrics.netProfit)}
+
+
+ )}
+
+
+ = 0 ? NEON_GREEN : NEON_RED}`}>
+ {profitMetrics.netProfit >= 0 ? '+' : ''}{formatCurrency(profitMetrics.netProfit)}
+
+
+ = 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
+
+
+
+
+
+ {/* Payments Received Table */}
+
+
+
+
+
Payments Received
+
+ {payments.length}
+
+
+
+
+
+ {payments.length > 0 ? (
+ <>
+
+
+
+
+ | From / Method |
+ togglePaymentSort('amount')}>
+ Amount {paymentSort.field === 'amount' ? (paymentSort.dir === 'asc' ? '↑' : '↓') : ''}
+ |
+ togglePaymentSort('date')}>
+ Date {paymentSort.field === 'date' ? (paymentSort.dir === 'asc' ? '↑' : '↓') : ''}
+ |
+ Check No. / Ref |
+ Memo |
+
+
+
+ {paginatedPayments.map((pay) => (
+
+ |
+ {pay.from}
+
+ {pay.method}
+
+ |
+
+ {formatCurrency(pay.amount)}
+ |
+ {pay.date} |
+ {pay.refNumber || '—'} |
+ {pay.memo || '—'} |
+
+ ))}
+
+
+
+
+
+ {paginatedPayments.map((pay) => (
+
+
+
+
{pay.from}
+
+ {pay.method}
+
+
+
{formatCurrency(pay.amount)}
+
+
+
+
+
Ref #
+
{pay.refNumber || '—'}
+
+
+ {pay.memo && (
+
{pay.memo}
+ )}
+
+ ))}
+
+
+ {totalPaymentPages > 1 && (
+
+
+ {paymentPage * PAYMENTS_PER_PAGE + 1}–{Math.min((paymentPage + 1) * PAYMENTS_PER_PAGE, sortedPayments.length)} of {sortedPayments.length}
+
+
+
+
+
+
+ )}
+ >
+ ) : (
+
+
+
No payments recorded yet.
+
Record your first payment to start tracking.
+
+ )}
+
)}
@@ -982,367 +1320,6 @@ const OwnerProjectDetail = () => {
)}
- {/* PAYMENTS TAB */}
- {activeTab === 'payments' && (
- openTooltip && setOpenTooltip(null)}>
- {/* Financial Summary Row */}
-
-
- {formatCurrency(totalReceived)}
- Total Received
-
-
- {formatCurrency(profitMetrics.totalCosts)}
- Total Costs
-
-
- {formatCurrency(profitMetrics.additionalExp)}
- Addl. Expenses
-
-
-
- {/* Commission Configuration Card */}
-
-
-
-
-
Commission
- {/* Source Badge */}
- {(() => {
- const src = resolvedCommission.source;
- const badgeCfg = {
- org: { icon: Building2, label: 'Org Default', color: 'text-sky-500 dark:text-[#00f0ff]', bg: 'bg-sky-50 dark:bg-[#00f0ff]/10', border: 'border-sky-200 dark:border-[#00f0ff]/20' },
- user: { icon: UserIcon, label: 'User Override', color: 'text-purple-500 dark:text-purple-400', bg: 'bg-purple-50 dark:bg-purple-500/10', border: 'border-purple-200 dark:border-purple-500/20' },
- job: { icon: Briefcase, label: 'Job Override', color: 'text-amber-600 dark:text-[#fda913]', bg: 'bg-amber-50 dark:bg-[#fda913]/10', border: 'border-amber-200 dark:border-[#fda913]/20' },
- };
- const b = badgeCfg[src];
- const BadgeIcon = b.icon;
- return (
-
-
- {b.label}
-
- );
- })()}
-
-
-
-
-
- {openTooltip === 'commission' && (
-
-
Commission Hierarchy
-
-
Job Override: Highest priority — set per job
-
User Override: Per-user commission settings
-
Org Default: Organization-wide fallback
-
-
-
Priority: Job → User → Org
-
-
- )}
-
-
-
-
-
- {/* Type Selector */}
-
-
-
-
-
- {/* Rate / Value Input */}
-
-
-
-
- {activeCommissionConfig.type === 'percent_gross' || activeCommissionConfig.type === 'percent_net' ? '%' : '$'}
-
- setCommissionConfig(prev => ({ ...(prev || { type: activeCommissionConfig.type, rate: activeCommissionConfig.rate }), type: activeCommissionConfig.type, rate: e.target.value }))}
- placeholder={activeCommissionConfig.type === 'percent_gross' || activeCommissionConfig.type === 'percent_net' ? 'e.g. 10' : 'e.g. 5000'}
- className="w-full bg-zinc-50 dark:bg-[#18181b] border border-zinc-200 dark:border-white/10 rounded-xl pl-8 pr-4 py-2.5 text-sm text-zinc-900 dark:text-white placeholder-zinc-400 dark:placeholder-zinc-600 outline-none focus:border-amber-500 dark:focus:border-amber-500 focus:ring-1 focus:ring-amber-500/30 transition-all"
- />
-
-
-
- {/* Calculated Result */}
-
-
-
- {formatCurrency(profitMetrics.commission)}
-
-
-
-
- {/* Live Formula Preview */}
-
-
- Formula:
- {profitMetrics.commissionFormula}
-
-
-
-
- {/* Profit Summary Cards */}
-
- {/* Gross Profit */}
-
= 0 ? "rgba(57, 255, 20, 0.12)" : "rgba(255, 0, 60, 0.12)"}
- innerClassName="!p-5"
- >
-
-
- = 0 ? NEON_GREEN : NEON_RED} />
- Gross Profit
-
-
-
- {openTooltip === 'gross' && (
-
-
Gross Profit Formula
-
Total Received − Total Actual Costs
-
-
{formatCurrency(totalReceived)} − {formatCurrency(profitMetrics.totalCosts)} = {formatCurrency(profitMetrics.grossProfit)}
-
-
- )}
-
-
- = 0 ? NEON_GREEN : NEON_RED}`}>
- {profitMetrics.grossProfit >= 0 ? '+' : ''}{formatCurrency(profitMetrics.grossProfit)}
-
-
- = 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
-
-
-
-
- {/* Net Profit */}
-
= 0 ? "rgba(57, 255, 20, 0.12)" : "rgba(255, 0, 60, 0.12)"}
- innerClassName="!p-5"
- >
-
-
- = 0 ? NEON_GREEN : NEON_RED} />
- Net Profit
-
-
-
- {openTooltip === 'net' && (
-
-
Net Profit Formula
-
Gross Profit − Commission − Addl. Expenses
-
-
{formatCurrency(profitMetrics.grossProfit)} − {formatCurrency(profitMetrics.commission)} − {formatCurrency(profitMetrics.additionalExp)} = {formatCurrency(profitMetrics.netProfit)}
-
-
- )}
-
-
- = 0 ? NEON_GREEN : NEON_RED}`}>
- {profitMetrics.netProfit >= 0 ? '+' : ''}{formatCurrency(profitMetrics.netProfit)}
-
-
- = 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
-
-
-
-
-
- {/* Payments Table */}
-
-
-
-
-
Payments Received
-
- {payments.length}
-
-
-
-
-
- {payments.length > 0 ? (
- <>
- {/* Desktop Table */}
-
-
-
-
- | From / Method |
- togglePaymentSort('amount')}
- >
- Amount {paymentSort.field === 'amount' ? (paymentSort.dir === 'asc' ? '↑' : '↓') : ''}
- |
- togglePaymentSort('date')}
- >
- Date {paymentSort.field === 'date' ? (paymentSort.dir === 'asc' ? '↑' : '↓') : ''}
- |
- Check No. / Ref |
- Memo |
-
-
-
- {paginatedPayments.map((pay) => (
-
- |
- {pay.from}
-
- {pay.method}
-
- |
-
- {formatCurrency(pay.amount)}
- |
- {pay.date} |
- {pay.refNumber || '—'} |
- {pay.memo || '—'} |
-
- ))}
-
-
-
-
- {/* Mobile Card View */}
-
- {paginatedPayments.map((pay) => (
-
-
-
-
{pay.from}
-
- {pay.method}
-
-
-
{formatCurrency(pay.amount)}
-
-
-
-
-
Ref #
-
{pay.refNumber || '—'}
-
-
- {pay.memo && (
-
{pay.memo}
- )}
-
- ))}
-
-
- {/* Pagination */}
- {totalPaymentPages > 1 && (
-
-
- {paymentPage * PAYMENTS_PER_PAGE + 1}–{Math.min((paymentPage + 1) * PAYMENTS_PER_PAGE, sortedPayments.length)} of {sortedPayments.length}
-
-
-
-
-
-
- )}
- >
- ) : (
-
-
-
No payments recorded yet.
-
Record your first payment to start tracking.
-
- )}
-
-
- )}
-
{/* RISK & ISSUES TAB */}
{activeTab === 'risks' && (