org team management section in org Settings module
This commit is contained in:
File diff suppressed because it is too large
Load Diff
+30
-14
@@ -5054,19 +5054,19 @@ export const MockStoreProvider = ({ children }) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const [orgMembers, setOrgMembers] = useState([
|
const [orgMembers, setOrgMembers] = useState([
|
||||||
{ id: 'om_001', userId: 'owner_001', name: 'Justin Johnson', email: 'justin@lynkeduppro.com', roleKey: 'OWNER', status: 'active', joinedAt: '2024-01-15' },
|
{ id: 'om_001', userId: 'owner_001', name: 'Justin Johnson', email: 'justin@lynkeduppro.com', roleKey: 'OWNER', status: 'active', joinedAt: '2024-01-15', lastActivity: '2025-05-12', assignedJobsCount: 14 },
|
||||||
{ id: 'om_002', userId: 'owner_002', name: 'Maria Garcia', email: 'maria@lynkeduppro.com', roleKey: 'OWNER', status: 'active', joinedAt: '2024-01-15' },
|
{ id: 'om_002', userId: 'owner_002', name: 'Maria Garcia', email: 'maria@lynkeduppro.com', roleKey: 'OWNER', status: 'active', joinedAt: '2024-01-15', lastActivity: '2025-05-10', assignedJobsCount: 8 },
|
||||||
{ id: 'om_003', userId: 'ADM01', name: 'Admin One', email: 'admin1@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-02-10' },
|
{ id: 'om_003', userId: 'ADM01', name: 'Admin One', email: 'admin1@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-02-10', lastActivity: '2025-05-11', assignedJobsCount: 6 },
|
||||||
{ id: 'om_004', userId: 'ADM02', name: 'Admin Two', email: 'admin2@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-03-01' },
|
{ id: 'om_004', userId: 'ADM02', name: 'Admin Two', email: 'admin2@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-03-01', lastActivity: '2025-05-09', assignedJobsCount: 4 },
|
||||||
{ id: 'om_005', userId: 'ADM03', name: 'Admin Three', email: 'admin3@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-04-20' },
|
{ id: 'om_005', userId: 'ADM03', name: 'Admin Three', email: 'admin3@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-04-20', lastActivity: '2025-05-08', assignedJobsCount: 3 },
|
||||||
{ id: 'om_006', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', roleKey: 'SALES_REP', status: 'active', joinedAt: '2024-03-15', individualCommission: { enabled: true, type: 'percent_gross', rate: 12 } },
|
{ id: 'om_006', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', roleKey: 'SALES_REP', status: 'active', joinedAt: '2024-03-15', lastActivity: '2025-05-12', assignedJobsCount: 9, individualCommission: { enabled: true, type: 'percent_gross', rate: 12 } },
|
||||||
{ id: 'om_007', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', roleKey: 'SALES_REP', status: 'active', joinedAt: '2024-04-01', individualCommission: { enabled: true, type: 'flat', rate: 2000 } },
|
{ id: 'om_007', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', roleKey: 'SALES_REP', status: 'active', joinedAt: '2024-04-01', lastActivity: '2025-05-11', assignedJobsCount: 7, individualCommission: { enabled: true, type: 'flat', rate: 2000 } },
|
||||||
{ id: 'om_008', userId: 'FA001', name: 'Frank Agent', email: 'agent1@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-05-10' },
|
{ id: 'om_008', userId: 'FA001', name: 'Frank Agent', email: 'agent1@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-05-10', lastActivity: '2025-05-12', assignedJobsCount: 2 },
|
||||||
{ id: 'om_009', userId: 'FA002', name: 'Fiona Field', email: 'agent2@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-05-15' },
|
{ id: 'om_009', userId: 'FA002', name: 'Fiona Field', email: 'agent2@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-05-15', lastActivity: '2025-05-07', assignedJobsCount: 3 },
|
||||||
{ id: 'om_010', userId: 'FA003', name: 'Fred Flyer', email: 'agent3@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-06-01' },
|
{ id: 'om_010', userId: 'FA003', name: 'Fred Flyer', email: 'agent3@plano.com', roleKey: 'CANVASSER', status: 'invited', joinedAt: '2024-06-01', lastActivity: null, assignedJobsCount: 0 },
|
||||||
{ id: 'om_011', userId: 'FA004', name: 'Felicity Fast', email: 'agent4@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-06-10' },
|
{ id: 'om_011', userId: 'FA004', name: 'Felicity Fast', email: 'agent4@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-06-10', lastActivity: '2025-05-06', assignedJobsCount: 1 },
|
||||||
{ id: 'om_012', userId: 'FA005', name: 'Felix Fixer', email: 'agent5@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-07-01' },
|
{ id: 'om_012', userId: 'FA005', name: 'Felix Fixer', email: 'agent5@plano.com', roleKey: 'CANVASSER', status: 'suspended', joinedAt: '2024-07-01', lastActivity: '2025-03-20', assignedJobsCount: 0 },
|
||||||
{ id: 'om_013', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', roleKey: 'SUBCONTRACTOR', status: 'active', joinedAt: '2024-08-01' },
|
{ id: 'om_013', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', roleKey: 'SUBCONTRACTOR', status: 'active', joinedAt: '2024-08-01', lastActivity: '2025-05-10', assignedJobsCount: 5 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Initialize properties once
|
// Initialize properties once
|
||||||
@@ -5322,13 +5322,29 @@ export const MockStoreProvider = ({ children }) => {
|
|||||||
setOrgMembers(prev => prev.map(m => m.id === memberId ? { ...m, ...data } : m));
|
setOrgMembers(prev => prev.map(m => m.id === memberId ? { ...m, ...data } : m));
|
||||||
},
|
},
|
||||||
addOrgMember: (member) => {
|
addOrgMember: (member) => {
|
||||||
const newMember = { ...member, id: `om_${Date.now()}`, joinedAt: new Date().toISOString().slice(0, 10) };
|
const newMember = {
|
||||||
|
...member,
|
||||||
|
id: `om_${Date.now()}`,
|
||||||
|
joinedAt: new Date().toISOString().slice(0, 10),
|
||||||
|
lastActivity: null,
|
||||||
|
assignedJobsCount: 0,
|
||||||
|
status: member.status || 'invited',
|
||||||
|
};
|
||||||
setOrgMembers(prev => [...prev, newMember]);
|
setOrgMembers(prev => [...prev, newMember]);
|
||||||
return newMember;
|
return newMember;
|
||||||
},
|
},
|
||||||
removeOrgMember: (memberId) => {
|
removeOrgMember: (memberId) => {
|
||||||
setOrgMembers(prev => prev.filter(m => m.id !== memberId));
|
setOrgMembers(prev => prev.filter(m => m.id !== memberId));
|
||||||
},
|
},
|
||||||
|
suspendOrgMember: (memberId) => {
|
||||||
|
setOrgMembers(prev => prev.map(m => m.id === memberId ? { ...m, status: 'suspended' } : m));
|
||||||
|
},
|
||||||
|
reactivateOrgMember: (memberId) => {
|
||||||
|
setOrgMembers(prev => prev.map(m => m.id === memberId ? { ...m, status: 'active' } : m));
|
||||||
|
},
|
||||||
|
resendOrgInvite: (memberId) => {
|
||||||
|
toast.success('Invite resent successfully');
|
||||||
|
},
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</MockStoreContext.Provider>
|
</MockStoreContext.Provider>
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import {
|
|||||||
Search, Check, X, Plus, Trash2, Edit3, Save, User, Crown,
|
Search, Check, X, Plus, Trash2, Edit3, Save, User, Crown,
|
||||||
ToggleLeft, ToggleRight, Info, ArrowRight, Mail, Phone, MapPin, Clock,
|
ToggleLeft, ToggleRight, Info, ArrowRight, Mail, Phone, MapPin, Clock,
|
||||||
AlertTriangle, UserPlus, UserMinus, Eye, Pencil, Lock,
|
AlertTriangle, UserPlus, UserMinus, Eye, Pencil, Lock,
|
||||||
RotateCcw, Filter, CheckSquare, Square, BarChart3, History
|
RotateCcw, Filter, CheckSquare, Square, BarChart3, History, UsersRound
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useMockStore } from '../../data/mockStore';
|
import { useMockStore } from '../../data/mockStore';
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
|
import { usePermissions } from '../../hooks/usePermissions';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { computeRolePermissionStats } from '../../utils/permissions';
|
import { computeRolePermissionStats } from '../../utils/permissions';
|
||||||
|
import OrgTeamManagement from '../../components/owner/OrgTeamManagement';
|
||||||
|
|
||||||
const COMMISSION_TYPES = [
|
const COMMISSION_TYPES = [
|
||||||
{ value: 'flat', label: 'Flat Amount', hint: 'Fixed dollar amount' },
|
{ value: 'flat', label: 'Flat Amount', hint: 'Fixed dollar amount' },
|
||||||
@@ -984,9 +986,11 @@ const UserOverridesSection = ({ members, roles, orgDefaults, roleOverrides, user
|
|||||||
const OrgSettings = () => {
|
const OrgSettings = () => {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const store = useMockStore();
|
const store = useMockStore();
|
||||||
|
const { orgRoleKey } = usePermissions();
|
||||||
|
|
||||||
const [openSections, setOpenSections] = useState({
|
const [openSections, setOpenSections] = useState({
|
||||||
org: true,
|
org: true,
|
||||||
|
team: false,
|
||||||
roles: false,
|
roles: false,
|
||||||
access: false,
|
access: false,
|
||||||
commission: false,
|
commission: false,
|
||||||
@@ -1009,6 +1013,29 @@ const OrgSettings = () => {
|
|||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'team',
|
||||||
|
icon: UsersRound,
|
||||||
|
title: 'Organization Team Management',
|
||||||
|
subtitle: `${store.orgMembers.length} members — invite, manage roles, and administer your team`,
|
||||||
|
color: '#8b5cf6',
|
||||||
|
content: (
|
||||||
|
<OrgTeamManagement
|
||||||
|
members={store.orgMembers}
|
||||||
|
roles={store.orgRoles}
|
||||||
|
users={store.users}
|
||||||
|
permissions={store.orgPermissions}
|
||||||
|
permissionModules={store.PERMISSION_MODULES}
|
||||||
|
currentUserRoleKey={orgRoleKey}
|
||||||
|
onAddMember={store.addOrgMember}
|
||||||
|
onUpdateMember={store.updateOrgMember}
|
||||||
|
onRemoveMember={store.removeOrgMember}
|
||||||
|
onSuspendMember={store.suspendOrgMember}
|
||||||
|
onReactivateMember={store.reactivateOrgMember}
|
||||||
|
onResendInvite={store.resendOrgInvite}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'roles',
|
id: 'roles',
|
||||||
icon: Crown,
|
icon: Crown,
|
||||||
@@ -1093,11 +1120,12 @@ const OrgSettings = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Stats */}
|
{/* Quick Stats */}
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mt-6">
|
<div className="grid grid-cols-2 md:grid-cols-5 gap-3 mt-6">
|
||||||
{[
|
{[
|
||||||
{ label: 'Roles', value: store.orgRoles.length, color: '#fda913' },
|
{ label: 'Roles', value: store.orgRoles.length, color: '#fda913' },
|
||||||
{ label: 'Members', value: store.orgMembers.length, color: '#3b82f6' },
|
{ label: 'Members', value: store.orgMembers.length, color: '#3b82f6' },
|
||||||
{ label: 'Modules', value: Object.keys(store.PERMISSION_MODULES).length, color: '#10b981' },
|
{ label: 'Active', value: store.orgMembers.filter(m => m.status === 'active').length, color: '#10b981' },
|
||||||
|
{ label: 'Modules', value: Object.keys(store.PERMISSION_MODULES).length, color: '#8b5cf6' },
|
||||||
{ label: 'Commission Overrides', value: store.roleCommissionOverrides.length + store.userCommissionOverrides.length, color: '#a855f7' },
|
{ label: 'Commission Overrides', value: store.roleCommissionOverrides.length + store.userCommissionOverrides.length, color: '#a855f7' },
|
||||||
].map(stat => (
|
].map(stat => (
|
||||||
<div key={stat.label} className="px-4 py-3 rounded-xl bg-white dark:bg-white/[0.03] border border-zinc-200 dark:border-white/5 shadow-sm dark:shadow-none">
|
<div key={stat.label} className="px-4 py-3 rounded-xl bg-white dark:bg-white/[0.03] border border-zinc-200 dark:border-white/5 shadow-sm dark:shadow-none">
|
||||||
|
|||||||
Reference in New Issue
Block a user