2025-08-21 09:41:46 +09:00
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
|
|
|
import { Eye, EyeOff, Loader2 } from "lucide-react";
|
|
|
|
|
import { LoginFormData } from "@/types/auth";
|
|
|
|
|
import { ErrorMessage } from "./ErrorMessage";
|
2025-11-13 16:06:39 +09:00
|
|
|
import { useRouter } from "next/navigation";
|
2025-08-21 09:41:46 +09:00
|
|
|
|
|
|
|
|
interface LoginFormProps {
|
|
|
|
|
formData: LoginFormData;
|
|
|
|
|
isLoading: boolean;
|
|
|
|
|
error: string;
|
|
|
|
|
showPassword: boolean;
|
|
|
|
|
onInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
|
|
|
onSubmit: (e: React.FormEvent) => void;
|
|
|
|
|
onTogglePassword: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 로그인 폼 컴포넌트
|
|
|
|
|
*/
|
|
|
|
|
export function LoginForm({
|
|
|
|
|
formData,
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
|
|
|
|
showPassword,
|
|
|
|
|
onInputChange,
|
|
|
|
|
onSubmit,
|
|
|
|
|
onTogglePassword,
|
|
|
|
|
}: LoginFormProps) {
|
2025-11-13 16:06:39 +09:00
|
|
|
const router = useRouter();
|
|
|
|
|
|
2025-08-21 09:41:46 +09:00
|
|
|
return (
|
|
|
|
|
<Card className="border-0 shadow-xl">
|
|
|
|
|
<CardHeader className="space-y-1">
|
|
|
|
|
<CardTitle className="text-center text-2xl">로그인</CardTitle>
|
|
|
|
|
<CardDescription className="text-center">계정 정보를 입력해주세요</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<ErrorMessage message={error} />
|
|
|
|
|
|
|
|
|
|
<form onSubmit={onSubmit} className="space-y-4">
|
|
|
|
|
{/* 연결 테스트 버튼 */}
|
|
|
|
|
|
|
|
|
|
{/* 사용자 ID */}
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<Label htmlFor="userId">사용자 ID</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="userId"
|
|
|
|
|
name="userId"
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="사용자 ID를 입력하세요"
|
|
|
|
|
value={formData.userId}
|
|
|
|
|
onChange={onInputChange}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
className="h-11"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 비밀번호 */}
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<Label htmlFor="password">비밀번호</Label>
|
|
|
|
|
<div className="relative">
|
|
|
|
|
<Input
|
|
|
|
|
id="password"
|
|
|
|
|
name="password"
|
|
|
|
|
type={showPassword ? "text" : "password"}
|
|
|
|
|
placeholder="비밀번호를 입력하세요"
|
|
|
|
|
value={formData.password}
|
|
|
|
|
onChange={onInputChange}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
className="h-11 pr-10"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={onTogglePassword}
|
|
|
|
|
className="absolute top-1/2 right-3 -translate-y-1/2 transform text-slate-400 transition-colors hover:text-slate-600"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
>
|
|
|
|
|
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 로그인 버튼 */}
|
|
|
|
|
<Button
|
|
|
|
|
type="submit"
|
|
|
|
|
className="h-11 w-full bg-slate-900 font-medium text-white hover:bg-slate-800"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
>
|
|
|
|
|
{isLoading ? (
|
|
|
|
|
<>
|
|
|
|
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
|
|
|
로그인 중...
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
"로그인"
|
|
|
|
|
)}
|
|
|
|
|
</Button>
|
2025-11-13 16:06:39 +09:00
|
|
|
|
|
|
|
|
{/* 회원가입 버튼 */}
|
|
|
|
|
<Button
|
|
|
|
|
type="button"
|
|
|
|
|
variant="outline"
|
|
|
|
|
className="h-11 w-full font-medium"
|
|
|
|
|
onClick={() => router.push("/signup")}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
>
|
|
|
|
|
회원가입
|
|
|
|
|
</Button>
|
2025-08-21 09:41:46 +09:00
|
|
|
</form>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
);
|
|
|
|
|
}
|