629 lines
29 KiB
TypeScript
629 lines
29 KiB
TypeScript
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
import { useSelector } from 'react-redux';
|
|
import {
|
|
Tabs, TabsContent, TabsList, TabsTrigger
|
|
} from '@/components/ui/tabs';
|
|
import { Globe, Shield, Mail, MapPin, SlidersHorizontal, Settings, FileText, Settings2 } from 'lucide-react';
|
|
import { toast } from 'sonner';
|
|
|
|
// Services & Hooks
|
|
import { masterService } from '@/services/master.service';
|
|
import { useMasterData } from '@/hooks/useMasterData';
|
|
|
|
// Sub-components
|
|
import { ZonesOverview } from '@/features/master/components/ZonesOverview';
|
|
import { ZoneDetails } from '@/features/master/components/ZoneDetails';
|
|
import { RegionalManagement } from '@/features/master/components/RegionalManagement';
|
|
import { ASMManagement } from '@/features/master/components/ASMManagement';
|
|
import { ZMManagement } from '@/features/master/components/ZMManagement';
|
|
import { UserManagementTable } from '@/features/master/components/UserManagementTable';
|
|
import { RolePermissions } from '@/features/master/components/RolePermissions';
|
|
import { RoleDialog } from '@/features/master/components/RoleDialog';
|
|
import { AddRoleDialog } from '@/features/master/components/AddRoleDialog';
|
|
import { EmailTemplates } from '@/features/master/components/EmailTemplates';
|
|
import { LocationManagement } from '@/features/master/components/LocationManagement';
|
|
import { ASMDialog } from '@/features/master/components/ASMDialog';
|
|
import { DealerAsmAssignment } from '@/features/master/components/DealerAsmAssignment';
|
|
import { ZMDialog } from '@/features/master/components/ZMDialog';
|
|
import { ZoneDialog } from '@/features/master/components/ZoneDialog';
|
|
import { RegionDialog } from '@/features/master/components/RegionDialog';
|
|
import { TemplateDialog } from '@/features/master/components/TemplateDialog';
|
|
import { LocationDialog } from '@/features/master/components/LocationDialog';
|
|
import { SecurityDepositMaster } from '@/features/master/components/SecurityDepositMaster';
|
|
import { DocumentConfigManagement } from '@/features/master/components/DocumentConfigManagement';
|
|
import { AutoAssignmentSettings } from '@/features/master/components/AutoAssignmentSettings';
|
|
import { ApprovalPoliciesPage } from '@/components/admin/ApprovalPoliciesPage';
|
|
import { RootState } from '@/store';
|
|
|
|
export const MasterPage: React.FC = () => {
|
|
const { fetchInitialData, fetchAreas } = useMasterData();
|
|
const {
|
|
asms, zonalManagerMappings,
|
|
allStates,
|
|
allDistricts,
|
|
users,
|
|
roles,
|
|
loading
|
|
} = useSelector((state: RootState) => state.master);
|
|
|
|
// Tab & Selection State
|
|
const [activeTab, setActiveTab] = useState('hierarchy');
|
|
const [selectedZone, setSelectedZone] = useState('all');
|
|
|
|
// Dialog Visibility
|
|
const [showASMDialog, setShowASMDialog] = useState(false);
|
|
const [showZoneDialog, setShowZoneDialog] = useState(false);
|
|
const [showRegionDialog, setShowRegionDialog] = useState(false);
|
|
const [showTemplateDialog, setShowTemplateDialog] = useState(false);
|
|
const [showLocationDialog, setShowLocationDialog] = useState(false);
|
|
|
|
// Form State (ASM)
|
|
const [editingASMId, setEditingASMId] = useState<string | null>(null);
|
|
const [asmManagerId, setAsmManagerId] = useState('');
|
|
const [asmStatus, setAsmStatus] = useState<'active' | 'inactive'>('active');
|
|
const [selectedASMZone, setSelectedASMZone] = useState('');
|
|
const [selectedASMRegion, setSelectedASMRegion] = useState('');
|
|
const [selectedASMStates, setSelectedASMStates] = useState<string[]>([]);
|
|
const [selectedASMDistricts, setSelectedASMDistricts] = useState<string[]>([]);
|
|
const [asmRoleCode, setAsmRoleCode] = useState<'ASM' | 'DD-AM'>('DD-AM');
|
|
|
|
// ZM Management State
|
|
const [showZMDialog, setShowZMDialog] = useState(false);
|
|
const [editingZMId, setEditingZMId] = useState<string | null>(null);
|
|
const [zmManagerId, setZmManagerId] = useState('');
|
|
const [zmStatus, setZmStatus] = useState<'active' | 'inactive'>('active');
|
|
const [selectedZMZone, setSelectedZMZone] = useState('');
|
|
const [selectedZMRegions, setSelectedZMRegions] = 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);
|
|
const [zoneName, setZoneName] = useState('');
|
|
const [zoneCode, setZoneCode] = useState('');
|
|
const [zoneDescription, setZoneDescription] = useState('');
|
|
const [zonalBusinessHeadId, setZonalBusinessHeadId] = useState('none');
|
|
|
|
// Form State (Region)
|
|
const [editingRegionId, setEditingRegionId] = useState<string | null>(null);
|
|
const [regionName, setRegionName] = useState('');
|
|
const [regionDescription, setRegionDescription] = useState('');
|
|
const [selectedRegionZone, setSelectedRegionZone] = useState('');
|
|
const [regionalManagerId, setRegionalManagerId] = useState('');
|
|
const [selectedRegionDistricts, setSelectedRegionDistricts] = useState<string[]>([]);
|
|
|
|
// Form State (Template)
|
|
const [editingTemplate, setEditingTemplate] = useState<any>(null);
|
|
const [testDataInput, setTestDataInput] = useState('{"applicant_name": "John Doe"}');
|
|
const [previewLoading, setPreviewLoading] = useState(false);
|
|
const [previewContent, setPreviewContent] = useState<any>(null);
|
|
|
|
// Form State (Location)
|
|
const [editingLocationId, setEditingLocationId] = useState<string | null>(null);
|
|
const [locationState, setLocationState] = useState('');
|
|
const [locationDistrict, setLocationDistrict] = useState('');
|
|
const [locationCity, setLocationCity] = useState('');
|
|
const [locationActiveFrom, setLocationActiveFrom] = useState('');
|
|
const [locationActiveTo, setLocationActiveTo] = useState('');
|
|
const [locationStatus, setLocationStatus] = useState('active');
|
|
|
|
// Search & Pagination State (Locations)
|
|
const [districtsSearch, setDistrictsSearch] = useState('');
|
|
const [districtsPage, setDistrictsPage] = useState(1);
|
|
const [locationStateFilter, setLocationStateFilter] = useState('all');
|
|
const [locationStatusFilter, setLocationStatusFilter] = useState('all');
|
|
|
|
// Initial Load
|
|
useEffect(() => {
|
|
fetchInitialData();
|
|
}, [fetchInitialData]);
|
|
|
|
// Sync editingRole with latest data from Redux
|
|
useEffect(() => {
|
|
if (editingRole && roles.length > 0) {
|
|
const latest = roles.find((r: any) => r.id === editingRole.id);
|
|
if (latest) setEditingRole(latest);
|
|
}
|
|
}, [roles, editingRole?.id]);
|
|
|
|
// Shared Data Helpers
|
|
const districtsAssignedToOthers = useMemo(() => {
|
|
const map: Record<string, string[]> = {};
|
|
[...asms, ...zonalManagerMappings].forEach(m => {
|
|
const dists = (m as any).areasManaged || (m as any).districts || [];
|
|
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;
|
|
}, [asms, zonalManagerMappings]);
|
|
|
|
const getDistrictsForSelectedState = useCallback((stateName: string, regionId?: string) => {
|
|
return allDistricts
|
|
.filter(d =>
|
|
d.stateName?.toUpperCase() === stateName?.toUpperCase() &&
|
|
(!regionId || d.regionId === regionId)
|
|
)
|
|
.map(d => ({ id: d.id, name: d.name }));
|
|
}, [allDistricts]);
|
|
|
|
// Handlers
|
|
const handleSaveASM = async () => {
|
|
if (!asmManagerId) {
|
|
toast.error('Please select a DD-AM user');
|
|
return;
|
|
}
|
|
try {
|
|
const payload = {
|
|
userId: asmManagerId,
|
|
roleCode: asmRoleCode,
|
|
districts: selectedASMDistricts,
|
|
status: asmStatus
|
|
};
|
|
const res = await masterService.saveASM(payload) as any;
|
|
if (res.success) {
|
|
toast.success(`DD Area Manager ${editingASMId ? 'updated' : 'assigned'} successfully`);
|
|
setShowASMDialog(false);
|
|
fetchInitialData();
|
|
} else {
|
|
toast.error(res.message || 'Failed to save ASM');
|
|
}
|
|
} catch (error: any) {
|
|
const msg = error?.response?.data?.message || error?.message || 'Failed to save ASM';
|
|
toast.error(msg);
|
|
}
|
|
};
|
|
|
|
const handleEditASM = (asm: any) => {
|
|
setEditingASMId(asm.id);
|
|
setAsmManagerId(asm.id);
|
|
setAsmStatus(asm.status.toLowerCase() as 'active' | 'inactive');
|
|
setSelectedASMZone(asm.zoneId);
|
|
setSelectedASMRegion(asm.regionId);
|
|
setSelectedASMStates(asm.stateNames || []);
|
|
setSelectedASMDistricts(asm.areasManaged?.map((a: any) => a.id) || []);
|
|
setAsmRoleCode('DD-AM');
|
|
setShowASMDialog(true);
|
|
};
|
|
|
|
const handleEditZM = (zm: any) => {
|
|
setEditingZMId(zm.id);
|
|
setZmManagerId(zm.id);
|
|
setZmStatus(zm.status?.toLowerCase() === 'active' ? 'active' : 'inactive');
|
|
setSelectedZMZone(zm.zoneId || '');
|
|
setSelectedZMRegions(zm.assignedRegionIds || []);
|
|
setShowZMDialog(true);
|
|
};
|
|
|
|
|
|
const handleSaveZM = async () => {
|
|
if (!zmManagerId || !selectedZMZone) {
|
|
toast.error('Manager and Zone are required');
|
|
return;
|
|
}
|
|
try {
|
|
const payload = {
|
|
userId: zmManagerId,
|
|
zoneId: selectedZMZone,
|
|
regionIds: selectedZMRegions,
|
|
status: zmStatus
|
|
};
|
|
|
|
const res = await (masterService as any).saveZonalManager(payload) as any;
|
|
if (res.success) {
|
|
toast.success(`Zonal Manager ${editingZMId ? 'updated' : 'assigned'} successfully`);
|
|
setShowZMDialog(false);
|
|
fetchInitialData();
|
|
} else {
|
|
toast.error(res.message || 'Failed to save Zonal Manager');
|
|
}
|
|
} catch (error: any) {
|
|
const msg = error?.response?.data?.message || error?.message || 'Failed to save Zonal Manager';
|
|
toast.error(msg);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
const handleSaveZone = async () => {
|
|
try {
|
|
const payload = {
|
|
id: editingZoneId,
|
|
name: zoneName,
|
|
code: zoneCode,
|
|
description: zoneDescription,
|
|
managerId: zonalBusinessHeadId === 'none' ? null : zonalBusinessHeadId
|
|
};
|
|
const res = await masterService.saveZone(payload) as any;
|
|
if (res.success) {
|
|
toast.success('Zone saved successfully');
|
|
setShowZoneDialog(false);
|
|
fetchInitialData();
|
|
} else {
|
|
toast.error(res.message || 'Error saving zone');
|
|
}
|
|
} catch (error: any) {
|
|
const msg = error?.response?.data?.message || error?.message || 'Error saving zone';
|
|
toast.error(msg);
|
|
}
|
|
};
|
|
|
|
const handleSaveRegion = async () => {
|
|
try {
|
|
const payload = {
|
|
...(editingRegionId ? { id: editingRegionId } : {}),
|
|
name: regionName,
|
|
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');
|
|
setShowRegionDialog(false);
|
|
fetchInitialData();
|
|
} else {
|
|
toast.error(res.message || 'Error saving region');
|
|
}
|
|
} catch (error: any) {
|
|
const msg = error?.response?.data?.message || error?.message || 'Error saving region';
|
|
toast.error(msg);
|
|
}
|
|
};
|
|
|
|
const handleSaveTemplate = async (body: string) => {
|
|
try {
|
|
if (!editingTemplate?.id) {
|
|
toast.error('Open a template from the list to edit.');
|
|
return;
|
|
}
|
|
const res = await masterService.updateEmailTemplate(editingTemplate.id, {
|
|
...editingTemplate,
|
|
body
|
|
}) as any;
|
|
if (res.success) {
|
|
toast.success('Template saved');
|
|
setShowTemplateDialog(false);
|
|
fetchInitialData();
|
|
} else {
|
|
toast.error(res.message || 'Error saving template');
|
|
}
|
|
} catch (error: any) {
|
|
const msg = error?.response?.data?.message || error?.message || 'Error saving template';
|
|
toast.error(msg);
|
|
}
|
|
};
|
|
|
|
const handlePreviewTemplate = async (body: string) => {
|
|
setPreviewLoading(true);
|
|
try {
|
|
let data: Record<string, unknown>;
|
|
try {
|
|
data = JSON.parse(testDataInput) as Record<string, unknown>;
|
|
} catch {
|
|
toast.error('Mock test data must be valid JSON');
|
|
return;
|
|
}
|
|
const res = await masterService.previewEmailTemplate({
|
|
subject: editingTemplate?.subject,
|
|
body,
|
|
data
|
|
}) as any;
|
|
if (res.success) {
|
|
setPreviewContent(res.data);
|
|
} else {
|
|
toast.error(res.message || 'Preview failed');
|
|
}
|
|
} catch (error: any) {
|
|
const d = error?.response?.data;
|
|
const detail = d?.error || d?.message;
|
|
toast.error(detail || error?.message || 'Preview failed');
|
|
} finally { setPreviewLoading(false); }
|
|
};
|
|
|
|
const handleSaveRole = async (roleId: string, permissions: string[]) => {
|
|
try {
|
|
const res = await masterService.updateRole(roleId, { permissions }) as any;
|
|
if (res.success) {
|
|
toast.success('Role permissions updated successfully');
|
|
setShowRoleDialog(false);
|
|
fetchInitialData();
|
|
} else {
|
|
toast.error(res.message || 'Error saving role permissions');
|
|
}
|
|
} catch (error: any) {
|
|
const msg = error?.response?.data?.message || error?.message || 'Error saving role permissions';
|
|
toast.error(msg);
|
|
}
|
|
};
|
|
|
|
const handleEditRole = (role: any) => {
|
|
setEditingRole(role);
|
|
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(matchedDistrict?.stateId || loc.stateId || '');
|
|
setLocationCity(loc.city || '');
|
|
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.isOpportunity ? 'active' : 'inactive');
|
|
setShowLocationDialog(true);
|
|
};
|
|
|
|
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,
|
|
stateId: locationState,
|
|
stateName: (selectedState as any)?.name || (selectedState as any)?.stateName || '',
|
|
districtId: locationDistrict,
|
|
name: locationCity || selectedDistrict?.name || 'New Location',
|
|
city: locationCity,
|
|
status: locationStatus,
|
|
openFrom: locationActiveFrom,
|
|
openTo: locationActiveTo,
|
|
isOpportunity: locationStatus === 'active'
|
|
};
|
|
const res = await (editingLocationId
|
|
? masterService.updateArea(editingLocationId, payload)
|
|
: masterService.createArea(payload)) as any;
|
|
if (res.success) {
|
|
toast.success('Location saved');
|
|
setShowLocationDialog(false);
|
|
fetchAreas({ search: districtsSearch, page: districtsPage });
|
|
}
|
|
} catch (error) { toast.error('Error saving location'); }
|
|
};
|
|
|
|
useEffect(() => {
|
|
const handler = setTimeout(() => {
|
|
fetchAreas({
|
|
search: districtsSearch,
|
|
page: districtsPage,
|
|
stateId: locationStateFilter === 'all' ? undefined : locationStateFilter,
|
|
isOpportunity: locationStatusFilter === 'all' ? undefined : (locationStatusFilter === 'active' ? 'true' : 'false')
|
|
});
|
|
}, 500);
|
|
return () => clearTimeout(handler);
|
|
}, [districtsSearch, districtsPage, locationStateFilter, locationStatusFilter, fetchAreas]);
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-slate-900 mb-2 font-bold text-2xl">Master Configuration</h1>
|
|
<p className="text-slate-600">Centralized governance for locations, roles, and operational policies</p>
|
|
</div>
|
|
</div>
|
|
|
|
{loading ? (
|
|
<div className="flex flex-col items-center justify-center p-20 space-y-4">
|
|
<div className="w-12 h-12 border-4 border-re-red border-t-transparent rounded-full animate-spin"></div>
|
|
<p className="text-slate-600 font-medium animate-pulse">Synchronizing Global Settings...</p>
|
|
</div>
|
|
) : (
|
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
|
|
<TabsList className="grid w-full grid-cols-8 h-auto sticky top-0 z-10 bg-white/80 backdrop-blur-sm border shadow-sm rounded-xl p-1">
|
|
<TabsTrigger value="hierarchy" className="flex items-center gap-2 py-3 rounded-lg data-[state=active]:bg-re-red data-[state=active]:text-white">
|
|
<Globe className="w-4 h-4" /> Organisation
|
|
</TabsTrigger>
|
|
<TabsTrigger value="roles" className="flex items-center gap-2 py-3 rounded-lg data-[state=active]:bg-re-red data-[state=active]:text-white">
|
|
<Shield className="w-4 h-4" /> Roles
|
|
</TabsTrigger>
|
|
<TabsTrigger value="templates" className="flex items-center gap-2 py-3 rounded-lg data-[state=active]:bg-re-red data-[state=active]:text-white">
|
|
<Mail className="w-4 h-4" /> Emails
|
|
</TabsTrigger>
|
|
<TabsTrigger value="locations" className="flex items-center gap-2 py-3 rounded-lg data-[state=active]:bg-re-red data-[state=active]:text-white">
|
|
<MapPin className="w-4 h-4" /> Locations
|
|
</TabsTrigger>
|
|
<TabsTrigger value="approvals" className="flex items-center gap-2 py-3 rounded-lg data-[state=active]:bg-re-red data-[state=active]:text-white transition-all transform hover:scale-[1.02]">
|
|
<SlidersHorizontal className="w-4 h-4" /> Approvals
|
|
</TabsTrigger>
|
|
<TabsTrigger value="documents" className="flex items-center gap-2 py-3 rounded-lg data-[state=active]:bg-re-red data-[state=active]:text-white transition-all transform hover:scale-[1.02]">
|
|
<FileText className="w-4 h-4" /> Docs Config
|
|
</TabsTrigger>
|
|
<TabsTrigger value="governance" className="flex items-center gap-2 py-3 rounded-lg data-[state=active]:bg-re-red data-[state=active]:text-white transition-all transform hover:scale-[1.02]">
|
|
<Settings2 className="w-4 h-4" /> Governance
|
|
</TabsTrigger>
|
|
<TabsTrigger value="settings" className="flex items-center gap-2 py-3 rounded-lg data-[state=active]:bg-re-red data-[state=active]:text-white transition-all transform hover:scale-[1.02]">
|
|
<Settings className="w-4 h-4" /> App Settings
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="hierarchy" className="space-y-8 animate-in fade-in slide-in-from-bottom-2 duration-300">
|
|
<ZonesOverview selectedZone={selectedZone} onZoneClick={(id) => setSelectedZone(selectedZone === id ? 'all' : id)} />
|
|
<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(''); setSelectedRegionZone(selectedZone === 'all' ? '' : selectedZone); setRegionalManagerId(''); setSelectedRegionDistricts([]); setShowRegionDialog(true); }}
|
|
onEditRegion={(r) => {
|
|
setEditingRegionId(r.id);
|
|
setRegionName(r.name);
|
|
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')} />
|
|
|
|
<ZMManagement selectedZone={selectedZone}
|
|
onAddZM={() => {
|
|
setEditingZMId(null); setZmManagerId('');
|
|
setZmStatus('active'); setSelectedZMZone(selectedZone === 'all' ? '' : selectedZone);
|
|
setSelectedZMRegions([]);
|
|
setShowZMDialog(true);
|
|
}}
|
|
onEditZM={handleEditZM}
|
|
onDeleteZM={() => toast.error('ZM deletion restricted')} />
|
|
|
|
<ASMManagement selectedZone={selectedZone} onAddASM={() => { setEditingASMId(null); setAsmManagerId(''); setSelectedASMZone(selectedZone === 'all' ? '' : selectedZone); setSelectedASMRegion(''); setSelectedASMStates([]); setSelectedASMDistricts([]); setAsmRoleCode('DD-AM'); setShowASMDialog(true); }}
|
|
onEditASM={handleEditASM} onDeleteASM={() => toast.error('ASM deletion restricted')} />
|
|
|
|
<DealerAsmAssignment />
|
|
|
|
|
|
<UserManagementTable userAssignedData={users.length > 0 ? users : asms} />
|
|
</TabsContent>
|
|
|
|
<TabsContent value="roles" className="animate-in fade-in duration-300">
|
|
<RolePermissions onAddRole={() => setShowAddRoleDialog(true)}
|
|
onEditRole={handleEditRole} />
|
|
</TabsContent>
|
|
|
|
<TabsContent value="templates" className="animate-in fade-in duration-300">
|
|
<EmailTemplates
|
|
onEditTemplate={(template) => {
|
|
setEditingTemplate(template);
|
|
if (template.placeholders && Array.isArray(template.placeholders)) {
|
|
const defaults = template.placeholders.reduce((acc: any, p: string) => {
|
|
acc[p] = `[${p}]`;
|
|
return acc;
|
|
}, {});
|
|
setTestDataInput(JSON.stringify(defaults, null, 2));
|
|
} else {
|
|
setTestDataInput('{}');
|
|
}
|
|
setShowTemplateDialog(true);
|
|
}}
|
|
onDeleteTemplate={() => toast.error('Delete Template restricted')}
|
|
/>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="locations" className="animate-in fade-in duration-300">
|
|
<LocationManagement
|
|
states={allStates}
|
|
stateFilter={locationStateFilter}
|
|
onStateFilterChange={(val: string) => {
|
|
setLocationStateFilter(val);
|
|
setDistrictsPage(1);
|
|
}}
|
|
statusFilter={locationStatusFilter}
|
|
onStatusFilterChange={(val: string) => {
|
|
setLocationStatusFilter(val);
|
|
setDistrictsPage(1);
|
|
}}
|
|
onAddLocation={() => {
|
|
setEditingLocationId(null);
|
|
setLocationState('');
|
|
setLocationCity('');
|
|
setLocationDistrict('');
|
|
setLocationActiveFrom('');
|
|
setLocationActiveTo('');
|
|
setLocationStatus('active');
|
|
setShowLocationDialog(true);
|
|
}}
|
|
onEditLocation={handleEditLocation}
|
|
onDeleteLocation={(id) => {
|
|
if (window.confirm('Are you sure you want to delete this location?')) {
|
|
(masterService as any).deleteArea(id).then((res: any) => {
|
|
if (res.success) {
|
|
toast.success('Location deleted');
|
|
fetchAreas({
|
|
search: districtsSearch,
|
|
page: districtsPage,
|
|
stateId: locationStateFilter === 'all' ? undefined : locationStateFilter
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}}
|
|
onSearch={(term) => {
|
|
setDistrictsSearch(term);
|
|
setDistrictsPage(1); // Reset to first page on search
|
|
}}
|
|
onPageChange={setDistrictsPage}
|
|
searchTerm={districtsSearch}
|
|
/>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="approvals" className="animate-in fade-in duration-300">
|
|
<ApprovalPoliciesPage />
|
|
</TabsContent>
|
|
|
|
<TabsContent value="documents" className="animate-in fade-in duration-300">
|
|
<DocumentConfigManagement />
|
|
</TabsContent>
|
|
|
|
<TabsContent value="governance" className="animate-in fade-in duration-300">
|
|
<AutoAssignmentSettings />
|
|
</TabsContent>
|
|
|
|
<TabsContent value="settings" className="animate-in fade-in duration-300">
|
|
<SecurityDepositMaster />
|
|
</TabsContent>
|
|
</Tabs>
|
|
)}
|
|
|
|
{/* 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} 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}
|
|
zmStatus={zmStatus}
|
|
setZmStatus={setZmStatus}
|
|
selectedZone={selectedZMZone}
|
|
setSelectedZone={setSelectedZMZone}
|
|
selectedRegions={selectedZMRegions}
|
|
setSelectedRegions={setSelectedZMRegions}
|
|
onSave={handleSaveZM}
|
|
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}
|
|
/>
|
|
<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} allStates={allStates} allDistricts={allDistricts} onSave={handleSaveLocation} />
|
|
<RoleDialog isOpen={showRoleDialog} onOpenChange={setShowRoleDialog} role={editingRole} onSave={handleSaveRole} />
|
|
<AddRoleDialog isOpen={showAddRoleDialog} onOpenChange={setShowAddRoleDialog} onSave={handleCreateRole} />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
|
|
// No default export as App.tsx expects named export MasterPage
|