From d4539633f24875249bebbf86314d051b7c6c3f01 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 6 Jun 2022 17:16:22 +0000 Subject: [PATCH 1/7] feat: Add page header and help tooltips --- .../HelpTooltip/HelpTooltip.stories.tsx | 35 ++++ .../components/HelpTooltip/HelpTooltip.tsx | 138 ++++++++++++++ .../PageHeader/PageHeader.stories.tsx | 15 ++ site/src/components/PageHeader/PageHeader.tsx | 45 +++++ .../pages/TemplatesPage/TemplatesPageView.tsx | 112 ++++++----- site/src/pages/UsersPage/UsersPageView.tsx | 72 +++----- .../pages/WorkspacesPage/WorkspacesPage.tsx | 170 ++--------------- .../WorkspacesPage/WorkspacesPageView.tsx | 174 +++++++++++++++++- 8 files changed, 513 insertions(+), 248 deletions(-) create mode 100644 site/src/components/HelpTooltip/HelpTooltip.stories.tsx create mode 100644 site/src/components/HelpTooltip/HelpTooltip.tsx create mode 100644 site/src/components/PageHeader/PageHeader.stories.tsx create mode 100644 site/src/components/PageHeader/PageHeader.tsx diff --git a/site/src/components/HelpTooltip/HelpTooltip.stories.tsx b/site/src/components/HelpTooltip/HelpTooltip.stories.tsx new file mode 100644 index 0000000000000..6780a5700a6d0 --- /dev/null +++ b/site/src/components/HelpTooltip/HelpTooltip.stories.tsx @@ -0,0 +1,35 @@ +import { ComponentMeta, Story } from "@storybook/react" +import { + HelpTooltip, + HelpTooltipLink, + HelpTooltipLinksGroup, + HelpTooltipProps, + HelpTooltipText, + HelpTooltipTitle, +} from "./HelpTooltip" + +export default { + title: "components/HelpTooltip", + component: HelpTooltip, +} as ComponentMeta + +const Template: Story = (args) => ( + + What is template? + + With templates you can create a common configuration for your workspaces using Terraform. So, you and your team + can use the same environment to deliver great software. + + + Creating a template + Updating a template + + +) + +export const Close = Template.bind({}) + +export const Open = Template.bind({}) +Open.args = { + open: true, +} diff --git a/site/src/components/HelpTooltip/HelpTooltip.tsx b/site/src/components/HelpTooltip/HelpTooltip.tsx new file mode 100644 index 0000000000000..152b0cd85e966 --- /dev/null +++ b/site/src/components/HelpTooltip/HelpTooltip.tsx @@ -0,0 +1,138 @@ +import Link from "@material-ui/core/Link" +import Popover from "@material-ui/core/Popover" +import { makeStyles } from "@material-ui/core/styles" +import HelpIcon from "@material-ui/icons/HelpOutline" +import OpenInNewIcon from "@material-ui/icons/OpenInNew" +import { useState } from "react" +import { Stack } from "../Stack/Stack" + +export interface HelpTooltipProps { + // Useful to test on storybook + open?: boolean +} + +export const HelpTooltip: React.FC = ({ children, open }) => { + const styles = useStyles() + const [anchorEl, setAnchorEl] = useState(null) + open = open ?? Boolean(anchorEl) + const id = open ? "help-popover" : undefined + + return ( + <> + + { + setAnchorEl(null) + }} + anchorOrigin={{ + vertical: "bottom", + horizontal: "left", + }} + transformOrigin={{ + vertical: "top", + horizontal: "left", + }} + > + {children} + + + ) +} + +export const HelpTooltipTitle: React.FC = ({ children }) => { + const styles = useStyles() + + return

{children}

+} + +export const HelpTooltipText: React.FC = ({ children }) => { + const styles = useStyles() + + return

{children}

+} + +export const HelpTooltipLink: React.FC<{ href: string }> = ({ children, href }) => { + const styles = useStyles() + + return ( + + + {children} + + ) +} + +export const HelpTooltipLinksGroup: React.FC = ({ children }) => { + const styles = useStyles() + + return ( + + {children} + + ) +} + +const useStyles = makeStyles((theme) => ({ + button: { + display: "flex", + alignItems: "center", + justifyContent: "center", + width: theme.spacing(3), + height: theme.spacing(3), + padding: 0, + border: 0, + background: "transparent", + color: theme.palette.text.secondary, + cursor: "pointer", + marginLeft: theme.spacing(1), + + "&:hover": { + color: theme.palette.text.primary, + }, + }, + + icon: { + width: theme.spacing(2), + height: theme.spacing(2), + }, + + popoverPaper: { + marginTop: theme.spacing(0.5), + width: theme.spacing(38), + padding: theme.spacing(2.5), + color: theme.palette.text.secondary, + }, + + title: { + marginTop: 0, + marginBottom: theme.spacing(1), + color: theme.palette.text.primary, + }, + + text: { + marginTop: theme.spacing(0.5), + marginBottom: theme.spacing(0.5), + }, + + link: { + display: "flex", + alignItems: "center", + }, + + linkIcon: { + color: "inherit", + width: 14, + height: 14, + marginRight: theme.spacing(1), + }, + + linksGroup: { + marginTop: theme.spacing(2), + }, +})) diff --git a/site/src/components/PageHeader/PageHeader.stories.tsx b/site/src/components/PageHeader/PageHeader.stories.tsx new file mode 100644 index 0000000000000..be1a594fd4381 --- /dev/null +++ b/site/src/components/PageHeader/PageHeader.stories.tsx @@ -0,0 +1,15 @@ +import { ComponentMeta, Story } from "@storybook/react" +import { PageHeader, PageHeaderTitle } from "./PageHeader" + +export default { + title: "components/PageHeader", + component: PageHeader, +} as ComponentMeta + +const Template: Story = () => ( + + Templates + +) + +export const Example = Template.bind({}) diff --git a/site/src/components/PageHeader/PageHeader.tsx b/site/src/components/PageHeader/PageHeader.tsx new file mode 100644 index 0000000000000..19679e43f0dbb --- /dev/null +++ b/site/src/components/PageHeader/PageHeader.tsx @@ -0,0 +1,45 @@ +import { makeStyles } from "@material-ui/core/styles" +import { Stack } from "../Stack/Stack" + +export const PageHeader: React.FC = ({ children }) => { + const styles = useStyles() + + return
{children}
+} + +export const PageHeaderTitle: React.FC = ({ children }) => { + const styles = useStyles() + + return

{children}

+} + +export const PageHeaderActions: React.FC = ({ children }) => { + const styles = useStyles() + + return ( + + {children} + + ) +} + +const useStyles = makeStyles((theme) => ({ + root: { + display: "flex", + alignItems: "center", + paddingTop: theme.spacing(6), + paddingBottom: theme.spacing(5), + }, + + title: { + fontSize: theme.spacing(4), + fontWeight: 400, + margin: 0, + display: "flex", + alignItems: "center", + }, + + actions: { + marginLeft: "auto", + }, +})) diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx index e7629627d1216..f58a7d215ed31 100644 --- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx @@ -12,8 +12,15 @@ import * as TypesGen from "../../api/typesGenerated" import { AvatarData } from "../../components/AvatarData/AvatarData" import { CodeExample } from "../../components/CodeExample/CodeExample" import { EmptyState } from "../../components/EmptyState/EmptyState" +import { + HelpTooltip, + HelpTooltipLink, + HelpTooltipLinksGroup, + HelpTooltipText, + HelpTooltipTitle, +} from "../../components/HelpTooltip/HelpTooltip" import { Margins } from "../../components/Margins/Margins" -import { Stack } from "../../components/Stack/Stack" +import { PageHeader, PageHeaderTitle } from "../../components/PageHeader/PageHeader" import { TableLoader } from "../../components/TableLoader/TableLoader" dayjs.extend(relativeTime) @@ -38,6 +45,23 @@ export const Language = { ), } +const TemplateHelpTooltip: React.FC = () => { + return ( + + What is template? + + With templates you can create a common configuration for your workspaces using Terraform. So, you and your team + can use the same environment to deliver great software. + + + + Manage templates + + + + ) +} + export interface TemplatesPageViewProps { loading?: boolean canCreateTemplate?: boolean @@ -47,56 +71,58 @@ export interface TemplatesPageViewProps { export const TemplatesPageView: FC = (props) => { const styles = useStyles() return ( - - - - + + + + Templates + + + + +
+ + + {Language.nameLabel} + {Language.usedByLabel} + {Language.lastUpdatedLabel} + + + + {props.loading && } + {!props.loading && !props.templates?.length && ( - {Language.nameLabel} - {Language.usedByLabel} - {Language.lastUpdatedLabel} + + } + /> + - - - {props.loading && } - {!props.loading && !props.templates?.length && ( - - - } - /> - - - )} - {props.templates?.map((template) => ( - - - - + )} + {props.templates?.map((template) => ( + + + + - {Language.developerCount(template.workspace_owner_count)} + {Language.developerCount(template.workspace_owner_count)} - {dayjs().to(dayjs(template.updated_at))} - - ))} - -
-
-
+ {dayjs().to(dayjs(template.updated_at))} + + ))} + + + ) } const useStyles = makeStyles((theme) => ({ - root: { - marginTop: theme.spacing(10), - }, emptyDescription: { maxWidth: theme.spacing(62), }, diff --git a/site/src/pages/UsersPage/UsersPageView.tsx b/site/src/pages/UsersPage/UsersPageView.tsx index 59fceebf935b9..38ca901351524 100644 --- a/site/src/pages/UsersPage/UsersPageView.tsx +++ b/site/src/pages/UsersPage/UsersPageView.tsx @@ -1,11 +1,10 @@ import Button from "@material-ui/core/Button" -import { makeStyles } from "@material-ui/core/styles" import AddCircleOutline from "@material-ui/icons/AddCircleOutline" import { FC } from "react" import * as TypesGen from "../../api/typesGenerated" import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary" import { Margins } from "../../components/Margins/Margins" -import { Stack } from "../../components/Stack/Stack" +import { PageHeader, PageHeaderActions, PageHeaderTitle } from "../../components/PageHeader/PageHeader" import { UsersTable } from "../../components/UsersTable/UsersTable" export const Language = { @@ -40,48 +39,33 @@ export const UsersPageView: FC = ({ canCreateUser, isLoading, }) => { - const styles = useStyles() - return ( - - -
-
- {canCreateUser && ( - - )} -
-
- {error ? ( - - ) : ( - - )} -
-
+ + + Users + + {canCreateUser && ( + + )} + + + + {error ? ( + + ) : ( + + )} + ) } - -const useStyles = makeStyles((theme) => ({ - actions: { - marginTop: theme.spacing(3), - marginBottom: theme.spacing(3), - display: "flex", - height: theme.spacing(6), - - "& > *": { - marginLeft: "auto", - }, - }, -})) diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index 1df0d52342306..b3c47f173e46a 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -1,174 +1,32 @@ -import Button from "@material-ui/core/Button" -import Fade from "@material-ui/core/Fade" -import InputAdornment from "@material-ui/core/InputAdornment" -import Link from "@material-ui/core/Link" -import Menu from "@material-ui/core/Menu" -import MenuItem from "@material-ui/core/MenuItem" -import { makeStyles } from "@material-ui/core/styles" -import TextField from "@material-ui/core/TextField" -import AddCircleOutline from "@material-ui/icons/AddCircleOutline" -import SearchIcon from "@material-ui/icons/Search" import { useMachine } from "@xstate/react" -import { FormikErrors, useFormik } from "formik" -import { FC, useState } from "react" +import { FC } from "react" import { Helmet } from "react-helmet" -import { Link as RouterLink } from "react-router-dom" -import { CloseDropdown, OpenDropdown } from "../../components/DropdownArrows/DropdownArrows" -import { Margins } from "../../components/Margins/Margins" -import { Stack } from "../../components/Stack/Stack" -import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils" import { pageTitle } from "../../util/page" import { workspacesMachine } from "../../xServices/workspaces/workspacesXService" import { WorkspacesPageView } from "./WorkspacesPageView" -interface FilterFormValues { - query: string -} - -const Language = { - filterName: "Filters", - createWorkspaceButton: "Create workspace", - yourWorkspacesButton: "Your workspaces", - allWorkspacesButton: "All workspaces", -} - -export type FilterFormErrors = FormikErrors - const WorkspacesPage: FC = () => { - const styles = useStyles() const [workspacesState, send] = useMachine(workspacesMachine) - const form = useFormik({ - initialValues: { - query: workspacesState.context.filter || "", - }, - onSubmit: (values) => { - send({ - type: "SET_FILTER", - query: values.query, - }) - }, - }) - - const getFieldHelpers = getFormHelpers(form) - - const [anchorEl, setAnchorEl] = useState(null) - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget) - } - - const handleClose = () => { - setAnchorEl(null) - } - - const setYourWorkspaces = () => { - void form.setFieldValue("query", "owner:me") - void form.submitForm() - handleClose() - } - - const setAllWorkspaces = () => { - void form.setFieldValue("query", "") - void form.submitForm() - handleClose() - } - return ( - + <> {pageTitle("Workspaces")} - - - - -
- - - - ), - }} - /> - - - - {Language.yourWorkspacesButton} - {Language.allWorkspacesButton} - -
-
- - - - -
- -
+ { + send({ + type: "SET_FILTER", + query, + }) + }} + /> + ) } -const useStyles = makeStyles((theme) => ({ - workspacesHeaderContainer: { - marginTop: theme.spacing(3), - marginBottom: theme.spacing(3), - justifyContent: "space-between", - }, - filterColumn: { - width: "60%", - cursor: "text", - }, - filterContainer: { - border: `1px solid ${theme.palette.divider}`, - borderRadius: "6px", - }, - filterForm: { - width: "100%", - }, - buttonRoot: { - border: "none", - borderRight: `1px solid ${theme.palette.divider}`, - borderRadius: "6px 0px 0px 6px", - }, - textFieldRoot: { - margin: "0px", - "& fieldset": { - border: "none", - }, - }, -})) - export default WorkspacesPage diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx index 2a7c8a22b4a40..5a7a827bc5c66 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx @@ -1,22 +1,40 @@ import Button from "@material-ui/core/Button" +import Fade from "@material-ui/core/Fade" +import InputAdornment from "@material-ui/core/InputAdornment" import Link from "@material-ui/core/Link" +import Menu from "@material-ui/core/Menu" +import MenuItem from "@material-ui/core/MenuItem" import { makeStyles, Theme } from "@material-ui/core/styles" import Table from "@material-ui/core/Table" import TableBody from "@material-ui/core/TableBody" import TableCell from "@material-ui/core/TableCell" import TableHead from "@material-ui/core/TableHead" import TableRow from "@material-ui/core/TableRow" +import TextField from "@material-ui/core/TextField" import AddCircleOutline from "@material-ui/icons/AddCircleOutline" +import SearchIcon from "@material-ui/icons/Search" import useTheme from "@material-ui/styles/useTheme" import dayjs from "dayjs" import relativeTime from "dayjs/plugin/relativeTime" -import { FC } from "react" +import { FormikErrors, useFormik } from "formik" +import { FC, useState } from "react" import { Link as RouterLink } from "react-router-dom" import * as TypesGen from "../../api/typesGenerated" import { AvatarData } from "../../components/AvatarData/AvatarData" +import { CloseDropdown, OpenDropdown } from "../../components/DropdownArrows/DropdownArrows" import { EmptyState } from "../../components/EmptyState/EmptyState" +import { + HelpTooltip, + HelpTooltipLink, + HelpTooltipLinksGroup, + HelpTooltipText, + HelpTooltipTitle, +} from "../../components/HelpTooltip/HelpTooltip" +import { Margins } from "../../components/Margins/Margins" +import { PageHeader, PageHeaderActions, PageHeaderTitle } from "../../components/PageHeader/PageHeader" import { Stack } from "../../components/Stack/Stack" import { TableLoader } from "../../components/TableLoader/TableLoader" +import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils" import { getDisplayStatus } from "../../util/workspace" dayjs.extend(relativeTime) @@ -25,19 +43,145 @@ export const Language = { createButton: "Create workspace", emptyMessage: "Create your first workspace", emptyDescription: "Start editing your source code and building your software", + filterName: "Filters", + createWorkspaceButton: "Create workspace", + yourWorkspacesButton: "Your workspaces", + allWorkspacesButton: "All workspaces", } +const WorkspaceHelpTooltip: React.FC = () => { + return ( + + What is workspace? + + It is your workstation. It is a workspace that will provide you the necessary compute and access to your + development environment. + + + + Create workspaces + + + Connect with SSH + + + Editors and IDEs + + + + ) +} + +interface FilterFormValues { + query: string +} + +export type FilterFormErrors = FormikErrors + export interface WorkspacesPageViewProps { loading?: boolean workspaces?: TypesGen.Workspace[] + filter?: string + onFilter: (query: string) => void } -export const WorkspacesPageView: FC = ({ loading, workspaces }) => { - useStyles() +export const WorkspacesPageView: FC = ({ loading, workspaces, filter, onFilter }) => { + const styles = useStyles() const theme: Theme = useTheme() + const form = useFormik({ + initialValues: { + query: filter ?? "", + }, + onSubmit: ({ query }) => { + onFilter(query) + }, + }) + + const getFieldHelpers = getFormHelpers(form) + + const [anchorEl, setAnchorEl] = useState(null) + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget) + } + + const handleClose = () => { + setAnchorEl(null) + } + + const setYourWorkspaces = () => { + void form.setFieldValue("query", "owner:me") + void form.submitForm() + handleClose() + } + + const setAllWorkspaces = () => { + void form.setFieldValue("query", "") + void form.submitForm() + handleClose() + } + return ( - + + + + Workspaces + + + + + + + + + + + + + +
+ + + + ), + }} + /> + + + + {Language.yourWorkspacesButton} + {Language.allWorkspacesButton} + +
+ @@ -98,7 +242,7 @@ export const WorkspacesPageView: FC = ({ loading, works })}
-
+ ) } @@ -110,6 +254,7 @@ const useStyles = makeStyles((theme) => ({ flexDirection: "column", alignItems: "center", justifyContent: "center", + "& span": { maxWidth: 600, textAlign: "center", @@ -117,4 +262,23 @@ const useStyles = makeStyles((theme) => ({ lineHeight: `${theme.spacing(3)}px`, }, }, + filterContainer: { + border: `1px solid ${theme.palette.divider}`, + borderRadius: theme.shape.borderRadius, + marginBottom: theme.spacing(2), + }, + filterForm: { + width: "100%", + }, + buttonRoot: { + border: "none", + borderRight: `1px solid ${theme.palette.divider}`, + borderRadius: `${theme.shape.borderRadius}px 0px 0px ${theme.shape.borderRadius}px`, + }, + textFieldRoot: { + margin: "0px", + "& fieldset": { + border: "none", + }, + }, })) From c4e15cd04cee500ab3b65fcdcfa6c339404f849d Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 6 Jun 2022 17:29:57 +0000 Subject: [PATCH 2/7] Add header in the logs page --- .../WorkspaceBuildPage/WorkspaceBuildPage.tsx | 34 +++---------------- .../WorkspaceBuildPageView.stories.tsx | 17 ++++++++++ .../WorkspaceBuildPageView.tsx | 34 +++++++++++++++++++ 3 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx create mode 100644 site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx index 85d6ddbc0ae7d..46550ac62225a 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx @@ -1,21 +1,10 @@ -import { makeStyles } from "@material-ui/core/styles" -import Typography from "@material-ui/core/Typography" import { useMachine } from "@xstate/react" import { FC } from "react" import { Helmet } from "react-helmet" import { useParams } from "react-router-dom" -import { ProvisionerJobLog } from "../../api/typesGenerated" -import { Loader } from "../../components/Loader/Loader" -import { Margins } from "../../components/Margins/Margins" -import { Stack } from "../../components/Stack/Stack" -import { WorkspaceBuildLogs } from "../../components/WorkspaceBuildLogs/WorkspaceBuildLogs" -import { WorkspaceBuildStats } from "../../components/WorkspaceBuildStats/WorkspaceBuildStats" import { pageTitle } from "../../util/page" import { workspaceBuildMachine } from "../../xServices/workspaceBuild/workspaceBuildXService" - -const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => { - return [...logs].sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()) -} +import { WorkspaceBuildPageView } from "./WorkspaceBuildPageView" const useBuildId = () => { const { buildId } = useParams() @@ -32,29 +21,14 @@ export const WorkspaceBuildPage: FC = () => { const [buildState] = useMachine(workspaceBuildMachine, { context: { buildId } }) const { logs, build } = buildState.context const isWaitingForLogs = !buildState.matches("logs.loaded") - const styles = useStyles() return ( - + <> {build ? pageTitle(`Build #${build.build_number} ยท ${build.workspace_name}`) : ""} - - - Logs - - {build && } - {!logs && } - {logs && } - - + + ) } - -const useStyles = makeStyles((theme) => ({ - title: { - paddingTop: theme.spacing(5), - paddingBottom: theme.spacing(2), - }, -})) diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx new file mode 100644 index 0000000000000..24e47c97918ce --- /dev/null +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.stories.tsx @@ -0,0 +1,17 @@ +import { ComponentMeta, Story } from "@storybook/react" +import { MockWorkspaceBuild, MockWorkspaceBuildLogs } from "../../testHelpers/entities" +import { WorkspaceBuildPageView, WorkspaceBuildPageViewProps } from "./WorkspaceBuildPageView" + +export default { + title: "pages/WorkspaceBuildPageView", + component: WorkspaceBuildPageView, +} as ComponentMeta + +const Template: Story = (args) => + +export const Example = Template.bind({}) +Example.args = { + build: MockWorkspaceBuild, + logs: MockWorkspaceBuildLogs, + isWaitingForLogs: false, +} diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx new file mode 100644 index 0000000000000..5dd1e0a47c813 --- /dev/null +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx @@ -0,0 +1,34 @@ +import { FC } from "react" +import { ProvisionerJobLog, WorkspaceBuild } from "../../api/typesGenerated" +import { Loader } from "../../components/Loader/Loader" +import { Margins } from "../../components/Margins/Margins" +import { PageHeader, PageHeaderTitle } from "../../components/PageHeader/PageHeader" +import { Stack } from "../../components/Stack/Stack" +import { WorkspaceBuildLogs } from "../../components/WorkspaceBuildLogs/WorkspaceBuildLogs" +import { WorkspaceBuildStats } from "../../components/WorkspaceBuildStats/WorkspaceBuildStats" + +const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => { + return [...logs].sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()) +} + +export interface WorkspaceBuildPageViewProps { + logs: ProvisionerJobLog[] | undefined + build: WorkspaceBuild | undefined + isWaitingForLogs: boolean +} + +export const WorkspaceBuildPageView: FC = ({ logs, build, isWaitingForLogs }) => { + return ( + + + Logs + + + + {build && } + {!logs && } + {logs && } + + + ) +} From c725b33d1d2b2f442eca3ddf82b3eb58dc83a1a7 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 6 Jun 2022 17:43:55 +0000 Subject: [PATCH 3/7] Refactor page header title and subtitle --- site/src/components/PageHeader/PageHeader.tsx | 33 ++++++++--- .../pages/TemplatePage/TemplatePageView.tsx | 59 ++++--------------- site/src/pages/UsersPage/UsersPageView.tsx | 15 ++--- .../WorkspacesPage/WorkspacesPageView.tsx | 18 +++--- 4 files changed, 53 insertions(+), 72 deletions(-) diff --git a/site/src/components/PageHeader/PageHeader.tsx b/site/src/components/PageHeader/PageHeader.tsx index 19679e43f0dbb..9a6416972dd53 100644 --- a/site/src/components/PageHeader/PageHeader.tsx +++ b/site/src/components/PageHeader/PageHeader.tsx @@ -1,10 +1,21 @@ import { makeStyles } from "@material-ui/core/styles" import { Stack } from "../Stack/Stack" -export const PageHeader: React.FC = ({ children }) => { +export interface PageHeaderProps { + actions?: JSX.Element +} + +export const PageHeader: React.FC = ({ children, actions }) => { const styles = useStyles() - return
{children}
+ return ( +
+
{children}
+ + {actions} + +
+ ) } export const PageHeaderTitle: React.FC = ({ children }) => { @@ -13,14 +24,10 @@ export const PageHeaderTitle: React.FC = ({ children }) => { return

{children}

} -export const PageHeaderActions: React.FC = ({ children }) => { +export const PageHeaderSubtitle: React.FC = ({ children }) => { const styles = useStyles() - return ( - - {children} - - ) + return

{children}

} const useStyles = makeStyles((theme) => ({ @@ -37,6 +44,16 @@ const useStyles = makeStyles((theme) => ({ margin: 0, display: "flex", alignItems: "center", + lineHeight: "140%", + }, + + subtitle: { + fontSize: theme.spacing(2.5), + color: theme.palette.text.secondary, + fontWeight: 400, + display: "block", + margin: 0, + marginTop: theme.spacing(1), }, actions: { diff --git a/site/src/pages/TemplatePage/TemplatePageView.tsx b/site/src/pages/TemplatePage/TemplatePageView.tsx index 83d9ac64f5608..c9b572041a877 100644 --- a/site/src/pages/TemplatePage/TemplatePageView.tsx +++ b/site/src/pages/TemplatePage/TemplatePageView.tsx @@ -1,7 +1,6 @@ import Button from "@material-ui/core/Button" import Link from "@material-ui/core/Link" import { makeStyles } from "@material-ui/core/styles" -import Typography from "@material-ui/core/Typography" import AddCircleOutline from "@material-ui/icons/AddCircleOutline" import frontMatter from "front-matter" import { FC } from "react" @@ -9,11 +8,11 @@ import ReactMarkdown from "react-markdown" import { Link as RouterLink } from "react-router-dom" import { Template, TemplateVersion, WorkspaceResource } from "../../api/typesGenerated" import { Margins } from "../../components/Margins/Margins" +import { PageHeader, PageHeaderSubtitle, PageHeaderTitle } from "../../components/PageHeader/PageHeader" import { Stack } from "../../components/Stack/Stack" import { TemplateResourcesTable } from "../../components/TemplateResourcesTable/TemplateResourcesTable" import { TemplateStats } from "../../components/TemplateStats/TemplateStats" import { WorkspaceSection } from "../../components/WorkspaceSection/WorkspaceSection" -import { MONOSPACE_FONT_FAMILY } from "../../theme/constants" const Language = { createButton: "Create workspace", @@ -38,23 +37,19 @@ export const TemplatePageView: FC = ({ template, activeTe return ( -
-
- - {template.name} - - - - {template.description === "" ? Language.noDescription : template.description} - -
- -
+ -
-
+ } + > + {template.name} + + {" "} + {template.description === "" ? Language.noDescription : template.description} + + @@ -83,38 +78,6 @@ export const TemplatePageView: FC = ({ template, activeTe export const useStyles = makeStyles((theme) => { return { - root: { - display: "flex", - flexDirection: "column", - }, - header: { - paddingTop: theme.spacing(5), - paddingBottom: theme.spacing(5), - fontFamily: MONOSPACE_FONT_FAMILY, - display: "flex", - alignItems: "center", - }, - headerActions: { - marginLeft: "auto", - }, - title: { - fontWeight: 600, - fontFamily: "inherit", - }, - subtitle: { - fontFamily: "inherit", - marginTop: theme.spacing(0.5), - }, - layout: { - alignItems: "flex-start", - }, - main: { - width: "100%", - }, - sidebar: { - width: theme.spacing(32), - flexShrink: 0, - }, readmeContents: { margin: 0, }, diff --git a/site/src/pages/UsersPage/UsersPageView.tsx b/site/src/pages/UsersPage/UsersPageView.tsx index 38ca901351524..af315017c3462 100644 --- a/site/src/pages/UsersPage/UsersPageView.tsx +++ b/site/src/pages/UsersPage/UsersPageView.tsx @@ -4,7 +4,7 @@ import { FC } from "react" import * as TypesGen from "../../api/typesGenerated" import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary" import { Margins } from "../../components/Margins/Margins" -import { PageHeader, PageHeaderActions, PageHeaderTitle } from "../../components/PageHeader/PageHeader" +import { PageHeader, PageHeaderTitle } from "../../components/PageHeader/PageHeader" import { UsersTable } from "../../components/UsersTable/UsersTable" export const Language = { @@ -41,15 +41,16 @@ export const UsersPageView: FC = ({ }) => { return ( - - Users - - {canCreateUser && ( + }> {Language.createButton} - )} - + ) : undefined + } + > + Users {error ? ( diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx index 5a7a827bc5c66..ca77085178f5f 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx @@ -31,7 +31,7 @@ import { HelpTooltipTitle, } from "../../components/HelpTooltip/HelpTooltip" import { Margins } from "../../components/Margins/Margins" -import { PageHeader, PageHeaderActions, PageHeaderTitle } from "../../components/PageHeader/PageHeader" +import { PageHeader, PageHeaderTitle } from "../../components/PageHeader/PageHeader" import { Stack } from "../../components/Stack/Stack" import { TableLoader } from "../../components/TableLoader/TableLoader" import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils" @@ -124,19 +124,19 @@ export const WorkspacesPageView: FC = ({ loading, works return ( - - - Workspaces - - - - + - + } + > + + Workspaces + + From 9e2b90c39e465514d158ef94a144e3af9f4d5d6a Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 6 Jun 2022 17:49:34 +0000 Subject: [PATCH 4/7] Update page header --- site/src/components/Workspace/Workspace.tsx | 52 ++++++---------- .../src/pages/WorkspacePage/WorkspacePage.tsx | 61 +++++++++---------- 2 files changed, 47 insertions(+), 66 deletions(-) diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index 3903dcdd2a1c6..0646421e5be78 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -1,9 +1,10 @@ import { makeStyles } from "@material-ui/core/styles" -import Typography from "@material-ui/core/Typography" import { FC } from "react" import * as TypesGen from "../../api/typesGenerated" import { MONOSPACE_FONT_FAMILY } from "../../theme/constants" import { BuildsTable } from "../BuildsTable/BuildsTable" +import { Margins } from "../Margins/Margins" +import { PageHeader, PageHeaderSubtitle, PageHeaderTitle } from "../PageHeader/PageHeader" import { Resources } from "../Resources/Resources" import { Stack } from "../Stack/Stack" import { WorkspaceActions } from "../WorkspaceActions/WorkspaceActions" @@ -46,33 +47,22 @@ export const Workspace: FC = ({ const styles = useStyles() return ( -
- - -
-
- - {workspace.name} - - - - {workspace.owner_name} - -
- - -
-
- - -
+ + + } + > + {workspace.name} + {workspace.owner_name} + @@ -95,16 +85,12 @@ export const Workspace: FC = ({ -
+
) } export const useStyles = makeStyles((theme) => { return { - root: { - display: "flex", - flexDirection: "column", - }, firstColumnSpacer: { flex: 2, }, diff --git a/site/src/pages/WorkspacePage/WorkspacePage.tsx b/site/src/pages/WorkspacePage/WorkspacePage.tsx index 8d9f3d30f08ab..a65fa95e55bc6 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.tsx @@ -5,8 +5,6 @@ import { useNavigate, useParams } from "react-router-dom" import { DeleteWorkspaceDialog } from "../../components/DeleteWorkspaceDialog/DeleteWorkspaceDialog" import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary" import { FullScreenLoader } from "../../components/Loader/FullScreenLoader" -import { Margins } from "../../components/Margins/Margins" -import { Stack } from "../../components/Stack/Stack" import { Workspace } from "../../components/Workspace/Workspace" import { firstOrItem } from "../../util/array" import { pageTitle } from "../../util/page" @@ -37,40 +35,37 @@ export const WorkspacePage: React.FC = () => { return } else { return ( - + <> {pageTitle(`${workspace.owner_name}/${workspace.name}`)} - - <> - { - bannerSend({ type: "EXTEND_DEADLINE_DEFAULT", workspaceId: workspace.id }) - }, - }} - workspace={workspace} - handleStart={() => workspaceSend("START")} - handleStop={() => workspaceSend("STOP")} - handleDelete={() => workspaceSend("ASK_DELETE")} - handleUpdate={() => workspaceSend("UPDATE")} - handleCancel={() => workspaceSend("CANCEL")} - resources={resources} - getResourcesError={getResourcesError instanceof Error ? getResourcesError : undefined} - builds={builds} - /> - workspaceSend("CANCEL_DELETE")} - handleConfirm={() => { - workspaceSend("DELETE") - navigate("/workspaces") - }} - /> - - - + + { + bannerSend({ type: "EXTEND_DEADLINE_DEFAULT", workspaceId: workspace.id }) + }, + }} + workspace={workspace} + handleStart={() => workspaceSend("START")} + handleStop={() => workspaceSend("STOP")} + handleDelete={() => workspaceSend("ASK_DELETE")} + handleUpdate={() => workspaceSend("UPDATE")} + handleCancel={() => workspaceSend("CANCEL")} + resources={resources} + getResourcesError={getResourcesError instanceof Error ? getResourcesError : undefined} + builds={builds} + /> + workspaceSend("CANCEL_DELETE")} + handleConfirm={() => { + workspaceSend("DELETE") + navigate("/workspaces") + }} + /> + ) } } From 915c3c55e2f30fd98e7a5e5bb2a94beb0ee82371 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 6 Jun 2022 18:08:48 +0000 Subject: [PATCH 5/7] Add tooltip for agent and resource --- .../components/HelpTooltip/HelpTooltip.tsx | 35 +++++++++++--- site/src/components/Resources/Resources.tsx | 46 ++++++++++++++++++- site/src/components/Stack/Stack.tsx | 8 +++- 3 files changed, 78 insertions(+), 11 deletions(-) diff --git a/site/src/components/HelpTooltip/HelpTooltip.tsx b/site/src/components/HelpTooltip/HelpTooltip.tsx index 152b0cd85e966..8d14452ec2421 100644 --- a/site/src/components/HelpTooltip/HelpTooltip.tsx +++ b/site/src/components/HelpTooltip/HelpTooltip.tsx @@ -6,13 +6,15 @@ import OpenInNewIcon from "@material-ui/icons/OpenInNew" import { useState } from "react" import { Stack } from "../Stack/Stack" +type Size = "small" | "medium" export interface HelpTooltipProps { // Useful to test on storybook open?: boolean + size?: Size } -export const HelpTooltip: React.FC = ({ children, open }) => { - const styles = useStyles() +export const HelpTooltip: React.FC = ({ children, open, size = "medium" }) => { + const styles = useStyles({ size }) const [anchorEl, setAnchorEl] = useState(null) open = open ?? Boolean(anchorEl) const id = open ? "help-popover" : undefined @@ -78,19 +80,38 @@ export const HelpTooltipLinksGroup: React.FC = ({ children }) => { ) } +const getButtonSpacingFromSize = (size?: Size): number => { + switch (size) { + case "small": + return 2.75 + case "medium": + default: + return 3 + } +} + +const getIconSpacingFromSize = (size?: Size): number => { + switch (size) { + case "small": + return 1.75 + case "medium": + default: + return 2 + } +} + const useStyles = makeStyles((theme) => ({ button: { display: "flex", alignItems: "center", justifyContent: "center", - width: theme.spacing(3), - height: theme.spacing(3), + width: ({ size }: { size?: Size }) => theme.spacing(getButtonSpacingFromSize(size)), + height: ({ size }: { size?: Size }) => theme.spacing(getButtonSpacingFromSize(size)), padding: 0, border: 0, background: "transparent", color: theme.palette.text.secondary, cursor: "pointer", - marginLeft: theme.spacing(1), "&:hover": { color: theme.palette.text.primary, @@ -98,8 +119,8 @@ const useStyles = makeStyles((theme) => ({ }, icon: { - width: theme.spacing(2), - height: theme.spacing(2), + width: ({ size }: { size?: Size }) => theme.spacing(getIconSpacingFromSize(size)), + height: ({ size }: { size?: Size }) => theme.spacing(getIconSpacingFromSize(size)), }, popoverPaper: { diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index 73ae16713d1f5..5e138219cdc07 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -9,6 +9,13 @@ import { FC } from "react" import { Workspace, WorkspaceResource } from "../../api/typesGenerated" import { getDisplayAgentStatus } from "../../util/workspace" import { AppLink } from "../AppLink/AppLink" +import { + HelpTooltip, + HelpTooltipLink, + HelpTooltipLinksGroup, + HelpTooltipText, + HelpTooltipTitle, +} from "../HelpTooltip/HelpTooltip" import { Stack } from "../Stack/Stack" import { TableHeaderRow } from "../TableHeaders/TableHeaders" import { TerminalLink } from "../TerminalLink/TerminalLink" @@ -23,6 +30,31 @@ const Language = { accessLabel: "Access", } +const ResourcesHelpTooltip: React.FC = () => { + return ( + + What is a resource? + + A resource is an infrastructure object that is create when the workspace is provisioned. + + + + Persistent and ephemeral resources + + + + ) +} + +const AgentHelpTooltip: React.FC = () => { + return ( + + What is an agent? + An agent is a software that executes Coder inside of the resource. + + ) +} + interface ResourcesProps { resources?: WorkspaceResource[] getResourcesError?: Error @@ -41,8 +73,18 @@ export const Resources: FC = ({ resources, getResourcesError, wo - {Language.resourceLabel} - {Language.agentLabel} + + + {Language.resourceLabel} + + + + + + {Language.agentLabel} + + + {Language.accessLabel} {Language.statusLabel} diff --git a/site/src/components/Stack/Stack.tsx b/site/src/components/Stack/Stack.tsx index dc91c716f863c..e0089c06f0382 100644 --- a/site/src/components/Stack/Stack.tsx +++ b/site/src/components/Stack/Stack.tsx @@ -1,4 +1,5 @@ import { makeStyles } from "@material-ui/core/styles" +import { CSSProperties } from "@material-ui/core/styles/withStyles" import { FC } from "react" import { combineClasses } from "../../util/combineClasses" @@ -7,6 +8,7 @@ type Direction = "column" | "row" interface StyleProps { direction: Direction spacing: number + alignItems?: CSSProperties["alignItems"] } const useStyles = makeStyles((theme) => ({ @@ -14,6 +16,7 @@ const useStyles = makeStyles((theme) => ({ display: "flex", flexDirection: ({ direction }: StyleProps) => direction, gap: ({ spacing }: StyleProps) => theme.spacing(spacing), + alignItems: ({ alignItems }: StyleProps) => alignItems, }, })) @@ -21,10 +24,11 @@ export interface StackProps { className?: string direction?: Direction spacing?: number + alignItems?: CSSProperties["alignItems"] } -export const Stack: FC = ({ children, className, direction = "column", spacing = 2 }) => { - const styles = useStyles({ spacing, direction }) +export const Stack: FC = ({ children, className, direction = "column", spacing = 2, alignItems }) => { + const styles = useStyles({ spacing, direction, alignItems }) return
{children}
} From 05af10aaf9e2ec07c42ae33a6986b6b1b08d89cb Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 6 Jun 2022 18:11:34 +0000 Subject: [PATCH 6/7] Add help space from titles --- site/src/pages/TemplatesPage/TemplatesPageView.tsx | 7 +++++-- site/src/pages/WorkspacesPage/WorkspacesPageView.tsx | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx index f58a7d215ed31..40b3fc18786a1 100644 --- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx @@ -21,6 +21,7 @@ import { } from "../../components/HelpTooltip/HelpTooltip" import { Margins } from "../../components/Margins/Margins" import { PageHeader, PageHeaderTitle } from "../../components/PageHeader/PageHeader" +import { Stack } from "../../components/Stack/Stack" import { TableLoader } from "../../components/TableLoader/TableLoader" dayjs.extend(relativeTime) @@ -74,8 +75,10 @@ export const TemplatesPageView: FC = (props) => { - Templates - + + Templates + + diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx index ca77085178f5f..3460959fcccce 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx @@ -134,8 +134,10 @@ export const WorkspacesPageView: FC = ({ loading, works } > - Workspaces - + + Workspaces + + From 81f2e16cebbf5a1030cdc5da1a8730a3bbe7fce9 Mon Sep 17 00:00:00 2001 From: Bruno Date: Tue, 7 Jun 2022 13:34:51 +0000 Subject: [PATCH 7/7] Move tooltip content to language object --- site/src/components/Resources/Resources.tsx | 18 +++++++++++------- .../pages/TemplatesPage/TemplatesPageView.tsx | 12 ++++++------ .../WorkspacesPage/WorkspacesPageView.tsx | 19 +++++++++++-------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index 5e138219cdc07..8929457e1ef19 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -28,18 +28,22 @@ const Language = { agentLabel: "Agent", statusLabel: "Status", accessLabel: "Access", + resourceTooltipTitle: "What is a resource?", + resourceTooltipText: "A resource is an infrastructure object that is create when the workspace is provisioned.", + resourceTooltipLink: "Persistent and ephemeral resources", + agentTooltipTitle: "What is an agent?", + agentTooltipText: + "The Coder agent runs inside your resource and gives you direct access to the shell via the UI or CLI.", } const ResourcesHelpTooltip: React.FC = () => { return ( - What is a resource? - - A resource is an infrastructure object that is create when the workspace is provisioned. - + {Language.resourceTooltipTitle} + {Language.resourceTooltipText} - Persistent and ephemeral resources + {Language.resourceTooltipLink} @@ -49,8 +53,8 @@ const ResourcesHelpTooltip: React.FC = () => { const AgentHelpTooltip: React.FC = () => { return ( - What is an agent? - An agent is a software that executes Coder inside of the resource. + {Language.agentTooltipTitle} + {Language.agentTooltipTitle} ) } diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx index 40b3fc18786a1..c78e09be89f43 100644 --- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx @@ -44,19 +44,19 @@ export const Language = { or use a built-in template using the following Coder CLI command: ), + templateTooltipTitle: "What is template?", + templateTooltipText: "With templates you can create a common configuration for your workspaces using Terraform.", + templateTooltipLink: "Manage templates", } const TemplateHelpTooltip: React.FC = () => { return ( - What is template? - - With templates you can create a common configuration for your workspaces using Terraform. So, you and your team - can use the same environment to deliver great software. - + {Language.templateTooltipTitle} + {Language.templateTooltipText} - Manage templates + {Language.templateTooltipLink} diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx index 3460959fcccce..555ea0d0d3a00 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx @@ -47,25 +47,28 @@ export const Language = { createWorkspaceButton: "Create workspace", yourWorkspacesButton: "Your workspaces", allWorkspacesButton: "All workspaces", + workspaceTooltipTitle: "What is workspace?", + workspaceTooltipText: + "It is your workstation. It is a workspace that will provide you the necessary compute and access to your development environment.", + workspaceTooltipLink1: "Create workspaces", + workspaceTooltipLink2: "Connect with SSH", + workspaceTooltipLink3: "Editors and IDEs", } const WorkspaceHelpTooltip: React.FC = () => { return ( - What is workspace? - - It is your workstation. It is a workspace that will provide you the necessary compute and access to your - development environment. - + {Language.workspaceTooltipTitle} + {Language.workspaceTooltipTitle} - Create workspaces + {Language.workspaceTooltipLink1} - Connect with SSH + {Language.workspaceTooltipLink2} - Editors and IDEs + {Language.workspaceTooltipLink3} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy