Skip to content

feat: Implement simple Project Summary page #71

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 5 commits into from
Jan 26, 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
Next Next commit
Implement simple projects page
  • Loading branch information
bryphe-coder committed Jan 26, 2022
commit 2e875aae9b5a53e21a9cb316476607e4d6bb368c
10 changes: 10 additions & 0 deletions site/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ export namespace Project {
}
}

// Must be kept in sync with backend Workspace struct
export interface Workspace {
id: string
created_at: string
updated_at: string
owner_id: string
project_id: string
name: string
}

export const login = async (email: string, password: string): Promise<LoginResponse> => {
const response = await fetch("/api/v2/login", {
method: "POST",
Expand Down
119 changes: 119 additions & 0 deletions site/pages/projects/[organization]/[project].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React from "react"
import Box from "@material-ui/core/Box"
import { makeStyles } from "@material-ui/core/styles"
import Paper from "@material-ui/core/Paper"
import Link from "next/link"
import { useRouter } from "next/router"
import useSWR from "swr"

import { Project, Workspace } from "../../../api"
import { Header } from "../../../components/Header"
import { FullScreenLoader } from "../../../components/Loader/FullScreenLoader"
import { Navbar } from "../../../components/Navbar"
import { Footer } from "../../../components/Page"
import { Column, Table } from "../../../components/Table"
import { useUser } from "../../../contexts/UserContext"
import { ErrorSummary } from "../../../components/ErrorSummary"
import { firstOrItem } from "../../../util/array"
import { EmptyState } from "../../../components/EmptyState"

import { MockWorkspace } from "../../../test_helpers"

const ProjectPage: React.FC = () => {
const styles = useStyles()
const { me, signOut } = useUser(true)

const router = useRouter()
const { project, organization } = router.query

const { data: projectInfo, error: projectError } = useSWR<Project, Error>(
() => `/api/v2/projects/${organization}/${project}`,
)
let { data: workspaces, error: workspacesError } = useSWR<Workspace[], Error>(
() => `/api/v2/projects/${organization}/${project}/workspaces`,
)

workspaces = [MockWorkspace]

if (projectError) {
return <ErrorSummary error={projectError} />
}

if (workspacesError) {
return <ErrorSummary error={workspacesError} />
}

if (!me || !projectInfo || !workspaces) {
return <FullScreenLoader />
}

const createWorkspace = () => {
void router.push(`/projects/${organization}/${project}/create`)
}

const emptyState = (
<EmptyState
button={{
children: "Create Workspace",
onClick: createWorkspace,
}}
message="No workspaces have been created yet"
description="Create a workspace to get started"
/>
)

const columns: Column<Workspace>[] = [
{
key: "name",
name: "Name",
renderer: (nameField: string, data: Workspace) => {
return <Link href={`/projects/${organization}/${project}/workspaces/${data.id}`}>{nameField}</Link>
},
},
]

const tableProps = {
title: "Workspaces",
columns,
data: workspaces,
emptyState: emptyState,
}

return (
<div className={styles.root}>
<Navbar user={me} onSignOut={signOut} />
<Header
title={firstOrItem(project)}
description={firstOrItem(organization)}
subTitle={`${workspaces.length} workspaces`}
action={{
text: "Create Workspace",
onClick: createWorkspace,
}}
/>

<Paper style={{ maxWidth: "1380px", margin: "1em auto", width: "100%" }}>
<Table {...tableProps} />
</Paper>
<Footer />
</div>
)
}

const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
flexDirection: "column",
},
header: {
display: "flex",
flexDirection: "row-reverse",
justifyContent: "space-between",
margin: "1em auto",
maxWidth: "1380px",
padding: theme.spacing(2, 6.25, 0),
width: "100%",
},
}))

export default ProjectPage
10 changes: 9 additions & 1 deletion site/test_helpers/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { User } from "../contexts/UserContext"
import { Provisioner, Organization, Project } from "../api"
import { Provisioner, Organization, Project, Workspace } from "../api"

export const MockUser: User = {
id: "test-user-id",
Expand Down Expand Up @@ -29,3 +29,11 @@ export const MockOrganization: Organization = {
created_at: "",
updated_at: "",
}

export const MockWorkspace: Workspace = {
id: "test-workspace",
name: "Test-Workspace",
created_at: "",
updated_at: "",
project_id: "project-id",
}
7 changes: 7 additions & 0 deletions site/util/array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const firstOrItem = <T>(itemOrItems: T | T[]): T | null => {
if (Array.isArray(itemOrItems)) {
return itemOrItems.length > 0 ? itemOrItems[0] : null
}

return itemOrItems
}