Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
9,927 changes: 9,927 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
"react-dom": "^19.2.0",
"react-hook-form": "^7.71.1",
"react-markdown": "^10.1.0",
"react-remove-scroll": "^2.7.2",
"react-router-dom": "^7.12.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.1",
"tailwindcss": "^4.1.18",
"zod": "^4.3.5",
"zustand": "^5.0.10"
Expand Down
5 changes: 2 additions & 3 deletions src/api/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { ICommonResponse } from "@/types/common/common";

import type {
IEmailSendRequest,
IEmailSendResponse,
Expand All @@ -9,7 +7,8 @@ import type {
ISignUpRequest,
ISignUpResponse,
ITokenRefreshResponse,
} from "../../types/auth/auth";
} from "@/types/auth/auth";
import type { ICommonResponse } from "@/types/common/common";

import { axiosInstance } from "@/lib/axiosInstance";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
SkeletonCircle,
} from "@/components/common/skeleton/Skeleton";

export default function LoginSkeleton() {
export default function LoginPageSkeleton() {
return (
<div className="flex min-h-screen w-full items-center justify-center bg-white">
<div className="w-full max-w-130 px-6 pt-30 pb-12">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Skeleton } from "@/components/common/skeleton/Skeleton";

export default function SignupStep01Skeleton() {
export default function SignupEmailStepSkeleton() {
return (
<div className="flex w-full flex-col items-center">
<div className="w-full max-w-90">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Skeleton } from "@/components/common/skeleton/Skeleton";

export default function SignupSkeleton() {
export default function SignupPageSkeleton() {
return (
<div className="flex w-full flex-col items-center">
<div className="flex w-full flex-col gap-10">
Expand Down
170 changes: 69 additions & 101 deletions src/components/auth/common/CommonAuthInput.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { InputHTMLAttributes } from "react";
import React, { useState } from "react";

import formatInputNumber from "@/utils/formatPhoneNumber";
import formatPhoneNumber from "@/utils/formatPhoneNumber";

import Button from "@/components/common/Button";
import Input, { type IInputProps } from "@/components/common/input/Input";

import InputActions from "./InputActions";

import EyeIcon from "@/assets/auth/password/eye.svg?react";
import EyeOffIcon from "@/assets/auth/password/eye-off.svg?react";
Expand All @@ -19,10 +20,17 @@ type TCommonAuthInputProps = {
errorMessage?: string;
button?: boolean;
buttonText?: string;
buttonOnclick?: () => void;
short?: boolean;
onButtonClick?: () => void;
timer?: string;
} & InputHTMLAttributes<HTMLInputElement>;
} & Omit<
IInputProps,
| "label"
| "helperText"
| "success"
| "rightElement"
| "error"
| "containerClassName"
>;

const CommonAuthInput = React.forwardRef<
HTMLInputElement,
Expand All @@ -31,20 +39,19 @@ const CommonAuthInput = React.forwardRef<
(
{
type,
placeholder,
title,
validation = false,
value,
errorMessage,
error,
errorMessage,
button,
buttonText,
buttonOnclick,
short,
onButtonClick,
validationState,
timer,
onChange,
className,
...rest
}: TCommonAuthInputProps,
},
ref,
) => {
const [showPassword, setShowPassword] = useState(false);
Expand All @@ -54,99 +61,60 @@ const CommonAuthInput = React.forwardRef<
};

const inputType = type === "password" && showPassword ? "text" : type;
const isPhoneNum = type === "tel";

return (
<div className="flex flex-col w-full gap-2 relative">
{title && (
<div className={`font-label text-brand-900 select-none ml-1 mb-2`}>
{title}
</div>
)}
const renderRightElement = () => {
if (type === "password") {
return (
<button
type="button"
onClick={handleTogglePassword}
className="flex items-center justify-center w-6 h-6 outline-none mr-2"
aria-label={showPassword ? "비밀번호 숨기기" : "비밀번호 보기"}
>
{showPassword ? (
<EyeIcon className="w-5 h-auto text-text-auth-sub" />
) : (
<EyeOffIcon className="w-5 h-auto text-text-auth-sub" />
)}
</button>
);
}

<div className="relative w-full">
<input
ref={ref}
type={inputType === "phoneNum" ? "text" : inputType}
placeholder={placeholder}
value={value}
className={`w-full h-14 px-5 bg-gray-50 border-transparent rounded-2xl text-body1 text-brand-900
placeholder:text-text-placeholder focus:outline-none focus:bg-white focus:ring-2 focus:ring-logo-1/30 transition-all duration-200
${
error
? "ring-2 ring-status-red bg-status-red/5"
: validation
? "ring-2 ring-logo-1 bg-white"
: "hover:bg-gray-100"
}
${
button || short || validationState || timer
? "pr-25"
: "pr-5"
}
`}
{...rest}
onChange={(e) => {
const rawValue = e.target.value;
const formatted =
type === "phoneNum" ? formatInputNumber(rawValue) : rawValue;
return (
<InputActions
button={button}
buttonText={buttonText}
onButtonClick={onButtonClick}
validationState={validationState}
timer={timer}
validation={validation}
error={error}
type={type}
/>
);
};

if (rest.onChange) {
e.target.value = formatted;
rest.onChange(e);
}
}}
/>
{type === "password" && (
<button
type="button"
onClick={handleTogglePassword}
className="absolute right-4 top-1/2 -translate-y-1/2 flex items-center justify-center w-6 h-6"
>
{showPassword ? (
<EyeIcon className="w-5 h-auto text-text-auth-sub" />
) : (
<EyeOffIcon className="w-5 h-auto text-text-auth-sub" />
)}
</button>
)}
{(button || validationState || timer) && (
<div className="absolute right-2 top-1/2 -translate-y-1/2">
{button && (
<Button
size="small"
children={buttonText}
disabled={type === "code" ? false : error || !validation}
variant={validation ? "dark" : "custom"}
className={`py-1! px-3! h-full`}
onClick={buttonOnclick}
type="button"
/>
)}
{validationState && (
<Button
size="small"
children={validationState}
disabled={!validation}
variant={validation ? "dark" : "custom"}
className={`py-1! px-3! h-full cursor-default`}
/>
)}
{timer && (
<span className="text-status-red font-body2 mr-3">{timer}</span>
)}
</div>
)}
{short && (
<div className="absolute right-0 top-0 h-full w-20 bg-transparent" />
)}
</div>
return (
<Input
ref={ref}
type={isPhoneNum ? "tel" : inputType}
label={title}
helperText={errorMessage}
error={error}
success={validation}
rightElement={renderRightElement()}
containerClassName={className}
onChange={(e) => {
if (isPhoneNum) {
const formatted = formatPhoneNumber(e.target.value);
e.target.value = formatted;
}

{error && errorMessage && (
<div className="font-caption text-status-red pl-1">
{errorMessage}
</div>
)}
</div>
onChange?.(e);
}}
{...rest}
/>
);
},
);
Expand Down
59 changes: 59 additions & 0 deletions src/components/auth/common/InputActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { twMerge } from "tailwind-merge";

import Button from "@/components/common/button/Button";

interface IInputActionsProps {
button?: boolean;
buttonText?: string;
onButtonClick?: () => void;
validationState?: string;
timer?: string;
validation?: boolean;
error?: boolean;
type?: string;
}

export default function InputActions({
button,
buttonText,
onButtonClick,
validationState,
timer,
validation = false,
error = false,
type,
}: IInputActionsProps) {
if (!button && !validationState && !timer) {
return null;
}

return (
<div className="flex items-center gap-2">
{timer && (
<span className="text-status-red font-body2 mr-3">{timer}</span>
)}
{button && (
<Button
size="small"
disabled={type === "code" ? false : error || !validation}
variant={validation ? "primary" : "custom"}
className="py-1 px-3 h-full"
onClick={onButtonClick}
type="button"
>
{buttonText}
</Button>
)}
{validationState && (
<span
className={twMerge(
"flex items-center justify-center rounded-2xl font-body1 whitespace-nowrap py-1 px-3 h-full cursor-default",
validation ? "bg-brand-800 text-white" : "opacity-50",
)}
>
{validationState}
</span>
)}
</div>
);
}
Loading
Loading