feat: integrate React Query for data fetching and state management across dashboard components
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Card, Spinner } from "@heroui/react";
|
||||
import {
|
||||
Thunderbolt,
|
||||
@@ -191,37 +191,21 @@ export default function DashboardPage() {
|
||||
const { data: sessionData, isPending } = useSession();
|
||||
const isAdmin = sessionData?.user?.role === "admin";
|
||||
|
||||
const [adminStats, setAdminStats] = useState<Stats | null>(null);
|
||||
const [userStats, setUserStats] = useState<UserStats | null>(null);
|
||||
const [recentTxns, setRecentTxns] = useState<Transaction[]>([]);
|
||||
const [chargePoints, setChargePoints] = useState<ChargePoint[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
if (isPending) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const [statsData, txnsData, cpsData] = await Promise.all([
|
||||
const { data, isPending: queryPending } = useQuery({
|
||||
queryKey: ["dashboard", isAdmin],
|
||||
queryFn: async () => {
|
||||
const [statsRes, txRes, cpsData] = await Promise.all([
|
||||
api.stats.get(),
|
||||
api.transactions.list({ limit: 6 }),
|
||||
isAdmin ? api.chargePoints.list() : Promise.resolve(null),
|
||||
isAdmin ? api.chargePoints.list() : Promise.resolve([] as ChargePoint[]),
|
||||
]);
|
||||
if ("totalChargePoints" in statsData) {
|
||||
setAdminStats(statsData as Stats);
|
||||
} else {
|
||||
setUserStats(statsData as UserStats);
|
||||
}
|
||||
setRecentTxns(txnsData.data);
|
||||
if (cpsData) setChargePoints(cpsData);
|
||||
} catch {}
|
||||
finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [isPending, isAdmin]);
|
||||
return { stats: statsRes, txns: txRes.data, cps: cpsData };
|
||||
},
|
||||
refetchInterval: 3_000,
|
||||
enabled: !isPending,
|
||||
});
|
||||
|
||||
useEffect(() => { void load(); }, [load]);
|
||||
|
||||
if (isPending || loading) {
|
||||
if (isPending || queryPending) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
@@ -238,7 +222,7 @@ export default function DashboardPage() {
|
||||
// ── Admin view ────────────────────────────────────────────────────────────
|
||||
|
||||
if (isAdmin) {
|
||||
const s = adminStats;
|
||||
const s = data?.stats as Stats | undefined;
|
||||
const todayKwh = s ? (s.todayEnergyWh / 1000).toFixed(1) : "—";
|
||||
const todayRevenue = s ? `¥${(s.todayRevenue / 100).toFixed(2)}` : "—";
|
||||
const offlineCount = (s?.totalChargePoints ?? 0) - (s?.onlineChargePoints ?? 0);
|
||||
@@ -327,12 +311,12 @@ export default function DashboardPage() {
|
||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-5">
|
||||
<div className="lg:col-span-2">
|
||||
<Panel title="充电桩状态">
|
||||
<ChargePointStatus cps={chargePoints} />
|
||||
<ChargePointStatus cps={data?.cps ?? []} />
|
||||
</Panel>
|
||||
</div>
|
||||
<div className="lg:col-span-3">
|
||||
<Panel title="最近充电会话">
|
||||
<RecentTransactions txns={recentTxns} />
|
||||
<RecentTransactions txns={data?.txns ?? []} />
|
||||
</Panel>
|
||||
</div>
|
||||
</div>
|
||||
@@ -342,7 +326,7 @@ export default function DashboardPage() {
|
||||
|
||||
// ── User view ─────────────────────────────────────────────────────────────
|
||||
|
||||
const s = userStats;
|
||||
const s = data?.stats as UserStats | undefined;
|
||||
const totalYuan = s ? (s.totalBalance / 100).toFixed(2) : "—";
|
||||
const todayKwh = s ? (s.todayEnergyWh / 1000).toFixed(2) : "—";
|
||||
|
||||
@@ -394,7 +378,7 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
|
||||
<Panel title="最近充电记录">
|
||||
<RecentTransactions txns={recentTxns} />
|
||||
<RecentTransactions txns={data?.txns ?? []} />
|
||||
</Panel>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user