feat: form hook

This commit is contained in:
2025-04-23 12:32:27 +03:00
parent 2a7d41bba5
commit c629f0dcf8
7 changed files with 62 additions and 41 deletions

View File

@@ -1,5 +1,5 @@
@reference '../../index.scss';
.button {
@apply rounded-4xl px-4 py-3 text-xl text-black shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-colors hover:cursor-pointer;
@apply rounded-4xl px-4 py-3 text-xl text-black shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-colors hover:cursor-pointer focus:outline-1;
}

View File

@@ -16,6 +16,7 @@ interface ButtonProps {
color?: "primary" | "secondary" | "red";
onClick?: () => void;
className?: string;
type?: "button" | "submit";
}
const Button: FunctionComponent<ButtonProps> = ({
@@ -23,9 +24,10 @@ const Button: FunctionComponent<ButtonProps> = ({
onClick = () => {},
color = "primary",
className = "",
type = "button",
}) => {
return (
<button type="button" class={button({ color: color, class: className })} onClick={onClick}>
<button type={type} class={button({ color: color, class: className })} onClick={onClick}>
{children}
</button>
);

View File

@@ -1,5 +1,5 @@
import { cn } from "@/utils/class-merge";
import { FunctionComponent, Ref } from "preact";
import { forwardRef, HTMLProps, useEffect } from "preact/compat";
import { tv } from "tailwind-variants";
import classes from "./Input.module.scss";
@@ -20,29 +20,19 @@ const input = tv({
},
});
interface InputProps {
isPassword?: boolean;
placeholder?: string;
interface InputProps extends HTMLProps<HTMLInputElement> {
textAlign?: "center" | "left";
error?: string;
textRef?: Ref<HTMLInputElement> | null;
}
const Input: FunctionComponent<InputProps> = ({
isPassword = false,
placeholder = "",
textAlign,
error = "",
textRef = null,
}: InputProps) => {
const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
const { textAlign, error, type = "text", ...rest } = props;
useEffect(() => {
console.log(`error: ${error}`);
}, [error]);
return (
<div class="flex w-full flex-col items-center gap-1">
<input
type={isPassword ? "password" : "text"}
class={input({ "text-align": textAlign, "border-error": error !== "" })}
placeholder={placeholder}
ref={textRef}
/>
<input class={input({ "text-align": textAlign, "border-error": !!error })} ref={ref} type={type} {...rest} />
<p
class={cn("invisible h-10 w-[80%] text-center text-[0.7rem] break-words text-red-500", {
visible: error !== "",
@@ -52,6 +42,8 @@ const Input: FunctionComponent<InputProps> = ({
</p>
</div>
);
};
});
Input.displayName = "AHInput";
export default Input;

4
src/pages/login.dto.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface ILoginForm {
login: string;
password: string;
}

View File

@@ -6,7 +6,9 @@ import { useAppContext } from "@/providers/AuthProvider";
import { FunctionComponent } from "preact";
import { useLocation } from "preact-iso";
import "preact/debug";
import { useRef, useState } from "preact/hooks";
import { useState } from "preact/hooks";
import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { ILoginForm } from "./login.dto";
import classes from "./login.module.scss";
const testUser = {
@@ -16,37 +18,54 @@ const testUser = {
const LoginPage: FunctionComponent = () => {
const { isLoggedIn } = useAppContext();
const { route } = useLocation();
const loginRef = useRef<HTMLInputElement | null>(null);
const passwordRef = useRef<HTMLInputElement | null>(null);
const [loginError, setLoginError] = useState("");
const [passwordError, setPasswordError] = useState("");
const login = async () => {
if (!loginRef.current || !passwordRef.current) return;
setLoginError("");
setPasswordError("");
if (!loginRef.current.value.length || !passwordRef.current.value.length) {
if (!loginRef.current.value.length) setLoginError("Введите логин");
if (!passwordRef.current.value.length) setPasswordError("Введите пароль");
return;
}
if (loginRef.current.value !== testUser.login || passwordRef.current.value !== testUser.password) {
setLoginError("Неправильный логин или пароль");
const login: SubmitHandler<ILoginForm> = async (data) => {
console.log(data);
if (data.login !== testUser.login || data.password !== testUser.password) {
setError("login", { message: "Неверный" }); //TODO: не показывает ошибку
return;
}
isLoggedIn.value = true;
localStorage.setItem("loggedIn", "true");
route("/profile/tasks", true);
};
const { control, handleSubmit, formState, setError } = useForm({
defaultValues: {
login: "",
password: "",
},
mode: "onChange",
});
if (isLoggedIn.value) route("/profile/tasks", true);
return !isLoggedIn.value ? (
<div class={classes.login_container}>
<div class={classes.login_card}>
<p class={classes.login_card_name}>Антихвост</p>
<Input placeholder="Логин" textAlign="center" textRef={loginRef} error={loginError} />
<Input isPassword placeholder="Пароль" textAlign="center" textRef={passwordRef} error={passwordError} />
<Button color="secondary" onClick={login}>
Войти
</Button>
<form onSubmit={handleSubmit((data) => login(data))}>
<Controller
name="login"
control={control}
rules={{
required: "Введите логин",
}}
render={({ field }) => (
<Input placeholder="Логин" textAlign="center" error={formState.errors.login?.message} {...field} />
)}
/>
<Controller
name="password"
control={control}
rules={{
required: "Введите пароль",
}}
render={({ field }) => (
<Input placeholder="Пароль" textAlign="center" type="password" error={passwordError} {...field} />
)}
/>
<Button type="submit" color="secondary" className="w-full">
Войти
</Button>
</form>
</div>
</div>
) : (