Skip to content

Commit

Permalink
Merge branch 'refs/heads/frontend' into feature/fe/#197-doLogin
Browse files Browse the repository at this point in the history
  • Loading branch information
happyhyep committed Nov 18, 2024
2 parents 87a2f1d + 795b38d commit 7ae65bd
Show file tree
Hide file tree
Showing 37 changed files with 1,013 additions and 409 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
## ๐Ÿ— ๋งํฌ ์•ˆ๋‚ด

<p>
<a href="https://www.notion.so/127b1b2b649180e88f70d6a4648924a0?pvs=4">ํŒ€ ๋…ธ์…˜</a> |
<a href="https://ddara-docs.vercel.app/">ํŒ€ ์œ„ํ‚ค</a> |
<a href="https://www.notion.so/127b1b2b649180e88f70d6a4648924a0?pvs=4">ํŒ€ ๋…ธ์…˜</a> |
<a href="https://www.figma.com/design/r9nl4Jcz9VXIMbrpf50wY6/PickMeUp?node-id=90-1897">๊ธฐํš์„œ</a> |
<a href="https://www.figma.com/design/r9nl4Jcz9VXIMbrpf50wY6/PickMeUp?node-id=87-929">๋””์ž์ธ</a>
<!-- <br />
Expand Down
139 changes: 139 additions & 0 deletions frontend/src/component/authmodal/AuthModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React, { useState } from 'react';
import { Modal } from '@/component/common/modal/Modal';

interface IAuthModalProps {
/** ๋ชจ๋‹ฌ์ด ์—ด๋ ค ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. */
isOpen: boolean;
/** ๋ชจ๋‹ฌ์„ ๋‹ซ๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. */
onClose: () => void;
/** ๋ชจ๋‹ฌ์˜ ํƒ€์ž…์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฐ’์œผ๋กœ, 'login' ๋˜๋Š” 'register'๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. */
type: 'login' | 'register';
}

export const AuthModal = (props: IAuthModalProps) => {
const [loginData, setLoginData] = useState({
id: '',
pw: '',
});

const [registerData, setRegisterData] = useState({
id: '',
email: '',
name: '',
pw: '',
confirmPw: '',
});

const [modalType, setModalType] = useState<'login' | 'register'>(props.type);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;

if (modalType === 'login') {
setLoginData(prevState => ({
...prevState,
[name]: value,
}));
} else {
setRegisterData(prevState => ({
...prevState,
[name]: value,
}));
}
};

const handleLoginClick = () => {
console.log('๋กœ๊ทธ์ธ ๋ฐ์ดํ„ฐ:', loginData);
};

const handleSignUpClick = () => {
if (registerData.pw !== registerData.confirmPw) {
alert('๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.');
return;
}
console.log('ํšŒ์›๊ฐ€์ž… ๋ฐ์ดํ„ฐ:', registerData);
};

const switchToRegister = () => {
setModalType('register');
};

const switchToLogin = () => {
setModalType('login');
};

return (
<Modal isOpen={props.isOpen} onClose={props.onClose}>
{modalType === 'login' ? (
<>
<Modal.Header content="Log In" onClose={props.onClose} />
<Modal.Input
title="ID"
name="id"
placeholder="ID"
value={loginData.id}
onChange={handleChange}
/>
<Modal.Input
title="PW"
name="pw"
placeholder="PW"
value={loginData.pw}
onChange={handleChange}
/>
<Modal.Footer
text="๋กœ๊ทธ์ธ"
onClick={handleLoginClick}
text2="ํšŒ์›๊ฐ€์ž…"
onClick2={switchToRegister}
/>
</>
) : (
<>
<Modal.Header content="Sign Up" onClose={props.onClose} />
<Modal.Input
title="ID"
name="id"
placeholder="์‚ฌ์šฉํ•  ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
value={registerData.id}
onChange={handleChange}
/>
<Modal.Input
title="Email"
name="email"
placeholder="Email ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
value={registerData.email}
onChange={handleChange}
/>
<Modal.Input
title="Name"
name="name"
placeholder="์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
value={registerData.name}
onChange={handleChange}
/>
<Modal.Input
title="PW"
name="pw"
placeholder="์‚ฌ์šฉํ•  ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
value={registerData.pw}
onChange={handleChange}
/>
<Modal.Input
title=""
name="confirmPw"
placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ•œ ๋ฒˆ ๋” ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
value={registerData.confirmPw}
onChange={handleChange}
/>
<Modal.Footer
text="ํšŒ์›๊ฐ€์ž…"
onClick={handleSignUpClick}
text2="๋กœ๊ทธ์ธ"
onClick2={switchToLogin}
/>
</>
)}
</Modal>
);
};
43 changes: 27 additions & 16 deletions frontend/src/component/common/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
import React, { createContext, ReactNode, useMemo, useState } from 'react';
import { ReactNode } from 'react';
import { DropdownTrigger } from '@/component/common/dropdown/DropdownTrigger.tsx';
import { DropdownItem } from '@/component/common/dropdown/DropdownItem.tsx';
import { DropdownMenu } from '@/component/common/dropdown/DropdownMenu.tsx';
import { ToggleProvider } from '@/component/common/dropdown/DropdownContext.tsx';

interface IDropdownProps {
/** ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ๋“ค์–ด๊ฐˆ ์ปจํ…์ธ  */
children: ReactNode;
}

export interface IToggleContext {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
}

export const ToggleContext = createContext<IToggleContext>({
isOpen: false,
setIsOpen: () => {},
});
/**
* ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.
*
* @param {ReactNode} children - ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ๋“ค์–ด๊ฐˆ ์ปจํ…์ธ 
* @return {JSX.Element} ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ
*
* @remarks
* - ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ๋Š” ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
*
* @example
* ```tsx
* <Dropdown>
* <Dropdown.Trigger>
* <MdMenu className="h-6 w-6" />
* <span>๋ฉ”๋‰ด</span>
* </Dropdown.Trigger>
* <Dropdown.Menu>
* <Dropdown.Item>๋ฉ”๋‰ด ํ•ญ๋ชฉ 1</Dropdown.Item>
* <Dropdown.Item>๋ฉ”๋‰ด ํ•ญ๋ชฉ 2</Dropdown.Item>
* </Dropdown.Menu>
* </Dropdown>
* ```
*/

// Todo : ๋“œ๋ž๋‹ค์šด ์™ธ๋ถ€์—์„œ ํด๋ฆญ์‹œ ์ฐฝ ๋‹ซํžˆ๊ฒŒ ์„ค์ •
export const Dropdown = (props: IDropdownProps) => {
const [isOpen, setIsOpen] = useState(false);

const toggleContextValue = useMemo(() => ({ isOpen, setIsOpen }), [isOpen, setIsOpen]);

return (
<aside className="relative flex w-fit flex-col">
<ToggleContext.Provider value={toggleContextValue}>{props.children}</ToggleContext.Provider>
<ToggleProvider>{props.children}</ToggleProvider>
</aside>
);
};
Expand Down
25 changes: 25 additions & 0 deletions frontend/src/component/common/dropdown/DropdownContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createContext, ReactNode, useMemo, useState } from 'react';

export interface IToggleContext {
isOpen: boolean;
toggle: () => void;
}

interface IToggleProviderProps {
children: ReactNode;
}

export const ToggleContext = createContext<IToggleContext>({
isOpen: false,
toggle: () => {},
});

export const ToggleProvider = (props: IToggleProviderProps) => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(prevIsOpen => !prevIsOpen);
const toggleContextValue = useMemo(() => ({ isOpen, toggle }), [isOpen]);

return (
<ToggleContext.Provider value={toggleContextValue}>{props.children}</ToggleContext.Provider>
);
};
24 changes: 22 additions & 2 deletions frontend/src/component/common/dropdown/DropdownItem.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
import { ReactNode } from 'react';

interface IDropdownItemProps {
/** ๋“œ๋กญ๋‹ค์šด ์•„์ดํ…œ ๋‚ด์šฉ */
children: ReactNode;
/** ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜ */
onClick?: React.MouseEventHandler<HTMLButtonElement>;
}

/**
* ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด์˜ ์•„์ดํ…œ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.
*
* @param {ReactNode} children - ๋“œ๋กญ๋‹ค์šด ์•„์ดํ…œ ๋‚ด์šฉ
* @param {React.MouseEventHandler<HTMLButtonElement>} onClick - ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜
* @return {JSX.Element} ๋“œ๋กญ๋‹ค์šด ์•„์ดํ…œ ์ปดํฌ๋„ŒํŠธ
*
* @remarks
* - ๋“œ๋กญ๋‹ค์šด ์•„์ดํ…œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
* - ๋“œ๋กญ๋‹ค์šด ์•„์ดํ…œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด์˜ ์•„์ดํ…œ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
* - ๋“œ๋กญ๋‹ค์šด ์•„์ดํ…œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฒ„ํŠผ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
* - ๋“œ๋กญ๋‹ค์šด ์•„์ดํ…œ ์ปดํฌ๋„ŒํŠธ๋Š” ํด๋ฆญ ์‹œ onClick ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
*
* @example
* ```tsx
* <Dropdown.Item onClick={handleOnClick}>์•„์ดํ…œ</Dropdown.Item>
* ```
*/

export const DropdownItem = (props: IDropdownItemProps) => {
// undefined๋Š” react์—์„œ ๋žœ๋”๋ง ํ•˜์ง€ ์•Š์Œ
return (
<li className="px-3 py-1.5 text-base">
<li className="list-none px-3 py-1.5 text-base">
<button
type="button"
className="flex w-full items-center justify-between whitespace-nowrap bg-transparent"
Expand Down
27 changes: 24 additions & 3 deletions frontend/src/component/common/dropdown/DropdownMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
import { ReactNode, useContext, useRef, useEffect } from 'react';
import classNames from 'classnames';
import { ToggleContext } from '@/component/common/dropdown/Dropdown.tsx';
import { ToggleContext } from '@/component/common/dropdown/DropdownContext';

interface IDropdownMenuProps {
/** ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด๊ฐ€ ์—ด๋ ค์žˆ๋Š”์ง€ ์—ฌ๋ถ€ */
children: ReactNode | ReactNode[];
}

/**
* ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.
*
* @param {ReactNode} children - ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด์— ๋“ค์–ด๊ฐˆ ์ปจํ…์ธ 
* @return {JSX.Element} ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ์ปดํฌ๋„ŒํŠธ
*
* @remarks
* - ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
* - ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
*
* @example
* ```tsx
* <Dropdown.Menu>
* <Dropdown.Item>๋ฉ”๋‰ด ํ•ญ๋ชฉ 1</Dropdown.Item>
* <Dropdown.Item>๋ฉ”๋‰ด ํ•ญ๋ชฉ 2</Dropdown.Item>
* </Dropdown.Menu>
* ```
*/

export const DropdownMenu = (props: IDropdownMenuProps) => {
const { isOpen, setIsOpen } = useContext(ToggleContext);
const { isOpen, toggle } = useContext(ToggleContext);
const ref = useRef<HTMLUListElement | null>(null);

const handleOutSideClick = (event: MouseEvent) => {
Expand All @@ -23,7 +43,7 @@ export const DropdownMenu = (props: IDropdownMenuProps) => {
!ref.current.contains(target) &&
target.dataset.component !== 'DropdownTrigger'
) {
setIsOpen(false);
toggle();
}
};

Expand Down Expand Up @@ -55,6 +75,7 @@ export const DropdownMenu = (props: IDropdownMenuProps) => {
'shadow-2xl',
'w-fit',
'bg-white',
'list-none',
)}
>
{props.children}
Expand Down
27 changes: 24 additions & 3 deletions frontend/src/component/common/dropdown/DropdownTrigger.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import { ReactNode, useContext } from 'react';
import classNames from 'classnames';
import { ToggleContext } from '@/component/common/dropdown/Dropdown.tsx';
import { ToggleContext } from '@/component/common/dropdown/DropdownContext';

interface IDropdownTriggerProps {
/** ๋ฒ„ํŠผ ๋‚ด๋ถ€์— ๋“ค์–ด๊ฐˆ ์ปจํ…์ธ  */
children: ReactNode;
}

/**
* ๋“œ๋กญ๋‹ค์šด์—์„œ ๋ฉ”๋‰ด๋ฅผ ์—ด๊ณ  ๋‹ซ๋Š” ํŠธ๋ฆฌ๊ฑฐ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.
*
* @param {ReactNode} children - ๋ฒ„ํŠผ ๋‚ด๋ถ€์— ๋“ค์–ด๊ฐˆ ์ปจํ…์ธ 
* @return {JSX.Element} ๋“œ๋กญ๋‹ค์šด ํŠธ๋ฆฌ๊ฑฐ ์ปดํฌ๋„ŒํŠธ
*
* @remarks
* - ๋“œ๋กญ๋‹ค์šด ํŠธ๋ฆฌ๊ฑฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด๋ฅผ ์—ด๊ณ  ๋‹ซ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
* - ๋“œ๋กญ๋‹ค์šด ํŠธ๋ฆฌ๊ฑฐ ์ปดํฌ๋„ŒํŠธ๋Š” ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
* - ๋“œ๋กญ๋‹ค์šด ํŠธ๋ฆฌ๊ฑฐ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฒ„ํŠผ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
*
* @example
* ```tsx
* <Dropdown.Trigger>
* <MdMenu className="h-6 w-6" />
* <span>๋ฉ”๋‰ด</span>
* </Dropdown.Trigger>
* ```
*/

export const DropdownTrigger = (props: IDropdownTriggerProps) => {
const { setIsOpen } = useContext(ToggleContext);
const { toggle } = useContext(ToggleContext);

const handleOnClick = () => {
setIsOpen(prevIsOpen => !prevIsOpen);
toggle();
};

return (
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/component/common/modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { ModalHeader } from '@/component/common/modal/ModalHeader';
import { ModalInput } from '@/component/common/modal/ModalInput';
import { ModalFooter } from '@/component/common/modal/ModalFooter';

interface IModalProps {
/** ๋ชจ๋‹ฌ์ด ์—ด๋ ค ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. */
isOpen: boolean;
/** ๋ชจ๋‹ฌ ๋‚ด์—์„œ ๋ Œ๋”๋งํ•  ์ž์‹ ์š”์†Œ๋“ค์ž…๋‹ˆ๋‹ค. */
children: React.ReactNode;
}

export const Modal = (props: IModalProps) => {
if (!props.isOpen) return null;

return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-20">
<div className="relative w-[22rem] max-w-lg rounded-2xl bg-white px-6 shadow-lg">
{props.children}
</div>
</div>
);
};

Modal.Header = ModalHeader;
Modal.Footer = ModalFooter;
Modal.Input = ModalInput;
Loading

0 comments on commit 7ae65bd

Please sign in to comment.