Skip to content

feat(site): add documentation links to webterminal notifications #8019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions site/src/components/WarningAlert/WarningAlert.stories.tsx
Original file line number Diff line number Diff line change
@@ -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 = (
Expand All @@ -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,
},
}
74 changes: 22 additions & 52 deletions site/src/pages/TerminalPage/TerminalPage.tsx
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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: ",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
37 changes: 37 additions & 0 deletions site/src/pages/TerminalPage/TerminalPageAlert.stories.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 113 additions & 0 deletions site/src/pages/TerminalPage/TerminalPageAlert.tsx
Original file line number Diff line number Diff line change
@@ -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>
)
}