diff --git a/src/pages/owner/OwnerProjectDetail.jsx b/src/pages/owner/OwnerProjectDetail.jsx index e50a2e5..1c20dcf 100644 --- a/src/pages/owner/OwnerProjectDetail.jsx +++ b/src/pages/owner/OwnerProjectDetail.jsx @@ -13,7 +13,7 @@ import { ArrowLeft, CheckCircle, Clock, AlertTriangle, PauseCircle, ShieldAlert, FileText, DollarSign, GitPullRequest, AlertCircle, Activity, Milestone, 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, Info, Settings, Building2, User as UserIcon, Briefcase, Users, HardHat, Wrench, UserCheck, Wallet + Upload, Trash2, Eye, Pencil, X as XIcon, FolderOpen, Download, File, StickyNote, Banknote, ChevronLeft, ChevronDown as ChevronDownIcon, Info, Settings, Building2, User as UserIcon, Briefcase, Users, HardHat, Wrench, UserCheck, Wallet, ShieldCheck } from 'lucide-react'; import ChangeOrderDrawer from '../../components/owner/ChangeOrderDrawer'; import InvoiceDetailModal from '../../components/owner/InvoiceDetailModal'; @@ -74,6 +74,7 @@ const tabs = [ { id: 'milestones', label: 'Milestones', icon: Milestone }, { id: 'invoices', label: 'Invoices', icon: DollarSign }, { id: 'risks', label: 'Risk & Issues', icon: AlertCircle }, + { id: 'inspections', label: 'Inspections', icon: ShieldCheck }, { id: 'activity', label: 'Activity', icon: Clock }, { id: 'docs', label: 'Docs', icon: FolderOpen }, ]; @@ -192,6 +193,7 @@ const OwnerProjectDetail = () => { removeUserCommissionOverride, updateProject, addJobTeamMember, removeJobTeamMember, updateJobTeamMember, advanceLifecycleStage, + addInspection, addInspectionIssue, resolveInspectionIssue, } = useMockStore(); const navigate = useNavigate(); const { can } = usePermissions(); @@ -215,6 +217,11 @@ const OwnerProjectDetail = () => { const [commissionConfig, setCommissionConfig] = useState(null); const [commissionSettingsOpen, setCommissionSettingsOpen] = useState(false); + // ── Inspections state ───────────────────────────────────────────────────── + // issuePhotoMap: { [issueId]: [{id, url, caption}] } — session-only local photos + const [issuePhotoMap, setIssuePhotoMap] = useState({}); + const photoInputRefs = useRef({}); + // ── Docs state ──────────────────────────────────────────────────────────── const [docs, setDocs] = useState(() => seedDocsMock(projectId)); const [viewDoc, setViewDoc] = useState(null); // doc object being viewed @@ -1792,6 +1799,250 @@ const OwnerProjectDetail = () => { )} + {/* INSPECTIONS TAB */} + {activeTab === 'inspections' && (() => { + const inspections = project.inspections || []; + const isOwnerAdmin = ['OWNER', 'ADMIN'].includes(user?.role); + + // Find latest final inspection for re-inspection scheduling + const finalInspections = inspections.filter(ins => ins.kind === 'final'); + const latestFinal = finalInspections.length > 0 + ? finalInspections.reduce((latest, ins) => (ins.round || 1) > (latest.round || 1) ? ins : latest, finalInspections[0]) + : null; + const showScheduleReinspection = isOwnerAdmin && latestFinal && latestFinal.result === 'fail'; + + const handlePhotoAttach = (issueId, file) => { + if (!file) return; + const url = URL.createObjectURL(file); + const photo = { id: Date.now(), url, caption: file.name }; + setIssuePhotoMap(prev => ({ + ...prev, + [issueId]: [...(prev[issueId] || []), photo], + })); + }; + + return ( +
No inspections recorded yet.
+Damage verifications and final inspections will appear here.
++ Inspector:{' '} + {inspection.inspectorName} + {isDamage && inspection.inspectorRole && ` — ${inspection.inspectorRole}`} +
+ )} + {!isDamage && inspection.inspectorContact && ( +
+ {inspection.inspectorContact.phone && (
+
Date:{' '}{inspection.inspectionDate}
+ )} ++ {inspection.feedbackNote} +
+ )} ++ Issues ({issues.length}) +
+ {issues.map((issue) => { + const localPhotos = issuePhotoMap[issue.id] || []; + const allPhotos = [...(issue.photos || []), ...localPhotos]; + const isOpen = issue.status === 'open'; + return ( +{issue.note}
+ )} +No issues recorded for this inspection.
+ )} +