fix(pages): scope order views to the active role
This commit is contained in:
@@ -12,9 +12,17 @@ import { CheckCircle, Clock, DollarSign, ListOrdered, Star, TrendingUp, Users }
|
|||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
function getOrderRole(role: "consumer" | "player" | "owner" | "admin") {
|
||||||
|
if (role === "consumer" || role === "player" || role === "owner") {
|
||||||
|
return role
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
const { currentRole } = useAuthStore()
|
const { currentRole, user } = useAuthStore()
|
||||||
const isOwner = currentRole === "owner"
|
const isOwner = currentRole === "owner"
|
||||||
|
const orderRole = getOrderRole(currentRole)
|
||||||
|
|
||||||
const [player, setPlayer] = useState<Player | null>(null)
|
const [player, setPlayer] = useState<Player | null>(null)
|
||||||
const [shop, setShop] = useState<Shop | null>(null)
|
const [shop, setShop] = useState<Shop | null>(null)
|
||||||
@@ -28,8 +36,8 @@ export default function DashboardPage() {
|
|||||||
Promise.all([listPlayers(), listShops(), listServices()])
|
Promise.all([listPlayers(), listShops(), listServices()])
|
||||||
.then(([players, shops, services]) => {
|
.then(([players, shops, services]) => {
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
setPlayer(players[0] ?? null)
|
setPlayer(players.find((item) => item.user.id === user?.id) ?? null)
|
||||||
setShop(shops[0] ?? null)
|
setShop(shops.find((item) => item.owner.id === user?.id) ?? null)
|
||||||
setServices(services)
|
setServices(services)
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -42,14 +50,19 @@ export default function DashboardPage() {
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
}, [])
|
}, [user?.id])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
void (async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
const orders = await Promise.resolve(listOrders())
|
if (!orderRole) {
|
||||||
|
setOrders([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const orders = await Promise.resolve(listOrders({ role: orderRole }))
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
setOrders(orders)
|
setOrders(orders)
|
||||||
} catch {
|
} catch {
|
||||||
@@ -61,7 +74,7 @@ export default function DashboardPage() {
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
}, [])
|
}, [orderRole])
|
||||||
|
|
||||||
const totalOrders = isOwner ? (shop?.totalOrders ?? 0) : (player?.totalOrders ?? 0)
|
const totalOrders = isOwner ? (shop?.totalOrders ?? 0) : (player?.totalOrders ?? 0)
|
||||||
const rating = isOwner ? (shop?.rating ?? 0) : (player?.rating ?? 0)
|
const rating = isOwner ? (shop?.rating ?? 0) : (player?.rating ?? 0)
|
||||||
|
|||||||
+98
-73
@@ -56,6 +56,13 @@ const ownerTabs = [
|
|||||||
{ value: "disputed", label: "争议" },
|
{ value: "disputed", label: "争议" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
function getOrderRole(role: UserRole): "consumer" | "player" | "owner" | undefined {
|
||||||
|
if (role === "consumer" || role === "player" || role === "owner") {
|
||||||
|
return role
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
export default function OrderListPage() {
|
export default function OrderListPage() {
|
||||||
const { currentRole, user } = useAuthStore()
|
const { currentRole, user } = useAuthStore()
|
||||||
const shops = useShopStore((state) => state.shops)
|
const shops = useShopStore((state) => state.shops)
|
||||||
@@ -80,6 +87,7 @@ function OrderListContent({
|
|||||||
userId?: string
|
userId?: string
|
||||||
ownerShopId?: string
|
ownerShopId?: string
|
||||||
}) {
|
}) {
|
||||||
|
const orderRole = getOrderRole(currentRole)
|
||||||
const [tab, setTab] = useState<TabFilter | "pending">("all")
|
const [tab, setTab] = useState<TabFilter | "pending">("all")
|
||||||
const [orders, setOrders] = useState<Awaited<ReturnType<typeof listOrders>>>([])
|
const [orders, setOrders] = useState<Awaited<ReturnType<typeof listOrders>>>([])
|
||||||
const [sessions, setSessions] = useState<Awaited<ReturnType<typeof listChatSessions>>>([])
|
const [sessions, setSessions] = useState<Awaited<ReturnType<typeof listChatSessions>>>([])
|
||||||
@@ -87,9 +95,15 @@ function OrderListContent({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
|
if (!orderRole) {
|
||||||
|
return () => {
|
||||||
|
cancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void (async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
const items = await Promise.resolve(listOrders())
|
const items = await Promise.resolve(listOrders({ role: orderRole }))
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
setOrders(items)
|
setOrders(items)
|
||||||
} catch {
|
} catch {
|
||||||
@@ -101,7 +115,7 @@ function OrderListContent({
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
}, [])
|
}, [orderRole])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
@@ -148,83 +162,94 @@ function OrderListContent({
|
|||||||
? "客户视角"
|
? "客户视角"
|
||||||
: currentRole === "player"
|
: currentRole === "player"
|
||||||
? "打手视角"
|
? "打手视角"
|
||||||
: "店主视角"}
|
: currentRole === "owner"
|
||||||
|
? "店主视角"
|
||||||
|
: "管理员视角"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Tabs value={tab} onValueChange={(v) => setTab(v as TabFilter | "pending")}>
|
{!orderRole ? (
|
||||||
<TabsList>
|
<Card className="hover:shadow-card-hover">
|
||||||
{tabs.map((item) => (
|
<CardContent className="py-8 text-center text-sm text-muted-foreground">
|
||||||
<TabsTrigger key={item.value} value={item.value}>
|
当前身份暂不支持订单视角
|
||||||
{item.label}
|
</CardContent>
|
||||||
</TabsTrigger>
|
</Card>
|
||||||
))}
|
) : (
|
||||||
</TabsList>
|
<Tabs value={tab} onValueChange={(v) => setTab(v as TabFilter | "pending")}>
|
||||||
|
<TabsList>
|
||||||
|
{tabs.map((item) => (
|
||||||
|
<TabsTrigger key={item.value} value={item.value}>
|
||||||
|
{item.label}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value={tab} className="mt-4 space-y-4">
|
<TabsContent value={tab} className="mt-4 space-y-4">
|
||||||
{filtered.length === 0 ? (
|
{filtered.length === 0 ? (
|
||||||
<Card className="hover:shadow-card-hover">
|
<Card className="hover:shadow-card-hover">
|
||||||
<CardContent className="py-8 text-center text-sm text-muted-foreground">
|
<CardContent className="py-8 text-center text-sm text-muted-foreground">
|
||||||
暂无订单
|
暂无订单
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
) : (
|
|
||||||
filtered.map((order) => (
|
|
||||||
<Card key={order.id} className="hover:shadow-card-hover">
|
|
||||||
<CardHeader className="pb-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<CardTitle className="text-base">{order.service.title}</CardTitle>
|
|
||||||
<Badge className={cn("text-xs", statusColors[order.status])}>
|
|
||||||
{statusLabels[order.status]}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-muted-foreground">{order.service.title}</p>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
|
||||||
<span className="font-medium text-foreground">¥{order.totalPrice}</span>
|
|
||||||
<span className="flex items-center gap-1">
|
|
||||||
<Clock className="h-3.5 w-3.5" />
|
|
||||||
{new Date(order.createdAt).toLocaleDateString("zh-CN")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{(() => {
|
|
||||||
if (order.status !== "in_progress" && order.status !== "pending_close")
|
|
||||||
return null
|
|
||||||
const session = sessions.find(
|
|
||||||
(item) => item.type === "order" && item.orderId === order.id,
|
|
||||||
)
|
|
||||||
if (!session) return null
|
|
||||||
return (
|
|
||||||
<Button variant="outline" size="sm" asChild>
|
|
||||||
<Link href={`/chat/${session.id}`}>
|
|
||||||
<MessageSquare className="mr-1 h-3.5 w-3.5" />
|
|
||||||
聊天
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})()}
|
|
||||||
{order.status === "completed" && (
|
|
||||||
<Button variant="outline" size="sm" asChild>
|
|
||||||
<Link href={`/order/new?serviceId=${order.service.id}`}>
|
|
||||||
<RefreshCw className="mr-1 h-3.5 w-3.5" />
|
|
||||||
再来一单
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button size="sm" asChild>
|
|
||||||
<Link href={`/order/${order.id}`}>查看详情</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))
|
) : (
|
||||||
)}
|
filtered.map((order) => (
|
||||||
</TabsContent>
|
<Card key={order.id} className="hover:shadow-card-hover">
|
||||||
</Tabs>
|
<CardHeader className="pb-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<CardTitle className="text-base">{order.service.title}</CardTitle>
|
||||||
|
<Badge className={cn("text-xs", statusColors[order.status])}>
|
||||||
|
{statusLabels[order.status]}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">{order.service.title}</p>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
||||||
|
<span className="font-medium text-foreground">¥{order.totalPrice}</span>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Clock className="h-3.5 w-3.5" />
|
||||||
|
{new Date(order.createdAt).toLocaleDateString("zh-CN")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{(() => {
|
||||||
|
if (order.status !== "in_progress" && order.status !== "pending_close") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const session = sessions.find(
|
||||||
|
(item) => item.type === "order" && item.orderId === order.id,
|
||||||
|
)
|
||||||
|
if (!session) return null
|
||||||
|
return (
|
||||||
|
<Button variant="outline" size="sm" asChild>
|
||||||
|
<Link href={`/chat/${session.id}`}>
|
||||||
|
<MessageSquare className="mr-1 h-3.5 w-3.5" />
|
||||||
|
聊天
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})()}
|
||||||
|
{order.status === "completed" && (
|
||||||
|
<Button variant="outline" size="sm" asChild>
|
||||||
|
<Link href={`/order/new?serviceId=${order.service.id}`}>
|
||||||
|
<RefreshCw className="mr-1 h-3.5 w-3.5" />
|
||||||
|
再来一单
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button size="sm" asChild>
|
||||||
|
<Link href={`/order/${order.id}`}>查看详情</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export function Header() {
|
|||||||
const ownerShop = useShopStore((state) =>
|
const ownerShop = useShopStore((state) =>
|
||||||
user ? state.shops.find((shop) => shop.owner.id === user.id) : undefined,
|
user ? state.shops.find((shop) => shop.owner.id === user.id) : undefined,
|
||||||
)
|
)
|
||||||
|
const canOpenDashboard = currentRole === "player" || currentRole === "owner"
|
||||||
|
|
||||||
const navLinks =
|
const navLinks =
|
||||||
currentRole === "consumer"
|
currentRole === "consumer"
|
||||||
@@ -64,7 +65,7 @@ export function Header() {
|
|||||||
{ href: "/community", label: "社区" },
|
{ href: "/community", label: "社区" },
|
||||||
{ href: "/orders", label: "订单" },
|
{ href: "/orders", label: "订单" },
|
||||||
{ href: "/chat", label: "消息" },
|
{ href: "/chat", label: "消息" },
|
||||||
{ href: "/dashboard", label: "管理后台" },
|
...(canOpenDashboard ? [{ href: "/dashboard", label: "管理后台" }] : []),
|
||||||
]
|
]
|
||||||
|
|
||||||
const availableRoles = (Object.entries(roleLabels) as [UserRole, string][]).filter(([role]) =>
|
const availableRoles = (Object.entries(roleLabels) as [UserRole, string][]).filter(([role]) =>
|
||||||
@@ -209,7 +210,7 @@ export function Header() {
|
|||||||
钱包
|
钱包
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{(currentRole === "player" || currentRole === "owner") && (
|
{canOpenDashboard && (
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href="/dashboard">
|
<Link href="/dashboard">
|
||||||
<Gamepad2 className="mr-2 h-4 w-4" />
|
<Gamepad2 className="mr-2 h-4 w-4" />
|
||||||
|
|||||||
Reference in New Issue
Block a user