Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as React from "react";
import { Flex, FlexItem, Icon, Pagination } from "@patternfly/react-core";
import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";
import CheckCircleIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
import { capitalizeFirstLetter } from "@app/utils/utils";
import { ExclamationTriangleIcon } from "@patternfly/react-icons";
import { type TrustRootKind } from "../../data/TrustRoots.data";
import TrustRootNotFound from "../TrustRootNotFound";

export interface TrustRootCertificateTabProps {
trustRoot?: TrustRootKind;
}

const TrustRootCertificateTab: React.FC<TrustRootCertificateTabProps> = ({ trustRoot }) => {
return trustRoot ? (
<>
<Pagination itemCount={trustRoot.certificates.length} perPage={10} page={1} variant="top" />
<Table variant="compact">
<Thead>
<Tr>
<Th>Subject</Th>
<Th>Issuer</Th>
<Th>Type</Th>
<Th>Status</Th>
</Tr>
</Thead>
<Tbody>
{trustRoot.certificates.map((cert, idx) => (
<Tr key={idx}>
<Td>{cert.subject}</Td>
<Td>{cert.issuer}</Td>
<Td>{cert.type}</Td>
<Td>
<Flex spaceItems={{ default: "spaceItemsSm" }}>
<FlexItem>
{cert.status === "valid" ? (
<Icon status="success">
<CheckCircleIcon />
</Icon>
) : (
<Icon status="warning">
<ExclamationTriangleIcon />
</Icon>
)}{" "}
{capitalizeFirstLetter(cert.status)}
</FlexItem>
</Flex>
</Td>
</Tr>
))}
</Tbody>
</Table>
<Pagination itemCount={trustRoot.certificates.length} perPage={10} page={1} variant="bottom" />
</>
) : (
<TrustRootNotFound />
);
};

export default TrustRootCertificateTab;
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {
Button,
DescriptionList,
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTermHelpText,
DescriptionListTermHelpTextButton,
PageSection,
Popover,
} from "@patternfly/react-core";

import PlusCircleIcon from "@patternfly/react-icons/dist/esm/icons/plus-circle-icon";
import { formatDate } from "@app/utils/utils";
import TrustRootNotFound from "../TrustRootNotFound";
import type { TrustRootKind } from "../../data/TrustRoots.data";

interface TrustRootInfoTabProps {
trustRoot?: TrustRootKind;
}

const TrustRootInfoTab: React.FC<TrustRootInfoTabProps> = ({ trustRoot }) => {
return trustRoot ? (
<>
<PageSection>
<DescriptionList>
<DescriptionListGroup>
<DescriptionListTermHelpText>
<Popover headerContent={<div>ID</div>} bodyContent={<div>Additional info</div>}>
<DescriptionListTermHelpTextButton> ID </DescriptionListTermHelpTextButton>
</Popover>
</DescriptionListTermHelpText>
<DescriptionListDescription>{trustRoot.id}</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTermHelpText>
<Popover headerContent={<div>Source</div>} bodyContent={<div>Additional source info</div>}>
<DescriptionListTermHelpTextButton> Source </DescriptionListTermHelpTextButton>
</Popover>
</DescriptionListTermHelpText>
<DescriptionListDescription>
<a href="#">{trustRoot.source}</a>
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTermHelpText>
<Popover headerContent={<div>Last Updated</div>} bodyContent={<div>Additional info</div>}>
<DescriptionListTermHelpTextButton> Last Updated </DescriptionListTermHelpTextButton>
</Popover>
</DescriptionListTermHelpText>
<DescriptionListDescription>{formatDate(trustRoot.lastUpdated)}</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTermHelpText>
<Popover headerContent={<div>Type</div>} bodyContent={<div>Additional info</div>}>
<DescriptionListTermHelpTextButton> Type </DescriptionListTermHelpTextButton>
</Popover>
</DescriptionListTermHelpText>
<DescriptionListDescription>
<Button variant="link" isInline icon={<PlusCircleIcon />}>
{trustRoot.type}
</Button>
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTermHelpText>
<Popover headerContent={<div>Certificates</div>} bodyContent={<div>Additional info</div>}>
<DescriptionListTermHelpTextButton> Certificates </DescriptionListTermHelpTextButton>
</Popover>
</DescriptionListTermHelpText>
<DescriptionListDescription>{trustRoot.certificates.length} Certificate(s)</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTermHelpText>
<Popover headerContent={<div>TUF Metadata</div>} bodyContent={<div>Additional info</div>}>
<DescriptionListTermHelpTextButton> TUF Metadata </DescriptionListTermHelpTextButton>
</Popover>
</DescriptionListTermHelpText>
<DescriptionListDescription>{trustRoot.tufMetadata.length} Metadata File(s)</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</PageSection>
</>
) : (
<TrustRootNotFound />
);
};

export default TrustRootInfoTab;
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import * as React from "react";
import { Button, Card, CardBody, CardFooter, CardTitle, Content, Flex, FlexItem, Icon } from "@patternfly/react-core";
import CheckCircleIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
import { capitalizeFirstLetter, formatDate } from "@app/utils/utils";
import { ExclamationTriangleIcon, ExternalLinkAltIcon } from "@patternfly/react-icons";
import { type TrustRootKind } from "../../data/TrustRoots.data";
import TrustRootNotFound from "../TrustRootNotFound";

export interface TrustRootMetadataTabProps {
trustRoot?: TrustRootKind;
}

const TrustRootMetadataTab: React.FC<TrustRootMetadataTabProps> = ({ trustRoot }) => {
if (!trustRoot?.tufMetadata || !Array.isArray(trustRoot.tufMetadata) || trustRoot.tufMetadata.length === 0) {
return <TrustRootNotFound />;
}
const latestTUF = trustRoot?.tufMetadata[0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth double checking that the backend will not return a trustRoot object at all if the tufMetadata array is empty. Because if it still does, and that array is empty, this will result in a type error when we try to access its properties. One way around this is to check the length of tufMetadata is greater than 0 (see above).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in earlier line


return (
<Card id="certificate-health" isPlain isFullHeight key="card-1">
<CardTitle>
<Content component="h3">TUF metadata</Content>
</CardTitle>
<CardBody>
<Flex grow={{ default: "grow" }} spaceItems={{ default: "spaceItemsXl" }}>
<Flex flex={{ default: "flex_1" }}>
<Flex direction={{ default: "column" }} spaceItems={{ default: "spaceItemsLg" }}>
<FlexItem>
<Flex direction={{ default: "column" }}>
<FlexItem>
<Content component="h4">Status</Content>
</FlexItem>
<FlexItem>
{latestTUF.status === "valid" ? (
<Icon status="success">
<CheckCircleIcon />
</Icon>
) : (
<Icon status="warning">
<ExclamationTriangleIcon />
</Icon>
)}{" "}
{capitalizeFirstLetter(latestTUF.status)}
</FlexItem>
</Flex>
</FlexItem>

<FlexItem>
<Flex direction={{ default: "column" }}>
<FlexItem>
<Content component="h4">Version</Content>
</FlexItem>
<FlexItem>{latestTUF.version}</FlexItem>
</Flex>
</FlexItem>
</Flex>
</Flex>
<Flex flex={{ default: "flex_1" }} align={{ default: "alignRight" }}>
<Flex direction={{ default: "column" }} spaceItems={{ default: "spaceItemsLg" }}>
<FlexItem>
<FlexItem>
<Flex direction={{ default: "column" }}>
<FlexItem>
<Content component="h4">Role</Content>
</FlexItem>
<FlexItem>{latestTUF.role}</FlexItem>
</Flex>
</FlexItem>
</FlexItem>
<Flex>
<FlexItem align={{ default: "alignRight" }}>
<FlexItem>
<Flex direction={{ default: "column" }}>
<FlexItem>
<Content component="h4">Expires</Content>
</FlexItem>
<FlexItem>{formatDate(latestTUF.expires)}</FlexItem>
</Flex>
</FlexItem>
</FlexItem>
</Flex>
</Flex>
</Flex>
</Flex>
</CardBody>
<CardFooter>
<Button variant="link" isInline icon={<ExternalLinkAltIcon />}>
See the trust root definition on store
</Button>
</CardFooter>
</Card>
);
};

export default TrustRootMetadataTab;
33 changes: 33 additions & 0 deletions client/src/app/pages/TrustRoots/components/StatusIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Icon } from "@patternfly/react-core";
import * as React from "react";
import CheckCircleIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
import TimesCircleIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon";
import { MinusIcon } from "@patternfly/react-icons";

interface StatusIconProps {
status: string;
}

const StatusIcon: React.FC<StatusIconProps> = ({ status }) => {
if (status === "success") {
return (
<Icon size="xl" status="success">
<CheckCircleIcon />
</Icon>
);
} else if (status === "error") {
return (
<Icon size="xl" status="danger">
<TimesCircleIcon />
</Icon>
);
} else {
return (
<Icon size="xl">
<MinusIcon />
</Icon>
);
}
};

export default StatusIcon;
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ErrorState } from "@patternfly/react-component-groups";

const TrustRootNotFound = () => {
return <ErrorState title="Trust root not found" />;
};

export default TrustRootNotFound;
Loading