feat: wire order and chat state flow

This commit is contained in:
zetaloop
2026-02-22 06:40:40 +08:00
parent 4ce7303258
commit 02269dd9c3
10 changed files with 372 additions and 57 deletions
+102
View File
@@ -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,
),
}))
},
}))
+83
View File
@@ -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,
}
}
}),
})),
}))