import { motion } from 'framer-motion'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Badge } from '@/components/ui/badge'; import { Users, Settings, Shield, User, CheckCircle, Minus, Plus } from 'lucide-react'; import { FormData } from '@/hooks/useCreateRequestForm'; import { useMultiUserSearch } from '@/hooks/useUserSearch'; import { ensureUserExists } from '@/services/userApi'; interface ApprovalWorkflowStepProps { formData: FormData; updateFormData: (field: keyof FormData, value: any) => void; onValidationError: (error: { type: string; email: string; message: string }) => void; } /** * Component: ApprovalWorkflowStep * * Purpose: Step 3 - Approval workflow configuration * * Features: * - Configure number of approvers * - Define approval hierarchy with TAT * - User search with @ prefix * - Test IDs for testing * * Note: This is a simplified version. Full implementation includes complex approver management. */ export function ApprovalWorkflowStep({ formData, updateFormData, onValidationError }: ApprovalWorkflowStepProps) { const { userSearchResults, userSearchLoading, searchUsersForIndex, clearSearchForIndex } = useMultiUserSearch(); const handleApproverEmailChange = (index: number, value: string) => { const newApprovers = [...formData.approvers]; const previousEmail = newApprovers[index]?.email; const emailChanged = previousEmail !== value; newApprovers[index] = { ...newApprovers[index], email: value, level: index + 1, userId: emailChanged ? undefined : newApprovers[index]?.userId, name: emailChanged ? undefined : newApprovers[index]?.name, department: emailChanged ? undefined : newApprovers[index]?.department, avatar: emailChanged ? undefined : newApprovers[index]?.avatar }; updateFormData('approvers', newApprovers); if (!value || !value.startsWith('@') || value.length < 2) { clearSearchForIndex(index); return; } searchUsersForIndex(index, value, 10); }; const handleUserSelect = async (index: number, selectedUser: any) => { try { const dbUser = await ensureUserExists({ userId: selectedUser.userId, email: selectedUser.email, displayName: selectedUser.displayName, firstName: selectedUser.firstName, lastName: selectedUser.lastName, department: selectedUser.department, phone: selectedUser.phone, mobilePhone: selectedUser.mobilePhone, designation: selectedUser.designation, jobTitle: selectedUser.jobTitle, manager: selectedUser.manager, employeeId: selectedUser.employeeId, employeeNumber: selectedUser.employeeNumber, secondEmail: selectedUser.secondEmail, location: selectedUser.location }); const updated = [...formData.approvers]; updated[index] = { ...updated[index], email: selectedUser.email, name: selectedUser.displayName || [selectedUser.firstName, selectedUser.lastName].filter(Boolean).join(' '), userId: dbUser.userId, level: index + 1, }; updateFormData('approvers', updated); clearSearchForIndex(index); } catch (err) { console.error('Failed to ensure user exists:', err); onValidationError({ type: 'error', email: selectedUser.email, message: 'Failed to validate user. Please try again.' }); } }; return (

Approval Workflow

Define the approval hierarchy and assign approvers by email ID.

{/* Number of Approvers */} Approval Configuration Configure how many approvers you need and define the approval sequence.
{formData.approverCount || 1}

Maximum 10 approvers allowed. Each approver will review sequentially.

{/* Approval Hierarchy */} Approval Hierarchy * Define the approval sequence. Each approver will review the request in order from Level 1 to Level {formData.approverCount || 1}. {/* Initiator Card */}
Request Initiator YOU

Creates and submits the request

{/* Dynamic Approver Cards */} {Array.from({ length: formData.approverCount || 1 }, (_, index) => { const level = index + 1; const isLast = level === (formData.approverCount || 1); if (!formData.approvers[index]) { const newApprovers = [...formData.approvers]; newApprovers[index] = { email: '', name: '', level: level, tat: '' as any }; updateFormData('approvers', newApprovers); } return (
{level}
Approver Level {level} {isLast && ( FINAL APPROVER )}
{formData.approvers[index]?.email && formData.approvers[index]?.userId && ( Verified )}
handleApproverEmailChange(index, e.target.value)} className="h-10 border-2 border-gray-300 focus:border-blue-500 mt-1 w-full" data-testid={`approval-workflow-approver-${level}-email-input`} /> {/* Search suggestions dropdown */} {(userSearchLoading[index] || (userSearchResults[index]?.length || 0) > 0) && (
{userSearchLoading[index] ? (
Searching...
) : (
    {userSearchResults[index]?.map((u) => (
  • handleUserSelect(index, u)} data-testid={`approval-workflow-approver-${level}-search-result-${u.userId}`} >
    {u.displayName || u.email}
    {u.email}
  • ))}
)}
)}
{ const newApprovers = [...formData.approvers]; newApprovers[index] = { ...newApprovers[index], tat: parseInt(e.target.value) || '', level: level, tatType: formData.approvers[index]?.tatType || 'hours' }; updateFormData('approvers', newApprovers); }} className="h-10 border-2 border-gray-300 focus:border-blue-500 flex-1" data-testid={`approval-workflow-approver-${level}-tat-input`} />
); })}
); }