From c210f640d87c7ac16f24f6a4b32563e1e97a6dd4 Mon Sep 17 00:00:00 2001 From: laxman h Date: Fri, 27 Mar 2026 23:06:08 +0530 Subject: [PATCH] nbased on new hierach ui updated still flow not stable --- src/components/applications/MasterPage.tsx | 169 +++++++--- .../applications/MasterPage/ASMDialog.tsx | 44 +-- .../applications/MasterPage/ASMManagement.tsx | 8 +- .../MasterPage/LocationManagement.tsx | 26 +- .../applications/MasterPage/RegionDialog.tsx | 92 ++++-- .../MasterPage/RegionalManagement.tsx | 12 +- .../applications/MasterPage/ZMDialog.tsx | 257 +++++++++++++++ .../applications/MasterPage/ZMManagement.tsx | 4 +- .../applications/MasterPage/ZoneDetails.tsx | 12 +- .../applications/MasterPage/ZoneDialog.tsx | 126 ++++--- .../applications/MasterPage/ZonesOverview.tsx | 14 +- src/hooks/useLocationHelpers.ts | 14 +- src/hooks/useMasterData.ts | 310 ++++++------------ src/store/slices/masterSlice.ts | 42 ++- 14 files changed, 723 insertions(+), 407 deletions(-) create mode 100644 src/components/applications/MasterPage/ZMDialog.tsx diff --git a/src/components/applications/MasterPage.tsx b/src/components/applications/MasterPage.tsx index df1ab97..8176586 100644 --- a/src/components/applications/MasterPage.tsx +++ b/src/components/applications/MasterPage.tsx @@ -3,9 +3,7 @@ import { useSelector } from 'react-redux'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs'; -import { - Globe, Shield, Clock, Mail, MapPin, SlidersHorizontal -} from 'lucide-react'; +import { Globe, Shield, Clock, Mail, MapPin, SlidersHorizontal } from 'lucide-react'; import { Badge } from '../ui/badge'; import { toast } from 'sonner'; @@ -25,6 +23,7 @@ import { RolePermissions } from './MasterPage/RolePermissions'; import { EmailTemplates } from './MasterPage/EmailTemplates'; import { LocationManagement } from './MasterPage/LocationManagement'; import { ASMDialog } from './MasterPage/ASMDialog'; +import { ZMDialog } from './MasterPage/ZMDialog'; import { ZoneDialog } from './MasterPage/ZoneDialog'; import { RegionDialog } from './MasterPage/RegionDialog'; import { TemplateDialog } from './MasterPage/TemplateDialog'; @@ -36,10 +35,12 @@ export const MasterPage: React.FC = () => { const { fetchInitialData } = useMasterData(); const { asms, zonalManagerMappings, - allStates, allDistricts, + allDistricts, + users, loading } = useSelector((state: RootState) => state.master); + // Tab & Selection State const [activeTab, setActiveTab] = useState('hierarchy'); const [selectedZone, setSelectedZone] = useState('all'); @@ -62,12 +63,25 @@ export const MasterPage: React.FC = () => { const [selectedASMRegion, setSelectedASMRegion] = useState(''); const [selectedASMStates, setSelectedASMStates] = useState([]); const [selectedASMDistricts, setSelectedASMDistricts] = useState([]); + + // ZM Management State + const [showZMDialog, setShowZMDialog] = useState(false); + const [editingZMId, setEditingZMId] = useState(null); + const [zmManagerId, setZmManagerId] = useState(''); + const [zmName, setZmName] = useState(''); + const [zmCode, setZmCode] = useState(''); + const [zmEmployeeId, setZmEmployeeId] = useState(''); + const [zmStatus, setZmStatus] = useState<'active' | 'inactive'>('active'); + const [selectedZMZone, setSelectedZMZone] = useState(''); + const [selectedZMStates, setSelectedZMStates] = useState([]); + const [selectedZMDistricts, setSelectedZMDistricts] = useState([]); // Form State (Zone) const [editingZoneId, setEditingZoneId] = useState(null); const [zoneName, setZoneName] = useState(''); const [zoneCode, setZoneCode] = useState(''); const [zoneDescription, setZoneDescription] = useState(''); + const [zonalBusinessHeadId, setZonalBusinessHeadId] = useState(''); // Form State (Region) const [editingRegionId, setEditingRegionId] = useState(null); @@ -76,7 +90,7 @@ export const MasterPage: React.FC = () => { const [regionDescription, setRegionDescription] = useState(''); const [selectedRegionZone, setSelectedRegionZone] = useState(''); const [regionalManagerId, setRegionalManagerId] = useState(''); - const [selectedRegionStates, setSelectedRegionStates] = useState([]); + const [selectedRegionDistricts, setSelectedRegionDistricts] = useState([]); // Form State (Template) const [editingTemplate, setEditingTemplate] = useState(null); @@ -104,9 +118,10 @@ export const MasterPage: React.FC = () => { const map: Record = {}; [...asms, ...zonalManagerMappings].forEach(m => { const dists = (m as any).areasManaged || (m as any).districts || []; - dists.forEach((d: string) => { - if (!map[d]) map[d] = []; - if (!map[d].includes(m.name)) map[d].push(m.name); + dists.forEach((d: any) => { + const id = typeof d === 'string' ? d : d.id; + if (!map[id]) map[id] = []; + if (!map[id].includes(m.name)) map[id].push(m.name); }); }); return map; @@ -114,12 +129,9 @@ export const MasterPage: React.FC = () => { const getDistrictsForSelectedState = useCallback((stateName: string) => { return allDistricts - .filter(d => { - const sObj = allStates.find(s => s.id === d.stateId); - return sObj?.stateName === stateName; - }) - .map(d => d.districtName); - }, [allDistricts, allStates]); + .filter(d => d.stateName?.toUpperCase() === stateName?.toUpperCase()) + .map(d => ({ id: d.id, name: d.name })); + }, [allDistricts]); // Handlers const handleSaveASM = async () => { @@ -148,13 +160,51 @@ export const MasterPage: React.FC = () => { setSelectedASMZone(asm.zoneId); setSelectedASMRegion(asm.regionId); setSelectedASMStates(asm.stateNames || []); - setSelectedASMDistricts(asm.areasManaged || []); + setSelectedASMDistricts(asm.areasManaged?.map((a: any) => a.id) || []); setShowASMDialog(true); }; + const handleEditZM = (zm: any) => { + setEditingZMId(zm.id); + setZmManagerId(zm.id); + setZmName(zm.name); + setZmCode(zm.code === 'N/A' ? '' : zm.code); + setZmEmployeeId(zm.code === 'N/A' ? '' : zm.code); + setZmStatus(zm.status.toLowerCase() as 'active' | 'inactive'); + setSelectedZMZone(zm.zoneId || ''); + setSelectedZMStates(zm.stateNames || []); + setSelectedZMDistricts(zm.districts?.map((d: any) => d.id) || []); + setShowZMDialog(true); + }; + + const handleSaveZM = async () => { + if (!zmManagerId || !selectedZMZone) { + toast.error('Manager and Zone are required'); + return; + } + try { + const payload = { + userId: zmManagerId, + roleCode: 'DD-ZM', + zmCode, + districts: selectedZMDistricts, + status: zmStatus, + locationId: selectedZMZone // ZM primary location is the zone + }; + + // Use the generic saveASM method which I generalized on the backend + const res = await masterService.saveASM(payload) as any; + if (res.success) { + toast.success(`Zonal Manager ${editingZMId ? 'updated' : 'assigned'} successfully`); + setShowZMDialog(false); + fetchInitialData(); + } + } catch (error) { toast.error('Failed to save Zonal Manager'); } + }; + const handleSaveZone = async () => { try { - const payload = { id: editingZoneId, name: zoneName, code: zoneCode, description: zoneDescription }; + const payload = { id: editingZoneId, name: zoneName, code: zoneCode, description: zoneDescription, managerId: zonalBusinessHeadId }; const res = await masterService.saveZone(payload) as any; if (res.success) { toast.success('Zone saved successfully'); @@ -166,15 +216,15 @@ export const MasterPage: React.FC = () => { const handleSaveRegion = async () => { try { - const payload = { - id: editingRegionId, - zoneId: selectedRegionZone, - name: regionName, - code: regionCode, - description: regionDescription, - managerId: regionalManagerId, - states: selectedRegionStates - }; + const payload = { + name: regionName, + code: regionCode, + description: regionDescription, + parentId: selectedRegionZone, + managerId: regionalManagerId, + districts: selectedRegionDistricts, + status: 'Active' + }; const res = await masterService.saveRegion(payload) as any; if (res.success) { toast.success('Region saved successfully'); @@ -272,25 +322,35 @@ export const MasterPage: React.FC = () => { setSelectedZone(selectedZone === id ? 'all' : id)} /> -
-
- { setEditingZoneId(null); setZoneName(''); setZoneCode(''); setZoneDescription(''); setShowZoneDialog(true); }} - onEditZone={(z) => { setEditingZoneId(z.id); setZoneName(z.name); setZoneCode(z.code); setZoneDescription(z.description); setShowZoneDialog(true); }} /> -
-
- { setEditingRegionId(null); setRegionName(''); setRegionCode(''); setSelectedRegionZone(selectedZone === 'all' ? '' : selectedZone); setRegionalManagerId(''); setSelectedRegionStates([]); setShowRegionDialog(true); }} - onEditRegion={(r) => { setEditingRegionId(r.id); setRegionName(r.name); setRegionCode(r.code); setSelectedRegionZone(r.zoneId); setRegionalManagerId(r.managerId || ''); setSelectedRegionStates(r.states || []); setShowRegionDialog(true); }} - onDeleteRegion={() => toast.error('Regional office deletion is restricted via portal')} /> -
-
+ { setEditingZoneId(null); setZoneName(''); setZoneCode(''); setZoneDescription(''); setZonalBusinessHeadId(''); setShowZoneDialog(true); }} + onEditZone={(z) => { setEditingZoneId(z.id); setZoneName(z.name); setZoneCode(z.code); setZoneDescription(z.description); setZonalBusinessHeadId(z.zonalBusinessHead?.id || ''); setShowZoneDialog(true); }} /> + + { setEditingRegionId(null); setRegionName(''); setRegionCode(''); setSelectedRegionZone(selectedZone === 'all' ? '' : selectedZone); setRegionalManagerId(''); setSelectedRegionDistricts([]); setShowRegionDialog(true); }} + onEditRegion={(r) => { + setEditingRegionId(r.id); + setRegionName(r.name); + setRegionCode(r.code); + setSelectedRegionZone(r.zoneId); + setRegionalManagerId(r.regionalManager?.id || ''); + setSelectedRegionDistricts(r.districts?.map((d: any) => d.id) || []); + setShowRegionDialog(true); + }} + onDeleteRegion={() => toast.error('Regional office deletion is restricted via portal')} /> - toast.info('ZM assignment functionality being updated')} - onEditZM={() => toast.info('Edit ZM restricted')} onDeleteZM={() => toast.error('Delete ZM restricted')} /> + { + setEditingZMId(null); setZmManagerId(''); setZmName(''); setZmCode(''); setZmEmployeeId(''); + setZmStatus('active'); setSelectedZMZone(selectedZone === 'all' ? '' : selectedZone); + setSelectedZMStates([]); setSelectedZMDistricts([]); + setShowZMDialog(true); + }} + onEditZM={handleEditZM} + onDeleteZM={() => toast.error('ZM deletion restricted')} /> { setEditingASMId(null); setAsmManagerId(''); setAsmName(''); setAsmCode(''); setAsmEmployeeId(''); setSelectedASMZone(selectedZone === 'all' ? '' : selectedZone); setSelectedASMRegion(''); setSelectedASMStates([]); setSelectedASMDistricts([]); setShowASMDialog(true); }} onEditASM={handleEditASM} onDeleteASM={() => toast.error('ASM deletion restricted')} /> - + 0 ? users : asms} />
@@ -319,9 +379,34 @@ export const MasterPage: React.FC = () => { )} {/* Main Dialogs */} - - - + 0 ? users.map(u => ({...u, name: u.fullName || u.name, role: u.role?.roleName, roleCode: u.role?.roleCode, allRoles: u.allRoles})) : asms} /> + 0 ? users.map(u => ({...u, name: u.fullName || u.name, role: u.role?.roleName, roleCode: u.role?.roleCode, allRoles: u.allRoles})) : asms} /> + 0 ? users.map(u => ({...u, name: u.fullName || u.name, role: u.role?.roleName, roleCode: u.role?.roleCode, allRoles: u.allRoles})) : asms} districtsAssignedToOthers={districtsAssignedToOthers} getDistrictsForSelectedState={getDistrictsForSelectedState} /> + 0 ? users.map(u => ({...u, name: u.fullName || u.name, role: u.role?.roleName, roleCode: u.role?.roleCode, allRoles: u.allRoles})) : asms} + districtsAssignedToOthers={districtsAssignedToOthers} + getDistrictsForSelectedState={getDistrictsForSelectedState} + /> diff --git a/src/components/applications/MasterPage/ASMDialog.tsx b/src/components/applications/MasterPage/ASMDialog.tsx index 639fc60..4174e44 100644 --- a/src/components/applications/MasterPage/ASMDialog.tsx +++ b/src/components/applications/MasterPage/ASMDialog.tsx @@ -7,7 +7,6 @@ import { Input } from '../../ui/input'; import { Checkbox } from '../../ui/checkbox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/select'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip'; -import { UserCog, Users } from 'lucide-react'; import { RootState } from '../../../store'; interface ASMDialogProps { @@ -35,7 +34,7 @@ interface ASMDialogProps { onSave: () => void; userAssignedData: any[]; districtsAssignedToOthers: Record; - getDistrictsForSelectedState: (state: string) => string[]; + getDistrictsForSelectedState: (state: string) => { id: string; name: string }[]; } export const ASMDialog: React.FC = ({ @@ -49,9 +48,12 @@ export const ASMDialog: React.FC = ({ const { zones, regionalOffices } = useSelector((state: RootState) => state.master); const filteredASMUsers = userAssignedData.filter(u => { - const code = (u.roleCode || '').toLowerCase(); - const name = (u.role || '').toLowerCase(); - return code === 'asm' || name.includes('area sales manager'); + const roles = u.allRoles || []; + return roles.some((r: string) => { + const roleStr = (r || '').toUpperCase(); + return ['ASM', 'AREA SALES MANAGER', 'RM', 'RBM', 'REGIONAL MANAGER', 'ZBH', 'ZONE BUSINESS HEAD', 'ZONAL BUSINESS HEAD'].includes(roleStr) || + roleStr.includes('AREA SALES') || roleStr.includes('REGIONAL') || roleStr.includes('ZONAL'); + }); }); return ( @@ -109,7 +111,7 @@ export const ASMDialog: React.FC = ({
{(() => { const selectedRegion = regionalOffices.find(r => r.id === selectedASMRegion); - const availableStates = selectedRegion?.states || []; + const availableStates = (selectedRegion?.states || []).map((s: any) => typeof s === 'string' ? s : s.name); return availableStates.length > 0 ? (
@@ -123,8 +125,8 @@ export const ASMDialog: React.FC = ({ setSelectedASMStates([...selectedASMStates, state]); } else { setSelectedASMStates(selectedASMStates.filter(s => s !== state)); - const stateDistricts = getDistrictsForSelectedState(state); - setSelectedASMDistricts(selectedASMDistricts.filter(d => !stateDistricts.includes(d))); + const stateDistricts = getDistrictsForSelectedState(state); + setSelectedASMDistricts(selectedASMDistricts.filter(dId => !stateDistricts.some(sd => sd.id === dId))); } }} /> @@ -155,34 +157,34 @@ export const ASMDialog: React.FC = ({

{state}

- {districts.map((district: string) => ( -
+ {districts.map((district: any) => ( +
{ if (checked) { - setSelectedASMDistricts([...selectedASMDistricts, district]); + setSelectedASMDistricts([...selectedASMDistricts, district.id]); } else { - setSelectedASMDistricts(selectedASMDistricts.filter(d => d !== district)); + setSelectedASMDistricts(selectedASMDistricts.filter(id => id !== district.id)); } }} />
- {districtsAssignedToOthers[district] && ( + {districtsAssignedToOthers[district.id] && ( -

Already managed by: {districtsAssignedToOthers[district].join(', ')}

+

Already managed by: {districtsAssignedToOthers[district.id].join(', ')}

)}
@@ -208,7 +210,7 @@ export const ASMDialog: React.FC = ({ setAsmName(selectedUser.name); setAsmCode(selectedUser.asmCode || ''); setAsmEmployeeId(selectedUser.employeeId || ''); - setSelectedASMDistricts(selectedUser.areasManaged || []); + setSelectedASMDistricts(selectedUser.areasManaged?.map((a: any) => a.id) || []); setSelectedASMStates(selectedUser.stateNames || []); } }} diff --git a/src/components/applications/MasterPage/ASMManagement.tsx b/src/components/applications/MasterPage/ASMManagement.tsx index 61d7ad1..7ffa29e 100644 --- a/src/components/applications/MasterPage/ASMManagement.tsx +++ b/src/components/applications/MasterPage/ASMManagement.tsx @@ -77,8 +77,10 @@ export const ASMManagement: React.FC = ({ {asm.regionName}
- {asm.areasManaged.map((area: string, idx: number) => { - const otherManagers = (districtsAssignedToOthers[area] || []).filter((name: string) => name !== asm.name); + {asm.areasManaged.map((area: any, idx: number) => { + const areaId = typeof area === 'string' ? area : area.id; + const areaName = typeof area === 'string' ? area : area.name; + const otherManagers = (districtsAssignedToOthers[areaId] || []).filter((name: string) => name !== asm.name); const isShared = otherManagers.length > 0; return ( = ({ className={`text-xs ${isShared ? "border-amber-300 bg-amber-50 text-amber-700 font-medium" : ""}`} title={isShared ? `Also managed by: ${otherManagers.join(', ')}` : undefined} > - {area} + {areaName} {isShared && } ); diff --git a/src/components/applications/MasterPage/LocationManagement.tsx b/src/components/applications/MasterPage/LocationManagement.tsx index 5d3b312..b9ce909 100644 --- a/src/components/applications/MasterPage/LocationManagement.tsx +++ b/src/components/applications/MasterPage/LocationManagement.tsx @@ -16,7 +16,7 @@ interface LocationManagementProps { export const LocationManagement: React.FC = ({ onAddLocation, onEditLocation, onDeleteLocation }) => { - const { allAreas } = useSelector((state: RootState) => state.master); + const { allDistricts } = useSelector((state: RootState) => state.master); return (
@@ -47,35 +47,35 @@ export const LocationManagement: React.FC = ({ - {allAreas.map((area) => ( - + {allDistricts.map((district) => ( +
- {area.district?.state?.stateName || area.state?.stateName || 'N/A'} + {district.stateName || 'N/A'}
- {area.areaName} ({area.city}) - {area.district?.districtName || 'N/A'} - {area.pincode} + {district.name} + {district.regionName || 'N/A'} + {district.code || 'N/A'} - {area.manager ? ( - {area.manager.fullName} + {district.asmName ? ( + {district.asmName} ) : ( Unassigned )} - - {area.isActive ? 'Active' : 'Inactive'} + + {district.isActive ? 'Active' : 'Inactive'}
- -
diff --git a/src/components/applications/MasterPage/RegionDialog.tsx b/src/components/applications/MasterPage/RegionDialog.tsx index e60f49c..a6be6a2 100644 --- a/src/components/applications/MasterPage/RegionDialog.tsx +++ b/src/components/applications/MasterPage/RegionDialog.tsx @@ -7,6 +7,7 @@ import { Input } from '../../ui/input'; import { Textarea } from '../../ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/select'; import { Checkbox } from '../../ui/checkbox'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip'; import { RootState } from '../../../store'; interface RegionDialogProps { @@ -23,8 +24,8 @@ interface RegionDialogProps { setSelectedRegionZone: (id: string) => void; regionalManagerId: string; setRegionalManagerId: (id: string) => void; - selectedRegionStates: string[]; - setSelectedRegionStates: (states: string[]) => void; + selectedRegionStates: string[]; // This now contains District IDs + setSelectedRegionStates: (districts: string[]) => void; onSave: () => void; userAssignedData: any[]; // Used for RM selection } @@ -35,12 +36,28 @@ export const RegionDialog: React.FC = ({ selectedRegionZone, setSelectedRegionZone, regionalManagerId, setRegionalManagerId, selectedRegionStates, setSelectedRegionStates, onSave, userAssignedData }) => { - const { zones, allStates } = useSelector((state: RootState) => state.master); + const { zones, allDistricts, regionalOffices } = useSelector((state: RootState) => state.master); + + // Map of District ID -> Region Name for districts assigned to other regions + const districtsAssignedToOthers = React.useMemo(() => { + const mapping: Record = {}; + (regionalOffices || []).forEach((r: any) => { + if (r.id !== editingRegionId) { + (r.districts || []).forEach((d: any) => { + mapping[d.id] = r.name; + }); + } + }); + return mapping; + }, [regionalOffices, editingRegionId]); const filteredRMUsers = userAssignedData.filter(u => { - const code = (u.roleCode || '').toLowerCase(); - const name = (u.role || '').toLowerCase(); - return code === 'rm' || code === 'rbm' || name.includes('regional manager'); + const roles = u.allRoles || []; + return roles.some((r: string) => { + const roleStr = (r || '').toUpperCase(); + return ['RM', 'RBM', 'REGIONAL MANAGER', 'ZBH', 'ZONE BUSINESS HEAD', 'ZONAL BUSINESS HEAD', 'ASM', 'AREA SALES MANAGER'].includes(roleStr) || + roleStr.includes('REGIONAL') || roleStr.includes('ZONAL') || roleStr.includes('AREA SALES'); + }); }); return ( @@ -99,29 +116,50 @@ export const RegionDialog: React.FC = ({
- +
- {allStates - .filter(s => !selectedRegionZone || s.zoneId === selectedRegionZone) - .map((state) => ( -
- { - if (checked) { - setSelectedRegionStates([...selectedRegionStates, state.stateName]); - } else { - setSelectedRegionStates(selectedRegionStates.filter(s => s !== state.stateName)); - } - }} - /> - -
- ))} + {allDistricts + .map((district) => { + const assignedRegionName = districtsAssignedToOthers[district.id]; + const isDisabled = !!assignedRegionName; + + return ( +
+ + + +
+ { + if (checked) { + setSelectedRegionStates([...selectedRegionStates, district.id]); + } else { + setSelectedRegionStates(selectedRegionStates.filter(id => id !== district.id)); + } + }} + /> + +
+
+ {isDisabled && ( + +

Already assigned to {assignedRegionName}

+
+ )} +
+
+
+ ); + })}
diff --git a/src/components/applications/MasterPage/RegionalManagement.tsx b/src/components/applications/MasterPage/RegionalManagement.tsx index 7f5aa56..5eab784 100644 --- a/src/components/applications/MasterPage/RegionalManagement.tsx +++ b/src/components/applications/MasterPage/RegionalManagement.tsx @@ -43,7 +43,7 @@ export const RegionalManagement: React.FC = ({ Region Name Zone Regional Manager - States + Districts Cities Regional Officers ASMs @@ -73,15 +73,15 @@ export const RegionalManagement: React.FC = ({ )} -
- {region.states.slice(0, 2).map((state: string, idx: number) => ( +
+ {(region.districts || []).slice(0, 2).map((district: any, idx: number) => ( - {state} + {district.name || district} ))} - {region.states.length > 2 && ( + {(region.districts || []).length > 2 && ( - +{region.states.length - 2} + +{(region.districts || []).length - 2} )}
diff --git a/src/components/applications/MasterPage/ZMDialog.tsx b/src/components/applications/MasterPage/ZMDialog.tsx new file mode 100644 index 0000000..48d9d3a --- /dev/null +++ b/src/components/applications/MasterPage/ZMDialog.tsx @@ -0,0 +1,257 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../../ui/dialog'; +import { Button } from '../../ui/button'; +import { Label } from '../../ui/label'; +import { Input } from '../../ui/input'; +import { Checkbox } from '../../ui/checkbox'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/select'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip'; +import { RootState } from '../../../store'; + +interface ZMDialogProps { + isOpen: boolean; + onOpenChange: (open: boolean) => void; + editingZMId: string | null; + zmManagerId: string; + setZmManagerId: (id: string) => void; + zmName: string; + setZmName: (name: string) => void; + zmCode: string; + setZmCode: (code: string) => void; + zmEmployeeId: string; + setZmEmployeeId: (id: string) => void; + zmStatus: 'active' | 'inactive'; + setZmStatus: (status: 'active' | 'inactive') => void; + selectedZMZone: string; + setSelectedZMZone: (id: string) => void; + selectedZMStates: string[]; + setSelectedZMStates: (states: string[]) => void; + selectedZMDistricts: string[]; + setSelectedZMDistricts: (districts: string[]) => void; + onSave: () => void; + userAssignedData: any[]; + districtsAssignedToOthers: Record; + getDistrictsForSelectedState: (state: string) => { id: string; name: string }[]; +} + +export const ZMDialog: React.FC = ({ + isOpen, onOpenChange, editingZMId, zmManagerId, setZmManagerId, + zmName, setZmName, zmCode, setZmCode, zmEmployeeId, setZmEmployeeId, + zmStatus, setZmStatus, selectedZMZone, setSelectedZMZone, + selectedZMStates, setSelectedZMStates, + selectedZMDistricts, setSelectedZMDistricts, onSave, + userAssignedData, districtsAssignedToOthers, getDistrictsForSelectedState +}) => { + const { zones, regionalOffices } = useSelector((state: RootState) => state.master); + + const filteredZMUsers = userAssignedData.filter(u => { + const roles = u.allRoles || []; + return roles.some((r: string) => { + const roleStr = (r || '').toUpperCase(); + return ['ZM', 'ZONAL MANAGER', 'ZBH', 'ZONE BUSINESS HEAD', 'RM', 'RBM', 'REGIONAL MANAGER', 'ASM', 'AREA SALES MANAGER'].includes(roleStr) || + roleStr.includes('ZONAL') || roleStr.includes('REGIONAL') || roleStr.includes('AREA SALES'); + }); + }); + + return ( + + + + {editingZMId ? 'Edit' : 'Add'} Zonal Manager + Configure ZM details and district-level assignments + +
+
+ + +
+ + {selectedZMZone && ( +
+ +
+ {(() => { + const relevantRegions = regionalOffices.filter(r => r.zoneId === selectedZMZone); + const availableStates = Array.from(new Set(relevantRegions.flatMap(r => r.states || []).map((s: any) => typeof s === 'string' ? s : s.name))).sort(); + + return availableStates.length > 0 ? ( +
+ {availableStates.map((state) => ( +
+ { + if (checked) { + setSelectedZMStates([...selectedZMStates, state]); + } else { + setSelectedZMStates(selectedZMStates.filter(s => s !== state)); + const stateDistricts = getDistrictsForSelectedState(state); + setSelectedZMDistricts(selectedZMDistricts.filter(dId => !stateDistricts.some(sd => sd.id === dId))); + } + }} + /> + +
+ ))} +
+ ) : ( +

No states found for this zone

+ ); + })()} +
+
+ )} + + {selectedZMStates.length > 0 && ( +
+ +
+ + {selectedZMStates.map((state) => { + const districts = getDistrictsForSelectedState(state); + if (districts.length === 0) return null; + + return ( +
+

{state}

+
+ {districts.map((district: any) => ( +
+ + +
+ { + if (checked) { + setSelectedZMDistricts([...selectedZMDistricts, district.id]); + } else { + setSelectedZMDistricts(selectedZMDistricts.filter(id => id !== district.id)); + } + }} + /> + +
+
+ {districtsAssignedToOthers[district.id] && ( + +

Also assigned to: {districtsAssignedToOthers[district.id].join(', ')}

+
+ )} +
+
+ ))} +
+
+ ); + })} +
+
+
+ )} + +
+ + +
+ +
+
+ + +
+
+ + setZmCode(e.target.value)} /> +
+
+ +
+ + setZmName(e.target.value)} /> +
+ +
+ + +
+ +
+ + +
+
+
+
+ ); +}; diff --git a/src/components/applications/MasterPage/ZMManagement.tsx b/src/components/applications/MasterPage/ZMManagement.tsx index 9ad4799..3844ca4 100644 --- a/src/components/applications/MasterPage/ZMManagement.tsx +++ b/src/components/applications/MasterPage/ZMManagement.tsx @@ -65,9 +65,9 @@ export const ZMManagement: React.FC = ({ {zm.regionName}
- {zm.districts.slice(0, 3).map((district: string, idx: number) => ( + {zm.districts.slice(0, 3).map((district: any, idx: number) => ( - {district} + {district.name} ))} {zm.districts.length > 3 && ( diff --git a/src/components/applications/MasterPage/ZoneDetails.tsx b/src/components/applications/MasterPage/ZoneDetails.tsx index b07b771..344f253 100644 --- a/src/components/applications/MasterPage/ZoneDetails.tsx +++ b/src/components/applications/MasterPage/ZoneDetails.tsx @@ -72,23 +72,23 @@ export const ZoneDetails: React.FC = ({ selectedZone, onAddZon
- {zone.zbh && zone.zbh.name && ( + {zone.zonalBusinessHead && zone.zonalBusinessHead.name && (
- {zone.zbh.name} + {zone.zonalBusinessHead.name}
- {zone.zbh.email && ( + {zone.zonalBusinessHead.email && (
- {zone.zbh.email} + {zone.zonalBusinessHead.email}
)} - {zone.zbh.phone && ( + {zone.zonalBusinessHead.phone && (
- {zone.zbh.phone} + {zone.zonalBusinessHead.phone}
)}
diff --git a/src/components/applications/MasterPage/ZoneDialog.tsx b/src/components/applications/MasterPage/ZoneDialog.tsx index e627b94..1ee6eb2 100644 --- a/src/components/applications/MasterPage/ZoneDialog.tsx +++ b/src/components/applications/MasterPage/ZoneDialog.tsx @@ -4,6 +4,7 @@ import { Button } from '../../ui/button'; import { Label } from '../../ui/label'; import { Input } from '../../ui/input'; import { Textarea } from '../../ui/textarea'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/select'; interface ZoneDialogProps { isOpen: boolean; @@ -15,55 +16,88 @@ interface ZoneDialogProps { setZoneCode: (code: string) => void; zoneDescription: string; setZoneDescription: (desc: string) => void; + zonalBusinessHeadId: string; + setZonalBusinessHeadId: (id: string) => void; + userAssignedData: any[]; onSave: () => void; } export const ZoneDialog: React.FC = ({ - isOpen, onOpenChange, editingZoneId, zoneName, setZoneName, - zoneCode, setZoneCode, zoneDescription, setZoneDescription, onSave + isOpen, onOpenChange, editingZoneId, zoneName, setZoneName, + zoneCode, setZoneCode, zoneDescription, setZoneDescription, + zonalBusinessHeadId, setZonalBusinessHeadId, userAssignedData, onSave }) => { - return ( - - - - {editingZoneId ? 'Edit' : 'Add'} Zone - Configure zonal details and geographical boundaries - -
-
- - setZoneName(e.target.value)} - /> -
-
- - setZoneCode(e.target.value)} - /> -
-
- -