diff --git a/ui/src/components/certificate/CertificateList.tsx b/ui/src/components/certificate/CertificateList.tsx deleted file mode 100644 index 458186fd..00000000 --- a/ui/src/components/certificate/CertificateList.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import CertificateDetailDrawer from "@/components/certificate/CertificateDetailDrawer"; -import { Button } from "@/components/ui/button"; -import { DataTable } from "@/components/workflow/DataTable"; -import { Certificate as CertificateType } from "@/domain/certificate"; -import { diffDays, getLeftDays } from "@/lib/time"; -import { list } from "@/repository/certificate"; -import { ColumnDef } from "@tanstack/react-table"; -import { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { useNavigate, useSearchParams } from "react-router-dom"; - -type CertificateListProps = { - withPagination?: boolean; -}; - -const CertificateList = ({ withPagination }: CertificateListProps) => { - const [data, setData] = useState([]); - const [pageCount, setPageCount] = useState(0); - const [open, setOpen] = useState(false); - const [selectedCertificate, setSelectedCertificate] = useState(); - - const { t } = useTranslation(); - - const [searchParams] = useSearchParams(); - - const fetchData = async (page: number, pageSize?: number) => { - const state = searchParams.get("state"); - const resp = await list({ page: page, perPage: pageSize, state: state ?? "" }); - setData(resp.items); - setPageCount(resp.totalPages); - }; - - const navigate = useNavigate(); - - const columns: ColumnDef[] = [ - { - accessorKey: "san", - header: t("certificate.props.domain"), - cell: ({ row }) => { - let san: string = row.getValue("san"); - if (!san) { - san = ""; - } - - return ( -
- {san.split(";").map((item, i) => { - return ( -
- {item} -
- ); - })} -
- ); - }, - }, - { - accessorKey: "expireAt", - header: t("certificate.props.expiry"), - cell: ({ row }) => { - const expireAt: string = row.getValue("expireAt"); - const data = row.original; - const leftDays = getLeftDays(expireAt); - const allDays = diffDays(data.expireAt, data.created); - return ( -
- {leftDays > 0 ? ( -
{t("certificate.props.expiry.left_days", { left: leftDays, total: allDays })}
- ) : ( -
{t("certificate.props.expiry.expired")}
- )} - -
{t("certificate.props.expiry.expiration", { date: new Date(expireAt).toLocaleString().split(" ")[0] })}
-
- ); - }, - }, - { - accessorKey: "workflow", - header: t("certificate.props.workflow"), - cell: ({ row }) => { - const name = row.original.expand.workflow?.name; - const workflowId: string = row.getValue("workflow"); - return ( -
- -
- ); - }, - }, - { - accessorKey: "created", - header: t("common.text.created_at"), - cell: ({ row }) => { - const date: string = row.getValue("created"); - return new Date(date).toLocaleString(); - }, - }, - { - id: "actions", - cell: ({ row }) => { - return ( -
- -
- ); - }, - }, - ]; - - const handleWorkflowClick = (id: string) => { - navigate(`/workflows/detail?id=${id}`); - }; - - const handleView = (id: string) => { - setOpen(true); - const certificate = data.find((item) => item.id === id); - setSelectedCertificate(certificate); - }; - - return ( - <> - -
{t("certificate.nodata")}
- - - } - /> - - setOpen(false)} /> - - ); -}; - -export default CertificateList; diff --git a/ui/src/i18n/locales/en/nls.dashboard.json b/ui/src/i18n/locales/en/nls.dashboard.json index 4006c0c7..68d61004 100644 --- a/ui/src/i18n/locales/en/nls.dashboard.json +++ b/ui/src/i18n/locales/en/nls.dashboard.json @@ -1,15 +1,12 @@ { "dashboard.page.title": "Dashboard", - "dashboard.statistics.all.certificate": "All Certificates", - "dashboard.statistics.near_expired.certificate": "Certificates Near Expiry", - "dashboard.statistics.expired.certificate": "Expired Certificates", - - "dashboard.statistics.all.workflow": "All Workflows", - "dashboard.statistics.enabled.workflow": "Enabled Workflows", - + "dashboard.statistics.all_certificates": "All Certificates", + "dashboard.statistics.expire_soon_certificates": "Expire Soon Certificates", + "dashboard.statistics.expired_certificates": "Expired Certificates", + "dashboard.statistics.all_workflows": "All Workflows", + "dashboard.statistics.enabled_workflows": "Enabled Workflows", "dashboard.statistics.unit": "", "dashboard.certificate": "Latest Certificate" } - diff --git a/ui/src/i18n/locales/zh/nls.dashboard.json b/ui/src/i18n/locales/zh/nls.dashboard.json index 57c429b9..07100082 100644 --- a/ui/src/i18n/locales/zh/nls.dashboard.json +++ b/ui/src/i18n/locales/zh/nls.dashboard.json @@ -1,15 +1,12 @@ { "dashboard.page.title": "仪表盘", - "dashboard.statistics.all.certificate": "所有证书", - "dashboard.statistics.near_expired.certificate": "即将过期证书", - "dashboard.statistics.expired.certificate": "已过期证书", - - "dashboard.statistics.all.workflow": "所有工作流", - "dashboard.statistics.enabled.workflow": "已启用工作流", - + "dashboard.statistics.all_certificates": "所有证书", + "dashboard.statistics.expire_soon_certificates": "即将过期证书", + "dashboard.statistics.expired_certificates": "已过期证书", + "dashboard.statistics.all_workflows": "所有工作流", + "dashboard.statistics.enabled_workflows": "已启用工作流", "dashboard.statistics.unit": "个", "dashboard.certificate": "最新证书" } - diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx index 5e91ce33..f473d77e 100644 --- a/ui/src/pages/certificates/CertificateList.tsx +++ b/ui/src/pages/certificates/CertificateList.tsx @@ -38,6 +38,7 @@ const CertificateList = () => { { key: "expiry", title: t("certificate.props.expiry"), + defaultFilteredValue: searchParams.has("state") ? [searchParams.get("state") as string] : undefined, filterDropdown: ({ setSelectedKeys, confirm, clearFilters }) => { const items: Required["items"] = [ ["expireSoon", "certificate.props.expiry.filter.expire_soon"], @@ -164,21 +165,19 @@ const CertificateList = () => { const [tableData, setTableData] = useState([]); const [tableTotal, setTableTotal] = useState(0); - const [filters, setFilters] = useState>({}); + const [filters, setFilters] = useState>(() => { + return { + state: searchParams.get("state"), + }; + }); - const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [page, setPage] = useState(() => parseInt(+searchParams.get("page")! + "") || 1); + const [pageSize, setPageSize] = useState(() => parseInt(+searchParams.get("perPage")! + "") || 10); const [currentRecord, setCurrentRecord] = useState(); const [drawerOpen, setDrawerOpen] = useState(false); - useEffect(() => { - setFilters({ ...filters, state: searchParams.get("state") }); - setPage(parseInt(+searchParams.get("page")! + "") || 1); - setPageSize(parseInt(+searchParams.get("perPage")! + "") || 10); - }, []); - const fetchTableData = useCallback(async () => { if (loading) return; setLoading(true); @@ -238,10 +237,6 @@ const CertificateList = () => { }, }} rowKey={(record) => record.id} - onChange={(_, filters, __, extra) => { - console.log(filters); - extra.action === "filter" && fetchTableData(); - }} /> { - const [statistic, setStatistic] = useState(); + const navigate = useNavigate(); const { t } = useTranslation(); - useEffect(() => { - const fetchStatistic = async () => { - const data = await get(); - setStatistic(data); - }; + const { token: themeToken } = theme.useToken(); + const [notificationApi, NotificationContextHolder] = notification.useNotification(); + + const [loading, setLoading] = useState(false); + + const statisticGridSpans = { + xs: { flex: "50%" }, + md: { flex: "50%" }, + lg: { flex: "33.3333%" }, + xl: { flex: "33.3333%" }, + xxl: { flex: "20%" }, + }; + + const [statistic, setStatistic] = useState(); + + const fetchStatistic = useCallback(async () => { + if (loading) return; + setLoading(true); + + try { + const data = await getStatistics(); + setStatistic(data); + } catch (err) { + console.error(err); + notificationApi.error({ message: t("common.text.request_error"), description: <>{String(err)} }); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { fetchStatistic(); }, []); return ( -
-
-
{t("dashboard.page.title")}
-
-
-
-
- -
-
-
{t("dashboard.statistics.all.certificate")}
-
-
- {statistic?.certificateTotal ? ( - - {statistic?.certificateTotal} - - ) : ( - 0 - )} -
-
{t("dashboard.statistics.unit")}
-
-
-
+ <> + {NotificationContextHolder} -
-
- -
-
-
{t("dashboard.statistics.near_expired.certificate")}
-
-
- {statistic?.certificateExpireSoon ? ( - - {statistic?.certificateExpireSoon} - - ) : ( - 0 - )} -
-
{t("dashboard.statistics.unit")}
-
-
-
+ -
-
- -
-
-
{t("dashboard.statistics.expired.certificate")}
-
-
- {statistic?.certificateExpired ? ( - - {statistic?.certificateExpired} - - ) : ( - 0 - )} -
-
{t("dashboard.statistics.unit")}
-
-
-
+ + + } + label={t("dashboard.statistics.all_certificates")} + value={statistic?.certificateTotal ?? "-"} + suffix={t("dashboard.statistics.unit")} + onClick={() => navigate("/certificates")} + /> + + + } + label={t("dashboard.statistics.expire_soon_certificates")} + value={statistic?.certificateExpireSoon ?? "-"} + suffix={t("dashboard.statistics.unit")} + onClick={() => navigate("/certificates?state=expireSoon")} + /> + + + } + label={t("dashboard.statistics.expired_certificates")} + value={statistic?.certificateExpired ?? "-"} + suffix={t("dashboard.statistics.unit")} + onClick={() => navigate("/certificates?state=expired")} + /> + + + } + label={t("dashboard.statistics.all_workflows")} + value={statistic?.workflowTotal ?? "-"} + suffix={t("dashboard.statistics.unit")} + onClick={() => navigate("/workflows")} + /> + + + } + label={t("dashboard.statistics.enabled_workflows")} + value={statistic?.workflowEnabled ?? "-"} + suffix={t("dashboard.statistics.unit")} + onClick={() => navigate("/workflows?state=enabled")} + /> + + -
-
- -
-
-
{t("dashboard.statistics.all.workflow")}
-
-
- {statistic?.workflowTotal ? ( - - {statistic?.workflowTotal} - - ) : ( - 0 - )} -
-
{t("dashboard.statistics.unit")}
-
-
-
+ -
-
- -
-
-
{t("dashboard.statistics.enabled.workflow")}
-
-
- {statistic?.workflowEnabled ? ( - - {statistic?.workflowEnabled} - - ) : ( - 0 - )} -
-
{t("dashboard.statistics.unit")}
-
-
-
-
+
TODO: 最近执行的工作流 LatestWorkflowRun
+ + ); +}; -
-
-
- -
-
{t("dashboard.certificate")}
- - -
-
+const StatisticCard = ({ + label, + icon, + value, + suffix, + onClick, +}: { + label: React.ReactNode; + icon: React.ReactNode; + value?: string | number | React.ReactNode; + suffix?: React.ReactNode; + onClick?: () => void; +}) => { + return ( + + + {icon} + { + return {value}; + }} + suffix={{suffix}} + /> + + ); }; diff --git a/ui/src/pages/workflows/WorkflowList.tsx b/ui/src/pages/workflows/WorkflowList.tsx index 17775637..5863aabf 100644 --- a/ui/src/pages/workflows/WorkflowList.tsx +++ b/ui/src/pages/workflows/WorkflowList.tsx @@ -80,6 +80,7 @@ const WorkflowList = () => { { key: "state", title: t("workflow.props.state"), + defaultFilteredValue: searchParams.has("state") ? [searchParams.get("state") as string] : undefined, filterDropdown: ({ setSelectedKeys, confirm, clearFilters }) => { const items: Required["items"] = [ ["enabled", "workflow.props.state.filter.enabled"], @@ -195,16 +196,14 @@ const WorkflowList = () => { const [tableData, setTableData] = useState([]); const [tableTotal, setTableTotal] = useState(0); - const [filters, setFilters] = useState>({}); + const [filters, setFilters] = useState>(() => { + return { + state: searchParams.get("state"), + }; + }); - const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(10); - - useEffect(() => { - setFilters({ ...filters, state: searchParams.get("state") }); - setPage(parseInt(+searchParams.get("page")! + "") || 1); - setPageSize(parseInt(+searchParams.get("perPage")! + "") || 10); - }, []); + 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;