import { Fragment, useState } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useNavigate, useRouterState } from '@tanstack/react-router' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import 'dayjs/locale/vi' import { BellOutlined, CheckOutlined, ClockCircleOutlined, GlobalOutlined, InboxOutlined, LogoutOutlined, MoonOutlined, QuestionCircleOutlined, SearchOutlined, SettingOutlined, ShoppingCartOutlined, TeamOutlined, UserOutlined, } from '@ant-design/icons' import { Avatar, Badge, Breadcrumb, Button, ConfigProvider, Divider, Dropdown, Flex, Input, List, Popover, Select, Space, Switch, Tag, Tooltip, Typography, } from 'antd' import { useAuthStore } from '@/stores/auth-store' import { SidebarTrigger } from '@/components/ui/sidebar' import { fetchNotifications, markAllNotificationsRead, markNotificationRead, type NotificationItem, } from '@/features/notifications/api' dayjs.extend(relativeTime) dayjs.locale('vi') const { Text } = Typography // ---------------------------------------------------------------------------- // Breadcrumb labels (mirrors src/components/layout/app-breadcrumb.tsx) // ---------------------------------------------------------------------------- const segmentLabels: Record = { '': 'Tổng quan', dashboard: 'Tổng quan', create: 'Thêm mới', groups: 'Nhóm', tasks: 'Công việc', customers: 'Khách hàng', 'product-service': 'Sản phẩm / Dịch vụ', products: 'Sản phẩm', 'product-groups': 'Nhóm sản phẩm', 'vcenter-manager': 'vCenter Manager', 'infra-management': 'Hạ tầng', 'order-invoice': 'Đơn hàng / Hoá đơn', apps: 'Ứng dụng', chats: 'Tin nhắn', users: 'Người dùng', settings: 'Cài đặt', account: 'Tài khoản', appearance: 'Giao diện', notifications: 'Thông báo', display: 'Hiển thị', 'help-center': 'Trung tâm trợ giúp', tickets: 'Ticket hỗ trợ', roles: 'Vai trò', 'activity-logs': 'Nhật ký hoạt động', 'server-manager': 'Server Manager', 'domain-manager': 'Domain Manager', 'domain-fluctuations': 'Khai báo biến động tên miền', 'ip-pool': 'IP Pool', 'os-groups': 'OS Groups', addons: 'Addons', transactions: 'Giao dịch', posts: 'Bài viết', 'post-categories': 'Danh mục bài viết', resolve: 'Phân giải', } function labelFor(segment: string) { if (/^\d+$/.test(segment)) return 'Chi tiết' return segmentLabels[segment] ?? segment } // ---------------------------------------------------------------------------- // Notifications // ---------------------------------------------------------------------------- const notifIconMap: Record< NotificationItem['type'], { icon: typeof BellOutlined; color: string } > = { ticket_reply: { icon: TeamOutlined, color: '#722ed1' }, deposit_success: { icon: ShoppingCartOutlined, color: '#f16723' }, } // ---------------------------------------------------------------------------- // AppHeader // ---------------------------------------------------------------------------- export function AppHeader() { const pathname = useRouterState({ select: (s) => s.location.pathname }) const { auth } = useAuthStore() const [notifOpen, setNotifOpen] = useState(false) const [settingsOpen, setSettingsOpen] = useState(false) const [darkMode, setDarkMode] = useState(false) const [compact, setCompact] = useState(false) const [language, setLanguage] = useState<'vi' | 'en'>('vi') const qc = useQueryClient() const { data: notifData } = useQuery({ queryKey: ['notifications'], queryFn: fetchNotifications, refetchInterval: 60_000, }) const notifs = notifData?.data.items ?? [] const unreadCount = notifData?.data.unread_count ?? 0 const markOneRead = useMutation({ mutationFn: markNotificationRead, onSuccess: () => qc.invalidateQueries({ queryKey: ['notifications'] }), }) const markAllRead = useMutation({ mutationFn: markAllNotificationsRead, onSuccess: () => qc.invalidateQueries({ queryKey: ['notifications'] }), }) const segments = pathname.split('/').filter(Boolean) const crumbs = segments.length === 0 ? [{ title: Tổng quan }] : segments.map((seg, idx) => { const isLast = idx === segments.length - 1 const label = labelFor(seg) return { title: isLast ? {label} : {label}, } }) const fullName = auth.user ? `${auth.user.firstname ?? ''} ${auth.user.lastname ?? ''}`.trim() || auth.user.username : 'Khách' const userRole = auth.user?.roles?.[0] ?? 'Người dùng' const initials = fullName .split(' ') .filter(Boolean) .slice(-2) .map((s) => s[0]?.toUpperCase()) .join('') || 'U' const navigate = useNavigate() const userMenuItems = [ { key: 'profile', icon: , label: 'Hồ sơ', onClick: () => navigate({ to: '/settings' }), }, { key: 'logout', icon: , label: 'Đăng xuất', danger: true, onClick: () => auth.reset(), }, ] return (
} placeholder='Tìm kiếm...' className='!hidden lg:!inline-flex' style={{ width: 280 }} /> {/* Mobile/Tablet: search as icon button only */}
{notifs.length === 0 ? (
Chưa có thông báo
) : ( { const meta = notifIconMap[n.type] ?? { icon: BellOutlined, color: '#8c8c8c' } const Icon = meta.icon const unread = !n.is_read return ( { if (unread) markOneRead.mutate(n.id) }} style={{ padding: '10px 8px', borderRadius: 6, cursor: 'pointer', background: unread ? '#f0f7ff' : 'transparent', marginBottom: 4, }} >
{n.title} {unread && ( )} {n.body && ( {n.body} )} {dayjs(n.created_at).fromNow()}
) }} /> )}
} >