diff --git a/src/pages/owner/OwnerProjectDetail.jsx b/src/pages/owner/OwnerProjectDetail.jsx index f89007c..b450ff7 100644 --- a/src/pages/owner/OwnerProjectDetail.jsx +++ b/src/pages/owner/OwnerProjectDetail.jsx @@ -12,12 +12,13 @@ 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 + Upload, Trash2, Eye, Pencil, X as XIcon, FolderOpen, Download, File, StickyNote, Banknote, ChevronLeft, ChevronDown as ChevronDownIcon, Info, Settings, Building2, User as UserIcon, Briefcase, Users } from 'lucide-react'; import ChangeOrderDrawer from '../../components/owner/ChangeOrderDrawer'; import InvoiceDetailModal from '../../components/owner/InvoiceDetailModal'; import CreateItemModal from '../../components/owner/CreateItemModal'; import CommissionSettingsModal from '../../components/owner/CommissionSettingsModal'; +import TeamManagementPanel from '../../components/owner/TeamManagementPanel'; import { usePermissions } from '../../hooks/usePermissions'; import { resolveCommission, findAssignedUserRoleKey } from '../../utils/commissionResolver'; @@ -66,6 +67,7 @@ const statusConfig = { const tabs = [ { id: 'overview', label: 'Overview', icon: Activity }, { id: 'budget', label: 'Budget & Costs', icon: DollarSign }, + { id: 'team', label: 'Team', icon: Users }, { id: 'changeOrders', label: 'Change Orders', icon: GitPullRequest }, { id: 'rfis', label: 'RFIs', icon: FileText }, { id: 'milestones', label: 'Milestones', icon: Milestone }, @@ -137,11 +139,12 @@ const OwnerProjectDetail = () => { const { projectId } = useParams(); const { user } = useAuth(); const { - projects, vendors, personnel, + projects, vendors, personnel, users, orgCommissionDefaults, userCommissionOverrides, roleCommissionOverrides, orgMembers, orgRoles, updateOrgCommissionDefaults, setUserCommissionOverride, removeUserCommissionOverride, updateProject, + addJobTeamMember, removeJobTeamMember, updateJobTeamMember, } = useMockStore(); const navigate = useNavigate(); const { can } = usePermissions(); @@ -245,6 +248,16 @@ const OwnerProjectDetail = () => { projects.find(p => p.id === projectId && p.ownerId === user?.id) , [projects, projectId, user]); + const teamFinancials = useMemo(() => { + if (!project) return { totalReceived: 0, grossProfit: 0 }; + const paid = (project.paymentSchedule || []) + .filter(ps => ps.status === 'paid') + .reduce((s, ps) => s + (ps.amount || 0), 0); + const tr = paid || (project.budget || 0); + const totalCosts = Number(project.actualCost ?? project.spent) || 0; + return { totalReceived: tr, grossProfit: tr - totalCosts }; + }, [project]); + // ── Commission hierarchy: Job → Individual → Role → Org ────────────── const jobCommissionOverride = useMemo(() => { if (commissionConfig) return commissionConfig; @@ -1620,6 +1633,27 @@ const OwnerProjectDetail = () => { )} )} + + {activeTab === 'team' && ( + + + + )} diff --git a/src/pages/owner/OwnerProjectList.jsx b/src/pages/owner/OwnerProjectList.jsx index 9eec28a..cc03fad 100644 --- a/src/pages/owner/OwnerProjectList.jsx +++ b/src/pages/owner/OwnerProjectList.jsx @@ -3,10 +3,9 @@ import { useNavigate } from 'react-router-dom'; import { useAuth } from '../../context/AuthContext'; import { useMockStore } from '../../data/mockStore'; import { SpotlightCard } from '../../components/SpotlightCard'; -import TeamManagementPanel from '../../components/owner/TeamManagementPanel'; import { Search, ChevronRight, ArrowUpDown, Briefcase, LayoutGrid, - CheckCircle, Clock, AlertTriangle, PauseCircle, ShieldAlert, User, Users, ChevronDown + CheckCircle, Clock, AlertTriangle, PauseCircle, ShieldAlert, User } from 'lucide-react'; const statusConfig = { @@ -50,12 +49,7 @@ function getPipelineHealth(lead) { const OwnerProjectList = () => { const { user } = useAuth(); - const { - projects, vendors, kanbanLeads, kanbanColumns, - users, personnel, orgCommissionDefaults, userCommissionOverrides, - roleCommissionOverrides, orgMembers, - addJobTeamMember, removeJobTeamMember, updateJobTeamMember, - } = useMockStore(); + const { projects, vendors, kanbanLeads, kanbanColumns } = useMockStore(); const navigate = useNavigate(); const [activeSection, setActiveSection] = useState('construction'); // 'construction' | 'pipeline' @@ -75,23 +69,6 @@ const OwnerProjectList = () => { projects.filter(p => p.ownerId === user?.id) , [projects, user]); - // ── Team Management ── - const [selectedProjectId, setSelectedProjectId] = useState(null); - const selectedProject = useMemo(() => { - const id = selectedProjectId || ownerProjects.find(p => p.status === 'active')?.id; - return ownerProjects.find(p => p.id === id) || ownerProjects[0] || null; - }, [selectedProjectId, ownerProjects]); - - const teamFinancials = useMemo(() => { - if (!selectedProject) return { totalReceived: 0, grossProfit: 0 }; - const paid = (selectedProject.paymentSchedule || []) - .filter(ps => ps.status === 'paid') - .reduce((s, ps) => s + (ps.amount || 0), 0); - const totalReceived = paid || (selectedProject.budget || 0); - const totalCosts = Number(selectedProject.actualCost ?? selectedProject.spent) || 0; - return { totalReceived, grossProfit: totalReceived - totalCosts }; - }, [selectedProject]); - // Apply filters const filteredProjects = useMemo(() => { return ownerProjects.filter(p => { @@ -658,50 +635,6 @@ const OwnerProjectList = () => { >)} - {/* ── Owner's Box — Team Management ── */} - {selectedProject && ( - - - - - Team Management - - - setSelectedProjectId(e.target.value)} - className="appearance-none pl-3 pr-8 py-2 text-sm font-semibold rounded-xl bg-zinc-100 dark:bg-white/5 border border-zinc-200 dark:border-white/10 text-zinc-900 dark:text-white outline-none focus:border-violet-500 transition-colors cursor-pointer" - > - {ownerProjects.map(p => ( - - {p.projectType} — {p.address?.split(',')[0] || p.id} - - ))} - - - - - - - - - )} - igotsar.matyas | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?