diff options
Diffstat (limited to 'components/login')
| -rw-r--r-- | components/login/login-form copy 2.tsx | 470 | ||||
| -rw-r--r-- | components/login/login-form copy.tsx | 468 | ||||
| -rw-r--r-- | components/login/saml-login-button.tsx | 8 |
3 files changed, 7 insertions, 939 deletions
diff --git a/components/login/login-form copy 2.tsx b/components/login/login-form copy 2.tsx deleted file mode 100644 index d5ac01b9..00000000 --- a/components/login/login-form copy 2.tsx +++ /dev/null @@ -1,470 +0,0 @@ -'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">“{t("blockquote")}”</p> - {/* <footer className="text-sm">SHI</footer> */} - </blockquote> - </div> - </div> - </div> - ) -}
\ No newline at end of file diff --git a/components/login/login-form copy.tsx b/components/login/login-form copy.tsx deleted file mode 100644 index ef9eba10..00000000 --- a/components/login/login-form copy.tsx +++ /dev/null @@ -1,468 +0,0 @@ -'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 } 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 - -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 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'); - - // 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'), - }); - - router.push(`/${lng}/partners/dashboard`); - - } 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 { - 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"> - <div className="flex items-center space-x-2"> - {/* <img - src="/images/logo.png" - alt="logo" - className="h-8 w-auto" - /> */} - <Ship className="w-4 h-4" /> - <span className="text-md font-bold">eVCP</span> - </div> - <Link - href="/partners/repository" - className={cn(buttonVariants({ variant: "ghost" }))} - > - <InfoIcon className="w-4 h-4 mr-1" /> - {'업체 등록 신청'} - </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]"> - - {/* Here's your existing login/OTP forms: */} - {/* {!otpSent ? ( */} - - {/* <form onSubmit={handleSubmit} className="p-6 md:p-8"> */} - <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-xs text-muted-foreground mt-2"> - {'등록된 업체만 로그인하실 수 있습니다. 아직 등록되지 않은 업체라면 상단의 업체 등록 신청 버튼을 이용해주세요.'} - </p> - </div> - - {/* S-Gips 로그인 폼이 표시되지 않을 때만 이메일 입력 필드 표시 */} - {!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}> - {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-Gips 로그인 버튼 */} - <Button - type="button" - className="w-full" - // variant="" - onClick={() => setShowCredentialsForm(true)} - > - S-Gips로 로그인하기 - </Button> - - {/* 업체 등록 안내 링크 추가 */} - <Button - type="button" - variant="link" - className="text-blue-600 hover:text-blue-800" - onClick={goToVendorRegistration} - > - {'신규 업체이신가요? 여기서 등록하세요'} - </Button> - </> - )} - - {/* ID/비밀번호 로그인 폼 - 버튼 클릭 시에만 표시 */} - {showCredentialsForm && ( - <> - <div className="grid gap-4"> - <Input - id="username" - type="text" - placeholder="S-Gips 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> - </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> - {/* ) : ( - <form onSubmit={handleOtpSubmit} className="flex flex-col gap-4 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> - </div> - <div className="grid gap-2 justify-center"> - <InputOTP - maxLength={6} - value={otp} - onChange={(value) => setOtp(value)} - > - <InputOTPGroup> - <InputOTPSlot index={0} /> - <InputOTPSlot index={1} /> - <InputOTPSlot index={2} /> - <InputOTPSlot index={3} /> - <InputOTPSlot index={4} /> - <InputOTPSlot index={5} /> - </InputOTPGroup> - </InputOTP> - </div> - <Button type="submit" className="w-full" disabled={isLoading}> - {isLoading ? t('verifying') : t('verifyOtp')} - </Button> - <div className="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">“{t("blockquote")}”</p> - {/* <footer className="text-sm">SHI</footer> */} - </blockquote> - </div> - </div> - </div> - ) -}
\ No newline at end of file diff --git a/components/login/saml-login-button.tsx b/components/login/saml-login-button.tsx index c0aae0f1..02825e2f 100644 --- a/components/login/saml-login-button.tsx +++ b/components/login/saml-login-button.tsx @@ -32,8 +32,14 @@ export function SAMLLoginButton({ try { setIsLoading(true) + // 현재 페이지 경로를 RelayState로 설정 (로그인 후 이 페이지로 돌아옴) + const currentPath = window.location.pathname + window.location.search + const relayState = encodeURIComponent(currentPath) + + console.log('Setting RelayState to:', currentPath) + // API 엔드포인트를 통해 SAML AuthnRequest URL 생성 - const response = await fetch('/api/auth/saml/authn-request', { + const response = await fetch(`/api/auth/saml/authn-request?relayState=${relayState}`, { method: 'GET', headers: { 'Content-Type': 'application/json', |
