소스 검색

feat: update

jerry 3 달 전
부모
커밋
5cde8927de
1개의 변경된 파일77개의 추가작업 그리고 32개의 파일을 삭제
  1. 77 32
      src/app/slots/page.tsx

+ 77 - 32
src/app/slots/page.tsx

@@ -2,13 +2,22 @@
 
 import { useState } from 'react';
 import api from '@/lib/api';
-import { Search, Calendar, Clock, RefreshCw, AlertCircle, CheckCircle } from 'lucide-react';
+import { Search, Calendar, Clock, RefreshCw, AlertCircle, CheckCircle, ExternalLink } from 'lucide-react';
 import { useLanguage } from '@/lib/i18n/LanguageContext';
 import LocalTime from '@/components/common/LocalTime';
 
-// === 类型定义 (保持不变) ===
-interface TimeSlot { time: string; label?: string; }
-interface DayAvailability { date: string; times: TimeSlot[]; }
+// === 类型定义 ===
+
+interface TimeSlot {
+  time: string;
+  label?: string;
+}
+
+interface DayAvailability {
+  date: string;
+  times?: TimeSlot[]; 
+}
+
 interface SlotSnapshot {
   id: number;
   country: string;
@@ -17,6 +26,8 @@ interface SlotSnapshot {
   availability_status: 'None' | 'Available' | 'Waitlist';
   earliest_date: string | null;
   snapshot_at: string;
+  // 新增 website 字段
+  website?: string; 
   availability: DayAvailability[]; 
 }
 
@@ -29,7 +40,7 @@ export default function SlotQueryPage() {
   const [city, setCity] = useState('Dublin');
   const [visaType, setVisaType] = useState('Tourist');
 
-  // ... (options 和 fetchSlots 保持不变) ...
+  // ... (options 保持不变)
   const options = {
     countries: ['Austria','Croatia','Denmark','Finland','France','Germany','Greece','Hungary','Iceland','Italy','Netherlands','Poland','Spain'],
     cities: ['Dublin','Edinburgh','London','Manchester','Melbourne','Montreal','Singapore','Sydney','Toronto'],
@@ -60,7 +71,6 @@ export default function SlotQueryPage() {
   };
 
   return (
-    // 调整 1: 移动端 Padding
     <div className="min-h-screen bg-slate-50 py-6 px-4 md:py-12 md:px-6">
       <div className="max-w-5xl mx-auto">
         
@@ -72,7 +82,6 @@ export default function SlotQueryPage() {
 
         {/* 筛选区 */}
         <div className="bg-white p-5 md:p-6 rounded-2xl shadow-sm border border-slate-200 mb-8">
-          {/* 调整 2: 移动端单列布局 */}
           <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
             <div>
               <label className="block text-xs font-bold text-slate-500 uppercase mb-1">{t('product.country')}</label>
@@ -104,7 +113,6 @@ export default function SlotQueryPage() {
           </div>
           
           <div className="mt-6 flex justify-end">
-            {/* 调整 3: 移动端按钮全宽 */}
             <button 
               onClick={fetchSlots}
               disabled={loading}
@@ -148,34 +156,71 @@ export default function SlotQueryPage() {
               )}
             </div>
 
-            {/* 具体的 Slot 列表 */}
+            {/* 
+               === 列表渲染逻辑优化 === 
+               如果没有具体时间段,不要显示空网格,而是显示行动按钮或紧凑状态
+            */}
             {snapshot.availability && snapshot.availability.length > 0 && (
-              // 调整 4: 移动端单列卡片
               <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
-                {snapshot.availability.map((day, idx) => (
-                  <div key={idx} className="bg-white border border-slate-200 rounded-xl overflow-hidden hover:shadow-md transition">
-                    <div className="bg-slate-50 px-4 py-3 border-b border-slate-100 flex justify-between items-center">
-                      <div className="flex items-center gap-2 font-bold text-slate-700">
-                        <Calendar size={16} className="text-blue-500" />
-                        {formatDate(day.date)}
-                      </div>
-                      <span className="text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full font-medium">
-                        {day.times.length} {t('slots.slots_count')}
-                      </span>
-                    </div>
-                    
-                    <div className="p-4 grid grid-cols-2 gap-2">
-                      {day.times.map((slot, tIdx) => (
-                        <div key={tIdx} className="text-sm border border-slate-100 rounded p-2 text-center hover:border-blue-300 hover:bg-blue-50 transition cursor-default">
-                          <div className="font-mono font-bold text-slate-800">{slot.time}</div>
-                          {slot.label && (
-                            <div className="text-[10px] text-orange-500 font-medium mt-0.5">{slot.label}</div>
-                          )}
+                {snapshot.availability.map((day, idx) => {
+                  const times = day.times || [];
+                  const hasTimes = times.length > 0;
+
+                  return (
+                    <div key={idx} className="bg-white border border-slate-200 rounded-xl overflow-hidden hover:shadow-md transition flex flex-col">
+                      
+                      {/* Card Header */}
+                      <div className="bg-slate-50 px-4 py-3 border-b border-slate-100 flex justify-between items-center">
+                        <div className="flex items-center gap-2 font-bold text-slate-700">
+                          <Calendar size={16} className="text-blue-500" />
+                          {formatDate(day.date)}
                         </div>
-                      ))}
+                        {/* 优化徽章:有时间显示数量,没时间显示 Available */}
+                        <span className={`text-xs px-2 py-0.5 rounded-full font-medium ${
+                          hasTimes ? 'bg-blue-100 text-blue-700' : 'bg-green-100 text-green-700'
+                        }`}>
+                          {hasTimes ? `${times.length} ${t('slots.slots_count')}` : t('slots.status_available')}
+                        </span>
+                      </div>
+                      
+                      {/* Card Body */}
+                      <div className="p-4 flex-1 flex flex-col justify-center">
+                        {hasTimes ? (
+                          // 场景 A: 有具体时间段
+                          <div className="grid grid-cols-2 gap-2">
+                            {times.map((slot, tIdx) => (
+                              <div key={tIdx} className="text-sm border border-slate-100 rounded p-2 text-center hover:border-blue-300 hover:bg-blue-50 transition cursor-default">
+                                <div className="font-mono font-bold text-slate-800">{slot.time}</div>
+                                {slot.label && (
+                                  <div className="text-[10px] text-orange-500 font-medium mt-0.5">{slot.label}</div>
+                                )}
+                              </div>
+                            ))}
+                          </div>
+                        ) : (
+                          // 场景 B: 无具体时间段 (Date Only)
+                          // 显示更友好的提示或跳转按钮
+                          <div className="text-center space-y-3">
+                            <p className="text-xs text-slate-500">
+                              {/* 翻译:该日期已开放预约,具体时间请前往官网查看 */}
+                              {lang === 'zh' ? '该日期已开放预约,具体时间请前往官网查看' : 'Slots available. Please check details on the official website.'}
+                            </p>
+                            {snapshot.website && (
+                              <a 
+                                href={snapshot.website} 
+                                target="_blank" 
+                                rel="noopener noreferrer"
+                                className="flex items-center justify-center gap-2 w-full py-2 bg-blue-600 text-white rounded-lg text-sm font-bold hover:bg-blue-700 transition"
+                              >
+                                {lang === 'zh' ? '前往官网预约' : 'Book on Website'} <ExternalLink size={14} />
+                              </a>
+                            )}
+                          </div>
+                        )}
+                      </div>
                     </div>
-                  </div>
-                ))}
+                  );
+                })}
               </div>
             )}
           </div>