-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #1354 fix: improve playbook run as suggested in the PR feat: add duration and date columns to the runs list
- Loading branch information
1 parent
3400e95
commit 53da9c3
Showing
12 changed files
with
670 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { formatLongDate, relativeDateTime } from "../../../utils/date"; | ||
import { PlaybookRunAction } from "./PlaybookRunsActions"; | ||
import PlaybookRunsStatus from "./PlaybookRunsStatus"; | ||
|
||
type PlaybookRunsActionItemProps = { | ||
action: PlaybookRunAction; | ||
onClick?: () => void; | ||
isSelected?: boolean; | ||
}; | ||
|
||
export default function PlaybookRunsActionItem({ | ||
action, | ||
onClick = () => {}, | ||
isSelected = false | ||
}: PlaybookRunsActionItemProps) { | ||
return ( | ||
<div | ||
role="button" | ||
onClick={onClick} | ||
key={action.id} | ||
className="flex flex-row items-center justify-between px-4 py-2 bg-white border border-gray-200 hover:bg-gray-200 rounded cursor-pointer" | ||
> | ||
<div className="flex flex-col"> | ||
<div className="text-sm font-medium text-gray-600">{action.name}</div> | ||
<div className={`text-xs flex flex-row gap-1 items-center`}> | ||
<PlaybookRunsStatus status={action.status} /> | ||
</div> | ||
</div> | ||
<div className="flex flex-col"> | ||
<div className="text-sm font-medium text-gray-600"> | ||
{" "} | ||
{!isSelected | ||
? relativeDateTime(action.start_time!, action.end_time) | ||
: relativeDateTime(action.start_time!)} | ||
</div> | ||
{isSelected && ( | ||
<div className="text-sm font-medium text-gray-500"> | ||
{relativeDateTime(action.end_time!)} | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} |
86 changes: 86 additions & 0 deletions
86
src/components/Playbooks/Runs/PlaybookRunsActions.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { StoryObj } from "@storybook/react"; | ||
import PlaybookRunsActions, { | ||
PlaybookRunWithActions | ||
} from "./PlaybookRunsActions"; | ||
|
||
export default { | ||
title: "PlaybookRunsActions", | ||
component: PlaybookRunsActions | ||
}; | ||
|
||
const mockPlaybookRun: PlaybookRunWithActions = { | ||
id: "1", | ||
playbook_id: "1", | ||
status: "completed", | ||
created_at: "2021-08-19T07:00:00.000Z", | ||
start_time: "2021-08-19T07:00:00.000Z", | ||
end_time: "2021-08-19T07:00:00.000Z", | ||
created_by: { | ||
id: "1", | ||
name: "John Doe", | ||
email: "" | ||
}, | ||
component_id: "1", | ||
parameters: {}, | ||
component: { | ||
id: "1", | ||
name: "Topology 1", | ||
icon: "TopologyIcon" | ||
}, | ||
actions: [ | ||
{ | ||
id: "1", | ||
name: "Action 1", | ||
status: "completed", | ||
end_time: "2021-08-19T07:00:00.000Z", | ||
start_time: "2021-08-19T07:00:00.000Z", | ||
playbook_run_id: "1", | ||
result: `You can also use variant modifiers to target media queries like responsive breakpoints, dark mode, prefers-reduced-motion, and more. For example, use md:font-serif to apply the font-serif utility at only medium screen sizes and above. | ||
You can also use variant modifiers to target media queries like responsive breakpoints, dark mode, prefers-reduced-motion, and more. For example, use md:font-serif to apply the font-serif utility at only medium screen sizes and above. | ||
You can also use variant modifiers to target media queries like responsive breakpoints, dark mode, prefers-reduced-motion, and more. For example, use md:font-serif to apply the font-serif utility at only medium screen sizes and above. | ||
You can also use variant modifiers to target media queries like responsive breakpoints, dark mode, prefers-reduced-motion, and more. For example, use md:font-serif to apply the font-serif utility at only medium screen sizes and above.`, | ||
error: "" | ||
}, | ||
{ | ||
id: "2", | ||
name: "Action 2", | ||
status: "completed", | ||
end_time: "2021-08-19T07:00:00.000Z", | ||
start_time: "2021-08-19T07:00:00.000Z", | ||
playbook_run_id: "1", | ||
result: `You can also use variant modifiers to target media queries like responsive breakpoints, dark mode, prefers-reduced-motion, and more. For example, use md:font-serif to apply the font-serif utility at only medium screen sizes and above. | ||
You can also use variant modifiers to target media queries like responsive breakpoints, dark mode, prefers-reduced-motion, and more. For example, use md:font-serif to apply the font-serif utility at only medium screen sizes and above.`, | ||
error: "" | ||
}, | ||
{ | ||
id: "3", | ||
name: "Action 3", | ||
status: "pending", | ||
end_time: undefined, | ||
error: "", | ||
result: "", | ||
start_time: "2021-08-19T07:00:00.000Z", | ||
playbook_run_id: "1" | ||
}, | ||
{ | ||
id: "4", | ||
name: "Action 4", | ||
status: "running", | ||
end_time: undefined, | ||
error: "", | ||
result: "", | ||
start_time: "2021-08-19T07:00:00.000Z", | ||
playbook_run_id: "1" | ||
} | ||
] | ||
}; | ||
|
||
type Story = StoryObj<typeof PlaybookRunsActions>; | ||
|
||
export const Default: Story = { | ||
render: () => <PlaybookRunsActions data={mockPlaybookRun} /> | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { ReactNode, useMemo, useState } from "react"; | ||
import { Link } from "react-router-dom"; | ||
import { relativeDateTime } from "../../../utils/date"; | ||
import { DescriptionCard } from "../../DescriptionCard"; | ||
import { Icon } from "../../Icon"; | ||
import PlaybookRunsActionItem from "./PlaybookRunsActionItem"; | ||
import { PlaybookRun, PlaybookRunStatus } from "./PlaybookRunsList"; | ||
import PlaybookRunsStatus from "./PlaybookRunsStatus"; | ||
import { Avatar } from "../../Avatar"; | ||
|
||
export type PlaybookRunAction = { | ||
id: string; | ||
name: string; | ||
status: PlaybookRunStatus; | ||
playbook_run_id: string; | ||
start_time?: string; | ||
end_time?: string; | ||
result?: { | ||
stdout?: string; | ||
}; | ||
error?: string; | ||
}; | ||
|
||
export type PlaybookRunWithActions = PlaybookRun & { | ||
actions: PlaybookRunAction[]; | ||
}; | ||
|
||
type PlaybookRunActionsProps = { | ||
data: PlaybookRunWithActions; | ||
}; | ||
|
||
export default function PlaybookRunsActions({ data }: PlaybookRunActionsProps) { | ||
const [selectedAction, setSelectedAction] = useState<PlaybookRunAction>(); | ||
|
||
const headerContent = useMemo( | ||
() => | ||
new Map<string, ReactNode>([ | ||
[ | ||
"Triggered By", | ||
data.created_by ? <Avatar user={data.created_by} /> : null | ||
], | ||
[ | ||
"Playbook", | ||
<Link | ||
className="text-blue-500 hover:underline" | ||
to={`/playbooks/${data.playbook_id}`} | ||
> | ||
{data.playbooks?.name} | ||
</Link> | ||
], | ||
[ | ||
"Component", | ||
<Link | ||
className="text-blue-500 hover:underline" | ||
to={`/topologies/${data.component_id}`} | ||
> | ||
<Icon name={data.component?.icon} className="mr-1 h-5 w-5" /> | ||
{data.component?.name} | ||
</Link> | ||
], | ||
[ | ||
"Status", | ||
<div className="flex flex-row gap-2 items-center"> | ||
<PlaybookRunsStatus status={data.status} /> | ||
</div> | ||
], | ||
["Start Time", relativeDateTime(data.start_time)], | ||
["Duration", relativeDateTime(data.start_time, data.end_time)] | ||
]), | ||
[data] | ||
); | ||
|
||
return ( | ||
<div className="flex flex-col flex-1 gap-4"> | ||
<div className="flex flex-row gap-2 px-4 py-2"> | ||
<DescriptionCard | ||
className="flex flex-wrap gap-2" | ||
title="Playbook" | ||
contentClassName="font-semibold text-gray-600" | ||
columns={5} | ||
labelStyle="top" | ||
items={Array.from(headerContent.entries()).map(([label, value]) => ({ | ||
label, | ||
value | ||
}))} | ||
/> | ||
</div> | ||
<div className="flex flex-row h-full"> | ||
<div className="flex flex-col w-[30rem] h-full px-2 border-r border-gray-200"> | ||
<div className="flex flex-row items-center justify-between px-4 py-2 mb-2 border-b border-gray-100"> | ||
<div className="font-semibold text-gray-600">Actions</div> | ||
</div> | ||
<div className="flex flex-col flex-1 overflow-y-auto gap-2"> | ||
{data.actions.map((action) => ( | ||
<PlaybookRunsActionItem | ||
isSelected={selectedAction?.id === action.id} | ||
key={action.id} | ||
action={action} | ||
onClick={() => setSelectedAction(action)} | ||
/> | ||
))} | ||
</div> | ||
</div> | ||
<div className="flex flex-col flex-1 h-full font-mono px-4 py-2 text-white bg-gray-700"> | ||
{selectedAction && ( | ||
<> | ||
<div className="flex flex-col gap-2 py-6"> | ||
<div className="flex flex-row gap-4 items-center"> | ||
<PlaybookRunsStatus | ||
status={selectedAction.status} | ||
hideStatusLabel | ||
className="flex-shrink-0" | ||
/> | ||
<div className="font-semibold text-xl"> | ||
{selectedAction?.name} | ||
</div> | ||
</div> | ||
<div className="text-sm"> | ||
Duration:{" "} | ||
{relativeDateTime( | ||
selectedAction.start_time!, | ||
selectedAction.end_time | ||
)} | ||
</div> | ||
</div> | ||
|
||
<div className="flex flex-col gap-2 whitespace-pre-wrap "> | ||
{selectedAction.result?.stdout | ||
? selectedAction.result.stdout | ||
: selectedAction.error || "No result"} | ||
</div> | ||
</> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.