refactor(site): Refactor alerts (#7587)

This commit is contained in:
Bruno Quaresma
2023-05-18 13:17:16 -03:00
committed by GitHub
parent 63a9e34381
commit 8e31ed4072
45 changed files with 407 additions and 543 deletions
+4
View File
@@ -128,6 +128,10 @@ rules:
message:
"You should use the Avatar component provided on
components/Avatar/Avatar"
- name: "@mui/material/Alert"
message:
"You should use the Alert component provided on
components/Alert/Alert"
no-unused-vars: "off"
"object-curly-spacing": "off"
react-hooks/exhaustive-deps: warn
@@ -0,0 +1,61 @@
import { Alert } from "./Alert"
import Button from "@mui/material/Button"
import Link from "@mui/material/Link"
import type { Meta, StoryObj } from "@storybook/react"
const meta: Meta<typeof Alert> = {
title: "components/Alert",
component: Alert,
}
export default meta
type Story = StoryObj<typeof Alert>
const ExampleAction = (
<Button onClick={() => null} size="small" variant="text">
Button
</Button>
)
export const Warning: Story = {
args: {
children: "This is a warning",
severity: "warning",
},
}
export const WarningWithDismiss: Story = {
args: {
children: "This is a warning",
dismissible: true,
severity: "warning",
},
}
export const WarningWithAction: Story = {
args: {
children: "This is a warning",
actions: [ExampleAction],
severity: "warning",
},
}
export const WarningWithActionAndDismiss: Story = {
args: {
children: "This is a warning",
actions: [ExampleAction],
dismissible: true,
severity: "warning",
},
}
export const WithChildren: Story = {
args: {
severity: "warning",
children: (
<div>
This is a message with a <Link href="#">link</Link>
</div>
),
},
}
+63
View File
@@ -0,0 +1,63 @@
import { useState, FC, ReactNode, PropsWithChildren } from "react"
import Collapse from "@mui/material/Collapse"
// eslint-disable-next-line no-restricted-imports -- It is the base component
import MuiAlert, { AlertProps as MuiAlertProps } from "@mui/material/Alert"
import Button from "@mui/material/Button"
export interface AlertProps extends PropsWithChildren {
severity: MuiAlertProps["severity"]
actions?: ReactNode[]
dismissible?: boolean
onRetry?: () => void
onDismiss?: () => void
}
export const Alert: FC<AlertProps> = ({
children,
actions = [],
onRetry,
dismissible,
severity,
onDismiss,
}) => {
const [open, setOpen] = useState(true)
return (
<Collapse in={open}>
<MuiAlert
severity={severity}
action={
<>
{/* CTAs passed in by the consumer */}
{actions.length > 0 &&
actions.map((action) => <div key={String(action)}>{action}</div>)}
{/* retry CTA */}
{onRetry && (
<Button variant="text" size="small" onClick={onRetry}>
Retry
</Button>
)}
{/* close CTA */}
{dismissible && (
<Button
variant="text"
size="small"
onClick={() => {
setOpen(false)
onDismiss && onDismiss()
}}
data-testid="dismiss-banner-btn"
>
Dismiss
</Button>
)}
</>
}
>
{children}
</MuiAlert>
</Collapse>
)
}
@@ -0,0 +1,77 @@
import Button from "@mui/material/Button"
import { mockApiError } from "testHelpers/entities"
import type { Meta, StoryObj } from "@storybook/react"
import { action } from "@storybook/addon-actions"
import { ErrorAlert } from "./ErrorAlert"
const mockError = mockApiError({
message: "Email or password was invalid",
detail: "Password is invalid",
})
const meta: Meta<typeof ErrorAlert> = {
title: "components/ErrorAlert",
component: ErrorAlert,
args: {
error: mockError,
dismissible: false,
onRetry: undefined,
},
}
export default meta
type Story = StoryObj<typeof ErrorAlert>
const ExampleAction = (
<Button onClick={() => null} size="small" variant="text">
Button
</Button>
)
export const WithOnlyMessage: Story = {
args: {
error: mockApiError({
message: "Email or password was invalid",
}),
},
}
export const WithDismiss: Story = {
args: {
dismissible: true,
},
}
export const WithAction: Story = {
args: {
actions: [ExampleAction],
},
}
export const WithActionAndDismiss: Story = {
args: {
actions: [ExampleAction],
dismissible: true,
},
}
export const WithRetry: Story = {
args: {
onRetry: action("retry"),
dismissible: true,
},
}
export const WithActionRetryAndDismiss: Story = {
args: {
actions: [ExampleAction],
onRetry: action("retry"),
dismissible: true,
},
}
export const WithNonApiError: Story = {
args: {
error: new Error("Non API error here"),
},
}
+32
View File
@@ -0,0 +1,32 @@
import { AlertProps, Alert } from "./Alert"
import AlertTitle from "@mui/material/AlertTitle"
import Box from "@mui/material/Box"
import { getErrorMessage, getErrorDetail } from "api/errors"
import { FC } from "react"
export const ErrorAlert: FC<
Omit<AlertProps, "severity" | "children"> & { error: unknown }
> = ({ error, ...alertProps }) => {
const message = getErrorMessage(error, "Something went wrong.")
const detail = getErrorDetail(error)
return (
<Alert severity="error" {...alertProps}>
{detail ? (
<>
<AlertTitle>{message}</AlertTitle>
<Box
component="span"
color={(theme) => theme.palette.text.secondary}
fontSize={13}
data-chromatic="ignore"
>
{detail}
</Box>
</>
) : (
message
)}
</Alert>
)
}
@@ -1,122 +0,0 @@
import { Story } from "@storybook/react"
import { AlertBanner } from "./AlertBanner"
import Button from "@mui/material/Button"
import { mockApiError } from "testHelpers/entities"
import { AlertBannerProps } from "./alertTypes"
import Link from "@mui/material/Link"
export default {
title: "components/AlertBanner",
component: AlertBanner,
}
const ExampleAction = (
<Button onClick={() => null} size="small">
Button
</Button>
)
const mockError = mockApiError({
message: "Email or password was invalid",
detail: "Password is invalid",
})
const Template: Story<AlertBannerProps> = (args) => <AlertBanner {...args} />
export const Warning = Template.bind({})
Warning.args = {
text: "This is a warning",
severity: "warning",
}
export const ErrorWithDefaultMessage = Template.bind({})
ErrorWithDefaultMessage.args = {
text: "This is an error",
severity: "error",
}
export const ErrorWithErrorMessage = Template.bind({})
ErrorWithErrorMessage.args = {
error: mockError,
severity: "error",
}
export const WarningWithDismiss = Template.bind({})
WarningWithDismiss.args = {
text: "This is a warning",
dismissible: true,
severity: "warning",
}
export const ErrorWithDismiss = Template.bind({})
ErrorWithDismiss.args = {
error: mockError,
dismissible: true,
severity: "error",
}
export const WarningWithAction = Template.bind({})
WarningWithAction.args = {
text: "This is a warning",
actions: [ExampleAction],
severity: "warning",
}
export const ErrorWithAction = Template.bind({})
ErrorWithAction.args = {
error: mockError,
actions: [ExampleAction],
severity: "error",
}
export const WarningWithActionAndDismiss = Template.bind({})
WarningWithActionAndDismiss.args = {
text: "This is a warning",
actions: [ExampleAction],
dismissible: true,
severity: "warning",
}
export const ErrorWithActionAndDismiss = Template.bind({})
ErrorWithActionAndDismiss.args = {
error: mockError,
actions: [ExampleAction],
dismissible: true,
severity: "error",
}
export const ErrorWithRetry = Template.bind({})
ErrorWithRetry.args = {
error: mockError,
retry: () => null,
dismissible: true,
severity: "error",
}
export const ErrorWithActionRetryAndDismiss = Template.bind({})
ErrorWithActionRetryAndDismiss.args = {
error: mockError,
actions: [ExampleAction],
retry: () => null,
dismissible: true,
severity: "error",
}
export const ErrorAsWarning = Template.bind({})
ErrorAsWarning.args = {
error: mockError,
severity: "warning",
}
const WithChildren: Story<AlertBannerProps> = (args) => (
<AlertBanner {...args}>
<div>
This is a message with a <Link href="#">link</Link>
</div>
</AlertBanner>
)
export const InfoWithChildContent = WithChildren.bind({})
InfoWithChildContent.args = {
severity: "info",
}
@@ -1,122 +0,0 @@
import { useState, FC, Children } from "react"
import Collapse from "@mui/material/Collapse"
import { Stack } from "components/Stack/Stack"
import { makeStyles } from "@mui/styles"
import { colors } from "theme/colors"
import { useTranslation } from "react-i18next"
import { getErrorDetail, getErrorMessage } from "api/errors"
import { Expander } from "components/Expander/Expander"
import { Severity, AlertBannerProps } from "./alertTypes"
import { severityConstants } from "./severityConstants"
import { AlertBannerCtas } from "./AlertBannerCtas"
import { Theme } from "@mui/material/styles"
/**
* @param children: the children to be displayed in the alert
* @param severity: the level of alert severity (see ./severityTypes.ts)
* @param text: default text to be displayed to the user; useful for warnings or as a fallback error message
* @param error: should be passed in if the severity is 'Error'; warnings can use 'text' instead
* @param actions: an array of CTAs passed in by the consumer
* @param retry: a handler to retry the action that spawned the error
* @param dismissible: determines whether or not the banner should have a `Dismiss` CTA
* @param onDismiss: a handler that is called when the `Dismiss` CTA is clicked, after the animation has finished
*/
export const AlertBanner: FC<React.PropsWithChildren<AlertBannerProps>> = ({
children,
severity,
text,
error,
actions = [],
retry,
dismissible = false,
onDismiss,
}) => {
const { t } = useTranslation("common")
const [open, setOpen] = useState(true)
// Set a fallback message if no text or children are provided.
const defaultMessage =
text ??
(Children.count(children) === 0
? t("warningsAndErrors.somethingWentWrong")
: "")
// if an error is passed in, display that error, otherwise
// display the text passed in, e.g. warning text
const alertMessage = getErrorMessage(error, defaultMessage)
// if we have an error, check if there's detail to display
const detail = error ? getErrorDetail(error) : undefined
const classes = useStyles({ severity, hasDetail: Boolean(detail) })
const [showDetails, setShowDetails] = useState(false)
return (
<Collapse in={open} onExited={() => onDismiss && onDismiss()}>
<Stack
className={classes.alertContainer}
direction="row"
alignItems="center"
spacing={0}
justifyContent="space-between"
>
<Stack
direction="row"
alignItems="center"
spacing={2}
className={classes.fullWidth}
>
{severityConstants[severity].icon}
<Stack spacing={0} className={classes.fullWidth}>
{children}
{alertMessage}
{detail && (
<Expander expanded={showDetails} setExpanded={setShowDetails}>
<div>{detail}</div>
</Expander>
)}
</Stack>
</Stack>
<AlertBannerCtas
actions={actions}
dismissible={dismissible}
retry={retry}
setOpen={setOpen}
/>
</Stack>
</Collapse>
)
}
interface StyleProps {
severity: Severity
hasDetail: boolean
}
const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
alertContainer: (props) => ({
...theme.typography.body2,
borderColor: severityConstants[props.severity].color,
border: `1px solid ${colors.orange[7]}`,
borderRadius: theme.shape.borderRadius,
padding: theme.spacing(2),
backgroundColor: `${colors.gray[16]}`,
textAlign: "left",
"& > span": {
paddingTop: theme.spacing(0.25),
},
// targeting the alert icon rather than the expander icon
"& svg:nth-child(2)": {
marginTop: props.hasDetail ? theme.spacing(1) : "inherit",
marginRight: theme.spacing(1),
},
}),
fullWidth: {
width: "100%",
},
}))
@@ -1,50 +0,0 @@
import { FC } from "react"
import { AlertBannerProps } from "./alertTypes"
import { Stack } from "components/Stack/Stack"
import Button from "@mui/material/Button"
import RefreshIcon from "@mui/icons-material/Refresh"
import { useTranslation } from "react-i18next"
type AlertBannerCtasProps = Pick<
AlertBannerProps,
"actions" | "dismissible" | "retry"
> & {
setOpen: (arg0: boolean) => void
}
export const AlertBannerCtas: FC<AlertBannerCtasProps> = ({
actions = [],
dismissible,
retry,
setOpen,
}) => {
const { t } = useTranslation("common")
return (
<Stack direction="row">
{/* CTAs passed in by the consumer */}
{actions.length > 0 &&
actions.map((action) => <div key={String(action)}>{action}</div>)}
{/* retry CTA */}
{retry && (
<div>
<Button size="small" onClick={retry} startIcon={<RefreshIcon />}>
{t("ctas.retry")}
</Button>
</div>
)}
{/* close CTA */}
{dismissible && (
<Button
size="small"
onClick={() => setOpen(false)}
data-testid="dismiss-banner-btn"
>
{t("ctas.dismissCta")}
</Button>
)}
</Stack>
)
}
@@ -1,14 +0,0 @@
import { ApiError } from "api/errors"
import { ReactElement } from "react"
export type Severity = "warning" | "error" | "info"
export interface AlertBannerProps {
severity: Severity
text?: JSX.Element | string
error?: ApiError | Error | unknown
actions?: ReactElement[]
dismissible?: boolean
onDismiss?: () => void
retry?: () => void
}
@@ -1,32 +0,0 @@
import ReportProblemOutlinedIcon from "@mui/icons-material/ReportProblemOutlined"
import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined"
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"
import { colors } from "theme/colors"
import { Severity } from "./alertTypes"
import { ReactElement } from "react"
export const severityConstants: Record<
Severity,
{ color: string; icon: ReactElement }
> = {
warning: {
color: colors.orange[7],
icon: (
<ReportProblemOutlinedIcon
style={{ color: colors.orange[7], fontSize: 16 }}
/>
),
},
error: {
color: colors.red[7],
icon: (
<ErrorOutlineOutlinedIcon
style={{ color: colors.red[7], fontSize: 16 }}
/>
),
},
info: {
color: colors.blue[7],
icon: <InfoOutlinedIcon style={{ color: colors.blue[7], fontSize: 16 }} />,
},
}
@@ -9,7 +9,7 @@ import {
} from "../../utils/formUtils"
import { LoadingButton } from "../LoadingButton/LoadingButton"
import { Stack } from "../Stack/Stack"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export interface AccountFormValues {
username: string
@@ -62,7 +62,7 @@ export const AccountForm: FC<React.PropsWithChildren<AccountFormProps>> = ({
<form onSubmit={form.handleSubmit}>
<Stack>
{Boolean(updateProfileError) && (
<AlertBanner severity="error" error={updateProfileError} />
<ErrorAlert error={updateProfileError} />
)}
<TextField
disabled
@@ -5,7 +5,7 @@ import * as Yup from "yup"
import { getFormHelpers } from "../../utils/formUtils"
import { LoadingButton } from "../LoadingButton/LoadingButton"
import { Stack } from "../Stack/Stack"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { ErrorAlert } from "components/Alert/ErrorAlert"
interface SecurityFormValues {
old_password: string
@@ -73,7 +73,7 @@ export const SecurityForm: FC<SecurityFormProps> = ({
<form onSubmit={form.handleSubmit}>
<Stack>
{Boolean(updateSecurityError) && (
<AlertBanner severity="error" error={updateSecurityError} />
<ErrorAlert error={updateSecurityError} />
)}
<TextField
{...getFieldHelpers("old_password")}
@@ -9,7 +9,8 @@ import { OAuthSignInForm } from "./OAuthSignInForm"
import { BuiltInAuthFormValues } from "./SignInForm.types"
import Button from "@mui/material/Button"
import EmailIcon from "@mui/icons-material/EmailOutlined"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export const Language = {
emailLabel: "Email",
@@ -100,7 +101,7 @@ export const SignInForm: FC<React.PropsWithChildren<SignInFormProps>> = ({
</h1>
<Maybe condition={error !== undefined}>
<div className={styles.error}>
<AlertBanner severity="error" error={error} />
<ErrorAlert error={error} />
</div>
</Maybe>
<Maybe condition={passwordEnabled && showPasswordAuth}>
@@ -126,10 +127,7 @@ export const SignInForm: FC<React.PropsWithChildren<SignInFormProps>> = ({
</Maybe>
<Maybe condition={!passwordEnabled && !oAuthEnabled}>
<AlertBanner
severity="error"
text="No authentication methods configured!"
/>
<Alert severity="error">No authentication methods configured!</Alert>
</Maybe>
<Maybe condition={passwordEnabled && !showPasswordAuth}>
@@ -7,7 +7,6 @@ import { Margins } from "components/Margins/Margins"
import { Stack } from "components/Stack/Stack"
import { Loader } from "components/Loader/Loader"
import { TemplatePageHeader } from "./TemplatePageHeader"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import {
checkAuthorization,
getTemplateByName,
@@ -15,6 +14,7 @@ import {
} from "api/api"
import { useQuery } from "@tanstack/react-query"
import { AuthorizationRequest } from "api/typesGenerated"
import { ErrorAlert } from "components/Alert/ErrorAlert"
const templatePermissions = (
templateId: string,
@@ -75,7 +75,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
if (error) {
return (
<div className={styles.error}>
<AlertBanner severity="error" error={error} />
<ErrorAlert error={error} />
</div>
)
}
@@ -13,7 +13,7 @@ import {
VariableValue,
WorkspaceResource,
} from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
import { Avatar } from "components/Avatar/Avatar"
import { AvatarData } from "components/AvatarData/AvatarData"
import { bannerHeight } from "components/DeploymentBanner/DeploymentBannerView"
@@ -206,7 +206,6 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
<Button
title="Build template (Ctrl + Enter)"
size="small"
disabled={disablePreview}
onClick={() => {
triggerPreview()
@@ -224,7 +223,6 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
? "Something"
: ""
}
size="small"
disabled={dirty || disableUpdate}
onClick={onPublish}
>
@@ -240,7 +238,6 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
<div className={styles.sidebarActions}>
<Tooltip title="Create File" placement="top">
<IconButton
size="small"
aria-label="Create File"
onClick={(event) => {
setCreateFileOpen(true)
@@ -377,10 +374,7 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
}`}
>
{templateVersion.job.error && (
<AlertBanner
severity="error"
text={templateVersion.job.error}
/>
<Alert severity="error">{templateVersion.job.error}</Alert>
)}
{buildLogs && buildLogs.length > 0 && (
@@ -1,6 +1,6 @@
import { FC } from "react"
import * as TypesGen from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
import { Maybe } from "components/Conditionals/Maybe"
import Link from "@mui/material/Link"
@@ -18,16 +18,14 @@ export const TemplateVersionWarnings: FC<
return (
<Maybe condition={Boolean(warnings.includes("DEPRECATED_PARAMETERS"))}>
<div data-testid="warning-deprecated-parameters">
<AlertBanner severity="warning">
<div>
This template uses legacy parameters which will be deprecated in the
next Coder release. Learn how to migrate in{" "}
<Link href="https://coder.com/docs/v2/latest/templates/parameters#migration">
our documentation
</Link>
.
</div>
</AlertBanner>
<Alert severity="warning">
This template uses legacy parameters which will be deprecated in the
next Coder release. Learn how to migrate in{" "}
<Link href="https://coder.com/docs/v2/latest/templates/parameters#migration">
our documentation
</Link>
.
</Alert>
</div>
</Maybe>
)
@@ -73,7 +73,6 @@ export const VersionRow: React.FC<VersionRowProps> = ({
) : (
onPromoteClick && (
<Button
size="small"
className={styles.promoteButton}
onClick={(e) => {
e.preventDefault()
@@ -16,7 +16,7 @@ ExampleWithDismiss.args = {
}
const ExampleAction = (
<Button onClick={() => null} size="small">
<Button onClick={() => null} size="small" variant="text">
Button
</Button>
)
+7 -9
View File
@@ -12,7 +12,7 @@ import { FC } from "react"
import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"
import * as TypesGen from "../../api/typesGenerated"
import { AlertBanner } from "../AlertBanner/AlertBanner"
import { Alert } from "../Alert/Alert"
import { BuildsTable } from "../BuildsTable/BuildsTable"
import { Margins } from "../Margins/Margins"
import { Resources } from "../Resources/Resources"
@@ -27,6 +27,7 @@ import {
PageHeaderSubtitle,
} from "components/PageHeader/FullWidthPageHeader"
import { TemplateVersionWarnings } from "components/TemplateVersionWarnings/TemplateVersionWarnings"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export enum WorkspaceErrors {
GET_BUILDS_ERROR = "getBuildsError",
@@ -106,8 +107,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
const { t } = useTranslation("workspacePage")
const buildError = Boolean(workspaceErrors[WorkspaceErrors.BUILD_ERROR]) && (
<AlertBanner
severity="error"
<ErrorAlert
error={workspaceErrors[WorkspaceErrors.BUILD_ERROR]}
dismissible
/>
@@ -116,8 +116,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
const cancellationError = Boolean(
workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR],
) && (
<AlertBanner
severity="error"
<ErrorAlert
error={workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]}
dismissible
/>
@@ -193,7 +192,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
{failedBuildLogs && (
<Stack>
<AlertBanner severity="error">
<Alert severity="error">
<Stack
className={styles.fullWidth}
direction="row"
@@ -219,7 +218,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
</div>
)}
</Stack>
</AlertBanner>
</Alert>
<WorkspaceBuildLogs logs={failedBuildLogs} />
</Stack>
)}
@@ -251,8 +250,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
)}
{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
<AlertBanner
severity="error"
<ErrorAlert
error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]}
/>
) : (
@@ -22,7 +22,6 @@ export const UpdateButton: FC<WorkspaceAction> = ({
loading={loading}
loadingIndicator="Updating..."
loadingPosition="start"
size="small"
data-testid="workspace-update-button"
startIcon={<CloudQueueIcon />}
onClick={handleAction}
@@ -35,7 +34,6 @@ export const UpdateButton: FC<WorkspaceAction> = ({
export const StartButton: FC<WorkspaceAction> = ({ handleAction, loading }) => {
return (
<LoadingButton
size="small"
loading={loading}
loadingIndicator="Starting..."
loadingPosition="start"
@@ -50,7 +48,6 @@ export const StartButton: FC<WorkspaceAction> = ({ handleAction, loading }) => {
export const StopButton: FC<WorkspaceAction> = ({ handleAction, loading }) => {
return (
<LoadingButton
size="small"
loading={loading}
loadingIndicator="Stopping..."
loadingPosition="start"
@@ -71,7 +68,6 @@ export const RestartButton: FC<WorkspaceAction> = ({
loading={loading}
loadingIndicator="Restarting..."
loadingPosition="start"
size="small"
startIcon={<ReplayIcon />}
onClick={handleAction}
data-testid="workspace-restart-button"
@@ -83,7 +79,7 @@ export const RestartButton: FC<WorkspaceAction> = ({
export const CancelButton: FC<WorkspaceAction> = ({ handleAction }) => {
return (
<Button size="small" startIcon={<BlockIcon />} onClick={handleAction}>
<Button startIcon={<BlockIcon />} onClick={handleAction}>
Cancel
</Button>
)
@@ -95,7 +91,7 @@ interface DisabledProps {
export const DisabledButton: FC<DisabledProps> = ({ label }) => {
return (
<Button size="small" startIcon={<BlockOutlined />} disabled>
<Button startIcon={<BlockOutlined />} disabled>
{label}
</Button>
)
@@ -109,7 +105,6 @@ export const ActionLoadingButton: FC<LoadingProps> = ({ label }) => {
return (
<LoadingButton
loading
size="small"
loadingPosition="start"
loadingIndicator={label}
// This icon can be anything
@@ -1,7 +1,7 @@
import Button from "@mui/material/Button"
import { FC } from "react"
import * as TypesGen from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
import { useTranslation } from "react-i18next"
import { Maybe } from "components/Conditionals/Maybe"
@@ -16,18 +16,16 @@ export const WorkspaceDeletedBanner: FC<
const { t } = useTranslation("workspacePage")
const NewWorkspaceButton = (
<Button onClick={handleClick} size="small">
<Button onClick={handleClick} size="small" variant="text">
{t("ctas.createWorkspaceCta")}
</Button>
)
return (
<Maybe condition={workspace.latest_build.status === "deleted"}>
<AlertBanner
text={t("warningsAndErrors.workspaceDeletedWarning")}
actions={[NewWorkspaceButton]}
severity="warning"
/>
<Alert severity="warning" actions={[NewWorkspaceButton]}>
{t("warningsAndErrors.workspaceDeletedWarning")}
</Alert>
</Maybe>
)
}
@@ -1,6 +1,5 @@
import { useMachine } from "@xstate/react"
import { isApiValidationError } from "api/errors"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Maybe } from "components/Conditionals/Maybe"
import { useDashboard } from "components/Dashboard/DashboardProvider"
import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizontalForm"
@@ -14,6 +13,7 @@ import { useNavigate, useSearchParams } from "react-router-dom"
import { pageTitle } from "utils/page"
import { createTemplateMachine } from "xServices/createTemplate/createTemplateXService"
import { CreateTemplateForm } from "./CreateTemplateForm"
import { ErrorAlert } from "components/Alert/ErrorAlert"
const CreateTemplatePage: FC = () => {
const { t } = useTranslation("createTemplatePage")
@@ -64,7 +64,7 @@ const CreateTemplatePage: FC = () => {
<Stack spacing={6}>
<Maybe condition={Boolean(error && !isApiValidationError(error))}>
<AlertBanner error={error} severity="error" />
<ErrorAlert error={error} />
</Maybe>
{shouldDisplayForm && (
@@ -11,10 +11,10 @@ import { useMutation, useQuery } from "@tanstack/react-query"
import { createToken, getTokenConfig } from "api/api"
import { CreateTokenForm } from "./CreateTokenForm"
import { NANO_HOUR, CreateTokenData } from "./utils"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"
import { CodeExample } from "components/CodeExample/CodeExample"
import { makeStyles } from "@mui/styles"
import { ErrorAlert } from "components/Alert/ErrorAlert"
const initialValues: CreateTokenData = {
name: "",
@@ -87,9 +87,7 @@ export const CreateTokenPage: FC = () => {
<Helmet>
<title>{pageTitle(t("createToken.title"))}</title>
</Helmet>
{tokenFetchFailed && (
<AlertBanner severity="error" error={tokenFetchError} />
)}
{tokenFetchFailed && <ErrorAlert error={tokenFetchError} />}
<FullPageHorizontalForm
title={t("createToken.title")}
detail={t("createToken.detail")}
@@ -8,7 +8,6 @@ import { FC, useEffect, useState } from "react"
import { useTranslation } from "react-i18next"
import { getFormHelpers, nameValidator, onChangeTrimmed } from "utils/formUtils"
import * as Yup from "yup"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizontalForm"
import { SelectedTemplate } from "./SelectedTemplate"
import { Loader } from "components/Loader/Loader"
@@ -29,6 +28,7 @@ import {
ImmutableTemplateParametersSection,
MutableTemplateParametersSection,
} from "components/TemplateParameters/TemplateParameters"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export enum CreateWorkspaceErrors {
GET_TEMPLATES_ERROR = "getTemplatesError",
@@ -174,8 +174,7 @@ export const CreateWorkspacePageView: FC<
CreateWorkspaceErrors.GET_TEMPLATES_ERROR
],
) && (
<AlertBanner
severity="error"
<ErrorAlert
error={
props.createWorkspaceErrors[
CreateWorkspaceErrors.GET_TEMPLATES_ERROR
@@ -188,8 +187,7 @@ export const CreateWorkspacePageView: FC<
CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR
],
) && (
<AlertBanner
severity="error"
<ErrorAlert
error={
props.createWorkspaceErrors[
CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR
@@ -202,8 +200,7 @@ export const CreateWorkspacePageView: FC<
CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR
],
) && (
<AlertBanner
severity="error"
<ErrorAlert
error={
props.createWorkspaceErrors[
CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR
@@ -219,8 +216,7 @@ export const CreateWorkspacePageView: FC<
CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR
],
) && (
<AlertBanner
severity="error"
<ErrorAlert
error={
props.createWorkspaceErrors[
CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR
@@ -1,6 +1,6 @@
import { DeploymentOption } from "api/types"
import { DeploymentDAUsResponse } from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { ErrorAlert } from "components/Alert/ErrorAlert"
import { DAUChart } from "components/DAUChart/DAUChart"
import { Header } from "components/DeploySettingsLayout/Header"
import OptionsTable from "components/DeploySettingsLayout/OptionsTable"
@@ -26,7 +26,7 @@ export const GeneralSettingsPageView = ({
/>
<Stack spacing={4}>
{Boolean(getDeploymentDAUsError) && (
<AlertBanner error={getDeploymentDAUsError} severity="error" />
<ErrorAlert error={getDeploymentDAUsError} />
)}
{deploymentDAUs && <DAUChart daus={deploymentDAUs} />}
<OptionsTable
@@ -6,7 +6,7 @@ import TableContainer from "@mui/material/TableContainer"
import TableHead from "@mui/material/TableHead"
import TableRow from "@mui/material/TableRow"
import { DeploymentValues, GitAuthConfig } from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
import { EnterpriseBadge } from "components/DeploySettingsLayout/Badges"
import { Header } from "components/DeploySettingsLayout/Header"
@@ -40,11 +40,9 @@ export const GitAuthSettingsPageView = ({
/>
<div className={styles.description}>
<AlertBanner
severity="info"
text="Integrating with multiple Git providers is an Enterprise feature."
actions={[<EnterpriseBadge key="enterprise" />]}
/>
<Alert severity="info" actions={[<EnterpriseBadge key="enterprise" />]}>
Integrating with multiple Git providers is an Enterprise feature.
</Alert>
</div>
<TableContainer>
@@ -1,7 +1,6 @@
import Button from "@mui/material/Button"
import TextField from "@mui/material/TextField"
import { makeStyles } from "@mui/styles"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Fieldset } from "components/DeploySettingsLayout/Fieldset"
import { Header } from "components/DeploySettingsLayout/Header"
import { FileUpload } from "components/FileUpload/FileUpload"
@@ -11,6 +10,7 @@ import { Stack } from "components/Stack/Stack"
import { DividerWithText } from "pages/DeploySettingsPage/LicensesSettingsPage/DividerWithText"
import { FC } from "react"
import { Link as RouterLink } from "react-router-dom"
import { ErrorAlert } from "components/Alert/ErrorAlert"
type AddNewLicenseProps = {
onSaveLicenseKey: (license: string) => void
@@ -54,7 +54,7 @@ export const AddNewLicensePageView: FC<AddNewLicenseProps> = ({
justifyContent="space-between"
>
<Header
title="Add a License"
title="Add a license"
description="Get access to high availability, RBAC, quotas, and more."
/>
<Button
@@ -66,9 +66,7 @@ export const AddNewLicensePageView: FC<AddNewLicenseProps> = ({
</Button>
</Stack>
{savingLicenseError && (
<AlertBanner severity="error" error={savingLicenseError}></AlertBanner>
)}
{savingLicenseError && <ErrorAlert error={savingLicenseError} />}
<FileUpload
isUploading={isUploading}
@@ -10,6 +10,7 @@ import { FC } from "react"
import Confetti from "react-confetti"
import { Link } from "react-router-dom"
import useWindowSize from "react-use/lib/useWindowSize"
import MuiLink from "@mui/material/Link"
type Props = {
showConfetti: boolean
@@ -59,7 +60,7 @@ const LicensesSettingsPageView: FC<Props> = ({
to="/settings/deployment/licenses/add"
startIcon={<AddIcon />}
>
Add a License
Add a license
</Button>
</Stack>
@@ -89,9 +90,12 @@ const LicensesSettingsPageView: FC<Props> = ({
</span>
<span className={styles.description}>
You{"'"}re missing out on high availability, RBAC, quotas, and
much more. Contact <a href="mailto:sales@coder.com">sales</a> or{" "}
<a href="https://coder.com/trial">request a trial license</a> to
get started.
much more. Contact{" "}
<MuiLink href="mailto:sales@coder.com">sales</MuiLink> or{" "}
<MuiLink href="https://coder.com/trial">
request a trial license
</MuiLink>{" "}
to get started.
</span>
</Stack>
</Stack>
@@ -52,7 +52,6 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
href="https://coder.com/docs/coder-oss/latest/enterprise"
target="_blank"
rel="noreferrer"
size="small"
startIcon={<ArrowRightAltOutlined />}
variant="contained"
>
@@ -12,10 +12,10 @@ import { FC } from "react"
import { StarterTemplateContext } from "xServices/starterTemplates/starterTemplateXService"
import ViewCodeIcon from "@mui/icons-material/OpenInNewOutlined"
import PlusIcon from "@mui/icons-material/AddOutlined"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { useTranslation } from "react-i18next"
import { Stack } from "components/Stack/Stack"
import { Link } from "react-router-dom"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export interface StarterTemplatePageViewProps {
context: StarterTemplateContext
@@ -31,7 +31,7 @@ export const StarterTemplatePageView: FC<StarterTemplatePageViewProps> = ({
if (context.error) {
return (
<Margins>
<AlertBanner error={context.error} severity="error" />
<ErrorAlert error={context.error} />
</Margins>
)
}
@@ -1,5 +1,5 @@
import { makeStyles } from "@mui/styles"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { ErrorAlert } from "components/Alert/ErrorAlert"
import { Maybe } from "components/Conditionals/Maybe"
import { Loader } from "components/Loader/Loader"
import { Margins } from "components/Margins/Margins"
@@ -57,7 +57,7 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
</PageHeader>
<Maybe condition={Boolean(context.error)}>
<AlertBanner error={context.error} severity="error" />
<ErrorAlert error={context.error} />
</Maybe>
<Maybe condition={Boolean(!starterTemplatesByTag)}>
@@ -43,7 +43,6 @@ export const TemplatePermissionsPage: FC<
rel="noreferrer"
>
<Button
size="small"
startIcon={<ArrowRightAltOutlined />}
variant="contained"
>
@@ -3,14 +3,14 @@ import {
TemplateVersion,
TemplateVersionVariable,
} from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
import { Loader } from "components/Loader/Loader"
import { ComponentProps, FC } from "react"
import { TemplateVariablesForm } from "./TemplateVariablesForm"
import { Stack } from "components/Stack/Stack"
import { makeStyles } from "@mui/styles"
import { useTranslation } from "react-i18next"
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export interface TemplateVariablesPageViewProps {
templateVersion?: TemplateVersion
@@ -50,21 +50,15 @@ export const TemplateVariablesPageView: FC<TemplateVariablesPageViewProps> = ({
<PageHeader className={classes.pageHeader}>
<PageHeaderTitle>{t("title")}</PageHeaderTitle>
</PageHeader>
{Boolean(errors.getTemplateDataError) && (
<Stack className={classes.errorContainer}>
<AlertBanner severity="error" error={errors.getTemplateDataError} />
</Stack>
)}
{Boolean(errors.updateTemplateError) && (
<Stack className={classes.errorContainer}>
<AlertBanner severity="error" error={errors.updateTemplateError} />
</Stack>
)}
{Boolean(errors.jobError) && (
<Stack className={classes.errorContainer}>
<AlertBanner severity="error" text={errors.jobError} />
</Stack>
)}
<div className={classes.errorContainer}>
{Boolean(errors.getTemplateDataError) && (
<ErrorAlert error={errors.getTemplateDataError} />
)}
{Boolean(errors.updateTemplateError) && (
<ErrorAlert error={errors.updateTemplateError} />
)}
{Boolean(errors.jobError) && <ErrorAlert error={errors.jobError} />}
</div>
{isLoading && <Loader />}
{templateVersion && templateVariables && templateVariables.length > 0 && (
<TemplateVariablesForm
@@ -78,7 +72,7 @@ export const TemplateVariablesPageView: FC<TemplateVariablesPageViewProps> = ({
/>
)}
{templateVariables && templateVariables.length === 0 && (
<AlertBanner severity="info" text={t("unusedVariablesNotice")} />
<Alert severity="info">{t("unusedVariablesNotice")}</Alert>
)}
</>
)
@@ -1,7 +1,6 @@
import Button from "@mui/material/Button"
import Link from "@mui/material/Link"
import EditIcon from "@mui/icons-material/Edit"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Loader } from "components/Loader/Loader"
import { Margins } from "components/Margins/Margins"
import {
@@ -18,6 +17,7 @@ import { useTranslation } from "react-i18next"
import { Link as RouterLink } from "react-router-dom"
import { createDayString } from "utils/createDayString"
import { TemplateVersionMachineContext } from "xServices/templateVersion/templateVersionXService"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export interface TemplateVersionPageViewProps {
/**
@@ -57,7 +57,7 @@ export const TemplateVersionPageView: FC<TemplateVersionPageViewProps> = ({
{!currentFiles && !error && <Loader />}
<Stack spacing={4}>
{Boolean(error) && <AlertBanner severity="error" error={error} />}
{Boolean(error) && <ErrorAlert error={error} />}
{currentVersion && currentFiles && (
<>
<Stats>
@@ -8,7 +8,6 @@ import TableContainer from "@mui/material/TableContainer"
import TableHead from "@mui/material/TableHead"
import TableRow from "@mui/material/TableRow"
import AddIcon from "@mui/icons-material/AddOutlined"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
import { Maybe } from "components/Conditionals/Maybe"
import { FC } from "react"
@@ -42,6 +41,7 @@ import { combineClasses } from "utils/combineClasses"
import { colors } from "theme/colors"
import ArrowForwardOutlined from "@mui/icons-material/ArrowForwardOutlined"
import { Avatar } from "components/Avatar/Avatar"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export const Language = {
developerCount: (activeCount: number): string => {
@@ -193,7 +193,7 @@ export const TemplatesPageView: FC<
<ChooseOne>
<Cond condition={Boolean(error)}>
<AlertBanner severity="error" error={error} />
<ErrorAlert error={error} />
</Cond>
<Cond>
@@ -5,10 +5,6 @@ import { useAuth } from "components/AuthProvider/AuthProvider"
import { useMe } from "hooks/useMe"
import { usePermissions } from "hooks/usePermissions"
export const Language = {
title: "Account",
}
export const AccountPage: FC = () => {
const [authState, authSend] = useAuth()
const me = useMe()
@@ -17,7 +13,7 @@ export const AccountPage: FC = () => {
const canEditUsers = permissions && permissions.updateUsers
return (
<Section title={Language.title} description="Update your account info">
<Section title="Account" description="Update your account info">
<AccountForm
editable={Boolean(canEditUsers)}
email={me.email}
@@ -4,7 +4,7 @@ import { renderWithAuth } from "../../../testHelpers/renderHelpers"
import { Language as SSHKeysPageLanguage, SSHKeysPage } from "./SSHKeysPage"
import { Language as SSHKeysPageViewLanguage } from "./SSHKeysPageView"
import { i18n } from "i18n"
import { MockGitSSHKey } from "testHelpers/entities"
import { MockGitSSHKey, mockApiError } from "testHelpers/entities"
const { t } = i18n
@@ -66,7 +66,11 @@ describe("SSH keys Page", () => {
// Wait to the ssh be rendered on the screen
await screen.findByText(MockGitSSHKey.public_key)
jest.spyOn(API, "regenerateUserSSHKey").mockRejectedValueOnce({})
jest.spyOn(API, "regenerateUserSSHKey").mockRejectedValueOnce(
mockApiError({
message: "Error regenerating SSH key",
}),
)
// Click on the "Regenerate" button to display the confirm dialog
const regenerateButton = screen.getByRole("button", {
@@ -85,7 +89,7 @@ describe("SSH keys Page", () => {
fireEvent.click(confirmButton)
// Check if the error message is displayed
await screen.findByText(SSHKeysPageViewLanguage.errorRegenerateSSHKey)
await screen.findByText("Error regenerating SSH key")
// Check if the API was called correctly
expect(API.regenerateUserSSHKey).toBeCalledTimes(1)
@@ -3,13 +3,12 @@ import Box from "@mui/material/Box"
import Button from "@mui/material/Button"
import CircularProgress from "@mui/material/CircularProgress"
import { GitSSHKey } from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { CodeExample } from "components/CodeExample/CodeExample"
import { Stack } from "components/Stack/Stack"
import { FC } from "react"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export const Language = {
errorRegenerateSSHKey: "Error on regenerating the SSH Key",
regenerateLabel: "Regenerate",
}
@@ -46,16 +45,9 @@ export const SSHKeysPageView: FC<
<Stack>
{/* Regenerating the key is not an option if getSSHKey fails.
Only one of the error messages will exist at a single time */}
{Boolean(getSSHKeyError) && (
<AlertBanner severity="error" error={getSSHKeyError} />
)}
{Boolean(getSSHKeyError) && <ErrorAlert error={getSSHKeyError} />}
{Boolean(regenerateSSHKeyError) && (
<AlertBanner
severity="error"
error={regenerateSSHKeyError}
text={Language.errorRegenerateSSHKey}
dismissible
/>
<ErrorAlert error={regenerateSSHKeyError} dismissible />
)}
{hasLoaded && sshKey && (
<>
@@ -12,11 +12,11 @@ import { TableLoader } from "components/TableLoader/TableLoader"
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"
import dayjs from "dayjs"
import { FC } from "react"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import IconButton from "@mui/material/IconButton/IconButton"
import { useTranslation } from "react-i18next"
import { APIKeyWithOwner } from "api/typesGenerated"
import relativeTime from "dayjs/plugin/relativeTime"
import { ErrorAlert } from "components/Alert/ErrorAlert"
dayjs.extend(relativeTime)
@@ -50,12 +50,8 @@ export const TokensPageView: FC<
return (
<Stack>
{Boolean(getTokensError) && (
<AlertBanner severity="error" error={getTokensError} />
)}
{Boolean(deleteTokenError) && (
<AlertBanner severity="error" error={deleteTokenError} />
)}
{Boolean(getTokensError) && <ErrorAlert error={getTokensError} />}
{Boolean(deleteTokenError) && <ErrorAlert error={deleteTokenError} />}
<TableContainer>
<Table>
<TableHead>
@@ -9,10 +9,10 @@ import { Stack } from "components/Stack/Stack"
import { TableEmpty } from "components/TableEmpty/TableEmpty"
import { TableLoader } from "components/TableLoader/TableLoader"
import { FC } from "react"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Region } from "api/typesGenerated"
import { ProxyRow } from "./WorkspaceProxyRow"
import { ProxyLatencyReport } from "contexts/useProxyLatency"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export interface WorkspaceProxyViewProps {
proxies?: Region[]
@@ -40,11 +40,9 @@ export const WorkspaceProxyView: FC<
return (
<Stack>
{Boolean(getWorkspaceProxiesError) && (
<AlertBanner severity="error" error={getWorkspaceProxiesError} />
)}
{Boolean(selectProxyError) && (
<AlertBanner severity="error" error={selectProxyError} />
<ErrorAlert error={getWorkspaceProxiesError} />
)}
{Boolean(selectProxyError) && <ErrorAlert error={selectProxyError} />}
<TableContainer>
<Table>
<TableHead>
@@ -1,6 +1,6 @@
import Box from "@mui/material/Box"
import { WorkspaceBuild } from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
const Language = {
stateMessage:
@@ -18,22 +18,19 @@ export const WorkspaceBuildStateError: React.FC<
build.workspace_owner_name + "/" + build.workspace_name
} --orphan`
return (
<AlertBanner
severity="error"
text={
<Box>
{Language.stateMessage} A template admin may run{" "}
<Box
component="code"
display="inline-block"
width="fit-content"
fontWeight={600}
>
`{orphanCommand}`
</Box>{" "}
to delete the workspace skipping resource destruction.
</Box>
}
/>
<Alert severity="error">
<Box>
{Language.stateMessage} A template admin may run{" "}
<Box
component="code"
display="inline-block"
width="fit-content"
fontWeight={600}
>
`{orphanCommand}`
</Box>{" "}
to delete the workspace skipping resource destruction.
</Box>
</Alert>
)
}
+6 -11
View File
@@ -3,7 +3,6 @@ import { useQuery } from "@tanstack/react-query"
import { useMachine } from "@xstate/react"
import { getWorkspaceBuildLogs } from "api/api"
import { Workspace } from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
import { Loader } from "components/Loader/Loader"
import { FC, useRef } from "react"
@@ -12,6 +11,7 @@ import { quotaMachine } from "xServices/quotas/quotasXService"
import { workspaceMachine } from "xServices/workspace/workspaceXService"
import { WorkspaceReadyPage } from "./WorkspaceReadyPage"
import { RequirePermission } from "components/RequirePermission/RequirePermission"
import { ErrorAlert } from "components/Alert/ErrorAlert"
const useFailedBuildLogs = (workspace: Workspace | undefined) => {
const now = useRef(new Date())
@@ -61,23 +61,18 @@ export const WorkspacePage: FC = () => {
<Cond condition={workspaceState.matches("error")}>
<div className={styles.error}>
{Boolean(getWorkspaceError) && (
<AlertBanner severity="error" error={getWorkspaceError} />
<ErrorAlert error={getWorkspaceError} />
)}
{Boolean(getTemplateWarning) && (
<AlertBanner severity="error" error={getTemplateWarning} />
<ErrorAlert error={getTemplateWarning} />
)}
{Boolean(getTemplateParametersWarning) && (
<AlertBanner
severity="error"
error={getTemplateParametersWarning}
/>
<ErrorAlert error={getTemplateParametersWarning} />
)}
{Boolean(checkPermissionsError) && (
<AlertBanner severity="error" error={checkPermissionsError} />
)}
{Boolean(getQuotaError) && (
<AlertBanner severity="error" error={getQuotaError} />
<ErrorAlert error={checkPermissionsError} />
)}
{Boolean(getQuotaError) && <ErrorAlert error={getQuotaError} />}
</div>
</Cond>
<Cond
@@ -1,6 +1,6 @@
import { makeStyles } from "@mui/styles"
import { useMachine } from "@xstate/react"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"
import { Loader } from "components/Loader/Loader"
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"
@@ -22,6 +22,7 @@ import {
formValuesToAutostartRequest,
formValuesToTTLRequest,
} from "./formToRequest"
import { ErrorAlert } from "components/Alert/ErrorAlert"
const getAutostart = (workspace: TypesGen.Workspace) =>
scheduleToAutostart(workspace.autostart_schedule)
@@ -75,13 +76,10 @@ export const WorkspaceSchedulePage: FC = () => {
</PageHeader>
{(scheduleState.hasTag("loading") || !template) && <Loader />}
{scheduleState.matches("error") && (
<AlertBanner
severity="error"
error={checkPermissionsError || getTemplateError}
/>
<ErrorAlert error={checkPermissionsError || getTemplateError} />
)}
{permissions && !permissions.updateWorkspace && (
<AlertBanner severity="error" error={Error(t("forbiddenError"))} />
<Alert severity="error">{t("forbiddenError")}</Alert>
)}
{template &&
workspace &&
@@ -1,6 +1,6 @@
import Link from "@mui/material/Link"
import { Workspace } from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { Alert } from "components/Alert/Alert"
import { Maybe } from "components/Conditionals/Maybe"
import { PaginationWidgetBase } from "components/PaginationWidget/PaginationWidgetBase"
import { FC } from "react"
@@ -18,6 +18,7 @@ import { WorkspacesTable } from "components/WorkspacesTable/WorkspacesTable"
import { workspaceFilterQuery } from "utils/filters"
import { useLocalStorage } from "hooks"
import difference from "lodash/difference"
import { ErrorAlert } from "components/Alert/ErrorAlert"
export const Language = {
pageTitle: "Workspaces",
@@ -126,17 +127,10 @@ export const WorkspacesPageView: FC<
<Stack>
<Maybe condition={Boolean(error)}>
<AlertBanner
error={error}
severity={
workspaces !== undefined && workspaces.length > 0
? "warning"
: "error"
}
/>
<ErrorAlert error={error} />
</Maybe>
<Maybe condition={displayImpendingDeletionBanner}>
<AlertBanner
<Alert
severity="info"
onDismiss={() =>
saveLocal(
@@ -145,8 +139,9 @@ export const WorkspacesPageView: FC<
)
}
dismissible
text="You have workspaces that will be deleted soon."
/>
>
You have workspaces that will be deleted soon.
</Alert>
</Maybe>
<SearchBarWithFilter
+39 -9
View File
@@ -3,9 +3,9 @@ import { ThemeOptions, createTheme, Theme } from "@mui/material/styles"
import { BODY_FONT_FAMILY, borderRadius } from "./constants"
// MUI does not have aligned heights for buttons and inputs so we have to "hack" it a little bit
const BUTTON_LG_HEIGHT = 46
const BUTTON_MD_HEIGHT = 40
const BUTTON_SM_HEIGHT = 36
const BUTTON_LG_HEIGHT = 42
const BUTTON_MD_HEIGHT = 36
const BUTTON_SM_HEIGHT = 30
export type PaletteIndex = keyof Theme["palette"]
export type PaletteStatusIndex = Extract<
@@ -40,7 +40,7 @@ export let dark = createTheme({
divider: colors.gray[13],
warning: {
light: colors.orange[7],
main: colors.orange[11],
main: colors.orange[9],
dark: colors.orange[15],
},
success: {
@@ -48,13 +48,14 @@ export let dark = createTheme({
dark: colors.green[15],
},
info: {
light: colors.blue[9],
main: colors.blue[11],
light: colors.blue[7],
main: colors.blue[9],
dark: colors.blue[15],
contrastText: colors.gray[4],
},
error: {
main: colors.red[5],
light: colors.red[6],
main: colors.red[8],
dark: colors.red[15],
contrastText: colors.gray[4],
},
@@ -126,6 +127,7 @@ dark = createTheme(dark, {
fontWeight: 500,
height: BUTTON_MD_HEIGHT,
padding: theme.spacing(1, 2),
whiteSpace: "nowrap",
":focus-visible": {
outline: `2px solid ${theme.palette.primary.main}`,
@@ -164,12 +166,12 @@ dark = createTheme(dark, {
},
iconSizeMedium: {
"& > .MuiSvgIcon-root": {
fontSize: 16,
fontSize: 14,
},
},
iconSizeSmall: {
"& > .MuiSvgIcon-root": {
fontSize: 14,
fontSize: 13,
},
},
},
@@ -396,5 +398,33 @@ dark = createTheme(dark, {
},
},
},
MuiAlert: {
defaultProps: {
variant: "outlined",
},
styleOverrides: {
root: ({ theme }) => ({
background: theme.palette.background.paper,
}),
action: {
paddingTop: 2, // Idk why it is not aligned as expected
},
icon: {
fontSize: 16,
marginTop: "4px", // The size of text is 24 so (24 - 16)/2 = 4
},
message: ({ theme }) => ({
color: theme.palette.text.primary,
}),
},
},
MuiAlertTitle: {
styleOverrides: {
root: {
fontSize: "inherit",
marginBottom: 0,
},
},
},
},
} as ThemeOptions)