"use client" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { EmptyState } from "@/components/ui/empty-state" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { requestWithAuth } from "@/lib/api/client" import { listNotifications, markAllNotificationsAsRead } from "@/lib/api/notifications" import { toApiError } from "@/lib/errors" import { notifyInfo } from "@/lib/toast" import type { Notification } from "@/lib/types" import { cn } from "@/lib/utils" import { Bell, CheckCheck, Loader2, MessageSquare, ShoppingBag } from "lucide-react" import Link from "next/link" import { useCallback, useEffect, useMemo, useRef, useState } from "react" const typeIcons: Record = { order: ShoppingBag, community: MessageSquare, system: Bell, } const typeLabels: Record = { order: "订单", community: "社区", system: "系统", } const typeVariants: Record = { order: "info", community: "neutral", system: "neutral", } function NotificationItem({ notification }: { notification: Notification }) { const Icon = typeIcons[notification.type] const content = (
{notification.title} {!notification.read && ( )}

{notification.content}

{new Date(notification.createdAt).toLocaleString("zh-CN")}

{typeLabels[notification.type]}
) return notification.link ? ( {content} ) : (
{content}
) } export default function NotificationsPage() { const mountedRef = useRef(true) const markAllPendingRef = useRef(false) const [notifications, setNotifications] = useState([]) const [loading, setLoading] = useState(true) const [loadingError, setLoadingError] = useState(null) const [markingAll, setMarkingAll] = useState(false) const loadNotifications = useCallback(async function loadNotifications() { setLoading(true) setLoadingError(null) try { const res = await requestWithAuth(() => listNotifications({ offset: 0, limit: 50 }), { onUnauthorized: () => loadNotifications(), }) if (!mountedRef.current) return if (res === null) { setLoading(false) return } setNotifications(res) setLoading(false) } catch (err: unknown) { if (!mountedRef.current) return setLoading(false) setLoadingError(toApiError(err).msg) } }, []) useEffect(() => { mountedRef.current = true void loadNotifications() return () => { mountedRef.current = false } }, [loadNotifications]) const markAllAsRead = useCallback(async function markAllAsRead() { if (markAllPendingRef.current) return markAllPendingRef.current = true setMarkingAll(true) try { const res = await requestWithAuth(() => markAllNotificationsAsRead(), { onUnauthorized: () => markAllAsRead(), }) if (!mountedRef.current) return if (res === null) { return } setNotifications((prev) => prev.map((n) => (n.read ? n : { ...n, read: true }))) } catch (err: unknown) { if (!mountedRef.current) return notifyInfo(toApiError(err).msg) } finally { markAllPendingRef.current = false if (mountedRef.current) setMarkingAll(false) } }, []) const unreadCount = useMemo( () => notifications.filter((notification) => !notification.read).length, [notifications], ) const orderNotifs = useMemo( () => notifications.filter((notification) => notification.type === "order"), [notifications], ) const communityNotifs = useMemo( () => notifications.filter((notification) => notification.type === "community"), [notifications], ) const systemNotifs = useMemo( () => notifications.filter((notification) => notification.type === "system"), [notifications], ) const renderTab = useCallback( (items: Notification[], emptyText: string) => { if (loading) { return ( ) } if (loadingError) { return ( ) } if (items.length === 0) { return ( ) } return (
{items.map((notification) => ( ))}
) }, [loading, loadingError], ) return (

通知中心

{unreadCount > 0 && ( {unreadCount} )}
全部 订单 社区 系统 {renderTab(notifications, "暂无通知")} {renderTab(orderNotifs, "暂无订单通知")} {renderTab(communityNotifs, "暂无社区通知")} {renderTab(systemNotifs, "暂无系统通知")}
) }