mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-02-03 20:03:25 +01:00
fix(frontend): decouple table pagination from summary fetching (#11495)
Prevent the summary data from refetching unnecessarily when log pagination changes. - Split the data fetching into two separate useEffect hooks. - Summary is now fetched only on mount. - Logs refetch only when page or limit dependencies change. - Decoupled loading states to prevent chart loading animation during log updates.
This commit is contained in:
@@ -94,7 +94,8 @@ const chartConfigApps = {
|
|||||||
export default function DataPage() {
|
export default function DataPage() {
|
||||||
const [data, setData] = useState<DataModel[]>([]);
|
const [data, setData] = useState<DataModel[]>([]);
|
||||||
const [summary, setSummary] = useState<SummaryData | null>(null);
|
const [summary, setSummary] = useState<SummaryData | null>(null);
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [summaryLoading, setSummaryLoading] = useState<boolean>(true);
|
||||||
|
const [dataLoading, setDataLoading] = useState<boolean>(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [itemsPerPage, setItemsPerPage] = useState(25);
|
const [itemsPerPage, setItemsPerPage] = useState(25);
|
||||||
@@ -105,35 +106,40 @@ export default function DataPage() {
|
|||||||
|
|
||||||
const nf = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
|
const nf = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
|
||||||
|
|
||||||
|
// Fetch summary only once on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchSummary = async () => {
|
||||||
setLoading(true);
|
|
||||||
try {
|
try {
|
||||||
const [summaryRes, dataRes] = await Promise.all([
|
const summaryRes = await fetch("https://api.htl-braunau.at/data/summary");
|
||||||
fetch("https://api.htl-braunau.at/data/summary"),
|
|
||||||
fetch(
|
|
||||||
`https://api.htl-braunau.at/data/paginated?page=${currentPage}&limit=${
|
|
||||||
itemsPerPage === 0 ? "" : itemsPerPage
|
|
||||||
}`,
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!summaryRes.ok) {
|
if (!summaryRes.ok) {
|
||||||
throw new Error(`Failed to fetch summary: ${summaryRes.statusText}`);
|
throw new Error(`Failed to fetch summary: ${summaryRes.statusText}`);
|
||||||
}
|
}
|
||||||
|
const summaryData: SummaryData = await summaryRes.json();
|
||||||
|
setSummary(summaryData);
|
||||||
|
} catch (err) {
|
||||||
|
setError((err as Error).message);
|
||||||
|
} finally {
|
||||||
|
setSummaryLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchSummary();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchData = async () => {
|
||||||
|
setDataLoading(true);
|
||||||
|
try {
|
||||||
|
const dataRes = await fetch(`https://api.htl-braunau.at/data/paginated?page=${currentPage}&limit=${itemsPerPage}`);
|
||||||
if (!dataRes.ok) {
|
if (!dataRes.ok) {
|
||||||
throw new Error(`Failed to fetch data: ${dataRes.statusText}`);
|
throw new Error(`Failed to fetch data: ${dataRes.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const summaryData: SummaryData = await summaryRes.json();
|
|
||||||
const pageData: DataModel[] = await dataRes.json();
|
const pageData: DataModel[] = await dataRes.json();
|
||||||
|
|
||||||
setSummary(summaryData);
|
|
||||||
setData(pageData);
|
setData(pageData);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError((err as Error).message);
|
setError((err as Error).message);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setDataLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -306,7 +312,7 @@ export default function DataPage() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="pl-2">
|
<CardContent className="pl-2">
|
||||||
<div className="h-[300px] w-full">
|
<div className="h-[300px] w-full">
|
||||||
{loading ? (
|
{summaryLoading ? (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
@@ -411,7 +417,7 @@ export default function DataPage() {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{loading ? (
|
{dataLoading ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={8} className="h-24 text-center">
|
<TableCell colSpan={8} className="h-24 text-center">
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center gap-2">
|
||||||
@@ -478,7 +484,7 @@ export default function DataPage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))}
|
onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))}
|
||||||
disabled={currentPage === 1 || loading}
|
disabled={currentPage === 1 || dataLoading}
|
||||||
>
|
>
|
||||||
<ChevronLeft className="mr-2 h-4 w-4" />
|
<ChevronLeft className="mr-2 h-4 w-4" />
|
||||||
Previous
|
Previous
|
||||||
@@ -488,7 +494,7 @@ export default function DataPage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setCurrentPage((prev) => prev + 1)}
|
onClick={() => setCurrentPage((prev) => prev + 1)}
|
||||||
disabled={loading || sortedData.length < itemsPerPage}
|
disabled={dataLoading || sortedData.length < itemsPerPage}
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
<ChevronRight className="ml-2 h-4 w-4" />
|
<ChevronRight className="ml-2 h-4 w-4" />
|
||||||
|
|||||||
Reference in New Issue
Block a user