From 6c5d9ea5e8608e7f3353c615b1a2db7fdaf4ee21 Mon Sep 17 00:00:00 2001 From: Satyam Rastogi Date: Fri, 29 May 2026 17:48:23 +0530 Subject: [PATCH] feat(projects): Estimate Versions tab (history + full document view) --- src/pages/owner/OwnerProjectDetail.jsx | 97 ++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/pages/owner/OwnerProjectDetail.jsx b/src/pages/owner/OwnerProjectDetail.jsx index 1c20dcf..7351047 100644 --- a/src/pages/owner/OwnerProjectDetail.jsx +++ b/src/pages/owner/OwnerProjectDetail.jsx @@ -17,6 +17,7 @@ import { } from 'lucide-react'; import ChangeOrderDrawer from '../../components/owner/ChangeOrderDrawer'; import InvoiceDetailModal from '../../components/owner/InvoiceDetailModal'; +import EstimateDetailModal from '../../components/estimates/EstimateDetailModal'; import CreateItemModal from '../../components/owner/CreateItemModal'; import CommissionSettingsModal from '../../components/owner/CommissionSettingsModal'; import TeamManagementPanel from '../../components/owner/TeamManagementPanel'; @@ -73,6 +74,7 @@ const tabs = [ { id: 'rfis', label: 'RFIs', icon: FileText }, { id: 'milestones', label: 'Milestones', icon: Milestone }, { id: 'invoices', label: 'Invoices', icon: DollarSign }, + { id: 'estimates', label: 'Estimates', icon: FileText }, { id: 'risks', label: 'Risk & Issues', icon: AlertCircle }, { id: 'inspections', label: 'Inspections', icon: ShieldCheck }, { id: 'activity', label: 'Activity', icon: Clock }, @@ -200,6 +202,7 @@ const OwnerProjectDetail = () => { const [activeTab, setActiveTab] = useState('overview'); const [selectedCO, setSelectedCO] = useState(null); const [selectedInvoice, setSelectedInvoice] = useState(null); + const [selectedVersion, setSelectedVersion] = useState(null); const [createModalConfig, setCreateModalConfig] = useState(null); // ── Payments state ───────────────────────────────────────────────────────── @@ -1651,6 +1654,83 @@ const OwnerProjectDetail = () => { )} + {/* ESTIMATES TAB */} + {activeTab === 'estimates' && (() => { + const buildEstimate = (v) => ({ + id: `${project.id}-v${v.version}`, + clientName: project.customerName, + address: project.address, + date: v.date, + status: v.status, + total: v.total, + templateId: v.templateId, + templateName: v.templateName, + estimatedBy: (project.teamMembers || []).find(t => /Sales|Estimator/i.test(t.jobRole || ''))?.name || 'Sales Team', + roofArea: v.roofArea, + notes: v.note, + }); + const versions = [...(project.estimateVersions || [])].sort((a, b) => a.version - b.version); + return ( + +
+ +

Estimate Versions

+ + {versions.length} + +
+ {versions.length > 0 ? ( +
+ {versions.map((v) => ( +
setSelectedVersion(v)} + onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setSelectedVersion(v); } }} + > +
+ + v{v.version} + +
+
+ Version {v.version} + {v.date} + {formatCurrency(v.total)} +
+ {v.note && ( +

{v.note}

+ )} +
+
+
+ + {v.status === 'signed' ? 'Signed' : v.status === 'revised' ? 'Revised' : 'Sent'} + + +
+
+ ))} +
+ ) : ( +
+ +

No estimate versions for this project.

+

Estimate history will appear here once versions are created.

+
+ )} +
+ ); + })()} + {/* RISK & ISSUES TAB */} {activeTab === 'risks' && (
@@ -2220,6 +2300,23 @@ const OwnerProjectDetail = () => { onClose={() => setSelectedInvoice(null)} invoice={selectedInvoice} /> + setSelectedVersion(null)} + estimate={selectedVersion ? { + id: `${project.id}-v${selectedVersion.version}`, + clientName: project.customerName, + address: project.address, + date: selectedVersion.date, + status: selectedVersion.status, + total: selectedVersion.total, + templateId: selectedVersion.templateId, + templateName: selectedVersion.templateName, + estimatedBy: (project.teamMembers || []).find(t => /Sales|Estimator/i.test(t.jobRole || ''))?.name || 'Sales Team', + roofArea: selectedVersion.roofArea, + notes: selectedVersion.note, + } : null} + /> {createModalConfig && (