From 2792949861ec7277837801934c9d8b56e0ed5444 Mon Sep 17 00:00:00 2001 From: kira-pilot Date: Thu, 30 Jun 2022 20:02:55 +0000 Subject: [PATCH 1/2] cleaning up workspace table code --- .../components/Tooltips/HelpTooltip/index.ts | 1 + .../Tooltips/OutdatedHelpTooltip.tsx | 33 +++ .../Tooltips/WorkspaceHelpTooltip.tsx | 37 +++ site/src/components/Tooltips/index.ts | 4 + .../components/UsersTable/UsersTableBody.tsx | 2 +- .../WorkspacesTable/WorkspacesRow.tsx | 107 ++++++++ .../WorkspacesTable/WorkspacesTable.tsx | 42 +++ .../WorkspacesTable/WorkspacesTableBody.tsx | 69 +++++ site/src/pages/UsersPage/UsersPageView.tsx | 2 +- .../WorkspacesPage/WorkspacesPage.test.tsx | 4 +- .../pages/WorkspacesPage/WorkspacesPage.tsx | 2 +- .../WorkspacesPage/WorkspacesPageView.tsx | 242 +----------------- 12 files changed, 310 insertions(+), 235 deletions(-) create mode 100644 site/src/components/Tooltips/HelpTooltip/index.ts create mode 100644 site/src/components/Tooltips/OutdatedHelpTooltip.tsx create mode 100644 site/src/components/Tooltips/WorkspaceHelpTooltip.tsx create mode 100644 site/src/components/Tooltips/index.ts create mode 100644 site/src/components/WorkspacesTable/WorkspacesRow.tsx create mode 100644 site/src/components/WorkspacesTable/WorkspacesTable.tsx create mode 100644 site/src/components/WorkspacesTable/WorkspacesTableBody.tsx diff --git a/site/src/components/Tooltips/HelpTooltip/index.ts b/site/src/components/Tooltips/HelpTooltip/index.ts new file mode 100644 index 0000000000000..588febfa26ab1 --- /dev/null +++ b/site/src/components/Tooltips/HelpTooltip/index.ts @@ -0,0 +1 @@ +export * from "./HelpTooltip" diff --git a/site/src/components/Tooltips/OutdatedHelpTooltip.tsx b/site/src/components/Tooltips/OutdatedHelpTooltip.tsx new file mode 100644 index 0000000000000..06ebba16e6c05 --- /dev/null +++ b/site/src/components/Tooltips/OutdatedHelpTooltip.tsx @@ -0,0 +1,33 @@ +import RefreshIcon from "@material-ui/icons/Refresh" +import { FC } from "react" +import { + HelpTooltip, + HelpTooltipAction, + HelpTooltipLinksGroup, + HelpTooltipText, + HelpTooltipTitle, +} from "./HelpTooltip" + +const Language = { + outdatedLabel: "Outdated", + versionTooltipText: "This workspace version is outdated and a newer version is available.", + updateVersionLabel: "Update version", +} + +interface TooltipProps { + onUpdateVersion: () => void +} + +export const OutdatedHelpTooltip: FC = ({ onUpdateVersion }) => { + return ( + + {Language.outdatedLabel} + {Language.versionTooltipText} + + + {Language.updateVersionLabel} + + + + ) +} diff --git a/site/src/components/Tooltips/WorkspaceHelpTooltip.tsx b/site/src/components/Tooltips/WorkspaceHelpTooltip.tsx new file mode 100644 index 0000000000000..6e6629b1b8b86 --- /dev/null +++ b/site/src/components/Tooltips/WorkspaceHelpTooltip.tsx @@ -0,0 +1,37 @@ +import { FC } from "react" +import { + HelpTooltip, + HelpTooltipLink, + HelpTooltipLinksGroup, + HelpTooltipText, + HelpTooltipTitle, +} from "./HelpTooltip" + +const Language = { + workspaceTooltipTitle: "What is a workspace?", + workspaceTooltipText: + "A workspace is your development environment in the cloud. It includes the infrastructure and tools you need to work on your project.", + workspaceTooltipLink1: "Create workspaces", + workspaceTooltipLink2: "Connect with SSH", + workspaceTooltipLink3: "Editors and IDEs", +} + +export const WorkspaceHelpTooltip: FC = () => { + return ( + + {Language.workspaceTooltipTitle} + {Language.workspaceTooltipText} + + + {Language.workspaceTooltipLink1} + + + {Language.workspaceTooltipLink2} + + + {Language.workspaceTooltipLink3} + + + + ) +} diff --git a/site/src/components/Tooltips/index.ts b/site/src/components/Tooltips/index.ts new file mode 100644 index 0000000000000..f2813adc68f0c --- /dev/null +++ b/site/src/components/Tooltips/index.ts @@ -0,0 +1,4 @@ +export { AgentHelpTooltip } from "./AgentHelpTooltip" +export { OutdatedHelpTooltip } from "./OutdatedHelpTooltip" +export { ResourcesHelpTooltip } from "./ResourcesHelpTooltip" +export { WorkspaceHelpTooltip } from "./WorkspaceHelpTooltip" diff --git a/site/src/components/UsersTable/UsersTableBody.tsx b/site/src/components/UsersTable/UsersTableBody.tsx index 37ac9d8a7286c..32e549bb326a0 100644 --- a/site/src/components/UsersTable/UsersTableBody.tsx +++ b/site/src/components/UsersTable/UsersTableBody.tsx @@ -47,7 +47,7 @@ export const UsersTableBody: FC = ({ return } - if (!users || users.length === 0) { + if (!users || !users.length) { return ( diff --git a/site/src/components/WorkspacesTable/WorkspacesRow.tsx b/site/src/components/WorkspacesTable/WorkspacesRow.tsx new file mode 100644 index 0000000000000..82fe62a551bc5 --- /dev/null +++ b/site/src/components/WorkspacesTable/WorkspacesRow.tsx @@ -0,0 +1,107 @@ +import { fade, makeStyles, Theme } from "@material-ui/core/styles" +import TableRow from "@material-ui/core/TableRow" +import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight" +import useTheme from "@material-ui/styles/useTheme" +import { useActor } from "@xstate/react" +import dayjs from "dayjs" +import relativeTime from "dayjs/plugin/relativeTime" +import { FC } from "react" +import { useNavigate } from "react-router-dom" +import { getDisplayStatus } from "../../util/workspace" +import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService" +import { AvatarData } from "../AvatarData/AvatarData" +import { TableCellLink } from "../TableCellLink/TableCellLink" +import { OutdatedHelpTooltip } from "../Tooltips" + +dayjs.extend(relativeTime) + +const Language = { + upToDateLabel: "Up to date", + outdatedLabel: "Outdated", +} + +export const WorkspacesRow: FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ workspaceRef }) => { + const styles = useStyles() + const navigate = useNavigate() + const theme: Theme = useTheme() + const [workspaceState, send] = useActor(workspaceRef) + const { data: workspace } = workspaceState.context + const status = getDisplayStatus(theme, workspace.latest_build) + const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}` + + return ( + { + if (event.key === "Enter") { + navigate(workspacePageLink) + } + }} + className={styles.clickableTableRow} + > + + + + {workspace.template_name} + + {workspace.outdated ? ( + + {Language.outdatedLabel} + { + send("UPDATE_VERSION") + }} + /> + + ) : ( + {Language.upToDateLabel} + )} + + + + {dayjs().to(dayjs(workspace.latest_build.created_at))} + + + + {status.status} + + +
+ +
+
+
+ ) +} + +const useStyles = makeStyles((theme) => ({ + clickableTableRow: { + "&:hover td": { + backgroundColor: fade(theme.palette.primary.light, 0.1), + }, + + "&:focus": { + outline: `1px solid ${theme.palette.secondary.dark}`, + }, + + "& .MuiTableCell-root:last-child": { + paddingRight: theme.spacing(2), + }, + }, + arrowRight: { + color: fade(theme.palette.primary.contrastText, 0.7), + width: 20, + height: 20, + }, + arrowCell: { + display: "flex", + }, + outdatedLabel: { + color: theme.palette.error.main, + display: "flex", + alignItems: "center", + gap: theme.spacing(0.5), + }, +})) diff --git a/site/src/components/WorkspacesTable/WorkspacesTable.tsx b/site/src/components/WorkspacesTable/WorkspacesTable.tsx new file mode 100644 index 0000000000000..f4fc08dfcfc68 --- /dev/null +++ b/site/src/components/WorkspacesTable/WorkspacesTable.tsx @@ -0,0 +1,42 @@ +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 { FC } from "react" +import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService" +import { WorkspacesTableBody } from "./WorkspacesTableBody" + +const Language = { + name: "Name", + template: "Template", + version: "Version", + lastBuilt: "Last Built", + status: "Status", +} + +export interface WorkspacesTableProps { + isLoading?: boolean + workspaceRefs?: WorkspaceItemMachineRef[] + filter?: string +} + +export const WorkspacesTable: FC = ({ isLoading, workspaceRefs, filter }) => { + return ( + + + + {Language.name} + {Language.template} + {Language.version} + {Language.lastBuilt} + {Language.status} + + + + + + +
+ ) +} diff --git a/site/src/components/WorkspacesTable/WorkspacesTableBody.tsx b/site/src/components/WorkspacesTable/WorkspacesTableBody.tsx new file mode 100644 index 0000000000000..1a7f80f8af0fd --- /dev/null +++ b/site/src/components/WorkspacesTable/WorkspacesTableBody.tsx @@ -0,0 +1,69 @@ +import Button from "@material-ui/core/Button" +import Link from "@material-ui/core/Link" +import TableCell from "@material-ui/core/TableCell" +import TableRow from "@material-ui/core/TableRow" +import AddCircleOutline from "@material-ui/icons/AddCircleOutline" +import { FC } from "react" +import { Link as RouterLink } from "react-router-dom" +import { workspaceFilterQuery } from "../../util/filters" +import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService" +import { EmptyState } from "../EmptyState/EmptyState" +import { TableLoader } from "../TableLoader/TableLoader" +import { WorkspacesRow } from "./WorkspacesRow" + +export const Language = { + emptyCreateWorkspaceMessage: "Create your first workspace", + emptyCreateWorkspaceDescription: "Start editing your source code and building your software", + createFromTemplateButton: "Create from template", + emptyResultsMessage: "No results matched your search", +} + +interface TableBodyProps { + isLoading?: boolean + workspaceRefs?: WorkspaceItemMachineRef[] + filter?: string +} + +export const WorkspacesTableBody: FC = ({ isLoading, workspaceRefs, filter }) => { + if (isLoading) { + return + } + + if (!workspaceRefs || !workspaceRefs.length) { + return ( + <> + {filter === workspaceFilterQuery.me || filter === workspaceFilterQuery.all ? ( + + + + + + } + /> + + + ) : ( + + + + + + )} + + ) + } + + return ( + <> + {workspaceRefs.map((workspaceRef) => ( + + ))} + + ) +} diff --git a/site/src/pages/UsersPage/UsersPageView.tsx b/site/src/pages/UsersPage/UsersPageView.tsx index 95015a9ba4578..d411282a89177 100644 --- a/site/src/pages/UsersPage/UsersPageView.tsx +++ b/site/src/pages/UsersPage/UsersPageView.tsx @@ -64,7 +64,7 @@ export const UsersPageView: FC = ({ ) : undefined } > - Users + {Language.pageTitle} { beforeEach(() => { @@ -23,7 +23,7 @@ describe("WorkspacesPage", () => { render() // Then - await screen.findByText(Language.emptyCreateWorkspaceMessage) + await screen.findByText(WorkspacesTableBodyLanguage.emptyCreateWorkspaceMessage) }) it("renders a filled workspaces page", async () => { diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index 90e5fb80f1b2d..fda18b62e9fb3 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -32,7 +32,7 @@ const WorkspacesPage: FC = () => { { setSearchParams({ filter: query }) diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx index 5443c1ef7e8ff..05547cf582989 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx @@ -1,22 +1,6 @@ -import Button from "@material-ui/core/Button" import Link from "@material-ui/core/Link" -import { fade, 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 AddCircleOutline from "@material-ui/icons/AddCircleOutline" -import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight" -import RefreshIcon from "@material-ui/icons/Refresh" -import useTheme from "@material-ui/styles/useTheme" -import { useActor } from "@xstate/react" -import dayjs from "dayjs" -import relativeTime from "dayjs/plugin/relativeTime" import { FC } from "react" -import { Link as RouterLink, useNavigate } from "react-router-dom" -import { AvatarData } from "../../components/AvatarData/AvatarData" -import { EmptyState } from "../../components/EmptyState/EmptyState" +import { Link as RouterLink } from "react-router-dom" import { Margins } from "../../components/Margins/Margins" import { PageHeader, @@ -25,140 +9,28 @@ import { } from "../../components/PageHeader/PageHeader" import { SearchBarWithFilter } from "../../components/SearchBarWithFilter/SearchBarWithFilter" import { Stack } from "../../components/Stack/Stack" -import { TableCellLink } from "../../components/TableCellLink/TableCellLink" -import { TableLoader } from "../../components/TableLoader/TableLoader" -import { - HelpTooltip, - HelpTooltipAction, - HelpTooltipLink, - HelpTooltipLinksGroup, - HelpTooltipText, - HelpTooltipTitle, -} from "../../components/Tooltips/HelpTooltip/HelpTooltip" +import { WorkspaceHelpTooltip } from "../../components/Tooltips" +import { WorkspacesTable } from "../../components/WorkspacesTable/WorkspacesTable" import { workspaceFilterQuery } from "../../util/filters" -import { getDisplayStatus } from "../../util/workspace" import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService" -dayjs.extend(relativeTime) - export const Language = { - createFromTemplateButton: "Create from template", - emptyCreateWorkspaceMessage: "Create your first workspace", - emptyCreateWorkspaceDescription: "Start editing your source code and building your software", - emptyResultsMessage: "No results matched your search", + pageTitle: "Workspaces", yourWorkspacesButton: "Your workspaces", allWorkspacesButton: "All workspaces", - workspaceTooltipTitle: "What is a workspace?", - workspaceTooltipText: - "A workspace is your development environment in the cloud. It includes the infrastructure and tools you need to work on your project.", - workspaceTooltipLink1: "Create workspaces", - workspaceTooltipLink2: "Connect with SSH", - workspaceTooltipLink3: "Editors and IDEs", - outdatedLabel: "Outdated", - upToDateLabel: "Up to date", - versionTooltipText: "This workspace version is outdated and a newer version is available.", - updateVersionLabel: "Update version", -} - -const WorkspaceHelpTooltip: React.FC = () => { - return ( - - {Language.workspaceTooltipTitle} - {Language.workspaceTooltipText} - - - {Language.workspaceTooltipLink1} - - - {Language.workspaceTooltipLink2} - - - {Language.workspaceTooltipLink3} - - - - ) -} - -const OutdatedHelpTooltip: React.FC<{ onUpdateVersion: () => void }> = ({ onUpdateVersion }) => { - return ( - - {Language.outdatedLabel} - {Language.versionTooltipText} - - - {Language.updateVersionLabel} - - - - ) -} - -const WorkspaceRow: React.FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ workspaceRef }) => { - const styles = useStyles() - const navigate = useNavigate() - const theme: Theme = useTheme() - const [workspaceState, send] = useActor(workspaceRef) - const { data: workspace } = workspaceState.context - const status = getDisplayStatus(theme, workspace.latest_build) - const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}` - - return ( - { - if (event.key === "Enter") { - navigate(workspacePageLink) - } - }} - className={styles.clickableTableRow} - > - - - - {workspace.template_name} - - {workspace.outdated ? ( - - {Language.outdatedLabel} - { - send("UPDATE_VERSION") - }} - /> - - ) : ( - {Language.upToDateLabel} - )} - - - - {dayjs().to(dayjs(workspace.latest_build.created_at))} - - - - {status.status} - - -
- -
-
-
- ) + createANewWorkspace: `Create a new workspace from a `, + template: "Template", } export interface WorkspacesPageViewProps { - loading?: boolean + isLoading?: boolean workspaceRefs?: WorkspaceItemMachineRef[] filter?: string onFilter: (query: string) => void } export const WorkspacesPageView: FC = ({ - loading, + isLoading, workspaceRefs, filter, onFilter, @@ -173,15 +45,15 @@ export const WorkspacesPageView: FC = ({ - Workspaces + {Language.pageTitle} - Create a new workspace from a{" "} + {Language.createANewWorkspace} - Template + {Language.template} . @@ -189,97 +61,7 @@ export const WorkspacesPageView: FC = ({ - - - - Name - Template - Version - Last Built - Status - - - - - {!workspaceRefs && loading && } - {workspaceRefs && workspaceRefs.length === 0 && ( - <> - {filter === workspaceFilterQuery.me || filter === workspaceFilterQuery.all ? ( - - - - - - } - /> - - - ) : ( - - - - - - )} - - )} - {workspaceRefs && - workspaceRefs.map((workspaceRef) => ( - - ))} - -
+ ) } - -const useStyles = makeStyles((theme) => ({ - welcome: { - paddingTop: theme.spacing(12), - paddingBottom: theme.spacing(12), - display: "flex", - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - - "& span": { - maxWidth: 600, - textAlign: "center", - fontSize: theme.spacing(2), - lineHeight: `${theme.spacing(3)}px`, - }, - }, - clickableTableRow: { - "&:hover td": { - backgroundColor: fade(theme.palette.primary.light, 0.1), - }, - - "&:focus": { - outline: `1px solid ${theme.palette.secondary.dark}`, - }, - - "& .MuiTableCell-root:last-child": { - paddingRight: theme.spacing(2), - }, - }, - arrowRight: { - color: fade(theme.palette.primary.contrastText, 0.7), - width: 20, - height: 20, - }, - arrowCell: { - display: "flex", - }, - outdatedLabel: { - color: theme.palette.error.main, - display: "flex", - alignItems: "center", - gap: theme.spacing(0.5), - }, -})) From ae5a2f944c7678fa7cc6ad842c0631ece5cde92a Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 30 Jun 2022 16:29:31 -0400 Subject: [PATCH 2/2] Update site/src/components/WorkspacesTable/WorkspacesTableBody.tsx Co-authored-by: Joe Previte --- site/src/components/WorkspacesTable/WorkspacesTableBody.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/WorkspacesTable/WorkspacesTableBody.tsx b/site/src/components/WorkspacesTable/WorkspacesTableBody.tsx index 1a7f80f8af0fd..b71a6d3ba78b4 100644 --- a/site/src/components/WorkspacesTable/WorkspacesTableBody.tsx +++ b/site/src/components/WorkspacesTable/WorkspacesTableBody.tsx @@ -13,7 +13,7 @@ import { WorkspacesRow } from "./WorkspacesRow" export const Language = { emptyCreateWorkspaceMessage: "Create your first workspace", - emptyCreateWorkspaceDescription: "Start editing your source code and building your software", + emptyCreateWorkspaceDescription: "Start editing your source code and building your software.", createFromTemplateButton: "Create from template", emptyResultsMessage: "No results matched your search", }