Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ae59f16

Browse files
Kira-Pilotjsjoeio
andauthored
chore: cleaning up workspaces table code (coder#2765)
* cleaning up workspace table code * Update site/src/components/WorkspacesTable/WorkspacesTableBody.tsx Co-authored-by: Joe Previte <[email protected]> Co-authored-by: Joe Previte <[email protected]>
1 parent 29be359 commit ae59f16

File tree

12 files changed

+310
-235
lines changed

12 files changed

+310
-235
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./HelpTooltip"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import RefreshIcon from "@material-ui/icons/Refresh"
2+
import { FC } from "react"
3+
import {
4+
HelpTooltip,
5+
HelpTooltipAction,
6+
HelpTooltipLinksGroup,
7+
HelpTooltipText,
8+
HelpTooltipTitle,
9+
} from "./HelpTooltip"
10+
11+
const Language = {
12+
outdatedLabel: "Outdated",
13+
versionTooltipText: "This workspace version is outdated and a newer version is available.",
14+
updateVersionLabel: "Update version",
15+
}
16+
17+
interface TooltipProps {
18+
onUpdateVersion: () => void
19+
}
20+
21+
export const OutdatedHelpTooltip: FC<TooltipProps> = ({ onUpdateVersion }) => {
22+
return (
23+
<HelpTooltip size="small">
24+
<HelpTooltipTitle>{Language.outdatedLabel}</HelpTooltipTitle>
25+
<HelpTooltipText>{Language.versionTooltipText}</HelpTooltipText>
26+
<HelpTooltipLinksGroup>
27+
<HelpTooltipAction icon={RefreshIcon} onClick={onUpdateVersion}>
28+
{Language.updateVersionLabel}
29+
</HelpTooltipAction>
30+
</HelpTooltipLinksGroup>
31+
</HelpTooltip>
32+
)
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { FC } from "react"
2+
import {
3+
HelpTooltip,
4+
HelpTooltipLink,
5+
HelpTooltipLinksGroup,
6+
HelpTooltipText,
7+
HelpTooltipTitle,
8+
} from "./HelpTooltip"
9+
10+
const Language = {
11+
workspaceTooltipTitle: "What is a workspace?",
12+
workspaceTooltipText:
13+
"A workspace is your development environment in the cloud. It includes the infrastructure and tools you need to work on your project.",
14+
workspaceTooltipLink1: "Create workspaces",
15+
workspaceTooltipLink2: "Connect with SSH",
16+
workspaceTooltipLink3: "Editors and IDEs",
17+
}
18+
19+
export const WorkspaceHelpTooltip: FC = () => {
20+
return (
21+
<HelpTooltip>
22+
<HelpTooltipTitle>{Language.workspaceTooltipTitle}</HelpTooltipTitle>
23+
<HelpTooltipText>{Language.workspaceTooltipText}</HelpTooltipText>
24+
<HelpTooltipLinksGroup>
25+
<HelpTooltipLink href="https://github.com/coder/coder/blob/main/docs/workspaces.md#create-workspaces">
26+
{Language.workspaceTooltipLink1}
27+
</HelpTooltipLink>
28+
<HelpTooltipLink href="https://github.com/coder/coder/blob/main/docs/workspaces.md#connect-with-ssh">
29+
{Language.workspaceTooltipLink2}
30+
</HelpTooltipLink>
31+
<HelpTooltipLink href="https://github.com/coder/coder/blob/main/docs/workspaces.md#editors-and-ides">
32+
{Language.workspaceTooltipLink3}
33+
</HelpTooltipLink>
34+
</HelpTooltipLinksGroup>
35+
</HelpTooltip>
36+
)
37+
}

site/src/components/Tooltips/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { AgentHelpTooltip } from "./AgentHelpTooltip"
2+
export { OutdatedHelpTooltip } from "./OutdatedHelpTooltip"
3+
export { ResourcesHelpTooltip } from "./ResourcesHelpTooltip"
4+
export { WorkspaceHelpTooltip } from "./WorkspaceHelpTooltip"

site/src/components/UsersTable/UsersTableBody.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const UsersTableBody: FC<UsersTableBodyProps> = ({
4747
return <TableLoader />
4848
}
4949

50-
if (!users || users.length === 0) {
50+
if (!users || !users.length) {
5151
return (
5252
<TableRow>
5353
<TableCell colSpan={999}>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { fade, makeStyles, Theme } from "@material-ui/core/styles"
2+
import TableRow from "@material-ui/core/TableRow"
3+
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
4+
import useTheme from "@material-ui/styles/useTheme"
5+
import { useActor } from "@xstate/react"
6+
import dayjs from "dayjs"
7+
import relativeTime from "dayjs/plugin/relativeTime"
8+
import { FC } from "react"
9+
import { useNavigate } from "react-router-dom"
10+
import { getDisplayStatus } from "../../util/workspace"
11+
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
12+
import { AvatarData } from "../AvatarData/AvatarData"
13+
import { TableCellLink } from "../TableCellLink/TableCellLink"
14+
import { OutdatedHelpTooltip } from "../Tooltips"
15+
16+
dayjs.extend(relativeTime)
17+
18+
const Language = {
19+
upToDateLabel: "Up to date",
20+
outdatedLabel: "Outdated",
21+
}
22+
23+
export const WorkspacesRow: FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ workspaceRef }) => {
24+
const styles = useStyles()
25+
const navigate = useNavigate()
26+
const theme: Theme = useTheme()
27+
const [workspaceState, send] = useActor(workspaceRef)
28+
const { data: workspace } = workspaceState.context
29+
const status = getDisplayStatus(theme, workspace.latest_build)
30+
const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}`
31+
32+
return (
33+
<TableRow
34+
hover
35+
data-testid={`workspace-${workspace.id}`}
36+
tabIndex={0}
37+
onKeyDown={(event) => {
38+
if (event.key === "Enter") {
39+
navigate(workspacePageLink)
40+
}
41+
}}
42+
className={styles.clickableTableRow}
43+
>
44+
<TableCellLink to={workspacePageLink}>
45+
<AvatarData title={workspace.name} subtitle={workspace.owner_name} />
46+
</TableCellLink>
47+
<TableCellLink to={workspacePageLink}>{workspace.template_name}</TableCellLink>
48+
<TableCellLink to={workspacePageLink}>
49+
{workspace.outdated ? (
50+
<span className={styles.outdatedLabel}>
51+
{Language.outdatedLabel}
52+
<OutdatedHelpTooltip
53+
onUpdateVersion={() => {
54+
send("UPDATE_VERSION")
55+
}}
56+
/>
57+
</span>
58+
) : (
59+
<span style={{ color: theme.palette.text.secondary }}>{Language.upToDateLabel}</span>
60+
)}
61+
</TableCellLink>
62+
<TableCellLink to={workspacePageLink}>
63+
<span data-chromatic="ignore" style={{ color: theme.palette.text.secondary }}>
64+
{dayjs().to(dayjs(workspace.latest_build.created_at))}
65+
</span>
66+
</TableCellLink>
67+
<TableCellLink to={workspacePageLink}>
68+
<span style={{ color: status.color }}>{status.status}</span>
69+
</TableCellLink>
70+
<TableCellLink to={workspacePageLink}>
71+
<div className={styles.arrowCell}>
72+
<KeyboardArrowRight className={styles.arrowRight} />
73+
</div>
74+
</TableCellLink>
75+
</TableRow>
76+
)
77+
}
78+
79+
const useStyles = makeStyles((theme) => ({
80+
clickableTableRow: {
81+
"&:hover td": {
82+
backgroundColor: fade(theme.palette.primary.light, 0.1),
83+
},
84+
85+
"&:focus": {
86+
outline: `1px solid ${theme.palette.secondary.dark}`,
87+
},
88+
89+
"& .MuiTableCell-root:last-child": {
90+
paddingRight: theme.spacing(2),
91+
},
92+
},
93+
arrowRight: {
94+
color: fade(theme.palette.primary.contrastText, 0.7),
95+
width: 20,
96+
height: 20,
97+
},
98+
arrowCell: {
99+
display: "flex",
100+
},
101+
outdatedLabel: {
102+
color: theme.palette.error.main,
103+
display: "flex",
104+
alignItems: "center",
105+
gap: theme.spacing(0.5),
106+
},
107+
}))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Table from "@material-ui/core/Table"
2+
import TableBody from "@material-ui/core/TableBody"
3+
import TableCell from "@material-ui/core/TableCell"
4+
import TableHead from "@material-ui/core/TableHead"
5+
import TableRow from "@material-ui/core/TableRow"
6+
import { FC } from "react"
7+
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
8+
import { WorkspacesTableBody } from "./WorkspacesTableBody"
9+
10+
const Language = {
11+
name: "Name",
12+
template: "Template",
13+
version: "Version",
14+
lastBuilt: "Last Built",
15+
status: "Status",
16+
}
17+
18+
export interface WorkspacesTableProps {
19+
isLoading?: boolean
20+
workspaceRefs?: WorkspaceItemMachineRef[]
21+
filter?: string
22+
}
23+
24+
export const WorkspacesTable: FC<WorkspacesTableProps> = ({ isLoading, workspaceRefs, filter }) => {
25+
return (
26+
<Table>
27+
<TableHead>
28+
<TableRow>
29+
<TableCell width="35%">{Language.name}</TableCell>
30+
<TableCell width="15%">{Language.template}</TableCell>
31+
<TableCell width="15%">{Language.version}</TableCell>
32+
<TableCell width="20%">{Language.lastBuilt}</TableCell>
33+
<TableCell width="15%">{Language.status}</TableCell>
34+
<TableCell width="1%"></TableCell>
35+
</TableRow>
36+
</TableHead>
37+
<TableBody>
38+
<WorkspacesTableBody isLoading={isLoading} workspaceRefs={workspaceRefs} filter={filter} />
39+
</TableBody>
40+
</Table>
41+
)
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Button from "@material-ui/core/Button"
2+
import Link from "@material-ui/core/Link"
3+
import TableCell from "@material-ui/core/TableCell"
4+
import TableRow from "@material-ui/core/TableRow"
5+
import AddCircleOutline from "@material-ui/icons/AddCircleOutline"
6+
import { FC } from "react"
7+
import { Link as RouterLink } from "react-router-dom"
8+
import { workspaceFilterQuery } from "../../util/filters"
9+
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
10+
import { EmptyState } from "../EmptyState/EmptyState"
11+
import { TableLoader } from "../TableLoader/TableLoader"
12+
import { WorkspacesRow } from "./WorkspacesRow"
13+
14+
export const Language = {
15+
emptyCreateWorkspaceMessage: "Create your first workspace",
16+
emptyCreateWorkspaceDescription: "Start editing your source code and building your software.",
17+
createFromTemplateButton: "Create from template",
18+
emptyResultsMessage: "No results matched your search",
19+
}
20+
21+
interface TableBodyProps {
22+
isLoading?: boolean
23+
workspaceRefs?: WorkspaceItemMachineRef[]
24+
filter?: string
25+
}
26+
27+
export const WorkspacesTableBody: FC<TableBodyProps> = ({ isLoading, workspaceRefs, filter }) => {
28+
if (isLoading) {
29+
return <TableLoader />
30+
}
31+
32+
if (!workspaceRefs || !workspaceRefs.length) {
33+
return (
34+
<>
35+
{filter === workspaceFilterQuery.me || filter === workspaceFilterQuery.all ? (
36+
<TableRow>
37+
<TableCell colSpan={999}>
38+
<EmptyState
39+
message={Language.emptyCreateWorkspaceMessage}
40+
description={Language.emptyCreateWorkspaceDescription}
41+
cta={
42+
<Link underline="none" component={RouterLink} to="/templates">
43+
<Button startIcon={<AddCircleOutline />}>
44+
{Language.createFromTemplateButton}
45+
</Button>
46+
</Link>
47+
}
48+
/>
49+
</TableCell>
50+
</TableRow>
51+
) : (
52+
<TableRow>
53+
<TableCell colSpan={999}>
54+
<EmptyState message={Language.emptyResultsMessage} />
55+
</TableCell>
56+
</TableRow>
57+
)}
58+
</>
59+
)
60+
}
61+
62+
return (
63+
<>
64+
{workspaceRefs.map((workspaceRef) => (
65+
<WorkspacesRow workspaceRef={workspaceRef} key={workspaceRef.id} />
66+
))}
67+
</>
68+
)
69+
}

site/src/pages/UsersPage/UsersPageView.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export const UsersPageView: FC<UsersPageViewProps> = ({
6464
) : undefined
6565
}
6666
>
67-
<PageHeaderTitle>Users</PageHeaderTitle>
67+
<PageHeaderTitle>{Language.pageTitle}</PageHeaderTitle>
6868
</PageHeader>
6969

7070
<SearchBarWithFilter

site/src/pages/WorkspacesPage/WorkspacesPage.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { screen } from "@testing-library/react"
22
import { rest } from "msw"
3+
import { Language as WorkspacesTableBodyLanguage } from "../../components/WorkspacesTable/WorkspacesTableBody"
34
import { MockWorkspace } from "../../testHelpers/entities"
45
import { history, render } from "../../testHelpers/renderHelpers"
56
import { server } from "../../testHelpers/server"
67
import WorkspacesPage from "./WorkspacesPage"
7-
import { Language } from "./WorkspacesPageView"
88

99
describe("WorkspacesPage", () => {
1010
beforeEach(() => {
@@ -23,7 +23,7 @@ describe("WorkspacesPage", () => {
2323
render(<WorkspacesPage />)
2424

2525
// Then
26-
await screen.findByText(Language.emptyCreateWorkspaceMessage)
26+
await screen.findByText(WorkspacesTableBodyLanguage.emptyCreateWorkspaceMessage)
2727
})
2828

2929
it("renders a filled workspaces page", async () => {

site/src/pages/WorkspacesPage/WorkspacesPage.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const WorkspacesPage: FC = () => {
3232

3333
<WorkspacesPageView
3434
filter={workspacesState.context.filter}
35-
loading={workspacesState.hasTag("loading")}
35+
isLoading={workspacesState.hasTag("loading")}
3636
workspaceRefs={workspaceRefs}
3737
onFilter={(query) => {
3838
setSearchParams({ filter: query })

0 commit comments

Comments
 (0)