Skip to content

Commit 305556f

Browse files
authored
feat(site): use custom application name (#9902)
1 parent 0f94666 commit 305556f

File tree

6 files changed

+74
-4
lines changed

6 files changed

+74
-4
lines changed

site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const meta: Meta<typeof AppearanceSettingsPageView> = {
66
component: AppearanceSettingsPageView,
77
args: {
88
appearance: {
9-
application_name: "",
9+
application_name: "Foobar",
1010
logo_url: "https://github.com/coder.png",
1111
service_banner: {
1212
enabled: true,

site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ export const AppearanceSettingsPageView = ({
4242
const styles = useStyles();
4343
const theme = useTheme();
4444

45+
const applicationNameForm = useFormik<{
46+
application_name: string;
47+
}>({
48+
initialValues: {
49+
application_name: appearance.application_name,
50+
},
51+
onSubmit: (values) => onSaveAppearance(values, false),
52+
});
53+
const applicationNameFieldHelpers = getFormHelpers(applicationNameForm);
54+
4555
const logoForm = useFormik<{
4656
logo_url: string;
4757
}>({
@@ -87,6 +97,22 @@ export const AppearanceSettingsPageView = ({
8797
<EnterpriseBadge />
8898
</Badges>
8999

100+
<Fieldset
101+
title="Application name"
102+
subtitle="Specify a custom application name to be displayed on the login page."
103+
validation={!isEntitled ? "This is an Enterprise only feature." : ""}
104+
onSubmit={applicationNameForm.handleSubmit}
105+
button={!isEntitled && <Button disabled>Submit</Button>}
106+
>
107+
<TextField
108+
{...applicationNameFieldHelpers("application_name")}
109+
defaultValue={appearance.application_name}
110+
fullWidth
111+
placeholder='Leave empty to display "Coder".'
112+
disabled={!isEntitled}
113+
/>
114+
</Fieldset>
115+
90116
<Fieldset
91117
title="Logo URL"
92118
subtitle="Specify a custom URL for your logo to be displayed in the top left
@@ -99,6 +125,13 @@ export const AppearanceSettingsPageView = ({
99125
onSubmit={logoForm.handleSubmit}
100126
button={!isEntitled && <Button disabled>Submit</Button>}
101127
>
128+
<TextField
129+
{...logoFieldHelpers("application_name")}
130+
defaultValue={appearance.application_name}
131+
fullWidth
132+
placeholder='Leave empty to display "Coder".'
133+
disabled={!isEntitled}
134+
/>
102135
<TextField
103136
{...logoFieldHelpers("logo_url")}
104137
defaultValue={appearance.logo_url}

site/src/pages/LoginPage/LoginPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { Helmet } from "react-helmet-async";
44
import { Navigate, useLocation } from "react-router-dom";
55
import { retrieveRedirect } from "utils/redirect";
66
import { LoginPageView } from "./LoginPageView";
7+
import { getApplicationName } from "utils/appearance";
78

89
export const LoginPage: FC = () => {
910
const location = useLocation();
1011
const [authState, authSend] = useAuth();
1112
const redirectTo = retrieveRedirect(location.search);
13+
const applicationName = getApplicationName();
1214

1315
if (authState.matches("signedIn")) {
1416
return <Navigate to={redirectTo} replace />;
@@ -18,7 +20,7 @@ export const LoginPage: FC = () => {
1820
return (
1921
<>
2022
<Helmet>
21-
<title>Sign in to Coder</title>
23+
<title>Sign in to {applicationName}</title>
2224
</Helmet>
2325
<LoginPageView
2426
context={authState.context}

site/src/pages/LoginPage/LoginPageView.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { AuthContext, UnauthenticatedData } from "xServices/auth/authXService";
66
import { SignInForm } from "./SignInForm";
77
import { retrieveRedirect } from "utils/redirect";
88
import { CoderIcon } from "components/Icons/CoderIcon";
9+
import { getApplicationName, getLogoURL } from "utils/appearance";
910

1011
export interface LoginPageViewProps {
1112
context: AuthContext;
@@ -28,13 +29,29 @@ export const LoginPageView: FC<LoginPageViewProps> = ({
2829
// This allows messages to be displayed at the top of the sign in form.
2930
// Helpful for any redirects that want to inform the user of something.
3031
const info = new URLSearchParams(location.search).get("info") || undefined;
32+
const applicationName = getApplicationName();
33+
const logoURL = getLogoURL();
34+
const applicationLogo = logoURL ? (
35+
<div>
36+
<img
37+
alt={applicationName}
38+
src={logoURL}
39+
// This prevent browser to display the ugly error icon if the
40+
// image path is wrong or user didn't finish typing the url
41+
onError={(e) => (e.currentTarget.style.display = "none")}
42+
onLoad={(e) => (e.currentTarget.style.display = "inline")}
43+
/>
44+
</div>
45+
) : (
46+
<CoderIcon fill="white" opacity={1} className={styles.icon} />
47+
);
3148

3249
return isLoading ? (
3350
<FullScreenLoader />
3451
) : (
3552
<div className={styles.root}>
3653
<div className={styles.container}>
37-
<CoderIcon fill="white" opacity={1} className={styles.icon} />
54+
{applicationLogo}
3855
<SignInForm
3956
authMethods={data.authMethods}
4057
redirectTo={redirectTo}

site/src/pages/LoginPage/SignInForm.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Button from "@mui/material/Button";
99
import EmailIcon from "@mui/icons-material/EmailOutlined";
1010
import { Alert } from "components/Alert/Alert";
1111
import { ErrorAlert } from "components/Alert/ErrorAlert";
12+
import { getApplicationName } from "utils/appearance";
1213

1314
export const Language = {
1415
emailLabel: "Email",
@@ -90,11 +91,12 @@ export const SignInForm: FC<React.PropsWithChildren<SignInFormProps>> = ({
9091
// Hide password auth by default if any OAuth method is enabled
9192
const [showPasswordAuth, setShowPasswordAuth] = useState(!oAuthEnabled);
9293
const styles = useStyles();
94+
const applicationName = getApplicationName();
9395

9496
return (
9597
<div className={styles.root}>
9698
<h1 className={styles.title}>
97-
Sign in to <strong>Coder</strong>
99+
Sign in to <strong>{applicationName}</strong>
98100
</h1>
99101

100102
{Boolean(error) && (

site/src/utils/appearance.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export const getApplicationName = (): string => {
2+
const c = document.head
3+
.querySelector(`meta[name=application-name]`)
4+
?.getAttribute("content");
5+
// Fallback to "Coder" if the application name is not available for some reason.
6+
// We need to check if the content does not look like {{ .ApplicationName}}
7+
// as it means that Coder is running in development mode (port :8080).
8+
return c && !c.startsWith("{{ .") ? c : "Coder";
9+
};
10+
11+
export const getLogoURL = (): string => {
12+
const c = document.head
13+
.querySelector(`meta[property=logo-url]`)
14+
?.getAttribute("content");
15+
return c && !c.startsWith("{{ .") ? c : "";
16+
};

0 commit comments

Comments
 (0)