'use client'; import { useState, useEffect } from "react"; import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Card, CardContent } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { SendIcon, Loader2, GlobeIcon, ChevronDownIcon, Ship } from "lucide-react"; import { useToast } from "@/hooks/use-toast"; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem } from "@/components/ui/dropdown-menu" import { useTranslation } from '@/i18n/client' import { useRouter, useParams, usePathname, useSearchParams } from 'next/navigation'; import { InputOTP, InputOTPGroup, InputOTPSlot, } from "@/components/ui/input-otp" import { signIn } from 'next-auth/react'; import { sendOtpAction } from "@/lib/users/send-otp"; import { verifyTokenAction } from "@/lib/users/verifyToken"; import { buttonVariants } from "@/components/ui/button" import Link from "next/link" import Image from 'next/image'; // 추가: Image 컴포넌트 import import { KnoxSSOButton } from './saml-login-button'; // SAML 로그인 버튼 import export function LoginFormSHI({ className, ...props }: React.ComponentProps<"div">) { const params = useParams() || {}; const pathname = usePathname() || ''; const router = useRouter(); const searchParams = useSearchParams(); const token = searchParams?.get('token') || null; const lng = params.lng as string; const { t, i18n } = useTranslation(lng, 'login'); const { toast } = useToast(); const handleChangeLanguage = (lang: string) => { const segments = pathname.split('/'); segments[1] = lang; router.push(segments.join('/')); }; const currentLanguageText = i18n.language === 'ko' ? t('languages.korean') : t('languages.english'); const [email, setEmail] = useState(''); const [otpSent, setOtpSent] = useState(false); const [isLoading, setIsLoading] = useState(false); const [otp, setOtp] = useState(''); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsLoading(true); try { const result = await sendOtpAction(email, lng); if (result.success) { setOtpSent(true); toast({ title: t('otpSentTitle'), description: t('otpSentMessage'), }); } else { // Handle specific error types let errorMessage = t('defaultErrorMessage'); // You can handle different error types differently if (result.error === 'userNotFound') { errorMessage = t('userNotFoundMessage'); } toast({ title: t('errorTitle'), description: result.message || errorMessage, variant: 'destructive', }); } } catch (error) { // This will catch network errors or other unexpected issues console.error(error); toast({ title: t('errorTitle'), description: t('networkErrorMessage'), variant: 'destructive', }); } finally { setIsLoading(false); } }; async function handleOtpSubmit(e: React.FormEvent) { e.preventDefault(); setIsLoading(true); try { // next-auth의 Credentials Provider로 로그인 시도 const result = await signIn('credentials', { email, code: otp, redirect: false, // 커스텀 처리 위해 redirect: false }); if (result?.ok) { // 토스트 메시지 표시 toast({ title: t('loginSuccess'), description: t('youAreLoggedIn'), }); const callbackUrlParam = searchParams?.get('callbackUrl'); if (callbackUrlParam) { try { // URL 객체로 파싱 const callbackUrl = new URL(callbackUrlParam); // pathname + search만 사용 (호스트 제거) const relativeUrl = callbackUrl.pathname + callbackUrl.search; router.push(relativeUrl); } catch (e) { // 유효하지 않은 URL이면 그대로 사용 (이미 상대 경로일 수 있음) router.push(callbackUrlParam); } } else { // callbackUrl이 없으면 기본 대시보드로 리다이렉트 router.push(`/${lng}/evcp/report`); } } else { toast({ title: t('errorTitle'), description: t('defaultErrorMessage'), variant: 'destructive', }); } } catch (error) { console.error('Login error:', error); toast({ title: t('errorTitle'), description: t('defaultErrorMessage'), variant: 'destructive', }); } finally { setIsLoading(false); } } useEffect(() => { const verifyToken = async () => { if (!token) return; setIsLoading(true); try { const data = await verifyTokenAction(token); if (data.valid) { setOtpSent(true); setEmail(data.email ?? ''); } else { toast({ title: t('errorTitle'), description: t('invalidToken'), variant: 'destructive', }); } } catch (error) { toast({ title: t('errorTitle'), description: t('defaultErrorMessage'), variant: 'destructive', }); } finally { setIsLoading(false); } }; verifyToken(); }, [token, toast, t]); return (
{/* Left Content */}
{/* Top bar with Logo + eVCP (left) and "Request Vendor Repository" (right) */}
{/* logo */} eVCP
{/* Content section that occupies remaining space, centered vertically */}
{/* Your form container */}
{/* Here's your existing login/OTP forms: */} {!otpSent ? ( // ( */}
{/* */}

{t('loginMessage')}

setEmail(e.target.value)} />
{/* 구분선과 "Or continue with" 섹션 추가 */}
{t('orContinueWith')}
{/* SAML 로그인 버튼 - 로직 분리 */} {/* 언어 선택 드롭다운 */}
handleChangeLanguage(value)} > {t('languages.english')} {t('languages.korean')}
) : (

{t('loginMessage')}

setOtp(value)} >
handleChangeLanguage(value)} > {t('languages.english')} {t('languages.korean')}
)}
{t('termsMessage')} {t('termsOfService')} {t('and')} {t('privacyPolicy')}.
{/* Right BG 이미지 영역 - Image 컴포넌트로 수정 */}
{/* Image 컴포넌트로 대체 */}
Background image

“{t("blockquote")}”

{/* */}
) }