diff --git a/src/dealer-claim/components/request-creation/ClaimManagementWizard.tsx b/src/dealer-claim/components/request-creation/ClaimManagementWizard.tsx index ca00cd7..a8bea62 100644 --- a/src/dealer-claim/components/request-creation/ClaimManagementWizard.tsx +++ b/src/dealer-claim/components/request-creation/ClaimManagementWizard.tsx @@ -29,7 +29,7 @@ import { } from 'lucide-react'; import { format } from 'date-fns'; import { toast } from 'sonner'; -import { getAllDealers as fetchDealersFromAPI, verifyDealerLogin, type DealerInfo } from '@/services/dealerApi'; +import { verifyDealerLogin, searchExternalDealerByCode, type DealerInfo } from '@/services/dealerApi'; import { ClaimApproverSelectionStep } from './ClaimApproverSelectionStep'; import { useAuth } from '@/contexts/AuthContext'; import { getPublicConfigurations, AdminConfiguration, getActivityTypes, type ActivityType } from '@/services/adminApi'; @@ -68,7 +68,7 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar const dealerSearchTimer = useRef(null); const [isSubmitting, setIsSubmitting] = useState(false); const submitTimeoutRef = useRef(null); - + // System policy state const [systemPolicy, setSystemPolicy] = useState({ maxApprovalLevels: 10, @@ -76,7 +76,7 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar allowSpectators: true, maxSpectators: 20 }); - + const [policyViolationModal, setPolicyViolationModal] = useState<{ open: boolean; violations: Array<{ type: string; message: string; currentValue?: number; maxValue?: number }>; @@ -140,7 +140,7 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar } }; }, []); - + const [formData, setFormData] = useState({ activityName: '', activityType: '', @@ -175,7 +175,7 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar // Handle dealer search input with debouncing const handleDealerSearchInputChange = (value: string) => { setDealerSearchInput(value); - + // Clear previous timer if (dealerSearchTimer.current) { clearTimeout(dealerSearchTimer.current); @@ -194,10 +194,26 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar // Debounce search dealerSearchTimer.current = setTimeout(async () => { try { - const results = await fetchDealersFromAPI(value, 10); // Limit to 10 results - setDealerSearchResults(results); + const result = await searchExternalDealerByCode(value); + if (result) { + // Map external API response to DealerInfo structure + const mappedDealer: DealerInfo = { + dealerId: result.dealer || result.dealer_code || value, + dealerCode: result.dealer || result.dealer_code || value, + dealerName: result['dealer name'] || result.dealer_name || 'Unknown Dealer', + displayName: result['dealer name'] || result.dealer_name || 'Unknown Dealer', + email: result['dealer email'] || '', + phone: result['dealer phone'] || '', + city: result['re city'] || result.city || '', + state: result['re state code'] || result.state || '', + isLoggedIn: true, // We'll verify this in the next step + }; + setDealerSearchResults([mappedDealer]); + } else { + setDealerSearchResults([]); + } } catch (error) { - console.error('Error searching dealers:', error); + console.error('Error searching external dealer:', error); setDealerSearchResults([]); } finally { setDealerSearchLoading(false); @@ -208,7 +224,7 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar const updateFormData = (field: string, value: any) => { setFormData(prev => { const updated = { ...prev, [field]: value }; - + // Validate period dates if (field === 'periodStartDate') { // If start date is selected and end date exists, validate end date @@ -225,7 +241,7 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar return prev; } } - + return updated; }); }; @@ -233,18 +249,18 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar const isStepValid = () => { switch (currentStep) { case 1: - return formData.activityName && - formData.activityType && - formData.dealerCode && - formData.dealerName && - formData.activityDate && - formData.location && - formData.requestDescription; + return formData.activityName && + formData.activityType && + formData.dealerCode && + formData.dealerName && + formData.activityDate && + formData.location && + formData.requestDescription; case 2: // Validate that all required approvers are assigned (Step 3 only, Step 8 is now system/Finance) const approvers = formData.approvers || []; // Find step 3 approver by originalStepLevel first, then fallback to level - const step3Approver = approvers.find((a: any) => + const step3Approver = approvers.find((a: any) => a.originalStepLevel === 3 || (!a.originalStepLevel && a.level === 3 && !a.isAdditional) ); // Step 8 is now a system step, no validation needed @@ -263,15 +279,15 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar if (currentStep === 2) { const approvers = formData.approvers || []; // Find step 3 approver by originalStepLevel first, then fallback to level - const step3Approver = approvers.find((a: any) => + const step3Approver = approvers.find((a: any) => a.originalStepLevel === 3 || (!a.originalStepLevel && a.level === 3 && !a.isAdditional) ); const missingSteps: string[] = []; - + if (!step3Approver?.email || !step3Approver?.userId || !step3Approver?.tat) { missingSteps.push('Department Lead Approval'); } - + if (missingSteps.length > 0) { toast.error(`Please add missing approvers: ${missingSteps.join(', ')}`); } else { @@ -297,7 +313,7 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar setVerifyingDealer(true); try { const verifiedDealer = await verifyDealerLogin(selectedDealer.dealerCode); - + if (!verifiedDealer.isLoggedIn) { toast.error( `Dealer "${verifiedDealer.dealerName || verifiedDealer.displayName}" (${verifiedDealer.dealerCode}) is not mapped to the system.`, @@ -321,14 +337,14 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar updateFormData('dealerEmail', verifiedDealer.email || ''); updateFormData('dealerPhone', verifiedDealer.phone || ''); updateFormData('dealerAddress', ''); // Address not available in API response - + // Clear search input and results setDealerSearchInput(verifiedDealer.dealerName || verifiedDealer.displayName); setDealerSearchResults([]); - + toast.success(`Dealer "${verifiedDealer.dealerName || verifiedDealer.displayName}" verified and mapped to the System`); } catch (error: any) { - const errorMessage = 'Dealer is not mapped to the system' + const errorMessage = 'Dealer is not mapped to the system' toast.error(errorMessage, { duration: 5000 }); // Clear the selection setDealerSearchInput(''); @@ -353,11 +369,11 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar // Just sort them and prepare for submission const approvers = formData.approvers || []; const sortedApprovers = [...approvers].sort((a, b) => a.level - b.level); - + // Check for duplicate levels (should not happen, but safeguard) const levelMap = new Map(); const duplicates: number[] = []; - + sortedApprovers.forEach((approver) => { if (levelMap.has(approver.level)) { duplicates.push(approver.level); @@ -365,13 +381,13 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar levelMap.set(approver.level, approver); } }); - + if (duplicates.length > 0) { toast.error(`Duplicate approver levels detected: ${duplicates.join(', ')}. Please refresh and try again.`); console.error('Duplicate levels found:', duplicates, sortedApprovers); return; } - + // Prepare final approvers array - preserve stepName for additional approvers // The backend will use stepName to set the levelName for approval levels // Also preserve originalStepLevel so backend can identify which step each approver belongs to @@ -384,18 +400,18 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar tat: approver.tat, tatType: approver.tatType, }; - + // Preserve stepName for additional approvers if (approver.isAdditional && approver.stepName) { result.stepName = approver.stepName; result.isAdditional = true; } - + // Preserve originalStepLevel for fixed steps (so backend can identify which step this is) if (approver.originalStepLevel) { result.originalStepLevel = approver.originalStepLevel; } - + return result; }); @@ -486,8 +502,8 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar
- handleExpenseChange(item.id, 'hsnCode', e.target.value) diff --git a/src/dealer-claim/components/request-detail/modals/DealerProposalSubmissionModal.tsx b/src/dealer-claim/components/request-detail/modals/DealerProposalSubmissionModal.tsx index e3a0095..bf26ed0 100644 --- a/src/dealer-claim/components/request-detail/modals/DealerProposalSubmissionModal.tsx +++ b/src/dealer-claim/components/request-detail/modals/DealerProposalSubmissionModal.tsx @@ -902,7 +902,7 @@ export function DealerProposalSubmissionModal({
- + handleCostItemChange(item.id, 'hsnCode', e.target.value)} diff --git a/src/services/dealerApi.ts b/src/services/dealerApi.ts index c75728b..fdf0a6c 100644 --- a/src/services/dealerApi.ts +++ b/src/services/dealerApi.ts @@ -105,3 +105,16 @@ export async function verifyDealerLogin(dealerCode: string): Promise } } +/** + * Search dealer by code from external Royal Enfield API + * @param dealerCode - The code to search for + */ +export async function searchExternalDealerByCode(dealerCode: string): Promise { + try { + const res = await apiClient.get(`/dealers-external/search/${dealerCode}`); + return res.data?.data || res.data || null; + } catch (error) { + console.error('[DealerAPI] Error searching external dealer:', error); + return null; + } +}