refactor: align client state and ui with backend contract
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
const cached = window.localStorage.getItem(cacheKey)
|
||||||
|
if (cached) {
|
||||||
|
joinSession(cached)
|
||||||
|
} else {
|
||||||
createDM(targetUserId)
|
createDM(targetUserId)
|
||||||
}, [connected, createDM, targetUserId])
|
}
|
||||||
|
}, [connected, cacheKey, createDM, joinSession, targetUserId])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!sessionId || !cacheKey) return
|
||||||
|
window.localStorage.setItem(cacheKey, sessionId)
|
||||||
|
}, [sessionId, cacheKey])
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => () => {
|
() => () => {
|
||||||
|
|||||||
+1
-1
@@ -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
@@ -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,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user