From ab8235f53e76a63cba758203d1a0b36224e03ac5 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 6 Jun 2022 09:50:07 -0500 Subject: [PATCH] feat: Add links to the resource card for workspace applications (#2067) * fix: Use proper webpack config for dev mode This was broken when improving the build times. The typechecker unfortunately missed it! * feat: Add links to the resource card for workspace applications Fixes #1907 and #805. I'll make this pretty in another PR! * Improve style --- site/src/AppRouter.tsx | 182 +++++++++--------- .../components/AppLink/AppLink.stories.tsx | 25 +++ site/src/components/AppLink/AppLink.tsx | 48 +++++ site/src/components/Resources/Resources.tsx | 30 ++- .../components/TerminalLink/TerminalLink.tsx | 2 +- .../WorkspaceAppErrorPage.tsx | 18 ++ .../WorkspaceAppErrorPageView.stories.tsx | 16 ++ .../WorkspaceAppErrorPageView.tsx | 31 +++ site/static/code.svg | 41 ++++ 9 files changed, 298 insertions(+), 95 deletions(-) create mode 100644 site/src/components/AppLink/AppLink.stories.tsx create mode 100644 site/src/components/AppLink/AppLink.tsx create mode 100644 site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPage.tsx create mode 100644 site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPageView.stories.tsx create mode 100644 site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPageView.tsx create mode 100644 site/static/code.svg diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index 6633ef917b7ec..e2b1a828da266 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -19,6 +19,7 @@ import { WorkspaceBuildPage } from "./pages/WorkspaceBuildPage/WorkspaceBuildPag import { WorkspacePage } from "./pages/WorkspacePage/WorkspacePage" import { WorkspaceSchedulePage } from "./pages/WorkspaceSchedulePage/WorkspaceSchedulePage" +const WorkspaceAppErrorPage = lazy(() => import("./pages/WorkspaceAppErrorPage/WorkspaceAppErrorPage")) const TerminalPage = lazy(() => import("./pages/TerminalPage/TerminalPage")) const WorkspacesPage = lazy(() => import("./pages/WorkspacesPage/WorkspacesPage")) const CreateWorkspacePage = lazy(() => import("./pages/CreateWorkspacePage/CreateWorkspacePage")) @@ -26,138 +27,147 @@ const CreateWorkspacePage = lazy(() => import("./pages/CreateWorkspacePage/Creat export const AppRouter: FC = () => ( }> - + + + + } + /> + + } /> + } /> + + + + } + /> + + - - + + + } /> - } /> - } /> - + } /> - + - + } /> - - + } /> - - - - - - } - /> - - - - } - /> - + - - - - - } - /> + + + + + } + /> - - - - } - /> - + + + + } + /> + - - - - - } - /> + + + + + } + /> + + + + } + /> + + + }> + } /> + } /> + } /> + + + + + + } + /> + + + - + } /> - - }> - } /> - } /> - } /> - - - - + - - + + + } /> + - - - - } - /> - - {/* Using path="*"" means "match anything", so this route + {/* Using path="*"" means "match anything", so this route acts like a catch-all for URLs that we don't have explicit routes for. */} - } /> - + } /> ) diff --git a/site/src/components/AppLink/AppLink.stories.tsx b/site/src/components/AppLink/AppLink.stories.tsx new file mode 100644 index 0000000000000..1b67d439784e6 --- /dev/null +++ b/site/src/components/AppLink/AppLink.stories.tsx @@ -0,0 +1,25 @@ +import { Story } from "@storybook/react" +import { MockWorkspace } from "../../testHelpers/renderHelpers" +import { AppLink, AppLinkProps } from "./AppLink" + +export default { + title: "components/AppLink", + component: AppLink, +} + +const Template: Story = (args) => + +export const WithIcon = Template.bind({}) +WithIcon.args = { + userName: "developer", + workspaceName: MockWorkspace.name, + appName: "code-server", + appIcon: "/code.svg", +} + +export const WithoutIcon = Template.bind({}) +WithoutIcon.args = { + userName: "developer", + workspaceName: MockWorkspace.name, + appName: "code-server", +} diff --git a/site/src/components/AppLink/AppLink.tsx b/site/src/components/AppLink/AppLink.tsx new file mode 100644 index 0000000000000..425e60fd63bda --- /dev/null +++ b/site/src/components/AppLink/AppLink.tsx @@ -0,0 +1,48 @@ +import Link from "@material-ui/core/Link" +import { makeStyles } from "@material-ui/core/styles" +import React, { FC } from "react" +import * as TypesGen from "../../api/typesGenerated" +import { combineClasses } from "../../util/combineClasses" + +export interface AppLinkProps { + userName: TypesGen.User["username"] + workspaceName: TypesGen.Workspace["name"] + appName: TypesGen.WorkspaceApp["name"] + appIcon: TypesGen.WorkspaceApp["icon"] +} + +export const AppLink: FC = ({ userName, workspaceName, appName, appIcon }) => { + const styles = useStyles() + const href = `/@${userName}/${workspaceName}/apps/${appName}` + + return ( + + {`${appName} + {appName} + + ) +} + +const useStyles = makeStyles((theme) => ({ + link: { + color: theme.palette.text.secondary, + display: "flex", + alignItems: "center", + }, + + icon: { + width: 16, + height: 16, + marginRight: theme.spacing(1.5), + + // If no icon is provided we still want the padding on the left + // to occur. + "&.empty": { + opacity: 0, + }, + }, +})) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index d959d53a501a9..73ae16713d1f5 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -8,6 +8,8 @@ import useTheme from "@material-ui/styles/useTheme" import { FC } from "react" import { Workspace, WorkspaceResource } from "../../api/typesGenerated" import { getDisplayAgentStatus } from "../../util/workspace" +import { AppLink } from "../AppLink/AppLink" +import { Stack } from "../Stack/Stack" import { TableHeaderRow } from "../TableHeaders/TableHeaders" import { TerminalLink } from "../TerminalLink/TerminalLink" import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection" @@ -83,14 +85,26 @@ export const Resources: FC = ({ resources, getResourcesError, wo {agent.operating_system} - {agent.status === "connected" && ( - - )} + + {agent.status === "connected" && ( + + )} + {agent.status === "connected" && + agent.apps.map((app) => ( + + ))} + diff --git a/site/src/components/TerminalLink/TerminalLink.tsx b/site/src/components/TerminalLink/TerminalLink.tsx index dbe7941151ad7..fbfc2d07eb183 100644 --- a/site/src/components/TerminalLink/TerminalLink.tsx +++ b/site/src/components/TerminalLink/TerminalLink.tsx @@ -26,7 +26,7 @@ export interface TerminalLinkProps { */ export const TerminalLink: FC = ({ agentName, userName = "me", workspaceName, className }) => { const styles = useStyles() - const href = `/${userName}/${workspaceName}${agentName ? `.${agentName}` : ""}/terminal` + const href = `/@${userName}/${workspaceName}${agentName ? `.${agentName}` : ""}/terminal` return ( { + const { app } = useParams() + const message = useMemo(() => { + const tag = document.getElementById("api-response") + if (!tag) { + throw new Error("dev error: api-response meta tag not found") + } + return tag.getAttribute("data-message") as string + }, []) + + return +} + +export default WorkspaceAppErrorView diff --git a/site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPageView.stories.tsx b/site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPageView.stories.tsx new file mode 100644 index 0000000000000..e8f0023f62d9f --- /dev/null +++ b/site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPageView.stories.tsx @@ -0,0 +1,16 @@ +import { Story } from "@storybook/react" +import { WorkspaceAppErrorPageView, WorkspaceAppErrorPageViewProps } from "./WorkspaceAppErrorPageView" + +export default { + title: "pages/WorkspaceAppErrorPageView", + component: WorkspaceAppErrorPageView, +} + +const Template: Story = (args) => + +export const NotRunning = Template.bind({}) +NotRunning.args = { + appName: "code-server", + // This is a real message copied and pasted from the backend! + message: "remote dial error: dial 'tcp://localhost:13337': dial tcp 127.0.0.1:13337: connect: connection refused", +} diff --git a/site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPageView.tsx b/site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPageView.tsx new file mode 100644 index 0000000000000..b84e02465678e --- /dev/null +++ b/site/src/pages/WorkspaceAppErrorPage/WorkspaceAppErrorPageView.tsx @@ -0,0 +1,31 @@ +import { makeStyles } from "@material-ui/core/styles" +import { FC } from "react" + +export interface WorkspaceAppErrorPageViewProps { + appName: string + message: string +} + +export const WorkspaceAppErrorPageView: FC = (props) => { + const styles = useStyles() + + return ( +
+

{props.appName} is offline!

+

{props.message}

+
+ ) +} + +const useStyles = makeStyles((theme) => ({ + root: { + flex: 1, + padding: theme.spacing(10), + }, + title: { + textAlign: "center", + }, + message: { + textAlign: "center", + }, +})) diff --git a/site/static/code.svg b/site/static/code.svg new file mode 100644 index 0000000000000..c6ee366939ba4 --- /dev/null +++ b/site/static/code.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +