feat(orders): migrate orders to backend API

This commit is contained in:
zetaloop
2026-02-28 18:13:42 +08:00
parent e94a7e68ff
commit 9739c94bdc
9 changed files with 263 additions and 130 deletions
+35 -2
View File
@@ -4,11 +4,11 @@ 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 { getOrderById } from "@/lib/api"
import { ORDER_ACCEPT_TIMEOUT_MS, ORDER_CLOSE_TIMEOUT_MS } from "@/lib/config/demo-timers"
import { statusLabels } from "@/lib/constants"
import type { OrderStatus } from "@/lib/types"
import { useChatStore } from "@/store/chat"
import { useOrderStore } from "@/store/orders"
import { useReviewStore } from "@/store/reviews"
import { ArrowLeft, CheckCircle, Clock, Star } from "lucide-react"
import Link from "next/link"
@@ -35,7 +35,6 @@ const cancelledStatusSteps: OrderStatus[] = ["pending_payment", "pending_accept"
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 allReviews = useReviewStore((state) => state.reviews)
// Filtering is deferred to useMemo after reading the raw store array.
@@ -43,8 +42,34 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri
// Returning a fresh filtered array from the selector can re-trigger updates
// and loop under useSyncExternalStore (pmndrs/zustand#1936, #3155).
const reviews = useMemo(() => allReviews.filter((item) => item.orderId === id), [allReviews, id])
const [order, setOrder] = useState<Awaited<ReturnType<typeof getOrderById>> | undefined>(
undefined,
)
const [loading, setLoading] = useState(true)
const [nowTs, setNowTs] = useState(0)
useEffect(() => {
let cancelled = false
setLoading(true)
;(async () => {
try {
const order = await Promise.resolve(getOrderById(id))
if (cancelled) return
setOrder(order)
setLoading(false)
} catch {
if (cancelled) return
setOrder(undefined)
setLoading(false)
}
})()
return () => {
cancelled = true
}
}, [id])
useEffect(() => {
if (!order) return
if (order.status !== "pending_accept" && order.status !== "pending_close") return
@@ -56,6 +81,12 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri
return () => clearInterval(timer)
}, [order])
if (loading) {
return (
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">...</div>
)
}
if (!order) {
return (
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
@@ -257,6 +288,8 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri
<OrderActions
orderId={order.id}
order={order}
onOrderChange={(next) => setOrder(next)}
initialStatus={order.status}
chatSessionId={chatSession?.id}
serviceId={order.service.id}
+15 -26
View File
@@ -7,13 +7,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 type { Actor } from "@/lib/actor"
import { getPlayerById, getServiceById } from "@/lib/api"
import { createPaidOrder } from "@/lib/api/orders"
import { notifySuccess } from "@/lib/toast"
import type { Player, PlayerService } from "@/lib/types"
import { useRequireAuth } from "@/lib/use-require-auth"
import { useAuthStore } from "@/store/auth"
import { useOrderStore } from "@/store/orders"
import { useWalletStore } from "@/store/wallet"
import { ArrowLeft, CheckCircle, CreditCard, ShieldCheck } from "lucide-react"
import Link from "next/link"
@@ -24,7 +22,6 @@ export default function NewOrderPage() {
const router = useRouter()
const searchParams = useSearchParams()
const { requireAuth } = useRequireAuth()
const createPaidOrder = useOrderStore((state) => state.createPaidOrder)
const balance = useWalletStore((state) => state.balance)
const serviceId = searchParams.get("serviceId")
@@ -234,34 +231,26 @@ export default function NewOrderPage() {
disabled={balance < totalPrice}
onClick={() =>
requireAuth(() => {
const authUser = useAuthStore.getState().user
if (!authUser) return
const actor: Actor = {
userId: authUser.id,
role: "consumer",
}
const result = createPaidOrder(
{
Promise.resolve(
createPaidOrder({
playerId: player.id,
serviceId: service.id,
shopId: player.shopId,
quantity,
note,
},
actor,
)
if (!result.decision.ok || !result.order) {
return
}
const nextOrder = result.order
}),
).then((result) => {
if (!result.decision.ok || !result.order) {
return
}
const nextOrder = result.order
setSubmitted(true)
notifySuccess("下单成功")
setTimeout(() => {
router.push(`/order/${nextOrder.id}`)
}, 800)
setSubmitted(true)
notifySuccess("下单成功")
setTimeout(() => {
router.push(`/order/${nextOrder.id}`)
}, 800)
})
})
}
>
+22 -3
View File
@@ -4,6 +4,7 @@ import { Badge } from "@/components/ui/badge"
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 { listOrders } from "@/lib/api"
import { statusLabels } from "@/lib/constants"
import {
isActiveOrder,
@@ -16,11 +17,10 @@ import type { OrderStatus, UserRole } from "@/lib/types"
import { cn } from "@/lib/utils"
import { useAuthStore } from "@/store/auth"
import { useChatStore } from "@/store/chat"
import { useOrderStore } from "@/store/orders"
import { useShopStore } from "@/store/shops"
import { Clock, MessageSquare, RefreshCw } from "lucide-react"
import Link from "next/link"
import { useState } from "react"
import { useEffect, useState } from "react"
const statusColors: Record<OrderStatus, string> = {
pending_payment: "bg-yellow-100 text-yellow-800",
@@ -82,9 +82,28 @@ function OrderListContent({
ownerShopId?: string
}) {
const [tab, setTab] = useState<TabFilter | "pending">("all")
const orders = useOrderStore((state) => state.orders)
const [orders, setOrders] = useState<Awaited<ReturnType<typeof listOrders>>>([])
const sessions = useChatStore((state) => state.sessions)
useEffect(() => {
let cancelled = false
;(async () => {
try {
const items = await Promise.resolve(listOrders())
if (cancelled) return
setOrders(items)
} catch {
if (cancelled) return
setOrders([])
}
})()
return () => {
cancelled = true
}
}, [])
const tabs =
currentRole === "consumer" ? consumerTabs : currentRole === "player" ? playerTabs : ownerTabs