ui and extra inputs fwhile creeating region zone, assigning zm are removed

This commit is contained in:
laxman h 2026-04-15 21:13:39 +05:30
parent 8ea748fde6
commit bc2b7faf08
14 changed files with 251 additions and 179 deletions

View File

@ -9,6 +9,7 @@ export const API = {
// Master module routes
getRoles: () => client.get('/admin/roles'),
createRole: (data: any) => client.post('/admin/roles', data),
getPermissions: () => client.get('/admin/permissions'),
updateRole: (id: string, data: any) => client.put(`/admin/roles/${id}`, data),

View File

@ -490,7 +490,8 @@ export function UserManagementPage() {
{/* Geographical Assignments */}
<div className="col-span-2 border-t pt-4 mt-2">
<h3 className="text-sm font-semibold text-slate-900 mb-4">Geographical Assignments</h3>
<h3 className="text-sm font-semibold text-slate-900 mb-1">Geographical Assignments</h3>
<p className="text-xs text-slate-500 mb-4">Optional: you can create users without territory mapping and assign later.</p>
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="zoneId">Zone (Top Level)</Label>

View File

@ -174,7 +174,7 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
];
const getStatusColor = (status: ApplicationStatus) => {
const colors: Record<ApplicationStatus, string> = {
const colors: Partial<Record<ApplicationStatus, string>> = {
'Submitted': 'bg-blue-100 text-blue-800',
'Questionnaire Pending': 'bg-yellow-100 text-yellow-800',
'Questionnaire Completed': 'bg-cyan-100 text-cyan-800',
@ -219,12 +219,8 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
'Security Details Approved': 'bg-green-100 text-green-800',
'Security Details': 'bg-amber-100 text-amber-800',
'LOA Issued': 'bg-pink-100 text-pink-800',
'EOR Complete': 'bg-violet-100 text-violet-800',
'Level 1 Approved': 'bg-green-100 text-green-800',
'Level 2 Approved': 'bg-green-100 text-green-800',
'Level 3 Approved': 'bg-green-100 text-green-800',
};
return (statusColors as any)[status] || 'bg-gray-100 text-gray-800';
return colors[status] || 'bg-gray-100 text-gray-800';
};
return (

View File

@ -1875,6 +1875,8 @@ export const ApplicationDetails = () => {
].includes(application.status);
const isLoaLocked = application.status === 'LOA Pending' && getDeposit('FIRST_FILL')?.status !== 'Verified';
const isSecurityDetailsLocked = ['Security Details', 'Payment Pending'].includes(application.status) &&
getDeposit('SECURITY_DEPOSIT')?.status !== 'Verified';
const isFinalState = application.status === 'Onboarded' || application.status === 'Rejected';
// 2. Interview Specific Logic
@ -1903,15 +1905,18 @@ export const ApplicationDetails = () => {
// 5. Final Permission Bits
const isDecisionMade = (activeInterviewForUser ? hasMadeInterviewDecision : false) || hasMadeStageDecision;
const canApproveReject = !isLoaLocked && !isFinalState && !isDecisionMade && (
const canApproveReject = !isFinalState && !isDecisionMade && (
(!!activeInterviewForUser && !!hasSubmittedFeedback) ||
(isAdminRole && isAdministrativeStage && sequenceMet && (!['EOR In Progress', 'Inauguration', 'Approved'].includes(application.status) || eorProgress === 100))
);
const canApprove = canApproveReject && !isLoaLocked && !isSecurityDetailsLocked;
const canReject = canApproveReject && !isLoaLocked;
return {
canApprove: canApproveReject,
canReject: canApproveReject,
canApprove,
canReject,
isLoaLocked,
isSecurityDetailsLocked,
showDecisionMessage: isDecisionMade && (!isAdministrativeStage || hasMadeStageDecision),
canSchedule: ['DD Admin', 'Super Admin', 'DD AM', 'ASM'].includes(currentUser.role) &&
!isFinalState &&
@ -3643,6 +3648,17 @@ export const ApplicationDetails = () => {
</Alert>
)}
{permissions.isSecurityDetailsLocked && (
<Alert variant="destructive" className="mb-4 bg-amber-50 border-amber-200 text-amber-800">
<Lock className="w-4 h-4 text-amber-600" />
<AlertTitle className="text-amber-900 font-semibold">Security Details approval locked</AlertTitle>
<AlertDescription className="text-amber-800">
Finance must verify the <span className="font-medium">Security Deposit</span> before this stage can be approved.
You can still use <span className="font-medium">Reject</span> if needed.
</AlertDescription>
</Alert>
)}
{['Security Details', 'Payment Pending'].includes(application.status) && (
<Alert className="mb-4 border-sky-200 bg-sky-50/90 text-sky-900">
<Info className="h-4 w-4 text-sky-700" />

View File

@ -21,6 +21,7 @@ import { UserManagementTable } from './MasterPage/UserManagementTable';
import { SLAConfiguration } from './MasterPage/SLAConfiguration';
import { RolePermissions } from './MasterPage/RolePermissions';
import { RoleDialog } from './MasterPage/RoleDialog';
import { AddRoleDialog } from './MasterPage/AddRoleDialog';
import { EmailTemplates } from './MasterPage/EmailTemplates';
import { LocationManagement } from './MasterPage/LocationManagement';
import { ASMDialog } from './MasterPage/ASMDialog';
@ -40,6 +41,7 @@ export const MasterPage: React.FC = () => {
const { fetchInitialData, fetchAreas } = useMasterData();
const {
asms, zonalManagerMappings, ddLeads,
allStates,
allDistricts,
users,
roles,
@ -60,9 +62,6 @@ export const MasterPage: React.FC = () => {
// Form State (ASM)
const [editingASMId, setEditingASMId] = useState<string | null>(null);
const [asmManagerId, setAsmManagerId] = useState('');
const [asmName, setAsmName] = useState('');
const [asmCode, setAsmCode] = useState('');
const [asmEmployeeId, setAsmEmployeeId] = useState('');
const [asmStatus, setAsmStatus] = useState<'active' | 'inactive'>('active');
const [selectedASMZone, setSelectedASMZone] = useState('');
const [selectedASMRegion, setSelectedASMRegion] = useState('');
@ -74,9 +73,6 @@ export const MasterPage: React.FC = () => {
const [showZMDialog, setShowZMDialog] = useState(false);
const [editingZMId, setEditingZMId] = useState<string | null>(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 [selectedZMRegions, setSelectedZMRegions] = useState<string[]>([]);
@ -85,15 +81,13 @@ export const MasterPage: React.FC = () => {
const [showDDLeadDialog, setShowDDLeadDialog] = useState(false);
const [editingDDLeadId, setEditingDDLeadId] = useState<string | null>(null);
const [ddLeadManagerId, setDdLeadManagerId] = useState('');
const [ddLeadName, setDdLeadName] = useState('');
const [ddLeadCode, setDdLeadCode] = useState('');
const [ddLeadEmployeeId, setDdLeadEmployeeId] = useState('');
const [ddLeadStatus, setDdLeadStatus] = useState<'active' | 'inactive'>('active');
const [selectedDDLeadZones, setSelectedDDLeadZones] = useState<string[]>([]);
// Role Management State
const [showRoleDialog, setShowRoleDialog] = useState(false);
const [editingRole, setEditingRole] = useState<any>(null);
const [showAddRoleDialog, setShowAddRoleDialog] = useState(false);
// Form State (Zone)
const [editingZoneId, setEditingZoneId] = useState<string | null>(null);
@ -105,7 +99,6 @@ export const MasterPage: React.FC = () => {
// Form State (Region)
const [editingRegionId, setEditingRegionId] = useState<string | null>(null);
const [regionName, setRegionName] = useState('');
const [regionCode, setRegionCode] = useState('');
const [regionDescription, setRegionDescription] = useState('');
const [selectedRegionZone, setSelectedRegionZone] = useState('');
const [regionalManagerId, setRegionalManagerId] = useState('');
@ -176,7 +169,6 @@ export const MasterPage: React.FC = () => {
const payload = {
userId: asmManagerId,
roleCode: asmRoleCode,
asmCode,
districts: selectedASMDistricts,
status: asmStatus
};
@ -197,9 +189,6 @@ export const MasterPage: React.FC = () => {
const handleEditASM = (asm: any) => {
setEditingASMId(asm.id);
setAsmManagerId(asm.id);
setAsmName(asm.name);
setAsmCode(asm.asmCode);
setAsmEmployeeId(asm.employeeId);
setAsmStatus(asm.status.toLowerCase() as 'active' | 'inactive');
setSelectedASMZone(asm.zoneId);
setSelectedASMRegion(asm.regionId);
@ -212,9 +201,6 @@ export const MasterPage: React.FC = () => {
const handleEditZM = (zm: any) => {
setEditingZMId(zm.id);
setZmManagerId(zm.id);
setZmName(zm.name);
setZmCode(zm.zmCode || zm.code || '');
setZmEmployeeId(zm.employeeId || '');
setZmStatus(zm.status?.toLowerCase() === 'active' ? 'active' : 'inactive');
setSelectedZMZone(zm.zoneId || '');
setSelectedZMRegions(zm.assignedRegionIds || []);
@ -224,9 +210,6 @@ export const MasterPage: React.FC = () => {
const handleEditDDLead = (lead: any) => {
setEditingDDLeadId(lead.id);
setDdLeadManagerId(lead.id);
setDdLeadName(lead.name);
setDdLeadCode(lead.leadCode || '');
setDdLeadEmployeeId(lead.employeeId || '');
setDdLeadStatus(lead.status?.toLowerCase() === 'active' ? 'active' : 'inactive');
setSelectedDDLeadZones(lead.assignedZoneIds || []);
setShowDDLeadDialog(true);
@ -240,7 +223,6 @@ export const MasterPage: React.FC = () => {
try {
const payload = {
userId: zmManagerId,
zmCode,
zoneId: selectedZMZone,
regionIds: selectedZMRegions,
status: zmStatus
@ -268,7 +250,6 @@ export const MasterPage: React.FC = () => {
try {
const payload = {
userId: ddLeadManagerId,
leadCode: ddLeadCode,
zoneIds: selectedDDLeadZones,
status: ddLeadStatus
};
@ -316,7 +297,6 @@ export const MasterPage: React.FC = () => {
const payload = {
...(editingRegionId ? { id: editingRegionId } : {}),
name: regionName,
code: regionCode,
description: regionDescription,
parentId: selectedRegionZone,
managerId: regionalManagerId,
@ -384,11 +364,32 @@ export const MasterPage: React.FC = () => {
setShowRoleDialog(true);
};
const handleCreateRole = async (data: { roleCode: string; roleName: string; description?: string }) => {
try {
const payload = {
...data,
permissionIds: []
};
const res = await (masterService as any).createRole(payload);
if (res?.success) {
toast.success('Role created successfully');
fetchInitialData();
return;
}
throw new Error(res?.message || 'Failed to create role');
} catch (error: any) {
const msg = error?.response?.data?.message || error?.message || 'Failed to create role';
toast.error(msg);
throw error;
}
};
const handleEditLocation = (loc: any) => {
const matchedDistrict = allDistricts.find((d: any) => d.id === loc.districtId);
setEditingLocationId(loc.id);
setLocationState(loc.stateName || '');
setLocationState(matchedDistrict?.stateId || loc.stateId || '');
setLocationCity(loc.city || '');
setLocationDistrict(loc.name || '');
setLocationDistrict(loc.districtId || '');
setLocationActiveFrom(loc.openFrom ? new Date(loc.openFrom).toISOString().split('T')[0] : '');
setLocationActiveTo(loc.openTo ? new Date(loc.openTo).toISOString().split('T')[0] : '');
setLocationStatus(loc.isActive ? 'active' : 'inactive');
@ -397,10 +398,23 @@ export const MasterPage: React.FC = () => {
const handleSaveLocation = async () => {
try {
if (!locationState) {
toast.error('Please select a state');
return;
}
if (!locationDistrict) {
toast.error('Please select a district');
return;
}
const selectedState = allStates.find((s: any) => s.id === locationState);
const selectedDistrict = allDistricts.find((d: any) => d.id === locationDistrict);
const payload = {
id: editingLocationId,
stateName: locationState,
name: locationDistrict,
stateId: locationState,
stateName: selectedState?.name || selectedState?.stateName || '',
districtId: locationDistrict,
name: locationCity || selectedDistrict?.name || 'New Location',
city: locationCity,
status: locationStatus,
openFrom: locationActiveFrom,
@ -474,11 +488,10 @@ export const MasterPage: React.FC = () => {
<ZoneDetails selectedZone={selectedZone} onAddZone={() => { setEditingZoneId(null); setZoneName(''); setZoneCode(''); setZoneDescription(''); setZonalBusinessHeadId('none'); setShowZoneDialog(true); }}
onEditZone={(z) => { setEditingZoneId(z.id); setZoneName(z.name); setZoneCode(z.code); setZoneDescription(z.description); setZonalBusinessHeadId(z.zonalBusinessHead?.id || 'none'); setShowZoneDialog(true); }} />
<RegionalManagement selectedZone={selectedZone} onAddRegion={() => { setEditingRegionId(null); setRegionName(''); setRegionCode(''); setSelectedRegionZone(selectedZone === 'all' ? '' : selectedZone); setRegionalManagerId(''); setSelectedRegionDistricts([]); setShowRegionDialog(true); }}
<RegionalManagement selectedZone={selectedZone} onAddRegion={() => { setEditingRegionId(null); setRegionName(''); 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) || []);
@ -488,7 +501,7 @@ export const MasterPage: React.FC = () => {
<ZMManagement selectedZone={selectedZone}
onAddZM={() => {
setEditingZMId(null); setZmManagerId(''); setZmName(''); setZmCode(''); setZmEmployeeId('');
setEditingZMId(null); setZmManagerId('');
setZmStatus('active'); setSelectedZMZone(selectedZone === 'all' ? '' : selectedZone);
setSelectedZMRegions([]);
setShowZMDialog(true);
@ -496,12 +509,12 @@ export const MasterPage: React.FC = () => {
onEditZM={handleEditZM}
onDeleteZM={() => toast.error('ZM deletion restricted')} />
<ASMManagement selectedZone={selectedZone} onAddASM={() => { setEditingASMId(null); setAsmManagerId(''); setAsmName(''); setAsmCode(''); setAsmEmployeeId(''); setSelectedASMZone(selectedZone === 'all' ? '' : selectedZone); setSelectedASMRegion(''); setSelectedASMStates([]); setSelectedASMDistricts([]); setShowASMDialog(true); }}
<ASMManagement selectedZone={selectedZone} onAddASM={() => { setEditingASMId(null); setAsmManagerId(''); setSelectedASMZone(selectedZone === 'all' ? '' : selectedZone); setSelectedASMRegion(''); setSelectedASMStates([]); setSelectedASMDistricts([]); setShowASMDialog(true); }}
onEditASM={handleEditASM} onDeleteASM={() => toast.error('ASM deletion restricted')} />
<DDLeadManagement selectedZone={selectedZone}
onAddLead={() => {
setEditingDDLeadId(null); setDdLeadManagerId(''); setDdLeadName(''); setDdLeadCode(''); setDdLeadEmployeeId('');
setEditingDDLeadId(null); setDdLeadManagerId('');
setDdLeadStatus('active'); setSelectedDDLeadZones([]);
setShowDDLeadDialog(true);
}}
@ -512,7 +525,7 @@ export const MasterPage: React.FC = () => {
</TabsContent>
<TabsContent value="roles" className="animate-in fade-in duration-300">
<RolePermissions onAddRole={() => toast.info('Unified Role Management interface being updated')}
<RolePermissions onAddRole={() => setShowAddRoleDialog(true)}
onEditRole={handleEditRole} />
</TabsContent>
@ -592,20 +605,14 @@ export const MasterPage: React.FC = () => {
{/* Main Dialogs */}
<ZoneDialog isOpen={showZoneDialog} onOpenChange={setShowZoneDialog} editingZoneId={editingZoneId} zoneName={zoneName} setZoneName={setZoneName} zoneCode={zoneCode} setZoneCode={setZoneCode} zoneDescription={zoneDescription} setZoneDescription={setZoneDescription} zonalBusinessHeadId={zonalBusinessHeadId} setZonalBusinessHeadId={setZonalBusinessHeadId} onSave={handleSaveZone} userAssignedData={users.length > 0 ? users.map(u => ({...u, name: u.fullName || u.name, role: u.role?.roleName, roleCode: u.role?.roleCode, allRoles: u.allRoles})) : asms} />
<RegionDialog isOpen={showRegionDialog} onOpenChange={setShowRegionDialog} editingRegionId={editingRegionId} regionName={regionName} setRegionName={setRegionName} regionCode={regionCode} setRegionCode={setRegionCode} regionDescription={regionDescription} setRegionDescription={setRegionDescription} selectedRegionZone={selectedRegionZone} setSelectedRegionZone={setSelectedRegionZone} regionalManagerId={regionalManagerId} setRegionalManagerId={setRegionalManagerId} selectedRegionStates={selectedRegionDistricts} setSelectedRegionStates={setSelectedRegionDistricts} onSave={handleSaveRegion} userAssignedData={users.length > 0 ? users.map(u => ({...u, name: u.fullName || u.name, role: u.role?.roleName, roleCode: u.role?.roleCode, allRoles: u.allRoles})) : asms} />
<ASMDialog isOpen={showASMDialog} onOpenChange={setShowASMDialog} editingASMId={editingASMId} asmManagerId={asmManagerId} setAsmManagerId={setAsmManagerId} asmName={asmName} setAsmName={setAsmName} asmCode={asmCode} setAsmCode={setAsmCode} asmEmployeeId={asmEmployeeId} setAsmEmployeeId={setAsmEmployeeId} asmStatus={asmStatus} setAsmStatus={setAsmStatus} selectedASMZone={selectedASMZone} setSelectedASMZone={setSelectedASMZone} selectedASMRegion={selectedASMRegion} setSelectedASMRegion={setSelectedASMRegion} selectedASMStates={selectedASMStates} setSelectedASMStates={setSelectedASMStates} selectedASMDistricts={selectedASMDistricts} setSelectedASMDistricts={setSelectedASMDistricts} onSave={handleSaveASM} asmRoleCode={asmRoleCode} setAsmRoleCode={setAsmRoleCode} userAssignedData={users.length > 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={(state) => getDistrictsForSelectedState(state, selectedASMRegion || undefined)} />
<RegionDialog isOpen={showRegionDialog} onOpenChange={setShowRegionDialog} editingRegionId={editingRegionId} regionName={regionName} setRegionName={setRegionName} regionDescription={regionDescription} setRegionDescription={setRegionDescription} selectedRegionZone={selectedRegionZone} setSelectedRegionZone={setSelectedRegionZone} regionalManagerId={regionalManagerId} setRegionalManagerId={setRegionalManagerId} selectedRegionStates={selectedRegionDistricts} setSelectedRegionStates={setSelectedRegionDistricts} onSave={handleSaveRegion} userAssignedData={users.length > 0 ? users.map(u => ({...u, name: u.fullName || u.name, role: u.role?.roleName, roleCode: u.role?.roleCode, allRoles: u.allRoles})) : asms} />
<ASMDialog isOpen={showASMDialog} onOpenChange={setShowASMDialog} editingASMId={editingASMId} asmManagerId={asmManagerId} setAsmManagerId={setAsmManagerId} asmStatus={asmStatus} setAsmStatus={setAsmStatus} selectedASMZone={selectedASMZone} setSelectedASMZone={setSelectedASMZone} selectedASMRegion={selectedASMRegion} setSelectedASMRegion={setSelectedASMRegion} selectedASMStates={selectedASMStates} setSelectedASMStates={setSelectedASMStates} selectedASMDistricts={selectedASMDistricts} setSelectedASMDistricts={setSelectedASMDistricts} onSave={handleSaveASM} asmRoleCode={asmRoleCode} setAsmRoleCode={setAsmRoleCode} userAssignedData={users.length > 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={(state) => getDistrictsForSelectedState(state, selectedASMRegion || undefined)} />
<ZMDialog
isOpen={showZMDialog}
onOpenChange={setShowZMDialog}
editingZMId={editingZMId}
zmManagerId={zmManagerId}
setZmManagerId={setZmManagerId}
zmName={zmName}
setZmName={setZmName}
zmCode={zmCode}
setZmCode={setZmCode}
zmEmployeeId={zmEmployeeId}
setZmEmployeeId={setZmEmployeeId}
zmStatus={zmStatus}
setZmStatus={setZmStatus}
selectedZone={selectedZMZone}
@ -621,12 +628,6 @@ export const MasterPage: React.FC = () => {
editingLeadId={editingDDLeadId}
leadManagerId={ddLeadManagerId}
setLeadManagerId={setDdLeadManagerId}
leadName={ddLeadName}
setLeadName={setDdLeadName}
leadCode={ddLeadCode}
setLeadCode={setDdLeadCode}
leadEmployeeId={ddLeadEmployeeId}
setLeadEmployeeId={setDdLeadEmployeeId}
leadStatus={ddLeadStatus}
setLeadStatus={setDdLeadStatus}
selectedZones={selectedDDLeadZones}
@ -635,8 +636,9 @@ export const MasterPage: React.FC = () => {
userAssignedData={users.length > 0 ? users.map(u => ({...u, name: u.fullName || u.name, role: u.role?.roleName, roleCode: u.role?.roleCode, allRoles: u.allRoles, assignedZoneIds: (ddLeads.find(l => l.id === u.id)?.assignedZoneIds || [])})) : asms}
/>
<TemplateDialog isOpen={showTemplateDialog} onOpenChange={setShowTemplateDialog} editingTemplate={editingTemplate} setEditingTemplate={setEditingTemplate} testDataInput={testDataInput} setTestDataInput={setTestDataInput} previewLoading={previewLoading} handlePreviewTemplate={handlePreviewTemplate} previewContent={previewContent} handleSaveTemplate={handleSaveTemplate} />
<LocationDialog isOpen={showLocationDialog} onOpenChange={setShowLocationDialog} editingLocationId={editingLocationId} locationState={locationState} setLocationState={setLocationState} locationDistrict={locationDistrict} setLocationDistrict={setLocationDistrict} locationCity={locationCity} setLocationCity={setLocationCity} locationActiveFrom={locationActiveFrom} setLocationActiveFrom={setLocationActiveFrom} locationActiveTo={locationActiveTo} setLocationActiveTo={setLocationActiveTo} locationStatus={locationStatus} setLocationStatus={setLocationStatus} onSave={handleSaveLocation} />
<LocationDialog isOpen={showLocationDialog} onOpenChange={setShowLocationDialog} editingLocationId={editingLocationId} locationState={locationState} setLocationState={setLocationState} locationDistrict={locationDistrict} setLocationDistrict={setLocationDistrict} locationCity={locationCity} setLocationCity={setLocationCity} locationActiveFrom={locationActiveFrom} setLocationActiveFrom={setLocationActiveFrom} locationActiveTo={locationActiveTo} setLocationActiveTo={setLocationActiveTo} locationStatus={locationStatus} setLocationStatus={setLocationStatus} allStates={allStates} allDistricts={allDistricts} onSave={handleSaveLocation} />
<RoleDialog isOpen={showRoleDialog} onOpenChange={setShowRoleDialog} role={editingRole} onSave={handleSaveRole} />
<AddRoleDialog isOpen={showAddRoleDialog} onOpenChange={setShowAddRoleDialog} onSave={handleCreateRole} />
</div>
);
};

View File

@ -3,7 +3,6 @@ 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';
@ -15,12 +14,6 @@ interface ASMDialogProps {
editingASMId: string | null;
asmManagerId: string;
setAsmManagerId: (id: string) => void;
asmName: string;
setAsmName: (name: string) => void;
asmCode: string;
setAsmCode: (code: string) => void;
asmEmployeeId: string;
setAsmEmployeeId: (id: string) => void;
asmStatus: 'active' | 'inactive';
setAsmStatus: (status: 'active' | 'inactive') => void;
selectedASMZone: string;
@ -41,7 +34,6 @@ interface ASMDialogProps {
export const ASMDialog: React.FC<ASMDialogProps> = ({
isOpen, onOpenChange, editingASMId, asmManagerId, setAsmManagerId,
asmName, setAsmName, asmCode, setAsmCode, asmEmployeeId, setAsmEmployeeId,
asmStatus, setAsmStatus, selectedASMZone, setSelectedASMZone,
selectedASMRegion, setSelectedASMRegion, selectedASMStates, setSelectedASMStates,
selectedASMDistricts, setSelectedASMDistricts, onSave,
@ -257,13 +249,7 @@ export const ASMDialog: React.FC<ASMDialogProps> = ({
setAsmManagerId(value);
const selectedUser = userAssignedData.find(u => u.id === value);
if (selectedUser) {
setAsmName(selectedUser.name);
setAsmEmployeeId(selectedUser.employeeId || '');
// Extraction logic: Look for managerCode in the territoryProfile matching the current role
const roleProfile = (selectedUser.territoryProfile || []).find((t: any) => t.roleCode === asmRoleCode);
const existingCode = roleProfile?.managerCode || selectedUser.asmCode || selectedUser.employeeId || '';
setAsmCode(existingCode);
// Extract zone/region from assignments if present
if (roleProfile?.zoneId) setSelectedASMZone(roleProfile.zoneId);
@ -295,22 +281,6 @@ export const ASMDialog: React.FC<ASMDialogProps> = ({
</Select>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Employee ID</Label>
<Input readOnly placeholder="Auto-populated" className="mt-2 bg-slate-100" value={asmEmployeeId} />
</div>
<div>
<Label>ASM Code</Label>
<Input placeholder="Enter ASM Code" className="mt-2 text-slate-900" value={asmCode} onChange={(e) => setAsmCode(e.target.value)} />
</div>
</div>
<div>
<Label>Full Name</Label>
<Input placeholder="Enter full name" className="mt-2 text-slate-900" value={asmName} onChange={(e) => setAsmName(e.target.value)} />
</div>
<div>
<Label>Status</Label>
<Select value={asmStatus} onValueChange={(val: 'active' | 'inactive') => setAsmStatus(val)}>

View File

@ -0,0 +1,99 @@
import React, { useEffect, useState } from 'react';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../../ui/dialog';
import { Button } from '../../ui/button';
import { Input } from '../../ui/input';
import { Label } from '../../ui/label';
import { Textarea } from '../../ui/textarea';
interface AddRoleDialogProps {
isOpen: boolean;
onOpenChange: (open: boolean) => void;
onSave: (data: { roleCode: string; roleName: string; description?: string }) => Promise<void>;
}
export const AddRoleDialog: React.FC<AddRoleDialogProps> = ({
isOpen,
onOpenChange,
onSave
}) => {
const [roleName, setRoleName] = useState('');
const [roleCode, setRoleCode] = useState('');
const [description, setDescription] = useState('');
const [saving, setSaving] = useState(false);
useEffect(() => {
if (!isOpen) return;
setRoleName('');
setRoleCode('');
setDescription('');
setSaving(false);
}, [isOpen]);
const handleSubmit = async () => {
if (!roleName.trim() || !roleCode.trim()) return;
setSaving(true);
try {
await onSave({
roleName: roleName.trim(),
roleCode: roleCode.trim(),
description: description.trim() || undefined
});
onOpenChange(false);
} finally {
setSaving(false);
}
};
return (
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Add Role</DialogTitle>
<DialogDescription>Create a new role for Master Configuration.</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="role-name">Role Name</Label>
<Input
id="role-name"
value={roleName}
onChange={(e) => setRoleName(e.target.value)}
placeholder="e.g. Finance Admin"
/>
</div>
<div className="space-y-2">
<Label htmlFor="role-code">Role Code</Label>
<Input
id="role-code"
value={roleCode}
onChange={(e) => setRoleCode(e.target.value)}
placeholder="e.g. FINANCE_ADMIN"
/>
</div>
<div className="space-y-2">
<Label htmlFor="role-description">Description</Label>
<Textarea
id="role-description"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Optional"
rows={3}
/>
</div>
</div>
<div className="flex justify-end gap-2">
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={saving}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={saving || !roleName.trim() || !roleCode.trim()}>
{saving ? 'Saving...' : 'Create Role'}
</Button>
</div>
</DialogContent>
</Dialog>
);
};

View File

@ -3,7 +3,6 @@ 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 { RootState } from '../../../store';
@ -14,12 +13,6 @@ interface DDLeadDialogProps {
editingLeadId: string | null;
leadManagerId: string;
setLeadManagerId: (id: string) => void;
leadName: string;
setLeadName: (name: string) => void;
leadCode: string;
setLeadCode: (code: string) => void;
leadEmployeeId: string;
setLeadEmployeeId: (id: string) => void;
leadStatus: 'active' | 'inactive';
setLeadStatus: (status: 'active' | 'inactive') => void;
selectedZones: string[];
@ -30,7 +23,6 @@ interface DDLeadDialogProps {
export const DDLeadDialog: React.FC<DDLeadDialogProps> = ({
isOpen, onOpenChange, editingLeadId, leadManagerId, setLeadManagerId,
leadName, setLeadName, leadCode, setLeadCode, leadEmployeeId, setLeadEmployeeId,
leadStatus, setLeadStatus, selectedZones, setSelectedZones, onSave,
userAssignedData
}) => {
@ -70,10 +62,6 @@ export const DDLeadDialog: React.FC<DDLeadDialogProps> = ({
setLeadManagerId(value);
const selectedUser = userAssignedData.find(u => u.id === value);
if (selectedUser) {
setLeadName(selectedUser.name);
setLeadEmployeeId(selectedUser.employeeId || '');
setLeadCode(selectedUser.leadCode || selectedUser.employeeId || '');
// If they have existing assigned zones
if (selectedUser.assignedZoneIds) {
setSelectedZones(selectedUser.assignedZoneIds);
@ -124,22 +112,6 @@ export const DDLeadDialog: React.FC<DDLeadDialogProps> = ({
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Employee ID</Label>
<Input readOnly placeholder="Auto-populated" className="mt-2 bg-slate-50 border-slate-200" value={leadEmployeeId} />
</div>
<div>
<Label>Lead Code</Label>
<Input placeholder="Enter Lead Code" className="mt-2 text-slate-900 border-slate-200" value={leadCode} onChange={(e) => setLeadCode(e.target.value)} />
</div>
</div>
<div>
<Label>Full Name</Label>
<Input placeholder="Enter full name" className="mt-2 text-slate-900 border-slate-200" value={leadName} onChange={(e) => setLeadName(e.target.value)} />
</div>
<div>
<Label>Status</Label>
<Select value={leadStatus} onValueChange={(val: 'active' | 'inactive') => setLeadStatus(val)}>

View File

@ -9,9 +9,9 @@ interface LocationDialogProps {
isOpen: boolean;
onOpenChange: (open: boolean) => void;
editingLocationId: string | null;
locationState: string;
locationState: string; // stateId
setLocationState: (id: string) => void;
locationDistrict: string;
locationDistrict: string; // districtId
setLocationDistrict: (id: string) => void;
locationCity: string;
setLocationCity: (city: string) => void;
@ -21,6 +21,8 @@ interface LocationDialogProps {
setLocationActiveTo: (date: string) => void;
locationStatus: string;
setLocationStatus: (status: string) => void;
allStates: any[];
allDistricts: any[];
onSave: () => void;
}
@ -28,8 +30,14 @@ export const LocationDialog: React.FC<LocationDialogProps> = ({
isOpen, onOpenChange, editingLocationId, locationState, setLocationState,
locationDistrict, setLocationDistrict, locationCity, setLocationCity,
locationActiveFrom, setLocationActiveFrom,
locationActiveTo, setLocationActiveTo, locationStatus, setLocationStatus, onSave
locationActiveTo, setLocationActiveTo, locationStatus, setLocationStatus,
allStates, allDistricts, onSave
}) => {
const filteredDistricts = React.useMemo(() => {
if (!locationState) return [];
return (allDistricts || []).filter((d: any) => d.stateId === locationState);
}, [allDistricts, locationState]);
return (
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent>
@ -40,12 +48,24 @@ export const LocationDialog: React.FC<LocationDialogProps> = ({
<div className="space-y-4">
<div>
<Label className="flex items-center gap-2 text-sm leading-none font-medium text-slate-700">State</Label>
<Input
placeholder="Enter state name"
className="mt-2 text-slate-900 border-slate-200 focus-visible:ring-amber-500/30 focus-visible:border-amber-500"
<Select
value={locationState}
onChange={(e) => setLocationState(e.target.value)}
/>
onValueChange={(value) => {
setLocationState(value);
setLocationDistrict('');
}}
>
<SelectTrigger className="mt-2 text-slate-900 border-slate-200 focus:ring-amber-500/30 focus:border-amber-500">
<SelectValue placeholder="Select state" />
</SelectTrigger>
<SelectContent>
{(allStates || []).map((state: any) => (
<SelectItem key={state.id} value={state.id}>
{state.name || state.stateName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label className="flex items-center gap-2 text-sm leading-none font-medium text-slate-700">City</Label>
@ -58,12 +78,18 @@ export const LocationDialog: React.FC<LocationDialogProps> = ({
</div>
<div>
<Label className="flex items-center gap-2 text-sm leading-none font-medium text-slate-700">District</Label>
<Input
placeholder="Enter district name"
className="mt-2 text-slate-900 border-slate-200 focus-visible:ring-amber-500/30 focus-visible:border-amber-500"
value={locationDistrict}
onChange={(e) => setLocationDistrict(e.target.value)}
/>
<Select value={locationDistrict} onValueChange={setLocationDistrict} disabled={!locationState}>
<SelectTrigger className="mt-2 text-slate-900 border-slate-200 focus:ring-amber-500/30 focus:border-amber-500">
<SelectValue placeholder={locationState ? 'Select district' : 'Select state first'} />
</SelectTrigger>
<SelectContent>
{filteredDistricts.map((district: any) => (
<SelectItem key={district.id} value={district.id}>
{district.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="border rounded-lg p-4 bg-gradient-to-br from-blue-50 to-cyan-50 border-blue-200">

View File

@ -16,8 +16,6 @@ interface RegionDialogProps {
editingRegionId: string | null;
regionName: string;
setRegionName: (name: string) => void;
regionCode: string;
setRegionCode: (code: string) => void;
regionDescription: string;
setRegionDescription: (desc: string) => void;
selectedRegionZone: string;
@ -32,7 +30,7 @@ interface RegionDialogProps {
export const RegionDialog: React.FC<RegionDialogProps> = ({
isOpen, onOpenChange, editingRegionId, regionName, setRegionName,
regionCode, setRegionCode, regionDescription, setRegionDescription,
regionDescription, setRegionDescription,
selectedRegionZone, setSelectedRegionZone, regionalManagerId, setRegionalManagerId,
selectedRegionStates, setSelectedRegionStates, onSave, userAssignedData
}) => {
@ -142,16 +140,10 @@ export const RegionDialog: React.FC<RegionDialogProps> = ({
</DialogHeader>
<div className="space-y-4">
{/* Region Code & Name */}
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Region Code</Label>
<Input placeholder="e.g., NZ-R1" className="mt-2 text-slate-900" value={regionCode} onChange={(e) => setRegionCode(e.target.value)} />
</div>
<div>
<Label>Region Name</Label>
<Input placeholder="e.g., Delhi NCR Region" className="mt-2 text-slate-900" value={regionName} onChange={(e) => setRegionName(e.target.value)} />
</div>
{/* Region Name */}
<div>
<Label>Region Name</Label>
<Input placeholder="e.g., Delhi NCR Region" className="mt-2 text-slate-900" value={regionName} onChange={(e) => setRegionName(e.target.value)} />
</div>
{/* Zone */}

View File

@ -3,7 +3,6 @@ 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 { RootState } from '../../../store';
@ -14,12 +13,6 @@ interface ZMDialogProps {
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;
selectedZone: string;
@ -32,7 +25,6 @@ interface ZMDialogProps {
export const ZMDialog: React.FC<ZMDialogProps> = ({
isOpen, onOpenChange, editingZMId, zmManagerId, setZmManagerId,
zmName, setZmName, zmCode, setZmCode, zmEmployeeId, setZmEmployeeId,
zmStatus, setZmStatus, selectedZone, setSelectedZone,
selectedRegions, setSelectedRegions, onSave,
userAssignedData
@ -66,10 +58,6 @@ export const ZMDialog: React.FC<ZMDialogProps> = ({
setZmManagerId(value);
const selectedUser = userAssignedData.find(u => u.id === value);
if (selectedUser) {
setZmName(selectedUser.name);
setZmEmployeeId(selectedUser.employeeId || '');
setZmCode(selectedUser.zmCode || selectedUser.employeeId || '');
if (selectedUser.zoneId) setSelectedZone(selectedUser.zoneId);
if (selectedUser.assignedRegionIds) setSelectedRegions(selectedUser.assignedRegionIds);
}
@ -92,17 +80,6 @@ export const ZMDialog: React.FC<ZMDialogProps> = ({
</Select>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Employee ID</Label>
<Input readOnly className="mt-2 bg-slate-50 border-slate-200" value={zmEmployeeId} />
</div>
<div>
<Label>ZM Code</Label>
<Input placeholder="Enter ZM Code" className="mt-2" value={zmCode} onChange={(e) => setZmCode(e.target.value)} />
</div>
</div>
<div>
<Label>Managed Zone <span className="text-red-500">*</span></Label>
<Select value={selectedZone} onValueChange={(value) => {

View File

@ -31,14 +31,15 @@ export const ZoneDialog: React.FC<ZoneDialogProps> = ({
// Always include the currently assigned head to ensure pre-filling works
if (zonalBusinessHeadId !== 'none' && u.id === zonalBusinessHeadId) return true;
const roles = u.allRoles || [];
const topLevelRole = (u.roleCode || '').toUpperCase();
const roles = (u.allRoles || []).map((r: string) => String(r || '').toUpperCase());
const topLevelRole = String(u.roleCode || u.role || '').toUpperCase();
return topLevelRole === 'ZBH' || roles.some((r: string) => {
const roleStr = (r || '').toUpperCase();
return topLevelRole === 'ZBH' || topLevelRole.includes('ZONAL BUSINESS HEAD') || roles.some((r: string) => {
const roleStr = String(r || '').toUpperCase();
return roleStr === 'ZBH' || roleStr === 'ZONE BUSINESS HEAD' || roleStr === 'ZONAL BUSINESS HEAD';
});
});
const dropdownUsers = filteredZBHUsers.length > 0 ? filteredZBHUsers : (userAssignedData || []);
// PRE-FILLING: When editing an existing zone, find its current ZBH from master data
React.useEffect(() => {
@ -84,7 +85,7 @@ export const ZoneDialog: React.FC<ZoneDialogProps> = ({
</SelectTrigger>
<SelectContent className="max-h-60">
<SelectItem value="none">None / Unassigned</SelectItem>
{filteredZBHUsers.map((user) => (
{dropdownUsers.map((user) => (
<SelectItem key={user.id} value={user.id}>
{user.name} ({user.email})
</SelectItem>

View File

@ -50,14 +50,29 @@ export const useMasterData = () => {
const bodyZms = getBody(zmsRes);
const bodyDdLeads = getBody(ddLeadsRes);
const users = (bodyUsers?.users || bodyUsers?.data || []).map((u: any) => ({
...u,
name: u.fullName || u.name,
role: u.role?.roleName || 'System User',
zone: u.allZones?.join(', ') || 'Global',
region: u.allRegions?.join(', ') || 'Unassigned',
status: u.isActive !== false ? 'Active' : 'Inactive'
}));
const users = (bodyUsers?.users || bodyUsers?.data || []).map((u: any) => {
const territory = Array.isArray(u.territoryProfile) ? u.territoryProfile : [];
const territoryZones = territory
.filter((t: any) => String(t.locationType || '').toLowerCase() === 'zone')
.map((t: any) => t.locationName || t.zone)
.filter(Boolean);
const territoryRegions = territory
.filter((t: any) => String(t.locationType || '').toLowerCase() === 'region')
.map((t: any) => t.locationName || t.region)
.filter(Boolean);
const zones = Array.from(new Set([...(u.allZones || []), ...territoryZones]));
const regions = Array.from(new Set([...(u.allRegions || []), ...territoryRegions]));
return {
...u,
name: u.fullName || u.name,
role: u.role?.roleName || (Array.isArray(u.allRoles) && u.allRoles.length > 0 ? u.allRoles[0] : 'System User'),
zone: zones.length > 0 ? zones.join(', ') : 'Global',
region: regions.length > 0 ? regions.join(', ') : 'Unassigned',
status: u.isActive !== false ? 'Active' : 'Inactive'
};
});
const roles = (bodyRoles?.roles || bodyRoles?.data || []).map((r: any) => ({
id: r.id, name: r.roleName, permissions: r.permissions?.map((p: any) => p.permissionCode) || [], userCount: r.userCount || 0

View File

@ -14,6 +14,10 @@ export const masterService = {
const response = await API.updateRole(id, data);
return response.data;
},
createRole: async (data: any) => {
const response = await (API as any).createRole(data);
return response.data;
},
// Zones & Regions
getZones: async () => {