From 595a078ec89bf801c0dcfaf9e8d4ca6abb3d8ea7 Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Sat, 25 Apr 2026 05:27:07 +0300 Subject: [PATCH] feat: Add login/register page with form validation and token persistence - Add LoginPage component with toggle between login and registration modes - Add form state management for username, password, email, first_name, last_name fields - Add handleSubmit with api.auth.login/register integration and token storage (access_token, refresh_token) - Add error handling with red alert banner display - Add conditional rendering for registration fields (email, first_name, last_name) - Add loading state with --- apps/web-dashboard/src/app/login/page.tsx | 151 ++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 apps/web-dashboard/src/app/login/page.tsx diff --git a/apps/web-dashboard/src/app/login/page.tsx b/apps/web-dashboard/src/app/login/page.tsx new file mode 100644 index 0000000..fb975d3 --- /dev/null +++ b/apps/web-dashboard/src/app/login/page.tsx @@ -0,0 +1,151 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { api, setAuthToken, type LoginRequest, type RegisterRequest } from "@/lib/api"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; + +export default function LoginPage() { + const router = useRouter(); + const [isLogin, setIsLogin] = useState(true); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + + const [formData, setFormData] = useState({ + username: "", + password: "", + }); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setError(""); + + try { + if (isLogin) { + const response = await api.auth.login(formData as LoginRequest); + setAuthToken(response.access_token); + localStorage.setItem("refresh_token", response.refresh_token); + router.push("/"); + } else { + const response = await api.auth.register(formData as RegisterRequest); + setAuthToken(response.access_token); + localStorage.setItem("refresh_token", response.refresh_token); + router.push("/"); + } + } catch (err) { + setError(err instanceof Error ? err.message : "Authentication failed"); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

+ {isLogin ? "Login" : "Register"} +

+ + {error && ( +
+ {error} +
+ )} + +
+
+ + setFormData({ ...formData, username: e.target.value })} + required + className="w-full" + /> +
+ + {!isLogin && ( + <> +
+ + setFormData({ ...formData as RegisterRequest, email: e.target.value })} + required={!isLogin} + className="w-full" + /> +
+
+ + setFormData({ ...formData as RegisterRequest, first_name: e.target.value })} + required={!isLogin} + className="w-full" + /> +
+
+ + setFormData({ ...formData as RegisterRequest, last_name: e.target.value })} + required={!isLogin} + className="w-full" + /> +
+ + )} + +
+ + setFormData({ ...formData, password: e.target.value })} + required + className="w-full" + /> +
+ + +
+ +
+ +
+
+
+ ); +}