coder/site/src/pages/HealthPage/Content.tsx

267 lines
5.7 KiB
TypeScript

/* eslint-disable jsx-a11y/heading-has-content -- infer from props */
import { css } from "@emotion/css";
import { useTheme } from "@emotion/react";
import CheckCircleOutlined from "@mui/icons-material/CheckCircleOutlined";
import DoNotDisturbOnOutlined from "@mui/icons-material/DoNotDisturbOnOutlined";
import ErrorOutline from "@mui/icons-material/ErrorOutline";
import Link from "@mui/material/Link";
import {
cloneElement,
type ComponentProps,
type FC,
forwardRef,
type HTMLAttributes,
type ReactElement,
} from "react";
import type { HealthCode, HealthSeverity } from "api/typesGenerated";
import { docs } from "utils/docs";
import { healthyColor } from "./healthyColor";
const CONTENT_PADDING = 36;
export const Header: FC<HTMLAttributes<HTMLDivElement>> = (props) => {
return (
<header
css={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: `36px ${CONTENT_PADDING}px`,
}}
{...props}
/>
);
};
export const HeaderTitle: FC<HTMLAttributes<HTMLDivElement>> = (props) => {
return (
<h2
css={{
margin: 0,
lineHeight: "1.2",
fontSize: 20,
fontWeight: 500,
display: "flex",
alignItems: "center",
gap: 16,
}}
{...props}
/>
);
};
interface HealthIconProps {
size: number;
severity: HealthSeverity;
}
export const HealthIcon: FC<HealthIconProps> = ({ size, severity }) => {
const theme = useTheme();
const color = healthyColor(theme, severity);
const Icon = severity === "error" ? ErrorOutline : CheckCircleOutlined;
return <Icon css={{ width: size, height: size, color }} />;
};
interface HealthyDotProps {
severity: HealthSeverity;
}
export const HealthyDot: FC<HealthyDotProps> = ({ severity }) => {
const theme = useTheme();
return (
<div
css={{
width: 8,
height: 8,
borderRadius: 9999,
backgroundColor: healthyColor(theme, severity),
}}
/>
);
};
export const Main: FC<HTMLAttributes<HTMLDivElement>> = (props) => {
return (
<main
css={{
padding: `0 ${CONTENT_PADDING}px ${CONTENT_PADDING}px`,
display: "flex",
flexDirection: "column",
gap: 36,
}}
{...props}
/>
);
};
export const GridData: FC<HTMLAttributes<HTMLDivElement>> = (props) => {
return (
<div
css={{
lineHeight: "1.4",
display: "grid",
gridTemplateColumns: "auto auto",
gap: 12,
columnGap: 48,
width: "min-content",
whiteSpace: "nowrap",
}}
{...props}
/>
);
};
export const GridDataLabel: FC<HTMLAttributes<HTMLSpanElement>> = (props) => {
const theme = useTheme();
return (
<span
css={{
fontSize: 14,
fontWeight: 500,
color: theme.palette.text.secondary,
}}
{...props}
/>
);
};
export const GridDataValue: FC<HTMLAttributes<HTMLSpanElement>> = (props) => {
const theme = useTheme();
return (
<span
css={{
fontSize: 14,
color: theme.palette.text.primary,
}}
{...props}
/>
);
};
export const SectionLabel: FC<HTMLAttributes<HTMLHeadingElement>> = (props) => {
return (
<h4
{...props}
css={{
fontSize: 14,
fontWeight: 500,
margin: 0,
lineHeight: "1.2",
marginBottom: 16,
}}
/>
);
};
type PillProps = HTMLAttributes<HTMLDivElement> & {
icon: ReactElement;
};
export const Pill = forwardRef<HTMLDivElement, PillProps>((props, ref) => {
const theme = useTheme();
const { icon, children, ...divProps } = props;
return (
<div
ref={ref}
css={{
display: "inline-flex",
alignItems: "center",
height: 32,
borderRadius: 9999,
border: `1px solid ${theme.palette.divider}`,
fontSize: 12,
fontWeight: 500,
padding: 8,
gap: 8,
cursor: "default",
}}
{...divProps}
>
{cloneElement(icon, { className: css({ width: 14, height: 14 }) })}
{children}
</div>
);
});
type BooleanPillProps = Omit<ComponentProps<typeof Pill>, "icon" | "value"> & {
value: boolean;
};
export const BooleanPill: FC<BooleanPillProps> = ({
value,
children,
...divProps
}) => {
const theme = useTheme();
const color = value ? theme.palette.success.light : theme.palette.error.light;
return (
<Pill
icon={
value ? (
<CheckCircleOutlined css={{ color }} />
) : (
<DoNotDisturbOnOutlined css={{ color }} />
)
}
{...divProps}
>
{children}
</Pill>
);
};
type LogsProps = HTMLAttributes<HTMLDivElement> & { lines: readonly string[] };
export const Logs: FC<LogsProps> = ({ lines, ...divProps }) => {
const theme = useTheme();
return (
<div
css={{
fontFamily: "monospace",
fontSize: 13,
lineHeight: "160%",
padding: 24,
backgroundColor: theme.palette.background.paper,
overflowX: "auto",
whiteSpace: "pre-wrap",
wordBreak: "break-all",
}}
{...divProps}
>
{lines.map((line, index) => (
<span css={{ display: "block" }} key={index}>
{line}
</span>
))}
{lines.length === 0 && (
<span css={{ color: theme.palette.text.secondary }}>
No logs available
</span>
)}
</div>
);
};
interface HealthMessageDocsLinkProps {
code: HealthCode;
}
export const HealthMessageDocsLink: FC<HealthMessageDocsLinkProps> = ({
code,
}) => {
return (
<Link
href={docs(`/admin/healthcheck#${code.toLocaleLowerCase()}`)}
target="_blank"
rel="noreferrer"
>
Docs for {code}
</Link>
);
};