From ef4c533ebacc2cdc97e518f30e9a9350004fcdfb Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Mon, 28 Apr 2025 02:13:30 +0000
Subject: ~20250428 작업사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/login/login-form copy 2.tsx | 470 +++++++++++++++++++++++++++++++++
 1 file changed, 470 insertions(+)
 create mode 100644 components/login/login-form copy 2.tsx

(limited to 'components/login/login-form copy 2.tsx')

diff --git a/components/login/login-form copy 2.tsx b/components/login/login-form copy 2.tsx
new file mode 100644
index 00000000..d5ac01b9
--- /dev/null
+++ b/components/login/login-form copy 2.tsx	
@@ -0,0 +1,470 @@
+'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, InfoIcon, ArrowRight } 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';
+import {
+    Alert,
+    AlertDescription,
+    AlertTitle,
+} from "@/components/ui/alert";
+
+export function LoginForm({
+    className,
+    ...props
+}: React.ComponentProps<"div">) {
+
+    const params = useParams() || {};
+    const pathname = usePathname() || '';
+    const router = useRouter();
+    const searchParams = useSearchParams();
+    const token = searchParams?.get('token') || null;
+    const [showCredentialsForm, setShowCredentialsForm] = useState(false);
+    // 새로운 상태: 업체 등록 안내 표시 여부
+    const [showVendorRegistrationInfo, setShowVendorRegistrationInfo] = useState(false);
+
+    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 [username, setUsername] = useState('');
+    const [password, setPassword] = useState('');
+
+    // 업체 등록 페이지로 이동하는 함수
+    const goToVendorRegistration = () => {
+        router.push(`/${lng}/partners/repository`);
+    };
+
+    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');
+
+                // 업체 미등록 사용자 에러 처리
+                if (result.error === 'userNotFound' || result.error === 'vendorNotRegistered') {
+                    setShowVendorRegistrationInfo(true);
+                    errorMessage = t('vendorNotRegistered') || '등록된 업체가 아닙니다. 먼저 업체 등록 신청을 해주세요.';
+                }
+
+                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'),
+                });
+
+                router.push(`/${lng}/partners/dashboard`);
+            } else {
+                // 로그인 실패 시 에러 메시지에 업체 등록 관련 정보 포함
+                if (result?.error === 'vendorNotRegistered') {
+                    setShowVendorRegistrationInfo(true);
+                    toast({
+                        title: t('errorTitle'),
+                        description: t('vendorNotRegistered') || '등록된 업체가 아닙니다. 먼저 업체 등록 신청을 해주세요.',
+                        variant: 'destructive',
+                    });
+                } 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);
+        }
+    }
+
+    // 새로운 로그인 처리 함수 추가
+    const handleCredentialsLogin = async () => {
+        if (!username || !password) {
+            toast({
+                title: t('errorTitle'),
+                description: t('credentialsRequired'),
+                variant: 'destructive',
+            });
+            return;
+        }
+
+        setIsLoading(true);
+
+        try {
+            // next-auth의 다른 credentials provider로 로그인 시도
+            const result = await signIn('credentials-password', {
+                username,
+                password,
+                redirect: false,
+            });
+
+            if (result?.ok) {
+                toast({
+                    title: t('loginSuccess'),
+                    description: t('youAreLoggedIn'),
+                });
+
+                router.push(`/${lng}/partners/dashboard`);
+            } else {
+                // 로그인 실패 시 업체 등록 관련 정보 표시 여부 결정
+                if (result?.error === 'vendorNotRegistered') {
+                    setShowVendorRegistrationInfo(true);
+                    toast({
+                        title: t('errorTitle'), 
+                        description: t('vendorNotRegistered') || '등록된 업체가 아닙니다. 먼저 업체 등록 신청을 해주세요.',
+                        variant: 'destructive',
+                    });
+                } else {
+                    toast({
+                        title: t('errorTitle'),
+                        description: t('invalidCredentials'),
+                        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 (
+        <div className="container relative flex h-screen flex-col items-center justify-center md:grid lg:max-w-none lg:grid-cols-2 lg:px-0">
+            {/* Left Content */}
+            <div className="flex flex-col w-full h-screen lg:p-2">
+                {/* Top bar with Logo + eVCP (left) and "Request Vendor Repository" (right) */}
+                <div className="flex items-center justify-between p-4">
+                    <div className="flex items-center space-x-2">
+                        <Ship className="w-4 h-4" />
+                        <span className="text-md font-bold">eVCP</span>
+                    </div>
+                    
+                    {/* 업체 등록 신청 버튼 - 가시성 향상을 위해 variant 변경 */}
+                    <Link
+                        href={`/${lng}/partners/repository`}
+                        className={cn(buttonVariants({ variant: "outline" }), "border-blue-500 text-blue-600 hover:bg-blue-50")}
+                    >
+                        <InfoIcon className="w-4 h-4 mr-2" />
+                        {t('registerVendor') || '업체 등록 신청'}
+                    </Link>
+                </div>
+
+                {/* Content section that occupies remaining space, centered vertically */}
+                <div className="flex-1 flex items-center justify-center">
+                    {/* Your form container */}
+                    <div className="mx-auto w-full flex flex-col space-y-6 sm:w-[350px]">
+                        {/* 업체 등록 안내 알림 - 특정 상황에서만 표시 */}
+                        {showVendorRegistrationInfo && (
+                            <Alert className="border-blue-500 bg-blue-50">
+                                <InfoIcon className="h-4 w-4 text-blue-600" />
+                                <AlertTitle className="text-blue-700">
+                                    {t('vendorRegistrationRequired') || '업체 등록이 필요합니다'}
+                                </AlertTitle>
+                                <AlertDescription className="text-blue-600">
+                                    {t('vendorRegistrationMessage') || '로그인하시려면 먼저 업체 등록이 필요합니다. 아래 버튼을 클릭하여 등록을 진행해주세요.'}
+                                </AlertDescription>
+                                <Button 
+                                    onClick={goToVendorRegistration}
+                                    className="mt-2 w-full bg-blue-600 hover:bg-blue-700 text-white"
+                                >
+                                    {t('goToVendorRegistration') || '업체 등록 신청하기'} 
+                                    <ArrowRight className="ml-2 h-4 w-4" />
+                                </Button>
+                            </Alert>
+                        )}
+
+                        <form onSubmit={handleOtpSubmit} className="p-6 md:p-8">
+                            <div className="flex flex-col gap-6">
+                                <div className="flex flex-col items-center text-center">
+                                    <h1 className="text-2xl font-bold">{t('loginMessage')}</h1>
+                                    
+                                    {/* 설명 텍스트 추가 - 업체 등록 관련 안내 */}
+                                    <p className="text-sm text-muted-foreground mt-2">
+                                        {t('loginDescription') || '등록된 업체만 로그인하실 수 있습니다. 아직 등록되지 않은 업체라면 상단의 업체 등록 신청 버튼을 이용해주세요.'}
+                                    </p>
+                                </div>
+
+                                {/* S-chips 로그인 폼이 표시되지 않을 때만 이메일 입력 필드 표시 */}
+                                {!showCredentialsForm && (
+                                    <>
+                                        <div className="grid gap-2">
+                                            <Input
+                                                id="email"
+                                                type="email"
+                                                placeholder={t('email')}
+                                                required
+                                                className="h-10"
+                                                value={email}
+                                                onChange={(e) => setEmail(e.target.value)}
+                                            />
+                                        </div>
+                                        <Button 
+                                            type="submit" 
+                                            className="w-full" 
+                                            variant="samsung" 
+                                            disabled={isLoading}
+                                            onClick={handleSubmit}
+                                        >
+                                            {isLoading ? t('sending') : t('ContinueWithEmail')}
+                                        </Button>
+
+                                        {/* 구분선과 "Or continue with" 섹션 추가 */}
+                                        <div className="relative">
+                                            <div className="absolute inset-0 flex items-center">
+                                                <span className="w-full border-t"></span>
+                                            </div>
+                                            <div className="relative flex justify-center text-xs uppercase">
+                                                <span className="bg-background px-2 text-muted-foreground">
+                                                    {t('orContinueWith')}
+                                                </span>
+                                            </div>
+                                        </div>
+
+                                        {/* S-chips 로그인 버튼 */}
+                                        <Button
+                                            type="button"
+                                            className="w-full"
+                                            onClick={() => setShowCredentialsForm(true)}
+                                        >
+                                            S-Gips로 로그인하기
+                                        </Button>
+                                        
+                                        {/* 업체 등록 안내 링크 추가 */}
+                                        <Button
+                                            type="button"
+                                            variant="link"
+                                            className="text-blue-600 hover:text-blue-800"
+                                            onClick={goToVendorRegistration}
+                                        >
+                                            {t('newVendor') || '신규 업체이신가요? 여기서 등록하세요'}
+                                        </Button>
+                                    </>
+                                )}
+
+                                {/* ID/비밀번호 로그인 폼 - 버튼 클릭 시에만 표시 */}
+                                {showCredentialsForm && (
+                                    <>
+                                        <div className="grid gap-4">
+                                            <Input
+                                                id="username"
+                                                type="text"
+                                                placeholder="S-chips ID"
+                                                className="h-10"
+                                                value={username}
+                                                onChange={(e) => setUsername(e.target.value)}
+                                            />
+                                            <Input
+                                                id="password"
+                                                type="password"
+                                                placeholder="비밀번호"
+                                                className="h-10"
+                                                value={password}
+                                                onChange={(e) => setPassword(e.target.value)}
+                                            />
+                                            <Button
+                                                type="button"
+                                                className="w-full"
+                                                variant="samsung"
+                                                onClick={handleCredentialsLogin}
+                                                disabled={isLoading}
+                                            >
+                                                {isLoading ? "로그인 중..." : "로그인"}
+                                            </Button>
+
+                                            {/* 뒤로 가기 버튼 */}
+                                            <Button
+                                                type="button"
+                                                variant="ghost"
+                                                className="w-full text-sm"
+                                                onClick={() => setShowCredentialsForm(false)}
+                                            >
+                                                이메일로 로그인하기
+                                            </Button>
+                                            
+                                            {/* 업체 등록 안내 링크 추가 */}
+                                            <Button
+                                                type="button"
+                                                variant="link"
+                                                className="text-blue-600 hover:text-blue-800"
+                                                onClick={goToVendorRegistration}
+                                            >
+                                                {t('newVendor') || '신규 업체이신가요? 여기서 등록하세요'}
+                                            </Button>
+                                        </div>
+                                    </>
+                                )}
+
+                                <div className="text-center text-sm mx-auto">
+                                    <DropdownMenu>
+                                        <DropdownMenuTrigger asChild>
+                                            <Button variant="ghost" className="flex items-center gap-2">
+                                                <GlobeIcon className="h-4 w-4" />
+                                                <span>{currentLanguageText}</span>
+                                                <ChevronDownIcon className="h-4 w-4" />
+                                            </Button>
+                                        </DropdownMenuTrigger>
+                                        <DropdownMenuContent align="end">
+                                            <DropdownMenuRadioGroup
+                                                value={i18n.language}
+                                                onValueChange={(value) => handleChangeLanguage(value)}
+                                            >
+                                                <DropdownMenuRadioItem value="en">
+                                                    {t('languages.english')}
+                                                </DropdownMenuRadioItem>
+                                                <DropdownMenuRadioItem value="ko">
+                                                    {t('languages.korean')}
+                                                </DropdownMenuRadioItem>
+                                            </DropdownMenuRadioGroup>
+                                        </DropdownMenuContent>
+                                    </DropdownMenu>
+                                </div>
+                            </div>
+                        </form>
+
+                        <div className="text-balance text-center text-xs text-muted-foreground [&_a]:underline [&_a]:underline-offset-4 hover:[&_a]:text-primary">
+                            {t('termsMessage')} <a href="#">{t('termsOfService')}</a> {t('and')}
+                            <a href="#">{t('privacyPolicy')}</a>.
+                        </div>
+                    </div>
+                </div>
+            </div>
+
+            {/* Right BG 이미지 영역 - Image 컴포넌트로 수정 */}
+            <div className="relative hidden h-full flex-col p-10 text-white dark:border-r md:flex">
+                {/* Image 컴포넌트로 대체 */}
+                <div className="absolute inset-0">
+                    <Image
+                        src="/images/02.jpg"
+                        alt="Background image"
+                        fill
+                        priority
+                        sizes="(max-width: 1024px) 100vw, 50vw"
+                        className="object-cover"
+                    />
+                </div>
+                <div className="relative z-10 mt-auto">
+                    <blockquote className="space-y-2">
+                        <p className="text-sm">&ldquo;{t("blockquote")}&rdquo;</p>
+                        {/* <footer className="text-sm">SHI</footer> */}
+                    </blockquote>
+                </div>
+            </div>
+        </div>
+    )
+}
\ No newline at end of file
-- 
cgit v1.2.3

