feat: refactor <AuditLogRow* /> (#22014)

This pull-request takes the MUI based components from `<AuditLogRow />`
and its subsidiaries and updates them to use the correct newer Tailwind
based components.
This commit is contained in:
Jake Howell
2026-02-10 16:10:19 +11:00
committed by GitHub
parent 8bcfeab500
commit 342d2e4bed
3 changed files with 180 additions and 233 deletions
@@ -1,5 +1,5 @@
import Link from "@mui/material/Link";
import type { AuditLog } from "api/typesGenerated";
import { Link } from "components/Link/Link";
import type { FC } from "react";
import { Link as RouterLink } from "react-router";
import { BuildAuditDescription } from "./BuildAuditDescription";
@@ -52,8 +52,10 @@ export const AuditLogDescription: FC<AuditLogDescriptionProps> = ({
<span>
{truncatedDescription}
{auditLog.resource_link ? (
<Link component={RouterLink} to={auditLog.resource_link}>
<strong>{target}</strong>
<Link asChild showExternalIcon={false} className="text-base px-0">
<RouterLink to={auditLog.resource_link}>
<strong>{target}</strong>
</RouterLink>
</Link>
) : (
<strong>{target}</strong>
@@ -70,8 +72,10 @@ function AppSessionAuditLogDescription({ auditLog }: AuditLogDescriptionProps) {
return (
<>
{connection_type} session to {workspace_owner}'s{" "}
<Link component={RouterLink} to={`${auditLog.resource_link}`}>
<strong>{workspace_name}</strong>
<Link asChild showExternalIcon={false} className="text-base px-0">
<RouterLink to={`${auditLog.resource_link}`}>
<strong>{workspace_name}</strong>
</RouterLink>
</Link>{" "}
workspace{" "}
<strong>{auditLog.action === "disconnect" ? "closed" : "opened"}</strong>
@@ -1,5 +1,5 @@
import Link from "@mui/material/Link";
import type { AuditLog } from "api/typesGenerated";
import { Link } from "components/Link/Link";
import { type FC, useMemo } from "react";
import { Link as RouterLink } from "react-router";
import { systemBuildReasons } from "utils/workspace";
@@ -38,8 +38,10 @@ export const BuildAuditDescription: FC<BuildAuditDescriptionProps> = ({
<span>
{user} <strong>{action}</strong> workspace{" "}
{auditLog.resource_link ? (
<Link component={RouterLink} to={auditLog.resource_link}>
<strong>{workspaceName}</strong>
<Link asChild showExternalIcon={false} className="text-base px-0">
<RouterLink to={auditLog.resource_link}>
<strong>{workspaceName}</strong>
</RouterLink>
</Link>
) : (
<strong>{workspaceName}</strong>
@@ -1,10 +1,11 @@
import type { CSSObject, Interpolation, Theme } from "@emotion/react";
import Collapse from "@mui/material/Collapse";
import Link from "@mui/material/Link";
import type { AuditLog, BuildReason } from "api/typesGenerated";
import { Avatar } from "components/Avatar/Avatar";
import {
Collapsible,
CollapsibleContent,
} from "components/Collapsible/Collapsible";
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
import { Stack } from "components/Stack/Stack";
import { Link } from "components/Link/Link";
import { StatusPill } from "components/StatusPill/StatusPill";
import { TableCell } from "components/Table/Table";
import { TimelineEntry } from "components/Timeline/TimelineEntry";
@@ -71,246 +72,186 @@ export const AuditLogRow: FC<AuditLogRowProps> = ({
data-testid={`audit-log-row-${auditLog.id}`}
clickable={shouldDisplayDiff}
>
<TableCell css={styles.auditLogCell}>
<Stack
direction="row"
alignItems="center"
css={styles.auditLogHeader}
tabIndex={0}
onClick={toggle}
onKeyDown={(event) => {
if (event.key === "Enter") {
toggle();
}
}}
>
<Stack
direction="row"
alignItems="center"
css={styles.auditLogHeaderInfo}
<TableCell className="!p-0 border-0 border-b text-base">
<Collapsible open={isDiffOpen} onOpenChange={setIsDiffOpen}>
<div
className="flex flex-row items-center gap-4 py-4 px-8"
tabIndex={0}
role="button"
onClick={toggle}
onKeyDown={(event) => {
if (event.key === "Enter") {
toggle();
}
}}
>
<Stack direction="row" alignItems="center" css={styles.fullWidth}>
{/*
* Session logs don't have an associated user to the log,
* so when it happens we display a default icon to represent non user actions
*/}
{auditLog.user ? (
<Avatar
fallback={auditLog.user.username}
src={auditLog.user.avatar_url}
/>
) : (
<Avatar>
<NetworkIcon className="h-full w-full p-1" />
</Avatar>
)}
<div className="flex flex-row items-center gap-4 flex-1">
<div className="flex flex-row items-center gap-4 w-full">
{/*
* Session logs don't have an associated user to the log,
* so when it happens we display a default icon to represent non user actions
*/}
{auditLog.user ? (
<Avatar
fallback={auditLog.user.username}
src={auditLog.user.avatar_url}
/>
) : (
<Avatar>
<NetworkIcon className="h-full w-full p-1" />
</Avatar>
)}
<Stack
alignItems="baseline"
css={styles.fullWidth}
justifyContent="space-between"
direction="row"
>
<Stack
css={styles.auditLogSummary}
direction="row"
alignItems="baseline"
spacing={1}
>
<AuditLogDescription auditLog={auditLog} />
{auditLog.is_deleted && (
<span css={styles.deletedLabel}>(deleted)</span>
)}
<span css={styles.auditLogTime}>
{new Date(auditLog.time).toLocaleTimeString()}
</span>
</Stack>
<div className="flex flex-row items-baseline justify-between w-full font-normal gap-4">
<div className="flex flex-row items-baseline gap-2">
<AuditLogDescription auditLog={auditLog} />
{auditLog.is_deleted && (
<span className="text-xs text-content-secondary">
(deleted)
</span>
)}
<span className="text-content-secondary text-xs">
{new Date(auditLog.time).toLocaleTimeString()}
</span>
</div>
<Stack direction="row" alignItems="center">
<StatusPill isHttpCode={true} code={auditLog.status_code} />
<div className="flex flex-row items-center gap-4">
<StatusPill isHttpCode={true} code={auditLog.status_code} />
{/* With multi-org, there is not enough space so show
{/* With multi-org, there is not enough space so show
everything in a tooltip. */}
{showOrgDetails ? (
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon
css={(theme) => ({
color: theme.palette.info.light,
})}
/>
</TooltipTrigger>
<TooltipContent side="bottom">
<div css={styles.auditLogInfoTooltip}>
{auditLog.ip && (
<div>
<h4 css={styles.auditLogInfoHeader}>IP:</h4>
<div>{auditLog.ip}</div>
</div>
)}
{userAgent?.os.name && (
<div>
<h4 css={styles.auditLogInfoHeader}>OS:</h4>
<div>{userAgent.os.name}</div>
</div>
)}
{userAgent?.browser.name && (
<div>
<h4 css={styles.auditLogInfoHeader}>Browser:</h4>
{showOrgDetails ? (
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="text-content-link" />
</TooltipTrigger>
<TooltipContent side="bottom">
<div className="flex flex-col gap-2">
{auditLog.ip && (
<div>
{userAgent.browser.name}{" "}
{userAgent.browser.version}
<h4 className="m-0 text-content-primary leading-[150%] font-semibold">
IP:
</h4>
<div>{auditLog.ip}</div>
</div>
</div>
)}
{auditLog.organization && (
<div>
<h4 css={styles.auditLogInfoHeader}>
Organization:
</h4>
<Link
component={RouterLink}
to={`/organizations/${auditLog.organization.name}`}
>
{auditLog.organization.display_name ||
auditLog.organization.name}
</Link>
</div>
)}
{auditLog.additional_fields?.build_reason &&
auditLog.action === "start" && (
)}
{userAgent?.os.name && (
<div>
<h4 css={styles.auditLogInfoHeader}>Reason:</h4>
<h4 className="m-0 text-content-primary leading-[150%] font-semibold">
OS:
</h4>
<div>{userAgent.os.name}</div>
</div>
)}
{userAgent?.browser.name && (
<div>
<h4 className="m-0 text-content-primary leading-[150%] font-semibold">
Browser:
</h4>
<div>
{
buildReasonLabels[
auditLog.additional_fields
.build_reason as BuildReason
]
}
{userAgent.browser.name}{" "}
{userAgent.browser.version}
</div>
</div>
)}
</div>
</TooltipContent>
</Tooltip>
) : (
<Stack direction="row" spacing={1} alignItems="baseline">
{auditLog.ip && (
<span css={styles.auditLogInfo}>
<span>IP: </span>
<strong>{auditLog.ip}</strong>
</span>
)}
{userAgent?.os.name && (
<span css={styles.auditLogInfo}>
<span>OS: </span>
<strong>{userAgent.os.name}</strong>
</span>
)}
{userAgent?.browser.name && (
<span css={styles.auditLogInfo}>
<span>Browser: </span>
<strong>
{userAgent.browser.name} {userAgent.browser.version}
</strong>
</span>
)}
{auditLog.additional_fields?.build_reason &&
auditLog.action === "start" && (
<span css={styles.auditLogInfo}>
<span>Reason: </span>
{auditLog.organization && (
<div>
<h4 className="m-0 text-content-primary leading-[150%] font-semibold">
Organization:
</h4>
<Link
asChild
showExternalIcon={false}
className="px-0"
>
<RouterLink
to={`/organizations/${auditLog.organization.name}`}
>
{auditLog.organization.display_name ||
auditLog.organization.name}
</RouterLink>
</Link>
</div>
)}
{auditLog.additional_fields?.build_reason &&
auditLog.action === "start" && (
<div>
<h4 className="m-0 text-content-primary leading-normal font-semibold">
Reason:
</h4>
<div>
{
buildReasonLabels[
auditLog.additional_fields
.build_reason as BuildReason
]
}
</div>
</div>
)}
</div>
</TooltipContent>
</Tooltip>
) : (
<div className="flex flex-row items-baseline gap-2">
{auditLog.ip && (
<span className="text-xs text-content-secondary block">
<span>IP: </span>
<strong>{auditLog.ip}</strong>
</span>
)}
{userAgent?.os.name && (
<span className="text-xs text-content-secondary block">
<span>OS: </span>
<strong>{userAgent.os.name}</strong>
</span>
)}
{userAgent?.browser.name && (
<span className="text-xs text-content-secondary block">
<span>Browser: </span>
<strong>
{
buildReasonLabels[
auditLog.additional_fields
.build_reason as BuildReason
]
}
{userAgent.browser.name}{" "}
{userAgent.browser.version}
</strong>
</span>
)}
</Stack>
)}
</Stack>
</Stack>
</Stack>
</Stack>
{auditLog.additional_fields?.build_reason &&
auditLog.action === "start" && (
<span className="text-xs text-content-secondary block">
<span>Reason: </span>
<strong>
{
buildReasonLabels[
auditLog.additional_fields
.build_reason as BuildReason
]
}
</strong>
</span>
)}
</div>
)}
</div>
</div>
</div>
</div>
{shouldDisplayDiff ? (
<div> {<DropdownArrow close={isDiffOpen} />}</div>
) : (
<div css={styles.columnWithoutDiff} />
{shouldDisplayDiff ? (
<div>
<DropdownArrow close={isDiffOpen} />
</div>
) : (
<div className="ml-6" />
)}
</div>
{shouldDisplayDiff && (
<CollapsibleContent>
<AuditLogDiff diff={auditDiff} />
</CollapsibleContent>
)}
</Stack>
{shouldDisplayDiff && (
<Collapse in={isDiffOpen}>
<AuditLogDiff diff={auditDiff} />
</Collapse>
)}
</Collapsible>
</TableCell>
</TimelineEntry>
);
};
const styles = {
auditLogCell: {
padding: "0 !important",
border: 0,
},
auditLogHeader: {
padding: "16px 32px",
},
auditLogHeaderInfo: {
flex: 1,
},
auditLogSummary: (theme) => ({
...(theme.typography.body1 as CSSObject),
fontFamily: "inherit",
}),
auditLogTime: (theme) => ({
color: theme.palette.text.secondary,
fontSize: 12,
}),
auditLogInfo: (theme) => ({
...(theme.typography.body2 as CSSObject),
fontSize: 12,
fontFamily: "inherit",
color: theme.palette.text.secondary,
display: "block",
}),
auditLogInfoHeader: (theme) => ({
margin: 0,
color: theme.palette.text.primary,
fontSize: 14,
lineHeight: "150%",
fontWeight: 600,
}),
auditLogInfoTooltip: {
display: "flex",
flexDirection: "column",
gap: 8,
},
// offset the absence of the arrow icon on diff-less logs
columnWithoutDiff: {
marginLeft: "24px",
},
fullWidth: {
width: "100%",
},
deletedLabel: (theme) => ({
...(theme.typography.caption as CSSObject),
color: theme.palette.text.secondary,
}),
} satisfies Record<string, Interpolation<Theme>>;