'use client'; import { useEffect, useState } from 'react'; import { useLocale, useTranslations } from 'next-intl'; import { Anchor, ClipboardList, Clock, ExternalLink, FileText, Globe, History, Maximize2, Minimize2, Package, Search, Ship, X, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Callout } from '@/components/ui/callout'; import { FormField } from '@/components/ui/form-field'; import { EmptyState } from '@/components/ui/states'; import { PageHeader } from '@/components/ui/PageHeader'; import { cn } from '@/lib/utils'; interface SearchHistoryItem { id: string; trackingNumber: string; carrierId: string; carrierName: string; timestamp: Date; } type CarrierDescKey = 'containerOrBl' | 'containerBlOrBooking' | 'containerOnly'; /** * Les couleurs `color` ci-dessous sont les identites visuelles des compagnies * maritimes. Ce sont des marques tierces : elles ne relevent pas de la charte * Xpeditis et sont reproduites telles quelles, a l'identique de l'original. */ const carriers = [ { id: 'maersk', name: 'Maersk', color: '#00243D', textColor: 'text-white', trackingUrl: 'https://www.maersk.com/tracking/', placeholder: 'Ex: MSKU1234567', descKey: 'containerOrBl' as CarrierDescKey, }, { id: 'msc', name: 'MSC', color: '#002B5C', textColor: 'text-white', trackingUrl: 'https://www.msc.com/track-a-shipment?query=', placeholder: 'Ex: MSCU1234567', descKey: 'containerBlOrBooking' as CarrierDescKey, }, { id: 'cma-cgm', name: 'CMA CGM', color: '#E30613', textColor: 'text-white', trackingUrl: 'https://www.cma-cgm.com/ebusiness/tracking/search?SearchBy=Container&Reference=', placeholder: 'Ex: CMAU1234567', descKey: 'containerOrBl' as CarrierDescKey, }, { id: 'hapag-lloyd', name: 'Hapag-Lloyd', color: '#FF6600', textColor: 'text-white', trackingUrl: 'https://www.hapag-lloyd.com/en/online-business/track/track-by-container-solution.html?container=', placeholder: 'Ex: HLCU1234567', descKey: 'containerOnly' as CarrierDescKey, }, { id: 'cosco', name: 'COSCO', color: '#003A70', textColor: 'text-white', trackingUrl: 'https://elines.coscoshipping.com/ebusiness/cargoTracking?trackingNumber=', placeholder: 'Ex: COSU1234567', descKey: 'containerOrBl' as CarrierDescKey, }, { id: 'one', name: 'ONE', color: '#FF00FF', textColor: 'text-white', trackingUrl: 'https://ecomm.one-line.com/one-ecom/manage-shipment/cargo-tracking?trkNoParam=', placeholder: 'Ex: ONEU1234567', descKey: 'containerOrBl' as CarrierDescKey, }, { id: 'evergreen', name: 'Evergreen', color: '#006633', textColor: 'text-white', trackingUrl: 'https://www.shipmentlink.com/servlet/TDB1_CargoTracking.do?BL=', placeholder: 'Ex: EGHU1234567', descKey: 'containerOrBl' as CarrierDescKey, }, { id: 'yangming', name: 'Yang Ming', color: '#FFD700', textColor: 'text-brand-navy', trackingUrl: 'https://www.yangming.com/e-service/Track_Trace/track_trace_cargo_tracking.aspx?rdolType=CT&str=', placeholder: 'Ex: YMLU1234567', descKey: 'containerOnly' as CarrierDescKey, }, { id: 'zim', name: 'ZIM', color: '#1E3A8A', textColor: 'text-white', trackingUrl: 'https://www.zim.com/tools/track-a-shipment?consnumber=', placeholder: 'Ex: ZIMU1234567', descKey: 'containerOrBl' as CarrierDescKey, }, { id: 'hmm', name: 'HMM', color: '#E65100', textColor: 'text-white', trackingUrl: 'https://www.hmm21.com/cms/business/ebiz/trackTrace/trackTrace/index.jsp?type=1&number=', placeholder: 'Ex: HDMU1234567', descKey: 'containerOrBl' as CarrierDescKey, }, ]; const HISTORY_KEY = 'xpeditis_track_history'; export default function TrackTracePage() { const t = useTranslations('dashboard.trackTrace'); const tCommon = useTranslations('common'); const locale = useLocale(); const [trackingNumber, setTrackingNumber] = useState(''); const [selectedCarrier, setSelectedCarrier] = useState(''); const [error, setError] = useState(''); const [searchHistory, setSearchHistory] = useState([]); const [showMap, setShowMap] = useState(false); const [isMapFullscreen, setIsMapFullscreen] = useState(false); const [isMapLoading, setIsMapLoading] = useState(true); useEffect(() => { const saved = localStorage.getItem(HISTORY_KEY); if (!saved) return; try { const parsed = JSON.parse(saved); setSearchHistory(parsed.map((item: any) => ({ ...item, timestamp: new Date(item.timestamp) }))); } catch (e) { console.error('Failed to parse search history:', e); } }, []); // Le plein ecran de la carte doit pouvoir se quitter au clavier. useEffect(() => { if (!isMapFullscreen) return; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setIsMapFullscreen(false); }; document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); }, [isMapFullscreen]); const saveHistory = (history: SearchHistoryItem[]) => { localStorage.setItem(HISTORY_KEY, JSON.stringify(history)); setSearchHistory(history); }; const handleTrack = () => { if (!trackingNumber.trim()) return setError(t('errors.noTrackingNumber')); if (!selectedCarrier) return setError(t('errors.noCarrier')); setError(''); const carrier = carriers.find(c => c.id === selectedCarrier); if (!carrier) return; const entry: SearchHistoryItem = { id: Date.now().toString(), trackingNumber: trackingNumber.trim(), carrierId: carrier.id, carrierName: carrier.name, timestamp: new Date(), }; saveHistory( [ entry, ...searchHistory.filter( h => !(h.trackingNumber === entry.trackingNumber && h.carrierId === entry.carrierId) ), ].slice(0, 10) ); window.open( carrier.trackingUrl + encodeURIComponent(trackingNumber.trim()), '_blank', 'noopener,noreferrer' ); }; const selectedCarrierData = carriers.find(c => c.id === selectedCarrier); const formatTimeAgo = (date: Date) => { const diffMs = Date.now() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) return t('timeAgo.justNow'); if (diffMins < 60) return t('timeAgo.minutesAgo', { count: diffMins }); if (diffHours < 24) return t('timeAgo.hoursAgo', { count: diffHours }); if (diffDays < 7) return t('timeAgo.daysAgo', { count: diffDays }); return date.toLocaleDateString(locale); }; const helpCards = [ { icon: Package, key: 'containerNumber' }, { icon: FileText, key: 'billOfLading' }, { icon: ClipboardList, key: 'bookingRef' }, ] as const; return ( <>
{t('searchCard.title')}
{t('searchCard.selectCarrier')}
{carriers.map(carrier => { const active = selectedCarrier === carrier.id; return ( ); })}
{({ id, describedBy, invalid }) => (
{ setTrackingNumber(e.target.value.toUpperCase()); setError(''); }} onKeyDown={e => e.key === 'Enter' && handleTrack()} placeholder={selectedCarrierData?.placeholder || 'Ex: MSKU1234567'} className="font-mono" />
)}
{showMap && (
{isMapLoading && (
)}