Skip to content

refactor: Improve the load state for the list pages #1428

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 10 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add workspaces table
  • Loading branch information
BrunoQuaresma committed May 13, 2022
commit b3bdc36ae3ade14efd0d37715c99cd984357e12a
5 changes: 3 additions & 2 deletions site/src/components/NavbarView/NavbarView.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Story } from "@storybook/react"
import React from "react"
import { MockUser, MockUser2 } from "../../testHelpers/entities"
import { NavbarView, NavbarViewProps } from "./NavbarView"

export default {
Expand All @@ -14,15 +15,15 @@ const Template: Story<NavbarViewProps> = (args: NavbarViewProps) => <NavbarView

export const ForAdmin = Template.bind({})
ForAdmin.args = {
user: { id: "1", username: "Administrator", email: "admin@coder.com", created_at: "dawn" },
user: MockUser,
onSignOut: () => {
return Promise.resolve()
},
}

export const ForMember = Template.bind({})
ForMember.args = {
user: { id: "1", username: "CathyCoder", email: "cathy@coder.com", created_at: "dawn" },
user: MockUser2,
onSignOut: () => {
return Promise.resolve()
},
Expand Down
29 changes: 29 additions & 0 deletions site/src/components/WorkspacesTable/WorkspacesTable.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ComponentMeta, Story } from "@storybook/react"
import React from "react"
import { MockTemplate, MockWorkspace } from "../../testHelpers/entities"
import { WorkspacesTable, WorkspacesTableProps } from "./WorkspacesTable"

export default {
title: "components/WorkspacesTable",
component: WorkspacesTable,
} as ComponentMeta<typeof WorkspacesTable>

const Template: Story<WorkspacesTableProps> = (args) => <WorkspacesTable {...args} />

export const Example = Template.bind({})
Example.args = {
templateInfo: MockTemplate,
workspaces: [MockWorkspace],
onCreateWorkspace: () => {
console.info("Create workspace")
},
}

export const Empty = Template.bind({})
Empty.args = {
templateInfo: MockTemplate,
workspaces: [],
onCreateWorkspace: () => {
console.info("Create workspace")
},
}
72 changes: 72 additions & 0 deletions site/src/components/WorkspacesTable/WorkspacesTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Box from "@material-ui/core/Box"
import Button from "@material-ui/core/Button"
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 React from "react"
import { Link } from "react-router-dom"
import * as TypesGen from "../../api/typesGenerated"
import { EmptyState } from "../EmptyState/EmptyState"
import { TableHeaderRow } from "../TableHeaders/TableHeaders"
import { TableLoader } from "../TableLoader/TableLoader"
import { TableTitle } from "../TableTitle/TableTitle"

export const Language = {
title: "Workspaces",
nameLabel: "Name",
emptyMessage: "No workspaces have been created yet",
emptyDescription: "Create a workspace to get started",
ctaAction: "Create workspace",
}

export interface WorkspacesTableProps {
templateInfo?: TypesGen.Template
workspaces?: TypesGen.Workspace[]
onCreateWorkspace: () => void
}

export const WorkspacesTable: React.FC<WorkspacesTableProps> = ({ templateInfo, workspaces, onCreateWorkspace }) => {
const isLoading = !templateInfo || !workspaces

return (
<Table>
<TableHead>
<TableTitle title={Language.title} />
<TableHeaderRow>
<TableCell size="small">{Language.nameLabel}</TableCell>
</TableHeaderRow>
</TableHead>
<TableBody>
{isLoading && <TableLoader />}
{workspaces &&
workspaces.map((w) => (
<TableRow key={w.id}>
<TableCell>
<Link to={`/workspaces/${w.id}`}>{w.name}</Link>
</TableCell>
</TableRow>
))}

{workspaces && workspaces.length === 0 && (
<TableRow>
<TableCell colSpan={999}>
<Box p={4}>
<EmptyState
message={Language.emptyMessage}
description={Language.emptyDescription}
cta={
<Button variant="contained" color="primary" onClick={onCreateWorkspace}>
{Language.ctaAction}
</Button>
}
/>
</Box>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
)
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
import Box from "@material-ui/core/Box"
import Button from "@material-ui/core/Button"
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 React from "react"
import { Link, useNavigate, useParams } from "react-router-dom"
import { useNavigate, useParams } from "react-router-dom"
import useSWR from "swr"
import * as TypesGen from "../../../../api/typesGenerated"
import { EmptyState } from "../../../../components/EmptyState/EmptyState"
import { ErrorSummary } from "../../../../components/ErrorSummary/ErrorSummary"
import { Header } from "../../../../components/Header/Header"
import { Margins } from "../../../../components/Margins/Margins"
import { Stack } from "../../../../components/Stack/Stack"
import { TableHeaderRow } from "../../../../components/TableHeaders/TableHeaders"
import { TableLoader } from "../../../../components/TableLoader/TableLoader"
import { TableTitle } from "../../../../components/TableTitle/TableTitle"
import { WorkspacesTable } from "../../../../components/WorkspacesTable/WorkspacesTable"
import { unsafeSWRArgument } from "../../../../util"
import { firstOrItem } from "../../../../util/array"

export const Language = {
tableTitle: "Workspaces",
nameLabel: "Name",
emptyMessage: "No workspaces have been created yet",
emptyDescription: "Create a workspace to get started",
totalLabel: "total",
ctaAction: "Create workspace",
subtitlePosfix: "workspaces",
subtitle: "workspaces",
}

export const TemplatePage: React.FC = () => {
Expand All @@ -44,13 +28,11 @@ export const TemplatePage: React.FC = () => {

// This just grabs all workspaces... and then later filters them to match the
// current template.

const { data: workspaces, error: workspacesError } = useSWR<TypesGen.Workspace[], Error>(
() => `/api/v2/organizations/${unsafeSWRArgument(organizationInfo).id}/workspaces`,
)

const hasError = organizationError || templateError || workspacesError
const isLoading = !templateInfo || !workspaces

const createWorkspace = () => {
navigate(`/templates/${organizationName}/${templateName}/create`)
Expand All @@ -68,7 +50,7 @@ export const TemplatePage: React.FC = () => {
<Header
title={firstOrItem(templateName, "")}
description={firstOrItem(organizationName, "")}
subTitle={perTemplateWorkspaces ? `${perTemplateWorkspaces.length} ${Language.subtitlePosfix}` : ""}
subTitle={perTemplateWorkspaces ? `${perTemplateWorkspaces.length} ${Language.subtitle}` : ""}
action={{
text: "Create Workspace",
onClick: createWorkspace,
Expand All @@ -80,43 +62,7 @@ export const TemplatePage: React.FC = () => {
{templateError && <ErrorSummary error={templateError} />}
{workspacesError && <ErrorSummary error={workspacesError} />}
{!hasError && (
<Table>
<TableHead>
<TableTitle title={Language.tableTitle} />
<TableHeaderRow>
<TableCell size="small">{Language.nameLabel}</TableCell>
</TableHeaderRow>
</TableHead>
<TableBody>
{isLoading && <TableLoader />}
{workspaces &&
workspaces.map((w) => (
<TableRow key={w.id}>
<TableCell>
<Link to={`/workspaces/${w.id}`}>{w.name}</Link>
</TableCell>
</TableRow>
))}

{workspaces && workspaces.length === 0 && (
<TableRow>
<TableCell colSpan={999}>
<Box p={4}>
<EmptyState
message={Language.emptyMessage}
description={Language.emptyDescription}
cta={
<Button variant="contained" color="primary" onClick={createWorkspace}>
{Language.ctaAction}
</Button>
}
/>
</Box>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<WorkspacesTable templateInfo={templateInfo} workspaces={workspaces} onCreateWorkspace={createWorkspace} />
)}
</Margins>
</Stack>
Expand Down