diff --git a/site/src/components/WarningAlert/WarningAlert.stories.tsx b/site/src/components/WarningAlert/WarningAlert.stories.tsx new file mode 100644 index 0000000000000..dc8eed293441e --- /dev/null +++ b/site/src/components/WarningAlert/WarningAlert.stories.tsx @@ -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 = (args) => + +export const ExampleWithDismiss = Template.bind({}) +ExampleWithDismiss.args = { + text: "This is a warning", + dismissible: true, +} + +const ExampleAction = ( + +) + +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, +} diff --git a/site/src/components/WarningAlert/WarningAlert.tsx b/site/src/components/WarningAlert/WarningAlert.tsx new file mode 100644 index 0000000000000..7b0a024927913 --- /dev/null +++ b/site/src/components/WarningAlert/WarningAlert.tsx @@ -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 = ({ + text, + dismissible = false, + actions = [], +}) => { + const { t } = useTranslation("common") + const [open, setOpen] = useState(true) + const classes = useStyles() + + return ( + + + + + {text} + + + {actions.length > 0 && actions.map((action) =>
{action}
)} + {dismissible && ( + + )} +
+
+
+ ) +} + +const useStyles = makeStyles((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]}`, + }, +})) diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index 959c7dddfb924..e68063fe9e94b 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -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> = ({ 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]) && ( - ) : ( - <> ) - const cancellationError = workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR] ? ( + + const cancellationError = Boolean(workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]) && ( - ) : ( - <> + ) + + const workspaceRefreshWarning = Boolean(workspaceErrors[WorkspaceErrors.GET_RESOURCES_ERROR]) && ( + ) return ( @@ -127,6 +131,7 @@ export const Workspace: FC> = ({ {buildError} {cancellationError} + {workspaceRefreshWarning} { - const styles = useStyles() + const { t } = useTranslation("workspacePage") + const NewWorkspaceButton = ( + + ) return ( - - {Language.createWorkspaceCta} - - } - severity="warning" - > - {Language.bannerTitle} - + ) } - -export const useStyles = makeStyles(() => { - return { - root: { - alignItems: "center", - "& .MuiAlertTitle-root": { - marginBottom: "0px", - }, - }, - } -}) diff --git a/site/src/components/WorkspaceScheduleBanner/WorkspaceScheduleBanner.tsx b/site/src/components/WorkspaceScheduleBanner/WorkspaceScheduleBanner.tsx index 81538af5352df..2ff5ca57096e1 100644 --- a/site/src/components/WorkspaceScheduleBanner/WorkspaceScheduleBanner.tsx +++ b/site/src/components/WorkspaceScheduleBanner/WorkspaceScheduleBanner.tsx @@ -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 { + const { t } = useTranslation("workspacePage") + if (!shouldDisplay(workspace)) { return null - } else { - return ( - - {Language.bannerAction} - - } - severity="warning" - > - {Language.bannerTitle} - - ) } + + const ScheduleButton = ( + + ) + + return ( + + ) } diff --git a/site/src/i18n/en/common.json b/site/src/i18n/en/common.json index 66c66cb6fbee4..c3b136edefae7 100644 --- a/site/src/i18n/en/common.json +++ b/site/src/i18n/en/common.json @@ -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" } } diff --git a/site/src/i18n/en/workspacePage.json b/site/src/i18n/en/workspacePage.json index ceebe7b5a0356..781e03121ef42 100644 --- a/site/src/i18n/en/workspacePage.json +++ b/site/src/i18n/en/workspacePage.json @@ -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",