From e4340d92ce772b9df8fc07f51c087f2f573db4ec Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 16 Aug 2023 18:07:06 +0000 Subject: [PATCH 1/5] Add base changes --- site/src/api/api.ts | 4 +- site/src/components/BuildIcon/BuildIcon.tsx | 22 ++ .../components/BuildsTable/BuildAvatar.tsx | 14 +- site/src/components/Sidebar/Sidebar.tsx | 42 ++++ .../WorkspaceBuildStats.stories.tsx | 37 ---- .../WorkspaceBuildStats.tsx | 38 ---- .../WorkspaceBuildPage/WorkspaceBuildPage.tsx | 30 ++- .../WorkspaceBuildPageView.tsx | 208 +++++++++++++++--- .../WorkspacePage/WorkspaceReadyPage.tsx | 2 +- site/src/utils/workspace.tsx | 2 +- .../workspaceBuild/workspaceBuildXService.ts | 4 +- 11 files changed, 276 insertions(+), 127 deletions(-) create mode 100644 site/src/components/BuildIcon/BuildIcon.tsx create mode 100644 site/src/components/Sidebar/Sidebar.tsx delete mode 100644 site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.stories.tsx delete mode 100644 site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 4faea039e3792..4d5c88fe74b16 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -488,7 +488,7 @@ export function waitForBuild(build: TypesGen.WorkspaceBuild) { const { job } = await getWorkspaceBuildByNumber( build.workspace_owner_name, build.workspace_name, - String(build.build_number), + build.build_number, ) latestJobInfo = job @@ -772,7 +772,7 @@ export const getWorkspaceBuilds = async ( export const getWorkspaceBuildByNumber = async ( username = "me", workspaceName: string, - buildNumber: string, + buildNumber: number, ): Promise => { const response = await axios.get( `/api/v2/users/${username}/workspace/${workspaceName}/builds/${buildNumber}`, diff --git a/site/src/components/BuildIcon/BuildIcon.tsx b/site/src/components/BuildIcon/BuildIcon.tsx new file mode 100644 index 0000000000000..ccdc68da1cb3e --- /dev/null +++ b/site/src/components/BuildIcon/BuildIcon.tsx @@ -0,0 +1,22 @@ +import PlayArrowOutlined from "@mui/icons-material/PlayArrowOutlined" +import StopOutlined from "@mui/icons-material/StopOutlined" +import DeleteOutlined from "@mui/icons-material/DeleteOutlined" +import { WorkspaceTransition } from "api/typesGenerated" +import { ComponentProps } from "react" + +type SVGIcon = typeof PlayArrowOutlined + +type SVGIconProps = ComponentProps + +const iconByTransition: Record = { + start: PlayArrowOutlined, + stop: StopOutlined, + delete: DeleteOutlined, +} + +export const BuildIcon = ( + props: SVGIconProps & { transition: WorkspaceTransition }, +) => { + const Icon = iconByTransition[props.transition] + return +} diff --git a/site/src/components/BuildsTable/BuildAvatar.tsx b/site/src/components/BuildsTable/BuildAvatar.tsx index e095bda76a32e..3733db4650ffc 100644 --- a/site/src/components/BuildsTable/BuildAvatar.tsx +++ b/site/src/components/BuildsTable/BuildAvatar.tsx @@ -1,14 +1,12 @@ import Badge from "@mui/material/Badge" import { useTheme, withStyles } from "@mui/styles" import { FC } from "react" -import PlayArrowOutlined from "@mui/icons-material/PlayArrowOutlined" -import PauseOutlined from "@mui/icons-material/PauseOutlined" -import DeleteOutlined from "@mui/icons-material/DeleteOutlined" -import { WorkspaceBuild, WorkspaceTransition } from "api/typesGenerated" +import { WorkspaceBuild } from "api/typesGenerated" import { getDisplayWorkspaceBuildStatus } from "utils/workspace" import { Avatar, AvatarProps } from "components/Avatar/Avatar" import { PaletteIndex } from "theme/theme" import { Theme } from "@mui/material/styles" +import { BuildIcon } from "components/BuildIcon/BuildIcon" interface StylesBadgeProps { type: PaletteIndex @@ -31,12 +29,6 @@ export interface BuildAvatarProps { size?: AvatarProps["size"] } -const iconByTransition: Record = { - start: , - stop: , - delete: , -} - export const BuildAvatar: FC = ({ build, size }) => { const theme = useTheme() const displayBuildStatus = getDisplayWorkspaceBuildStatus(theme, build) @@ -55,7 +47,7 @@ export const BuildAvatar: FC = ({ build, size }) => { badgeContent={
} > - {iconByTransition[build.transition]} + ) diff --git a/site/src/components/Sidebar/Sidebar.tsx b/site/src/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000000000..5222e183f8bba --- /dev/null +++ b/site/src/components/Sidebar/Sidebar.tsx @@ -0,0 +1,42 @@ +import Box, { BoxProps } from "@mui/material/Box" +import { styled } from "@mui/styles" +import { colors } from "theme/colors" + +export const Sidebar = styled((props: BoxProps) => ( + +))(({ theme }) => ({ + width: theme.spacing(32), + flexShrink: 0, + borderRight: `1px solid ${theme.palette.divider}`, + height: "100%", + overflowY: "auto", +})) + +export const SidebarItem = styled((props: BoxProps & { active?: boolean }) => ( + +))(({ theme, active }) => ({ + background: active ? colors.gray[13] : "none", + border: "none", + fontSize: 14, + width: "100%", + textAlign: "left", + padding: theme.spacing(0, 3), + cursor: "pointer", + pointerEvents: active ? "none" : "auto", + color: active ? theme.palette.text.primary : theme.palette.text.secondary, + "&:hover": { + background: theme.palette.action.hover, + color: theme.palette.text.primary, + }, + paddingTop: theme.spacing(1.25), + paddingBottom: theme.spacing(1.25), +})) + +export const SidebarCaption = styled(Box)(({ theme }) => ({ + fontSize: 10, + textTransform: "uppercase", + fontWeight: 500, + color: theme.palette.text.secondary, + padding: theme.spacing(1.5, 3), + letterSpacing: "0.5px", +})) diff --git a/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.stories.tsx b/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.stories.tsx deleted file mode 100644 index 2bd93dc11f943..0000000000000 --- a/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.stories.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { ComponentMeta, Story } from "@storybook/react" -import { MockWorkspaceBuild } from "../../testHelpers/entities" -import { - WorkspaceBuildStats, - WorkspaceBuildStatsProps, -} from "./WorkspaceBuildStats" - -export default { - title: "components/WorkspaceBuildStats", - component: WorkspaceBuildStats, -} as ComponentMeta - -const Template: Story = (args) => ( - -) - -export const Example = Template.bind({}) -Example.args = { - build: MockWorkspaceBuild, -} - -export const Autostart = Template.bind({}) -Autostart.args = { - build: { - ...MockWorkspaceBuild, - reason: "autostart", - }, -} - -export const Autostop = Template.bind({}) -Autostop.args = { - build: { - ...MockWorkspaceBuild, - transition: "stop", - reason: "autostop", - }, -} diff --git a/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx b/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx deleted file mode 100644 index e6500b233ee73..0000000000000 --- a/site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Stats, StatsItem } from "components/Stats/Stats" -import { FC } from "react" -import { useTranslation } from "react-i18next" -import { Link } from "react-router-dom" -import { WorkspaceBuild } from "../../api/typesGenerated" -import { displayWorkspaceBuildDuration } from "../../utils/workspace" - -export interface WorkspaceBuildStatsProps { - build: WorkspaceBuild -} - -export const WorkspaceBuildStats: FC = ({ - build, -}) => { - const { t } = useTranslation("buildPage") - - return ( - - - {build.workspace_name} - - } - /> - - - - - ) -} diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx index 3cffeeff8456b..9f48e92846c7d 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx @@ -1,10 +1,13 @@ import { useMachine } from "@xstate/react" -import { FC } from "react" +import { FC, useEffect } from "react" import { Helmet } from "react-helmet-async" import { useParams } from "react-router-dom" import { pageTitle } from "../../utils/page" import { workspaceBuildMachine } from "../../xServices/workspaceBuild/workspaceBuildXService" import { WorkspaceBuildPageView } from "./WorkspaceBuildPageView" +import { useQuery } from "@tanstack/react-query" +import { getWorkspaceBuilds } from "api/api" +import dayjs from "dayjs" export const WorkspaceBuildPage: FC = () => { const params = useParams() as { @@ -13,12 +16,26 @@ export const WorkspaceBuildPage: FC = () => { buildNumber: string } const workspaceName = params.workspace - const buildNumber = params.buildNumber + const buildNumber = Number(params.buildNumber) const username = params.username.replace("@", "") - const [buildState] = useMachine(workspaceBuildMachine, { + const [buildState, send] = useMachine(workspaceBuildMachine, { context: { username, workspaceName, buildNumber, timeCursor: new Date() }, }) const { logs, build } = buildState.context + const { data: builds } = useQuery({ + queryKey: ["builds", username, build?.workspace_id], + queryFn: () => { + return getWorkspaceBuilds( + build?.workspace_id ?? "", + dayjs().add(-30, "day").toDate(), + ) + }, + enabled: Boolean(build), + }) + + useEffect(() => { + send("RESET", { buildNumber, timeCursor: new Date() }) + }, [buildNumber, send]) return ( <> @@ -32,7 +49,12 @@ export const WorkspaceBuildPage: FC = () => { - + ) } diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx index f2bf9905e6608..dfff971a4357e 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx @@ -2,16 +2,29 @@ import { BuildAvatar } from "components/BuildsTable/BuildAvatar" import { FC } from "react" import { ProvisionerJobLog, WorkspaceBuild } from "../../api/typesGenerated" import { Loader } from "../../components/Loader/Loader" -import { Margins } from "../../components/Margins/Margins" -import { - PageHeader, - PageHeaderSubtitle, - PageHeaderTitle, -} from "../../components/PageHeader/PageHeader" import { Stack } from "../../components/Stack/Stack" import { WorkspaceBuildLogs } from "../../components/WorkspaceBuildLogs/WorkspaceBuildLogs" -import { WorkspaceBuildStats } from "../../components/WorkspaceBuildStats/WorkspaceBuildStats" import { WorkspaceBuildStateError } from "./WorkspaceBuildStateError" +import { makeStyles, useTheme } from "@mui/styles" +import { + FullWidthPageHeader, + PageHeaderTitle, + PageHeaderSubtitle, +} from "components/PageHeader/FullWidthPageHeader" +import { Link } from "react-router-dom" +import { Stats, StatsItem } from "components/Stats/Stats" +import { + displayWorkspaceBuildDuration, + getDisplayWorkspaceBuildInitiatedBy, + getDisplayWorkspaceBuildStatus, +} from "utils/workspace" +import Box from "@mui/material/Box" +import { + Sidebar, + SidebarCaption, + SidebarItem, +} from "components/Sidebar/Sidebar" +import { BuildIcon } from "components/BuildIcon/BuildIcon" const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => { return [...logs].sort( @@ -23,38 +36,171 @@ const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => { export interface WorkspaceBuildPageViewProps { logs: ProvisionerJobLog[] | undefined build: WorkspaceBuild | undefined + builds: WorkspaceBuild[] | undefined + activeBuildNumber: number } export const WorkspaceBuildPageView: FC = ({ logs, build, + builds, + activeBuildNumber, }) => { + const styles = useStyles() + const theme = useTheme() + + if (!build) { + return + } + return ( - - {build && ( - - - -
- Build #{build.build_number} - - {build.initiator_name} - -
-
-
- )} - - - {build && - build.transition === "delete" && - build.job.status === "failed" && ( + + + + +
+ Build #{build.build_number} + {build.initiator_name} +
+
+ + + + {build.workspace_name} + + } + /> + + + + +
+ + + + Builds + {builds?.map((b) => ( + + + + + + theme.palette.text.primary, + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + }} + > + {b.transition} by{" "} + {getDisplayWorkspaceBuildInitiatedBy(b)} + + theme.palette.text.secondary, + mt: 0.25, + }} + > + {displayWorkspaceBuildDuration(b)} + + + + + + ))} + + + + {build.transition === "delete" && build.job.status === "failed" && ( )} - {build && } - {!logs && } - {logs && } -
-
+ {!logs && } + {logs && ( + + )} +
+
+ ) } + +const useStyles = makeStyles((theme) => ({ + stats: { + padding: 0, + border: 0, + gap: theme.spacing(6), + rowGap: theme.spacing(3), + flex: 1, + + [theme.breakpoints.down("md")]: { + display: "flex", + flexDirection: "column", + alignItems: "flex-start", + gap: theme.spacing(1), + }, + }, + + statsItem: { + flexDirection: "column", + gap: 0, + padding: 0, + + "& > span:first-of-type": { + fontSize: 12, + fontWeight: 500, + }, + }, +})) diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 3fd3124399da3..4dc714960e57e 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -351,7 +351,7 @@ const WarningDialog: FC< } const useBuildLogs = (workspace: TypesGen.Workspace) => { - const buildNumber = workspace.latest_build.build_number.toString() + const buildNumber = workspace.latest_build.build_number const [buildState, buildSend] = useMachine(workspaceBuildMachine, { context: { buildNumber, diff --git a/site/src/utils/workspace.tsx b/site/src/utils/workspace.tsx index 26b436aafe79e..53f065d98b741 100644 --- a/site/src/utils/workspace.tsx +++ b/site/src/utils/workspace.tsx @@ -37,7 +37,7 @@ export const getDisplayWorkspaceBuildStatus = ( case "succeeded": return { type: "success", - color: theme.palette.success.main, + color: theme.palette.success.light, status: DisplayWorkspaceBuildStatusLanguage.succeeded, } as const case "pending": diff --git a/site/src/xServices/workspaceBuild/workspaceBuildXService.ts b/site/src/xServices/workspaceBuild/workspaceBuildXService.ts index 980cb5c910e42..1156a7b06cc97 100644 --- a/site/src/xServices/workspaceBuild/workspaceBuildXService.ts +++ b/site/src/xServices/workspaceBuild/workspaceBuildXService.ts @@ -6,7 +6,7 @@ type LogsContext = { // Build username: string workspaceName: string - buildNumber: string + buildNumber: number buildId: string // Used to reference logs before + after. timeCursor: Date @@ -26,7 +26,7 @@ type LogsEvent = } | { type: "RESET" - buildNumber: string + buildNumber: number timeCursor: Date } From 6ab1c5f6ae89ae3cf3d4fc9a941945fb81efa40e Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Thu, 17 Aug 2023 13:39:04 +0000 Subject: [PATCH 2/5] Add loader state and template version --- site/src/components/Sidebar/Sidebar.tsx | 8 +- .../WorkspaceBuildPageView.tsx | 134 ++++++++++++------ 2 files changed, 93 insertions(+), 49 deletions(-) diff --git a/site/src/components/Sidebar/Sidebar.tsx b/site/src/components/Sidebar/Sidebar.tsx index 5222e183f8bba..bbcf743f48da9 100644 --- a/site/src/components/Sidebar/Sidebar.tsx +++ b/site/src/components/Sidebar/Sidebar.tsx @@ -12,9 +12,11 @@ export const Sidebar = styled((props: BoxProps) => ( overflowY: "auto", })) -export const SidebarItem = styled((props: BoxProps & { active?: boolean }) => ( - -))(({ theme, active }) => ({ +export const SidebarItem = styled( + ({ active, ...props }: BoxProps & { active?: boolean }) => ( + + ), +)(({ theme, active }) => ({ background: active ? colors.gray[13] : "none", border: "none", fontSize: 14, diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx index dfff971a4357e..190e6f2ba37eb 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx @@ -25,6 +25,7 @@ import { SidebarItem, } from "components/Sidebar/Sidebar" import { BuildIcon } from "components/BuildIcon/BuildIcon" +import Skeleton from "@mui/material/Skeleton" const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => { return [...logs].sort( @@ -47,7 +48,6 @@ export const WorkspaceBuildPageView: FC = ({ activeBuildNumber, }) => { const styles = useStyles() - const theme = useTheme() if (!build) { return @@ -85,6 +85,11 @@ export const WorkspaceBuildPageView: FC = ({ } /> + = ({ + {build.transition} + + } /> @@ -113,50 +122,16 @@ export const WorkspaceBuildPageView: FC = ({ > Builds - {builds?.map((b) => ( - - - - - - theme.palette.text.primary, - textOverflow: "ellipsis", - overflow: "hidden", - whiteSpace: "nowrap", - }} - > - {b.transition} by{" "} - {getDisplayWorkspaceBuildInitiatedBy(b)} - - theme.palette.text.secondary, - mt: 0.25, - }} - > - {displayWorkspaceBuildDuration(b)} - - - - - + {!builds && + [...Array(15).keys()].map((i) => ( + + ))} + {builds?.map((build) => ( + ))} @@ -177,6 +152,73 @@ export const WorkspaceBuildPageView: FC = ({ ) } +const BuildSidebarItem = ({ + build, + active, +}: { + build: WorkspaceBuild + active: boolean +}) => { + const theme = useTheme() + + return ( + + + + + + theme.palette.text.primary, + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + }} + > + {build.transition} by{" "} + {getDisplayWorkspaceBuildInitiatedBy(build)} + + theme.palette.text.secondary, + mt: 0.25, + }} + > + {displayWorkspaceBuildDuration(build)} + + + + + + ) +} + +const BuildSidebarItemSkeleton = () => { + return ( + + + + + + + + + + ) +} + const useStyles = makeStyles((theme) => ({ stats: { padding: 0, From 37542df8cb4fff2229c60b505c1aca4ad0e15506 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Thu, 17 Aug 2023 16:45:24 +0000 Subject: [PATCH 3/5] Fix test --- site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.test.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.test.tsx index 42554fc513b00..13a3bcb4a5af0 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.test.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.test.tsx @@ -22,7 +22,7 @@ describe("WorkspaceBuildPage", () => { expect(getWorkspaceBuildSpy).toBeCalledWith( MockWorkspace.owner_name, MockWorkspace.name, - `${MockWorkspaceBuild.build_number}`, + MockWorkspaceBuild.build_number, ), ) }) From 075d96eb1be095d3296ec5a514b15a1d453b41ec Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Thu, 17 Aug 2023 17:36:43 +0000 Subject: [PATCH 4/5] improve storybook --- .../WorkspaceBuildPageView.stories.tsx | 55 ++++++++++++------- .../WorkspaceBuildPageView.tsx | 38 +++++++++++-- .../WorkspaceBuildStateError.tsx | 36 ------------ site/src/testHelpers/entities.ts | 2 +- 4 files changed, 69 insertions(+), 62 deletions(-) delete mode 100644 site/src/pages/WorkspaceBuildPage/WorkspaceBuildStateError.tsx diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx index bc397dc80ba1a..23b34ea5157e5 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx @@ -1,31 +1,48 @@ -import { ComponentMeta, Story } from "@storybook/react" +import { Meta, StoryObj } from "@storybook/react" import { MockFailedWorkspaceBuild, MockWorkspaceBuild, MockWorkspaceBuildLogs, } from "../../testHelpers/entities" -import { - WorkspaceBuildPageView, - WorkspaceBuildPageViewProps, -} from "./WorkspaceBuildPageView" +import { WorkspaceBuildPageView } from "./WorkspaceBuildPageView" + +const defaultBuilds = [...Array(15).keys()].map((i) => ({ + ...MockWorkspaceBuild, + id: `${i}`, + build_number: i, +})) -export default { - title: "pages/WorkspaceBuildPageView", +const meta: Meta = { + title: "components/WorkspaceBuildPageView", component: WorkspaceBuildPageView, -} as ComponentMeta + args: { + build: MockWorkspaceBuild, + logs: MockWorkspaceBuildLogs, + builds: defaultBuilds, + activeBuildNumber: defaultBuilds[0].build_number, + }, +} -const Template: Story = (args) => ( - -) +export default meta +type Story = StoryObj + +export const Loaded: Story = {} + +export const LoadingBuildLogs: Story = { + args: { + builds: undefined, + }, +} -export const Example = Template.bind({}) -Example.args = { - build: MockWorkspaceBuild, - logs: MockWorkspaceBuildLogs, +const failedBuild = { + ...MockFailedWorkspaceBuild("delete"), + build_number: new Date().getDate(), } -export const FailedDelete = Template.bind({}) -FailedDelete.args = { - build: MockFailedWorkspaceBuild("delete"), - logs: MockWorkspaceBuildLogs, +export const FailedDelete: Story = { + args: { + build: failedBuild, + builds: [failedBuild, ...defaultBuilds], + activeBuildNumber: failedBuild.build_number, + }, } diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx index 190e6f2ba37eb..ad6324931412e 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx @@ -4,8 +4,7 @@ import { ProvisionerJobLog, WorkspaceBuild } from "../../api/typesGenerated" import { Loader } from "../../components/Loader/Loader" import { Stack } from "../../components/Stack/Stack" import { WorkspaceBuildLogs } from "../../components/WorkspaceBuildLogs/WorkspaceBuildLogs" -import { WorkspaceBuildStateError } from "./WorkspaceBuildStateError" -import { makeStyles, useTheme } from "@mui/styles" +import { makeStyles } from "@mui/styles" import { FullWidthPageHeader, PageHeaderTitle, @@ -26,6 +25,7 @@ import { } from "components/Sidebar/Sidebar" import { BuildIcon } from "components/BuildIcon/BuildIcon" import Skeleton from "@mui/material/Skeleton" +import { Alert } from "components/Alert/Alert" const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => { return [...logs].sort( @@ -137,7 +137,33 @@ export const WorkspaceBuildPageView: FC = ({ {build.transition === "delete" && build.job.status === "failed" && ( - + theme.palette.error.dark, + borderBottom: (theme) => `1px solid ${theme.palette.divider}`, + }} + > + + The workspace may have failed to delete due to a Terraform state + mismatch. A template admin may run{" "} + + ` + {`coder rm ${ + build.workspace_owner_name + "/" + build.workspace_name + } --orphan`} + ` + {" "} + to delete the workspace skipping resource destruction. + + )} {!logs && } {logs && ( @@ -159,8 +185,6 @@ const BuildSidebarItem = ({ build: WorkspaceBuild active: boolean }) => { - const theme = useTheme() - return ( + theme.palette[getDisplayWorkspaceBuildStatus(theme, build).type] + .light, }} /> diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildStateError.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildStateError.tsx deleted file mode 100644 index 1a654a3f6715b..0000000000000 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildStateError.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import Box from "@mui/material/Box" -import { WorkspaceBuild } from "api/typesGenerated" -import { Alert } from "components/Alert/Alert" - -const Language = { - stateMessage: - "The workspace may have failed to delete due to a Terraform state mismatch.", -} - -export interface WorkspaceBuildStateErrorProps { - build: WorkspaceBuild -} - -export const WorkspaceBuildStateError: React.FC< - WorkspaceBuildStateErrorProps -> = ({ build }) => { - const orphanCommand = `coder rm ${ - build.workspace_owner_name + "/" + build.workspace_name - } --orphan` - return ( - - - {Language.stateMessage} A template admin may run{" "} - - `{orphanCommand}` - {" "} - to delete the workspace skipping resource destruction. - - - ) -} diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 7f163f489d9ff..777ece4dd47ea 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -815,7 +815,7 @@ export const MockFailedWorkspaceBuild = ( deadline: "2022-05-17T23:39:00.00Z", reason: "initiator", resources: [], - status: "running", + status: "failed", daily_cost: 20, }) From a1928ffe767978d0dada6231570dceb689e1cad0 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Thu, 17 Aug 2023 21:49:56 +0000 Subject: [PATCH 5/5] Solve PR comments --- .../WorkspaceBuildPage/WorkspaceBuildPage.tsx | 3 +++ .../WorkspaceBuildPageView.stories.tsx | 5 +++-- .../WorkspaceBuildPageView.tsx | 18 ++++++++++++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx index 9f48e92846c7d..efdb9aee357cb 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx @@ -8,6 +8,7 @@ import { WorkspaceBuildPageView } from "./WorkspaceBuildPageView" import { useQuery } from "@tanstack/react-query" import { getWorkspaceBuilds } from "api/api" import dayjs from "dayjs" +import { usePermissions } from "hooks" export const WorkspaceBuildPage: FC = () => { const params = useParams() as { @@ -32,6 +33,7 @@ export const WorkspaceBuildPage: FC = () => { }, enabled: Boolean(build), }) + const permissions = usePermissions() useEffect(() => { send("RESET", { buildNumber, timeCursor: new Date() }) @@ -54,6 +56,7 @@ export const WorkspaceBuildPage: FC = () => { build={build} builds={builds} activeBuildNumber={buildNumber} + hasDeploymentBanner={permissions.viewDeploymentStats} /> ) diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx index 23b34ea5157e5..e846a5533abc6 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx @@ -6,20 +6,21 @@ import { } from "../../testHelpers/entities" import { WorkspaceBuildPageView } from "./WorkspaceBuildPageView" -const defaultBuilds = [...Array(15).keys()].map((i) => ({ +const defaultBuilds = Array.from({ length: 15 }, (_, i) => ({ ...MockWorkspaceBuild, id: `${i}`, build_number: i, })) const meta: Meta = { - title: "components/WorkspaceBuildPageView", + title: "pages/WorkspaceBuildPageView", component: WorkspaceBuildPageView, args: { build: MockWorkspaceBuild, logs: MockWorkspaceBuildLogs, builds: defaultBuilds, activeBuildNumber: defaultBuilds[0].build_number, + hasDeploymentBanner: false, }, } diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx index ad6324931412e..5095ae7ea2665 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx @@ -39,6 +39,7 @@ export interface WorkspaceBuildPageViewProps { build: WorkspaceBuild | undefined builds: WorkspaceBuild[] | undefined activeBuildNumber: number + hasDeploymentBanner: boolean } export const WorkspaceBuildPageView: FC = ({ @@ -46,8 +47,14 @@ export const WorkspaceBuildPageView: FC = ({ build, builds, activeBuildNumber, + hasDeploymentBanner, }) => { const styles = useStyles() + const navbarHeight = 62 + const deploymentBannerHeight = 48 + const heightOffset = hasDeploymentBanner + ? navbarHeight + deploymentBannerHeight + : navbarHeight if (!build) { return @@ -56,7 +63,8 @@ export const WorkspaceBuildPageView: FC = ({ return ( = ({ Builds {!builds && - [...Array(15).keys()].map((i) => ( + Array.from({ length: 15 }, (_, i) => ( ))} + {builds?.map((build) => ( = ({ )} - {!logs && } - {logs && ( + {logs ? ( + ) : ( + )}