fix: sync notification and shop dashboard state
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { Bell, CheckCheck, MessageSquare, ShoppingBag } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { mockNotifications } from "@/lib/mock"
|
||||
import type { Notification } from "@/lib/types"
|
||||
import { useNotificationStore } from "@/store/notifications"
|
||||
|
||||
const typeIcons: Record<Notification["type"], typeof Bell> = {
|
||||
order: ShoppingBag,
|
||||
@@ -46,10 +48,12 @@ function NotificationItem({ notification }: { notification: Notification }) {
|
||||
}
|
||||
|
||||
export default function NotificationsPage() {
|
||||
const unreadCount = mockNotifications.filter((n) => !n.read).length
|
||||
const orderNotifs = mockNotifications.filter((n) => n.type === "order")
|
||||
const communityNotifs = mockNotifications.filter((n) => n.type === "community")
|
||||
const systemNotifs = mockNotifications.filter((n) => n.type === "system")
|
||||
const notifications = useNotificationStore((state) => state.notifications)
|
||||
const markAllAsRead = useNotificationStore((state) => state.markAllAsRead)
|
||||
const unreadCount = notifications.filter((notification) => !notification.read).length
|
||||
const orderNotifs = notifications.filter((notification) => notification.type === "order")
|
||||
const communityNotifs = notifications.filter((notification) => notification.type === "community")
|
||||
const systemNotifs = notifications.filter((notification) => notification.type === "system")
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl space-y-6">
|
||||
@@ -58,7 +62,7 @@ export default function NotificationsPage() {
|
||||
<h1 className="text-2xl font-bold">通知中心</h1>
|
||||
{unreadCount > 0 && <Badge>{unreadCount} 条未读</Badge>}
|
||||
</div>
|
||||
<Button variant="outline" size="sm">
|
||||
<Button variant="outline" size="sm" onClick={markAllAsRead}>
|
||||
<CheckCheck className="mr-1 h-4 w-4" />
|
||||
全部已读
|
||||
</Button>
|
||||
@@ -73,8 +77,8 @@ export default function NotificationsPage() {
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="all" className="space-y-2 mt-4">
|
||||
{mockNotifications.map((n) => (
|
||||
<NotificationItem key={n.id} notification={n} />
|
||||
{notifications.map((notification) => (
|
||||
<NotificationItem key={notification.id} notification={notification} />
|
||||
))}
|
||||
</TabsContent>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowDownLeft, ArrowUpRight, CreditCard, DollarSign } from "lucide-react"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
@@ -9,10 +11,18 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
import { mockOrders, mockTransactions } from "@/lib/mock"
|
||||
import { mockTransactions } from "@/lib/mock"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
import { useShopStore } from "@/store/shops"
|
||||
|
||||
export default function ShopIncomePage() {
|
||||
const completedOrders = mockOrders.filter((o) => o.status === "completed")
|
||||
const userId = useAuthStore((state) => state.user?.id)
|
||||
const shops = useShopStore((state) => state.shops)
|
||||
const orders = useOrderStore((state) => state.orders)
|
||||
const shop = shops.find((item) => item.owner.id === userId) ?? shops[0]
|
||||
const shopOrders = orders.filter((order) => order.shopId === shop?.id)
|
||||
const completedOrders = shopOrders.filter((o) => o.status === "completed")
|
||||
const totalIncome = completedOrders.reduce((acc, order) => acc + order.totalPrice, 0)
|
||||
|
||||
const currentMonth = new Date().getMonth()
|
||||
@@ -20,10 +30,19 @@ export default function ShopIncomePage() {
|
||||
.filter((o) => new Date(o.completedAt || "").getMonth() === currentMonth)
|
||||
.reduce((acc, order) => acc + order.totalPrice, 0)
|
||||
|
||||
const pendingSettlement = mockOrders
|
||||
const pendingSettlement = shopOrders
|
||||
.filter((o) => ["in_progress", "pending_close", "pending_review"].includes(o.status))
|
||||
.reduce((acc, order) => acc + order.totalPrice, 0)
|
||||
|
||||
const shopOrderIds = new Set(shopOrders.map((order) => order.id))
|
||||
const relatedTransactions = mockTransactions.filter((transaction) => {
|
||||
if (transaction.type === "withdrawal") return true
|
||||
if (transaction.type !== "income") return false
|
||||
const match = transaction.description.match(/ord\d+/)
|
||||
if (!match) return false
|
||||
return shopOrderIds.has(match[0])
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h1 className="text-2xl font-bold">收入统计</h1>
|
||||
@@ -73,7 +92,7 @@ export default function ShopIncomePage() {
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{mockTransactions.map((transaction) => (
|
||||
{relatedTransactions.map((transaction) => (
|
||||
<TableRow key={transaction.id}>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { AlertCircle, CheckCircle, Clock, ListOrdered } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
@@ -12,11 +14,19 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
import { statusLabels } from "@/lib/constants"
|
||||
import { mockOrders } from "@/lib/mock"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
import { useShopStore } from "@/store/shops"
|
||||
|
||||
export default function ShopOrdersPage() {
|
||||
const totalOrders = mockOrders.length
|
||||
const activeOrders = mockOrders.filter((o) =>
|
||||
const userId = useAuthStore((state) => state.user?.id)
|
||||
const shops = useShopStore((state) => state.shops)
|
||||
const orders = useOrderStore((state) => state.orders)
|
||||
const shop = shops.find((item) => item.owner.id === userId) ?? shops[0]
|
||||
const shopOrders = orders.filter((order) => order.shopId === shop?.id)
|
||||
|
||||
const totalOrders = shopOrders.length
|
||||
const activeOrders = shopOrders.filter((o) =>
|
||||
[
|
||||
"pending_payment",
|
||||
"pending_accept",
|
||||
@@ -25,8 +35,8 @@ export default function ShopOrdersPage() {
|
||||
"pending_review",
|
||||
].includes(o.status),
|
||||
).length
|
||||
const completedOrders = mockOrders.filter((o) => o.status === "completed").length
|
||||
const disputedOrders = mockOrders.filter((o) => o.status === "disputed").length
|
||||
const completedOrders = shopOrders.filter((o) => o.status === "completed").length
|
||||
const disputedOrders = shopOrders.filter((o) => o.status === "disputed").length
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -91,7 +101,7 @@ export default function ShopOrdersPage() {
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{mockOrders.map((order) => (
|
||||
{shopOrders.map((order) => (
|
||||
<TableRow key={order.id}>
|
||||
<TableCell className="font-medium">{order.service.title}</TableCell>
|
||||
<TableCell>{order.consumerName}</TableCell>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card"
|
||||
import { roleLabels } from "@/lib/constants"
|
||||
import { mockGames, mockPosts } from "@/lib/mock"
|
||||
import { mockGames, mockOrders, mockPlayers, mockPosts } from "@/lib/mock"
|
||||
|
||||
export default function CommunityPage() {
|
||||
const [sortMode, setSortMode] = useState<"latest" | "hot">("latest")
|
||||
@@ -74,7 +74,16 @@ export default function CommunityPage() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{filteredPosts.map((post) => (
|
||||
{filteredPosts.map((post) =>
|
||||
(() => {
|
||||
const linkedOrder = post.linkedOrderId
|
||||
? mockOrders.find((order) => order.id === post.linkedOrderId)
|
||||
: null
|
||||
const linkedPlayer = linkedOrder
|
||||
? mockPlayers.find((player) => player.id === linkedOrder.playerId)
|
||||
: null
|
||||
|
||||
return (
|
||||
<Link key={post.id} href={`/post/${post.id}`}>
|
||||
<Card className="hover:shadow-md transition-shadow">
|
||||
<CardHeader className="pb-3">
|
||||
@@ -110,15 +119,30 @@ export default function CommunityPage() {
|
||||
</div>
|
||||
)}
|
||||
{post.linkedOrderId && (
|
||||
<div className="mt-2 rounded border bg-muted/30 px-3 py-2 text-xs text-muted-foreground flex items-center gap-1.5">
|
||||
<div className="mt-2 rounded border bg-muted/30 px-3 py-2 text-xs text-muted-foreground space-y-1.5">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<ClipboardList className="h-3.5 w-3.5" />
|
||||
关联订单秀单
|
||||
</div>
|
||||
{linkedOrder && (
|
||||
<div className="pl-5">
|
||||
<p>
|
||||
{linkedOrder.service.gameName} · {linkedOrder.service.title}
|
||||
</p>
|
||||
<p>
|
||||
{linkedOrder.playerName}
|
||||
{linkedPlayer ? ` · ${linkedPlayer.rating}` : ""}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
<CardFooter className="pt-0 text-sm text-muted-foreground gap-4">
|
||||
<span className="flex items-center gap-1">
|
||||
<Heart className={`h-4 w-4 ${post.liked ? "fill-red-500 text-red-500" : ""}`} />
|
||||
<Heart
|
||||
className={`h-4 w-4 ${post.liked ? "fill-red-500 text-red-500" : ""}`}
|
||||
/>
|
||||
{post.likeCount}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
@@ -128,7 +152,9 @@ export default function CommunityPage() {
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
)
|
||||
})(),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -29,10 +29,11 @@ import {
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"
|
||||
import { currentUser, mockNotifications, mockShops } from "@/lib/mock"
|
||||
import { currentUser, mockShops } from "@/lib/mock"
|
||||
import type { UserRole } from "@/lib/types"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useNotificationStore } from "@/store/notifications"
|
||||
|
||||
const roleLabels: Record<UserRole, string> = {
|
||||
consumer: "消费者",
|
||||
@@ -72,7 +73,9 @@ export function Header() {
|
||||
setMobileOpen(false)
|
||||
}
|
||||
|
||||
const unreadCount = mockNotifications.filter((n) => !n.read).length
|
||||
const unreadCount = useNotificationStore(
|
||||
(state) => state.notifications.filter((notification) => !notification.read).length,
|
||||
)
|
||||
|
||||
const handleSearch = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { create } from "zustand"
|
||||
import { mockNotifications } from "@/lib/mock"
|
||||
import type { Notification } from "@/lib/types"
|
||||
|
||||
interface NotificationState {
|
||||
notifications: Notification[]
|
||||
markAllAsRead: () => void
|
||||
}
|
||||
|
||||
export const useNotificationStore = create<NotificationState>((set) => ({
|
||||
notifications: mockNotifications,
|
||||
markAllAsRead: () =>
|
||||
set((state) => ({
|
||||
notifications: state.notifications.map((notification) => ({ ...notification, read: true })),
|
||||
})),
|
||||
}))
|
||||
Reference in New Issue
Block a user