import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate, useSearchParams } from "react-router-dom"; import { DeleteOutlined as DeleteOutlinedIcon, SelectOutlined as SelectOutlinedIcon } from "@ant-design/icons"; import { PageHeader } from "@ant-design/pro-components"; import { useRequest } from "ahooks"; import { Button, Divider, Empty, Menu, type MenuProps, Modal, Radio, Space, Table, type TableProps, Tooltip, Typography, notification, theme } from "antd"; import dayjs from "dayjs"; import { ClientResponseError } from "pocketbase"; import CertificateDetailDrawer from "@/components/certificate/CertificateDetailDrawer"; import { CERTIFICATE_SOURCES, type CertificateModel } from "@/domain/certificate"; import { type ListCertificateRequest, list as listCertificate, remove as removeCertificate } from "@/repository/certificate"; import { getErrMsg } from "@/utils/error"; const CertificateList = () => { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const { t } = useTranslation(); const { token: themeToken } = theme.useToken(); const [modalApi, ModalContextHolder] = Modal.useModal(); const [notificationApi, NotificationContextHolder] = notification.useNotification(); const tableColumns: TableProps["columns"] = [ { key: "$index", align: "center", fixed: "left", width: 50, render: (_, __, index) => (page - 1) * pageSize + index + 1, }, { key: "name", title: t("certificate.props.subject_alt_names"), render: (_, record) => {record.subjectAltNames}, }, { key: "expiry", title: t("certificate.props.validity"), ellipsis: true, defaultFilteredValue: searchParams.has("state") ? [searchParams.get("state") as string] : undefined, filterDropdown: ({ setSelectedKeys, confirm, clearFilters }) => { const items: Required["items"] = [ ["expireSoon", "certificate.props.validity.filter.expire_soon"], ["expired", "certificate.props.validity.filter.expired"], ].map(([key, label]) => { return { key, label: {t(label)}, onClick: () => { if (filters["state"] !== key) { setPage(1); setFilters((prev) => ({ ...prev, state: key })); setSelectedKeys([key]); } confirm({ closeDropdown: true }); }, }; }); const handleResetClick = () => { setPage(1); setFilters((prev) => ({ ...prev, state: undefined })); setSelectedKeys([]); clearFilters?.(); confirm(); }; const handleConfirmClick = () => { confirm(); }; return (
); }, render: (_, record) => { const total = dayjs(record.expireAt).diff(dayjs(record.created), "d") + 1; const left = dayjs(record.expireAt).diff(dayjs(), "d"); return ( {left > 0 ? ( {t("certificate.props.validity.left_days", { left, total })} ) : ( {t("certificate.props.validity.expired")} )} {t("certificate.props.validity.expiration", { date: dayjs(record.expireAt).format("YYYY-MM-DD") })} ); }, }, { key: "issuer", title: t("certificate.props.brand"), render: (_, record) => ( {record.issuer} {record.keyAlgorithm} ), }, { key: "source", title: t("certificate.props.source"), ellipsis: true, render: (_, record) => { if (record.source === CERTIFICATE_SOURCES.WORKFLOW) { const workflowId = record.workflowId; return ( {t("certificate.props.source.workflow")} { if (workflowId) { navigate(`/workflows/${workflowId}`); } }} > {record.expand?.workflowId?.name ?? {t(`#${workflowId}`)}} ); } else if (record.source === CERTIFICATE_SOURCES.UPLOAD) { return {t("certificate.props.source.upload")}; } return <>; }, }, { key: "createdAt", title: t("certificate.props.created_at"), ellipsis: true, render: (_, record) => { return dayjs(record.created!).format("YYYY-MM-DD HH:mm:ss"); }, }, { key: "updatedAt", title: t("certificate.props.updated_at"), ellipsis: true, render: (_, record) => { return dayjs(record.updated!).format("YYYY-MM-DD HH:mm:ss"); }, }, { key: "$action", align: "end", fixed: "right", width: 120, render: (_, record) => (