mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
feat(site): add documentation links to webterminal notifications (#8019)
* feat(site): add documentation links to webterminal notifications * change button variant
This commit is contained in:
@ -1,18 +1,21 @@
|
||||
import { Story } from "@storybook/react"
|
||||
import { WarningAlert, WarningAlertProps } from "./WarningAlert"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import { WarningAlert } from "./WarningAlert"
|
||||
import Button from "@mui/material/Button"
|
||||
|
||||
export default {
|
||||
const meta: Meta<typeof WarningAlert> = {
|
||||
title: "components/WarningAlert",
|
||||
component: WarningAlert,
|
||||
}
|
||||
|
||||
const Template: Story<WarningAlertProps> = (args) => <WarningAlert {...args} />
|
||||
export default meta
|
||||
|
||||
export const ExampleWithDismiss = Template.bind({})
|
||||
ExampleWithDismiss.args = {
|
||||
text: "This is a warning",
|
||||
dismissible: true,
|
||||
type Story = StoryObj<typeof WarningAlert>
|
||||
|
||||
export const ExampleWithDismiss: Story = {
|
||||
args: {
|
||||
text: "This is a warning",
|
||||
dismissible: true,
|
||||
},
|
||||
}
|
||||
|
||||
const ExampleAction = (
|
||||
@ -21,15 +24,17 @@ const ExampleAction = (
|
||||
</Button>
|
||||
)
|
||||
|
||||
export const ExampleWithAction = Template.bind({})
|
||||
ExampleWithAction.args = {
|
||||
text: "This is a warning",
|
||||
actions: [ExampleAction],
|
||||
export const 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,
|
||||
export const ExampleWithActionAndDismiss = {
|
||||
args: {
|
||||
text: "This is a warning",
|
||||
actions: [ExampleAction],
|
||||
dismissible: true,
|
||||
},
|
||||
}
|
||||
|
@ -1,7 +1,4 @@
|
||||
import Button from "@mui/material/Button"
|
||||
import { makeStyles, useTheme } from "@mui/styles"
|
||||
import WarningIcon from "@mui/icons-material/ErrorOutlineRounded"
|
||||
import RefreshOutlined from "@mui/icons-material/RefreshOutlined"
|
||||
import { useMachine } from "@xstate/react"
|
||||
import { portForwardURL } from "components/PortForwardButton/PortForwardButton"
|
||||
import { Stack } from "components/Stack/Stack"
|
||||
@ -18,13 +15,13 @@ import { MONOSPACE_FONT_FAMILY } from "../../theme/constants"
|
||||
import { pageTitle } from "../../utils/page"
|
||||
import { terminalMachine } from "../../xServices/terminal/terminalXService"
|
||||
import { useProxy } from "contexts/ProxyContext"
|
||||
import { combineClasses } from "utils/combineClasses"
|
||||
import Box from "@mui/material/Box"
|
||||
import { useDashboard } from "components/Dashboard/DashboardProvider"
|
||||
import { Region } from "api/typesGenerated"
|
||||
import { getLatencyColor } from "utils/latency"
|
||||
import Popover from "@mui/material/Popover"
|
||||
import { ProxyStatusLatency } from "components/ProxyStatusLatency/ProxyStatusLatency"
|
||||
import TerminalPageAlert, { TerminalPageAlertType } from "./TerminalPageAlert"
|
||||
|
||||
export const Language = {
|
||||
workspaceErrorMessagePrefix: "Unable to fetch workspace: ",
|
||||
@ -80,12 +77,26 @@ const TerminalPage: FC = () => {
|
||||
websocketError,
|
||||
} = terminalState.context
|
||||
const reloading = useReloading(isDisconnected)
|
||||
const shouldDisplayStartupWarning = workspaceAgent
|
||||
? ["starting", "starting_timeout"].includes(workspaceAgent.lifecycle_state)
|
||||
: false
|
||||
const shouldDisplayStartupError = workspaceAgent
|
||||
? workspaceAgent.lifecycle_state === "start_error"
|
||||
: false
|
||||
const lifecycleState = workspaceAgent?.lifecycle_state
|
||||
const [startupWarning, setStartupWarning] = useState<
|
||||
TerminalPageAlertType | undefined
|
||||
>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
if (lifecycleState === "start_error") {
|
||||
setStartupWarning("error")
|
||||
} else if (lifecycleState === "starting") {
|
||||
setStartupWarning("starting")
|
||||
} else {
|
||||
setStartupWarning((prev) => {
|
||||
if (prev === "starting") {
|
||||
return "success"
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
}
|
||||
}, [lifecycleState])
|
||||
|
||||
const dashboard = useDashboard()
|
||||
const proxyContext = useProxy()
|
||||
const selectedProxy = proxyContext.proxy.proxy
|
||||
@ -305,49 +316,8 @@ const TerminalPage: FC = () => {
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
{shouldDisplayStartupError && (
|
||||
<div
|
||||
className={combineClasses([styles.alert, styles.alertError])}
|
||||
role="alert"
|
||||
>
|
||||
<WarningIcon className={styles.alertIcon} />
|
||||
<div>
|
||||
<div className={styles.alertTitle}>Startup script failed</div>
|
||||
<div className={styles.alertMessage}>
|
||||
You can continue using this terminal, but something may be missing
|
||||
or not fully set up.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Box display="flex" flexDirection="column" height="100vh">
|
||||
{shouldDisplayStartupWarning && (
|
||||
<div className={styles.alert} role="alert">
|
||||
<WarningIcon className={styles.alertIcon} />
|
||||
<div>
|
||||
<div className={styles.alertTitle}>
|
||||
Startup script is still running
|
||||
</div>
|
||||
<div className={styles.alertMessage}>
|
||||
You can continue using this terminal, but something may be
|
||||
missing or not fully set up.
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.alertActions}>
|
||||
<Button
|
||||
startIcon={<RefreshOutlined />}
|
||||
size="small"
|
||||
onClick={() => {
|
||||
// By redirecting the user without the session in the URL we
|
||||
// create a new one
|
||||
window.location.href = window.location.pathname
|
||||
}}
|
||||
>
|
||||
Refresh session
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{startupWarning && <TerminalPageAlert alertType={startupWarning} />}
|
||||
<div
|
||||
className={styles.terminal}
|
||||
ref={xtermRef}
|
||||
|
37
site/src/pages/TerminalPage/TerminalPageAlert.stories.tsx
Normal file
37
site/src/pages/TerminalPage/TerminalPageAlert.stories.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react"
|
||||
|
||||
import TerminalPageAlert from "./TerminalPageAlert"
|
||||
|
||||
const meta: Meta<typeof TerminalPageAlert> = {
|
||||
component: TerminalPageAlert,
|
||||
title: "components/TerminalPageAlert",
|
||||
argTypes: {
|
||||
alertType: {
|
||||
control: {
|
||||
type: "radio",
|
||||
},
|
||||
options: ["error", "starting", "success"],
|
||||
},
|
||||
},
|
||||
}
|
||||
type Story = StoryObj<typeof TerminalPageAlert>
|
||||
|
||||
export const Error: Story = {
|
||||
args: {
|
||||
alertType: "error",
|
||||
},
|
||||
}
|
||||
|
||||
export const Starting: Story = {
|
||||
args: {
|
||||
alertType: "starting",
|
||||
},
|
||||
}
|
||||
|
||||
export const Success: Story = {
|
||||
args: {
|
||||
alertType: "success",
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
113
site/src/pages/TerminalPage/TerminalPageAlert.tsx
Normal file
113
site/src/pages/TerminalPage/TerminalPageAlert.tsx
Normal file
@ -0,0 +1,113 @@
|
||||
import { AlertColor } from "@mui/material/Alert/Alert"
|
||||
import Button from "@mui/material/Button"
|
||||
import Link from "@mui/material/Link"
|
||||
import { Alert } from "components/Alert/Alert"
|
||||
import { ReactNode } from "react"
|
||||
|
||||
export type TerminalPageAlertType = "error" | "starting" | "success"
|
||||
|
||||
type MapAlertTypeToComponent = {
|
||||
[key in TerminalPageAlertType]: {
|
||||
severity: AlertColor
|
||||
children: ReactNode | undefined
|
||||
}
|
||||
}
|
||||
|
||||
const mapAlertTypeToText: MapAlertTypeToComponent = {
|
||||
error: {
|
||||
severity: "warning",
|
||||
children: (
|
||||
<>
|
||||
The workspace{" "}
|
||||
<Link
|
||||
title="startup script has exited with an error"
|
||||
href="https://coder.com/docs/v2/latest/templates#startup-script-exited-with-an-error"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
startup script has exited with an error
|
||||
</Link>
|
||||
, we recommend reloading this session and{" "}
|
||||
<Link
|
||||
title=" debugging the startup script"
|
||||
href="https://coder.com/docs/v2/latest/templates#debugging-the-startup-script"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
debugging the startup script
|
||||
</Link>{" "}
|
||||
because{" "}
|
||||
<Link
|
||||
title="your workspace may be incomplete."
|
||||
href="https://coder.com/docs/v2/latest/templates#your-workspace-may-be-incomplete"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
your workspace may be incomplete.
|
||||
</Link>{" "}
|
||||
</>
|
||||
),
|
||||
},
|
||||
starting: {
|
||||
severity: "info",
|
||||
children: (
|
||||
<>
|
||||
Startup script is still running. You can continue using this terminal,
|
||||
but{" "}
|
||||
<Link
|
||||
title="your workspace may be incomplete."
|
||||
href="https://coder.com/docs/v2/latest/templates#your-workspace-may-be-incomplete"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{" "}
|
||||
your workspace may be incomplete.
|
||||
</Link>
|
||||
</>
|
||||
),
|
||||
},
|
||||
success: {
|
||||
severity: "success",
|
||||
children: (
|
||||
<>
|
||||
Startup script has completed successfully. The workspace is ready but
|
||||
this{" "}
|
||||
<Link
|
||||
title="session was started before the startup script finished"
|
||||
href="https://coder.com/docs/v2/latest/templates#your-workspace-may-be-incomplete"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
session was started before the startup script finished.
|
||||
</Link>{" "}
|
||||
To ensure your shell environment is up-to-date, we recommend reloading
|
||||
this session.
|
||||
</>
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
export default ({ alertType }: { alertType: TerminalPageAlertType }) => {
|
||||
return (
|
||||
<Alert
|
||||
severity={mapAlertTypeToText[alertType].severity}
|
||||
dismissible
|
||||
actions={[
|
||||
<Button
|
||||
key="refresh-session"
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
// By redirecting the user without the session in the URL we
|
||||
// create a new one
|
||||
window.location.href = window.location.pathname
|
||||
}}
|
||||
>
|
||||
Refresh session
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
{mapAlertTypeToText[alertType].children}
|
||||
</Alert>
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user