From cef4be74b894bfff0032e353ab53879705066a27 Mon Sep 17 00:00:00 2001 From: Satyam Rastogi Date: Thu, 14 May 2026 19:08:31 +0530 Subject: [PATCH] adds on /off commission for team members in jobs,when comm is on then owner can edit comm --- src/components/owner/TeamManagementPanel.jsx | 101 +++++++++++++++++-- src/data/mockStore.jsx | 64 ++++++------ 2 files changed, 123 insertions(+), 42 deletions(-) diff --git a/src/components/owner/TeamManagementPanel.jsx b/src/components/owner/TeamManagementPanel.jsx index 0702fe4..48fa5ac 100644 --- a/src/components/owner/TeamManagementPanel.jsx +++ b/src/components/owner/TeamManagementPanel.jsx @@ -1,10 +1,10 @@ -import React, { useState, useMemo, useEffect, useRef } from 'react'; +import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react'; import { createPortal } from 'react-dom'; import { motion, AnimatePresence } from 'framer-motion'; import { UserPlus, UserMinus, Shield, ChevronDown, Search, Mail, X as XIcon, Users, Briefcase, DollarSign, MoreVertical, - Check, Clock, Crown, ChevronUp + Check, Clock, Crown, ChevronUp, ToggleLeft, ToggleRight } from 'lucide-react'; const JOB_ROLES = [ @@ -100,6 +100,45 @@ const TeamManagementPanel = ({ const menuRef = useRef(null); + // ── Commission toggle default ───────────────────────────────────────── + // Reads commissionEnabled directly from the member data in mockStore. + // Falls back to true if a jobCommission override is explicitly set. + const getMemberCommissionDefault = useCallback((member) => { + if (typeof member.commissionEnabled === 'boolean') return member.commissionEnabled; + return !!member.jobCommission; // fallback for legacy data without the field + }, []); + + // Per-member commission toggle: { [memberId]: boolean } + const [commissionToggle, setCommissionToggle] = useState(() => { + const init = {}; + teamMembers.forEach(m => { init[m.id] = getMemberCommissionDefault(m); }); + return init; + }); + + // Keep new members initialized when teamMembers changes + useEffect(() => { + setCommissionToggle(prev => { + const next = { ...prev }; + let changed = false; + teamMembers.forEach(m => { + if (!(m.id in next)) { + next[m.id] = getMemberCommissionDefault(m); + changed = true; + } + }); + return changed ? next : prev; + }); + }, [teamMembers, getMemberCommissionDefault]); + + // Toggle and persist back to store so the change survives re-renders + const toggleMemberCommission = useCallback((memberId) => { + setCommissionToggle(prev => { + const next = { ...prev, [memberId]: !prev[memberId] }; + onUpdateMember(project.id, memberId, { commissionEnabled: next[memberId] }); + return next; + }); + }, [project.id, onUpdateMember]); + const canManageTeam = ['OWNER', 'ADMIN'].includes(userRole); const canManageCommissions = ['OWNER', 'ADMIN'].includes(userRole); const canViewCommissions = ['OWNER', 'ADMIN', 'CONTRACTOR'].includes(userRole); @@ -258,25 +297,67 @@ const TeamManagementPanel = ({ animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.95, y: -4 }} transition={{ duration: 0.15 }} - className="absolute right-0 top-8 z-30 w-44 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-white/10 rounded-xl shadow-2xl overflow-hidden" + className="absolute right-0 top-8 z-30 w-52 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-white/10 rounded-xl shadow-2xl overflow-hidden" > + {/* Change Job Role */} + + {/* Commission Toggle */} {canManageCommissions && ( - +
+
+ + Commission +
+ +
)} + + {/* Edit Commission — visible only when toggle is ON */} + + {canManageCommissions && commissionToggle[member.id] && ( + + + + )} + + + {/* Remove from Job */} diff --git a/src/data/mockStore.jsx b/src/data/mockStore.jsx index ec359a9..0bedd8a 100644 --- a/src/data/mockStore.jsx +++ b/src/data/mockStore.jsx @@ -2068,11 +2068,11 @@ const MOCK_PROJECTS = [ { name: 'Material Order PO-2604', url: '#', type: 'invoice' } ], teamMembers: [ - { id: 'jtm_001_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', jobCommission: { type: 'percent_gross', rate: 12 } }, - { id: 'jtm_001_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null }, - { id: 'jtm_001_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', jobCommission: null }, - { id: 'jtm_001_4', userId: 'e1', name: 'Frank Agent', email: 'agent1@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', jobCommission: null }, - { id: 'jtm_001_5', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'pending', jobCommission: null } + { id: 'jtm_001_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: { type: 'percent_gross', rate: 12 } }, + { id: 'jtm_001_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, + { id: 'jtm_001_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null }, + { id: 'jtm_001_4', userId: 'e1', name: 'Frank Agent', email: 'agent1@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, + { id: 'jtm_001_5', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'pending', commissionEnabled: true, jobCommission: null } ] }, @@ -2124,8 +2124,8 @@ const MOCK_PROJECTS = [ { name: 'Warranty Registration', url: '#', type: 'warranty' } ], teamMembers: [ - { id: 'jtm_002_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', jobCommission: null }, - { id: 'jtm_002_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null } + { id: 'jtm_002_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, + { id: 'jtm_002_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, @@ -2203,9 +2203,9 @@ const MOCK_PROJECTS = [ { name: 'Equipment Spec Sheets', url: '#', type: 'specification' } ], teamMembers: [ - { id: 'jtm_003_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', jobCommission: { type: 'flat', rate: 2000 } }, - { id: 'jtm_003_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Project Manager', status: 'active', jobCommission: null }, - { id: 'jtm_003_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', jobCommission: null } + { id: 'jtm_003_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: { type: 'flat', rate: 2000 } }, + { id: 'jtm_003_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Project Manager', status: 'active', commissionEnabled: false, jobCommission: null }, + { id: 'jtm_003_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null } ] }, @@ -2294,9 +2294,9 @@ const MOCK_PROJECTS = [ { name: 'Asbestos Abatement Report', url: '#', type: 'report' } ], teamMembers: [ - { id: 'jtm_004_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', jobCommission: { type: 'percent_net', rate: 8 } }, - { id: 'jtm_004_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null }, - { id: 'jtm_004_3', userId: 'e2', name: 'Fiona Field', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Inspector', status: 'active', jobCommission: null } + { id: 'jtm_004_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', commissionEnabled: true, jobCommission: { type: 'percent_net', rate: 8 } }, + { id: 'jtm_004_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, + { id: 'jtm_004_3', userId: 'e2', name: 'Fiona Field', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Inspector', status: 'active', commissionEnabled: false, jobCommission: null } ] }, @@ -2372,8 +2372,8 @@ const MOCK_PROJECTS = [ { name: 'Site Photos - Defective Panels', url: '#', type: 'photos' } ], teamMembers: [ - { id: 'jtm_005_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', jobCommission: null }, - { id: 'jtm_005_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null } + { id: 'jtm_005_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, + { id: 'jtm_005_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, @@ -2423,8 +2423,8 @@ const MOCK_PROJECTS = [ { name: 'Load Calculation Report', url: '#', type: 'report' } ], teamMembers: [ - { id: 'jtm_006_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', jobCommission: null }, - { id: 'jtm_006_2', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null } + { id: 'jtm_006_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, + { id: 'jtm_006_2', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, @@ -2526,10 +2526,10 @@ const MOCK_PROJECTS = [ { name: 'Safety Plan - Fall Protection', url: '#', type: 'safety' } ], teamMembers: [ - { id: 'jtm_007_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', jobCommission: { type: 'percent_gross', rate: 5 } }, - { id: 'jtm_007_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null }, - { id: 'jtm_007_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', jobCommission: null }, - { id: 'jtm_007_4', userId: 'e4', name: 'Felicity Fast', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', jobCommission: null } + { id: 'jtm_007_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', commissionEnabled: true, jobCommission: { type: 'percent_gross', rate: 5 } }, + { id: 'jtm_007_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, + { id: 'jtm_007_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null }, + { id: 'jtm_007_4', userId: 'e4', name: 'Felicity Fast', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null } ] }, @@ -2587,8 +2587,8 @@ const MOCK_PROJECTS = [ { name: 'Soil Analysis Report', url: '#', type: 'report' } ], teamMembers: [ - { id: 'jtm_008_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', jobCommission: null }, - { id: 'jtm_008_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null } + { id: 'jtm_008_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, + { id: 'jtm_008_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, @@ -2639,8 +2639,8 @@ const MOCK_PROJECTS = [ { name: 'Energy Rating Certificates', url: '#', type: 'certificate' } ], teamMembers: [ - { id: 'jtm_009_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', jobCommission: null }, - { id: 'jtm_009_2', userId: 'e2', name: 'Fiona Field', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Inspector', status: 'active', jobCommission: null } + { id: 'jtm_009_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, + { id: 'jtm_009_2', userId: 'e2', name: 'Fiona Field', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Inspector', status: 'active', commissionEnabled: false, jobCommission: null } ] }, @@ -2691,9 +2691,9 @@ const MOCK_PROJECTS = [ { name: 'Final Payment Release', url: '#', type: 'invoice' } ], teamMembers: [ - { id: 'jtm_010_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', jobCommission: { type: 'flat', rate: 1500 } }, - { id: 'jtm_010_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null }, - { id: 'jtm_010_3', userId: 'e5', name: 'Felix Fixer', email: 'agent5@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'pending', jobCommission: null } + { id: 'jtm_010_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: { type: 'flat', rate: 1500 } }, + { id: 'jtm_010_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, + { id: 'jtm_010_3', userId: 'e5', name: 'Felix Fixer', email: 'agent5@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'pending', commissionEnabled: false, jobCommission: null } ] }, @@ -2752,8 +2752,8 @@ const MOCK_PROJECTS = [ { name: 'RFI #2 - Waterproofing Method', url: '#', type: 'rfi' } ], teamMembers: [ - { id: 'jtm_011_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', jobCommission: null }, - { id: 'jtm_011_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', jobCommission: null } + { id: 'jtm_011_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', commissionEnabled: false, jobCommission: null }, + { id: 'jtm_011_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, @@ -2806,8 +2806,8 @@ const MOCK_PROJECTS = [ { name: 'Material Delivery Receipt', url: '#', type: 'receipt' } ], teamMembers: [ - { id: 'jtm_012_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', jobCommission: null }, - { id: 'jtm_012_2', userId: 'e3', name: 'Fred Flyer', email: 'agent3@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', jobCommission: null } + { id: 'jtm_012_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, + { id: 'jtm_012_2', userId: 'e3', name: 'Fred Flyer', email: 'agent3@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: false, jobCommission: null } ] } ];