From c68ea1f26f513e4a11dfa023eb4c59df8dcf77c1 Mon Sep 17 00:00:00 2001 From: Thanh Quy - wolf <524H0124@student.tdtu.edu.vn> Date: Sun, 5 Apr 2026 16:16:48 +0700 Subject: [PATCH] Atomic for login page and register page --- app/(main)/login/page.tsx | 157 +----------------- app/(main)/payment/page.tsx | 1 + app/(main)/register/page.tsx | 23 +-- components/atoms/buttons/Button.tsx | 19 ++- components/atoms/buttons/Button.types.ts | 7 +- components/atoms/errors/Error.types.ts | 6 + components/atoms/errors/ErrorMessageLogin.tsx | 32 ++++ components/atoms/errors/index.ts | 4 + components/atoms/inputs/Input.types.ts | 9 + components/atoms/inputs/LoginInput.tsx | 56 +++++++ components/atoms/inputs/index.ts | 1 + .../molecules/cards/PaymentSummaryCard.tsx | 8 +- components/organisms/forms/LoginForm.tsx | 120 +++++++++++++ 13 files changed, 256 insertions(+), 187 deletions(-) create mode 100644 components/atoms/errors/Error.types.ts create mode 100644 components/atoms/errors/ErrorMessageLogin.tsx create mode 100644 components/atoms/errors/index.ts create mode 100644 components/atoms/inputs/LoginInput.tsx create mode 100644 components/organisms/forms/LoginForm.tsx diff --git a/app/(main)/login/page.tsx b/app/(main)/login/page.tsx index 7273a79..b801294 100644 --- a/app/(main)/login/page.tsx +++ b/app/(main)/login/page.tsx @@ -1,64 +1,10 @@ "use client"; -import { useAuth } from "@/lib/auth-context"; import { SHOP_INFO } from "@/lib/constants"; +import LoginForm from "@/components/organisms/forms/LoginForm"; import Image from "next/image"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; -import { FormEvent, useState } from "react"; export default function LoginPage() { - const router = useRouter(); - const { login } = useAuth(); - - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const [errors, setErrors] = useState({ - username: "", - password: "", - general: "", - }); - const [showPassword, setShowPassword] = useState(false); - - const validate = (): boolean => { - const newErrors = { username: "", password: "", general: "" }; - let isValid = true; - - if (!username.trim()) { - newErrors.username = "Vui lòng nhập tên đăng nhập"; - isValid = false; - } - - if (!password.trim()) { - newErrors.password = "Vui lòng nhập mật khẩu"; - isValid = false; - } else if (password.length < 4) { - newErrors.password = "Mật khẩu phải có ít nhất 4 ký tự"; - isValid = false; - } - - setErrors(newErrors); - return isValid; - }; - - const handleSubmit = (e: FormEvent) => { - e.preventDefault(); - - if (!validate()) return; - - const success = login(username, password); - - if (success) { - router.push("/"); - } else { - setErrors({ - username: "", - password: "", - general: "Tên đăng nhập hoặc mật khẩu không đúng", - }); - } - }; - return (
{/* Login Form Card */} @@ -82,106 +28,9 @@ export default function LoginPage() { Đăng nhập vào hệ thống

- - {/* Error Message */} - {errors.general && ( -
- - {errors.general} -
- )} - + {/* Login Form */} -
- {/* Username Input */} -
- -
- - { - setUsername(e.target.value); - setErrors({ ...errors, username: "", general: "" }); - }} - placeholder="admin / số điện thoại / tên nhân viên" - className={`text-foreground focus:ring-opacity-20 w-full rounded-xl border bg-white px-10 py-3 transition-all duration-150 outline-none placeholder:text-(--color-text-muted) focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary) lg:pl-11 ${errors.username ? "border-red-400" : "border-(--color-border)"} `} - /> -
- {errors.username && ( -

- - {errors.username} -

- )} -
- - {/* Password Input */} -
- -
- - { - setPassword(e.target.value); - setErrors({ ...errors, password: "", general: "" }); - }} - placeholder="Nhập mật khẩu" - className={`text-foreground focus:ring-opacity-20 w-full rounded-xl border bg-white px-10 py-3 pr-11 transition-all duration-150 outline-none placeholder:text-(--color-text-muted) focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary) lg:pl-11 ${errors.password ? "border-red-400" : "border-(--color-border)"} `} - /> - -
- {errors.password && ( -

- - {errors.password} -

- )} -
- - {/* Buttons */} -
- {/* Login Button */} - - - {/* Register Button */} - - Đăng ký tài khoản - -
-
+ {/* Demo Credentials Info */}
diff --git a/app/(main)/payment/page.tsx b/app/(main)/payment/page.tsx index febe020..f9377f4 100644 --- a/app/(main)/payment/page.tsx +++ b/app/(main)/payment/page.tsx @@ -104,6 +104,7 @@ export default function PaymentPage() { onClick={() => removeFromCart(item.id)} variant="danger" size="md" + style="payment" > Xóa sản phẩm diff --git a/app/(main)/register/page.tsx b/app/(main)/register/page.tsx index 807f836..b48bec8 100644 --- a/app/(main)/register/page.tsx +++ b/app/(main)/register/page.tsx @@ -6,6 +6,7 @@ import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { FormEvent, useState } from "react"; +import Button from "@/components/atoms/buttons/Button"; // Static OTP for demo (in production, this would be sent via SMS) const DEMO_OTP = "123456"; @@ -160,12 +161,9 @@ export default function RegisterPage() { {/* Buttons */}
{/* Submit Button */} - + {/* Back to Login */} {/* Submit Button */} - + {/* Back Button */} - +
{/* Resend OTP (disabled in demo) */} diff --git a/components/atoms/buttons/Button.tsx b/components/atoms/buttons/Button.tsx index d83b579..ab7c405 100644 --- a/components/atoms/buttons/Button.tsx +++ b/components/atoms/buttons/Button.tsx @@ -3,22 +3,21 @@ import type { ButtonProps } from "./Button.types"; export default function Button({ + style = "base", variant = "primary", size = "md", icon, iconPosition = "left", disabled = false, children, - className = "", - payment = false, ...props }: ButtonProps) { - const baseStyles = - "font-semibold rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center justify-center gap-1.5"; -// h-8 w-8 - const paymentStyles = - "inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl transition-colors"; - + const styles = { + base: "font-semibold rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center justify-center gap-1.5", + payment: "inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl transition-colors", + login: "w-full cursor-pointer rounded-xl py-3 font-semibold transition-all duration-150", + } + const variants = { primary: "bg-(--color-primary) text-white hover:bg-(--color-primary-dark) active:scale-95", @@ -26,6 +25,8 @@ export default function Button({ "border border-(--color-border) hover:bg-(--color-border-light) active:scale-95", danger: "bg-red-500 text-white hover:bg-red-600 active:scale-95", ghost: "bg-transparent hover:bg-(--color-border-light) active:scale-95", + primaryNoBorder: "border-none bg-(--color-primary) text-white hover:bg-(--color-primary-dark) active:scale-98", + bgWhite: "border-2 border-(--color-primary) bg-white text-(--color-primary) hover:bg-(--color-primary) hover:text-white active:scale-98", }; const sizes = { @@ -38,7 +39,7 @@ export default function Button({ return ( + ); + } + return ""; + } + + return ( +
+ +
+ + + {isPassword()} +
+
+ ); +} diff --git a/components/atoms/inputs/index.ts b/components/atoms/inputs/index.ts index e4016df..b2b03aa 100644 --- a/components/atoms/inputs/index.ts +++ b/components/atoms/inputs/index.ts @@ -5,4 +5,5 @@ export type { TextInputProps, SearchInputProps, TextareaProps, + LoginInputProps, } from "./Input.types"; diff --git a/components/molecules/cards/PaymentSummaryCard.tsx b/components/molecules/cards/PaymentSummaryCard.tsx index d0fdaf4..79214d0 100644 --- a/components/molecules/cards/PaymentSummaryCard.tsx +++ b/components/molecules/cards/PaymentSummaryCard.tsx @@ -36,7 +36,7 @@ export default function PaymentSummaryCard({
+ + {/* Register Button */} + + Đăng ký tài khoản + +
+ +
+ ); +}