xpeditis2.0/apps/frontend/app/[locale]/carrier/reject/[token]/page.tsx
2026-05-12 21:01:52 +02:00

145 lines
4.8 KiB
TypeScript

'use client';
import { useEffect, useState, useRef } from 'react';
import { useParams } from 'next/navigation';
import { useRouter } from '@/i18n/navigation';
import { useTranslations } from 'next-intl';
import { XCircle, Loader2, CheckCircle } from 'lucide-react';
export default function CarrierRejectPage() {
const params = useParams();
const router = useRouter();
const token = params.token as string;
const t = useTranslations('carrierPortal');
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [countdown, setCountdown] = useState(5);
// Prevent double API calls (React 18 StrictMode issue)
const hasCalledApi = useRef(false);
useEffect(() => {
const rejectBooking = async () => {
if (hasCalledApi.current) {
return;
}
hasCalledApi.current = true;
if (!token) {
setError(t('common.tokenMissing'));
setLoading(false);
return;
}
try {
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';
const response = await fetch(`${apiUrl}/api/v1/csv-booking-actions/reject/${token}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
let errorData;
try {
errorData = await response.json();
} catch (e) {
errorData = { message: `HTTP ${response.status}` };
}
let errorMessage = errorData.message || t('reject.errorFallback');
if (errorMessage.includes('status REJECTED')) {
errorMessage = t('common.bookingAlreadyRejected');
} else if (errorMessage.includes('status ACCEPTED')) {
errorMessage = t('common.bookingAlreadyAccepted');
} else if (
errorMessage.includes('not found') ||
errorMessage.includes('Booking not found')
) {
errorMessage = t('common.bookingNotFound');
}
throw new Error(errorMessage);
}
setLoading(false);
const timer = setInterval(() => {
setCountdown(prev => {
if (prev <= 1) {
clearInterval(timer);
router.push('/');
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(timer);
} catch (err) {
console.error('Error rejecting booking:', err);
setError(err instanceof Error ? err.message : t('reject.errorGeneric'));
setLoading(false);
}
};
rejectBooking();
}, [token, router, t]);
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-red-50 to-orange-50">
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md w-full text-center">
<Loader2 className="w-16 h-16 text-orange-600 mx-auto mb-4 animate-spin" />
<h1 className="text-2xl font-bold text-gray-900 mb-4">{t('reject.loadingTitle')}</h1>
<p className="text-gray-600">{t('reject.loadingMessage')}</p>
</div>
</div>
);
}
if (error) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-red-50 to-orange-50">
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md w-full text-center">
<XCircle className="w-16 h-16 text-red-500 mx-auto mb-4" />
<h1 className="text-2xl font-bold text-gray-900 mb-4">{t('common.errorTitle')}</h1>
<p className="text-gray-600 mb-6">{error}</p>
<button
onClick={() => router.push('/')}
className="w-full px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
{t('common.backHome')}
</button>
</div>
</div>
);
}
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-orange-50 to-red-50">
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md w-full text-center">
<CheckCircle className="w-20 h-20 text-orange-500 mx-auto mb-6" />
<h1 className="text-3xl font-bold text-gray-900 mb-4">{t('reject.thanksTitle')}</h1>
<div className="bg-orange-50 border-2 border-orange-200 rounded-lg p-6 mb-6">
<p className="text-orange-800 font-medium text-lg mb-2">{t('reject.successHeadline')}</p>
<p className="text-orange-700 text-sm">{t('reject.successBody')}</p>
</div>
<p className="text-gray-500 text-sm mb-4">{t('common.redirecting', { countdown })}</p>
<button
onClick={() => router.push('/')}
className="w-full px-6 py-3 bg-orange-600 text-white rounded-lg hover:bg-orange-700 font-semibold transition-colors"
>
{t('common.backHome')}
</button>
</div>
</div>
);
}