290 lines
13 KiB
TypeScript
290 lines
13 KiB
TypeScript
import React from 'react';
|
|
import { useSelector } from 'react-redux';
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
import { RootState } from '@/store';
|
|
|
|
interface ASMDialogProps {
|
|
isOpen: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
editingASMId: string | null;
|
|
asmManagerId: string;
|
|
setAsmManagerId: (id: string) => void;
|
|
asmStatus: 'active' | 'inactive';
|
|
setAsmStatus: (status: 'active' | 'inactive') => void;
|
|
selectedASMZone: string;
|
|
setSelectedASMZone: (id: string) => void;
|
|
selectedASMRegion: string;
|
|
setSelectedASMRegion: (id: string) => void;
|
|
selectedASMStates: string[];
|
|
setSelectedASMStates: (states: string[]) => void;
|
|
selectedASMDistricts: string[];
|
|
setSelectedASMDistricts: (districts: string[]) => void;
|
|
onSave: () => void;
|
|
asmRoleCode: 'ASM' | 'DD-AM';
|
|
setAsmRoleCode: (role: 'ASM' | 'DD-AM') => void;
|
|
userAssignedData: any[];
|
|
districtsAssignedToOthers: Record<string, string[]>;
|
|
getDistrictsForSelectedState: (state: string) => { id: string; name: string }[];
|
|
}
|
|
|
|
export const ASMDialog: React.FC<ASMDialogProps> = ({
|
|
isOpen, onOpenChange, editingASMId, asmManagerId, setAsmManagerId,
|
|
asmStatus, setAsmStatus, selectedASMZone, setSelectedASMZone,
|
|
selectedASMRegion, setSelectedASMRegion, selectedASMStates, setSelectedASMStates,
|
|
selectedASMDistricts, setSelectedASMDistricts, onSave,
|
|
asmRoleCode,
|
|
userAssignedData, districtsAssignedToOthers, getDistrictsForSelectedState
|
|
}) => {
|
|
const { zones, regionalOffices } = useSelector((state: RootState) => state.master);
|
|
|
|
const filteredASMUsers = userAssignedData.filter(u => {
|
|
const roles = (u.allRoles || []).map((r: string) => String(r || '').toUpperCase());
|
|
const roleCode = String(u.roleCode || '').toUpperCase();
|
|
return roles.includes('DD-AM') || roleCode === 'DD-AM';
|
|
});
|
|
|
|
// PRE-FILLING LOGIC: When manager or role changes, pre-select their districts
|
|
React.useEffect(() => {
|
|
if (asmManagerId && isOpen) {
|
|
const manager = userAssignedData.find(u => u.id === asmManagerId);
|
|
if (manager && manager.territoryProfile) {
|
|
const assignedDistricts = manager.territoryProfile
|
|
.filter((t: any) => t.roleCode === asmRoleCode && t.locationType === 'district')
|
|
.map((t: any) => t.locationId);
|
|
|
|
if (assignedDistricts.length > 0) {
|
|
setSelectedASMDistricts(assignedDistricts);
|
|
}
|
|
}
|
|
}
|
|
}, [asmManagerId, asmRoleCode, isOpen, userAssignedData, setSelectedASMDistricts]);
|
|
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>{editingASMId ? 'Edit' : 'Add'} DD Area Manager</DialogTitle>
|
|
<DialogDescription>Configure DD-AM details and district assignment</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<Label>Zone</Label>
|
|
<Select value={selectedASMZone} onValueChange={(value) => {
|
|
setSelectedASMZone(value);
|
|
setSelectedASMRegion('');
|
|
setSelectedASMStates([]);
|
|
setSelectedASMDistricts([]);
|
|
}}>
|
|
<SelectTrigger className="mt-2 text-slate-900">
|
|
<SelectValue placeholder="Select zone" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{zones.map((zone) => (
|
|
<SelectItem key={zone.id} value={zone.id}>{zone.name}</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{selectedASMZone && (
|
|
<div className="space-y-4">
|
|
<div>
|
|
<Label>Regional Office</Label>
|
|
<Select value={selectedASMRegion} onValueChange={(value) => {
|
|
setSelectedASMRegion(value);
|
|
setSelectedASMStates([]);
|
|
setSelectedASMDistricts([]);
|
|
}}>
|
|
<SelectTrigger className="mt-2 text-slate-900">
|
|
<SelectValue placeholder="Select regional office" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{regionalOffices
|
|
.filter((office) => office.zoneId === selectedASMZone)
|
|
.map((office) => (
|
|
<SelectItem key={office.id} value={office.id}>{office.name}</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div>
|
|
<Label>Select DD-AM User</Label>
|
|
<Select value={asmManagerId} onValueChange={setAsmManagerId}>
|
|
<SelectTrigger className="mt-2 text-slate-900">
|
|
<SelectValue placeholder="Select DD-AM" />
|
|
</SelectTrigger>
|
|
<SelectContent className="max-h-64">
|
|
{filteredASMUsers.map(user => (
|
|
<SelectItem key={user.id} value={user.id}>
|
|
{user.name} ({user.employeeId || 'No ID'})
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{selectedASMRegion && (
|
|
<div>
|
|
<Label>States Covered</Label>
|
|
<div className="mt-2 border rounded-lg p-3 max-h-48 overflow-y-auto bg-slate-50">
|
|
{(() => {
|
|
const selectedRegion = regionalOffices.find(r => r.id === selectedASMRegion);
|
|
const availableStates = (selectedRegion?.states || []).map((s: any) => typeof s === 'string' ? s : s.name);
|
|
|
|
return availableStates.length > 0 ? (
|
|
<div className="space-y-2">
|
|
{availableStates.map((state: string) => (
|
|
<div key={state} className="flex items-center space-x-2">
|
|
<Checkbox
|
|
id={`asm-state-${state}`}
|
|
checked={selectedASMStates.some(s => s.toLowerCase() === state.toLowerCase())}
|
|
onCheckedChange={(checked) => {
|
|
if (checked) {
|
|
setSelectedASMStates([...selectedASMStates, state]);
|
|
} else {
|
|
setSelectedASMStates(selectedASMStates.filter(s => s.toLowerCase() !== state.toLowerCase()));
|
|
const stateDistricts = getDistrictsForSelectedState(state);
|
|
setSelectedASMDistricts(selectedASMDistricts.filter(dId => !stateDistricts.some(sd => sd.id === dId)));
|
|
}
|
|
}}
|
|
/>
|
|
<label htmlFor={`asm-state-${state}`} className="text-sm cursor-pointer text-slate-900">
|
|
{state}
|
|
</label>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-slate-500">No states available for this regional office</p>
|
|
);
|
|
})()}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{selectedASMStates.length > 0 && (
|
|
<div>
|
|
<Label>Districts/Cities Covered</Label>
|
|
<div className="mt-2 border rounded-lg p-3 max-h-64 overflow-y-auto bg-slate-50">
|
|
<TooltipProvider>
|
|
{selectedASMStates.map((state) => {
|
|
const districts = getDistrictsForSelectedState(state);
|
|
if (districts.length === 0) return null;
|
|
|
|
return (
|
|
<div key={state} className="mb-4 last:mb-0">
|
|
<h4 className="text-sm text-re-red-hover mb-2 pb-1 border-b border-slate-200">{state}</h4>
|
|
<div className="space-y-2 ml-2">
|
|
{districts.map((district: any) => (
|
|
<div key={district.id}>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<div className="flex items-center space-x-2 py-0.5">
|
|
<Checkbox
|
|
id={`asm-district-${district.id}`}
|
|
checked={selectedASMDistricts.includes(district.id)}
|
|
disabled={!!districtsAssignedToOthers[district.id]}
|
|
onCheckedChange={(checked) => {
|
|
if (checked) {
|
|
setSelectedASMDistricts([...selectedASMDistricts, district.id]);
|
|
} else {
|
|
setSelectedASMDistricts(selectedASMDistricts.filter(id => id !== district.id));
|
|
}
|
|
}}
|
|
/>
|
|
<label
|
|
htmlFor={`asm-district-${district.id}`}
|
|
className={`text-sm flex items-center gap-1.5 ${districtsAssignedToOthers[district.id] ? "text-slate-400 cursor-not-allowed" : "cursor-pointer text-slate-900"}`}
|
|
>
|
|
{district.name}
|
|
</label>
|
|
</div>
|
|
</TooltipTrigger>
|
|
{districtsAssignedToOthers[district.id] && (
|
|
<TooltipContent>
|
|
<p>Already managed by: {districtsAssignedToOthers[district.id].join(', ')}</p>
|
|
</TooltipContent>
|
|
)}
|
|
</Tooltip>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</TooltipProvider>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="border-t pt-4">
|
|
<Label>DD Area Manager <span className="text-red-500">*</span></Label>
|
|
<Select
|
|
value={asmManagerId}
|
|
onValueChange={(value) => {
|
|
setAsmManagerId(value);
|
|
const selectedUser = userAssignedData.find(u => u.id === value);
|
|
if (selectedUser) {
|
|
const roleProfile = (selectedUser.territoryProfile || []).find((t: any) => t.roleCode === asmRoleCode);
|
|
|
|
// Extract zone/region from assignments if present
|
|
if (roleProfile?.zoneId) setSelectedASMZone(roleProfile.zoneId);
|
|
if (roleProfile?.regionId) setSelectedASMRegion(roleProfile.regionId);
|
|
|
|
setSelectedASMDistricts(selectedUser.areasManaged?.filter((a: any) => a.roleCode === asmRoleCode).map((a: any) => a.id) || []);
|
|
setSelectedASMStates(selectedUser.stateNames || []);
|
|
}
|
|
}}
|
|
disabled={!!editingASMId}
|
|
>
|
|
<SelectTrigger className="mt-2 w-full text-slate-900">
|
|
<SelectValue placeholder="Select DD-AM User" />
|
|
</SelectTrigger>
|
|
<SelectContent className="max-h-60">
|
|
{filteredASMUsers.length > 0 ? (
|
|
filteredASMUsers.map((user) => (
|
|
<SelectItem key={user.id} value={user.id}>
|
|
<div className="flex flex-col text-left">
|
|
<span className="font-medium text-slate-900">{user.name}</span>
|
|
<span className="text-xs text-slate-500">{user.email}</span>
|
|
</div>
|
|
</SelectItem>
|
|
))
|
|
) : (
|
|
<div className="p-2 text-sm text-slate-500 text-center">No users available</div>
|
|
)}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div>
|
|
<Label>Status</Label>
|
|
<Select value={asmStatus} onValueChange={(val: 'active' | 'inactive') => setAsmStatus(val)}>
|
|
<SelectTrigger className="mt-2 text-slate-900">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="active">Active</SelectItem>
|
|
<SelectItem value="inactive">Inactive</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="flex gap-3 pt-4">
|
|
<Button variant="outline" className="flex-1" onClick={() => onOpenChange(false)}>Cancel</Button>
|
|
<Button className="flex-1 bg-re-red hover:bg-re-red-hover" onClick={onSave}>Save DD-AM</Button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|