chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
import { ArrowRightIcon } from "@heroicons/react/20/solid";
|
||||
import { CheckIcon } from "@heroicons/react/24/solid";
|
||||
import { useLocation, useNavigation } from "@remix-run/react";
|
||||
import { formatDuration } from "@trigger.dev/core/v3/utils/durations";
|
||||
import { RunsIconExtraSmall } from "~/assets/icons/RunsIcon";
|
||||
import { MiddleTruncate } from "~/components/primitives/MiddleTruncate";
|
||||
import { DateTime } from "~/components/primitives/DateTime";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { PopoverMenuItem } from "~/components/primitives/Popover";
|
||||
import { Spinner } from "~/components/primitives/Spinner";
|
||||
import {
|
||||
Table,
|
||||
TableBlankRow,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableCellMenu,
|
||||
TableHeader,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
} from "~/components/primitives/Table";
|
||||
import { SimpleTooltip } from "~/components/primitives/Tooltip";
|
||||
import { LiveTimer } from "~/components/runs/v3/LiveTimer";
|
||||
import { RunTag } from "~/components/runs/v3/RunTag";
|
||||
import { useEnvironment } from "~/hooks/useEnvironment";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import {
|
||||
type SessionListItem,
|
||||
type SessionList,
|
||||
} from "~/presenters/v3/SessionListPresenter.server";
|
||||
import { v3RunPath, v3RunsPath, v3SessionPath } from "~/utils/pathBuilder";
|
||||
import {
|
||||
descriptionForSessionStatus,
|
||||
SessionStatusCombo,
|
||||
allSessionStatuses,
|
||||
} from "./SessionStatus";
|
||||
|
||||
type SessionsTableProps = Pick<SessionList, "sessions" | "filters" | "hasFilters"> & {
|
||||
showTopBorder?: boolean;
|
||||
stickyHeader?: boolean;
|
||||
};
|
||||
|
||||
export function SessionsTable({
|
||||
sessions,
|
||||
hasFilters,
|
||||
showTopBorder = true,
|
||||
stickyHeader = false,
|
||||
}: SessionsTableProps) {
|
||||
const navigation = useNavigation();
|
||||
const location = useLocation();
|
||||
const isLoading =
|
||||
navigation.state !== "idle" && navigation.location?.pathname === location.pathname;
|
||||
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
const environment = useEnvironment();
|
||||
|
||||
return (
|
||||
<Table
|
||||
className="max-h-full overflow-y-auto"
|
||||
showTopBorder={showTopBorder}
|
||||
stickyHeader={stickyHeader}
|
||||
>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell>ID</TableHeaderCell>
|
||||
<TableHeaderCell
|
||||
tooltip={
|
||||
<div className="flex flex-col divide-y divide-grid-dimmed">
|
||||
{allSessionStatuses.map((status) => (
|
||||
<div
|
||||
key={status}
|
||||
className="grid grid-cols-[4rem_1fr] gap-x-3 py-2 first:pt-1 last:pb-1"
|
||||
>
|
||||
<div className="mb-0.5 flex items-center gap-1.5 whitespace-nowrap">
|
||||
<SessionStatusCombo status={status} />
|
||||
</div>
|
||||
<Paragraph variant="extra-small" className="text-wrap! text-text-dimmed">
|
||||
{descriptionForSessionStatus(status)}
|
||||
</Paragraph>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
disableTooltipHoverableContent
|
||||
>
|
||||
Status
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell>Type</TableHeaderCell>
|
||||
<TableHeaderCell>Agent ID</TableHeaderCell>
|
||||
<TableHeaderCell>Test</TableHeaderCell>
|
||||
<TableHeaderCell>Tags</TableHeaderCell>
|
||||
<TableHeaderCell>Created</TableHeaderCell>
|
||||
<TableHeaderCell>Duration</TableHeaderCell>
|
||||
<TableHeaderCell>
|
||||
<span className="sr-only">Actions</span>
|
||||
</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{sessions.length === 0 ? (
|
||||
<TableBlankRow colSpan={9}>
|
||||
<div className="flex items-center justify-center">
|
||||
<Paragraph className="w-auto">
|
||||
{hasFilters
|
||||
? "No sessions match these filters"
|
||||
: "No sessions in this environment yet"}
|
||||
</Paragraph>
|
||||
</div>
|
||||
</TableBlankRow>
|
||||
) : (
|
||||
sessions.map((session) => {
|
||||
const runPath = session.currentRunFriendlyId
|
||||
? v3RunPath(organization, project, environment, {
|
||||
friendlyId: session.currentRunFriendlyId,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const displayId = session.externalId ?? session.friendlyId;
|
||||
const sessionPath = v3SessionPath(organization, project, environment, {
|
||||
friendlyId: session.friendlyId,
|
||||
});
|
||||
const allRunsPath = v3RunsPath(organization, project, environment, {
|
||||
tags: [`chat:${displayId}`],
|
||||
});
|
||||
|
||||
return (
|
||||
<TableRow key={session.id}>
|
||||
<TableCell to={sessionPath} isTabbableCell>
|
||||
<div className="w-[28ch]">
|
||||
<MiddleTruncate text={displayId} className="font-mono text-xs" />
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell to={sessionPath}>
|
||||
<SimpleTooltip
|
||||
content={descriptionForSessionStatus(session.status)}
|
||||
disableHoverableContent
|
||||
button={<SessionStatusCombo status={session.status} />}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell to={sessionPath}>
|
||||
<span className="font-mono text-xs">{session.type}</span>
|
||||
</TableCell>
|
||||
<TableCell to={sessionPath}>
|
||||
<div className="w-[24ch]">
|
||||
<MiddleTruncate text={session.taskIdentifier} className="font-mono text-xs" />
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell to={sessionPath}>
|
||||
<span className="sr-only">{session.isTest ? "Yes" : "No"}</span>
|
||||
{session.isTest ? (
|
||||
<CheckIcon
|
||||
aria-hidden
|
||||
className="size-4 text-text-dimmed group-hover/table-row:text-text-bright"
|
||||
/>
|
||||
) : (
|
||||
<span aria-hidden className="text-text-dimmed">
|
||||
–
|
||||
</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell to={sessionPath}>
|
||||
{session.tags.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{session.tags.map((tag) => (
|
||||
<RunTag key={tag} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-text-dimmed">–</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell to={sessionPath}>
|
||||
<DateTime date={session.createdAt} />
|
||||
</TableCell>
|
||||
<TableCell to={sessionPath} className="w-[1%]" actionClassName="pr-0 tabular-nums">
|
||||
<SessionDuration session={session} />
|
||||
</TableCell>
|
||||
<SessionActionsCell runPath={runPath} allRunsPath={allRunsPath} />
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
)}
|
||||
{isLoading && (
|
||||
<TableBlankRow
|
||||
colSpan={9}
|
||||
className="absolute left-0 top-0 flex h-full w-full items-center justify-center gap-2 bg-background-deep/90"
|
||||
>
|
||||
<Spinner /> <span className="text-text-dimmed">Loading…</span>
|
||||
</TableBlankRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
function SessionDuration({ session }: { session: SessionListItem }) {
|
||||
// Active sessions tick live; closed/expired sessions freeze at the
|
||||
// moment they ended (closedAt for explicit closes, expiresAt when the
|
||||
// TTL ran out without a close call).
|
||||
const endedAt =
|
||||
session.status === "CLOSED"
|
||||
? session.closedAt
|
||||
: session.status === "EXPIRED"
|
||||
? session.expiresAt
|
||||
: undefined;
|
||||
|
||||
if (endedAt) {
|
||||
return (
|
||||
<>{formatDuration(new Date(session.createdAt), new Date(endedAt), { style: "short" })}</>
|
||||
);
|
||||
}
|
||||
|
||||
return <LiveTimer startTime={new Date(session.createdAt)} />;
|
||||
}
|
||||
|
||||
function SessionActionsCell({ runPath, allRunsPath }: { runPath?: string; allRunsPath: string }) {
|
||||
return (
|
||||
<TableCellMenu
|
||||
isSticky
|
||||
popoverContent={
|
||||
<>
|
||||
{runPath && (
|
||||
<PopoverMenuItem
|
||||
to={runPath}
|
||||
icon={ArrowRightIcon}
|
||||
leadingIconClassName="text-runs"
|
||||
title="View current run"
|
||||
/>
|
||||
)}
|
||||
<PopoverMenuItem
|
||||
to={allRunsPath}
|
||||
icon={RunsIconExtraSmall}
|
||||
leadingIconClassName="text-runs"
|
||||
title="View all runs"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user