150 lines
3.9 KiB
TypeScript
150 lines
3.9 KiB
TypeScript
import { create } from "zustand"
|
|
import { generateId } from "@/lib/id"
|
|
import { mockOrders } from "@/lib/mock"
|
|
import type { Order, OrderStatus, PlayerService } from "@/lib/types"
|
|
import { useWalletStore } from "@/store/wallet"
|
|
|
|
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
|
|
}
|
|
|
|
const orderTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
|
|
|
function clearOrderTimeout(orderId: string) {
|
|
const timer = orderTimeouts.get(orderId)
|
|
if (!timer) return
|
|
clearTimeout(timer)
|
|
orderTimeouts.delete(orderId)
|
|
}
|
|
|
|
function scheduleOrderTimeout(orderId: string, status: OrderStatus) {
|
|
clearOrderTimeout(orderId)
|
|
|
|
if (status !== "pending_accept" && status !== "pending_close") {
|
|
return
|
|
}
|
|
|
|
const timer = setTimeout(() => {
|
|
const state = useOrderStore.getState()
|
|
const order = state.orders.find((item) => item.id === orderId)
|
|
if (!order || order.status !== status) {
|
|
orderTimeouts.delete(orderId)
|
|
return
|
|
}
|
|
|
|
if (status === "pending_accept") {
|
|
state.updateOrderStatus(orderId, "cancelled")
|
|
}
|
|
|
|
if (status === "pending_close") {
|
|
state.updateOrderStatus(orderId, "pending_review")
|
|
}
|
|
|
|
orderTimeouts.delete(orderId)
|
|
}, 30000)
|
|
|
|
orderTimeouts.set(orderId, timer)
|
|
}
|
|
|
|
export const useOrderStore = create<OrderState>((set) => ({
|
|
orders: mockOrders,
|
|
createOrder: (input) => {
|
|
const order: Order = {
|
|
id: generateId("ord"),
|
|
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],
|
|
}))
|
|
|
|
scheduleOrderTimeout(order.id, order.status)
|
|
|
|
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_close":
|
|
return {
|
|
...order,
|
|
status,
|
|
closedAt: order.closedAt ?? now,
|
|
}
|
|
case "pending_review":
|
|
return {
|
|
...order,
|
|
status,
|
|
closedAt: order.closedAt ?? now,
|
|
}
|
|
case "completed":
|
|
if (order.status !== "completed") {
|
|
useWalletStore.getState().addIncome(order.id, order.totalPrice)
|
|
}
|
|
return {
|
|
...order,
|
|
status,
|
|
closedAt: order.closedAt ?? now,
|
|
completedAt: order.completedAt ?? now,
|
|
}
|
|
default:
|
|
return {
|
|
...order,
|
|
status,
|
|
}
|
|
}
|
|
}),
|
|
})),
|
|
}))
|
|
|
|
useOrderStore.subscribe((state, prevState) => {
|
|
state.orders.forEach((order) => {
|
|
const prevOrder = prevState.orders.find((item) => item.id === order.id)
|
|
if (!prevOrder || prevOrder.status !== order.status) {
|
|
scheduleOrderTimeout(order.id, order.status)
|
|
}
|
|
})
|
|
|
|
prevState.orders.forEach((order) => {
|
|
if (!state.orders.some((item) => item.id === order.id)) {
|
|
clearOrderTimeout(order.id)
|
|
}
|
|
})
|
|
})
|