diff --git a/memo.txt b/memo.txt new file mode 100644 index 0000000..3e8abf1 --- /dev/null +++ b/memo.txt @@ -0,0 +1,9 @@ +font-100 : font-thin +font-200 : font-extralight +font-300 : font-light +font-400 : font-normal +font-500 : font-medium +font-600 : font-semibold +font-700 : font-bold +font-800 : font-extrabold +font-900 : font-black \ No newline at end of file diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index fad0b96..f6b0d69 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -12,21 +12,20 @@ export default function LoginPage() { function handleSubmit(e: React.FormEvent) { e.preventDefault(); - // TODO: 실제 인증 연동 시 이곳에서 처리 - // 현재는 UI만 구현 + /* todo */ console.log({ userId, password, rememberId, autoLogin }); } return ( -
- {/* 카드 컨테이너 */} -
+
+
+
{/* 로고 영역 */} -
+
-
+
XR LMS
@@ -45,13 +44,12 @@ export default function LoginPage() { value={userId} onChange={(e) => setUserId(e.target.value)} placeholder="아이디 (이메일)" - className="w-[480px] h-[56px] px-[12px] py-[7px] rounded-[8px] border border-neutral-40 - + className=" + 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:appearance-none focus:border-neutral-700 - - text-[18px] text-neutral-700 font-[400] leading-[150%] placeholder:text-input-placeholder-text " - + text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text + " />
{/* 비밀번호 */} @@ -66,38 +64,35 @@ export default function LoginPage() { value={password} onChange={(e) => setPassword(e.target.value)} placeholder="비밀번호" - className="h-14 w-full rounded-lg border px-4 text-[15px] outline-none transition-shadow" + className=" + 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:appearance-none focus:border-neutral-700 + text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text + " />
{/* 체크박스들 */}
-
- - {/* 카피라이트 */} -

+

+

Copyright ⓒ 2025 XL LMS. All rights reserved

diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx new file mode 100644 index 0000000..0ec830c --- /dev/null +++ b/src/app/register/page.tsx @@ -0,0 +1,613 @@ +\"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) { + e.preventDefault(); + // TODO: 서버 연동 시 실제 제출 로직으로 교체 + console.log({ + name, + phone, + email, + password, + confirmPassword, + gender, + birth, + agree: { all: agreeAll, age: agreeAge, tos: agreeTos, privacy: agreePrivacy }, + }); + } + + return ( +
+
+
+ {/* 로고/타이틀 */} +
+
+ +
+
회원가입
+
+ +
+
+ {/* 이름 */} +
+ + 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\" + /> +
+ + {/* 휴대폰 번호 */} +
+ + 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\" + /> +
+ + {/* 아이디(이메일) + 인증번호 전송 */} +
+ +
+ 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\" + /> + +
+
+ + {/* 비밀번호 */} +
+ + 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\" + /> +
+ + {/* 비밀번호 확인 */} +
+ + 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\" + /> +
+ + {/* 성별 */} +
+ +
+ + +
+
+ + {/* 생년월일 */} +
+ + 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\" + /> +
+
+ + {/* 약관 */} +
+ + +
+ +
+ + + + + +
+
+ + {/* 버튼들 */} +
+ + 돌아가기 + + +
+ +
+
+

+ Copyright ⓒ 2025 XL LMS. All rights reserved +

+
+ ); +} + +"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) { + 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 ( +
+
+
+ {/* 헤더 타이틀 */} +
+
+ 회원가입 +
+
+ + {/* 폼 */} +
+
+ {/* 이름 */} +
+ + 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 + " + /> +
+ + {/* 휴대폰 번호 */} +
+ + 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 + " + /> +
+ + {/* 아이디(이메일) + 인증번호 전송 */} +
+ +
+ 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 + " + /> + +
+
+ + {/* 비밀번호 */} +
+ + 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 + " + /> +
+ + {/* 비밀번호 확인 */} +
+ + 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 className="text-[15px] font-[600]" style={{ color: "var(--color-basic-text)" }}> + 성별 +
+
+ + +
+
+ + {/* 생년월일 */} +
+ + 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%] + " + /> +
+
+ + {/* 약관 동의 */} +
+ +
+
+ + + +
+
+ + {/* 하단 버튼 */} +
+ + 돌아가기 + + +
+ +
+
+

+ Copyright ⓒ 2025 XL LMS. All rights reserved +

+
+ ); +} + + diff --git a/src/app/svgs/logincheckboxactivesvg.tsx b/src/app/svgs/logincheckboxactivesvg.tsx new file mode 100644 index 0000000..86a4bca --- /dev/null +++ b/src/app/svgs/logincheckboxactivesvg.tsx @@ -0,0 +1,9 @@ +import React from "react"; + +export default function LoginCheckboxActiveSvg() { + return ( + + + + ); +} diff --git a/src/app/svgs/logincheckboxinactivesvg.tsx b/src/app/svgs/logincheckboxinactivesvg.tsx new file mode 100644 index 0000000..5277372 --- /dev/null +++ b/src/app/svgs/logincheckboxinactivesvg.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +export default function LoginCheckboxInactiveSvg() { + return ( + + + + + + ); +}