Share some inspector timeline components
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { ReactNode } from "react";
|
||||
import { cn } from "~/utils/cn";
|
||||
|
||||
type RunTimelineItemProps = {
|
||||
title: ReactNode;
|
||||
subtitle?: ReactNode;
|
||||
state: "complete" | "error";
|
||||
};
|
||||
|
||||
export function RunTimelineEvent({ title, subtitle, state }: RunTimelineItemProps) {
|
||||
return (
|
||||
<div className="grid h-5 grid-cols-[1.125rem_1fr] text-sm">
|
||||
<div className="flex items-center justify-center">
|
||||
<div
|
||||
className={cn(
|
||||
"size-[0.3125rem] rounded-full",
|
||||
state === "complete" ? "bg-success" : "bg-error"
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
<div className="flex items-baseline justify-between gap-3">
|
||||
<span className="font-medium text-text-bright">{title}</span>
|
||||
{subtitle ? <span className="text-xs text-text-dimmed">{subtitle}</span> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type RunTimelineLineProps = {
|
||||
title: ReactNode;
|
||||
state: "complete" | "delayed" | "inprogress";
|
||||
};
|
||||
|
||||
export function RunTimelineLine({ title, state }: RunTimelineLineProps) {
|
||||
return (
|
||||
<div className="grid h-6 grid-cols-[1.125rem_1fr] text-xs">
|
||||
<div className="flex items-stretch justify-center">
|
||||
<div
|
||||
className={cn(
|
||||
"w-px",
|
||||
state === "complete" ? "bg-success" : state === "delayed" ? "bg-text-dimmed" : ""
|
||||
)}
|
||||
style={
|
||||
state === "inprogress"
|
||||
? {
|
||||
width: "1px",
|
||||
height: "100%",
|
||||
background:
|
||||
"repeating-linear-gradient(to bottom, #3B82F6 0%, #3B82F6 50%, transparent 50%, transparent 100%)",
|
||||
backgroundSize: "1px 6px",
|
||||
maskImage: "linear-gradient(to bottom, black 50%, transparent 100%)",
|
||||
WebkitMaskImage: "linear-gradient(to bottom, black 50%, transparent 100%)",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
></div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-text-dimmed">{title}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
import { CheckIcon, ClockIcon, CloudArrowDownIcon, QueueListIcon } from "@heroicons/react/20/solid";
|
||||
import {
|
||||
formatDuration,
|
||||
formatDurationMilliseconds,
|
||||
nanosecondsToMilliseconds,
|
||||
TaskRunError,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import { Link } from "@remix-run/react";
|
||||
import { formatDuration, formatDurationMilliseconds, TaskRunError } from "@trigger.dev/core/v3";
|
||||
import { useEffect } from "react";
|
||||
import { useTypedFetcher } from "remix-typedjson";
|
||||
import { ExitIcon } from "~/assets/icons/ExitIcon";
|
||||
import { CodeBlock } from "~/components/code/CodeBlock";
|
||||
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
|
||||
@@ -25,6 +22,7 @@ import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { useSearchParams } from "~/hooks/useSearchParam";
|
||||
import { RawRun } from "~/hooks/useSyncTraceRuns";
|
||||
import { loader } from "~/routes/resources.runs.$runParam";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { formatCurrencyAccurate } from "~/utils/numberFormatter";
|
||||
import {
|
||||
@@ -35,15 +33,12 @@ import {
|
||||
v3SchedulePath,
|
||||
v3TraceSpanPath,
|
||||
} from "~/utils/pathBuilder";
|
||||
import { TraceSpan } from "~/utils/taskEvent";
|
||||
import { SpanLink } from "~/v3/eventRepository.server";
|
||||
import { isFinalRunStatus } from "~/v3/taskStatus";
|
||||
import { TaskRunStatusCombo } from "./TaskRunStatus";
|
||||
import { useTypedFetcher } from "remix-typedjson";
|
||||
import { RunInspectorData } from "~/routes/resources.runs.$runParam";
|
||||
import { loader } from "~/routes/resources.runs.$runParam";
|
||||
import { Link } from "@remix-run/react";
|
||||
import { RunTimelineEvent, RunTimelineLine } from "./InspectorTimeline";
|
||||
import { RunTag } from "./RunTag";
|
||||
import { TraceSpan } from "~/utils/taskEvent";
|
||||
import { TaskRunStatusCombo } from "./TaskRunStatus";
|
||||
|
||||
/**
|
||||
* The RunInspector displays live information about a run.
|
||||
@@ -529,67 +524,6 @@ function RunTimeline({ run }: { run: RawRun }) {
|
||||
);
|
||||
}
|
||||
|
||||
type RunTimelineItemProps = {
|
||||
title: ReactNode;
|
||||
subtitle?: ReactNode;
|
||||
state: "complete" | "error";
|
||||
};
|
||||
|
||||
function RunTimelineEvent({ title, subtitle, state }: RunTimelineItemProps) {
|
||||
return (
|
||||
<div className="grid h-5 grid-cols-[1.125rem_1fr] text-sm">
|
||||
<div className="flex items-center justify-center">
|
||||
<div
|
||||
className={cn(
|
||||
"size-[0.3125rem] rounded-full",
|
||||
state === "complete" ? "bg-success" : "bg-error"
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
<div className="flex items-baseline justify-between gap-3">
|
||||
<span className="font-medium text-text-bright">{title}</span>
|
||||
{subtitle ? <span className="text-xs text-text-dimmed">{subtitle}</span> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type RunTimelineLineProps = {
|
||||
title: ReactNode;
|
||||
state: "complete" | "delayed" | "inprogress";
|
||||
};
|
||||
|
||||
function RunTimelineLine({ title, state }: RunTimelineLineProps) {
|
||||
return (
|
||||
<div className="grid h-6 grid-cols-[1.125rem_1fr] text-xs">
|
||||
<div className="flex items-stretch justify-center">
|
||||
<div
|
||||
className={cn(
|
||||
"w-px",
|
||||
state === "complete" ? "bg-success" : state === "delayed" ? "bg-text-dimmed" : ""
|
||||
)}
|
||||
style={
|
||||
state === "inprogress"
|
||||
? {
|
||||
width: "1px",
|
||||
height: "100%",
|
||||
background:
|
||||
"repeating-linear-gradient(to bottom, #3B82F6 0%, #3B82F6 50%, transparent 50%, transparent 100%)",
|
||||
backgroundSize: "1px 6px",
|
||||
maskImage: "linear-gradient(to bottom, black 50%, transparent 100%)",
|
||||
WebkitMaskImage: "linear-gradient(to bottom, black 50%, transparent 100%)",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
></div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-text-dimmed">{title}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RunError({ error }: { error: TaskRunError }) {
|
||||
switch (error.type) {
|
||||
case "STRING_ERROR":
|
||||
@@ -673,91 +607,6 @@ function PacketDisplay({
|
||||
}
|
||||
}
|
||||
|
||||
type TimelineProps = {
|
||||
startTime: Date;
|
||||
duration: number;
|
||||
inProgress: boolean;
|
||||
isError: boolean;
|
||||
};
|
||||
|
||||
type TimelineState = "error" | "pending" | "complete";
|
||||
|
||||
function SpanTimeline({ startTime, duration, inProgress, isError }: TimelineProps) {
|
||||
const state = isError ? "error" : inProgress ? "pending" : "complete";
|
||||
return (
|
||||
<>
|
||||
<div className="min-w-fit max-w-80">
|
||||
<RunTimelineEvent
|
||||
title="Started"
|
||||
subtitle={<DateTimeAccurate date={startTime} />}
|
||||
state="complete"
|
||||
/>
|
||||
{state === "pending" ? (
|
||||
<RunTimelineLine
|
||||
title={
|
||||
<span className="flex items-center gap-1">
|
||||
<Spinner className="size-4" />
|
||||
<span>
|
||||
<LiveTimer startTime={startTime} />
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
state={"inprogress"}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<RunTimelineLine
|
||||
title={formatDuration(
|
||||
startTime,
|
||||
new Date(startTime.getTime() + nanosecondsToMilliseconds(duration))
|
||||
)}
|
||||
state={"complete"}
|
||||
/>
|
||||
<RunTimelineEvent
|
||||
title="Finished"
|
||||
subtitle={
|
||||
<DateTimeAccurate
|
||||
date={new Date(startTime.getTime() + nanosecondsToMilliseconds(duration))}
|
||||
/>
|
||||
}
|
||||
state={isError ? "error" : "complete"}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function VerticalBar({ state }: { state: TimelineState }) {
|
||||
return <div className={cn("h-3 w-0.75 rounded-full", classNameForState(state))}></div>;
|
||||
}
|
||||
|
||||
function DottedLine() {
|
||||
return (
|
||||
<div className="flex h-0.75 flex-1 items-center justify-evenly">
|
||||
<div className="h-0.75 w-0.75 bg-pending" />
|
||||
<div className="h-0.75 w-0.75 bg-pending" />
|
||||
<div className="h-0.75 w-0.75 bg-pending" />
|
||||
<div className="h-0.75 w-0.75 bg-pending" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function classNameForState(state: TimelineState) {
|
||||
switch (state) {
|
||||
case "pending": {
|
||||
return "bg-pending";
|
||||
}
|
||||
case "complete": {
|
||||
return "bg-success";
|
||||
}
|
||||
case "error": {
|
||||
return "bg-error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function SpanLinkElement({ link }: { link: SpanLink }) {
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { formatDuration, nanosecondsToMilliseconds } from "@trigger.dev/core/v3";
|
||||
import { ReactNode } from "react";
|
||||
import { ExitIcon } from "~/assets/icons/ExitIcon";
|
||||
import { CodeBlock } from "~/components/code/CodeBlock";
|
||||
import { Button } from "~/components/primitives/Buttons";
|
||||
import { DateTimeAccurate } from "~/components/primitives/DateTime";
|
||||
import { Header2 } from "~/components/primitives/Headers";
|
||||
import * as Property from "~/components/primitives/PropertyTable";
|
||||
import { Spinner } from "~/components/primitives/Spinner";
|
||||
import { TabButton, TabContainer } from "~/components/primitives/Tabs";
|
||||
import { TextLink } from "~/components/primitives/TextLink";
|
||||
import { InfoIconTooltip, SimpleTooltip } from "~/components/primitives/Tooltip";
|
||||
import { LiveTimer } from "~/components/runs/v3/LiveTimer";
|
||||
import { RunIcon } from "~/components/runs/v3/RunIcon";
|
||||
import { SpanEvents } from "~/components/runs/v3/SpanEvents";
|
||||
import { SpanTitle } from "~/components/runs/v3/SpanTitle";
|
||||
@@ -22,6 +18,10 @@ import { cn } from "~/utils/cn";
|
||||
import { v3RunPath, v3RunsPath, v3TraceSpanPath } from "~/utils/pathBuilder";
|
||||
import { TraceSpan } from "~/utils/taskEvent";
|
||||
import { SpanLink } from "~/v3/eventRepository.server";
|
||||
import { RunTimelineEvent, RunTimelineLine } from "./InspectorTimeline";
|
||||
import { Spinner } from "~/components/primitives/Spinner";
|
||||
import { LiveTimer } from "./LiveTimer";
|
||||
import { formatDuration, nanosecondsToMilliseconds } from "@trigger.dev/core/v3";
|
||||
|
||||
export function SpanInspector({
|
||||
span,
|
||||
@@ -234,67 +234,6 @@ export function SpanInspector({
|
||||
);
|
||||
}
|
||||
|
||||
type RunTimelineItemProps = {
|
||||
title: ReactNode;
|
||||
subtitle?: ReactNode;
|
||||
state: "complete" | "error";
|
||||
};
|
||||
|
||||
function RunTimelineEvent({ title, subtitle, state }: RunTimelineItemProps) {
|
||||
return (
|
||||
<div className="grid h-5 grid-cols-[1.125rem_1fr] text-sm">
|
||||
<div className="flex items-center justify-center">
|
||||
<div
|
||||
className={cn(
|
||||
"size-[0.3125rem] rounded-full",
|
||||
state === "complete" ? "bg-success" : "bg-error"
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
<div className="flex items-baseline justify-between gap-3">
|
||||
<span className="font-medium text-text-bright">{title}</span>
|
||||
{subtitle ? <span className="text-xs text-text-dimmed">{subtitle}</span> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type RunTimelineLineProps = {
|
||||
title: ReactNode;
|
||||
state: "complete" | "delayed" | "inprogress";
|
||||
};
|
||||
|
||||
function RunTimelineLine({ title, state }: RunTimelineLineProps) {
|
||||
return (
|
||||
<div className="grid h-6 grid-cols-[1.125rem_1fr] text-xs">
|
||||
<div className="flex items-stretch justify-center">
|
||||
<div
|
||||
className={cn(
|
||||
"w-px",
|
||||
state === "complete" ? "bg-success" : state === "delayed" ? "bg-text-dimmed" : ""
|
||||
)}
|
||||
style={
|
||||
state === "inprogress"
|
||||
? {
|
||||
width: "1px",
|
||||
height: "100%",
|
||||
background:
|
||||
"repeating-linear-gradient(to bottom, #3B82F6 0%, #3B82F6 50%, transparent 50%, transparent 100%)",
|
||||
backgroundSize: "1px 6px",
|
||||
maskImage: "linear-gradient(to bottom, black 50%, transparent 100%)",
|
||||
WebkitMaskImage: "linear-gradient(to bottom, black 50%, transparent 100%)",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
></div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-text-dimmed">{title}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type TimelineProps = {
|
||||
startTime: Date;
|
||||
duration: number;
|
||||
@@ -302,9 +241,7 @@ type TimelineProps = {
|
||||
isError: boolean;
|
||||
};
|
||||
|
||||
type TimelineState = "error" | "pending" | "complete";
|
||||
|
||||
function SpanTimeline({ startTime, duration, inProgress, isError }: TimelineProps) {
|
||||
export function SpanTimeline({ startTime, duration, inProgress, isError }: TimelineProps) {
|
||||
const state = isError ? "error" : inProgress ? "pending" : "complete";
|
||||
return (
|
||||
<>
|
||||
@@ -351,20 +288,6 @@ function SpanTimeline({ startTime, duration, inProgress, isError }: TimelineProp
|
||||
);
|
||||
}
|
||||
|
||||
function classNameForState(state: TimelineState) {
|
||||
switch (state) {
|
||||
case "pending": {
|
||||
return "bg-pending";
|
||||
}
|
||||
case "complete": {
|
||||
return "bg-success";
|
||||
}
|
||||
case "error": {
|
||||
return "bg-error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function SpanLinkElement({ link }: { link: SpanLink }) {
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
|
||||
Reference in New Issue
Block a user