refactor: align client state and ui with backend contract

This commit is contained in:
zetaloop
2026-05-03 05:40:01 +08:00
parent a3f0b49112
commit 0e7270aa8d
4 changed files with 51 additions and 9 deletions
+26 -2
View File
@@ -5,7 +5,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { EmptyState } from "@/components/ui/empty-state" import { EmptyState } from "@/components/ui/empty-state"
import { Progress } from "@/components/ui/progress" import { Progress } from "@/components/ui/progress"
import { StatusBadge, type StatusBadgeProps } from "@/components/ui/status-badge" import { StatusBadge, type StatusBadgeProps } from "@/components/ui/status-badge"
import { listOrders, listPlayers, listServices, listShops } from "@/lib/api" import { getShopIncomeStats, listOrders, listPlayers, listServices, listShops } from "@/lib/api"
import { statusLabels } from "@/lib/constants" import { statusLabels } from "@/lib/constants"
import type { Player, PlayerService, Shop } from "@/lib/types" import type { Player, PlayerService, Shop } from "@/lib/types"
import { useAuthStore } from "@/store/auth" import { useAuthStore } from "@/store/auth"
@@ -40,6 +40,7 @@ export default function DashboardPage() {
const [shop, setShop] = useState<Shop | null>(null) const [shop, setShop] = useState<Shop | null>(null)
const [services, setServices] = useState<PlayerService[]>([]) const [services, setServices] = useState<PlayerService[]>([])
const [orders, setOrders] = useState<Awaited<ReturnType<typeof listOrders>>>([]) const [orders, setOrders] = useState<Awaited<ReturnType<typeof listOrders>>>([])
const [monthlyIncome, setMonthlyIncome] = useState<string>("0")
const recentOrders = orders.slice(0, 3) const recentOrders = orders.slice(0, 3)
useEffect(() => { useEffect(() => {
@@ -88,6 +89,29 @@ export default function DashboardPage() {
} }
}, [orderRole]) }, [orderRole])
useEffect(() => {
if (!shop) {
setMonthlyIncome("0")
return
}
let cancelled = false
getShopIncomeStats(shop.id)
.then((stats) => {
if (cancelled) return
setMonthlyIncome(stats.monthlyIncome)
})
.catch(() => {
if (cancelled) return
setMonthlyIncome("0")
})
return () => {
cancelled = true
}
}, [shop])
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)
const playerCount = shop?.playerCount ?? 0 const playerCount = shop?.playerCount ?? 0
@@ -149,7 +173,7 @@ export default function DashboardPage() {
)} )}
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="text-2xl font-bold">{isOwner ? "——" : serviceCount}</div> <div className="text-2xl font-bold">{isOwner ? `¥${monthlyIncome}` : serviceCount}</div>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
+16 -3
View File
@@ -27,15 +27,28 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
messages, messages,
error, error,
createDM, createDM,
joinSession,
leaveSession, leaveSession,
sendTextMessage, sendTextMessage,
sendImageMessage, sendImageMessage,
} = useChatSocket() } = useChatSocket()
const cacheKey = user?.id ? `chat:dm:${user.id}:${targetUserId}` : null
useEffect(() => { useEffect(() => {
if (!connected) return if (!connected || !cacheKey) return
createDM(targetUserId) const cached = window.localStorage.getItem(cacheKey)
}, [connected, createDM, targetUserId]) if (cached) {
joinSession(cached)
} else {
createDM(targetUserId)
}
}, [connected, cacheKey, createDM, joinSession, targetUserId])
useEffect(() => {
if (!sessionId || !cacheKey) return
window.localStorage.setItem(cacheKey, sessionId)
}, [sessionId, cacheKey])
useEffect( useEffect(
() => () => { () => () => {
+1 -1
View File
@@ -168,7 +168,7 @@ export interface Post {
content: string content: string
images: string[] images: string[]
tags: string[] tags: string[]
linkedOrderId?: number linkedOrderId?: SnowflakeId
pinned: boolean pinned: boolean
likeCount: number likeCount: number
commentCount: number commentCount: number
+8 -3
View File
@@ -22,6 +22,7 @@ interface PersistedAuth {
currentRole: UserRole currentRole: UserRole
verifiedRoles: UserRole[] verifiedRoles: UserRole[]
verificationStatus: Partial<Record<UserRole, VerificationStatus>> verificationStatus: Partial<Record<UserRole, VerificationStatus>>
notificationPrefs: NotificationPrefs
themePreference: ThemePreference themePreference: ThemePreference
} }
@@ -50,6 +51,7 @@ function persistCurrent(state: AuthState) {
currentRole: state.currentRole, currentRole: state.currentRole,
verifiedRoles: state.verifiedRoles, verifiedRoles: state.verifiedRoles,
verificationStatus: state.verificationStatus, verificationStatus: state.verificationStatus,
notificationPrefs: state.notificationPrefs,
themePreference: state.themePreference, themePreference: state.themePreference,
}) })
} }
@@ -95,13 +97,15 @@ export const useAuthStore = create<AuthState>((set, get) => ({
persistCurrent(get()) persistCurrent(get())
} }
}, },
setNotificationPref: (type, enabled) => setNotificationPref: (type, enabled) => {
set((state) => ({ set((state) => ({
notificationPrefs: { notificationPrefs: {
...state.notificationPrefs, ...state.notificationPrefs,
[type]: enabled, [type]: enabled,
}, },
})), }))
persistCurrent(get())
},
setThemePreference: (theme) => { setThemePreference: (theme) => {
set({ themePreference: theme }) set({ themePreference: theme })
persistCurrent(get()) persistCurrent(get())
@@ -138,6 +142,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
currentRole: user.role, currentRole: user.role,
verifiedRoles: nextVerifiedRoles, verifiedRoles: nextVerifiedRoles,
verificationStatus: nextVerificationStatus, verificationStatus: nextVerificationStatus,
notificationPrefs: state.notificationPrefs,
themePreference: nextTheme, themePreference: nextTheme,
}) })
@@ -160,7 +165,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
verifiedRoles: ["consumer"], verifiedRoles: ["consumer"],
verificationStatus: { consumer: "approved" }, verificationStatus: { consumer: "approved" },
verificationReasons: {}, verificationReasons: {},
notificationPrefs: defaultNotificationPrefs, notificationPrefs: persisted?.notificationPrefs ?? defaultNotificationPrefs,
themePreference: "system", themePreference: "system",
user: null, user: null,
}) })