Skip to content

Commit 0074fdc

Browse files
committed
implement password verification during signup
1 parent 4d41ab7 commit 0074fdc

File tree

8 files changed

+75
-29
lines changed

8 files changed

+75
-29
lines changed

app/(blobs)/(setup)/_components/OnboardSteps/ConnectUploadThing.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function ConnectUploadThing() {
2727
to visit UploadThing. Create an app and copy and paste your API key
2828
below.
2929
</Paragraph>
30-
<Alert variant="info" className="mt-4">
30+
<Alert variant="info">
3131
<AlertTitle>Good to know:</AlertTitle>
3232
<AlertDescription>
3333
Your UploadThing account is unique to you, meaning that no one else

app/(blobs)/(setup)/_components/OnboardSteps/CreateAccount.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { SignUpForm } from '~/app/(blobs)/(setup)/_components/SignUpForm';
2+
import { Alert, AlertDescription, AlertTitle } from '~/components/ui/Alert';
23
import Heading from '~/components/ui/typography/Heading';
34
import Paragraph from '~/components/ui/typography/Paragraph';
45

@@ -13,6 +14,14 @@ function CreateAccount() {
1314
administrator account can be created.
1415
</Paragraph>
1516
</div>
17+
<Alert variant="warning">
18+
<AlertTitle>Important</AlertTitle>
19+
<AlertDescription>
20+
It is not possible to recover the account details if they are lost.
21+
Make sure to store the account details in a safe place, such as a
22+
password manager.
23+
</AlertDescription>
24+
</Alert>
1625
<SignUpForm />
1726
</div>
1827
);

app/(blobs)/(setup)/_components/OnboardSteps/UploadProtocol.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function ConfigureStudy() {
2828
the dashboard.
2929
</Paragraph>
3030
<ProtocolUploader
31-
className="m-14 p-6"
31+
className="m-10 p-8"
3232
buttonVariant="outline"
3333
buttonSize="lg"
3434
hideCancelButton

app/(blobs)/(setup)/_components/Sidebar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ function OnboardSteps({ steps }: { steps: string[] }) {
2525
>
2626
<div
2727
className={cn(
28-
'text-md flex h-10 w-10 items-center justify-center rounded-full border border-primary/[.06] font-bold',
28+
'border-primary/[.06] flex h-10 w-10 items-center justify-center rounded-full border text-sm font-bold',
2929
index < currentStep - 1 &&
30-
'border-teal-400 bg-success text-white',
30+
'bg-success border-teal-400 text-white',
3131
index === currentStep - 1 &&
3232
'border-primary bg-primary text-white',
3333
)}
@@ -39,7 +39,7 @@ function OnboardSteps({ steps }: { steps: string[] }) {
3939
)}
4040
</div>
4141
<div className="flex flex-col">
42-
<Heading variant={'h4-all-caps'} className="m-0">
42+
<Heading variant={'h4-all-caps'} className="m-0 text-xs">
4343
{step}
4444
</Heading>
4545
</div>

app/(blobs)/(setup)/_components/SignUpForm.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,20 @@ export const SignUpForm = () => {
1111
const {
1212
register,
1313
handleSubmit,
14+
watch,
15+
trigger,
1416
formState: { errors, isValid, isSubmitting },
1517
} = useZodForm({
1618
schema: createUserSchema,
19+
mode: 'onTouched',
1720
});
1821

1922
const onSubmit = async (data: unknown) => {
2023
await signup(data);
2124
};
2225

26+
const password = watch('password');
27+
2328
return (
2429
<form
2530
className="flex flex-col"
@@ -34,19 +39,35 @@ export const SignUpForm = () => {
3439
placeholder="username..."
3540
autoComplete="do-not-autofill"
3641
error={errors.username?.message}
37-
{...register('username')}
42+
{...register('username', {})}
3843
/>
3944
</div>
4045
<div className="mb-6 flex flex-wrap">
4146
<Input
47+
className="w-full"
4248
label="Password"
4349
hint="Your password must be at least 8 characters long, and contain at least one each of lowercase, uppercase, number and symbol characters."
4450
type="password"
4551
placeholder="******************"
4652
autoComplete="do-not-autofill"
4753
error={errors.password?.message}
48-
{...register('password')}
54+
{...register('password', {
55+
onChange: () => trigger('password'),
56+
})}
4957
/>
58+
{password && password.length > 0 && (
59+
<Input
60+
className="w-full"
61+
label="Confirm password"
62+
type="password"
63+
placeholder="******************"
64+
autoComplete="do-not-autofill"
65+
error={errors.confirmPassword?.message}
66+
{...register('confirmPassword', {
67+
onChange: () => trigger('confirmPassword'),
68+
})}
69+
/>
70+
)}
5071
</div>
5172
<div className="flex flex-wrap justify-end">
5273
<Button disabled={isSubmitting || !isValid} type="submit">

components/ui/Alert.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Heading from './typography/Heading';
77
import { paragraphVariants } from './typography/Paragraph';
88

99
const alertVariants = cva(
10-
'relative w-full bg-card text-foreground rounded-lg border p-4 [&>svg~*]:pl-6 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground',
10+
'relative w-full bg-card text-foreground rounded-lg border p-4 [&>svg~*]:pl-6 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground gap-2 grid my-6',
1111
{
1212
variants: {
1313
variant: {
@@ -17,6 +17,8 @@ const alertVariants = cva(
1717
'bg-destructive/5 border-destructive text-destructive [&>svg]:text-destructive [--color-link:var(--color-destructive)]',
1818
success:
1919
'bg-success/5 border-success text-success [&>svg]:text-success [--color-link:var(--color-success)]',
20+
warning:
21+
'bg-warning/2 border-warning text-warning [--color-link:var(--color-warning)]',
2022
},
2123
},
2224
defaultVariants: {

schemas/auth.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,36 @@ import { isStrongPassword } from 'validator';
22
import { z } from 'zod';
33
import { zfd } from 'zod-form-data';
44

5-
export const createUserSchema = z.object({
6-
username: z
7-
.string()
8-
.min(4, { message: 'Username must be at least 4 characters' })
9-
.refine((s) => !s.includes(' '), 'Username cannot contain spaces'),
10-
password: z.string().refine(
11-
(password) =>
12-
isStrongPassword(password, {
13-
minLowercase: 1,
14-
minUppercase: 1,
15-
minNumbers: 1,
16-
minSymbols: 1,
17-
}),
18-
{
19-
message:
20-
'Password must contain at least 1 lowercase, 1 uppercase, 1 number, and 1 symbol',
21-
},
22-
),
23-
});
5+
export const createUserSchema = z
6+
.object({
7+
username: z
8+
.string()
9+
.min(4, { message: 'Username must be at least 4 characters' })
10+
.refine((s) => !s.includes(' '), 'Username cannot contain spaces'),
11+
password: z.string().refine(
12+
(password) =>
13+
isStrongPassword(password, {
14+
minLowercase: 1,
15+
minUppercase: 1,
16+
minNumbers: 1,
17+
minSymbols: 1,
18+
}),
19+
{
20+
message:
21+
'Password must contain at least 1 lowercase, 1 uppercase, 1 number, and 1 symbol',
22+
},
23+
),
24+
confirmPassword: z.string().min(1),
25+
})
26+
.superRefine((val, ctx) => {
27+
if (val.password !== val.confirmPassword) {
28+
ctx.addIssue({
29+
code: z.ZodIssueCode.custom,
30+
message: 'Passwords do not match',
31+
path: ['confirmPassword'],
32+
});
33+
}
34+
});
2435

2536
export const createUserFormDataSchema = zfd.formData(createUserSchema);
2637

styles/globals.css

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@
7979
--color-info: hsl(var(--info));
8080
--color-info-foreground: hsl(var(--info-foreground));
8181

82+
--color-warning: hsl(var(--warning));
83+
--color-warning-foreground: hsl(var(--warning-foreground));
84+
8285
--color-muted: hsl(var(--muted));
8386
--color-muted-foreground: hsl(var(--muted-foreground));
8487

@@ -322,8 +325,8 @@
322325
calc(var(--kiwi-lightness) - var(--dark-mod));
323326

324327
--neon-carrot-hue: 32;
325-
--neon-carrot-saturation: 90%;
326-
--neon-carrot-lightness: 55%;
328+
--neon-carrot-saturation: 92%;
329+
--neon-carrot-lightness: 52%;
327330
--neon-carrot: var(--neon-carrot-hue) var(--neon-carrot-saturation)
328331
var(--neon-carrot-lightness);
329332
--neon-carrot--dark: var(--neon-carrot-hue) var(--neon-carrot-saturation)

0 commit comments

Comments
 (0)