137 lines
5.1 KiB
TypeScript
137 lines
5.1 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { ItineraryTimeline } from './ItineraryTimeline';
|
|
import { ExpenseManager } from './ExpenseManager';
|
|
import { useTourStore } from './useTourStore';
|
|
import {
|
|
Map as MapIcon,
|
|
Wallet,
|
|
Image as ImageIcon,
|
|
Calendar,
|
|
Users,
|
|
ChevronLeft
|
|
} from 'lucide-react';
|
|
|
|
export const TourDetailPage = () => {
|
|
const [activeTab, setActiveTab] = useState<'plan' | 'expense' | 'photo'>('plan');
|
|
const { currentTour, fetchTour, userRole } = useTourStore();
|
|
|
|
useEffect(() => {
|
|
// Gọi ID 1 (Dữ liệu từ file seed) để kiểm tra
|
|
fetchTour(1);
|
|
}, [fetchTour]);
|
|
|
|
// Định nghĩa các tab dựa trên quyền hạn (RBAC) từ store
|
|
const tabs = [
|
|
{ id: 'plan', label: 'Lộ trình', icon: MapIcon, visible: ['OWNER', 'EDITOR', 'MEMBER_PLAN_ONLY'].includes(userRole || '') },
|
|
{ id: 'expense', label: 'Chi phí', icon: Wallet, visible: ['OWNER', 'EDITOR'].includes(userRole || '') },
|
|
{ id: 'photo', label: 'Ảnh', icon: ImageIcon, visible: true },
|
|
].filter(t => t.visible);
|
|
|
|
// Tự động chuyển tab nếu người dùng không có quyền xem 'plan'
|
|
useEffect(() => {
|
|
if (userRole === 'MEMBER_PHOTO_ONLY') {
|
|
setActiveTab('photo');
|
|
}
|
|
}, [userRole]);
|
|
|
|
// Mock dữ liệu header (Trong thực tế sẽ lấy từ currentTour)
|
|
const tourInfo = {
|
|
title: currentTour?.title || "Hành trình khám phá TP.HCM",
|
|
date: "20 - 21 Tháng 11, 2023",
|
|
members: 5,
|
|
budget: "2.500.000 VND"
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white">
|
|
{/* Top Navigation Bar */}
|
|
<div className="sticky top-0 z-30 bg-white border-b border-gray-100 px-4 py-3 flex items-center justify-between">
|
|
<button className="p-2 hover:bg-gray-100 rounded-full transition-colors">
|
|
<ChevronLeft className="w-6 h-6 text-gray-600" />
|
|
</button>
|
|
<h1 className="text-lg font-bold text-gray-800 truncate px-4">
|
|
{tourInfo.title}
|
|
</h1>
|
|
<div className="w-10" /> {/* Spacer */}
|
|
</div>
|
|
|
|
{/* Tour Header Info */}
|
|
<div className="bg-blue-600 text-white p-6 pb-20">
|
|
<div className="max-w-2xl mx-auto space-y-4">
|
|
<div className="flex flex-wrap gap-4 text-sm opacity-90">
|
|
<div className="flex items-center">
|
|
<Calendar className="w-4 h-4 mr-1.5" />
|
|
{tourInfo.date}
|
|
</div>
|
|
<div className="flex items-center">
|
|
<Users className="w-4 h-4 mr-1.5" />
|
|
{tourInfo.members} thành viên
|
|
</div>
|
|
</div>
|
|
<div className="flex items-baseline gap-2">
|
|
<span className="text-3xl font-bold">{tourInfo.budget}</span>
|
|
<span className="text-blue-100 text-sm">dự kiến</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Content Area */}
|
|
<div className="max-w-2xl mx-auto -mt-12 px-4 pb-24">
|
|
{/* Tab Switcher */}
|
|
<div className="bg-white rounded-2xl shadow-xl border border-gray-100 p-1 flex mb-6">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id as any)}
|
|
className={`flex-1 flex items-center justify-center py-3 rounded-xl text-sm font-semibold transition-all ${
|
|
activeTab === tab.id
|
|
? 'bg-blue-50 text-blue-600 shadow-sm'
|
|
: 'text-gray-400 hover:text-gray-600'
|
|
}`}
|
|
>
|
|
<tab.icon className={`w-4 h-4 mr-2 ${activeTab === tab.id ? 'animate-pulse' : ''}`} />
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Tab Panels */}
|
|
<div className="transition-opacity duration-300">
|
|
{activeTab === 'plan' && (
|
|
<div className="animate-in fade-in slide-in-from-bottom-4">
|
|
<ItineraryTimeline />
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'expense' && (
|
|
<div className="animate-in fade-in slide-in-from-bottom-4">
|
|
<ExpenseManager />
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'photo' && (
|
|
<div className="grid grid-cols-3 gap-2 animate-in fade-in">
|
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
|
<div key={i} className="aspect-square bg-gray-100 rounded-lg overflow-hidden relative group">
|
|
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors" />
|
|
<img
|
|
src={`https://picsum.photos/seed/${i + 10}/400/400`}
|
|
alt="Tour photo"
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Floating Action Button (Mobile) */}
|
|
<div className="fixed bottom-6 left-1/2 -translate-x-1/2 z-40">
|
|
<button className="bg-blue-600 text-white px-6 py-3 rounded-full shadow-lg hover:bg-blue-700 transition-all flex items-center font-bold">
|
|
{activeTab === 'plan' ? 'Thêm địa điểm' : activeTab === 'expense' ? 'Thêm chi phí' : 'Đăng ảnh'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}; |