Skip to content

Commit f7bf759

Browse files
committed
frontend changes
1 parent 7f560bb commit f7bf759

File tree

4 files changed

+74
-24
lines changed

4 files changed

+74
-24
lines changed

site/src/index.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
--avatar-lg: 2.5rem;
4444
--avatar-default: 1.5rem;
4545
--avatar-sm: 1.125rem;
46+
--divider: 240, 6%, 90%; /* tailwind zinc-200 */
4647
}
4748
.dark {
4849
--content-primary: 0 0% 98%;
@@ -75,6 +76,7 @@
7576
--border: 240 3.7% 15.9%;
7677
--input: 240 3.7% 15.9%;
7778
--ring: 240 4.9% 83.9%;
79+
--divider: 240, 5%, 26%; /* tailwind zinc-700 */
7880
}
7981
}
8082

site/src/pages/SetupPage/SetupPage.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { buildInfo } from "api/queries/buildInfo";
2-
import { createFirstUser } from "api/queries/users";
2+
import { authMethods, createFirstUser } from "api/queries/users";
33
import { Loader } from "components/Loader/Loader";
44
import { useAuthContext } from "contexts/auth/AuthProvider";
55
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
@@ -19,6 +19,7 @@ export const SetupPage: FC = () => {
1919
isSignedIn,
2020
isSigningIn,
2121
} = useAuthContext();
22+
const authMethodsQuery = useQuery(authMethods());
2223
const createFirstUserMutation = useMutation(createFirstUser());
2324
const setupIsComplete = !isConfiguringTheFirstUser;
2425
const { metadata } = useEmbeddedMetadata();
@@ -34,7 +35,7 @@ export const SetupPage: FC = () => {
3435
});
3536
}, [buildInfoQuery.data]);
3637

37-
if (isLoading) {
38+
if (isLoading || authMethodsQuery.isLoading) {
3839
return <Loader fullscreen />;
3940
}
4041

@@ -54,6 +55,7 @@ export const SetupPage: FC = () => {
5455
<title>{pageTitle("Set up your account")}</title>
5556
</Helmet>
5657
<SetupPageView
58+
authMethods={authMethodsQuery.data}
5759
isLoading={isSigningIn || createFirstUserMutation.isLoading}
5860
error={createFirstUserMutation.error}
5961
onSubmit={async (firstUser) => {

site/src/pages/SetupPage/SetupPageView.tsx

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import GitHubIcon from "@mui/icons-material/GitHub";
12
import LoadingButton from "@mui/lab/LoadingButton";
23
import AlertTitle from "@mui/material/AlertTitle";
34
import Autocomplete from "@mui/material/Autocomplete";
5+
import Button from "@mui/material/Button";
46
import Checkbox from "@mui/material/Checkbox";
57
import Link from "@mui/material/Link";
68
import MenuItem from "@mui/material/MenuItem";
@@ -15,8 +17,7 @@ import { PasswordField } from "components/PasswordField/PasswordField";
1517
import { SignInLayout } from "components/SignInLayout/SignInLayout";
1618
import { Stack } from "components/Stack/Stack";
1719
import { type FormikContextType, useFormik } from "formik";
18-
import type { FC } from "react";
19-
import { useEffect } from "react";
20+
import { type ChangeEvent, type FC, useCallback } from "react";
2021
import { docs } from "utils/docs";
2122
import {
2223
getFormHelpers,
@@ -33,7 +34,8 @@ export const Language = {
3334
emailInvalid: "Please enter a valid email address.",
3435
emailRequired: "Please enter an email address.",
3536
passwordRequired: "Please enter a password.",
36-
create: "Create account",
37+
create: "Continue with email",
38+
githubCreate: "Continue with GitHub",
3739
welcomeMessage: <>Welcome to Coder</>,
3840
firstNameLabel: "First name",
3941
lastNameLabel: "Last name",
@@ -50,13 +52,29 @@ export const Language = {
5052
developersRequired: "Please select the number of developers in your company.",
5153
};
5254

55+
const usernameValidator = nameValidator(Language.usernameLabel);
56+
const usernameFromEmail = (email: string): string => {
57+
try {
58+
const emailPrefix = email.split("@")[0];
59+
const username = emailPrefix.toLowerCase().replace(/[^a-z0-9]/g, "-");
60+
usernameValidator.validateSync(username);
61+
return username;
62+
} catch (error) {
63+
console.warn(
64+
"failed to automatically generate username, defaulting to 'admin'",
65+
error,
66+
);
67+
return "admin";
68+
}
69+
};
70+
5371
const validationSchema = Yup.object({
5472
email: Yup.string()
5573
.trim()
5674
.email(Language.emailInvalid)
5775
.required(Language.emailRequired),
5876
password: Yup.string().required(Language.passwordRequired),
59-
username: nameValidator(Language.usernameLabel),
77+
username: usernameValidator,
6078
trial: Yup.bool(),
6179
trial_info: Yup.object().when("trial", {
6280
is: true,
@@ -81,16 +99,23 @@ const numberOfDevelopersOptions = [
8199
"2500+",
82100
];
83101

102+
const iconStyles = {
103+
width: 16,
104+
height: 16,
105+
};
106+
84107
export interface SetupPageViewProps {
85108
onSubmit: (firstUser: TypesGen.CreateFirstUserRequest) => void;
86109
error?: unknown;
87110
isLoading?: boolean;
111+
authMethods: TypesGen.AuthMethods | undefined;
88112
}
89113

90114
export const SetupPageView: FC<SetupPageViewProps> = ({
91115
onSubmit,
92116
error,
93117
isLoading,
118+
authMethods,
94119
}) => {
95120
const form: FormikContextType<TypesGen.CreateFirstUserRequest> =
96121
useFormik<TypesGen.CreateFirstUserRequest>({
@@ -112,12 +137,26 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
112137
},
113138
validationSchema,
114139
onSubmit,
140+
// With validate on blur set to true, the form lights up red whenever
141+
// you click out of it. This is a bit jarring. We instead validate
142+
// on submit and change.
143+
validateOnBlur: false,
115144
});
116145
const getFieldHelpers = getFormHelpers<TypesGen.CreateFirstUserRequest>(
117146
form,
118147
error,
119148
);
120149

150+
const onEmailChange = useCallback(
151+
(event: ChangeEvent<HTMLInputElement>) => {
152+
const email = event.target.value;
153+
const username = usernameFromEmail(email);
154+
form.setFieldValue("username", username);
155+
onChangeTrimmed(form)(event);
156+
},
157+
[form],
158+
);
159+
121160
return (
122161
<SignInLayout>
123162
<header css={{ textAlign: "center", marginBottom: 32 }}>
@@ -142,23 +181,31 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
142181
</header>
143182
<VerticalForm onSubmit={form.handleSubmit}>
144183
<FormFields>
145-
<TextField
146-
autoFocus
147-
{...getFieldHelpers("username")}
148-
onChange={onChangeTrimmed(form)}
149-
autoComplete="username"
150-
fullWidth
151-
label={Language.usernameLabel}
152-
/>
153-
<TextField
154-
{...getFieldHelpers("name")}
155-
autoComplete="name"
156-
fullWidth
157-
label={Language.nameLabel}
158-
/>
184+
{authMethods?.github.enabled && (
185+
<>
186+
<Button
187+
fullWidth
188+
component="a"
189+
href="/api/v2/users/oauth2/github/callback"
190+
variant="contained"
191+
startIcon={<GitHubIcon css={iconStyles} />}
192+
type="submit"
193+
size="xlarge"
194+
>
195+
{Language.githubCreate}
196+
</Button>
197+
<div className="flex items-center gap-4">
198+
<div className="h-[1px] w-full bg-divider" />
199+
<div className="shrink-0 text-xs uppercase text-content-secondary tracking-wider">
200+
or
201+
</div>
202+
<div className="h-[1px] w-full bg-divider" />
203+
</div>
204+
</>
205+
)}
159206
<TextField
160207
{...getFieldHelpers("email")}
161-
onChange={onChangeTrimmed(form)}
208+
onChange={onEmailChange}
162209
autoComplete="email"
163210
fullWidth
164211
label={Language.emailLabel}
@@ -340,9 +387,7 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
340387
loading={isLoading}
341388
type="submit"
342389
data-testid="create"
343-
size="large"
344-
variant="contained"
345-
color="primary"
390+
size="xlarge"
346391
>
347392
{Language.create}
348393
</LoadingButton>

site/tailwind.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ module.exports = {
6363
grey: "hsl(var(--highlight-grey))",
6464
sky: "hsl(var(--highlight-sky))",
6565
},
66+
divider: "hsl(var(--divider))",
6667
},
6768
keyframes: {
6869
loading: {

0 commit comments

Comments
 (0)