Skip to content

Commit

Permalink
fix: Remove 'Create Project' button, replace with CLI prompt (#245)
Browse files Browse the repository at this point in the history
For black-triangle and alpha builds, we won't be able to create projects in the UI, because they require collecting and tar'ing a set of assets associated with the project - so the CLI is going to be our entry point for creating projects.

This shifts the UI to remove the 'Create Project' button, and adds a prompt to copy a command to run.

__Before:__
<img width="1134" alt="image" src="https://user-images.githubusercontent.com/88213859/153534269-58dc95bd-0417-4bed-8e62-e2b6f479da61.png">

__After:__
![2022-02-10 19 38 01](https://user-images.githubusercontent.com/88213859/153534227-d22bd786-8c43-4858-bda6-3d9d1d614711.gif)
  • Loading branch information
bryphe-coder committed Feb 11, 2022
1 parent c0d547b commit df13fef
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 27 deletions.
2 changes: 0 additions & 2 deletions site/.eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ rules:
message:
"Use path imports to avoid pulling in unused modules. See:
https://material-ui.com/guides/minimizing-bundle-size/"
- name: "@material-ui/core/Tooltip"
message: "Use the custom Tooltip on componens/Tooltip"
no-storage/no-browser-storage: error
no-unused-vars: "off"
"object-curly-spacing": "off"
Expand Down
68 changes: 68 additions & 0 deletions site/components/Button/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { makeStyles } from "@material-ui/core/styles"
import Button from "@material-ui/core/Button"
import Tooltip from "@material-ui/core/Tooltip"
import Check from "@material-ui/icons/Check"
import React, { useState } from "react"
import { FileCopy } from "../Icons"

interface CopyButtonProps {
text: string
className?: string
}

/**
* Copy button used inside the CodeBlock component internally
*/
export const CopyButton: React.FC<CopyButtonProps> = ({ className = "", text }) => {
const styles = useStyles()
const [isCopied, setIsCopied] = useState<boolean>(false)

const copyToClipboard = async (): Promise<void> => {
try {
await window.navigator.clipboard.writeText(text)
setIsCopied(true)

window.setTimeout(() => {
setIsCopied(false)
}, 1000)
} catch (err) {
const wrappedErr = new Error("copyToClipboard: failed to copy text to clipboard")
if (err instanceof Error) {
wrappedErr.stack = err.stack
}
console.error(wrappedErr)
}
}

return (
<Tooltip title="Copy to Clipboard" placement="top">
<div className={`${styles.copyButtonWrapper} ${className}`}>
<Button className={styles.copyButton} onClick={copyToClipboard} size="small">
{isCopied ? <Check className={styles.fileCopyIcon} /> : <FileCopy className={styles.fileCopyIcon} />}
</Button>
</div>
</Tooltip>
)
}

const useStyles = makeStyles((theme) => ({
copyButtonWrapper: {
display: "flex",
marginLeft: theme.spacing(1),
},
copyButton: {
borderRadius: 7,
background: theme.palette.codeBlock.button.main,
color: theme.palette.codeBlock.button.contrastText,
padding: theme.spacing(0.85),
minWidth: 32,

"&:hover": {
background: theme.palette.codeBlock.button.hover,
},
},
fileCopyIcon: {
width: 20,
height: 20,
},
}))
1 change: 1 addition & 0 deletions site/components/Button/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./SplitButton"
export * from "./LoadingButton"
export * from "./CopyButton"
2 changes: 1 addition & 1 deletion site/components/CodeBlock/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default {
title: "CodeBlock",
component: CodeBlock,
argTypes: {
lines: { control: "object", defaultValue: sampleLines },
lines: { control: "text", defaultValue: sampleLines },
},
}

Expand Down
3 changes: 1 addition & 2 deletions site/components/CodeBlock/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { makeStyles } from "@material-ui/core/styles"
import React from "react"
import { MONOSPACE_FONT_FAMILY } from "../../theme/constants"

export interface CodeBlockProps {
lines: string[]
Expand All @@ -18,8 +19,6 @@ export const CodeBlock: React.FC<CodeBlockProps> = ({ lines }) => {
</div>
)
}
const MONOSPACE_FONT_FAMILY =
"'Fira Code', 'Lucida Console', 'Lucida Sans Typewriter', 'Liberation Mono', 'Monaco', 'Courier New', Courier, monospace"

const useStyles = makeStyles((theme) => ({
root: {
Expand Down
20 changes: 20 additions & 0 deletions site/components/CodeExample/CodeExample.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Story } from "@storybook/react"
import React from "react"
import { CodeExample, CodeExampleProps } from "./CodeExample"

const sampleCode = `echo "Hello, world"`

export default {
title: "CodeExample",
component: CodeExample,
argTypes: {
code: { control: "string", defaultValue: sampleCode },
},
}

const Template: Story<CodeExampleProps> = (args: CodeExampleProps) => <CodeExample {...args} />

export const Example = Template.bind({})
Example.args = {
code: sampleCode,
}
15 changes: 15 additions & 0 deletions site/components/CodeExample/CodeExample.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { screen } from "@testing-library/react"
import { render } from "../../test_helpers"
import React from "react"
import { CodeExample } from "./CodeExample"

describe("CodeExample", () => {
it("renders code", async () => {
// When
render(<CodeExample code="echo hello" />)

// Then
// Both lines should be rendered
await screen.findByText("echo hello")
})
})
38 changes: 38 additions & 0 deletions site/components/CodeExample/CodeExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { makeStyles } from "@material-ui/core/styles"
import React from "react"
import { MONOSPACE_FONT_FAMILY } from "../../theme/constants"

import { CopyButton } from "../Button"

export interface CodeExampleProps {
code: string
}

/**
* Component to show single-line code examples, with a copy button
*/
export const CodeExample: React.FC<CodeExampleProps> = ({ code }) => {
const styles = useStyles()

return (
<div className={styles.root}>
<code>{code}</code>
<CopyButton text={code} />
</div>
)
}

const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
background: theme.palette.background.default,
color: theme.palette.codeBlock.contrastText,
fontFamily: MONOSPACE_FONT_FAMILY,
fontSize: 13,
padding: theme.spacing(2),
borderRadius: theme.shape.borderRadius,
},
}))
1 change: 1 addition & 0 deletions site/components/CodeExample/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./CodeExample"
11 changes: 11 additions & 0 deletions site/components/Icons/FileCopy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import SvgIcon from "@material-ui/core/SvgIcon"
import React from "react"

export const FileCopy: typeof SvgIcon = (props) => (
<SvgIcon {...props} viewBox="0 0 20 20">
<path
d="M12.7412 2.2807H4.32014C3.5447 2.2807 2.91663 2.90877 2.91663 3.68421V13.5088H4.32014V3.68421H12.7412V2.2807ZM14.8465 5.08772H7.12716C6.35172 5.08772 5.72365 5.71579 5.72365 6.49123V16.3158C5.72365 17.0912 6.35172 17.7193 7.12716 17.7193H14.8465C15.6219 17.7193 16.25 17.0912 16.25 16.3158V6.49123C16.25 5.71579 15.6219 5.08772 14.8465 5.08772ZM14.8465 16.3158H7.12716V6.49123H14.8465V16.3158Z"
fill="currentColor"
/>
</SvgIcon>
)
1 change: 1 addition & 0 deletions site/components/Icons/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { CoderIcon } from "./CoderIcon"
export * from "./FileCopy"
export { Logo } from "./Logo"
export * from "./Logout"
export { WorkspacesIcon } from "./WorkspacesIcon"
35 changes: 13 additions & 22 deletions site/pages/projects/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from "react"
import { makeStyles } from "@material-ui/core/styles"
import Paper from "@material-ui/core/Paper"
import { useRouter } from "next/router"
import Link from "next/link"
import { EmptyState } from "../../components"
import { ErrorSummary } from "../../components/ErrorSummary"
Expand All @@ -14,10 +13,10 @@ import { FullScreenLoader } from "../../components/Loader/FullScreenLoader"

import { Organization, Project } from "./../../api"
import useSWR from "swr"
import { CodeExample } from "../../components/CodeExample/CodeExample"

const ProjectsPage: React.FC = () => {
const styles = useStyles()
const router = useRouter()
const { me, signOut } = useUser(true)
const { data: projects, error } = useSWR<Project[] | null, Error>("/api/v2/projects")
const { data: orgs, error: orgsError } = useSWR<Organization[], Error>("/api/v2/users/me/organizations")
Expand All @@ -34,15 +33,6 @@ const ProjectsPage: React.FC = () => {
return <FullScreenLoader />
}

const createProject = () => {
void router.push("/projects/create")
}

const action = {
text: "Create Project",
onClick: createProject,
}

// Create a dictionary of organization ID -> organization Name
// Needed to properly construct links to dive into a project
const orgDictionary = orgs.reduce((acc: Record<string, string>, curr: Organization) => {
Expand All @@ -62,17 +52,15 @@ const ProjectsPage: React.FC = () => {
},
]

const emptyState = (
<EmptyState
button={{
children: "Create Project",
onClick: createProject,
}}
message="No projects have been created yet"
description="Create a project to get started."
/>
const description = (
<div>
<div className={styles.descriptionLabel}>Run the following command to get started:</div>
<CodeExample code="coder project create" />
</div>
)

const emptyState = <EmptyState message="No projects have been created yet" description={description} />

const tableProps = {
title: "All Projects",
columns: columns,
Expand All @@ -85,7 +73,7 @@ const ProjectsPage: React.FC = () => {
return (
<div className={styles.root}>
<Navbar user={me} onSignOut={signOut} />
<Header title="Projects" subTitle={subTitle} action={action} />
<Header title="Projects" subTitle={subTitle} />
<Paper style={{ maxWidth: "1380px", margin: "1em auto", width: "100%" }}>
<Table {...tableProps} />
</Paper>
Expand All @@ -94,11 +82,14 @@ const ProjectsPage: React.FC = () => {
)
}

const useStyles = makeStyles(() => ({
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
flexDirection: "column",
},
descriptionLabel: {
marginBottom: theme.spacing(1),
},
}))

export default ProjectsPage
23 changes: 23 additions & 0 deletions site/theme/palettes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ declare module "@material-ui/core/styles/createPalette" {
contrastText: string
// Background color for codeblocks
main: string
button: {
// Background for buttons inside a codeblock
main: string
// Hover background color for buttons inside a codeblock
hover: string
// Text color for buttons inside a codeblock
contrastText: string
}
}
navbar: {
main: string
Expand All @@ -26,6 +34,11 @@ declare module "@material-ui/core/styles/createPalette" {
codeBlock: {
contrastText: string
main: string
button: {
main: string
hover: string
contrastText: string
}
}
navbar: {
main: string
Expand Down Expand Up @@ -71,6 +84,11 @@ export const lightPalette: CustomPalette = {
codeBlock: {
main: "#F3F3F3",
contrastText: "rgba(0, 0, 0, 0.9)",
button: {
main: "#E6ECE6",
hover: "#DAEBDA",
contrastText: "#000",
},
},
primary: {
main: "#519A54",
Expand Down Expand Up @@ -135,6 +153,11 @@ export const darkPalette: CustomPalette = {
codeBlock: {
main: "rgb(24, 26, 27)",
contrastText: "rgba(255, 255, 255, 0.8)",
button: {
main: "rgba(255, 255, 255, 0.1)",
hover: "rgba(255, 255, 255, 0.25)",
contrastText: "#FFF",
},
},
hero: {
main: "#141414",
Expand Down

0 comments on commit df13fef

Please sign in to comment.