feat: integrate React Query for data fetching and state management across dashboard components

This commit is contained in:
2026-03-11 00:15:07 +08:00
parent 984274bfb7
commit eac81d2fab
9 changed files with 121 additions and 154 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { use, useCallback, useEffect, useState } from "react";
import { use, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import Link from "next/link";
import {
Button,
@@ -93,14 +94,8 @@ type EditForm = {
export default function ChargePointDetailPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = use(params);
const [cp, setCp] = useState<ChargePointDetail | null>(null);
const [notFound, setNotFound] = useState(false);
const [loading, setLoading] = useState(true);
// transactions
const [txData, setTxData] = useState<PaginatedTransactions | null>(null);
const [txPage, setTxPage] = useState(1);
const [txLoading, setTxLoading] = useState(true);
// edit modal
const [editOpen, setEditOpen] = useState(false);
@@ -112,42 +107,22 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id
feePerKwh: "0",
});
const loadCp = useCallback(async () => {
setLoading(true);
try {
const data = await api.chargePoints.get(id);
setCp(data);
} catch {
setNotFound(true);
} finally {
setLoading(false);
}
}, [id]);
const cpQuery = useQuery({
queryKey: ["chargePoint", id],
queryFn: () => api.chargePoints.get(id),
refetchInterval: 3_000,
retry: false,
});
const loadTx = useCallback(
async (p: number) => {
setTxLoading(true);
try {
const data = await api.transactions.list({
page: p,
limit: TX_LIMIT,
chargePointId: id,
});
setTxData(data);
} finally {
setTxLoading(false);
}
},
[id],
);
const txQuery = useQuery({
queryKey: ["chargePointTransactions", id, txPage],
queryFn: () =>
api.transactions.list({ page: txPage, limit: TX_LIMIT, chargePointId: id }),
refetchInterval: 3_000,
});
useEffect(() => {
loadCp();
}, [loadCp]);
useEffect(() => {
loadTx(txPage);
}, [txPage, loadTx]);
const cp = cpQuery.data;
const txData = txQuery.data;
const openEdit = () => {
if (!cp) return;
@@ -165,13 +140,13 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id
setEditBusy(true);
try {
const fee = Math.max(0, Math.round(Number(editForm.feePerKwh) || 0));
const updated = await api.chargePoints.update(cp.id, {
await api.chargePoints.update(cp.id, {
chargePointVendor: editForm.chargePointVendor,
chargePointModel: editForm.chargePointModel,
registrationStatus: editForm.registrationStatus,
feePerKwh: fee,
});
setCp((prev) => (prev ? { ...prev, ...updated, connectors: prev.connectors } : prev));
await cpQuery.refetch();
setEditOpen(false);
} finally {
setEditBusy(false);
@@ -188,7 +163,7 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id
// ── Render: loading / not found ──────────────────────────────────────────
if (loading) {
if (cpQuery.isPending) {
return (
<div className="flex h-48 items-center justify-center">
<Spinner />
@@ -196,7 +171,7 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id
);
}
if (notFound || !cp) {
if (!cp) {
return (
<div className="space-y-4">
<Link
@@ -463,7 +438,7 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id
<Table.Body
renderEmptyState={() => (
<div className="py-8 text-center text-sm text-muted">
{txLoading ? "加载中…" : "暂无充电记录"}
{txQuery.isPending ? "加载中…" : "暂无充电记录"}
</div>
)}
>