Skip to content

Commit 0ac37b1

Browse files
authored
feat: Add page titles (#2070)
1 parent 3f3ecbf commit 0ac37b1

File tree

17 files changed

+134
-36
lines changed

17 files changed

+134
-36
lines changed

site/htmlTemplates/index.html

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
<link rel="mask-icon" href="/static/favicon.svg" color="#000000" crossorigin="use-credentials" />
2222
<link rel="alternate icon" type="image/png" href="/favicon.png" />
2323
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
24-
<title>Coder</title>
2524
</head>
2625

2726
<body>

site/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"history": "5.3.0",
4545
"react": "17.0.2",
4646
"react-dom": "17.0.2",
47+
"react-helmet": "^6.1.0",
4748
"react-markdown": "8.0.3",
4849
"react-router-dom": "6.3.0",
4950
"sourcemapped-stacktrace": "1.1.11",
@@ -73,6 +74,7 @@
7374
"@types/node": "14.18.16",
7475
"@types/react": "17.0.44",
7576
"@types/react-dom": "17.0.16",
77+
"@types/react-helmet": "^6.1.5",
7678
"@types/superagent": "4.1.15",
7779
"@types/uuid": "8.3.4",
7880
"@typescript-eslint/eslint-plugin": "5.23.0",

site/src/components/SettingsLayout/SettingsLayout.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Box from "@material-ui/core/Box"
22
import { FC } from "react"
3+
import { Helmet } from "react-helmet"
34
import { Outlet } from "react-router-dom"
5+
import { pageTitle } from "../../util/page"
46
import { AuthAndFrame } from "../AuthAndFrame/AuthAndFrame"
57
import { Margins } from "../Margins/Margins"
68
import { TabPanel } from "../TabPanel/TabPanel"
@@ -22,6 +24,9 @@ export const SettingsLayout: FC = () => {
2224
return (
2325
<AuthAndFrame>
2426
<Box display="flex" flexDirection="column">
27+
<Helmet>
28+
<title>{pageTitle("Settings")}</title>
29+
</Helmet>
2530
<Margins>
2631
<TabPanel title={Language.settingsLabel} menuItems={menuItems}>
2732
<Outlet />

site/src/pages/CliAuthPage/CliAuthPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { makeStyles } from "@material-ui/core/styles"
22
import { useActor } from "@xstate/react"
33
import React, { useContext, useEffect, useState } from "react"
4+
import { Helmet } from "react-helmet"
45
import { getApiKey } from "../../api/api"
56
import { CliAuthToken } from "../../components/CliAuthToken/CliAuthToken"
67
import { FullScreenLoader } from "../../components/Loader/FullScreenLoader"
8+
import { pageTitle } from "../../util/page"
79
import { XServiceContext } from "../../xServices/StateContext"
810

911
export const CliAuthenticationPage: React.FC = () => {
@@ -29,6 +31,9 @@ export const CliAuthenticationPage: React.FC = () => {
2931

3032
return (
3133
<div className={styles.root}>
34+
<Helmet>
35+
<title>{pageTitle("CLI Auth")}</title>
36+
</Helmet>
3237
<CliAuthToken sessionToken={apiKey} />
3338
</div>
3439
)

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx

+30-23
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { useMachine } from "@xstate/react"
22
import { FC } from "react"
3+
import { Helmet } from "react-helmet"
34
import { useNavigate, useSearchParams } from "react-router-dom"
45
import { Template } from "../../api/typesGenerated"
56
import { useOrganizationId } from "../../hooks/useOrganizationId"
7+
import { pageTitle } from "../../util/page"
68
import { createWorkspaceMachine } from "../../xServices/createWorkspace/createWorkspaceXService"
79
import { CreateWorkspacePageView } from "./CreateWorkspacePageView"
810

@@ -21,29 +23,34 @@ const CreateWorkspacePage: FC = () => {
2123
})
2224

2325
return (
24-
<CreateWorkspacePageView
25-
loadingTemplates={createWorkspaceState.matches("gettingTemplates")}
26-
loadingTemplateSchema={createWorkspaceState.matches("gettingTemplateSchema")}
27-
creatingWorkspace={createWorkspaceState.matches("creatingWorkspace")}
28-
templates={createWorkspaceState.context.templates}
29-
selectedTemplate={createWorkspaceState.context.selectedTemplate}
30-
templateSchema={createWorkspaceState.context.templateSchema}
31-
onCancel={() => {
32-
navigate(preSelectedTemplateName ? "/templates" : "/workspaces")
33-
}}
34-
onSubmit={(request) => {
35-
send({
36-
type: "CREATE_WORKSPACE",
37-
request,
38-
})
39-
}}
40-
onSelectTemplate={(template: Template) => {
41-
send({
42-
type: "SELECT_TEMPLATE",
43-
template,
44-
})
45-
}}
46-
/>
26+
<>
27+
<Helmet>
28+
<title>{pageTitle("Create Workspace")}</title>
29+
</Helmet>
30+
<CreateWorkspacePageView
31+
loadingTemplates={createWorkspaceState.matches("gettingTemplates")}
32+
loadingTemplateSchema={createWorkspaceState.matches("gettingTemplateSchema")}
33+
creatingWorkspace={createWorkspaceState.matches("creatingWorkspace")}
34+
templates={createWorkspaceState.context.templates}
35+
selectedTemplate={createWorkspaceState.context.selectedTemplate}
36+
templateSchema={createWorkspaceState.context.templateSchema}
37+
onCancel={() => {
38+
navigate(preSelectedTemplateName ? "/templates" : "/workspaces")
39+
}}
40+
onSubmit={(request) => {
41+
send({
42+
type: "CREATE_WORKSPACE",
43+
request,
44+
})
45+
}}
46+
onSelectTemplate={(template: Template) => {
47+
send({
48+
type: "SELECT_TEMPLATE",
49+
template,
50+
})
51+
}}
52+
/>
53+
</>
4754
)
4855
}
4956

site/src/pages/LoginPage/LoginPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { makeStyles } from "@material-ui/core/styles"
22
import { useActor } from "@xstate/react"
33
import React, { useContext } from "react"
4+
import { Helmet } from "react-helmet"
45
import { Navigate, useLocation } from "react-router-dom"
56
import { isApiError } from "../../api/errors"
67
import { Footer } from "../../components/Footer/Footer"
78
import { SignInForm } from "../../components/SignInForm/SignInForm"
9+
import { pageTitle } from "../../util/page"
810
import { retrieveRedirect } from "../../util/redirect"
911
import { XServiceContext } from "../../xServices/StateContext"
1012

@@ -50,6 +52,9 @@ export const LoginPage: React.FC = () => {
5052
} else {
5153
return (
5254
<div className={styles.root}>
55+
<Helmet>
56+
<title>{pageTitle("Login")}</title>
57+
</Helmet>
5358
<div className={styles.layout}>
5459
<div className={styles.container}>
5560
<SignInForm

site/src/pages/TemplatePage/TemplatePage.tsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { useMachine } from "@xstate/react"
22
import { FC } from "react"
3+
import { Helmet } from "react-helmet"
34
import { useParams } from "react-router-dom"
45
import { Loader } from "../../components/Loader/Loader"
56
import { useOrganizationId } from "../../hooks/useOrganizationId"
7+
import { pageTitle } from "../../util/page"
68
import { templateMachine } from "../../xServices/template/templateXService"
79
import { TemplatePageView } from "./TemplatePageView"
810

@@ -33,10 +35,15 @@ export const TemplatePage: FC = () => {
3335
}
3436

3537
return (
36-
<TemplatePageView
37-
template={template}
38-
activeTemplateVersion={activeTemplateVersion}
39-
templateResources={templateResources}
40-
/>
38+
<>
39+
<Helmet>
40+
<title>{pageTitle(`${template.name} · Template`)}</title>
41+
</Helmet>
42+
<TemplatePageView
43+
template={template}
44+
activeTemplateVersion={activeTemplateVersion}
45+
templateResources={templateResources}
46+
/>
47+
</>
4148
)
4249
}

site/src/pages/TemplatesPage/TemplatesPage.tsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { useActor, useMachine } from "@xstate/react"
22
import React, { useContext } from "react"
3+
import { Helmet } from "react-helmet"
4+
import { pageTitle } from "../../util/page"
35
import { XServiceContext } from "../../xServices/StateContext"
46
import { templatesMachine } from "../../xServices/templates/templatesXService"
57
import { TemplatesPageView } from "./TemplatesPageView"
@@ -10,11 +12,16 @@ const TemplatesPage: React.FC = () => {
1012
const [templatesState] = useMachine(templatesMachine)
1113

1214
return (
13-
<TemplatesPageView
14-
templates={templatesState.context.templates}
15-
canCreateTemplate={authState.context.permissions?.createTemplates}
16-
loading={templatesState.hasTag("loading")}
17-
/>
15+
<>
16+
<Helmet>
17+
<title>{pageTitle("Templates")}</title>
18+
</Helmet>
19+
<TemplatesPageView
20+
templates={templatesState.context.templates}
21+
canCreateTemplate={authState.context.permissions?.createTemplates}
22+
loading={templatesState.hasTag("loading")}
23+
/>
24+
</>
1825
)
1926
}
2027

site/src/pages/TerminalPage/TerminalPage.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { makeStyles } from "@material-ui/core/styles"
22
import { useMachine } from "@xstate/react"
33
import { FC, useEffect, useRef, useState } from "react"
4+
import { Helmet } from "react-helmet"
45
import { useLocation, useNavigate, useParams } from "react-router-dom"
56
import { v4 as uuidv4 } from "uuid"
67
import * as XTerm from "xterm"
78
import { FitAddon } from "xterm-addon-fit"
89
import { WebLinksAddon } from "xterm-addon-web-links"
910
import "xterm/css/xterm.css"
1011
import { MONOSPACE_FONT_FAMILY } from "../../theme/constants"
12+
import { pageTitle } from "../../util/page"
1113
import { terminalMachine } from "../../xServices/terminal/terminalXService"
1214

1315
export const Language = {
@@ -179,6 +181,15 @@ const TerminalPage: FC<{
179181

180182
return (
181183
<>
184+
<Helmet>
185+
<title>
186+
{terminalState.context.workspace
187+
? pageTitle(
188+
`Terminal · ${terminalState.context.workspace.owner_name}/${terminalState.context.workspace.name}`,
189+
)
190+
: ""}
191+
</title>
192+
</Helmet>
182193
{/* This overlay makes it more obvious that the terminal is disconnected. */}
183194
{/* It's nice for situations where Coder restarts, and they are temporarily disconnected. */}
184195
<div className={`${styles.overlay} ${isDisconnected ? "" : "connected"}`}>

site/src/pages/UsersPage/CreateUserPage/CreateUserPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { useActor, useSelector } from "@xstate/react"
22
import React, { useContext } from "react"
3+
import { Helmet } from "react-helmet"
34
import { useNavigate } from "react-router"
45
import * as TypesGen from "../../../api/typesGenerated"
56
import { CreateUserForm } from "../../../components/CreateUserForm/CreateUserForm"
67
import { Margins } from "../../../components/Margins/Margins"
8+
import { pageTitle } from "../../../util/page"
79
import { selectOrgId } from "../../../xServices/auth/authSelectors"
810
import { XServiceContext } from "../../../xServices/StateContext"
911

@@ -23,6 +25,9 @@ export const CreateUserPage: React.FC = () => {
2325

2426
return (
2527
<Margins>
28+
<Helmet>
29+
<title>{pageTitle("Create User")}</title>
30+
</Helmet>
2631
<CreateUserForm
2732
formErrors={createUserFormErrors}
2833
onSubmit={(user: TypesGen.CreateUserRequest) => usersSend({ type: "CREATE", user })}

site/src/pages/UsersPage/UsersPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { useActor, useSelector } from "@xstate/react"
22
import React, { useContext, useEffect } from "react"
3+
import { Helmet } from "react-helmet"
34
import { useNavigate } from "react-router"
45
import { ConfirmDialog } from "../../components/ConfirmDialog/ConfirmDialog"
56
import { ResetPasswordDialog } from "../../components/ResetPasswordDialog/ResetPasswordDialog"
7+
import { pageTitle } from "../../util/page"
68
import { selectPermissions } from "../../xServices/auth/authSelectors"
79
import { XServiceContext } from "../../xServices/StateContext"
810
import { UsersPageView } from "./UsersPageView"
@@ -48,6 +50,9 @@ export const UsersPage: React.FC = () => {
4850

4951
return (
5052
<>
53+
<Helmet>
54+
<title>{pageTitle("Users")}</title>
55+
</Helmet>
5156
<UsersPageView
5257
roles={roles}
5358
users={users}

site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { makeStyles } from "@material-ui/core/styles"
22
import Typography from "@material-ui/core/Typography"
33
import { useMachine } from "@xstate/react"
44
import { FC } from "react"
5+
import { Helmet } from "react-helmet"
56
import { useParams } from "react-router-dom"
67
import { ProvisionerJobLog } from "../../api/typesGenerated"
78
import { Loader } from "../../components/Loader/Loader"
89
import { Margins } from "../../components/Margins/Margins"
910
import { Stack } from "../../components/Stack/Stack"
1011
import { WorkspaceBuildLogs } from "../../components/WorkspaceBuildLogs/WorkspaceBuildLogs"
1112
import { WorkspaceBuildStats } from "../../components/WorkspaceBuildStats/WorkspaceBuildStats"
13+
import { pageTitle } from "../../util/page"
1214
import { workspaceBuildMachine } from "../../xServices/workspaceBuild/workspaceBuildXService"
1315

1416
const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => {
@@ -34,6 +36,9 @@ export const WorkspaceBuildPage: FC = () => {
3436

3537
return (
3638
<Margins>
39+
<Helmet>
40+
<title>{build ? pageTitle(`Build #${build.build_number} · ${build.workspace_name}`) : ""}</title>
41+
</Helmet>
3742
<Stack>
3843
<Typography variant="h4" className={styles.title}>
3944
Logs

site/src/pages/WorkspacePage/WorkspacePage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useMachine } from "@xstate/react"
22
import React, { useEffect } from "react"
3+
import { Helmet } from "react-helmet"
34
import { useNavigate, useParams } from "react-router-dom"
45
import { DeleteWorkspaceDialog } from "../../components/DeleteWorkspaceDialog/DeleteWorkspaceDialog"
56
import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary"
@@ -8,6 +9,7 @@ import { Margins } from "../../components/Margins/Margins"
89
import { Stack } from "../../components/Stack/Stack"
910
import { Workspace } from "../../components/Workspace/Workspace"
1011
import { firstOrItem } from "../../util/array"
12+
import { pageTitle } from "../../util/page"
1113
import { workspaceMachine } from "../../xServices/workspace/workspaceXService"
1214
import { workspaceScheduleBannerMachine } from "../../xServices/workspaceSchedule/workspaceScheduleBannerXService"
1315

@@ -36,6 +38,9 @@ export const WorkspacePage: React.FC = () => {
3638
} else {
3739
return (
3840
<Margins>
41+
<Helmet>
42+
<title>{pageTitle(`${workspace.owner_name}/${workspace.name}`)}</title>
43+
</Helmet>
3944
<Stack spacing={4}>
4045
<>
4146
<Workspace

site/src/pages/WorkspacesPage/WorkspacesPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import SearchIcon from "@material-ui/icons/Search"
1111
import { useMachine } from "@xstate/react"
1212
import { FormikErrors, useFormik } from "formik"
1313
import { FC, useState } from "react"
14+
import { Helmet } from "react-helmet"
1415
import { Link as RouterLink } from "react-router-dom"
1516
import { CloseDropdown, OpenDropdown } from "../../components/DropdownArrows/DropdownArrows"
1617
import { Margins } from "../../components/Margins/Margins"
1718
import { Stack } from "../../components/Stack/Stack"
1819
import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils"
20+
import { pageTitle } from "../../util/page"
1921
import { workspacesMachine } from "../../xServices/workspaces/workspacesXService"
2022
import { WorkspacesPageView } from "./WorkspacesPageView"
2123

@@ -74,6 +76,9 @@ const WorkspacesPage: FC = () => {
7476

7577
return (
7678
<Margins>
79+
<Helmet>
80+
<title>{pageTitle("Workspaces")}</title>
81+
</Helmet>
7782
<Stack direction="row" className={styles.workspacesHeaderContainer}>
7883
<Stack direction="column" className={styles.filterColumn}>
7984
<Stack direction="row" spacing={0} className={styles.filterContainer}>

site/src/util/page.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const pageTitle = (prefix: string): string => {
2+
return `${prefix} – Coder`
3+
}

site/webpack.dev.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const config: Configuration = {
6363
port: process.env.PORT || 8080,
6464
proxy: {
6565
"/api": {
66-
target: "http://localhost:3000",
66+
target: "https://dev.coder.com",
6767
ws: true,
6868
secure: false,
6969
},

0 commit comments

Comments
 (0)