diff --git a/package-lock.json b/package-lock.json
index f4daf9e9..60875e21 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,7 +21,9 @@
"react-icons": "^4.11.0",
"react-router-dom": "^6.16.0",
"react-spinners": "^0.13.8",
- "react-toastify": "^9.1.3"
+ "react-toastify": "^9.1.3",
+ "zustand": "^4.4.4",
+ "zustand-persist": "^0.4.0"
},
"devDependencies": {
"@rushstack/eslint-config": "^3.4.1",
@@ -2518,13 +2520,13 @@
"version": "15.7.9",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz",
"integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/react": {
"version": "18.2.30",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.30.tgz",
"integrity": "sha512-OfqdJnDsSo4UNw0bqAjFCuBpLYQM7wvZidz0hVxHRjrEkzRlvZL1pJVyOSY55HMiKvRNEo9DUBRuEl7FNlJ/Vg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -2544,7 +2546,7 @@
"version": "0.16.5",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.5.tgz",
"integrity": "sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/semver": {
"version": "7.5.4",
@@ -9208,6 +9210,42 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zustand": {
+ "version": "4.4.4",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.4.tgz",
+ "integrity": "sha512-5UTUIAiHMNf5+mFp7/AnzJXS7+XxktULFN0+D1sCiZWyX7ZG+AQpqs2qpYrynRij4QvoDdCD+U+bmg/cG3Ucxw==",
+ "dependencies": {
+ "use-sync-external-store": "1.2.0"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/zustand-persist": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/zustand-persist/-/zustand-persist-0.4.0.tgz",
+ "integrity": "sha512-u6bBIc4yZRpSKBKuTNhoqvoIb09gGHk2NkiPg4K7MPIWTYZg70PlpBn48QEDnKZwfNurnf58TaW5BuMGIMf5hw==",
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "zustand": ">=3.6.3"
+ }
}
}
}
diff --git a/package.json b/package.json
index 243bac8f..5c48947f 100644
--- a/package.json
+++ b/package.json
@@ -23,8 +23,10 @@
"react-hook-form": "^7.47.0",
"react-icons": "^4.11.0",
"react-router-dom": "^6.16.0",
- "react-spinners": "^0.13.8",
- "react-toastify": "^9.1.3"
+ "react-toastify": "^9.1.3",
+ "zustand": "^4.4.4",
+ "zustand-persist": "^0.4.0",
+ "react-spinners": "^0.13.8"
},
"devDependencies": {
"@rushstack/eslint-config": "^3.4.1",
diff --git a/src/assets/icons/Exclamation.svg b/src/assets/icons/Exclamation.svg
new file mode 100644
index 00000000..c6348371
--- /dev/null
+++ b/src/assets/icons/Exclamation.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/assets/icons/Warning.svg b/src/assets/icons/Warning.svg
new file mode 100644
index 00000000..8a1b6177
--- /dev/null
+++ b/src/assets/icons/Warning.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/components/common/BottomSheet/RandomMatchingSheet.tsx b/src/components/common/BottomSheet/RandomMatchingSheet.tsx
index 0664610b..8b6a68be 100644
--- a/src/components/common/BottomSheet/RandomMatchingSheet.tsx
+++ b/src/components/common/BottomSheet/RandomMatchingSheet.tsx
@@ -3,7 +3,7 @@ import { AnimatePresence, motion } from 'framer-motion'
import { MouseEvent, useState } from 'react'
import { AiOutlineClose } from 'react-icons/ai'
-import RandomMatchingJoinButton from '@/components/common/Buttons/IconButton/RandomMatchingJoin'
+import RandomMatchingJoinButton from '@/components/common/Buttons/IconButton/RandomMatchingJoinButton'
import { Text } from '@/components/common/Text'
import { palette } from '@/styles/palette'
diff --git a/src/components/common/BottomSheet/index.tsx b/src/components/common/BottomSheet/index.tsx
deleted file mode 100644
index dc8673ba..00000000
--- a/src/components/common/BottomSheet/index.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-import styled from '@emotion/styled'
-import { AnimatePresence, motion } from 'framer-motion'
-import { MouseEvent, useState } from 'react'
-import { AiOutlineClose } from 'react-icons/ai'
-
-import RandomMatchingJoinButton from '@/components/common/Buttons/IconButton/RandomMatchingJoin'
-import { Text } from '@/components/common/Text'
-import { palette } from '@/styles/palette'
-
-import Timer from './Timer'
-
-const Background = styled.div`
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.5);
- display: flex;
- justify-content: center;
- align-items: flex-end;
- overflow-y: hidden;
-`
-
-const BottomContentWrapper = styled(motion.div)<{
- isDarkMode: boolean
-}>`
- width: 100%;
- display: flex;
- flex-direction: column;
- height: 378px;
- border-top-left-radius: 20px;
- border-top-right-radius: 20px;
- background-color: ${({ isDarkMode }) => (isDarkMode ? palette.GRAY700 : palette.WHITE)};
-`
-
-const BottomContentHeader = styled.div<{
- isDarkMode: boolean
-}>`
- width: 100%;
- position: relative;
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid ${({ isDarkMode }) => (isDarkMode ? palette.GRAY500 : palette.GRAY200)};
- background-color: ${({ isDarkMode }) => (isDarkMode ? palette.GRAY700 : palette.WHITE)};
- border-top-left-radius: 20px;
- border-top-right-radius: 20px;
- padding: 24px 0;
-`
-
-const BottomContent = styled.div<{
- isDarkMode: boolean
-}>`
- display: flex;
- flex-direction: column;
- align-items: center;
- border-top-left-radius: 20px;
- border-top-right-radius: 20px;
- background-color: ${({ isDarkMode }) => (isDarkMode ? palette.GRAY700 : palette.WHITE)};
-`
-
-type BottomSheetProps = {
- title: string
- isDarkMode: boolean
-}
-
-const BottomSheet = ({ title, isDarkMode }: BottomSheetProps) => {
- const [isOpen, setIsOpen] = useState(true) // BottomSheet의 상태
-
- const handleWrapperClick = (e: MouseEvent) => {
- e.stopPropagation()
- }
-
- const toggleBottomSheet = () => {
- // isOpen이 true일 때만 상태를 토글
- if (isOpen) {
- console.log('매칭 참가 취소')
- setIsOpen(!isOpen)
- }
- }
-
- const slideUp = {
- hidden: { y: '100%', opacity: 0 },
- visible: { y: '0%', opacity: 1, transition: { type: 'spring', damping: 15, stiffness: 100 } },
- partiallyVisible: {
- y: '85%',
- opacity: 1,
- transition: { type: 'spring', damping: 15, stiffness: 100 },
- },
- exit: { y: '100%', opacity: 0, transition: { type: 'spring', damping: 20, stiffness: 100 } },
- }
- return (
-
-
-
-
-
-
- {title}
-
-
-
- {
- console.log('타이머 종료!')
- }}
- />
- {
- console.log('랜덤 매칭 참가')
- }}
- />
-
- {'현재 매칭에 참가하지 않으면 다음 매칭에 불이익이 있습니다.'}
-
-
-
-
-
- )
-}
-
-export default BottomSheet
diff --git a/src/components/common/Buttons/IconButton/InterestButton.tsx b/src/components/common/Buttons/IconButton/InterestButton.tsx
index 229c816a..942e060e 100644
--- a/src/components/common/Buttons/IconButton/InterestButton.tsx
+++ b/src/components/common/Buttons/IconButton/InterestButton.tsx
@@ -5,7 +5,7 @@ import { Divider } from '@/components/common/Divider'
import { Text, TextWrapper } from '@/components/common/Text'
import { palette } from '@/styles/palette'
-import { IconButtonWrapper, IconWrapper } from '.'
+import { StyleIconButtonWrapper, StyleIconWrapper } from '.'
type InterestButtonProps = {
nickName: string
@@ -17,7 +17,7 @@ const InterestButton = ({ nickName, interests, isDarkMode }: InterestButtonProps
const setButtonType = isDarkMode ? 'interest-dark' : 'interest'
return (
-
-
-
+
{`${nickName}의 관심사`}
@@ -61,12 +61,14 @@ const InterestButton = ({ nickName, interests, isDarkMode }: InterestButtonProps
{interests.map((interest, index) => (
{interest}
- {index !== interests.length - 1 && }
+ {index !== interests.length - 1 && (
+
+ )}
))}
-
+
)
}
diff --git a/src/components/common/Buttons/IconButton/KakaoButton.tsx b/src/components/common/Buttons/IconButton/KakaoButton.tsx
index 7c5068d4..37b7776c 100644
--- a/src/components/common/Buttons/IconButton/KakaoButton.tsx
+++ b/src/components/common/Buttons/IconButton/KakaoButton.tsx
@@ -4,7 +4,7 @@ import KakaoIcon from '@/assets/icons/KakaoIcon'
import { Text } from '@/components/common/Text'
import { palette } from '@/styles/palette'
-import { IconWrapper } from '.'
+import { StyleIconWrapper } from '.'
export const ButtonWrapper = styled.button<{
buttonTheme: 'kakao' | 'naver'
@@ -21,13 +21,13 @@ export const ButtonWrapper = styled.button<{
const KakaoButton = () => (
-
-
+
{
return (
-
-
+
{
const getSecondTextColor = isDarkMode ? palette.GRAY300 : palette.GRAY500
return (
- {
alignItems: 'center',
}}
>
- {
height: 20,
}}
/>
-
+
{
{'네트워크를 넓혀보세요!'}
- {
height: 30,
}}
/>
-
-
+
+
)
}
diff --git a/src/components/common/Buttons/IconButton/RandomMatchingButton.tsx b/src/components/common/Buttons/IconButton/RandomMatchingButton.tsx
index 1fde1eea..f705d881 100644
--- a/src/components/common/Buttons/IconButton/RandomMatchingButton.tsx
+++ b/src/components/common/Buttons/IconButton/RandomMatchingButton.tsx
@@ -5,7 +5,7 @@ import { Text, TextWrapper } from '@/components/common/Text'
import { palette } from '@/styles/palette'
import { getTimeDelta } from '@/utils/getTimeStamp'
-import { IconButtonWrapper, IconWrapper } from '.'
+import { StyleIconButtonWrapper, StyleIconWrapper } from '.'
type RandomMatchingButtonProps = {
date: string
@@ -16,7 +16,7 @@ const RandomMatchingButton = ({ date, isDarkMode }: RandomMatchingButtonProps) =
const setButtonType = isDarkMode ? 'random-matching-dark' : 'random-matching'
return (
-
-
-
+
-
-
-
+
+
)
}
diff --git a/src/components/common/Buttons/IconButton/RandomMatchingJoin.tsx b/src/components/common/Buttons/IconButton/RandomMatchingJoin.tsx
index 58afee29..e69de29b 100644
--- a/src/components/common/Buttons/IconButton/RandomMatchingJoin.tsx
+++ b/src/components/common/Buttons/IconButton/RandomMatchingJoin.tsx
@@ -1,61 +0,0 @@
-import { BiChevronRight } from 'react-icons/bi'
-
-import { Text, TextWrapper } from '@/components/common/Text'
-
-import { IconButtonWrapper, IconWrapper } from '.'
-
-type RandomMatchingJoinButtonProps = {
- isDarkMode: boolean
- moveToRandomMatching: () => void
-}
-
-const RandomMatchingJoinButton = ({
- isDarkMode,
- moveToRandomMatching,
-}: RandomMatchingJoinButtonProps) => {
- const setButtonType = isDarkMode ? 'random-matching-join-dark' : 'random-matching-join'
-
- return (
-
-
-
- {'매칭방에 접속해주세요!'}
-
-
-
-
-
-
- )
-}
-
-export default RandomMatchingJoinButton
diff --git a/src/components/common/Buttons/IconButton/RandomMatchingJoinButton.tsx b/src/components/common/Buttons/IconButton/RandomMatchingJoinButton.tsx
new file mode 100644
index 00000000..12174a8b
--- /dev/null
+++ b/src/components/common/Buttons/IconButton/RandomMatchingJoinButton.tsx
@@ -0,0 +1,61 @@
+import { BiChevronRight } from 'react-icons/bi'
+
+import { Text, TextWrapper } from '@/components/common/Text'
+
+import { StyleIconButtonWrapper, StyleIconWrapper } from '.'
+
+type RandomMatchingJoinButtonProps = {
+ isDarkMode: boolean
+ moveToRandomMatching: () => void
+}
+
+const RandomMatchingJoinButton = ({
+ isDarkMode,
+ moveToRandomMatching,
+}: RandomMatchingJoinButtonProps) => {
+ const setButtonType = isDarkMode ? 'random-matching-join-dark' : 'random-matching-join'
+
+ return (
+
+
+
+ {'매칭방에 접속해주세요!'}
+
+
+
+
+
+
+ )
+}
+
+export default RandomMatchingJoinButton
diff --git a/src/components/common/Buttons/IconButton/index.tsx b/src/components/common/Buttons/IconButton/index.tsx
index 07a56af8..95519bd2 100644
--- a/src/components/common/Buttons/IconButton/index.tsx
+++ b/src/components/common/Buttons/IconButton/index.tsx
@@ -10,7 +10,7 @@ import NaverButton from './NaverButton'
import ParticularTopicButton from './ParticularTopicButton'
import RandomMatchingButton from './RandomMatchingButton'
-export const IconButtonWrapper = styled.button<{
+export const StyleIconButtonWrapper = styled.button<{
iconButtonType: IconButtonType
}>`
${({ iconButtonType }) => {
@@ -30,7 +30,7 @@ export const IconButtonWrapper = styled.button<{
}}
`
-export const IconWrapper = styled.div<{
+export const StyleIconWrapper = styled.div<{
borderRadius?: string
backgroundColor?: string
}>`
diff --git a/src/components/common/Buttons/NormalButton/NormalButton.tsx b/src/components/common/Buttons/NormalButton/NormalButton.tsx
index af501a38..db596958 100644
--- a/src/components/common/Buttons/NormalButton/NormalButton.tsx
+++ b/src/components/common/Buttons/NormalButton/NormalButton.tsx
@@ -7,20 +7,28 @@ import { NormalButtonStyles, NormalButtonType } from './NormalButtonStyles'
const NormalButton = styled.button<{
normalButtonType: NormalButtonType
+ isDarkMode?: boolean
}>`
- ${({ normalButtonType }) => {
- const fontFunc = typo[NormalButtonStyles[normalButtonType].font]
+ ${({ normalButtonType, isDarkMode = false }) => {
+ const processedTypeKey = isDarkMode ? `${normalButtonType}-dark` : normalButtonType
+ const processedType = (
+ NormalButtonStyles[processedTypeKey as NormalButtonType] ? processedTypeKey : normalButtonType
+ ) as NormalButtonType
+
+ console.log(processedType)
+
+ const fontFunc = typo[NormalButtonStyles[processedType].font]
return css`
${fontFunc(
- NormalButtonStyles[normalButtonType].fontWeight,
- NormalButtonStyles[normalButtonType].letterSpacing,
+ NormalButtonStyles[processedType].fontWeight,
+ NormalButtonStyles[processedType].letterSpacing,
)}
- width: ${NormalButtonStyles[normalButtonType].width}px;
- height: ${NormalButtonStyles[normalButtonType].height}px;
- color: ${NormalButtonStyles[normalButtonType].fontColor};
- background-color: ${NormalButtonStyles[normalButtonType].backgroundColor};
- box-shadow: ${NormalButtonStyles[normalButtonType].boxShadow};
- border-radius: ${NormalButtonStyles[normalButtonType].borderRadius}px;
+ width: ${NormalButtonStyles[processedType].width}px;
+ height: ${NormalButtonStyles[processedType].height}px;
+ color: ${NormalButtonStyles[processedType].fontColor};
+ background-color: ${NormalButtonStyles[processedType].backgroundColor};
+ box-shadow: ${NormalButtonStyles[processedType].boxShadow};
+ border-radius: ${NormalButtonStyles[processedType].borderRadius}px;
`
}}
`
diff --git a/src/components/common/Buttons/NormalButton/NormalButtonStyles.ts b/src/components/common/Buttons/NormalButton/NormalButtonStyles.ts
index fb028b1d..2f97c3b7 100644
--- a/src/components/common/Buttons/NormalButton/NormalButtonStyles.ts
+++ b/src/components/common/Buttons/NormalButton/NormalButtonStyles.ts
@@ -144,7 +144,7 @@ export const NormalButtonStyles: Record = {
width: 85,
height: 40,
fontColor: palette.GRAY400,
- backgroundColor: palette.GRAY100,
+ backgroundColor: palette.GRAY200,
font: 'Body_14',
fontWeight: 600,
letterSpacing: -2,
diff --git a/src/components/common/Divider/index.tsx b/src/components/common/Divider/index.tsx
index 6553babe..af4efe1d 100644
--- a/src/components/common/Divider/index.tsx
+++ b/src/components/common/Divider/index.tsx
@@ -2,9 +2,16 @@ import styled from '@emotion/styled'
import { palette } from '@/styles/palette'
-export const Divider = styled.div`
- width: 1px;
- height: 10px;
- margin: 0 12px;
- background-color: ${palette.WHITE};
+type DividerProps = {
+ width: number
+ height: number
+ margin?: string
+ isDarkMode: boolean
+}
+
+export const Divider = styled.div`
+ width: ${({ width }) => width}px;
+ height: ${({ height }) => height}px;
+ margin: ${({ margin }) => margin};
+ background-color: ${({ isDarkMode }) => (isDarkMode ? palette.GRAY500 : palette.GRAY100)};
`
diff --git a/src/components/common/ListRow/AdminListRow.tsx b/src/components/common/ListRow/AdminListRow.tsx
new file mode 100644
index 00000000..7976bf03
--- /dev/null
+++ b/src/components/common/ListRow/AdminListRow.tsx
@@ -0,0 +1,90 @@
+import { FlexBox } from '@/components/common/Flexbox'
+import { StyleList } from '@/components/common/ListRow/ProfileListRow'
+import { Text } from '@/components/common/Text'
+import { palette } from '@/styles/palette'
+
+type ProfileListRowProps = {
+ height: number
+ nickname: string
+ infoMessage: string | number
+ isDarkMode: boolean
+}
+const AdminListRow = ({ height, nickname, infoMessage, isDarkMode }: ProfileListRowProps) => {
+ const renderInfoMessage = () => {
+ if (typeof infoMessage === 'number') {
+ return (
+
+
+ {'누적 '}
+
+
+ {infoMessage}
+
+
+ {' 회'}
+
+
+ )
+ }
+ return (
+
+ {infoMessage}
+
+ )
+ }
+
+ return (
+
+
+ {nickname}
+
+ {renderInfoMessage()}
+
+ )
+}
+
+export default AdminListRow
diff --git a/src/components/common/ListRow/ProfileListRow.tsx b/src/components/common/ListRow/ProfileListRow.tsx
new file mode 100644
index 00000000..d687e016
--- /dev/null
+++ b/src/components/common/ListRow/ProfileListRow.tsx
@@ -0,0 +1,94 @@
+import styled from '@emotion/styled'
+import { ReactNode } from 'react'
+
+import { FlexBox } from '@/components/common/Flexbox'
+import { Text } from '@/components/common/Text'
+import { palette } from '@/styles/palette'
+
+export const StyleList = styled(FlexBox)<{
+ width: number
+ height: number
+}>`
+ width: ${({ width }) => width}px;
+ height: ${({ height }) => height}px;
+ display: flex;
+ justify-content: space-between;
+`
+
+const StyleIconWrapper = styled.div<{
+ width: number
+ height: number
+ borderRadius?: string
+ backgroundColor: string
+}>`
+ width: ${({ width }) => width}px;
+ height: ${({ height }) => height}px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: ${({ borderRadius }) => borderRadius};
+ background-color: ${({ backgroundColor }) => backgroundColor};
+`
+
+type ProfileListRowProps = {
+ firstIcon: ReactNode
+ title: string
+ additionalContent?: ReactNode | string
+ isDarkMode?: boolean
+}
+const ProfileListRow = ({
+ firstIcon,
+ title,
+ additionalContent,
+ isDarkMode,
+}: ProfileListRowProps) => {
+ const isAdditionalContentString = typeof additionalContent === 'string'
+ const additionalContentColor = isAdditionalContentString ? palette.GRAY300 : undefined
+
+ return (
+
+
+ {firstIcon}
+
+
+ {title}
+
+
+ {additionalContent}
+
+
+ )
+}
+
+export default ProfileListRow
diff --git a/src/components/common/Modal/index.tsx b/src/components/common/Modal/index.tsx
new file mode 100644
index 00000000..75b43e9a
--- /dev/null
+++ b/src/components/common/Modal/index.tsx
@@ -0,0 +1,124 @@
+import styled from '@emotion/styled'
+
+import ExclamationIcon from '@/assets/icons/Exclamation.svg'
+import WarningIcon from '@/assets/icons/Warning.svg'
+import NormalButton from '@/components/common/Buttons/NormalButton/NormalButton'
+import useModalStore from '@/store/ModalStore'
+import { palette } from '@/styles/palette'
+import { typo } from '@/styles/typo'
+
+const Modal = () => {
+ const { modalState, setModalState, okFunc, mainText, subText, type } = useModalStore()
+ const OkAndClose = () => {
+ okFunc()
+ closeModal()
+ }
+ const closeModal = () => {
+ setModalState(false)
+ }
+ return (
+ <>
+ {modalState ? (
+
+
+ {type == 'confirm' ? (
+
+ ) : (
+
+ )}
+
+ {mainText}
+ {subText}
+ {type === 'confirm' ? (
+
+
+ {'확인'}
+
+
+ {'취소'}
+
+
+ ) : (
+
+
+ {'예, 나가겠습니다.'}
+
+
+ {'아니오, 돌아가겠습니다.'}
+
+
+ )}
+
+
+ ) : (
+ ''
+ )}
+ >
+ )
+}
+
+const StyleModalWrapper = styled.div`
+ z-index: 999;
+ display: flex;
+ position: absolute;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ background-color: rgba(0, 0, 0, 0.4);
+ border-radius: 10px;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+`
+const StyleModal = styled.div<{ type: string }>`
+ width: 344px;
+ height: ${({ type }) => (type == 'warn' ? '195.6px' : '246px')};
+ z-index: 1;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background-color: white;
+ border-radius: 10px;
+ box-shadow: 3px 3px 3px ${palette.GRAY400};
+ text-align: center;
+`
+
+const StyleButtonWrapper = styled.span`
+ justify-content: center;
+ margin: 10px;
+ display: flex;
+`
+const StyleMainText = styled.div<{ subTrue: boolean }>`
+ color: ${palette.BLACK};
+ text-align: center;
+ font-size: ${typo.Body_20()};
+ margin-top: ${({ subTrue }) => (subTrue ? '' : '10px')};
+ margin-bottom: ${({ subTrue }) => (subTrue ? '20px' : '30px')};
+`
+const StyleSubText = styled.span<{ type: string }>`
+ color: ${palette.GRAY500};
+ text-align: center;
+ font-size: ${typo.Body_14()};
+`
+const StyleIcon = styled.img`
+ margin: 22px;
+`
+export default Modal
diff --git a/src/components/common/SelectorButton/index.tsx b/src/components/common/SelectorButton/index.tsx
index 6eb7d94f..9d88f9af 100644
--- a/src/components/common/SelectorButton/index.tsx
+++ b/src/components/common/SelectorButton/index.tsx
@@ -4,36 +4,43 @@ import { useState } from 'react'
import { palette } from '@/styles/palette'
type SelectorButtonProps = {
+ isDarkMode: boolean
buttonName: string
- selectedButtonColor: string
- defaultButtonColor: string
- textColor: string
onClick?: (selected: boolean) => void
- selected?: boolean
-}
-
-type StyledButtonProps = {
- backgroundColor: string
- textColor: string
+ isButtonselected?: boolean
}
const SelectorButton = ({
+ isDarkMode,
buttonName,
- selectedButtonColor,
- defaultButtonColor = palette.TERTIARY,
- textColor = palette.SECONDARY,
onClick,
- selected = false,
+ isButtonselected = false,
}: SelectorButtonProps) => {
- const initialBackgroundColor = selected ? selectedButtonColor : defaultButtonColor
+ const defaultSettings = isDarkMode
+ ? {
+ selectedButtonColor: palette.SECONDARY,
+ defaultButtonColor: palette.WHITE,
+ textColor: palette.SECONDARY,
+ }
+ : {
+ selectedButtonColor: palette.BLUE,
+ defaultButtonColor: palette.TERTIARY,
+ textColor: palette.WHITE,
+ }
+
+ const initialBackgroundColor = isButtonselected
+ ? defaultSettings.selectedButtonColor
+ : defaultSettings.defaultButtonColor
const [backgroundColor, setBackgroundColor] = useState(initialBackgroundColor)
- const [currentTextColor, setCurrentTextColor] = useState(textColor)
+ const [currentTextColor, setCurrentTextColor] = useState(defaultSettings.textColor)
const handleButtonClick = () => {
- const isSelected = backgroundColor !== selectedButtonColor
- setBackgroundColor(isSelected ? selectedButtonColor : defaultButtonColor)
- if (textColor !== palette.WHITE) {
- setCurrentTextColor(isSelected ? palette.WHITE : textColor)
+ const isSelected = backgroundColor !== defaultSettings.selectedButtonColor
+ setBackgroundColor(
+ isSelected ? defaultSettings.selectedButtonColor : defaultSettings.defaultButtonColor,
+ )
+ if (defaultSettings.textColor !== palette.WHITE) {
+ setCurrentTextColor(isSelected ? palette.WHITE : defaultSettings.textColor)
}
if (onClick) onClick(isSelected)
}
diff --git a/src/components/layouts/Layout.tsx b/src/components/layouts/Layout.tsx
index ecdc23be..e6e41eb6 100644
--- a/src/components/layouts/Layout.tsx
+++ b/src/components/layouts/Layout.tsx
@@ -1,12 +1,18 @@
+import 'react-toastify/dist/ReactToastify.css'
+
import styled from '@emotion/styled'
import { Outlet } from 'react-router-dom'
+import { ToastContainer } from 'react-toastify'
+import Modal from '@/components/common/Modal'
import { theme } from '@/styles/theme'
const Layout = () => {
return (
+
+
)
}
diff --git a/src/hooks/useModal.tsx b/src/hooks/useModal.tsx
new file mode 100644
index 00000000..de18310e
--- /dev/null
+++ b/src/hooks/useModal.tsx
@@ -0,0 +1,22 @@
+import Modal from '@/components/common/Modal'
+import useModalStore from '@/store/ModalStore'
+
+type ModalConfirmPropsType = {
+ type: 'warn' | 'confirm'
+ okFunc: () => void
+ mainText: string
+ subText?: string
+}
+export const useModal = () => {
+ const { setModalState, setOkFunc, setMainText, setSubText, setType } = useModalStore()
+
+ const openModal = ({ mainText, subText, okFunc, type }: ModalConfirmPropsType) => {
+ setModalState(true)
+ setType(type)
+ setMainText(mainText)
+ setSubText(subText)
+ setOkFunc(okFunc)
+ }
+
+ return { openModal, Modal }
+}
diff --git a/src/hooks/useToast.tsx b/src/hooks/useToast.tsx
new file mode 100644
index 00000000..cf7581cb
--- /dev/null
+++ b/src/hooks/useToast.tsx
@@ -0,0 +1,25 @@
+import { ReactNode } from 'react'
+import { toast } from 'react-toastify'
+
+type ToastType = 'success' | 'error' | 'info' | 'warning'
+
+type ToastProps = {
+ message: string | ReactNode
+ type: ToastType
+ isDarkMode: boolean
+}
+
+export const useToast = () => {
+ const showToast = ({ message, type, isDarkMode }: ToastProps) => {
+ toast(message, {
+ position: 'top-center',
+ draggable: true,
+ theme: isDarkMode ? 'dark' : 'light',
+ type,
+ })
+ }
+
+ return { showToast }
+}
+
+export default useToast
diff --git a/src/store/ModalStore.tsx b/src/store/ModalStore.tsx
new file mode 100644
index 00000000..c87e8751
--- /dev/null
+++ b/src/store/ModalStore.tsx
@@ -0,0 +1,28 @@
+import { create } from 'zustand'
+
+type ModalState = {
+ modalState: boolean
+ type: 'confirm' | 'warn'
+ okFunc: () => void
+ mainText: string
+ subText?: string | undefined
+ setType: (type: 'confirm' | 'warn') => void
+ setSubText: (text: string | undefined) => void
+ setModalState: (state: boolean) => void
+ setMainText: (text: string) => void
+ setOkFunc: (func: () => void) => void
+}
+
+const useModalStore = create((set) => ({
+ modalState: false,
+ okFunc: () => {},
+ mainText: '',
+ subText: '',
+ type: 'confirm',
+ setType: (type) => set({ type: type }),
+ setSubText: (text) => set({ subText: text }),
+ setModalState: (state) => set({ modalState: state }),
+ setMainText: (text) => set({ mainText: text }),
+ setOkFunc: (func) => set({ okFunc: func }),
+}))
+export default useModalStore
diff --git a/src/styles/global.ts b/src/styles/global.ts
index cafaa3e1..2230a98c 100644
--- a/src/styles/global.ts
+++ b/src/styles/global.ts
@@ -6,19 +6,12 @@ export const globalStyle = css`
${emotionReset}
@font-face {
- font-family: 'InkLipquid';
- src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_one@1.0/InkLipquid.woff')
- format('woff');
- font-weight: normal;
- font-style: normal;
- }
-
- @font-face {
- font-family: 'Pretendard';
+ font-family: 'Pretendard-Regular';
src: url('https://cdn.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff')
format('woff');
font-weight: 400;
font-style: normal;
+ font-display: swap;
}
body {
diff --git a/tsconfig.json b/tsconfig.json
index 51f7e0f0..027d4a68 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -30,7 +30,8 @@
"@/hooks/*": ["src/hooks/*"],
"@/assets/*": ["src/assets/*"],
"@/styles/*": ["src/styles/*"],
- "@/mocks/*": ["src/mocks/*"]
+ "@/mocks/*": ["src/mocks/*"],
+ "@/store/*": ["src/store/*"]
}
},
"include": ["src"],