diff --git a/ui/src/pages/accesses/AccessList.tsx b/ui/src/pages/accesses/AccessList.tsx index 550e7c32..57f8a9ed 100644 --- a/ui/src/pages/accesses/AccessList.tsx +++ b/ui/src/pages/accesses/AccessList.tsx @@ -1,5 +1,6 @@ -import { useCallback, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useRequest } from "ahooks"; import { Avatar, Button, Empty, Modal, notification, Space, Table, Tooltip, Typography, type TableProps } from "antd"; import { PageHeader } from "@ant-design/pro-components"; import { Copy as CopyIcon, Pencil as PencilIcon, Plus as PlusIcon, Trash2 as Trash2Icon } from "lucide-react"; @@ -19,8 +20,6 @@ const AccessList = () => { const { accesses, fetchAccesses, deleteAccess } = useAccessStore(); - const [loading, setLoading] = useState(false); - const tableColumns: TableProps["columns"] = [ { key: "$index", @@ -124,32 +123,24 @@ const AccessList = () => { }); }, []); - const fetchTableData = useCallback(async () => { - if (loading) return; - setLoading(true); - - try { + const { loading } = useRequest( + () => { const startIndex = (page - 1) * pageSize; const endIndex = startIndex + pageSize; const items = accesses.slice(startIndex, endIndex); - - setTableData(items); - setTableTotal(accesses.length); - } catch (err) { - if (err instanceof ClientResponseError && err.isAbort) { - return; - } - - console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: <>{getErrMsg(err)} }); - } finally { - setLoading(false); + return Promise.resolve({ + items, + totalItems: accesses.length, + }); + }, + { + refreshDeps: [accesses, page, pageSize], + onSuccess: (data) => { + setTableData(data.items); + setTableTotal(data.totalItems); + }, } - }, [page, pageSize, accesses]); - - useEffect(() => { - fetchTableData(); - }, [fetchTableData]); + ); const handleDeleteClick = async (data: AccessModel) => { modalApi.confirm({ diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx index 0d7c1b88..f17888c6 100644 --- a/ui/src/pages/certificates/CertificateList.tsx +++ b/ui/src/pages/certificates/CertificateList.tsx @@ -1,6 +1,7 @@ -import { useCallback, useEffect, useState } from "react"; +import { useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; +import { useRequest } from "ahooks"; import { Button, Divider, Empty, Menu, notification, Radio, Space, Table, theme, Tooltip, Typography, type MenuProps, type TableProps } from "antd"; import { PageHeader } from "@ant-design/pro-components"; import { Eye as EyeIcon, Filter as FilterIcon } from "lucide-react"; @@ -22,8 +23,6 @@ const CertificateList = () => { const [notificationApi, NotificationContextHolder] = notification.useNotification(); - const [loading, setLoading] = useState(false); - const tableColumns: TableProps["columns"] = [ { key: "$index", @@ -177,34 +176,30 @@ const CertificateList = () => { const [page, setPage] = useState(() => parseInt(+searchParams.get("page")! + "") || 1); const [pageSize, setPageSize] = useState(() => parseInt(+searchParams.get("perPage")! + "") || 10); - const fetchTableData = useCallback(async () => { - if (loading) return; - setLoading(true); - - try { - const resp = await listCertificate({ + const { loading } = useRequest( + () => { + return listCertificate({ page: page, perPage: pageSize, state: filters["state"] as CertificateListReq["state"], }); + }, + { + refreshDeps: [filters, page, pageSize], + onSuccess: (data) => { + setTableData(data.items); + setTableTotal(data.totalItems); + }, + onError: (err) => { + if (err instanceof ClientResponseError && err.isAbort) { + return; + } - setTableData(resp.items); - setTableTotal(resp.totalItems); - } catch (err) { - if (err instanceof ClientResponseError && err.isAbort) { - return; - } - - console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: <>{getErrMsg(err)} }); - } finally { - setLoading(false); + console.error(err); + notificationApi.error({ message: t("common.text.request_error"), description: <>{getErrMsg(err)} }); + }, } - }, [filters, page, pageSize]); - - useEffect(() => { - fetchTableData(); - }, [fetchTableData]); + ); return ( <> diff --git a/ui/src/pages/dashboard/Dashboard.tsx b/ui/src/pages/dashboard/Dashboard.tsx index fc54ca89..564078df 100644 --- a/ui/src/pages/dashboard/Dashboard.tsx +++ b/ui/src/pages/dashboard/Dashboard.tsx @@ -1,6 +1,7 @@ -import React, { useCallback, useEffect, useState } from "react"; +import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; +import { useRequest } from "ahooks"; import { Card, Col, Divider, notification, Row, Space, Statistic, theme, Typography } from "antd"; import { PageHeader } from "@ant-design/pro-components"; import { @@ -25,8 +26,6 @@ const Dashboard = () => { const [notificationApi, NotificationContextHolder] = notification.useNotification(); - const [loading, setLoading] = useState(false); - const statisticsGridSpans = { xs: { flex: "50%" }, md: { flex: "50%" }, @@ -34,31 +33,26 @@ const Dashboard = () => { xl: { flex: "33.3333%" }, xxl: { flex: "20%" }, }; - const [statistics, setStatistics] = useState(); - const fetchStatistics = useCallback(async () => { - if (loading) return; - setLoading(true); + const { loading } = useRequest( + () => { + return getStatistics(); + }, + { + onSuccess: (data) => { + setStatistics(data); + }, + onError: (err) => { + if (err instanceof ClientResponseError && err.isAbort) { + return; + } - try { - const data = await getStatistics(); - setStatistics(data); - } catch (err) { - if (err instanceof ClientResponseError && err.isAbort) { - return; - } - - console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: <>{getErrMsg(err)} }); - } finally { - setLoading(false); + console.error(err); + notificationApi.error({ message: t("common.text.request_error"), description: <>{getErrMsg(err)} }); + }, } - }, []); - - useEffect(() => { - fetchStatistics(); - }, []); + ); return ( <> @@ -71,6 +65,7 @@ const Dashboard = () => { } label={t("dashboard.statistics.all_certificates")} + loading={loading} value={statistics?.certificateTotal ?? "-"} suffix={t("dashboard.statistics.unit")} onClick={() => navigate("/certificates")} @@ -80,6 +75,7 @@ const Dashboard = () => { } label={t("dashboard.statistics.expire_soon_certificates")} + loading={loading} value={statistics?.certificateExpireSoon ?? "-"} suffix={t("dashboard.statistics.unit")} onClick={() => navigate("/certificates?state=expireSoon")} @@ -89,6 +85,7 @@ const Dashboard = () => { } label={t("dashboard.statistics.expired_certificates")} + loading={loading} value={statistics?.certificateExpired ?? "-"} suffix={t("dashboard.statistics.unit")} onClick={() => navigate("/certificates?state=expired")} @@ -98,6 +95,7 @@ const Dashboard = () => { } label={t("dashboard.statistics.all_workflows")} + loading={loading} value={statistics?.workflowTotal ?? "-"} suffix={t("dashboard.statistics.unit")} onClick={() => navigate("/workflows")} @@ -107,6 +105,7 @@ const Dashboard = () => { } label={t("dashboard.statistics.enabled_workflows")} + loading={loading} value={statistics?.workflowEnabled ?? "-"} suffix={t("dashboard.statistics.unit")} onClick={() => navigate("/workflows?state=enabled")} @@ -123,19 +122,21 @@ const Dashboard = () => { const StatisticCard = ({ label, + loading, icon, value, suffix, onClick, }: { label: React.ReactNode; + loading?: boolean; icon: React.ReactNode; value?: string | number | React.ReactNode; suffix?: React.ReactNode; onClick?: () => void; }) => { return ( - + {icon} { const [modalApi, ModelContextHolder] = Modal.useModal(); const [notificationApi, NotificationContextHolder] = notification.useNotification(); - const [loading, setLoading] = useState(false); - const tableColumns: TableProps["columns"] = [ { key: "$index", @@ -209,34 +208,30 @@ const WorkflowList = () => { const [page, setPage] = useState(() => parseInt(+searchParams.get("page")! + "") || 1); const [pageSize, setPageSize] = useState(() => parseInt(+searchParams.get("perPage")! + "") || 10); - const fetchTableData = useCallback(async () => { - if (loading) return; - setLoading(true); - - try { - const resp = await listWorkflow({ + const { loading } = useRequest( + () => { + return listWorkflow({ page: page, perPage: pageSize, enabled: (filters["state"] as string) === "enabled" ? true : (filters["state"] as string) === "disabled" ? false : undefined, }); + }, + { + refreshDeps: [filters, page, pageSize], + onSuccess: (data) => { + setTableData(data.items); + setTableTotal(data.totalItems); + }, + onError: (err) => { + if (err instanceof ClientResponseError && err.isAbort) { + return; + } - setTableData(resp.items); - setTableTotal(resp.totalItems); - } catch (err) { - if (err instanceof ClientResponseError && err.isAbort) { - return; - } - - console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: <>{getErrMsg(err)} }); - } finally { - setLoading(false); + console.error(err); + notificationApi.error({ message: t("common.text.request_error"), description: <>{getErrMsg(err)} }); + }, } - }, [filters, page, pageSize]); - - useEffect(() => { - fetchTableData(); - }, [fetchTableData]); + ); const handleEnabledChange = async (workflow: WorkflowModel) => { try {