Skip to content

Commit 7f527a7

Browse files
authored
Merge pull request UnlockedLabs#178 from techeclecticdesign/formcomps
refactor(174): update 2 forms to use react-hook-forms and improve styling of these forms
2 parents 662093b + b82bc00 commit 7f527a7

File tree

6 files changed

+201
-147
lines changed

6 files changed

+201
-147
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import axios from "axios";
2+
import { useState } from "react";
3+
import { useForm, SubmitHandler } from "react-hook-form";
4+
import InputError from "@/Components/InputError";
5+
import PrimaryButton from "@/Components/PrimaryButton";
6+
import { TextInput } from "@/Components/inputs/TextInput";
7+
type Inputs = {
8+
password: string;
9+
confirmation: string;
10+
};
11+
12+
export default function ChangePasswordForm() {
13+
const [errorMessage, setErrorMessage] = useState("");
14+
const [processing, setProcessing] = useState(false);
15+
16+
const {
17+
register,
18+
handleSubmit,
19+
reset,
20+
formState: { errors },
21+
} = useForm<Inputs>();
22+
23+
const submit: SubmitHandler<Inputs> = async (data) => {
24+
try {
25+
setErrorMessage("");
26+
setProcessing(true);
27+
await axios.post(route("password.update", data));
28+
window.location.replace(route("dashboard"));
29+
} catch (error: any) {
30+
setProcessing(false);
31+
setErrorMessage(error.response.data.message);
32+
reset();
33+
}
34+
};
35+
36+
return (
37+
<form onSubmit={handleSubmit(submit)}>
38+
<TextInput
39+
label={"New password"}
40+
interfaceRef={"password"}
41+
length={50}
42+
required={true}
43+
errors={errors}
44+
register={register}
45+
password={true}
46+
autoComplete="new-password"
47+
isFocused={true}
48+
/>
49+
50+
<TextInput
51+
label={"Confirm password"}
52+
interfaceRef={"password_confirmation"}
53+
length={50}
54+
required={true}
55+
errors={errors}
56+
register={register}
57+
password={true}
58+
autoComplete="new-password"
59+
/>
60+
61+
{errorMessage && (
62+
<div className="block">
63+
<InputError message={errorMessage} className="pt-2" />
64+
</div>
65+
)}
66+
67+
<div className="flex items-center justify-end mt-4">
68+
<PrimaryButton className="ms-4 w-44 h-10" disabled={processing}>
69+
{processing ? (
70+
<span className="loading loading-spinner loading-sm mx-auto"></span>
71+
) : (
72+
<div className="m-auto">Reset Password</div>
73+
)}
74+
</PrimaryButton>
75+
</div>
76+
</form>
77+
);
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import axios from "axios";
2+
import { useState } from "react";
3+
import { useForm, SubmitHandler } from "react-hook-form";
4+
import Checkbox from "@/Components/inputs/Checkbox";
5+
import InputError from "@/Components/InputError";
6+
import PrimaryButton from "@/Components/PrimaryButton";
7+
import { TextInput } from "@/Components/inputs/TextInput";
8+
type Inputs = {
9+
username: string;
10+
password: string;
11+
remember: boolean;
12+
};
13+
14+
export default function LoginForm({ status }: { status?: string }) {
15+
const [errorMessage, setErrorMessage] = useState("");
16+
const [processing, setProcessing] = useState(false);
17+
18+
const {
19+
register,
20+
handleSubmit,
21+
formState: { errors },
22+
} = useForm<Inputs>();
23+
24+
const submit: SubmitHandler<Inputs> = async (data) => {
25+
try {
26+
setErrorMessage("");
27+
setProcessing(true);
28+
await axios.post(route("login", data));
29+
window.location.replace(route("dashboard"));
30+
} catch (error: any) {
31+
setProcessing(false);
32+
setErrorMessage(error.response.data.message);
33+
}
34+
};
35+
36+
return (
37+
<form onSubmit={handleSubmit(submit)}>
38+
<TextInput
39+
label={"Username"}
40+
interfaceRef={"username"}
41+
required={true}
42+
length={50}
43+
errors={errors}
44+
register={register}
45+
/>
46+
47+
<TextInput
48+
label={"Password"}
49+
interfaceRef={"password"}
50+
required={true}
51+
length={50}
52+
errors={errors}
53+
register={register}
54+
password={true}
55+
/>
56+
57+
{errorMessage && (
58+
<div className="block">
59+
<InputError message={errorMessage} className="pt-2" />
60+
</div>
61+
)}
62+
63+
<div className="block mt-4 ml-2">
64+
<label className="flex items-center">
65+
<Checkbox
66+
label={"Remember me"}
67+
interfaceRef={"remember"}
68+
register={register}
69+
/>
70+
</label>
71+
</div>
72+
73+
<div className="flex items-center justify-end mt-4">
74+
<PrimaryButton className="ms-4 w-24 h-10" disabled={processing}>
75+
{processing ? (
76+
<span className="loading loading-spinner loading-sm mx-auto"></span>
77+
) : (
78+
<div className="m-auto">Log in</div>
79+
)}
80+
</PrimaryButton>
81+
</div>
82+
</form>
83+
);
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
interface CheckboxProps {
2+
label: string;
3+
interfaceRef: string;
4+
register: Function;
5+
}
6+
7+
export default function Checkbox({
8+
label,
9+
interfaceRef,
10+
register,
11+
}: CheckboxProps) {
12+
return (
13+
<div className="form-control">
14+
<label className="label cursor-pointer gap-2">
15+
<input
16+
type="checkbox"
17+
className="checkbox"
18+
{...register(interfaceRef)}
19+
/>
20+
<span className="label-text">Remember me</span>
21+
</label>
22+
</div>
23+
);
24+
}

resources/js/Components/inputs/TextInput.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ interface TextProps {
77
length: number | null;
88
errors: FieldErrors<any>;
99
register: Function;
10+
password?: boolean;
11+
isFocused?: boolean;
12+
autoComplete?: string;
1013
}
1114

1215
export function TextInput({
@@ -16,6 +19,9 @@ export function TextInput({
1619
length,
1720
errors,
1821
register,
22+
password = false,
23+
isFocused = false,
24+
autoComplete = "on",
1925
}: TextProps) {
2026
const options = {
2127
required: {
@@ -35,9 +41,11 @@ export function TextInput({
3541
<span className="label-text">{label}</span>
3642
</div>
3743
<input
38-
type="text"
44+
type={`${password ? "password" : "text"}`}
3945
className="input input-bordered w-full"
4046
{...register(interfaceRef, options)}
47+
autoComplete={autoComplete}
48+
autoFocus={isFocused}
4149
/>
4250
<div className="text-error text-sm">
4351
{errors[interfaceRef]?.message?.toString()}

resources/js/Pages/Auth/Login.tsx

+3-75
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,17 @@
1-
import { useEffect, FormEventHandler } from "react";
2-
import Checkbox from "@/Components/Checkbox";
1+
import LoginForm from "@/Components/forms/LoginForm";
32
import GuestLayout from "@/Layouts/GuestLayout";
4-
import InputError from "@/Components/InputError";
5-
import InputLabel from "@/Components/InputLabel";
6-
import PrimaryButton from "@/Components/PrimaryButton";
7-
import TextInput from "@/Components/TextInput";
8-
import { Head, Link, useForm } from "@inertiajs/react";
3+
import { Head } from "@inertiajs/react";
94

105
export default function Login({ status }: { status?: string }) {
11-
const { data, setData, post, processing, errors } = useForm({
12-
username: "",
13-
password: "",
14-
remember: false,
15-
});
16-
17-
const submit: FormEventHandler = (e) => {
18-
e.preventDefault();
19-
20-
post(route("login"));
21-
};
22-
236
return (
247
<GuestLayout>
258
<Head title="Log in" />
26-
279
{status && (
2810
<div className="mb-4 font-medium text-sm text-green-600">
2911
{status}
3012
</div>
3113
)}
32-
33-
<form onSubmit={submit}>
34-
<div className="mt-4">
35-
<InputLabel htmlFor="username" value="Username" />
36-
37-
<TextInput
38-
id="username"
39-
type="username"
40-
name="username"
41-
value={data.username}
42-
className="mt-1 block w-full"
43-
autoComplete="username"
44-
onChange={(e) => setData("username", e.target.value)}
45-
/>
46-
47-
<InputError message={errors.username} className="mt-2" />
48-
</div>
49-
50-
<div className="mt-4">
51-
<InputLabel htmlFor="password" value="Password" />
52-
53-
<TextInput
54-
id="password"
55-
type="password"
56-
name="password"
57-
value={data.password}
58-
className="mt-1 block w-full"
59-
autoComplete="current-password"
60-
onChange={(e) => setData("password", e.target.value)}
61-
/>
62-
63-
<InputError message={errors.password} className="mt-2" />
64-
</div>
65-
66-
<div className="block mt-4">
67-
<label className="flex items-center">
68-
<Checkbox
69-
name="remember"
70-
checked={data.remember}
71-
onChange={(e) =>
72-
setData("remember", e.target.checked)
73-
}
74-
/>
75-
<span className="ms-2 text-sm text-slate-600 dark:text-slate-400">
76-
Remember me
77-
</span>
78-
</label>
79-
</div>
80-
81-
<div className="flex items-center justify-end mt-4">
82-
<PrimaryButton className="ms-4" disabled={processing}>
83-
Log in
84-
</PrimaryButton>
85-
</div>
86-
</form>
14+
<LoginForm />
8715
</GuestLayout>
8816
);
8917
}
+3-71
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,12 @@
1-
import { useEffect, FormEventHandler } from "react";
1+
import ChangePasswordForm from "@/Components/forms/ChangePasswordForm";
22
import GuestLayout from "@/Layouts/GuestLayout";
3-
import InputError from "@/Components/InputError";
4-
import InputLabel from "@/Components/InputLabel";
5-
import PrimaryButton from "@/Components/PrimaryButton";
6-
import TextInput from "@/Components/TextInput";
7-
import { Head, useForm } from "@inertiajs/react";
3+
import { Head } from "@inertiajs/react";
84

95
export default function ResetPassword() {
10-
const { data, setData, post, processing, errors, reset } = useForm({
11-
password: "",
12-
password_confirmation: "",
13-
});
14-
15-
useEffect(() => {
16-
return () => {
17-
reset("password", "password_confirmation");
18-
};
19-
}, []);
20-
21-
const submit: FormEventHandler = (e) => {
22-
e.preventDefault();
23-
24-
post(route("password.update"));
25-
};
26-
276
return (
287
<GuestLayout>
298
<Head title="Reset Password" />
30-
31-
<form onSubmit={submit}>
32-
<div className="mt-4">
33-
<InputLabel htmlFor="password" value="New Password" />
34-
35-
<TextInput
36-
id="password"
37-
type="password"
38-
name="password"
39-
value={data.password}
40-
className="mt-1 block w-full"
41-
autoComplete="new-password"
42-
isFocused={true}
43-
onChange={(e) => setData("password", e.target.value)}
44-
/>
45-
46-
<InputError message={errors.password} className="mt-2" />
47-
</div>
48-
49-
<div className="mt-4">
50-
<InputLabel
51-
htmlFor="password_confirmation"
52-
value="Confirm Password"
53-
/>
54-
55-
<TextInput
56-
type="password"
57-
name="password_confirmation"
58-
value={data.password_confirmation}
59-
className="mt-1 block w-full"
60-
autoComplete="new-password"
61-
onChange={(e) =>
62-
setData("password_confirmation", e.target.value)
63-
}
64-
/>
65-
66-
<InputError
67-
message={errors.password_confirmation}
68-
className="mt-2"
69-
/>
70-
</div>
71-
72-
<div className="flex items-center justify-end mt-4">
73-
<PrimaryButton className="ms-4" disabled={processing}>
74-
Reset Password
75-
</PrimaryButton>
76-
</div>
77-
</form>
9+
<ChangePasswordForm />
7810
</GuestLayout>
7911
);
8012
}

0 commit comments

Comments
 (0)