Skip to content
Merged
23 changes: 12 additions & 11 deletions site/src/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import React from "react"
import { Route, Routes } from "react-router-dom"
import { Navigate, Route, Routes } from "react-router-dom"
import { AuthAndNav, RequireAuth } from "./components"
import { PreferencesLayout } from "./components/Preferences/Layout"
import { IndexPage } from "./pages"
import { NotFoundPage } from "./pages/404"
import { CliAuthenticationPage } from "./pages/cli-auth"
import { HealthzPage } from "./pages/healthz"
import { SignInPage } from "./pages/login"
import { PreferencesPage } from "./pages/preferences"
import { PreferencesAccountPage } from "./pages/preferences/account"
import { PreferencesLinkedAccountsPage } from "./pages/preferences/linked-accounts"
import { PreferencesSecurityPage } from "./pages/preferences/security"
import { PreferencesSSHKeysPage } from "./pages/preferences/ssh-keys"
import { TemplatesPage } from "./pages/templates"
import { TemplatePage } from "./pages/templates/[organization]/[template]"
import { CreateWorkspacePage } from "./pages/templates/[organization]/[template]/create"
Expand Down Expand Up @@ -68,15 +72,12 @@ export const AppRouter: React.FC = () => (
/>
</Route>

<Route path="preferences">
<Route
index
element={
<AuthAndNav>
<PreferencesPage />
</AuthAndNav>
}
/>
<Route path="preferences" element={<PreferencesLayout />}>
<Route index element={<Navigate to="account" />} />
<Route path="account" element={<PreferencesAccountPage />} />
<Route path="security" element={<PreferencesSecurityPage />} />
<Route path="ssh-keys" element={<PreferencesSSHKeysPage />} />
<Route path="linked-accounts" element={<PreferencesLinkedAccountsPage />} />
</Route>

{/* Using path="*"" means "match anything", so this route
Expand Down
21 changes: 0 additions & 21 deletions site/src/components/Panel/Panel.stories.tsx

This file was deleted.

34 changes: 34 additions & 0 deletions site/src/components/Preferences/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Box from "@material-ui/core/Box"
import React from "react"
import { Outlet } from "react-router-dom"
import { AuthAndNav } from "../Page"
import { TabPanel } from "../TabPanel"

export const Language = {
accountLabel: "Account",
securityLabel: "Security",
sshKeysLabel: "SSH Keys",
linkedAccountsLabel: "Linked Accounts",
preferencesLabel: "Preferences",
}

const menuItems = [
{ label: Language.accountLabel, path: "/preferences/account" },
{ label: Language.securityLabel, path: "/preferences/security" },
{ label: Language.sshKeysLabel, path: "/preferences/ssh-keys" },
{ label: Language.linkedAccountsLabel, path: "/preferences/linked-accounts" },
]

export const PreferencesLayout: React.FC = () => {
return (
<AuthAndNav>
<Box display="flex" flexDirection="column">
<Box style={{ maxWidth: "1380px", margin: "1em auto" }}>
<TabPanel title={Language.preferencesLabel} menuItems={menuItems}>
<Outlet />
</TabPanel>
</Box>
</Box>
</AuthAndNav>
)
}
20 changes: 0 additions & 20 deletions site/src/components/Sidebar/Sidebar.stories.tsx

This file was deleted.

20 changes: 20 additions & 0 deletions site/src/components/TabPanel/TabPanel.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 { TabPanel, TabPanelProps } from "."

export default {
title: "TabPanel/TabPanel",
component: TabPanel,
}

const Template: Story<TabPanelProps> = (args: TabPanelProps) => <TabPanel {...args} />

export const Example = Template.bind({})
Example.args = {
title: "Title",
menuItems: [
{ label: "OAuth Settings", path: "oauthSettings" },
{ label: "Security", path: "oauthSettings", hasChanges: true },
{ label: "Hardware", path: "oauthSettings" },
],
}
19 changes: 19 additions & 0 deletions site/src/components/TabPanel/TabSidebar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Story } from "@storybook/react"
import React from "react"
import { TabSidebar, TabSidebarProps } from "./TabSidebar"

export default {
title: "TabPanel/TabSidebar",
component: TabSidebar,
}

const Template: Story<TabSidebarProps> = (args: TabSidebarProps) => <TabSidebar {...args} />

export const Example = Template.bind({})
Example.args = {
menuItems: [
{ label: "OAuth Settings", path: "oauthSettings" },
{ label: "Security", path: "security", hasChanges: true },
{ label: "Hardware", path: "hardware" },
],
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,35 @@ import List from "@material-ui/core/List"
import ListItem from "@material-ui/core/ListItem"
import { makeStyles } from "@material-ui/core/styles"
import React from "react"
import { NavLink } from "react-router-dom"
import { combineClasses } from "../../util/combine-classes"

export interface SidebarItem {
value: string
export interface TabSidebarItem {
path: string
label: string
hasChanges?: boolean
}

export interface SidebarProps {
menuItems: SidebarItem[]
activeItem?: string
onSelect?: (value: string) => void
export interface TabSidebarProps {
menuItems: TabSidebarItem[]
}

export const Sidebar: React.FC<SidebarProps> = ({ menuItems, activeItem, onSelect }) => {
export const TabSidebar: React.FC<TabSidebarProps> = ({ menuItems }) => {
const styles = useStyles()

return (
<List className={styles.menu}>
{menuItems.map(({ hasChanges, ...tab }) => {
const isActive = activeItem === tab.value
return (
<ListItem
key={tab.value}
button
onClick={onSelect ? () => onSelect(tab.value) : undefined}
className={styles.menuItem}
disableRipple
focusRipple={false}
component="li"
>
<span className={combineClasses({ [styles.menuItemSpan]: true, active: isActive })}>
{hasChanges ? `${tab.label}*` : tab.label}
</span>
</ListItem>
<NavLink to={tab.path} key={tab.path} className={styles.link}>
{({ isActive }) => (
<ListItem button className={styles.menuItem} disableRipple focusRipple={false} component="li">
<span className={combineClasses({ [styles.menuItemSpan]: true, active: isActive })}>
{hasChanges ? `${tab.label}*` : tab.label}
</span>
</ListItem>
)}
</NavLink>
)
})}
</List>
Expand All @@ -49,6 +43,10 @@ const useStyles = makeStyles((theme) => ({
marginTop: theme.spacing(5),
},

link: {
textDecoration: "none",
},

menuItem: {
letterSpacing: -theme.spacing(0.0375),
padding: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import { makeStyles } from "@material-ui/core/styles"
import { fade } from "@material-ui/core/styles/colorManipulator"
import React from "react"
import { Sidebar, SidebarItem } from "../Sidebar"
import { TabSidebar, TabSidebarItem } from "./TabSidebar"

export type AdminMenuItemCallback = (menuItem: string) => void

export interface PanelProps {
export interface TabPanelProps {
title: string
menuItems: SidebarItem[]
activeTab: string
onSelect: AdminMenuItemCallback
menuItems: TabSidebarItem[]
}

export const Panel: React.FC<PanelProps> = ({ children, title, menuItems, activeTab, onSelect }) => {
export const TabPanel: React.FC<TabPanelProps> = ({ children, title, menuItems }) => {
const styles = useStyles()

return (
<div className={styles.root}>
<div className={styles.inner}>
<div className={styles.menuPanel}>
<div className={styles.title}>{title}</div>
<Sidebar menuItems={menuItems} activeItem={activeTab} onSelect={onSelect} />
<TabSidebar menuItems={menuItems} />
</div>

<div className={styles.contentPanel}>{children}</div>
Expand Down
11 changes: 11 additions & 0 deletions site/src/pages/preferences/account.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react"
import { Section } from "../../components/Section"

const Language = {
title: "Account",
description: "Update your display name, email, profile picture, and dotfiles preferences.",
}

export const PreferencesAccountPage: React.FC = () => {
return <Section title={Language.title} description={Language.description} />
}
15 changes: 0 additions & 15 deletions site/src/pages/preferences/index.tsx

This file was deleted.

12 changes: 12 additions & 0 deletions site/src/pages/preferences/linked-accounts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react"
import { Section } from "../../components/Section"

const Language = {
title: "Linked Accounts",
description:
"Linking your Coder account will add your workspace SSH key, allowing you to perform Git actions on all your workspaces.",
}

export const PreferencesLinkedAccountsPage: React.FC = () => {
return <Section title={Language.title} description={Language.description} />
}
11 changes: 11 additions & 0 deletions site/src/pages/preferences/security.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react"
import { Section } from "../../components/Section"

const Language = {
title: "Security",
description: "Changing your password will sign you out of your current session.",
}

export const PreferencesSecurityPage: React.FC = () => {
return <Section title={Language.title} description={Language.description} />
}
12 changes: 12 additions & 0 deletions site/src/pages/preferences/ssh-keys.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react"
import { Section } from "../../components/Section"

const Language = {
title: "SSH Keys",
description:
"Coder automatically inserts a private key into every workspace; you can add the corresponding public key to any services (such as Git) that you need access to from your workspace.",
}

export const PreferencesSSHKeysPage: React.FC = () => {
return <Section title={Language.title} description={Language.description} />
}