로그인 페이지 완성
This commit is contained in:
46
src/app/login/LoginErrorModal.tsx
Normal file
46
src/app/login/LoginErrorModal.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type LoginErrorModalProps = {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LoginErrorModal({ open, onClose }: LoginErrorModalProps) {
|
||||||
|
if (!open) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="닫기"
|
||||||
|
className="absolute inset-0 bg-black/40 cursor-default"
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="login-error-title"
|
||||||
|
className="relative bg-white box-border flex flex-col items-stretch justify-start p-6 rounded-[8px] min-w-[500px] max-w-[calc(100%-48px)]"
|
||||||
|
>
|
||||||
|
<div className="text-[18px] leading-normal font-semibold text-neutral-700 mb-8" id="login-error-title">
|
||||||
|
아이디 또는 비밀번호가 일치하지 않습니다.
|
||||||
|
<br />
|
||||||
|
확인 후 다시 시도해 주세요.
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-end gap-[8px]">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onClose}
|
||||||
|
className="h-[40px] min-w-20 px-[12px] rounded-[8px] bg-active-button text-white text-[16px] font-semibold cursor-pointer"
|
||||||
|
>
|
||||||
|
확인
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
53
src/app/login/loginoption.tsx
Normal file
53
src/app/login/loginoption.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
"use client";
|
||||||
|
import React from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
type LoginOptionProps = {
|
||||||
|
onClick?: () => void;
|
||||||
|
className?: string;
|
||||||
|
loginErrorModalEnabled?: boolean;
|
||||||
|
setLoginErrorModalEnabled?: (enabled: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LoginOption({
|
||||||
|
className,
|
||||||
|
loginErrorModalEnabled,
|
||||||
|
setLoginErrorModalEnabled,
|
||||||
|
}: LoginOptionProps) {
|
||||||
|
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
className={`fixed bottom-2 right-2 bg-red-400 cursor-pointer rounded-full w-[40px] h-[40px] shadow-xl z-100`}
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
{ isOpen && (
|
||||||
|
<div className="fixed inset-0 flex items-center justify-center z-50">
|
||||||
|
<div className="w-[500px] h-[600px] flex bg-white/80 p-10 border rounded-lg">
|
||||||
|
<ul className="flex flex-col gap-4">
|
||||||
|
<li className="flex items-center justify-between">
|
||||||
|
<p className="mr-4">login error modal</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="login error modal 토글"
|
||||||
|
aria-pressed={!!loginErrorModalEnabled}
|
||||||
|
onClick={() => setLoginErrorModalEnabled?.(!loginErrorModalEnabled)}
|
||||||
|
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${loginErrorModalEnabled ? 'bg-blue-600' : 'bg-gray-300'}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`inline-block h-5 w-5 transform rounded-full bg-white transition ${loginErrorModalEnabled ? 'translate-x-5' : 'translate-x-1'}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,25 +3,48 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import MainLogo from "@/app/svgs/mainlogosvg"
|
import MainLogo from "@/app/svgs/mainlogosvg"
|
||||||
|
import LoginCheckboxActiveSvg from "@/app/svgs/logincheckboxactivesvg";
|
||||||
|
import LoginCheckboxInactiveSvg from "@/app/svgs/logincheckboxinactivesvg";
|
||||||
|
import LoginInputSvg from "@/app/svgs/inputformx";
|
||||||
|
import LoginErrorModal from "./LoginErrorModal";
|
||||||
|
import LoginOption from "@/app/login/loginoption";
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const [userId, setUserId] = useState("");
|
const [userId, setUserId] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [rememberId, setRememberId] = useState(false);
|
const [rememberId, setRememberId] = useState(false);
|
||||||
const [autoLogin, setAutoLogin] = useState(false);
|
const [autoLogin, setAutoLogin] = useState(false);
|
||||||
|
const [isUserIdFocused, setIsUserIdFocused] = useState(false);
|
||||||
|
const [isPasswordFocused, setIsPasswordFocused] = useState(false);
|
||||||
|
const [isLoginErrorOpen, setIsLoginErrorOpen] = useState(false);
|
||||||
|
const [idError, setIdError] = useState("");
|
||||||
|
const [passwordError, setPasswordError] = useState("");
|
||||||
|
|
||||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
/* todo */
|
// 실제 로그인 API 연동 전까지는 실패 모달을 노출합니다.
|
||||||
console.log({ userId, password, rememberId, autoLogin });
|
// API 연동 시 결과에 따라 성공/실패 분기에서 setIsLoginErrorOpen(true) 호출로 교체하세요.
|
||||||
|
// if (userId.trim().length > 0 && password.trim().length > 0) {
|
||||||
|
// setIsLoginErrorOpen(true);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen w-full flex flex-col items-center justify-between">
|
<div className="min-h-screen w-full flex flex-col items-center justify-between">
|
||||||
<div></div>
|
|
||||||
|
<LoginErrorModal
|
||||||
|
open={isLoginErrorOpen}
|
||||||
|
onClose={() => setIsLoginErrorOpen(false)}
|
||||||
|
/>
|
||||||
|
<LoginOption
|
||||||
|
onClick={() => setIsLoginErrorOpen(true)}
|
||||||
|
loginErrorModalEnabled={isLoginErrorOpen}
|
||||||
|
setLoginErrorModalEnabled={setIsLoginErrorOpen}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="rounded-xl bg-white max-w-[560px] px-[40px] w-full">
|
<div className="rounded-xl bg-white max-w-[560px] px-[40px] w-full">
|
||||||
{/* 로고 영역 */}
|
{/* 로고 영역 */}
|
||||||
<div className="my-10 flex flex-col items-center">
|
<div className="my-15 flex flex-col items-center">
|
||||||
<div className="mb-[7px]">
|
<div className="mb-[7px]">
|
||||||
<MainLogo/>
|
<MainLogo/>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,10 +54,10 @@ export default function LoginPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 폼 */}
|
{/* 폼 */}
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* 아이디 */}
|
{/* 아이디 */}
|
||||||
<div>
|
<div className="relative">
|
||||||
<label htmlFor="userId" className="sr-only">
|
<label htmlFor="userId" className="sr-only">
|
||||||
아이디
|
아이디
|
||||||
</label>
|
</label>
|
||||||
@@ -43,17 +66,33 @@ export default function LoginPage() {
|
|||||||
name="userId"
|
name="userId"
|
||||||
value={userId}
|
value={userId}
|
||||||
onChange={(e) => setUserId(e.target.value)}
|
onChange={(e) => setUserId(e.target.value)}
|
||||||
|
onFocus={() => setIsUserIdFocused(true)}
|
||||||
|
onBlur={() => setIsUserIdFocused(false)}
|
||||||
placeholder="아이디 (이메일)"
|
placeholder="아이디 (이메일)"
|
||||||
className="
|
className="
|
||||||
h-[56px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40
|
h-[56px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40
|
||||||
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
||||||
focus:appearance-none focus:border-neutral-700
|
focus:appearance-none focus:border-neutral-700
|
||||||
text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text
|
text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text
|
||||||
|
pr-[40px]
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
{userId.trim().length > 0 && isUserIdFocused && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setUserId("");
|
||||||
|
}}
|
||||||
|
aria-label="입력 지우기"
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer"
|
||||||
|
>
|
||||||
|
<LoginInputSvg />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* 비밀번호 */}
|
{/* 비밀번호 */}
|
||||||
<div>
|
<div className="relative">
|
||||||
<label htmlFor="password" className="sr-only">
|
<label htmlFor="password" className="sr-only">
|
||||||
비밀번호
|
비밀번호
|
||||||
</label>
|
</label>
|
||||||
@@ -63,27 +102,47 @@ export default function LoginPage() {
|
|||||||
type="password"
|
type="password"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
onFocus={() => setIsPasswordFocused(true)}
|
||||||
|
onBlur={() => setIsPasswordFocused(false)}
|
||||||
placeholder="비밀번호"
|
placeholder="비밀번호"
|
||||||
className="
|
className="
|
||||||
h-[56px] px-[12px] py-[7px] rounded-[8px] w-full border border-neutral-40
|
h-[56px] px-[12px] py-[7px] rounded-[8px] w-full border border-neutral-40
|
||||||
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
||||||
focus:appearance-none focus:border-neutral-700
|
focus:appearance-none focus:border-neutral-700
|
||||||
text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text
|
text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text
|
||||||
|
pr-[40px]
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
{password.trim().length > 0 && isPasswordFocused && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setPassword("");
|
||||||
|
}}
|
||||||
|
aria-label="입력 지우기"
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer"
|
||||||
|
>
|
||||||
|
<LoginInputSvg />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 체크박스들 */}
|
{/* 체크박스들 */}
|
||||||
<div className="flex items-center justify-start gap-6">
|
<div className="flex items-center justify-start gap-6 mb-15">
|
||||||
<label className="flex cursor-pointer select-none items-center gap-2 text-[15px] font-normal text-basic-text">
|
<label className="flex cursor-pointer select-none items-center gap-2 text-[15px] font-normal text-basic-text">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={rememberId}
|
checked={rememberId}
|
||||||
onChange={(e) => setRememberId(e.target.checked)}
|
onChange={(e) => setRememberId(e.target.checked)}
|
||||||
className="h-[18px] w-[18px] cursor-pointer rounded border
|
className="sr-only"
|
||||||
accent-input-border-select border-inactive-checkbox"
|
|
||||||
/>
|
/>
|
||||||
|
{rememberId ? (
|
||||||
|
<LoginCheckboxActiveSvg />
|
||||||
|
) : (
|
||||||
|
<LoginCheckboxInactiveSvg />
|
||||||
|
)}
|
||||||
아이디 기억하기
|
아이디 기억하기
|
||||||
</label>
|
</label>
|
||||||
<label className="flex cursor-pointer select-none items-center gap-2 text-[15px] font-normal text-basic-text">
|
<label className="flex cursor-pointer select-none items-center gap-2 text-[15px] font-normal text-basic-text">
|
||||||
@@ -91,9 +150,13 @@ export default function LoginPage() {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={autoLogin}
|
checked={autoLogin}
|
||||||
onChange={(e) => setAutoLogin(e.target.checked)}
|
onChange={(e) => setAutoLogin(e.target.checked)}
|
||||||
className="h-[18px] w-[18px] cursor-pointer rounded border
|
className="sr-only"
|
||||||
accent-input-border-select border-inactive-checkbox"
|
|
||||||
/>
|
/>
|
||||||
|
{autoLogin ? (
|
||||||
|
<LoginCheckboxActiveSvg />
|
||||||
|
) : (
|
||||||
|
<LoginCheckboxInactiveSvg />
|
||||||
|
)}
|
||||||
자동 로그인
|
자동 로그인
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,16 +164,16 @@ export default function LoginPage() {
|
|||||||
{/* 로그인 버튼 */}
|
{/* 로그인 버튼 */}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="h-14 w-full rounded-lg text-[16px] font-semibold text-white transition-opacity cursor-pointer bg-inactive-button"
|
className={`h-14 w-full rounded-lg text-[16px] font-semibold text-white transition-opacity cursor-pointer mb-3 ${userId.trim().length > 0 && password.trim().length > 0 ? "bg-active-button" : "bg-inactive-button"}`}
|
||||||
>
|
>
|
||||||
로그인
|
로그인
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* 하단 링크들 */}
|
{/* 하단 링크들 */}
|
||||||
<div className="flex items-center justify-between text-[15px]">
|
<div className="flex items-center justify-between text-[15px] leading-[150%] h-[36px]">
|
||||||
<Link
|
<Link
|
||||||
href="/register"
|
href="/register"
|
||||||
className="underline-offset-2 text-basic-text"
|
className="underline-offset-2 text-basic-text font-bold"
|
||||||
>
|
>
|
||||||
회원가입
|
회원가입
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,613 +0,0 @@
|
|||||||
\"use client\";
|
|
||||||
|
|
||||||
import { useState } from \"react\";
|
|
||||||
import Link from \"next/link\";
|
|
||||||
import MainLogo from \"@/app/svgs/mainlogosvg\";
|
|
||||||
|
|
||||||
export default function RegisterPage() {
|
|
||||||
const [name, setName] = useState(\"\");
|
|
||||||
const [phone, setPhone] = useState(\"\");
|
|
||||||
const [email, setEmail] = useState(\"\");
|
|
||||||
const [password, setPassword] = useState(\"\");
|
|
||||||
const [confirmPassword, setConfirmPassword] = useState(\"\");
|
|
||||||
const [gender, setGender] = useState<\"male\" | \"female\" | \"\">(\"\");
|
|
||||||
const [birth, setBirth] = useState(\"\");
|
|
||||||
|
|
||||||
const [agreeAll, setAgreeAll] = useState(false);
|
|
||||||
const [agreeAge, setAgreeAge] = useState(false);
|
|
||||||
const [agreeTos, setAgreeTos] = useState(false);
|
|
||||||
const [agreePrivacy, setAgreePrivacy] = useState(false);
|
|
||||||
|
|
||||||
function syncAgreeAll(nextAll: boolean) {
|
|
||||||
setAgreeAll(nextAll);
|
|
||||||
setAgreeAge(nextAll);
|
|
||||||
setAgreeTos(nextAll);
|
|
||||||
setAgreePrivacy(nextAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleIndividualAgree(next: { age?: boolean; tos?: boolean; privacy?: boolean }) {
|
|
||||||
const nextAge = next.age ?? agreeAge;
|
|
||||||
const nextTos = next.tos ?? agreeTos;
|
|
||||||
const nextPrivacy = next.privacy ?? agreePrivacy;
|
|
||||||
setAgreeAge(nextAge);
|
|
||||||
setAgreeTos(nextTos);
|
|
||||||
setAgreePrivacy(nextPrivacy);
|
|
||||||
setAgreeAll(nextAge && nextTos && nextPrivacy);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
|
||||||
e.preventDefault();
|
|
||||||
// TODO: 서버 연동 시 실제 제출 로직으로 교체
|
|
||||||
console.log({
|
|
||||||
name,
|
|
||||||
phone,
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
confirmPassword,
|
|
||||||
gender,
|
|
||||||
birth,
|
|
||||||
agree: { all: agreeAll, age: agreeAge, tos: agreeTos, privacy: agreePrivacy },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className=\"min-h-screen w-full flex flex-col items-center justify-between\">
|
|
||||||
<div></div>
|
|
||||||
<div className=\"rounded-xl bg-white max-w-[480px] w-full\">
|
|
||||||
{/* 로고/타이틀 */}
|
|
||||||
<div className=\"mb-10 flex flex-col items-center\">
|
|
||||||
<div className=\"mb-[7px]\">
|
|
||||||
<MainLogo />
|
|
||||||
</div>
|
|
||||||
<div className=\"text-[24px] font-[700] leading-[150%] text-neutral-700\">회원가입</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className=\"space-y-6\">
|
|
||||||
<div className=\"space-y-4\">
|
|
||||||
{/* 이름 */}
|
|
||||||
<div className=\"space-y-2\">
|
|
||||||
<label className=\"text-[15px] font-[600] text-[var(--color-basic-text)]\">이름</label>
|
|
||||||
<input
|
|
||||||
value={name}
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
|
||||||
placeholder=\"이름을 입력해 주세요.\"
|
|
||||||
className=\"h-[40px] px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40 text-[16px] text-neutral-700 placeholder:text-input-placeholder-text focus:outline-none focus:border-neutral-700\"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 휴대폰 번호 */}
|
|
||||||
<div className=\"space-y-2\">
|
|
||||||
<label className=\"text-[15px] font-[600] text-[var(--color-basic-text)]\">휴대폰 번호</label>
|
|
||||||
<input
|
|
||||||
value={phone}
|
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
|
||||||
placeholder=\"-없이 입력해 주세요.\"
|
|
||||||
className=\"h-[40px] px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40 text-[16px] text-neutral-700 placeholder:text-input-placeholder-text focus:outline-none focus:border-neutral-700\"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 아이디(이메일) + 인증번호 전송 */}
|
|
||||||
<div className=\"space-y-2\">
|
|
||||||
<label className=\"text-[15px] font-[600] text-[var(--color-basic-text)]\">아이디 (이메일)</label>
|
|
||||||
<div className=\"flex gap-2\">
|
|
||||||
<input
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
placeholder=\"이메일을 입력해 주세요.\"
|
|
||||||
className=\"h-[40px] px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40 text-[16px] text-neutral-700 placeholder:text-input-placeholder-text focus:outline-none focus:border-neutral-700\"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type=\"button\"
|
|
||||||
className=\"h-[40px] px-[12px] rounded-[8px] bg-[#f1f3f5] text-[16px] font-[600] text-[#4c5561]\"
|
|
||||||
>
|
|
||||||
인증번호 전송
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 비밀번호 */}
|
|
||||||
<div className=\"space-y-2\">
|
|
||||||
<label className=\"text-[15px] font-[600] text-[var(--color-basic-text)]\">비밀번호</label>
|
|
||||||
<input
|
|
||||||
type=\"password\"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
placeholder=\"8~16자의 영문/숫자/특수문자를 조합해서 입력해 주세요.\"
|
|
||||||
className=\"h-[40px] px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40 text-[16px] text-neutral-700 placeholder:text-input-placeholder-text focus:outline-none focus:border-neutral-700\"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 비밀번호 확인 */}
|
|
||||||
<div className=\"space-y-2\">
|
|
||||||
<label className=\"text-[15px] font-[600] text-[var(--color-basic-text)]\">비밀번호 확인</label>
|
|
||||||
<input
|
|
||||||
type=\"password\"
|
|
||||||
value={confirmPassword}
|
|
||||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
||||||
placeholder=\"비밀번호를 다시 입력해 주세요.\"
|
|
||||||
className=\"h-[40px] px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40 text-[16px] text-neutral-700 placeholder:text-input-placeholder-text focus:outline-none focus:border-neutral-700\"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 성별 */}
|
|
||||||
<div className=\"space-y-2\">
|
|
||||||
<label className=\"text-[15px] font-[600] text-[var(--color-basic-text)]\">성별</label>
|
|
||||||
<div className=\"flex items-center gap-5 h-[40px]\">
|
|
||||||
<label className=\"flex items-center gap-2 text-[15px] text-neutral-700\">
|
|
||||||
<input
|
|
||||||
type=\"radio\"
|
|
||||||
name=\"gender\"
|
|
||||||
checked={gender === \"male\"}
|
|
||||||
onChange={() => setGender(\"male\")}
|
|
||||||
className=\"h-[18px] w-[18px] cursor-pointer\"
|
|
||||||
style={{ accentColor: \"var(--color-input-border-select)\" }}
|
|
||||||
/>
|
|
||||||
남성
|
|
||||||
</label>
|
|
||||||
<label className=\"flex items-center gap-2 text-[15px] text-neutral-700\">
|
|
||||||
<input
|
|
||||||
type=\"radio\"
|
|
||||||
name=\"gender\"
|
|
||||||
checked={gender === \"female\"}
|
|
||||||
onChange={() => setGender(\"female\")}
|
|
||||||
className=\"h-[18px] w-[18px] cursor-pointer\"
|
|
||||||
style={{ accentColor: \"var(--color-input-border-select)\" }}
|
|
||||||
/>
|
|
||||||
여성
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 생년월일 */}
|
|
||||||
<div className=\"space-y-2\">
|
|
||||||
<label className=\"text-[15px] font-[600] text-[var(--color-basic-text)]\">생년월일</label>
|
|
||||||
<input
|
|
||||||
type=\"date\"
|
|
||||||
value={birth}
|
|
||||||
onChange={(e) => setBirth(e.target.value)}
|
|
||||||
className=\"h-[40px] px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40 text-[16px] text-neutral-700 focus:outline-none focus:border-neutral-700\"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 약관 */}
|
|
||||||
<div className=\"space-y-4\">
|
|
||||||
<label className=\"flex items-center gap-2 text-[18px] font-[600] text-neutral-700 cursor-pointer\">
|
|
||||||
<input
|
|
||||||
type=\"checkbox\"
|
|
||||||
checked={agreeAll}
|
|
||||||
onChange={(e) => syncAgreeAll(e.target.checked)}
|
|
||||||
className=\"h-[20px] w-[20px] cursor-pointer rounded border\"
|
|
||||||
style={{ accentColor: \"var(--color-input-border-select)\", borderColor: \"var(--color-inactive-checkbox)\" }}
|
|
||||||
/>
|
|
||||||
모든 항목에 동의합니다.
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className=\"h-px w-full bg-[var(--color-input-border)]\" />
|
|
||||||
|
|
||||||
<div className=\"space-y-3\">
|
|
||||||
<label className=\"flex items-center justify-between text-[15px]\">
|
|
||||||
<span className=\"flex items-center gap-2 text-neutral-700\">
|
|
||||||
<input
|
|
||||||
type=\"checkbox\"
|
|
||||||
checked={agreeAge}
|
|
||||||
onChange={(e) => handleIndividualAgree({ age: e.target.checked })}
|
|
||||||
className=\"h-[18px] w-[18px] cursor-pointer rounded border\"
|
|
||||||
style={{ accentColor: \"var(--color-input-border-select)\", borderColor: \"var(--color-inactive-checkbox)\" }}
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
만 14세 이상입니다. <span className=\"text-[#384fbf]\">(필수)</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className=\"flex items-center justify-between text-[15px]\">
|
|
||||||
<span className=\"flex items-center gap-2 text-neutral-700\">
|
|
||||||
<input
|
|
||||||
type=\"checkbox\"
|
|
||||||
checked={agreeTos}
|
|
||||||
onChange={(e) => handleIndividualAgree({ tos: e.target.checked })}
|
|
||||||
className=\"h-[18px] w-[18px] cursor-pointer rounded border\"
|
|
||||||
style={{ accentColor: \"var(--color-input-border-select)\", borderColor: \"var(--color-inactive-checkbox)\" }}
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
이용 약관 동의 <span className=\"text-[#384fbf]\">(필수)</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className=\"flex items-center justify-between text-[15px]\">
|
|
||||||
<span className=\"flex items-center gap-2 text-neutral-700\">
|
|
||||||
<input
|
|
||||||
type=\"checkbox\"
|
|
||||||
checked={agreePrivacy}
|
|
||||||
onChange={(e) => handleIndividualAgree({ privacy: e.target.checked })}
|
|
||||||
className=\"h-[18px] w-[18px] cursor-pointer rounded border\"
|
|
||||||
style={{ accentColor: \"var(--color-input-border-select)\", borderColor: \"var(--color-inactive-checkbox)\" }}
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
개인정보 수집 및 이용 동의 <span className=\"text-[#384fbf]\">(필수)</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 버튼들 */}
|
|
||||||
<div className=\"flex gap-3\">
|
|
||||||
<Link
|
|
||||||
href=\"/login\"
|
|
||||||
className=\"h-14 flex-1 rounded-[12px] bg-[#f1f3f5] text-center grid place-items-center text-[18px] font-[600] text-[#4c5561]\"
|
|
||||||
>
|
|
||||||
돌아가기
|
|
||||||
</Link>
|
|
||||||
<button
|
|
||||||
type=\"submit\"
|
|
||||||
className=\"h-14 flex-1 rounded-[12px] text-[18px] font-[600] text-white\"
|
|
||||||
style={{ backgroundColor: \"var(--color-active-button)\" }}
|
|
||||||
disabled={!(agreeAge && agreeTos && agreePrivacy)}
|
|
||||||
>
|
|
||||||
회원 가입 완료
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
<p className=\"text-center text-[15px] text-basic-text\">
|
|
||||||
Copyright ⓒ 2025 XL LMS. All rights reserved
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
export default function RegisterPage() {
|
|
||||||
// form states
|
|
||||||
const [name, setName] = useState("");
|
|
||||||
const [phone, setPhone] = useState("");
|
|
||||||
const [email, setEmail] = useState("");
|
|
||||||
const [password, setPassword] = useState("");
|
|
||||||
const [passwordConfirm, setPasswordConfirm] = useState("");
|
|
||||||
const [gender, setGender] = useState<"male" | "female">("male");
|
|
||||||
const [birth, setBirth] = useState("");
|
|
||||||
|
|
||||||
// agreements
|
|
||||||
const [agreeAge, setAgreeAge] = useState(false);
|
|
||||||
const [agreeTos, setAgreeTos] = useState(false);
|
|
||||||
const [agreePrivacy, setAgreePrivacy] = useState(false);
|
|
||||||
const agreeAll = useMemo(
|
|
||||||
() => agreeAge && agreeTos && agreePrivacy,
|
|
||||||
[agreeAge, agreeTos, agreePrivacy],
|
|
||||||
);
|
|
||||||
|
|
||||||
function handleToggleAll(next: boolean) {
|
|
||||||
setAgreeAge(next);
|
|
||||||
setAgreeTos(next);
|
|
||||||
setAgreePrivacy(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!agreeAge || !agreeTos || !agreePrivacy) {
|
|
||||||
alert("필수 약관에 동의해 주세요.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (password.length < 8 || password.length > 16) {
|
|
||||||
alert("비밀번호는 8~16자여야 합니다.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (password !== passwordConfirm) {
|
|
||||||
alert("비밀번호가 일치하지 않습니다.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: 실제 API 연동
|
|
||||||
console.log({
|
|
||||||
name,
|
|
||||||
phone,
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
gender,
|
|
||||||
birth,
|
|
||||||
agreements: { agreeAge, agreeTos, agreePrivacy },
|
|
||||||
});
|
|
||||||
alert("회원가입 폼이 제출되었습니다. (데모)");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen w-full flex flex-col items-center justify-between">
|
|
||||||
<div></div>
|
|
||||||
<div className="rounded-xl bg-white max-w-[480px] w-full">
|
|
||||||
{/* 헤더 타이틀 */}
|
|
||||||
<div className="mb-10 flex flex-col items-center">
|
|
||||||
<div className="text-[24px] font-[700] leading-[150%] text-neutral-700">
|
|
||||||
회원가입
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 폼 */}
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
|
||||||
<div className="space-y-4">
|
|
||||||
{/* 이름 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label
|
|
||||||
htmlFor="name"
|
|
||||||
className="text-[15px] font-[600]"
|
|
||||||
style={{ color: "var(--color-basic-text)" }}
|
|
||||||
>
|
|
||||||
이름
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="name"
|
|
||||||
name="name"
|
|
||||||
value={name}
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
|
||||||
placeholder="이름을 입력해 주세요."
|
|
||||||
className="
|
|
||||||
h-10 px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40
|
|
||||||
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
|
||||||
focus:appearance-none focus:border-neutral-700
|
|
||||||
text-[16px] text-neutral-700 font-[400] leading-[150%] placeholder:text-input-placeholder-text
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 휴대폰 번호 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label
|
|
||||||
htmlFor="phone"
|
|
||||||
className="text-[15px] font-[600]"
|
|
||||||
style={{ color: "var(--color-basic-text)" }}
|
|
||||||
>
|
|
||||||
휴대폰 번호
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="phone"
|
|
||||||
name="phone"
|
|
||||||
value={phone}
|
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
|
||||||
placeholder="-없이 입력해 주세요."
|
|
||||||
className="
|
|
||||||
h-10 px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40
|
|
||||||
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
|
||||||
focus:appearance-none focus:border-neutral-700
|
|
||||||
text-[16px] text-neutral-700 font-[400] leading-[150%] placeholder:text-input-placeholder-text
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 아이디(이메일) + 인증번호 전송 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label
|
|
||||||
htmlFor="email"
|
|
||||||
className="text-[15px] font-[600]"
|
|
||||||
style={{ color: "var(--color-basic-text)" }}
|
|
||||||
>
|
|
||||||
아이디 (이메일)
|
|
||||||
</label>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
placeholder="이메일을 입력해 주세요."
|
|
||||||
className="
|
|
||||||
h-10 px-[12px] py-[8px] rounded-[8px] w-full border border-neutral-40
|
|
||||||
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
|
||||||
focus:appearance-none focus:border-neutral-700
|
|
||||||
text-[16px] text-neutral-700 font-[400] leading-[150%] placeholder:text-input-placeholder-text
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="h-10 w-[136px] rounded-[8px] text-[16px] font-[600] bg-[#f9fafb] text-[#b1b8c0]"
|
|
||||||
>
|
|
||||||
인증번호 전송
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 비밀번호 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label
|
|
||||||
htmlFor="password"
|
|
||||||
className="text-[15px] font-[600]"
|
|
||||||
style={{ color: "var(--color-basic-text)" }}
|
|
||||||
>
|
|
||||||
비밀번호
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="password"
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
placeholder="8~16자의 영문/숫자/특수문자를 조합해서 입력해 주세요."
|
|
||||||
className="
|
|
||||||
h-10 px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40
|
|
||||||
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
|
||||||
focus:appearance-none focus:border-neutral-700
|
|
||||||
text-[16px] text-neutral-700 font-[400] leading-[150%] placeholder:text-input-placeholder-text
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 비밀번호 확인 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label
|
|
||||||
htmlFor="passwordConfirm"
|
|
||||||
className="text-[15px] font-[600]"
|
|
||||||
style={{ color: "var(--color-basic-text)" }}
|
|
||||||
>
|
|
||||||
비밀번호 확인
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="passwordConfirm"
|
|
||||||
name="passwordConfirm"
|
|
||||||
type="password"
|
|
||||||
value={passwordConfirm}
|
|
||||||
onChange={(e) => setPasswordConfirm(e.target.value)}
|
|
||||||
placeholder="비밀번호를 다시 입력해 주세요."
|
|
||||||
className="
|
|
||||||
h-10 px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40
|
|
||||||
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
|
||||||
focus:appearance-none focus:border-neutral-700
|
|
||||||
text-[16px] text-neutral-700 font-[400] leading-[150%] placeholder:text-input-placeholder-text
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 성별 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
div className="text-[15px] font-[600]" style={{ color: "var(--color-basic-text)" }}>
|
|
||||||
성별
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-5 h-10">
|
|
||||||
<label className="flex items-center gap-2 text-[15px] text-neutral-700">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="gender"
|
|
||||||
checked={gender === "male"}
|
|
||||||
onChange={() => setGender("male")}
|
|
||||||
className="h-[18px] w-[18px] rounded-full"
|
|
||||||
style={{ accentColor: "var(--color-active-button)" }}
|
|
||||||
/>
|
|
||||||
남성
|
|
||||||
</label>
|
|
||||||
<label className="flex items-center gap-2 text-[15px] text-neutral-700">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="gender"
|
|
||||||
checked={gender === "female"}
|
|
||||||
onChange={() => setGender("female")}
|
|
||||||
className="h-[18px] w-[18px] rounded-full"
|
|
||||||
style={{ accentColor: "var(--color-input-border)" }}
|
|
||||||
/>
|
|
||||||
여성
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 생년월일 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label
|
|
||||||
htmlFor="birth"
|
|
||||||
className="text-[15px] font-[600]"
|
|
||||||
style={{ color: "var(--color-basic-text)" }}
|
|
||||||
>
|
|
||||||
생년월일
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="birth"
|
|
||||||
name="birth"
|
|
||||||
type="date"
|
|
||||||
value={birth}
|
|
||||||
onChange={(e) => setBirth(e.target.value)}
|
|
||||||
className="
|
|
||||||
h-10 px-[12px] py-[8px] w-full rounded-[8px] border border-neutral-40
|
|
||||||
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
|
|
||||||
focus:appearance-none focus:border-neutral-700
|
|
||||||
text-[16px] text-neutral-700 font-[400] leading-[150%]
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 약관 동의 */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<label className="flex cursor-pointer select-none items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={agreeAll}
|
|
||||||
onChange={(e) => handleToggleAll(e.target.checked)}
|
|
||||||
className="h-[20px] w-[20px] cursor-pointer rounded border"
|
|
||||||
style={{
|
|
||||||
accentColor: "var(--color-input-border-select)",
|
|
||||||
borderColor: "var(--color-inactive-checkbox)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="text-[18px] font-[600] text-neutral-700">모든 항목에 동의합니다.</span>
|
|
||||||
</label>
|
|
||||||
<div className="h-px w-full bg-neutral-200" />
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="flex cursor-pointer select-none items-center justify-between">
|
|
||||||
<span className="flex items-center gap-2 text-[15px]" style={{ color: "var(--color-basic-text)" }}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={agreeAge}
|
|
||||||
onChange={(e) => setAgreeAge(e.target.checked)}
|
|
||||||
className="h-[20px] w-[20px] cursor-pointer rounded border"
|
|
||||||
style={{
|
|
||||||
accentColor: "var(--color-input-border-select)",
|
|
||||||
borderColor: "var(--color-inactive-checkbox)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
만 14세 이상입니다. <span className="text-[#384fbf]">(필수)</span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<label className="flex cursor-pointer select-none items-center justify-between">
|
|
||||||
<span className="flex items-center gap-2 text-[15px]" style={{ color: "var(--color-basic-text)" }}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={agreeTos}
|
|
||||||
onChange={(e) => setAgreeTos(e.target.checked)}
|
|
||||||
className="h-[20px] w-[20px] cursor-pointer rounded border"
|
|
||||||
style={{
|
|
||||||
accentColor: "var(--color-input-border-select)",
|
|
||||||
borderColor: "var(--color-inactive-checkbox)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
이용 약관 동의 <span className="text-[#384fbf]">(필수)</span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<label className="flex cursor-pointer select-none items-center justify-between">
|
|
||||||
<span className="flex items-center gap-2 text-[15px]" style={{ color: "var(--color-basic-text)" }}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={agreePrivacy}
|
|
||||||
onChange={(e) => setAgreePrivacy(e.target.checked)}
|
|
||||||
className="h-[20px] w-[20px] cursor-pointer rounded border"
|
|
||||||
style={{
|
|
||||||
accentColor: "var(--color-input-border-select)",
|
|
||||||
borderColor: "var(--color-inactive-checkbox)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
개인정보 수집 및 이용 동의 <span className="text-[#384fbf]">(필수)</span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 하단 버튼 */}
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
|
||||||
<Link
|
|
||||||
href="/login"
|
|
||||||
className="h-14 rounded-[12px] bg-[#f1f3f5] text-[18px] font-[600] text-center flex items-center justify-center"
|
|
||||||
style={{ color: "var(--color-basic-text)" }}
|
|
||||||
>
|
|
||||||
돌아가기
|
|
||||||
</Link>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="h-14 rounded-[12px] text-[18px] font-[600] text-white"
|
|
||||||
style={{ backgroundColor: "var(--color-inactive-button)" }}
|
|
||||||
>
|
|
||||||
회원 가입 완료
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
<p className="text-center text-[15px] text-basic-text">
|
|
||||||
Copyright ⓒ 2025 XL LMS. All rights reserved
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
11
src/app/svgs/inputformx.tsx
Normal file
11
src/app/svgs/inputformx.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
export default function LoginInputSvg() {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||||
|
<path fillRule="evenodd" clipRule="evenodd" d="M10 17.5C5.8575 17.5 2.5 14.1425 2.5 10C2.5 5.8575 5.8575 2.5 10 2.5C14.1425 2.5 17.5 5.8575 17.5 10C17.5 14.1425 14.1425 17.5 10 17.5Z" fill="#6C7682" stroke="#6C7682" strokeWidth="1.875" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
|
<path d="M12.3583 7.6416L7.6416 12.3583" stroke="white" strokeWidth="1.875" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
|
<path d="M12.3583 12.3583L7.6416 7.6416" stroke="white" strokeWidth="1.875" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user