adds on /off commission for team members in jobs,when comm is on then owner can edit comm

This commit is contained in:
Satyam Rastogi
2026-05-14 19:08:31 +05:30
parent 7525871510
commit cef4be74b8
2 changed files with 123 additions and 42 deletions
+91 -10
View File
@@ -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 */}
<button
onClick={() => { setEditingRole(member.id); setActionMenu(null); }}
className="w-full px-3 py-2 text-left text-xs font-semibold text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-white/5 flex items-center gap-2 transition-colors"
>
<Shield size={12} /> Change Job Role
</button>
{/* Commission Toggle */}
{canManageCommissions && (
<button
onClick={() => { setEditingCommission(member); setActionMenu(null); }}
className="w-full px-3 py-2 text-left text-xs font-semibold text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-white/5 flex items-center gap-2 transition-colors"
>
<DollarSign size={12} /> Edit Commission
</button>
<div className="px-3 py-2 flex items-center justify-between border-t border-zinc-100 dark:border-white/5">
<div className="flex items-center gap-2">
<DollarSign size={12} className={commissionToggle[member.id] ? 'text-amber-400' : 'text-zinc-400'} />
<span className="text-xs font-semibold text-zinc-700 dark:text-zinc-300">Commission</span>
</div>
<button
onClick={(e) => { e.stopPropagation(); toggleMemberCommission(member.id); }}
className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors duration-200 focus:outline-none
${commissionToggle[member.id]
? 'bg-amber-400'
: 'bg-zinc-300 dark:bg-zinc-700'
}`}
title={commissionToggle[member.id] ? 'Commission ON' : 'Commission OFF'}
>
<motion.span
layout
transition={{ type: 'spring', stiffness: 500, damping: 35 }}
className={`inline-block h-3.5 w-3.5 rounded-full bg-white shadow-sm transform transition-transform
${commissionToggle[member.id] ? 'translate-x-4' : 'translate-x-0.5'}`}
/>
</button>
</div>
)}
{/* Edit Commission — visible only when toggle is ON */}
<AnimatePresence>
{canManageCommissions && commissionToggle[member.id] && (
<motion.div
key="edit-commission"
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.18 }}
className="overflow-hidden"
>
<button
onClick={() => { setEditingCommission(member); setActionMenu(null); }}
className="w-full px-3 py-2 text-left text-xs font-semibold text-amber-500 hover:bg-amber-50 dark:hover:bg-amber-500/10 flex items-center gap-2 transition-colors border-t border-zinc-100 dark:border-white/5"
>
<DollarSign size={12} /> Edit Commission
</button>
</motion.div>
)}
</AnimatePresence>
{/* Remove from Job */}
<button
onClick={() => { setRemoveConfirm(member); setActionMenu(null); }}
className="w-full px-3 py-2 text-left text-xs font-semibold text-red-500 hover:bg-red-50 dark:hover:bg-red-500/5 flex items-center gap-2 transition-colors"
className="w-full px-3 py-2 text-left text-xs font-semibold text-red-500 hover:bg-red-50 dark:hover:bg-red-500/5 flex items-center gap-2 transition-colors border-t border-zinc-100 dark:border-white/5"
>
<UserMinus size={12} /> Remove from Job
</button>