feat: wire order and chat state flow
This commit is contained in:
@@ -8,14 +8,15 @@ import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { mockChatMessages, mockChatSessions } from "@/lib/mock"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useChatStore } from "@/store/chat"
|
||||
|
||||
export default function ChatDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = use(params)
|
||||
const session = mockChatSessions.find((s) => s.id === id)
|
||||
const messages = mockChatMessages.filter((m) => m.sessionId === id)
|
||||
const session = useChatStore((state) => state.sessions.find((item) => item.id === id))
|
||||
const messages = useChatStore((state) => state.messages.filter((item) => item.sessionId === id))
|
||||
const sendTextMessage = useChatStore((state) => state.sendTextMessage)
|
||||
const [input, setInput] = useState("")
|
||||
const { user } = useAuthStore()
|
||||
|
||||
@@ -103,6 +104,21 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
|
||||
className="flex gap-2 max-w-2xl mx-auto"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
const text = input.trim()
|
||||
if (!text) return
|
||||
|
||||
const sender = session.participants.find(
|
||||
(participant) => participant.id === currentUserId,
|
||||
)
|
||||
sendTextMessage(
|
||||
session.id,
|
||||
{
|
||||
id: currentUserId,
|
||||
name: sender?.name ?? user?.nickname ?? "",
|
||||
avatar: sender?.avatar ?? user?.avatar ?? "",
|
||||
},
|
||||
text,
|
||||
)
|
||||
setInput("")
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
"use client"
|
||||
|
||||
import { Lock, MessageSquare } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { mockChatSessions } from "@/lib/mock"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useChatStore } from "@/store/chat"
|
||||
|
||||
export default function ChatListPage() {
|
||||
const sessions = useChatStore((state) => state.sessions)
|
||||
const userId = useAuthStore((state) => state.user?.id)
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-8 px-4 max-w-2xl">
|
||||
<h1 className="text-2xl font-bold mb-6">消息</h1>
|
||||
|
||||
<div className="space-y-2">
|
||||
{mockChatSessions.map((session) => {
|
||||
const other = session.participants[1]
|
||||
{sessions.map((session) => {
|
||||
const other =
|
||||
session.participants.find((participant) => participant.id !== userId) ??
|
||||
session.participants[0]
|
||||
return (
|
||||
<Link key={session.id} href={`/chat/${session.id}`}>
|
||||
<Card className="hover:bg-accent/30 transition-colors">
|
||||
@@ -49,7 +57,7 @@ export default function ChatListPage() {
|
||||
)
|
||||
})}
|
||||
|
||||
{mockChatSessions.length === 0 && (
|
||||
{sessions.length === 0 && (
|
||||
<div className="text-center py-12 text-muted-foreground">
|
||||
<MessageSquare className="h-12 w-12 mx-auto mb-2 opacity-50" />
|
||||
暂无消息
|
||||
|
||||
@@ -11,7 +11,8 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { mockDisputes, mockOrders } from "@/lib/mock"
|
||||
import { mockDisputes } from "@/lib/mock"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
|
||||
const disputeStatusLabels: Record<string, string> = {
|
||||
open: "已提交",
|
||||
@@ -24,7 +25,8 @@ export default function DisputePage({ params }: { params: Promise<{ id: string }
|
||||
const { id } = use(params)
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const order = mockOrders.find((o) => o.id === id)
|
||||
const order = useOrderStore((state) => state.orders.find((item) => item.id === id))
|
||||
const updateOrderStatus = useOrderStore((state) => state.updateOrderStatus)
|
||||
const existingDispute = mockDisputes.find((d) => d.orderId === id)
|
||||
const [reason, setReason] = useState("")
|
||||
const [submitted, setSubmitted] = useState(false)
|
||||
@@ -70,6 +72,7 @@ export default function DisputePage({ params }: { params: Promise<{ id: string }
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
updateOrderStatus(id, "disputed")
|
||||
setSubmitted(true)
|
||||
router.replace(`/dispute/${id}?submitted=1`)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowLeft, CheckCircle, Clock, Star } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { notFound } from "next/navigation"
|
||||
import { use, useEffect } from "react"
|
||||
import OrderActions from "@/components/order-actions"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { statusLabels } from "@/lib/constants"
|
||||
import { mockChatSessions, mockOrders, mockReviews } from "@/lib/mock"
|
||||
import { mockReviews } from "@/lib/mock"
|
||||
import type { OrderStatus } from "@/lib/types"
|
||||
import { useChatStore } from "@/store/chat"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
|
||||
const normalStatusSteps: OrderStatus[] = [
|
||||
"pending_payment",
|
||||
@@ -28,13 +32,28 @@ const disputedStatusSteps: OrderStatus[] = [
|
||||
|
||||
const cancelledStatusSteps: OrderStatus[] = ["pending_payment", "pending_accept", "cancelled"]
|
||||
|
||||
export default async function OrderDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params
|
||||
const order = mockOrders.find((o) => o.id === id)
|
||||
if (!order) notFound()
|
||||
export default function OrderDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = use(params)
|
||||
const order = useOrderStore((state) => state.orders.find((item) => item.id === id))
|
||||
const sessions = useChatStore((state) => state.sessions)
|
||||
const ensureOrderSession = useChatStore((state) => state.ensureOrderSession)
|
||||
|
||||
useEffect(() => {
|
||||
if (!order) return
|
||||
if (order.status === "pending_payment" || order.status === "cancelled") return
|
||||
ensureOrderSession(order)
|
||||
}, [order, ensureOrderSession])
|
||||
|
||||
if (!order) {
|
||||
return (
|
||||
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
|
||||
订单不存在
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const reviews = mockReviews.filter((r) => r.orderId === id)
|
||||
const chatSession = mockChatSessions.find((s) => s.orderId === id)
|
||||
const chatSession = sessions.find((session) => session.type === "order" && session.orderId === id)
|
||||
const statusSteps =
|
||||
order.status === "disputed"
|
||||
? disputedStatusSteps
|
||||
|
||||
@@ -11,8 +11,11 @@ import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { mockOrders, mockPlayers, mockServices, walletBalance } from "@/lib/mock"
|
||||
import { mockPlayers, mockServices, walletBalance } from "@/lib/mock"
|
||||
import { useRequireAuth } from "@/lib/use-require-auth"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useChatStore } from "@/store/chat"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
|
||||
function showFeedback(message: string) {
|
||||
if (typeof window === "undefined") return
|
||||
@@ -23,6 +26,8 @@ export default function NewOrderPage() {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const { requireAuth } = useRequireAuth()
|
||||
const createOrder = useOrderStore((state) => state.createOrder)
|
||||
const ensureOrderSession = useChatStore((state) => state.ensureOrderSession)
|
||||
const serviceId = searchParams.get("serviceId")
|
||||
|
||||
const service = mockServices.find((s) => s.id === serviceId)
|
||||
@@ -41,8 +46,6 @@ export default function NewOrderPage() {
|
||||
}
|
||||
|
||||
const totalPrice = service.price * quantity
|
||||
const redirectOrderId =
|
||||
mockOrders.find((order) => order.service.id === service.id)?.id ?? mockOrders[0]?.id
|
||||
|
||||
if (submitted) {
|
||||
return (
|
||||
@@ -186,15 +189,28 @@ export default function NewOrderPage() {
|
||||
onClick={() =>
|
||||
requireAuth(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
const currentUser = useAuthStore.getState().user
|
||||
if (!currentUser) return
|
||||
|
||||
const order = createOrder({
|
||||
consumerId: currentUser.id,
|
||||
consumerName: currentUser.nickname,
|
||||
playerId: player.id,
|
||||
playerName: player.user.nickname,
|
||||
shopId: player.shopId,
|
||||
shopName: player.shopName,
|
||||
service,
|
||||
totalPrice,
|
||||
note,
|
||||
status: "pending_accept",
|
||||
})
|
||||
|
||||
ensureOrderSession(order)
|
||||
setSubmitted(true)
|
||||
showFeedback("下单成功")
|
||||
if (redirectOrderId) {
|
||||
setTimeout(() => {
|
||||
router.push(`/order/${redirectOrderId}`)
|
||||
router.push(`/order/${order.id}`)
|
||||
}, 800)
|
||||
return
|
||||
}
|
||||
router.push("/orders")
|
||||
})
|
||||
}
|
||||
>
|
||||
|
||||
@@ -8,10 +8,11 @@ import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { statusLabels } from "@/lib/constants"
|
||||
import { mockChatSessions, mockOrders } from "@/lib/mock"
|
||||
import type { OrderStatus } from "@/lib/types"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useChatStore } from "@/store/chat"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
|
||||
const statusColors: Record<OrderStatus, string> = {
|
||||
pending_payment: "bg-yellow-100 text-yellow-800",
|
||||
@@ -51,6 +52,9 @@ const ownerTabs = [
|
||||
export default function OrderListPage() {
|
||||
const [tab, setTab] = useState<TabFilter | "pending">("all")
|
||||
const { currentRole, user } = useAuthStore()
|
||||
const orders = useOrderStore((state) => state.orders)
|
||||
const sessions = useChatStore((state) => state.sessions)
|
||||
const ensureOrderSession = useChatStore((state) => state.ensureOrderSession)
|
||||
const currentUserId = user?.id ?? "u1"
|
||||
const ownerShopId = "shop1"
|
||||
|
||||
@@ -62,7 +66,14 @@ export default function OrderListPage() {
|
||||
const tabs =
|
||||
currentRole === "consumer" ? consumerTabs : currentRole === "player" ? playerTabs : ownerTabs
|
||||
|
||||
const roleFiltered = mockOrders.filter((order) => {
|
||||
useEffect(() => {
|
||||
orders.forEach((order) => {
|
||||
if (order.status === "pending_payment" || order.status === "cancelled") return
|
||||
ensureOrderSession(order)
|
||||
})
|
||||
}, [orders, ensureOrderSession])
|
||||
|
||||
const roleFiltered = orders.filter((order) => {
|
||||
if (currentRole === "consumer") return order.consumerId === currentUserId
|
||||
if (currentRole === "player") return order.playerId === currentUserId
|
||||
return order.shopId === ownerShopId
|
||||
@@ -70,8 +81,15 @@ export default function OrderListPage() {
|
||||
|
||||
const filtered = roleFiltered.filter((order) => {
|
||||
if (tab === "pending") return order.status === "pending_accept"
|
||||
if (tab === "active")
|
||||
return ["in_progress", "pending_close", "pending_review"].includes(order.status)
|
||||
if (tab === "active") {
|
||||
return [
|
||||
"pending_payment",
|
||||
"pending_accept",
|
||||
"in_progress",
|
||||
"pending_close",
|
||||
"pending_review",
|
||||
].includes(order.status)
|
||||
}
|
||||
if (tab === "completed") return order.status === "completed" || order.status === "cancelled"
|
||||
if (tab === "disputed") return order.status === "disputed"
|
||||
return true
|
||||
@@ -130,11 +148,14 @@ export default function OrderListPage() {
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{order.status === "in_progress" &&
|
||||
(() => {
|
||||
const session = mockChatSessions.find((s) => s.orderId === order.id)
|
||||
{(() => {
|
||||
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 (
|
||||
session && (
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<Link href={`/chat/${session.id}`}>
|
||||
<MessageSquare className="mr-1 h-3.5 w-3.5" />
|
||||
@@ -142,7 +163,6 @@ export default function OrderListPage() {
|
||||
</Link>
|
||||
</Button>
|
||||
)
|
||||
)
|
||||
})()}
|
||||
{order.status === "completed" && (
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
|
||||
@@ -7,11 +7,12 @@ import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { mockOrders } from "@/lib/mock"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
|
||||
export default function ReviewPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = use(params)
|
||||
const order = mockOrders.find((o) => o.id === id)
|
||||
const order = useOrderStore((state) => state.orders.find((item) => item.id === id))
|
||||
const updateOrderStatus = useOrderStore((state) => state.updateOrderStatus)
|
||||
const [rating, setRating] = useState(0)
|
||||
const [hoverRating, setHoverRating] = useState(0)
|
||||
const [content, setContent] = useState("")
|
||||
@@ -97,7 +98,14 @@ export default function ReviewPage({ params }: { params: Promise<{ id: string }>
|
||||
<span>评价采用密封机制:你的评价将在双方都提交后同时揭晓。</span>
|
||||
</div>
|
||||
|
||||
<Button className="w-full" disabled={rating === 0} onClick={() => setSubmitted(true)}>
|
||||
<Button
|
||||
className="w-full"
|
||||
disabled={rating === 0}
|
||||
onClick={() => {
|
||||
updateOrderStatus(id, "completed")
|
||||
setSubmitted(true)
|
||||
}}
|
||||
>
|
||||
提交评价
|
||||
</Button>
|
||||
</CardContent>
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
import { AlertTriangle, CheckCircle2, MessageSquare, RefreshCw, Star, XCircle } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { useState } from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import type { OrderStatus } from "@/lib/types"
|
||||
import { useChatStore } from "@/store/chat"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
|
||||
interface OrderActionsProps {
|
||||
orderId: string
|
||||
@@ -24,16 +26,32 @@ export default function OrderActions({
|
||||
chatSessionId,
|
||||
serviceId,
|
||||
}: OrderActionsProps) {
|
||||
const [status, setStatus] = useState<OrderStatus>(initialStatus)
|
||||
const order = useOrderStore((state) => state.orders.find((item) => item.id === orderId))
|
||||
const updateOrderStatus = useOrderStore((state) => state.updateOrderStatus)
|
||||
const ensureOrderSession = useChatStore((state) => state.ensureOrderSession)
|
||||
const [resolvedChatSessionId, setResolvedChatSessionId] = useState(chatSessionId)
|
||||
|
||||
const status = order?.status ?? initialStatus
|
||||
|
||||
useEffect(() => {
|
||||
if (chatSessionId) {
|
||||
setResolvedChatSessionId(chatSessionId)
|
||||
return
|
||||
}
|
||||
|
||||
if (!order) return
|
||||
const session = ensureOrderSession(order)
|
||||
setResolvedChatSessionId(session.id)
|
||||
}, [chatSessionId, order, ensureOrderSession])
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{status === "pending_accept" && (
|
||||
{status === "pending_payment" && (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setStatus("cancelled")
|
||||
updateOrderStatus(orderId, "cancelled")
|
||||
showFeedback("订单已取消")
|
||||
}}
|
||||
>
|
||||
@@ -42,7 +60,30 @@ export default function OrderActions({
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setStatus("in_progress")
|
||||
updateOrderStatus(orderId, "pending_accept")
|
||||
}}
|
||||
>
|
||||
<CheckCircle2 className="mr-1 h-4 w-4" />
|
||||
确认支付
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{status === "pending_accept" && (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
updateOrderStatus(orderId, "cancelled")
|
||||
showFeedback("订单已取消")
|
||||
}}
|
||||
>
|
||||
<XCircle className="mr-1 h-4 w-4" />
|
||||
取消订单
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
updateOrderStatus(orderId, "in_progress")
|
||||
showFeedback("已接单")
|
||||
}}
|
||||
>
|
||||
@@ -52,9 +93,9 @@ export default function OrderActions({
|
||||
</>
|
||||
)}
|
||||
|
||||
{(status === "in_progress" || status === "pending_close") && chatSessionId && (
|
||||
{(status === "in_progress" || status === "pending_close") && resolvedChatSessionId && (
|
||||
<Button asChild>
|
||||
<Link href={`/chat/${chatSessionId}`}>
|
||||
<Link href={`/chat/${resolvedChatSessionId}`}>
|
||||
<MessageSquare className="mr-1 h-4 w-4" />
|
||||
聊天
|
||||
</Link>
|
||||
@@ -65,7 +106,7 @@ export default function OrderActions({
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setStatus("pending_close")
|
||||
updateOrderStatus(orderId, "pending_close")
|
||||
showFeedback("已发起结单")
|
||||
}}
|
||||
>
|
||||
@@ -84,8 +125,7 @@ export default function OrderActions({
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setStatus("completed")
|
||||
showFeedback("订单已完成")
|
||||
updateOrderStatus(orderId, "pending_review")
|
||||
}}
|
||||
>
|
||||
确认结单
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import { create } from "zustand"
|
||||
import { mockChatMessages, mockChatSessions, mockUsers } from "@/lib/mock"
|
||||
import type { ChatMessage, ChatSession, Order } from "@/lib/types"
|
||||
|
||||
interface Sender {
|
||||
id: string
|
||||
name: string
|
||||
avatar: string
|
||||
}
|
||||
|
||||
interface ChatState {
|
||||
sessions: ChatSession[]
|
||||
messages: ChatMessage[]
|
||||
ensureOrderSession: (order: Order) => ChatSession
|
||||
sendTextMessage: (sessionId: string, sender: Sender, content: string) => void
|
||||
}
|
||||
|
||||
function resolveAvatar(userId: string) {
|
||||
return mockUsers.find((user) => user.id === userId)?.avatar ?? ""
|
||||
}
|
||||
|
||||
function shouldReadonly(status: Order["status"]) {
|
||||
return status === "pending_review" || status === "completed" || status === "cancelled"
|
||||
}
|
||||
|
||||
export const useChatStore = create<ChatState>((set, get) => ({
|
||||
sessions: mockChatSessions,
|
||||
messages: mockChatMessages,
|
||||
ensureOrderSession: (order) => {
|
||||
const existing = get().sessions.find(
|
||||
(session) => session.type === "order" && session.orderId === order.id,
|
||||
)
|
||||
|
||||
const readonly = shouldReadonly(order.status)
|
||||
|
||||
if (existing) {
|
||||
if (existing.readonly !== readonly) {
|
||||
set((state) => ({
|
||||
sessions: state.sessions.map((session) =>
|
||||
session.id === existing.id ? { ...session, readonly } : session,
|
||||
),
|
||||
}))
|
||||
}
|
||||
return get().sessions.find((session) => session.id === existing.id) ?? existing
|
||||
}
|
||||
|
||||
const session: ChatSession = {
|
||||
id: `chat-${order.id}`,
|
||||
type: "order",
|
||||
orderId: order.id,
|
||||
participants: [
|
||||
{
|
||||
id: order.consumerId,
|
||||
name: order.consumerName,
|
||||
avatar: resolveAvatar(order.consumerId),
|
||||
},
|
||||
{
|
||||
id: order.playerId,
|
||||
name: order.playerName,
|
||||
avatar: resolveAvatar(order.playerId),
|
||||
},
|
||||
],
|
||||
unreadCount: 0,
|
||||
readonly,
|
||||
}
|
||||
|
||||
set((state) => ({
|
||||
sessions: [session, ...state.sessions],
|
||||
}))
|
||||
|
||||
return session
|
||||
},
|
||||
sendTextMessage: (sessionId, sender, content) => {
|
||||
const text = content.trim()
|
||||
if (!text) return
|
||||
|
||||
const now = new Date().toISOString()
|
||||
const message: ChatMessage = {
|
||||
id: `msg-${Date.now()}`,
|
||||
sessionId,
|
||||
senderId: sender.id,
|
||||
senderName: sender.name,
|
||||
senderAvatar: sender.avatar,
|
||||
type: "text",
|
||||
content: text,
|
||||
createdAt: now,
|
||||
}
|
||||
|
||||
set((state) => ({
|
||||
messages: [...state.messages, message],
|
||||
sessions: state.sessions.map((session) =>
|
||||
session.id === sessionId
|
||||
? {
|
||||
...session,
|
||||
lastMessage: text,
|
||||
lastMessageAt: now,
|
||||
}
|
||||
: session,
|
||||
),
|
||||
}))
|
||||
},
|
||||
}))
|
||||
@@ -0,0 +1,83 @@
|
||||
import { create } from "zustand"
|
||||
import { mockOrders } from "@/lib/mock"
|
||||
import type { Order, OrderStatus, PlayerService } from "@/lib/types"
|
||||
|
||||
interface CreateOrderInput {
|
||||
consumerId: string
|
||||
consumerName: string
|
||||
playerId: string
|
||||
playerName: string
|
||||
shopId?: string
|
||||
shopName?: string
|
||||
service: PlayerService
|
||||
totalPrice: number
|
||||
note?: string
|
||||
status?: OrderStatus
|
||||
}
|
||||
|
||||
interface OrderState {
|
||||
orders: Order[]
|
||||
createOrder: (input: CreateOrderInput) => Order
|
||||
updateOrderStatus: (orderId: string, status: OrderStatus) => void
|
||||
}
|
||||
|
||||
export const useOrderStore = create<OrderState>((set) => ({
|
||||
orders: mockOrders,
|
||||
createOrder: (input) => {
|
||||
const order: Order = {
|
||||
id: `ord${Date.now()}`,
|
||||
consumerId: input.consumerId,
|
||||
consumerName: input.consumerName,
|
||||
playerId: input.playerId,
|
||||
playerName: input.playerName,
|
||||
shopId: input.shopId,
|
||||
shopName: input.shopName,
|
||||
service: input.service,
|
||||
status: input.status ?? "pending_payment",
|
||||
totalPrice: input.totalPrice,
|
||||
note: input.note?.trim() ? input.note.trim() : undefined,
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
|
||||
set((state) => ({
|
||||
orders: [order, ...state.orders],
|
||||
}))
|
||||
|
||||
return order
|
||||
},
|
||||
updateOrderStatus: (orderId, status) =>
|
||||
set((state) => ({
|
||||
orders: state.orders.map((order) => {
|
||||
if (order.id !== orderId) return order
|
||||
|
||||
const now = new Date().toISOString()
|
||||
|
||||
switch (status) {
|
||||
case "in_progress":
|
||||
return {
|
||||
...order,
|
||||
status,
|
||||
acceptedAt: order.acceptedAt ?? now,
|
||||
}
|
||||
case "pending_review":
|
||||
return {
|
||||
...order,
|
||||
status,
|
||||
closedAt: order.closedAt ?? now,
|
||||
}
|
||||
case "completed":
|
||||
return {
|
||||
...order,
|
||||
status,
|
||||
closedAt: order.closedAt ?? now,
|
||||
completedAt: order.completedAt ?? now,
|
||||
}
|
||||
default:
|
||||
return {
|
||||
...order,
|
||||
status,
|
||||
}
|
||||
}
|
||||
}),
|
||||
})),
|
||||
}))
|
||||
Reference in New Issue
Block a user