Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions public/icons/login/facebook.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions public/icons/login/google.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/login/kakao.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/login/naver.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/login/phone.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions public/icons/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 47 additions & 1 deletion src/app/(auth)/sign-in/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
import SignupButton from "@/components/feature/SignInPage/SignupButton";
import { SIGNUP_BUTTON_TYPES } from "@/constants/signUpButtonType";
import Image from "next/image";

export default function SignInPage() {
return <div>SignInPage</div>;
return (
<section className='w-[425px]'>
{/* Logo */}
<h1
aria-label='logo'
className='flex justify-center'
>
<Image
src={"/icons/logo.svg"}
alt='logo'
width={196}
height={36}
className='antialiased'
/>
</h1>

{/* 로그인/회원가입 구분선 */}
<div
aria-label='sign-divider'
className='relative mb-14 mt-20 flex items-center'
>
<div className='flex-grow border-t border-gray-300' />
<span className='absolute left-1/2 -translate-x-1/2 bg-white px-4 text-sm text-grey-400'>
로그인/회원가입
</span>
<div className='flex-grow border-t border-gray-300' />
</div>

{/* 로그인 버튼 */}
<nav aria-label='sign-in-buttons'>
<ul>
{SIGNUP_BUTTON_TYPES.map((button, idx) => (
<li key={idx}>
<SignupButton
type={button}
className='mb-4'
/>
</li>
))}
</ul>
</nav>
</section>
);
}
142 changes: 142 additions & 0 deletions src/app/(auth)/sign-in/phone/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"use client";

import { useForm } from "react-hook-form";
import Input from "@/components/commons/Input";
import Link from "next/link";
import { useState } from "react";
import CheckBox from "@/components/commons/CheckBox";
import UnderlineButton from "@/components/commons/UnderlineButton";
import Card from "@/components/commons/Card";
import Button from "@/components/commons/Button";
import { PhoneSignInForm } from "./type";

export default function PhoneSignInPage() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
watch,
setValue,
} = useForm<PhoneSignInForm>({
defaultValues: { phone: "", password: "" },
mode: "onSubmit",
});
const [isRemember, setIsRemember] = useState(false);

const phoneValue = watch("phone") || "";
const passwordValue = watch("password") || "";
const isPhoneError = phoneValue.length > 0 && /\D/.test(phoneValue);

const onSubmit = (data: PhoneSignInForm) => {
console.log(isRemember);
console.log(data);
};

return (
<form
onSubmit={handleSubmit(onSubmit)}
className='flex w-[378px] flex-col gap-4'
>
<h2 className='mb-2 text-body-2'>전화번호로 시작하기</h2>

{/* 전화번호 */}
<Input
type={errors.phone ? "error" : "default"}
id='phone'
label='전화번호'
placeholder='01012345678'
font='body-5'
text={phoneValue}
setText={setValue}
register={register("phone", {
required: "전화번호를 입력해주세요.",
pattern: {
value: /^(010|011|016|017|018|019)\d{7,8}$/,
message: "올바른 형식의 전화번호를 입력해 주세요.",
},
})}
/>
{isPhoneError && (
<span className='text-[14px] text-caution'>
숫자만 입력 가능합니다.
</span>
)}
{errors.phone && !isPhoneError && (
<span className='text-[14px] text-caution'>{errors.phone.message}</span>
)}

{/* 비밀번호 */}
<Input
type={errors.password ? "error" : "default"}
id='password'
label='비밀번호'
placeholder='비밀번호를 입력해주세요.'
font='body-5'
iconType='password'
text={passwordValue}
setText={setValue}
register={register("password", {
required: "비밀번호를 입력해주세요.",
pattern: {
value: /^.{8,}$/,
message: "8자 이상 입력해주세요.",
},
})}
/>
{errors.password && (
<span className='text-[14px] text-caution'>
{errors.password.message}
</span>
)}

{/* 로그인 유지 & 비밀번호 재설정 */}
{isRemember && (
<Card
title='주의해 주세요!'
description='로그인 유지는 개인정보를 위해 개인 기기에서 사용해 주세요.'
font='caption'
width={378}
/>
)}

<div className='flex items-center justify-between'>
<CheckBox
id='remember'
text='로그인 유지'
containerHeight={24}
boxSize={24}
isChecked={isRemember}
onCheck={() => setIsRemember(!isRemember)}
font='body-5'
/>

<Link href='#'>
<UnderlineButton
type='passwordReset'
text='비밀번호 재설정'
width={100}
/>
</Link>
</div>

{/* 로그인 버튼 */}
<Button
type='checkBasic'
text='로그인'
width={378}
height={56}
disabled={isSubmitting}
/>

<div className='flex items-center justify-center gap-1'>
<p className='text-grey-400'>계정이 없으신가요?</p>
<Link href='#'>
<UnderlineButton
type='signupEmail'
text='전화번호로 회원가입'
/>
</Link>
</div>
</form>
);
}
5 changes: 5 additions & 0 deletions src/app/(auth)/sign-in/phone/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface PhoneSignInForm {
phone: string;
password: string;
remember: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sign-in/phone 페이지와 동일하게 isRemember이 더 나을 것 같습니다!

}
2 changes: 1 addition & 1 deletion src/components/commons/Card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function Card({
onClick={onClick}
style={{ width: `${width}px`, height: `${height}px` }}
>
<strong className='text-primary-800'>{title}</strong>
<p className='text-primary-800'>{title}</p>
<p className='text-grey-600'>{description}</p>
</section>
);
Expand Down
9 changes: 5 additions & 4 deletions src/components/commons/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ export default function Input({
id,
label,
placeholder,
text,
setText,
font = "body-5",
iconType = "text",
iconSize = 18,
isRequired = false,
className = "",
text,
setText,
register,
}: InputProps) {
const [isVisible, setIsVisible] = useState(false);

Expand Down Expand Up @@ -66,11 +67,11 @@ export default function Input({
id={id}
className={typeClasses}
placeholder={placeholder}
value={text}
onChange={e => setText(e.target.value)}
type={
iconType === "password" ? (isVisible ? "text" : "password") : "text"
}
autoComplete='off'
{...register}
/>
{iconType === "text" ? (
<InputDeleteIcon
Expand Down
4 changes: 2 additions & 2 deletions src/components/commons/Input/inputDeleteIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export default function InputDeleteIcon({
className='absolute right-4 top-1/2 -translate-y-1/2 cursor-pointer'
width={iconSize}
height={iconSize}
style={{ display: text.length === 0 ? "none" : "block" }}
onClick={() => setText("")}
style={{ display: text?.length === 0 ? "none" : "block" }}
onClick={() => setText && setText("phone", "")}
/>
);
}
4 changes: 1 addition & 3 deletions src/components/commons/Input/inputVisibleIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ export default function InputVisibleIcon({
isVisible,
setIsVisible,
}: InputVisibleIconProps) {
const iconSrc = isVisible
? "./icons/eyeCrossLine.svg"
: "./icons/eyeOpen.svg";
const iconSrc = isVisible ? "/icons/eyeCrossLine.svg" : "/icons/eyeOpen.svg";

return (
<Image
Expand Down
72 changes: 72 additions & 0 deletions src/components/feature/SignInPage/SignupButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { SignupButtonProps } from "@/types/components/feature/SignInPage/SignUpButton";
import Image from "next/image";
import Link from "next/link";

export default function SignupButton({
type,
font = "body-5",
iconSize = 24,
className,
}: SignupButtonProps) {
const buttonTypes = {
kakao: {
icon: "/icons/login/kakao.svg",
text: "text-grey-800",
background: "bg-[#FEE500]",
hover: "hover:bg-[#FCDA00]",
phrase: "카카오로 시작하기",
},
naver: {
icon: "/icons/login/naver.svg",
text: "text-white",
background: "bg-[#03C75A]",
hover: "hover:bg-[#03C139]",
phrase: "네이버로 시작하기",
},
facebook: {
icon: "/icons/login/facebook.svg",
text: "text-white",
background: "bg-[#1A77F2]",
hover: "hover:bg-[#006CE0]",
phrase: "페이스북으로 시작하기",
},
google: {
icon: "/icons/login/google.svg",
text: "text-grey-600",
background: "bg-white border border-border",
hover: "hover:bg-background",
phrase: "구글로 시작하기",
},
phone: {
icon: "/icons/login/phone.svg",
text: "text-white",
background: "bg-primary-400",
hover: "hover:bg-primary-300",
phrase: "전화번호로 시작하기",
},
};

const currentStyle = buttonTypes[type];
const typeClasses = `${currentStyle.background} ${currentStyle.text} ${currentStyle.hover} ${currentStyle.phrase}`;

const baseClasses = `flex items-center justify-center rounded-xl gap-2
w-full h-12
focus:outline-none disabled:cursor-not-allowed cursor-pointer truncate text-${font}`;

const buttonClasses = `${typeClasses} ${baseClasses} ${className}`;

return (
<Link href={`/sign-in/${type}`}>
<button className={buttonClasses}>
<Image
src={currentStyle.icon}
alt={type}
width={iconSize}
height={iconSize}
className='mb-0.5'
/>
<span>{currentStyle.phrase}</span>
</button>
</Link>
);
}
9 changes: 9 additions & 0 deletions src/constants/signUpButtonType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ButtonType } from "@/types/components/feature/SignInPage/SignUpButton";

export const SIGNUP_BUTTON_TYPES: ButtonType[] = [
"kakao",
"naver",
"facebook",
"google",
"phone",
];
Loading