import React, { useState, useEffect, useMemo } from 'react';
import { ItineraryTimeline } from './ItineraryTimeline.js';
import { ExpenseManager } from './ExpenseManager.js';
import { useTourStore } from './useTourStore.js';
import { AddLocationModal } from './AddLocationModal.js';
import { MapContainer, TileLayer, Marker, Popup, useMapEvents, Polyline } from 'react-leaflet';
import {
Map as MapIcon,
Wallet,
Image as ImageIcon,
Calendar,
Users,
ChevronLeft,
Settings,
Quote,
Plus,
List,
Map as MapIconLucide
} from 'lucide-react';
import L from 'leaflet';
// Menu ngữ cảnh cho bản đồ
const MapContextMenu = ({ onAction }: { onAction: (action: string, latlng: L.LatLng) => void }) => {
const [menuPos, setMenuPos] = useState<{ x: number, y: number, latlng: L.LatLng } | null>(null);
const { legs } = useTourStore();
useMapEvents({
contextmenu: (e) => {
setMenuPos({ x: e.containerPoint.x, y: e.containerPoint.y, latlng: e.latlng });
},
click: () => setMenuPos(null),
dragstart: () => setMenuPos(null)
});
if (!menuPos) return null;
return (
Thêm vào chặng
{legs.map(leg => (
))}
);
};
export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
const [activeTab, setActiveTab] = useState<'plan' | 'expense' | 'photo' | 'settings'>('plan');
const [viewMode, setViewMode] = useState<'map' | 'timeline'>('timeline');
const [isAddLocationOpen, setIsAddLocationOpen] = useState(false);
const { currentTour, fetchTour, fetchPublicTours, publicTours, userRole, legs, addLocation } = useTourStore();
useEffect(() => {
const loadData = async () => {
// Nếu chưa có tour nào trong store, thử tải danh sách public trước
if (publicTours.length === 0) {
await fetchPublicTours();
}
};
loadData();
}, []);
useEffect(() => {
// Lấy tour đầu tiên từ danh sách khám phá để hiển thị demo
if (publicTours.length > 0 && !currentTour) {
fetchTour(publicTours[0].id);
}
}, [publicTours, currentTour, fetchTour]);
const handleMapAction = async (action: string, latlng: L.LatLng) => {
if (!currentTour || legs.length === 0) return;
let targetLegId = legs[0].id; // Mặc định là chặng đầu
let defaultName = "Địa điểm mới";
if (action === 'START') defaultName = "Điểm bắt đầu";
if (action === 'END') {
targetLegId = legs[legs.length - 1].id;
defaultName = "Điểm kết thúc";
}
if (action.startsWith('ADD_TO_LEG_')) {
targetLegId = action.replace('ADD_TO_LEG_', '');
}
const name = window.prompt("Nhập tên địa điểm:", defaultName);
if (name) {
await addLocation(currentTour.id, {
name,
latitude: latlng.lat,
longitude: latlng.lng,
legId: targetLegId,
type: 'VISIT'
});
}
};
// Đị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: true },
{ id: 'expense', label: 'Chi phí', icon: Wallet, visible: ['OWNER', 'MANAGER', 'MEMBER'].includes(userRole || '') },
{ id: 'photo', label: 'Ảnh', icon: ImageIcon, visible: true },
{ id: 'settings', label: 'Cài đặt', icon: Settings, visible: ['OWNER', 'MANAGER'].includes(userRole || '') },
].filter(t => t.visible);
const hasFinanceAccess = ['OWNER', 'MANAGER', 'MEMBER'].includes(userRole || '');
const travelQuotes = [
"Đừng nghe họ nói, hãy tự mình đi xem.",
"Thế giới là một cuốn sách, và ai không đi du lịch thì chỉ mới đọc được một trang.",
"Hành trình ngàn dặm bắt đầu từ một bước chân.",
"Đi là để trở về, nhưng với một tâm hồn mới."
];
const randomQuote = useMemo(() => travelQuotes[Math.floor(Math.random() * travelQuotes.length)], []);
const tourInfo = {
title: currentTour?.title || "Hành trình khám phá TP.HCM",
date: currentTour?.startDate ? `${new Date(currentTour.startDate).toLocaleDateString('vi-VN')} - ${new Date(currentTour.endDate).toLocaleDateString('vi-VN')}` : "Chưa xác định ngày",
membersCount: currentTour?.participants?.length || 0,
budget: currentTour?.totalCost ? `${Number(currentTour.totalCost).toLocaleString()} VND` : "0 VND",
coverImage: currentTour?.photos?.[0]?.imageUrl || "https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?q=80&w=2070&auto=format&fit=crop"
};
return (
{/* Top Navigation Bar */}
{tourInfo.title}
{/* Spacer */}
{/* Tour Header Info */}
{tourInfo.title}
{tourInfo.date}
{tourInfo.membersCount} thành viên
{/* Member Avatars Stack */}
{currentTour?.participants?.slice(0, 5).map((p: any, i: number) => (
))}
{tourInfo.membersCount > 5 && (
+{tourInfo.membersCount - 5}
)}
{/* Financial Quick-View Widget or Quote */}
{hasFinanceAccess ? (
<>
Tổng chi tiêu hiện tại
{tourInfo.budget}
>
) : (
)}
{/* Main Content Area - Điều chỉnh max-width khi ở chế độ bản đồ */}
{/* Tab Switcher */}
{tabs.map((tab) => (
))}
{/* Tab Panels */}
{activeTab === 'plan' && (
{/* View Mode Toggle */}
{viewMode === 'timeline' ? (
) : (
{/* Vẽ đường Polyline nối các điểm */}
{legs.map(leg => {
const positions = leg.locations.map((l: any) => [l.latitude, l.longitude]);
return ;
})}
{legs.flatMap(l => l.locations).map((loc: any) => (
{loc.name}
{loc.type}
))}
Mẹo: Nhấn giữ (Mobile) hoặc Chuột phải để ghim địa điểm
)}
)}
{activeTab === 'expense' && (
)}
{activeTab === 'photo' && (
{[1, 2, 3, 4, 5, 6].map((i) => (
))}
)}
{activeTab === 'settings' && (
Tính năng quản lý thành viên đang được cập nhật...
)}
{/* Floating Action Button (Mobile) */}
{/* Add Location Modal */}
{currentTour && (
setIsAddLocationOpen(false)}
tourId={currentTour.id}
/>
)}
);
};