'use client'; import { useRouter } from 'next/navigation'; import { Calendar, ExternalLink, AlertCircle, CheckCircle, Hourglass, Activity, History } from 'lucide-react'; import TimeAgo from '@/components/common/TimeAgo'; import { useLanguage } from '@/lib/i18n/LanguageContext'; export interface SlotSnapshot { id: number; country: string; city: string; visa_type: string; availability_status: 'None' | 'Available' | 'Waitlist'; earliest_date: string | null; snapshot_at: string; website?: string; last_check_at?: string | null; routing_key?: string; slot_snapshot?: { routing_key?: string; [key: string]: any; }; } export default function CitySlotCard({ data, isBlacklisted = false }: { data: SlotSnapshot; isBlacklisted?: boolean }) { const { t, lang } = useLanguage(); const router = useRouter(); // === 1. 时间计算与解析 === const now = new Date().getTime(); const THRESHOLD = 6 * 60 * 1000; // 5分钟阈值 const parseTime = (timeStr: string | null | undefined): number => { if (!timeStr) return 0; let t = timeStr; if (!t.endsWith('Z') && !t.includes('+')) t += 'Z'; return new Date(t).getTime(); }; const lastCheckTime = parseTime(data.last_check_at); const snapshotTime = parseTime(data.snapshot_at); // === 2. 状态判定逻辑 === const isMonitorStale = (now - lastCheckTime) > THRESHOLD; const isSnapshotStale = (now - snapshotTime) > THRESHOLD; const isDataStale = isMonitorStale || isSnapshotStale; // 统称数据过期 const rawStatus = data.availability_status; // 定义“有号”:包括具体的 Available 和 Waitlist const hasSlot = rawStatus === 'Available' || rawStatus === 'Waitlist'; // 1. 真正的有号 (Live Slots) -> 数据新鲜且有号 const isLiveSlot = hasSlot && !isDataStale; // 2. 历史记录 (History) -> 曾经有号,但数据旧了 const isHistorySlot = hasSlot && isDataStale; // 格式化日期 const formatDate = (dateStr: string | null) => { if (!dateStr) return 'N/A'; const d = new Date(dateStr); return d.toLocaleDateString(lang === 'zh' ? 'zh-CN' : 'en-US', { month: 'short', day: 'numeric', weekday: 'long' }); }; // === 3. 动态样式配置 === const getStyleConfig = () => { // 情况 A: 历史快照 (有号但过期) -> 橙色 if (isHistorySlot) { return { bg: 'bg-orange-50/40 border-orange-200', badgeBg: 'bg-orange-100 text-orange-700', icon: , label: lang === 'zh' ? '历史快照' : 'History/Stale', // 依然提示数据旧 contentColor: 'text-slate-700 opacity-70', isStale: true }; } // 情况 B: 真正的有号 (Available OR Waitlist) -> 绿色 // *修改点:Waitlist 现在完全共享 Available 的绿色样式* if (isLiveSlot) { return { bg: 'bg-green-50/60 border-green-200', badgeBg: 'bg-green-100 text-green-700', icon: , label: t('slots.status_available') || 'Available', // 统称 Available contentColor: 'text-green-700', isStale: false }; } // 情况 C: 监控过期 (Monitor Offline) 且原本就没号 if (isMonitorStale) { return { bg: 'bg-slate-50 border-slate-200', badgeBg: 'bg-slate-200 text-slate-500', icon: , label: lang === 'zh' ? '监控离线' : 'Monitor Offline', contentColor: 'text-slate-400', isStale: true }; } // 情况 D: 正常无号 (None) -> 灰色 return { bg: 'bg-white border-slate-200 opacity-90', badgeBg: 'bg-slate-100 text-slate-500', icon: , label: t('slots.status_unavailable') || 'None', contentColor: 'text-slate-400', isStale: false }; }; const style = getStyleConfig(); const slotRoutingKey = data.slot_snapshot?.routing_key || data.routing_key; const slotCity = data.city; const slotCountry = data.country; const slotVisaType = data.visa_type; const canSubscribe = Boolean(slotRoutingKey) && !isBlacklisted; const handleSubscribe = () => { if (!slotRoutingKey) return; const params = new URLSearchParams(); params.set('slot_routing_key', slotRoutingKey); if (slotCity) params.set('slot_city', slotCity); if (slotCountry) params.set('slot_country', slotCountry); if (slotVisaType) params.set('slot_visa_type', slotVisaType); router.push(`/create-order/16?${params.toString()}`); }; // 决定中间显示什么内容 const renderCenterContent = () => { // 如果是 Waitlist (无论是否过期) if (rawStatus === 'Waitlist') { return ( <> Waitlist {style.isStale && ( Exp? )} ); } // 如果是 Available (有日期) if (rawStatus === 'Available') { return ( <> {formatDate(data.earliest_date)} {style.isStale && ( Exp? )} ); } // 既不是 Available 也不是 Waitlist (即 None) if (style.isStale) { return {lang === 'zh' ? '数据已过期' : 'Data Stale'}; } return 暂无名额; }; return (
{/* 右上角状态标签 */}
{style.icon} {style.label}
{/* 标题 */}

{data.country.substring(0, 2).toUpperCase()} {data.country}

{data.visa_type}
{/* 核心指标区域 */}

{/* 这里文案也统一,如果是 Waitlist 也可以算作 Slot Info */} {rawStatus === 'Waitlist' ? 'Slot Status' : (t('slots.earliest_date') || 'Earliest Slot')}

{renderCenterContent()}
{/* 底部:监控状态 & 链接 */}
{lang === 'zh' ? '监控状态' : 'Monitor'}
{/* 只要是有号(日期或Waitlist)且新鲜,就闪烁绿灯 */} {isLiveSlot && ( )}
{/* 调试用:历史快照时间 */} {isHistorySlot && (
Last seen:
)} {data.website && ( )}
{!canSubscribe && !isBlacklisted && (

{t('slots.subscribe_unavailable') || 'Routing info unavailable'}

)}
); }