231 lines
10 KiB
TypeScript
231 lines
10 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Compass, Map, Image as ImageIcon, Settings, ShieldAlert, Users, LogOut, LogIn, Globe, Sun, Moon } from 'lucide-react';
|
|
import { useTranslation } from '@/hooks/useTranslation';
|
|
import { useTheme } from '@/hooks/useTheme';
|
|
|
|
interface MapProfileDropdownProps {
|
|
user: any;
|
|
onLogout?: () => void;
|
|
onOpenSettings: () => void;
|
|
onOpenCreateTour: () => void;
|
|
onOpenReport: () => void;
|
|
onOpenLogin: () => void;
|
|
onOpenMyPhotos: () => void;
|
|
onOpenMyTours: () => void;
|
|
onOpenFriends: () => void;
|
|
onOpenAdmin?: () => void;
|
|
}
|
|
|
|
export const MapProfileDropdown: React.FC<MapProfileDropdownProps> = ({
|
|
user,
|
|
onLogout,
|
|
onOpenSettings,
|
|
onOpenCreateTour,
|
|
onOpenReport,
|
|
onOpenLogin,
|
|
onOpenMyPhotos,
|
|
onOpenMyTours,
|
|
onOpenFriends,
|
|
onOpenAdmin,
|
|
}) => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const { lang, changeLanguage } = useTranslation();
|
|
const { theme, changeTheme } = useTheme();
|
|
|
|
const guestToken = localStorage.getItem('guest_token');
|
|
const isAuthenticated = !!user && !guestToken;
|
|
|
|
const renderInitialsAvatar = (name: string) => {
|
|
const initials = name
|
|
? name.split(' ').map(n => n[0]).slice(0, 2).join('').toUpperCase()
|
|
: 'U';
|
|
return (
|
|
<div className="w-full h-full flex items-center justify-center bg-indigo-600 text-white font-bold text-sm">
|
|
{initials}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const handleItemClick = (callback: () => void) => {
|
|
setIsOpen(false);
|
|
callback();
|
|
};
|
|
|
|
return (
|
|
<div className="relative inline-block text-left select-none pointer-events-auto">
|
|
{/* TRIGGER BUTTON */}
|
|
<button
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
className="w-11 h-11 rounded-full border-2 border-white dark:border-slate-800 bg-slate-800 flex items-center justify-center overflow-hidden shadow-xl active:scale-95 transition-all duration-150 cursor-pointer"
|
|
title="Menu cá nhân"
|
|
>
|
|
{isAuthenticated ? (
|
|
user?.avatar ? (
|
|
<img src={user.avatar} alt="Avatar" className="w-full h-full object-cover" />
|
|
) : (
|
|
renderInitialsAvatar(user?.name || 'User')
|
|
)
|
|
) : (
|
|
<div className="w-full h-full flex items-center justify-center bg-slate-700 text-slate-300 font-bold text-sm">
|
|
G
|
|
</div>
|
|
)}
|
|
</button>
|
|
|
|
{/* DROPDOWN MENU / MOBILE BOTTOM SHEET LAYER */}
|
|
{isOpen && (
|
|
<>
|
|
<div
|
|
className="fixed inset-0 z-[999998] bg-black/50 sm:bg-transparent"
|
|
onClick={() => setIsOpen(false)}
|
|
/>
|
|
|
|
<div
|
|
className="fixed bottom-0 left-0 right-0 sm:absolute sm:bottom-auto sm:top-14 sm:right-0 sm:left-auto z-[999999] w-full sm:w-64 bg-slate-900 border-t sm:border border-slate-800 rounded-t-3xl sm:rounded-2xl p-3 sm:p-2 shadow-2xl animate-in slide-in-from-bottom sm:slide-in-from-top-2 duration-300 text-xs text-slate-200"
|
|
>
|
|
<div className="w-12 h-1 bg-slate-700 rounded-full mx-auto mb-3 sm:hidden" />
|
|
|
|
{isAuthenticated ? (
|
|
<div className="flex flex-col space-y-1">
|
|
{/* User info header */}
|
|
<div className="px-4 py-2 border-b border-slate-800/60 mb-1 flex items-center gap-3">
|
|
<div className="w-8 h-8 rounded-full overflow-hidden shrink-0 border border-slate-700">
|
|
{user?.avatar ? (
|
|
<img src={user.avatar} alt="Avatar" className="w-full h-full object-cover" />
|
|
) : (
|
|
renderInitialsAvatar(user?.name || 'User')
|
|
)}
|
|
</div>
|
|
<div className="flex flex-col min-w-0">
|
|
<span className="font-bold text-white truncate text-sm">{user?.name}</span>
|
|
<span className="text-[10px] text-slate-400 truncate">{user?.email}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Extra System Admin option */}
|
|
{user?.isAdmin && (
|
|
<button
|
|
onClick={() => handleItemClick(onOpenAdmin || (() => {}))}
|
|
className="flex items-center gap-3 w-full px-4 py-2.5 bg-blue-950/20 hover:bg-slate-800 rounded-lg text-left transition-colors font-bold text-blue-400 cursor-pointer"
|
|
>
|
|
<Settings className="w-4 h-4 shrink-0 text-blue-400" /> Quản trị hệ thống
|
|
</button>
|
|
)}
|
|
|
|
{/* 1. Tạo tour */}
|
|
<button
|
|
onClick={() => handleItemClick(onOpenCreateTour)}
|
|
className="flex items-center gap-3 w-full px-4 py-2.5 hover:bg-slate-800 rounded-lg text-left transition-colors font-semibold cursor-pointer"
|
|
>
|
|
<Compass className="w-4 h-4 text-amber-400 shrink-0" /> Tạo tour
|
|
</button>
|
|
|
|
{/* 2. Hành trình của tôi */}
|
|
<button
|
|
onClick={() => handleItemClick(onOpenMyTours)}
|
|
className="flex items-center gap-3 w-full px-4 py-2.5 hover:bg-slate-800 rounded-lg text-left transition-colors font-semibold cursor-pointer"
|
|
>
|
|
<Map className="w-4 h-4 text-blue-400 shrink-0" /> Hành trình của tôi
|
|
</button>
|
|
|
|
{/* 3. Thư viện ảnh */}
|
|
<button
|
|
onClick={() => handleItemClick(onOpenMyPhotos)}
|
|
className="flex items-center gap-3 w-full px-4 py-2.5 hover:bg-slate-800 rounded-lg text-left transition-colors font-semibold cursor-pointer"
|
|
>
|
|
<ImageIcon className="w-4 h-4 text-emerald-400 shrink-0" /> Thư viện ảnh
|
|
</button>
|
|
|
|
{/* 4. Cài đặt */}
|
|
<button
|
|
onClick={() => handleItemClick(onOpenSettings)}
|
|
className="flex items-center gap-3 w-full px-4 py-2.5 hover:bg-slate-800 rounded-lg text-left transition-colors font-semibold cursor-pointer"
|
|
>
|
|
<Settings className="w-4 h-4 text-slate-400 shrink-0" /> Cài đặt
|
|
</button>
|
|
|
|
{/* 5. Báo cáo vi phạm */}
|
|
<button
|
|
onClick={() => handleItemClick(onOpenReport)}
|
|
className="flex items-center gap-3 w-full px-4 py-2.5 hover:bg-slate-800 rounded-lg text-left transition-colors font-semibold cursor-pointer"
|
|
>
|
|
<ShieldAlert className="w-4 h-4 text-rose-400 shrink-0" /> Báo cáo vi phạm
|
|
</button>
|
|
|
|
{/* STRICT VISUAL DIVIDER LINE */}
|
|
<div className="border-b border-slate-800 my-1 mx-2" />
|
|
|
|
{/* 6. Danh sách bạn bè */}
|
|
<button
|
|
onClick={() => handleItemClick(onOpenFriends)}
|
|
className="flex items-center gap-3 w-full px-4 py-2.5 hover:bg-slate-800 rounded-lg text-left transition-colors font-semibold cursor-pointer"
|
|
>
|
|
<Users className="w-4 h-4 text-indigo-400 shrink-0" /> Danh sách bạn bè
|
|
</button>
|
|
|
|
{/* 7. Đăng xuất */}
|
|
<button
|
|
onClick={() => handleItemClick(onLogout || (() => {}))}
|
|
className="flex items-center gap-3 w-full px-4 py-2.5 text-rose-400 hover:bg-slate-800 rounded-lg text-left font-bold transition-colors cursor-pointer"
|
|
>
|
|
<LogOut className="w-4 h-4 shrink-0 text-rose-500" /> Đăng xuất
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col space-y-1">
|
|
<div className="px-4 py-1.5 text-[10px] font-black uppercase tracking-wider text-slate-500">Tùy chỉnh nhanh</div>
|
|
|
|
<div className="px-3 py-2 flex flex-col gap-3 bg-slate-950/40 rounded-xl m-1 border border-slate-850">
|
|
<div className="flex justify-between items-center">
|
|
<span className="font-semibold flex items-center gap-1.5 text-slate-400">
|
|
<Globe className="w-3.5 h-3.5 text-indigo-400" /> Ngôn ngữ:
|
|
</span>
|
|
<select
|
|
value={lang}
|
|
onChange={(e) => changeLanguage(e.target.value as any)}
|
|
className="bg-slate-800 border border-slate-700 rounded-lg px-2 py-1 text-white text-[11px] font-bold focus:outline-none cursor-pointer"
|
|
>
|
|
<option value="vi">Tiếng Việt</option>
|
|
<option value="en">English</option>
|
|
<option value="zh">中文</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-center">
|
|
<span className="font-semibold flex items-center gap-1.5 text-slate-400">
|
|
{theme === 'light' ? (
|
|
<Sun className="w-3.5 h-3.5 text-amber-500" />
|
|
) : (
|
|
<Moon className="w-3.5 h-3.5 text-indigo-400" />
|
|
)}
|
|
Giao diện:
|
|
</span>
|
|
<select
|
|
value={theme}
|
|
onChange={(e) => changeTheme(e.target.value as any)}
|
|
className="bg-slate-800 border border-slate-700 rounded-lg px-2 py-1 text-white text-[11px] font-bold focus:outline-none cursor-pointer"
|
|
>
|
|
<option value="dark">Tối</option>
|
|
<option value="light">Sáng</option>
|
|
<option value="system">Hệ thống</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="h-[1px] bg-slate-850 my-1 mx-2" />
|
|
|
|
<button
|
|
onClick={() => handleItemClick(onOpenLogin)}
|
|
className="flex items-center gap-3 w-full px-4 py-3 text-blue-400 hover:bg-slate-800 rounded-xl text-left font-black transition-colors cursor-pointer"
|
|
>
|
|
<LogIn className="w-4 h-4 shrink-0" /> Đăng ký / Đăng nhập
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|