mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add warning if workspace page becomes stale (#4375)
* added a warning summary component * added warning to workspace page * consolidated warnings * prettier * updated design
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { Story } from "@storybook/react"
|
||||
import { WarningAlert, WarningAlertProps } from "./WarningAlert"
|
||||
import Button from "@material-ui/core/Button"
|
||||
|
||||
export default {
|
||||
title: "components/WarningAlert",
|
||||
component: WarningAlert,
|
||||
}
|
||||
|
||||
const Template: Story<WarningAlertProps> = (args) => <WarningAlert {...args} />
|
||||
|
||||
export const ExampleWithDismiss = Template.bind({})
|
||||
ExampleWithDismiss.args = {
|
||||
text: "This is a warning",
|
||||
dismissible: true,
|
||||
}
|
||||
|
||||
const ExampleAction = (
|
||||
<Button onClick={() => null} size="small">
|
||||
Button
|
||||
</Button>
|
||||
)
|
||||
|
||||
export const ExampleWithAction = Template.bind({})
|
||||
ExampleWithAction.args = {
|
||||
text: "This is a warning",
|
||||
actions: [ExampleAction],
|
||||
}
|
||||
|
||||
export const ExampleWithActionAndDismiss = Template.bind({})
|
||||
ExampleWithActionAndDismiss.args = {
|
||||
text: "This is a warning",
|
||||
actions: [ExampleAction],
|
||||
dismissible: true,
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { useState, FC, ReactElement } from "react"
|
||||
import Collapse from "@material-ui/core/Collapse"
|
||||
import { Stack } from "components/Stack/Stack"
|
||||
import { makeStyles, Theme } from "@material-ui/core/styles"
|
||||
import { colors } from "theme/colors"
|
||||
import ReportProblemOutlinedIcon from "@material-ui/icons/ReportProblemOutlined"
|
||||
import Button from "@material-ui/core/Button"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
export interface WarningAlertProps {
|
||||
text: string
|
||||
dismissible?: boolean
|
||||
actions?: ReactElement[]
|
||||
}
|
||||
|
||||
export const WarningAlert: FC<WarningAlertProps> = ({
|
||||
text,
|
||||
dismissible = false,
|
||||
actions = [],
|
||||
}) => {
|
||||
const { t } = useTranslation("common")
|
||||
const [open, setOpen] = useState(true)
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Collapse in={open}>
|
||||
<Stack
|
||||
className={classes.alertContainer}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={0}
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<ReportProblemOutlinedIcon fontSize="small" className={classes.alertIcon} />
|
||||
{text}
|
||||
</Stack>
|
||||
<Stack direction="row">
|
||||
{actions.length > 0 && actions.map((action) => <div key={String(action)}>{action}</div>)}
|
||||
{dismissible && (
|
||||
<Button size="small" onClick={() => setOpen(false)} variant="outlined">
|
||||
{t("ctas.dismissCta")}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Collapse>
|
||||
)
|
||||
}
|
||||
|
||||
const useStyles = makeStyles<Theme>((theme) => ({
|
||||
alertContainer: {
|
||||
border: `1px solid ${colors.orange[7]}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`,
|
||||
backgroundColor: `${colors.gray[16]}`,
|
||||
},
|
||||
alertIcon: {
|
||||
color: `${colors.orange[7]}`,
|
||||
},
|
||||
}))
|
||||
@@ -15,6 +15,8 @@ import { WorkspaceScheduleBanner } from "../WorkspaceScheduleBanner/WorkspaceSch
|
||||
import { WorkspaceScheduleButton } from "../WorkspaceScheduleButton/WorkspaceScheduleButton"
|
||||
import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection"
|
||||
import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"
|
||||
import { WarningAlert } from "../WarningAlert/WarningAlert"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
export enum WorkspaceErrors {
|
||||
GET_RESOURCES_ERROR = "getResourcesError",
|
||||
@@ -71,19 +73,21 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
|
||||
buildInfo,
|
||||
applicationsHost,
|
||||
}) => {
|
||||
const { t } = useTranslation("workspacePage")
|
||||
const styles = useStyles()
|
||||
const navigate = useNavigate()
|
||||
const hasTemplateIcon = workspace.template_icon && workspace.template_icon !== ""
|
||||
|
||||
const buildError = workspaceErrors[WorkspaceErrors.BUILD_ERROR] ? (
|
||||
const buildError = Boolean(workspaceErrors[WorkspaceErrors.BUILD_ERROR]) && (
|
||||
<ErrorSummary error={workspaceErrors[WorkspaceErrors.BUILD_ERROR]} dismissible />
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
const cancellationError = workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR] ? (
|
||||
|
||||
const cancellationError = Boolean(workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]) && (
|
||||
<ErrorSummary error={workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]} dismissible />
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
|
||||
const workspaceRefreshWarning = Boolean(workspaceErrors[WorkspaceErrors.GET_RESOURCES_ERROR]) && (
|
||||
<WarningAlert text={t("warningsAndErrors.workspaceRefreshWarning")} dismissible />
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -127,6 +131,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
|
||||
<Stack direction="column" className={styles.firstColumnSpacer} spacing={2.5}>
|
||||
{buildError}
|
||||
{cancellationError}
|
||||
{workspaceRefreshWarning}
|
||||
|
||||
<WorkspaceScheduleBanner
|
||||
isLoading={bannerProps.isLoading}
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import Button from "@material-ui/core/Button"
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import Alert from "@material-ui/lab/Alert"
|
||||
import AlertTitle from "@material-ui/lab/AlertTitle"
|
||||
import { Maybe } from "components/Conditionals/Maybe"
|
||||
import { FC } from "react"
|
||||
import * as TypesGen from "../../api/typesGenerated"
|
||||
|
||||
const Language = {
|
||||
bannerTitle: "This workspace has been deleted and cannot be edited.",
|
||||
createWorkspaceCta: "Create new workspace",
|
||||
}
|
||||
import { WarningAlert } from "components/WarningAlert/WarningAlert"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Maybe } from "components/Conditionals/Maybe"
|
||||
|
||||
export interface WorkspaceDeletedBannerProps {
|
||||
workspace: TypesGen.Workspace
|
||||
@@ -20,32 +14,19 @@ export const WorkspaceDeletedBanner: FC<React.PropsWithChildren<WorkspaceDeleted
|
||||
workspace,
|
||||
handleClick,
|
||||
}) => {
|
||||
const styles = useStyles()
|
||||
const { t } = useTranslation("workspacePage")
|
||||
const NewWorkspaceButton = (
|
||||
<Button onClick={handleClick} size="small">
|
||||
{t("ctas.createWorkspaceCta")}
|
||||
</Button>
|
||||
)
|
||||
|
||||
return (
|
||||
<Maybe condition={workspace.latest_build.status === "deleted"}>
|
||||
<Alert
|
||||
className={styles.root}
|
||||
action={
|
||||
<Button color="inherit" onClick={handleClick} size="small">
|
||||
{Language.createWorkspaceCta}
|
||||
</Button>
|
||||
}
|
||||
severity="warning"
|
||||
>
|
||||
<AlertTitle>{Language.bannerTitle}</AlertTitle>
|
||||
</Alert>
|
||||
<WarningAlert
|
||||
text={t("warningsAndErrors.workspaceDeletedWarning")}
|
||||
actions={[NewWorkspaceButton]}
|
||||
/>
|
||||
</Maybe>
|
||||
)
|
||||
}
|
||||
|
||||
export const useStyles = makeStyles(() => {
|
||||
return {
|
||||
root: {
|
||||
alignItems: "center",
|
||||
"& .MuiAlertTitle-root": {
|
||||
marginBottom: "0px",
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
import Button from "@material-ui/core/Button"
|
||||
import Alert from "@material-ui/lab/Alert"
|
||||
import AlertTitle from "@material-ui/lab/AlertTitle"
|
||||
import dayjs from "dayjs"
|
||||
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
|
||||
import utc from "dayjs/plugin/utc"
|
||||
import { FC } from "react"
|
||||
import * as TypesGen from "../../api/typesGenerated"
|
||||
import { isWorkspaceOn } from "../../util/workspace"
|
||||
import * as TypesGen from "api/typesGenerated"
|
||||
import { isWorkspaceOn } from "util/workspace"
|
||||
import { WarningAlert } from "components/WarningAlert/WarningAlert"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(isSameOrBefore)
|
||||
|
||||
export const Language = {
|
||||
bannerAction: "Extend",
|
||||
bannerTitle: "Your workspace is scheduled to automatically shut down soon.",
|
||||
}
|
||||
|
||||
export interface WorkspaceScheduleBannerProps {
|
||||
isLoading?: boolean
|
||||
onExtend: () => void
|
||||
@@ -36,26 +31,22 @@ export const WorkspaceScheduleBanner: FC<React.PropsWithChildren<WorkspaceSchedu
|
||||
onExtend,
|
||||
workspace,
|
||||
}) => {
|
||||
const { t } = useTranslation("workspacePage")
|
||||
|
||||
if (!shouldDisplay(workspace)) {
|
||||
return null
|
||||
} else {
|
||||
return (
|
||||
<Alert
|
||||
action={
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="inherit"
|
||||
disabled={isLoading}
|
||||
onClick={onExtend}
|
||||
size="small"
|
||||
>
|
||||
{Language.bannerAction}
|
||||
</Button>
|
||||
}
|
||||
severity="warning"
|
||||
>
|
||||
<AlertTitle>{Language.bannerTitle}</AlertTitle>
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
|
||||
const ScheduleButton = (
|
||||
<Button variant="outlined" disabled={isLoading} onClick={onExtend} size="small">
|
||||
{t("ctas.extendScheduleCta")}
|
||||
</Button>
|
||||
)
|
||||
|
||||
return (
|
||||
<WarningAlert
|
||||
text={t("warningsAndErrors.workspaceShutdownWarning")}
|
||||
actions={[ScheduleButton]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,5 +18,8 @@
|
||||
"confirm": "Are you sure you want to proceed? Type the name of this {{entity}} below to confirm.",
|
||||
"confirmLabel": "Name of {{entity}} to delete",
|
||||
"incorrectName": "Incorrect {{entity}} name."
|
||||
},
|
||||
"ctas": {
|
||||
"dismissCta": "Dismiss"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"editDeadlineMinus": "Subtract one hour",
|
||||
"editDeadlinePlus": "Add one hour"
|
||||
},
|
||||
"ctas": {
|
||||
"createWorkspaceCta": "Create new workspace",
|
||||
"extendScheduleCta": "Extend"
|
||||
},
|
||||
"warningsAndErrors": {
|
||||
"workspaceRefreshWarning": "We're having difficulty fetching the latest workspace state. Refresh the page to see the newest changes.",
|
||||
"workspaceDeletedWarning": "This workspace has been deleted and cannot be edited.",
|
||||
"workspaceShutdownWarning": "Your workspace is scheduled to automatically shut down soon."
|
||||
},
|
||||
"actionButton": {
|
||||
"start": "Start",
|
||||
"stop": "Stop",
|
||||
|
||||
Reference in New Issue
Block a user