Skip to content

Commit 3ad5e11

Browse files
authored
feat: add warning if workspace page becomes stale (coder#4375)
* added a warning summary component * added warning to workspace page * consolidated warnings * prettier * updated design
1 parent 9a670b9 commit 3ad5e11

File tree

7 files changed

+151
-66
lines changed

7 files changed

+151
-66
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Story } from "@storybook/react"
2+
import { WarningAlert, WarningAlertProps } from "./WarningAlert"
3+
import Button from "@material-ui/core/Button"
4+
5+
export default {
6+
title: "components/WarningAlert",
7+
component: WarningAlert,
8+
}
9+
10+
const Template: Story<WarningAlertProps> = (args) => <WarningAlert {...args} />
11+
12+
export const ExampleWithDismiss = Template.bind({})
13+
ExampleWithDismiss.args = {
14+
text: "This is a warning",
15+
dismissible: true,
16+
}
17+
18+
const ExampleAction = (
19+
<Button onClick={() => null} size="small">
20+
Button
21+
</Button>
22+
)
23+
24+
export const ExampleWithAction = Template.bind({})
25+
ExampleWithAction.args = {
26+
text: "This is a warning",
27+
actions: [ExampleAction],
28+
}
29+
30+
export const ExampleWithActionAndDismiss = Template.bind({})
31+
ExampleWithActionAndDismiss.args = {
32+
text: "This is a warning",
33+
actions: [ExampleAction],
34+
dismissible: true,
35+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useState, FC, ReactElement } from "react"
2+
import Collapse from "@material-ui/core/Collapse"
3+
import { Stack } from "components/Stack/Stack"
4+
import { makeStyles, Theme } from "@material-ui/core/styles"
5+
import { colors } from "theme/colors"
6+
import ReportProblemOutlinedIcon from "@material-ui/icons/ReportProblemOutlined"
7+
import Button from "@material-ui/core/Button"
8+
import { useTranslation } from "react-i18next"
9+
10+
export interface WarningAlertProps {
11+
text: string
12+
dismissible?: boolean
13+
actions?: ReactElement[]
14+
}
15+
16+
export const WarningAlert: FC<WarningAlertProps> = ({
17+
text,
18+
dismissible = false,
19+
actions = [],
20+
}) => {
21+
const { t } = useTranslation("common")
22+
const [open, setOpen] = useState(true)
23+
const classes = useStyles()
24+
25+
return (
26+
<Collapse in={open}>
27+
<Stack
28+
className={classes.alertContainer}
29+
direction="row"
30+
alignItems="center"
31+
spacing={0}
32+
justifyContent="space-between"
33+
>
34+
<Stack direction="row" spacing={1}>
35+
<ReportProblemOutlinedIcon fontSize="small" className={classes.alertIcon} />
36+
{text}
37+
</Stack>
38+
<Stack direction="row">
39+
{actions.length > 0 && actions.map((action) => <div key={String(action)}>{action}</div>)}
40+
{dismissible && (
41+
<Button size="small" onClick={() => setOpen(false)} variant="outlined">
42+
{t("ctas.dismissCta")}
43+
</Button>
44+
)}
45+
</Stack>
46+
</Stack>
47+
</Collapse>
48+
)
49+
}
50+
51+
const useStyles = makeStyles<Theme>((theme) => ({
52+
alertContainer: {
53+
border: `1px solid ${colors.orange[7]}`,
54+
borderRadius: theme.shape.borderRadius,
55+
padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`,
56+
backgroundColor: `${colors.gray[16]}`,
57+
},
58+
alertIcon: {
59+
color: `${colors.orange[7]}`,
60+
},
61+
}))

site/src/components/Workspace/Workspace.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { WorkspaceScheduleBanner } from "../WorkspaceScheduleBanner/WorkspaceSch
1515
import { WorkspaceScheduleButton } from "../WorkspaceScheduleButton/WorkspaceScheduleButton"
1616
import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection"
1717
import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"
18+
import { WarningAlert } from "../WarningAlert/WarningAlert"
19+
import { useTranslation } from "react-i18next"
1820

1921
export enum WorkspaceErrors {
2022
GET_RESOURCES_ERROR = "getResourcesError",
@@ -71,19 +73,21 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
7173
buildInfo,
7274
applicationsHost,
7375
}) => {
76+
const { t } = useTranslation("workspacePage")
7477
const styles = useStyles()
7578
const navigate = useNavigate()
7679
const hasTemplateIcon = workspace.template_icon && workspace.template_icon !== ""
7780

78-
const buildError = workspaceErrors[WorkspaceErrors.BUILD_ERROR] ? (
81+
const buildError = Boolean(workspaceErrors[WorkspaceErrors.BUILD_ERROR]) && (
7982
<ErrorSummary error={workspaceErrors[WorkspaceErrors.BUILD_ERROR]} dismissible />
80-
) : (
81-
<></>
8283
)
83-
const cancellationError = workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR] ? (
84+
85+
const cancellationError = Boolean(workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]) && (
8486
<ErrorSummary error={workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]} dismissible />
85-
) : (
86-
<></>
87+
)
88+
89+
const workspaceRefreshWarning = Boolean(workspaceErrors[WorkspaceErrors.GET_RESOURCES_ERROR]) && (
90+
<WarningAlert text={t("warningsAndErrors.workspaceRefreshWarning")} dismissible />
8791
)
8892

8993
return (
@@ -127,6 +131,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
127131
<Stack direction="column" className={styles.firstColumnSpacer} spacing={2.5}>
128132
{buildError}
129133
{cancellationError}
134+
{workspaceRefreshWarning}
130135

131136
<WorkspaceScheduleBanner
132137
isLoading={bannerProps.isLoading}
Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
import Button from "@material-ui/core/Button"
2-
import { makeStyles } from "@material-ui/core/styles"
3-
import Alert from "@material-ui/lab/Alert"
4-
import AlertTitle from "@material-ui/lab/AlertTitle"
5-
import { Maybe } from "components/Conditionals/Maybe"
62
import { FC } from "react"
73
import * as TypesGen from "../../api/typesGenerated"
8-
9-
const Language = {
10-
bannerTitle: "This workspace has been deleted and cannot be edited.",
11-
createWorkspaceCta: "Create new workspace",
12-
}
4+
import { WarningAlert } from "components/WarningAlert/WarningAlert"
5+
import { useTranslation } from "react-i18next"
6+
import { Maybe } from "components/Conditionals/Maybe"
137

148
export interface WorkspaceDeletedBannerProps {
159
workspace: TypesGen.Workspace
@@ -20,32 +14,19 @@ export const WorkspaceDeletedBanner: FC<React.PropsWithChildren<WorkspaceDeleted
2014
workspace,
2115
handleClick,
2216
}) => {
23-
const styles = useStyles()
17+
const { t } = useTranslation("workspacePage")
18+
const NewWorkspaceButton = (
19+
<Button onClick={handleClick} size="small">
20+
{t("ctas.createWorkspaceCta")}
21+
</Button>
22+
)
2423

2524
return (
2625
<Maybe condition={workspace.latest_build.status === "deleted"}>
27-
<Alert
28-
className={styles.root}
29-
action={
30-
<Button color="inherit" onClick={handleClick} size="small">
31-
{Language.createWorkspaceCta}
32-
</Button>
33-
}
34-
severity="warning"
35-
>
36-
<AlertTitle>{Language.bannerTitle}</AlertTitle>
37-
</Alert>
26+
<WarningAlert
27+
text={t("warningsAndErrors.workspaceDeletedWarning")}
28+
actions={[NewWorkspaceButton]}
29+
/>
3830
</Maybe>
3931
)
4032
}
41-
42-
export const useStyles = makeStyles(() => {
43-
return {
44-
root: {
45-
alignItems: "center",
46-
"& .MuiAlertTitle-root": {
47-
marginBottom: "0px",
48-
},
49-
},
50-
}
51-
})
Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
import Button from "@material-ui/core/Button"
2-
import Alert from "@material-ui/lab/Alert"
3-
import AlertTitle from "@material-ui/lab/AlertTitle"
42
import dayjs from "dayjs"
53
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
64
import utc from "dayjs/plugin/utc"
75
import { FC } from "react"
8-
import * as TypesGen from "../../api/typesGenerated"
9-
import { isWorkspaceOn } from "../../util/workspace"
6+
import * as TypesGen from "api/typesGenerated"
7+
import { isWorkspaceOn } from "util/workspace"
8+
import { WarningAlert } from "components/WarningAlert/WarningAlert"
9+
import { useTranslation } from "react-i18next"
1010

1111
dayjs.extend(utc)
1212
dayjs.extend(isSameOrBefore)
1313

14-
export const Language = {
15-
bannerAction: "Extend",
16-
bannerTitle: "Your workspace is scheduled to automatically shut down soon.",
17-
}
18-
1914
export interface WorkspaceScheduleBannerProps {
2015
isLoading?: boolean
2116
onExtend: () => void
@@ -36,26 +31,22 @@ export const WorkspaceScheduleBanner: FC<React.PropsWithChildren<WorkspaceSchedu
3631
onExtend,
3732
workspace,
3833
}) => {
34+
const { t } = useTranslation("workspacePage")
35+
3936
if (!shouldDisplay(workspace)) {
4037
return null
41-
} else {
42-
return (
43-
<Alert
44-
action={
45-
<Button
46-
variant="outlined"
47-
color="inherit"
48-
disabled={isLoading}
49-
onClick={onExtend}
50-
size="small"
51-
>
52-
{Language.bannerAction}
53-
</Button>
54-
}
55-
severity="warning"
56-
>
57-
<AlertTitle>{Language.bannerTitle}</AlertTitle>
58-
</Alert>
59-
)
6038
}
39+
40+
const ScheduleButton = (
41+
<Button variant="outlined" disabled={isLoading} onClick={onExtend} size="small">
42+
{t("ctas.extendScheduleCta")}
43+
</Button>
44+
)
45+
46+
return (
47+
<WarningAlert
48+
text={t("warningsAndErrors.workspaceShutdownWarning")}
49+
actions={[ScheduleButton]}
50+
/>
51+
)
6152
}

site/src/i18n/en/common.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,8 @@
1818
"confirm": "Are you sure you want to proceed? Type the name of this {{entity}} below to confirm.",
1919
"confirmLabel": "Name of {{entity}} to delete",
2020
"incorrectName": "Incorrect {{entity}} name."
21+
},
22+
"ctas": {
23+
"dismissCta": "Dismiss"
2124
}
2225
}

site/src/i18n/en/workspacePage.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77
"editDeadlineMinus": "Subtract one hour",
88
"editDeadlinePlus": "Add one hour"
99
},
10+
"ctas": {
11+
"createWorkspaceCta": "Create new workspace",
12+
"extendScheduleCta": "Extend"
13+
},
14+
"warningsAndErrors": {
15+
"workspaceRefreshWarning": "We're having difficulty fetching the latest workspace state. Refresh the page to see the newest changes.",
16+
"workspaceDeletedWarning": "This workspace has been deleted and cannot be edited.",
17+
"workspaceShutdownWarning": "Your workspace is scheduled to automatically shut down soon."
18+
},
1019
"actionButton": {
1120
"start": "Start",
1221
"stop": "Stop",

0 commit comments

Comments
 (0)