Dealer_Onboard_Frontend/src/components/applications/application-details/ApplicationDetailsActionModals.tsx

333 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Check, CheckCircle, Clock, Loader2 } from 'lucide-react';
import { toast } from 'sonner';
import { onboardingService } from '../../../services/onboarding.service';
import { Button } from '../../ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../../ui/dialog';
import { Input } from '../../ui/input';
import { Label } from '../../ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/select';
import { Textarea } from '../../ui/textarea';
interface ApplicationDetailsActionModalsProps {
application: any;
fetchApplication: () => void;
showApproveModal: boolean;
setShowApproveModal: (value: boolean) => void;
approvalRemark: string;
setApprovalRemark: (value: string) => void;
setApprovalFile: (file: File | null) => void;
isApproving: boolean;
handleApprove: () => void;
showOnboardModal: boolean;
setShowOnboardModal: (value: boolean) => void;
isOnboarding: boolean;
setIsOnboarding: (value: boolean) => void;
showRejectModal: boolean;
setShowRejectModal: (value: boolean) => void;
rejectionReason: string;
setRejectionReason: (value: string) => void;
isRejecting: boolean;
handleReject: () => void;
showScheduleModal: boolean;
setShowScheduleModal: (value: boolean) => void;
interviewType: string;
setInterviewType: (value: string) => void;
interviewMode: string;
setInterviewMode: (value: string) => void;
interviewDate: string;
setInterviewDate: (value: string) => void;
meetingLink: string;
setMeetingLink: (value: string) => void;
location: string;
setLocation: (value: string) => void;
isInterviewCompleted: (level: number) => boolean;
isInterviewActive: (level: number) => boolean;
users: any[];
selectedInterviewerId: string;
setSelectedInterviewerId: (value: string) => void;
handleAddInterviewer: () => void;
scheduledInterviewParticipants: any[];
handleRemoveInterviewer: (id: string) => void;
isScheduling: boolean;
handleScheduleInterview: () => void;
showAssignArchitectureModal: boolean;
setShowAssignArchitectureModal: (value: boolean) => void;
architectureLeadId: string;
setArchitectureLeadId: (value: string) => void;
isAssigningArchitecture: boolean;
handleAssignArchitecture: () => void;
showArchitectureStatusModal: boolean;
setShowArchitectureStatusModal: (value: boolean) => void;
architectureStatus: string;
setArchitectureStatus: (value: string) => void;
architectureRemarks: string;
setArchitectureRemarks: (value: string) => void;
isUpdatingArchitecture: boolean;
handleUpdateArchitectureStatus: () => void;
}
export function ApplicationDetailsActionModals(props: ApplicationDetailsActionModalsProps) {
const {
application,
fetchApplication,
showApproveModal,
setShowApproveModal,
approvalRemark,
setApprovalRemark,
setApprovalFile,
isApproving,
handleApprove,
showOnboardModal,
setShowOnboardModal,
isOnboarding,
setIsOnboarding,
showRejectModal,
setShowRejectModal,
rejectionReason,
setRejectionReason,
isRejecting,
handleReject,
showScheduleModal,
setShowScheduleModal,
interviewType,
setInterviewType,
interviewMode,
setInterviewMode,
interviewDate,
setInterviewDate,
meetingLink,
setMeetingLink,
location,
setLocation,
isInterviewCompleted,
isInterviewActive,
users,
selectedInterviewerId,
setSelectedInterviewerId,
handleAddInterviewer,
scheduledInterviewParticipants,
handleRemoveInterviewer,
isScheduling,
handleScheduleInterview,
showAssignArchitectureModal,
setShowAssignArchitectureModal,
architectureLeadId,
setArchitectureLeadId,
isAssigningArchitecture,
handleAssignArchitecture,
showArchitectureStatusModal,
setShowArchitectureStatusModal,
architectureStatus,
setArchitectureStatus,
architectureRemarks,
setArchitectureRemarks,
isUpdatingArchitecture,
handleUpdateArchitectureStatus,
} = props;
return (
<>
<Dialog open={showApproveModal} onOpenChange={setShowApproveModal}>
<DialogContent>
<DialogHeader>
<DialogTitle>Approve Application</DialogTitle>
<DialogDescription>Provide approval remarks and optionally attach supporting documents.</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div>
<Label>Remark (Required)</Label>
<Textarea placeholder="Enter approval remarks..." value={approvalRemark} onChange={(e) => setApprovalRemark(e.target.value)} className="mt-2" rows={4} />
</div>
<div>
<Label>Attach File (Optional)</Label>
<Input type="file" className="mt-2" onChange={(e) => setApprovalFile(e.target.files ? e.target.files[0] : null)} />
</div>
<div className="flex gap-3">
<Button variant="outline" className="flex-1" onClick={() => setShowApproveModal(false)} disabled={isApproving}>Cancel</Button>
<Button className="flex-1 bg-green-600 hover:bg-green-700" onClick={handleApprove} disabled={isApproving}>
{isApproving ? <><Loader2 className="w-4 h-4 mr-2 animate-spin" />Approving...</> : 'Submit Approval'}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showOnboardModal} onOpenChange={setShowOnboardModal}>
<DialogContent className="max-w-md">
<DialogHeader>
<div className="mx-auto w-12 h-12 bg-green-100 rounded-full flex items-center justify-center mb-4">
<CheckCircle className="w-8 h-8 text-green-600" />
</div>
<DialogTitle className="text-center text-xl font-bold">Finalize Onboarding</DialogTitle>
<DialogDescription className="text-center pt-2">
You are about to officially onboard <span className="font-semibold text-slate-900">{application.name}</span> as a Royal Enfield dealer.
</DialogDescription>
</DialogHeader>
<div className="bg-slate-50 p-4 rounded-lg border border-slate-200 mt-4 space-y-3">
<div className="flex items-start gap-3"><div className="mt-1 bg-green-500 rounded-full p-0.5"><Check className="w-3 h-3 text-white" /></div><p className="text-sm text-slate-600">Official dealer profile will be created.</p></div>
<div className="flex items-start gap-3"><div className="mt-1 bg-green-500 rounded-full p-0.5"><Check className="w-3 h-3 text-white" /></div><p className="text-sm text-slate-600">User account will be activated with role <span className="font-medium text-slate-900">Dealer</span>.</p></div>
<div className="flex items-start gap-3"><div className="mt-1 bg-green-500 rounded-full p-0.5"><Check className="w-3 h-3 text-white" /></div><p className="text-sm text-slate-600">Primary outlet will be registered in the system.</p></div>
</div>
<div className="mt-6 flex flex-col gap-3">
<Button
className="w-full bg-green-600 hover:bg-green-700 h-11 text-lg font-semibold shadow-lg shadow-green-100"
onClick={async () => {
setIsOnboarding(true);
try {
await onboardingService.createDealer({ applicationId: application.id });
toast.success('Dealer profile and login account created successfully!');
setShowOnboardModal(false);
fetchApplication();
} catch {
toast.error('Failed to create dealer profile');
} finally {
setIsOnboarding(false);
}
}}
disabled={isOnboarding}
>
{isOnboarding ? <><Loader2 className="w-5 h-5 mr-2 animate-spin" />Processing Onboarding...</> : 'Confirm & Onboard Dealer'}
</Button>
<Button variant="ghost" className="w-full text-slate-500 hover:text-slate-700" onClick={() => setShowOnboardModal(false)} disabled={isOnboarding}>Cancel</Button>
</div>
</DialogContent>
</Dialog>
<Dialog open={showRejectModal} onOpenChange={setShowRejectModal}>
<DialogContent>
<DialogHeader>
<DialogTitle>Reject Application</DialogTitle>
<DialogDescription>Please provide a clear reason for rejecting this application.</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div>
<Label>Reason for Rejection (Required)</Label>
<Textarea placeholder="Enter rejection reason..." value={rejectionReason} onChange={(e) => setRejectionReason(e.target.value)} className="mt-2" rows={4} />
</div>
<div className="flex gap-3">
<Button variant="outline" className="flex-1" onClick={() => setShowRejectModal(false)} disabled={isRejecting}>Cancel</Button>
<Button variant="destructive" className="flex-1" onClick={handleReject} disabled={isRejecting}>
{isRejecting ? <><Loader2 className="w-4 h-4 mr-2 animate-spin" />Rejecting...</> : 'Confirm Rejection'}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showScheduleModal} onOpenChange={setShowScheduleModal}>
<DialogContent>
<DialogHeader>
<DialogTitle>Schedule Interview</DialogTitle>
<DialogDescription>Set up an interview session with the applicant and relevant team members.</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div>
<Label>Interview Type</Label>
<Select value={interviewType} onValueChange={setInterviewType}>
<SelectTrigger className="mt-2"><SelectValue placeholder="Select interview type" /></SelectTrigger>
<SelectContent>
<SelectItem value="level1" disabled={isInterviewCompleted(1) || isInterviewActive(1)}><div className="flex items-center justify-between w-full"><span>Level 1</span>{isInterviewCompleted(1) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(1) && <Clock className="w-4 h-4 text-amber-500 ml-2 inline" />}</div></SelectItem>
<SelectItem value="level2" disabled={!isInterviewCompleted(1) || isInterviewCompleted(2) || isInterviewActive(2)}><div className="flex items-center justify-between w-full"><span>Level 2</span>{!isInterviewCompleted(1) && <span className="text-[10px] text-slate-400 ml-2">(Prerequisite: L1)</span>}{isInterviewCompleted(2) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(2) && <Clock className="w-4 h-4 text-amber-500 ml-2 inline" />}</div></SelectItem>
<SelectItem value="level3" disabled={!isInterviewCompleted(2) || isInterviewCompleted(3) || isInterviewActive(3)}><div className="flex items-center justify-between w-full"><span>Level 3</span>{!isInterviewCompleted(2) && <span className="text-[10px] text-slate-400 ml-2">(Prerequisite: L2)</span>}{isInterviewCompleted(3) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(3) && <Clock className="w-4 h-4 text-amber-500 ml-2 inline" />}</div></SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label>Interview Mode</Label>
<Select value={interviewMode} onValueChange={setInterviewMode}>
<SelectTrigger className="mt-2"><SelectValue placeholder="Select interview mode" /></SelectTrigger>
<SelectContent><SelectItem value="virtual">Virtual</SelectItem><SelectItem value="physical">Physical</SelectItem></SelectContent>
</Select>
</div>
<div><Label>Date & Time</Label><Input type="datetime-local" className="mt-2" value={interviewDate} onChange={(e) => setInterviewDate(e.target.value)} /></div>
{interviewMode === 'virtual' && <div><Label>Meeting Link</Label><Input placeholder="https://meet.google.com/..." className="mt-2" value={meetingLink} onChange={(e) => setMeetingLink(e.target.value)} /></div>}
{interviewMode === 'physical' && <div><Label>Location</Label><Input placeholder="Enter interview location address" className="mt-2" value={location} onChange={(e) => setLocation(e.target.value)} /></div>}
<div>
<Label>Interviewers</Label>
<div className="flex gap-2 mt-2">
<Select value={selectedInterviewerId} onValueChange={setSelectedInterviewerId}>
<SelectTrigger className="flex-1"><SelectValue placeholder="Select interviewer" /></SelectTrigger>
<SelectContent>{users.map((user) => <SelectItem key={user.id} value={user.id}>{user.fullName || user.name} ({user.role?.roleName || user.roleCode})</SelectItem>)}</SelectContent>
</Select>
<Button onClick={handleAddInterviewer} type="button" variant="secondary">Add</Button>
</div>
{scheduledInterviewParticipants.length > 0 && (
<div className="mt-3 space-y-2">
<Label className="text-xs text-muted-foreground">Selected Interviewers:</Label>
<div className="flex flex-wrap gap-2">
{scheduledInterviewParticipants.map((p) => (
<div key={p.id} className="flex items-center gap-1 bg-secondary px-2 py-1 rounded text-sm">
<span>{p.fullName || p.name || 'Unknown'}</span>
<button onClick={() => handleRemoveInterviewer(p.id)} className="text-muted-foreground hover:text-destructive">×</button>
</div>
))}
</div>
</div>
)}
</div>
<div className="flex gap-3">
<Button variant="outline" className="flex-1" onClick={() => setShowScheduleModal(false)} disabled={isScheduling}>Cancel</Button>
<Button className="flex-1 bg-amber-600 hover:bg-amber-700" onClick={handleScheduleInterview} disabled={isScheduling}>{isScheduling ? 'Scheduling...' : 'Schedule'}</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showAssignArchitectureModal} onOpenChange={setShowAssignArchitectureModal}>
<DialogContent>
<DialogHeader>
<DialogTitle>Assign Architecture Team</DialogTitle>
<DialogDescription>Select an architecture team lead for site planning and blueprints.</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div>
<Label>Select Architecture Lead</Label>
<Select value={architectureLeadId} onValueChange={setArchitectureLeadId}>
<SelectTrigger className="mt-2"><SelectValue placeholder="Search users..." /></SelectTrigger>
<SelectContent>
{users.filter(u => u.roleCode === 'ARCHITECTURE' || u.role?.roleCode === 'ARCHITECTURE' || u.role === 'Architecture' || u.role === 'Architecture Team').map((u) => (
<SelectItem key={u.id} value={u.id}>{u.fullName} ({u.email})</SelectItem>
))}
{users.filter(u => u.roleCode === 'ARCHITECTURE' || u.role?.roleCode === 'ARCHITECTURE' || u.role === 'Architecture' || u.role === 'Architecture Team').length === 0 && users.map((u) => (
<SelectItem key={u.id} value={u.id}>{u.fullName} ({u.roleCode || u.role})</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex gap-3">
<Button variant="outline" className="flex-1" onClick={() => setShowAssignArchitectureModal(false)} disabled={isAssigningArchitecture}>Cancel</Button>
<Button className="flex-1 bg-blue-600 hover:bg-blue-700" onClick={handleAssignArchitecture} disabled={isAssigningArchitecture}>{isAssigningArchitecture ? 'Assigning...' : 'Assign Team'}</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showArchitectureStatusModal} onOpenChange={setShowArchitectureStatusModal}>
<DialogContent>
<DialogHeader>
<DialogTitle>Update Architecture Status</DialogTitle>
<DialogDescription>Mark the architectural work as completed and optionally add remarks.</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div>
<Label>Status</Label>
<Select value={architectureStatus} onValueChange={setArchitectureStatus}>
<SelectTrigger className="mt-2"><SelectValue placeholder="Select status" /></SelectTrigger>
<SelectContent><SelectItem value="COMPLETED">Completed</SelectItem><SelectItem value="REJECTED">Rejected / Needs Revision</SelectItem></SelectContent>
</Select>
</div>
<div>
<Label>Remarks (Optional)</Label>
<Textarea placeholder="Enter any planning or site-visit remarks..." value={architectureRemarks} onChange={(e) => setArchitectureRemarks(e.target.value)} className="mt-2" rows={4} />
</div>
<div className="flex gap-3">
<Button variant="outline" className="flex-1" onClick={() => setShowArchitectureStatusModal(false)} disabled={isUpdatingArchitecture}>Cancel</Button>
<Button className="flex-1 bg-blue-600 hover:bg-blue-700" onClick={handleUpdateArchitectureStatus} disabled={isUpdatingArchitecture}>{isUpdatingArchitecture ? 'Updating...' : 'Update Status'}</Button>
</div>
</div>
</DialogContent>
</Dialog>
</>
);
}