import { useMemo, useState } from "react"; import { faBan, faCertificate, faEllipsis, faEye, faFileExport, faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { format } from "date-fns"; import { twMerge } from "tailwind-merge"; import { ProjectPermissionCan } from "@app/components/permissions"; import { Badge, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, EmptyState, Pagination, Table, TableContainer, TableSkeleton, TBody, Td, Th, THead, Tooltip, Tr } from "@app/components/v2"; import { ProjectPermissionCertificateActions, ProjectPermissionSub, useProject } from "@app/context"; import { useListWorkspaceCertificates } from "@app/hooks/api"; import { caSupportsCapability } from "@app/hooks/api/ca/constants"; import { CaCapability, CaType } from "@app/hooks/api/ca/enums"; import { useListCasByProjectId } from "@app/hooks/api/ca/queries"; import { CertStatus } from "@app/hooks/api/certificates/enums"; import { UsePopUpState } from "@app/hooks/usePopUp"; import { getCertValidUntilBadgeDetails } from "./CertificatesTable.utils"; type Props = { handlePopUpOpen: ( popUpName: keyof UsePopUpState< ["certificate", "deleteCertificate", "revokeCertificate", "certificateCert"] >, data?: { serialNumber?: string; commonName?: string; } ) => void; }; const PER_PAGE_INIT = 25; export const CertificatesTable = ({ handlePopUpOpen }: Props) => { const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(PER_PAGE_INIT); const { currentProject } = useProject(); const { data, isPending } = useListWorkspaceCertificates({ projectId: currentProject?.slug ?? "", offset: (page - 1) * perPage, limit: perPage }); // Fetch CA data to determine capabilities const { data: caData } = useListCasByProjectId(currentProject?.id ?? ""); // Create mapping from caId to CA type for capability checking const caCapabilityMap = useMemo(() => { if (!caData) return {}; const map: Record = {}; caData.forEach((ca) => { map[ca.id] = ca.type; }); return map; }, [caData]); return ( {isPending && } {!isPending && data?.certificates.map((certificate) => { const { variant, label } = getCertValidUntilBadgeDetails(certificate.notAfter); return ( ); })}
Common Name Status Not Before Not After
{certificate.commonName} {certificate.status === CertStatus.REVOKED ? ( Revoked ) : ( {label} )} {certificate.notBefore ? format(new Date(certificate.notBefore), "yyyy-MM-dd") : "-"} {certificate.notAfter ? format(new Date(certificate.notAfter), "yyyy-MM-dd") : "-"}
{(isAllowed) => ( handlePopUpOpen("certificateCert", { serialNumber: certificate.serialNumber }) } disabled={!isAllowed} icon={} > Export Certificate )} {(isAllowed) => ( handlePopUpOpen("certificate", { serialNumber: certificate.serialNumber }) } disabled={!isAllowed} icon={} > View Details )} {/* Only show revoke button if CA supports revocation */} {(() => { const caType = caCapabilityMap[certificate.caId]; // If caId not found in map, assume CA supports revocation to avoid hiding revoke option const supportsRevocation = !caType || caSupportsCapability(caType, CaCapability.REVOKE_CERTIFICATES); if (!supportsRevocation) { return null; } return ( {(isAllowed) => ( handlePopUpOpen("revokeCertificate", { serialNumber: certificate.serialNumber }) } disabled={!isAllowed} icon={} > Revoke Certificate )} ); })()} {(isAllowed) => ( handlePopUpOpen("deleteCertificate", { serialNumber: certificate.serialNumber, commonName: certificate.commonName }) } disabled={!isAllowed} icon={} > Delete Certificate )}
{!isPending && data?.totalCount !== undefined && data.totalCount >= PER_PAGE_INIT && ( setPage(newPage)} onChangePerPage={(newPerPage) => setPerPage(newPerPage)} /> )} {!isPending && !data?.certificates?.length && ( )}
); };