diff --git a/src/apis/user.tsx b/src/apis/user.tsx index c4910924..bc9e03e1 100644 --- a/src/apis/user.tsx +++ b/src/apis/user.tsx @@ -1,6 +1,6 @@ import { APIJob, APIJobGroup } from '@/types/job'; import { APIUser, APIUserProfile } from '@/types/user'; -import { publicApi } from './core/axios'; +import { publicApi } from '@/apis/core/axios'; const userAPI = { getUserProfile: ({ userId }: { userId: APIUser['userId'] }) => diff --git a/src/app/bookshelf/[bookshelfId]/page.tsx b/src/app/bookshelf/[bookshelfId]/page.tsx index 5a6846d6..8f791384 100644 --- a/src/app/bookshelf/[bookshelfId]/page.tsx +++ b/src/app/bookshelf/[bookshelfId]/page.tsx @@ -12,7 +12,7 @@ import useMutateBookshelfLikeQuery from '@/queries/bookshelf/useMutateBookshelfL import { useMyProfileId } from '@/queries/user/useMyProfileQuery'; import { checkAuthentication } from '@/utils/helpers'; import { IconKakao } from '@public/icons'; -import { KAKAO_LOGIN_URL } from '@/constants/url'; +import { KAKAO_LOGIN_URL } from '@/constants'; import useToast from '@/v1/base/Toast/useToast'; import TopNavigation from '@/v1/base/TopNavigation'; diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 76709dbd..23566dbe 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -4,7 +4,7 @@ import Image from 'next/image'; import Link from 'next/link'; import { IconKakao } from '@public/icons'; -import { KAKAO_LOGIN_URL } from '@/constants/url'; +import { KAKAO_LOGIN_URL } from '@/constants'; import Button from '@/v1/base/Button'; diff --git a/src/app/profile/me/page.tsx b/src/app/profile/me/page.tsx index 4c5722fa..ac94e0b6 100644 --- a/src/app/profile/me/page.tsx +++ b/src/app/profile/me/page.tsx @@ -2,11 +2,13 @@ import Link from 'next/link'; import { useRouter } from 'next/navigation'; +import { useQueryClient } from '@tanstack/react-query'; import userAPI from '@/apis/user'; +import userKeys from '@/queries/user/key'; import { checkAuthentication, removeAuth } from '@/utils/helpers'; - +import { KAKAO_LOGIN_URL } from '@/constants'; import { IconArrowRight } from '@public/icons'; import SSRSafeSuspense from '@/components/SSRSafeSuspense'; @@ -20,11 +22,8 @@ import BookShelf from '@/v1/bookShelf/BookShelf'; import ProfileBookShelf from '@/v1/profile/bookShelf/ProfileBookShelf'; import ProfileGroup from '@/v1/profile/group/ProfileGroup'; import ProfileInfo from '@/v1/profile/info/ProfileInfo'; -import userKeys from '@/queries/user/key'; -import { useQueryClient } from '@tanstack/react-query'; const USER_ID = 'me'; -const KAKAO_LOGIN_URL = `${process.env.NEXT_PUBLIC_API_URL}/oauth2/authorize/kakao?redirect_uri=${process.env.NEXT_PUBLIC_CLIENT_REDIRECT_URI}`; const MyProfilePage = () => { const isAuthenticated = checkAuthentication(); diff --git a/src/constants/FormRule/index.ts b/src/constants/FormRule/index.ts deleted file mode 100644 index 9b8a3b81..00000000 --- a/src/constants/FormRule/index.ts +++ /dev/null @@ -1,120 +0,0 @@ -import type { RegisterOptions } from 'react-hook-form'; - -const FORM_RULES: { - [index: string]: RegisterOptions; -} = { - nickname: { - required: '닉네임을 입력해주세요.', - minLength: { - value: 2, - message: '닉네임을 2자 이상 입력해주세요.', - }, - maxLength: { - value: 10, - message: '닉네임을 10자 이하로 입력해주세요.', - }, - pattern: { - value: /^[가-힣0-9a-zA-Z]{2,10}$/, - message: '한글, 영문, 숫자만 입력 가능해요.', - }, - }, - email: { - required: '이메일을 입력해주세요', - pattern: { - value: - /([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/, - message: '이메일 형식을 다시 확인해주세요.', - }, - }, - jobGroup: { - required: '직군을 선택해주세요.', - }, - job: { - required: '직업을 선택해주세요.', - }, - title: { - required: '모임 제목을 입력해 주세요.', - minLength: { - value: 2, - message: '모임 제목을 2자 이상 입력해 주세요.', - }, - maxLength: { - value: 30, - message: '모임 제목을 30자 이하로 입력해 주세요.', - }, - }, - introduce: { - required: '모임 설명을 입력해 주세요.', - minLength: { - value: 10, - message: '모임 설명을 10자 이상 입력해 주세요.', - }, - maxLength: { - value: 150, - message: '모임 설명을 150자 이하로 입력해 주세요.', - }, - }, - maxMemberCount: { - required: '모임 인원을 입력해 주세요.', - min: { - value: 1, - message: '모임 인원을 1명 이상 입력해 주세요.', - }, - max: { - value: 500, - message: '인원이 너무 많습니다.', - }, - pattern: { - value: /^[0-9]+$/, - message: '숫자를 입력해 주세요.', - }, - }, - startDate: { - required: '모임 시작일을 선택해 주세요.', - validate: value => - new Date(value).setHours(0, 0, 0, 0) >= new Date().setHours(0, 0, 0, 0) || - '시작일은 오늘부터 가능해요.', - }, - endDate: { - required: '모임 종료일을 선택해 주세요.', - validate: { - possible: (_, { startDate, endDate }) => - new Date(startDate) <= new Date(endDate) || - '종료일은 시작일보다 늦어야 해요.', - }, - }, - bookId: { - required: true, - validate: { - positive: value => Number(value) > 0, - }, - }, - joinQuestion: { - required: '문제 설명을 입력해 주세요.', - minLength: { - value: 5, - message: '문제 설명을 5자 이상 입력해 주세요.', - }, - maxLength: { - value: 30, - message: '문제 설명을 30자 이하로 입력해 주세요.', - }, - }, - joinPasswd: { - required: '정답을 입력해 주세요.', - minLength: { - value: 1, - message: '정답을 1자 이상 입력해 주세요.', - }, - maxLength: { - value: 30, - message: '정답을 10자 이하로 입력해 주세요.', - }, - pattern: { - value: /^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z0-9]{1,10}$/, - message: '띄어쓰기 없이 정답을 입력해 주세요.', - }, - }, -} as const; - -export default FORM_RULES; diff --git a/src/constants/groupRadioValues.ts b/src/constants/groupRadioValues.ts index 264d931c..6851c1b0 100644 --- a/src/constants/groupRadioValues.ts +++ b/src/constants/groupRadioValues.ts @@ -1,46 +1,3 @@ -export const MAX_MEMBER_COUNT_VALUE = [ - { - value: 'null', - text: '제한없음', - }, - { - value: '500', - text: '500명', - }, - { - value: '200', - text: '200명', - }, - { - value: '100', - text: '100명', - }, - { - value: '50', - text: '50명', - }, - { - value: '직접입력', - text: '직접입력', - }, -]; - -export const MAX_MEMBER_DEFAULT_VALUE = 'null'; - -export const IS_PUBLICK_VALUE = [ - { value: 'true', text: '공개' }, - { value: 'false', text: '비공개' }, -]; - -export const IS_PUBLICK_DEFAULT_VALUE = 'true'; - -export const HAS_JOIN_PASSWORD_VALUE = [ - { value: 'true', text: '필요' }, - { value: 'false', text: '불필요' }, -]; - -export const HAS_JOIN_PASSWORD_DEFAULT_VALUE = 'false'; - export const MAX_MEMBER_COUNT_OPTIONS = [ { label: '제한없음', value: 9999 }, { label: '50명', value: 50 }, diff --git a/src/constants/index.ts b/src/constants/index.ts index a42b42ad..c8d04931 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,3 +1,4 @@ export * from './storage'; export * from './error'; export * from './groupRadioValues'; +export * from './url'; diff --git a/src/constants/initialBookGroupComments.ts b/src/constants/initialBookGroupComments.ts deleted file mode 100644 index 7a7ad88f..00000000 --- a/src/constants/initialBookGroupComments.ts +++ /dev/null @@ -1,38 +0,0 @@ -export const initialBookGroupComments = [ - { - userId: 1, - commentId: 11414, - contents: - '다독 서비스 개발자 백민종님은 누구보다 열정적이고 훌륭한 개발자입니다. 리더십이 뛰어납니다. 그리고 내기를 좋아합니다. 도박을 좋아하는 것은 아니고 콜라, 커피내기를 주로 합니다. 승률은 낮습니다.', - userProfileImage: '', - nickname: '고양시MZ', - writtenByCurrentUser: false, - }, - { - userId: 2, - commentId: 53452, - contents: - '다독 서비스 개발자 우대현님은 착하고 성실하고 성숙하고 센스있고 어른스럽습니다. 죄송합니다. 제가 이 부분을 만들었기 때문에 제가 상상하는 미래의 제 모습을 적어 보았습니다.', - userProfileImage: '', - nickname: 'OLDBOY', - writtenByCurrentUser: false, - }, - { - userId: 3, - commentId: 36363, - contents: - '다독 서비스 개발자 김규란님은 근면성실은 아니지만 정말 열심히 공부하고 개발하는 개발자 입니다. 운동신경이 뛰어납니다. 저희 팀 내에서 운동 관련 승률이 가장 높습니다. 이기고 싶다면 규란님과 팀을 해야 합니다.', - userProfileImage: '', - nickname: 'fired계란', - writtenByCurrentUser: false, - }, - { - userId: 4, - commentId: 67951, - contents: - '다독 서비스 개발자 김재현님은 의외성 No.1 입니다. 맡은 일을 뚝딱뚝딱 잘해내 팀원들을 놀라게 하는것이 특기입니다. 또한 프로젝트 기간 동안 데브코스 강의장과 가장 가까이 지내면서 여유를 부리다 늦게와 구론산을 많이 샀습니다.(늦게 오는것은 아니고 항상 1-3분 정도? ㅎ)', - userProfileImage: '', - nickname: 'OneMetro', - writtenByCurrentUser: false, - }, -]; diff --git a/src/hooks/auth/atoms/index.ts b/src/hooks/auth/atoms/index.ts deleted file mode 100644 index db67d71f..00000000 --- a/src/hooks/auth/atoms/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { atom, useRecoilState, useResetRecoilState } from 'recoil'; - -import { ACCESS_TOKEN_STORAGE_KEY } from '@/constants/index'; -import webStorage from '@/utils/storage'; - -const accessTokenAtom = atom({ - key: 'accessToken', - default: null, - effects: [ - ({ setSelf }) => { - const storage = webStorage(ACCESS_TOKEN_STORAGE_KEY); - const storedToken = storage.get(); - - storedToken && setSelf(storedToken); - }, - ], -}); - -const useAccessToken = () => useRecoilState(accessTokenAtom); -const useResetAccessToken = () => useResetRecoilState(accessTokenAtom); - -export { useAccessToken, useResetAccessToken }; diff --git a/src/hooks/auth/index.ts b/src/hooks/auth/index.ts deleted file mode 100644 index 7fa087d9..00000000 --- a/src/hooks/auth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as useAuth } from './useAuth'; diff --git a/src/hooks/auth/useAuth.ts b/src/hooks/auth/useAuth.ts deleted file mode 100644 index 6a7783a3..00000000 --- a/src/hooks/auth/useAuth.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ACCESS_TOKEN_STORAGE_KEY } from '@/constants/index'; -import webStorage from '@/utils/storage'; - -const useAuth = () => { - const storage = webStorage(ACCESS_TOKEN_STORAGE_KEY); - const accessToken = storage.get(); - - const setAuth = (newToken: string) => { - storage.set(newToken); - }; - - const removeAuth = () => { - storage.remove(); - }; - - return { - isAuthed: !!accessToken, - setAuth, - removeAuth, - }; -}; - -export default useAuth; diff --git a/src/hooks/toast/atoms/index.ts b/src/hooks/toast/atoms/index.ts deleted file mode 100644 index 6d5cb7af..00000000 --- a/src/hooks/toast/atoms/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { atom } from 'recoil'; - -export interface ToastAtom { - id: number; - message: string; - duration?: number; -} - -export const toastsAtom = atom({ - key: 'toasts', - default: [], -}); diff --git a/src/hooks/toast/index.ts b/src/hooks/toast/index.ts deleted file mode 100644 index 1077520c..00000000 --- a/src/hooks/toast/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as useToast } from './useToast'; diff --git a/src/hooks/toast/useToast.ts b/src/hooks/toast/useToast.ts deleted file mode 100644 index 1d6759cc..00000000 --- a/src/hooks/toast/useToast.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { useCallback } from 'react'; -import { useSetRecoilState } from 'recoil'; -import { ToastAtom, toastsAtom } from './atoms'; - -const useToast = () => { - const setToasts = useSetRecoilState(toastsAtom); - - const hideToast = useCallback( - (toastId: ToastAtom['id']) => { - setToasts(currentToasts => - currentToasts.filter(toast => toast.id !== toastId) - ); - }, - [setToasts] - ); - - const showToast = useCallback( - ({ message, duration = 2000 }: Pick) => { - const id = new Date().getTime(); - setToasts(currentToasts => [...currentToasts, { message, duration, id }]); - setTimeout(() => hideToast(id), 500 + duration); - }, - [hideToast, setToasts] - ); - - return { showToast }; -}; - -export default useToast; diff --git a/src/hooks/useScroll.ts b/src/hooks/useScroll.ts deleted file mode 100644 index a2aefbb7..00000000 --- a/src/hooks/useScroll.ts +++ /dev/null @@ -1,21 +0,0 @@ -import debounce from '@/utils/debounce'; -import { useState, useEffect } from 'react'; - -export function useScroll() { - const [scroll, setScroll] = useState(0); - - const listener = () => { - setScroll(window.pageYOffset); - }; - - const delay = 60; - - useEffect(() => { - window.addEventListener('scroll', debounce(listener, delay)); - return () => window.removeEventListener('scroll', listener); - }); - - return { - scroll, - }; -} diff --git a/src/hooks/useToggle.ts b/src/hooks/useToggle.ts deleted file mode 100644 index a51f7d43..00000000 --- a/src/hooks/useToggle.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useState } from 'react'; - -const useToggle = () => { - const [value, setValue] = useState(false); - const toggle = () => setValue(value => !value); - - return { value, toggle, setValue }; -}; - -export default useToggle; diff --git a/src/ui/AuthRequired/index.tsx b/src/ui/AuthRequired/index.tsx deleted file mode 100644 index a58f4877..00000000 --- a/src/ui/AuthRequired/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ReactNode, useEffect, useState } from 'react'; - -import { useAuth } from '@/hooks/auth'; -import { useRouter } from 'next/navigation'; - -const AuthRequired = ({ children }: { children: ReactNode }) => { - const router = useRouter(); - const [mounted, setMounted] = useState(false); - const { isAuthed } = useAuth(); - - useEffect(() => { - if (!isAuthed) { - router.push('/'); - } - setMounted(true); - }, [isAuthed, router]); - - if (!isAuthed) return null; - - return <>{mounted && children}; -}; - -export default AuthRequired; diff --git a/src/ui/BookDetail/BookComment.tsx b/src/ui/BookDetail/BookComment.tsx deleted file mode 100644 index 476da646..00000000 --- a/src/ui/BookDetail/BookComment.tsx +++ /dev/null @@ -1,189 +0,0 @@ -import { - AlertDialog, - AlertDialogBody, - AlertDialogContent, - AlertDialogFooter, - AlertDialogOverlay, - Avatar, - Button, - Flex, - Text, - useDisclosure, - useTheme, - VStack, -} from '@chakra-ui/react'; -import { CSSProperties, MutableRefObject, useRef } from 'react'; -import Link from 'next/link'; - -import { useToast } from '@/hooks/toast'; -import type { APIBookComment } from '@/types/book'; -import { Menu, MenuItem } from '@/ui/common/Menu'; - -import CommentDrawer from './CommentDrawer'; - -interface Props - extends Pick< - APIBookComment, - | 'commentId' - | 'userId' - | 'nickname' - | 'userProfileImage' - | 'createdAt' - | 'contents' - > { - style?: CSSProperties; - editable?: boolean; - onEdit?: (commentId: number, comment: string) => void; - onDelete?: (commentId: number) => void; -} - -const BookComment = ({ - commentId, - userId, - nickname, - userProfileImage, - createdAt, - contents, - editable = false, - onEdit, - onDelete, - style, -}: Props) => { - const { - isOpen: isDeleteModalOpen, - onClose: onDeleteModalClose, - onOpen: onDeleteModalOpen, - } = useDisclosure(); - const { - isOpen: isEditDrawerOpen, - onClose: onEditDrawerClose, - onOpen: onEditDrawerOpen, - } = useDisclosure(); - const { showToast } = useToast(); - - const cancelRef = useRef(null); - const textareaRef = useRef(null); - - const handleCommentEdit = () => { - const comment = textareaRef.current?.value; - - if (!comment) { - showToast({ message: '코멘트를 입력해주세요!' }); - return; - } - - onEdit && onEdit(commentId, comment); - onEditDrawerClose(); - }; - - const handleCommentDelete = () => { - onDelete && onDelete(commentId); - onDeleteModalClose(); - }; - - return ( - - - - - - {nickname} - - - {createdAt} - - - {editable && ( - - - - - - - - - )} - - - {contents} - - - ); -}; - -const DeleteComfirmDialog = ({ - cancelRef, - isOpen, - onClose, - onDelete, -}: { - isOpen: boolean; - onClose: () => void; - onDelete: () => void; - cancelRef: MutableRefObject; -}) => { - const theme = useTheme(); - - return ( - - - - - 코멘트를 정말 삭제할까요? - - - - - - - - - ); -}; - -export default BookComment; diff --git a/src/ui/BookDetail/BookCommentList.tsx b/src/ui/BookDetail/BookCommentList.tsx deleted file mode 100644 index 2a10671d..00000000 --- a/src/ui/BookDetail/BookCommentList.tsx +++ /dev/null @@ -1,171 +0,0 @@ -'use clinet'; - -import { useDisclosure, VStack } from '@chakra-ui/react'; -import { useMemo, useRef } from 'react'; - -import bookAPI from '@/apis/book'; -import useBookCommentsQuery from '@/queries/book/useBookCommentsQuery'; -import Button from '@/ui/common/Button'; -import BookComment from './BookComment'; -import CommentDrawer from './CommentDrawer'; - -import type { APIBookComment, APIBookmarkedUserList } from '@/types/book'; - -import { SERVICE_ERROR_MESSAGE } from '@/constants'; -import { useToast } from '@/hooks/toast'; -import LoginBottomSheet from '@/ui/LoginBottomSheet'; -import { - checkAuthentication, - isAxiosErrorWithCustomCode, -} from '@/utils/helpers'; - -interface Props { - bookId: number; - isInMyBookshelf: APIBookmarkedUserList['isInMyBookshelf']; -} - -type CommentType = 'me' | 'user'; -type CommentRecordType = Record; - -const BookCommentList = ({ bookId, isInMyBookshelf }: Props) => { - const isAuthenticated = checkAuthentication(); - const { showToast } = useToast(); - const commentTextAreaRef = useRef(null); - const bookCommentsQueryInfo = useBookCommentsQuery(bookId); - - const { - isOpen: isCreateDrawerOpen, - onOpen: onCreateDrawerOpen, - onClose: onCreateDrawerClose, - } = useDisclosure(); - const { - isOpen: isLoginBottomSheetOpen, - onOpen: onLoginBottomSheetOpen, - onClose: onLoginBottomSheetsClose, - } = useDisclosure(); - - const comments = useMemo(() => { - const defaultComments = { me: [], user: [] }; - - if (!bookCommentsQueryInfo.isSuccess) { - return defaultComments; - } - - return bookCommentsQueryInfo.data.bookComments - .filter(comment => comment.bookId == bookId) - .reduce( - (acc, comment) => ({ - ...acc, - [comment.writtenByCurrentUser ? 'me' : 'user']: [ - ...acc[comment.writtenByCurrentUser ? 'me' : 'user'], - comment, - ], - }), - defaultComments - ); - }, [bookCommentsQueryInfo, bookId]); - - const handleCommentError = (error: unknown) => { - if (!isAxiosErrorWithCustomCode(error)) { - return; - } - - const { code } = error.response.data; - const message = SERVICE_ERROR_MESSAGE[code]; - - if (code === 'BC1' || code === 'BC2') { - showToast({ message, duration: 3000 }); - } - }; - - const handleCommentCreate = () => { - const comment = commentTextAreaRef.current?.value; - - if (!comment) { - showToast({ message: '코멘트를 입력해주세요!' }); - return; - } - - bookAPI - .creaetComment(bookId, comment) - .then(() => bookCommentsQueryInfo.refetch()) - .catch(handleCommentError) - .finally(onCreateDrawerClose); - }; - - const handleCommentEdit = (commentId: number, comment: string) => { - bookAPI - .patchComment({ bookId, data: { commentId, comment } }) - .then(() => bookCommentsQueryInfo.refetch()) - .catch(handleCommentError); - }; - - const handleCommentDelete = (commentId: number) => { - bookAPI - .deletComment(bookId, commentId) - .then(() => bookCommentsQueryInfo.refetch()) - .catch(handleCommentError); - }; - - const handleCreateCommentDrawerOpen = () => { - if (!isAuthenticated) { - onLoginBottomSheetOpen(); - return; - } - - if (!isInMyBookshelf) { - showToast({ - message: '책장에 책을 꽂은 후에 코멘트를 남길 수 있어요.', - duration: 3000, - }); - return; - } - - onCreateDrawerOpen(); - }; - - return ( - - {!isAuthenticated && ( - - )} - {!bookCommentsQueryInfo.isLoading && !comments.me.length && ( - <> - - - - )} - {comments.me.map(comment => ( - - ))} - {comments.user.map(comment => ( - - ))} - - ); -}; - -export default BookCommentList; diff --git a/src/ui/BookDetail/index.tsx b/src/ui/BookDetail/index.tsx index 9ed5ae06..8f1eb6a8 100644 --- a/src/ui/BookDetail/index.tsx +++ b/src/ui/BookDetail/index.tsx @@ -1,2 +1 @@ export { default as BookInfo } from './BookInfo'; -export { default as BookCommentList } from './BookCommentList'; diff --git a/src/ui/FormInput/index.tsx b/src/ui/FormInput/index.tsx deleted file mode 100644 index b1057f71..00000000 --- a/src/ui/FormInput/index.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import FORM_RULES from '@/constants/FormRule'; -import { - FormControl, - FormErrorMessage, - FormLabel, - Input, - InputGroup, - InputRightElement, - Text, - useTheme, -} from '@chakra-ui/react'; -import CloseIcon from '@public/icons/close.svg'; -import { useEffect, useState } from 'react'; -import { useFormContext } from 'react-hook-form'; -import type { MouseEvent, TouchEvent } from 'react'; - -interface FormInputProps - extends Partial> { - label: string; - name: string; -} - -const FormInput = ({ - label, - name, - disabled = false, - type = 'text', -}: FormInputProps) => { - const theme = useTheme(); - const { colors } = theme; - - const { - register, - unregister, - setValue, - trigger, - getValues, - formState: { errors }, - } = useFormContext(); - - const error = errors[name]; - - const rules = FORM_RULES[name]; - - const onClearButtonClick = async ( - event: MouseEvent | TouchEvent - ) => { - setValue(name, ''); - await trigger(name); - event.preventDefault(); - }; - - const [isFocus, setIsFocus] = useState(false); - - const onInputFocus = () => { - setIsFocus(true); - }; - - const onInputBlur = () => { - setIsFocus(false); - }; - - useEffect(() => { - return () => { - unregister(name); - }; - }, [unregister, name, disabled]); - - return ( - - - {label} - {!!rules.required && ( - - * - - )} - - - {disabled ? ( - - ) : ( - - )} - - {isFocus && getValues(name).length && ( - - - - )} - - {error?.message && ( - {String(error.message)} - )} - - ); -}; - -export default FormInput; diff --git a/src/ui/FormSelect/index.tsx b/src/ui/FormSelect/index.tsx deleted file mode 100644 index f2b23442..00000000 --- a/src/ui/FormSelect/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import FORM_RULES from '@/constants/FormRule'; -import { - FormControl, - FormErrorMessage, - FormLabel, - Select, - Text, - useTheme, -} from '@chakra-ui/react'; -import type { ReactNode } from 'react'; -import { useFormContext } from 'react-hook-form'; - -interface FormSelectProps { - label: string; - name: string; - children: ReactNode; -} - -const FormSelect = ({ label, name, children }: FormSelectProps) => { - const theme = useTheme(); - const { - register, - formState: { errors }, - } = useFormContext(); - - const error = errors[name]; - const rules = FORM_RULES[name]; - - return ( - - - {label} - {!!rules.required && ( - - * - - )} - - - {error?.message && ( - {String(error.message)} - )} - - ); -}; - -export default FormSelect; diff --git a/src/ui/Group/EditGroupForm.tsx b/src/ui/Group/EditGroupForm.tsx deleted file mode 100644 index 1c272e13..00000000 --- a/src/ui/Group/EditGroupForm.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { Box, Flex, Image, useTheme } from '@chakra-ui/react'; -import { FormProvider, useForm } from 'react-hook-form'; - -import FormInput from '@/ui/FormInput'; -import { APIGroupDetail } from '@/types/group'; -import GroupAPI from '@/apis/group'; -import { useRouter } from 'next/navigation'; - -interface EditGroupFormProps { - group: APIGroupDetail; -} - -const EditGroupForm = ({ group }: EditGroupFormProps) => { - const theme = useTheme(); - const router = useRouter(); - const methods = useForm({ - mode: 'all', - defaultValues: { - title: group.title, - introduce: group.introduce, - startDate: group.startDate, - maxMemberCount: group.maxMemberCount, - endDate: group.endDate, - }, - }); - - const onSubmit: Parameters[0] = async ({ - title, - introduce, - maxMemberCount, - endDate, - }) => { - try { - await GroupAPI.updateGroupInfo({ - bookGroupId: group.bookGroupId, - group: { title, introduce, endDate, maxMemberCount }, - }); - - router.push(`/group/${group.bookGroupId}`); - } catch (error) { - console.error(error); - } - }; - - return ( - <> - - - - book-cover - - - - - - - - - 모임 수정하기 - - - - - ); -}; - -export default EditGroupForm; diff --git a/src/ui/Group/GroupComment/index.tsx b/src/ui/Group/GroupComment/index.tsx deleted file mode 100644 index 78ddac9e..00000000 --- a/src/ui/Group/GroupComment/index.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { Avatar, Box, Flex, Highlight, Text } from '@chakra-ui/react'; -import Link from 'next/link'; - -import { initialBookGroupComments } from '@/constants/initialBookGroupComments'; -import { APIGroupComment } from '@/types/group'; -import { checkAuthentication } from '@/utils/helpers'; -import CommentDeleteModal from './CommentDeleteModal'; -import CommentModifyModal from './CommentModifyModal'; -import GuideMessage from './GuideMessage'; -import { Menu, MenuItem } from '@/ui/common/Menu'; - -interface commentsListProps { - isGroupMember: boolean; - isPublic: boolean; - isEmpty: boolean; - commentsListData: APIGroupComment[]; - handleDeleteCommentBtnClick: (commentId: number) => void; - handleModifyCommentBtnClick: ( - modifiedComment: string, - commentId: number - ) => void; -} - -const CommentsList = ({ - isGroupMember, - isPublic, - isEmpty, - commentsListData, - handleDeleteCommentBtnClick, - handleModifyCommentBtnClick, -}: commentsListProps) => { - const isAuthenticated = checkAuthentication(); - const getFilteredComments = () => { - const commentsLength = commentsListData.length; - - if (!isAuthenticated && !isPublic && commentsLength < 5) { - return initialBookGroupComments.slice(0, commentsLength); - } else if (!isAuthenticated && !isPublic) { - return initialBookGroupComments; - } - - if (isAuthenticated && !isPublic && !isGroupMember && commentsLength < 5) { - return initialBookGroupComments.slice(0, commentsLength); - } else if (isAuthenticated && !isPublic && !isGroupMember) { - return initialBookGroupComments; - } - return commentsListData; - }; - - const filteredComments = getFilteredComments(); - - return isEmpty ? ( - - - 첫 번째 글 작성의 주인공이 되어주세요. - - - ) : ( - - - 댓글 - - - {filteredComments && - filteredComments.map( - ({ - commentId, - userId, - contents, - userProfileImage, - nickname, - writtenByCurrentUser, - }) => { - return ( - - - - - - {nickname} - - - {writtenByCurrentUser && ( - - - - - - - - - )} - - - {contents} - - - ); - } - )} - - - - ); -}; - -export default CommentsList; diff --git a/src/ui/Group/GroupDetail/GroupInfo.tsx b/src/ui/Group/GroupDetail/GroupInfo.tsx deleted file mode 100644 index 6d0caa75..00000000 --- a/src/ui/Group/GroupDetail/GroupInfo.tsx +++ /dev/null @@ -1,322 +0,0 @@ -import { - AlertDialog, - AlertDialogBody, - AlertDialogContent, - AlertDialogFooter, - AlertDialogOverlay, - Box, - Button, - Flex, - Image, - Input, - Text, - useDisclosure, - useTheme, -} from '@chakra-ui/react'; -import { MutableRefObject, useRef, useState } from 'react'; -import Link from 'next/link'; - -import { APIGroupDetail } from '@/types/group'; -import BottomSheet from '@/ui/common/BottomSheet'; - -import { useToast } from '@/hooks/toast'; -import TopNavigation from '@/ui/common/TopNavigation'; -import { Menu, MenuItem } from '@/ui/common/Menu'; -import LoginBottomSheet from '@/ui/LoginBottomSheet'; -import { checkAuthentication } from '@/utils/helpers'; - -interface GroupInfoProps { - groupInfoData: APIGroupDetail; - handleParticipateBtnClick: ( - password?: string, - onSuccess?: () => void, - onFailed?: () => void - ) => void; - handleDeleteGroupBtnClick: () => void; -} - -const GroupInfo = ({ - groupInfoData, - handleParticipateBtnClick, - handleDeleteGroupBtnClick, -}: GroupInfoProps) => { - const isAuthenticated = checkAuthentication(); - const [password, setPassword] = useState(''); - const cancelRef = useRef(null); - const { - isOpen: isLoginModalOpen, - onOpen: onLoginModalOpen, - onClose: onLoginModalClose, - } = useDisclosure(); - const { - isOpen: isPasswordModalOpen, - onOpen: onPasswordModalOpen, - onClose: onPasswordModalClose, - } = useDisclosure(); - const { - isOpen: isDeleteModalOpen, - onClose: onDeleteModalClose, - onOpen: onDeleteModalOpen, - } = useDisclosure(); - - const { showToast } = useToast(); - - const { - bookGroupId, - title, - introduce, - startDate, - endDate, - hasJoinPasswd, - joinQuestion, - isPublic: _isPublic, - maxMemberCount: _maxMemberCount, - currentMemberCount, - commentCount, - owner: _owner, - book, - isOwner, - isGroupMember, - } = groupInfoData; - - const message = hasJoinPasswd ? '가입 비밀번호 입력 필요' : '바로 참여 가능'; - - const onDeleteGroupClick = () => { - if (currentMemberCount > 1) { - showToast({ - message: '혼자가 아니면 다른 모임원들이 있어 모임 삭제가 불가능해요!', - }); - onDeleteModalClose(); - return; - } - handleDeleteGroupBtnClick(); - onDeleteModalClose(); - }; - - const onChangePassword = (event: React.ChangeEvent) => { - setPassword(event.target.value); - }; - - const onPartInButtonClick = () => { - if (!isAuthenticated) { - onLoginModalOpen(); - return; - } - hasJoinPasswd ? onPasswordModalOpen() : handleParticipateBtnClick(); - }; - - return ( - <> - - - {isOwner && ( - - - - - - - )} - - - - - {title} - - - {introduce} - - - - - - - - {startDate} ~ {endDate} - - - - {book.title} - - - - - - {isGroupMember ? '' : message} - - - - - peopleIcon - - - {currentMemberCount} - - - - - commentIcon - - - {commentCount} - - - - - - - - - bookCover - - - - - {!isOwner && ( - <> - - - - - - - 질문 - - - {joinQuestion} - - - - - 정답 - - - - - - - - )} - - - - ); -}; - -export default GroupInfo; - -const DeleteComfirmDialog = ({ - cancelRef, - isOpen, - onClose, - onDelete, -}: { - isOpen: boolean; - onClose: () => void; - onDelete: () => void; - cancelRef: MutableRefObject; -}) => { - const theme = useTheme(); - - return ( - - - - - 모임을 정말 삭제할까요? - - - - - - - - - ); -}; diff --git a/src/ui/Group/GroupDetail/index.tsx b/src/ui/Group/GroupDetail/index.tsx deleted file mode 100644 index b9f8756e..00000000 --- a/src/ui/Group/GroupDetail/index.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { Flex, VStack, Skeleton } from '@chakra-ui/react'; -import { useRouter } from 'next/navigation'; - -import GroupInfo from '@/ui/Group/GroupDetail/GroupInfo'; -import CommentInputBox from '../GroupComment/CommentInputBox'; -import CommentsList from '../GroupComment'; -import useGroupInfoQuery from '@/queries/group/useGroupInfoQuery'; -import useGroupCommentsQuery from '@/queries/group/useBookGroupCommentsQuery'; -import GroupAPI from '@/apis/group'; -import { useToast } from '@/hooks/toast'; - -interface GroupDetailProps { - bookGroupId: number; -} - -const GroupDetail = ({ bookGroupId }: GroupDetailProps) => { - const groupInfoQuery = useGroupInfoQuery({ bookGroupId }); - const groupCommentsQuery = useGroupCommentsQuery(bookGroupId); - const { showToast } = useToast(); - const router = useRouter(); - - if (groupInfoQuery.isLoading || groupCommentsQuery.isLoading) - return ( - - - - - - - ); - - const isSuccess = groupInfoQuery.isSuccess && groupCommentsQuery.isSuccess; - if (!isSuccess) return null; - - const handleParticipateBtnClick = async ( - password?: string, - onSuccess?: () => void - ) => { - try { - await GroupAPI.joinGroup({ bookGroupId, password }); - onSuccess && onSuccess(); - showToast({ message: '모임에 가입되었어요' }); - } catch (error) { - error && - showToast({ message: '정답이 아니에요 다시 한 번 도전해 주세요' }); - } - groupInfoQuery.refetch(); - }; - - const handleCreateCommentBtnClick = async (comment: string) => { - if (comment.trim() === '') return; - try { - await GroupAPI.createGroupComment({ bookGroupId, comment }); - } catch (error) { - console.error(error); - } - groupInfoQuery.refetch(); - groupCommentsQuery.refetch(); - }; - - const handleModifyCommentBtnClick = async ( - modifiedComment: string, - commentId: number - ) => { - try { - await GroupAPI.updateGroupComment({ - bookGroupId, - commentId, - comment: modifiedComment, - }); - } catch (error) { - console.error(error); - } - groupCommentsQuery.refetch(); - }; - - const handleDeleteCommentBtnClick = async (commentId: number) => { - try { - await GroupAPI.deleteGroupComment({ bookGroupId, commentId }); - } catch (error) { - console.error(error); - } - groupInfoQuery.refetch(); - groupCommentsQuery.refetch(); - }; - - const handleDeleteGroupBtnClick = async () => { - try { - await GroupAPI.deleteGroup({ bookGroupId }); - } catch (error) { - console.error(error); - } - - router.push('/group'); - }; - - const { isGroupMember, isPublic } = groupInfoQuery.data; - const { bookGroupComments, isEmpty } = groupCommentsQuery.data; - - return ( - - - - - - ); -}; - -export default GroupDetail; diff --git a/src/ui/common/Toast/ToastItem.tsx b/src/ui/common/Toast/ToastItem.tsx deleted file mode 100644 index 3ae62189..00000000 --- a/src/ui/common/Toast/ToastItem.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ToastAtom } from '@/hooks/toast/atoms'; -import { Box } from '@chakra-ui/react'; -import { PropsWithChildren, useEffect, useState } from 'react'; - -const ToastItem = ({ - duration, - children, -}: PropsWithChildren>) => { - const [visible, setVisible] = useState(false); - - useEffect(() => { - setTimeout(() => { - setVisible(true); - }, 100); - - const handleSetTimeout = setTimeout(() => { - setVisible(false); - clearTimeout(handleSetTimeout); - }, duration); - }, [duration]); - - return ( - - - {children} - - - ); -}; - -export default ToastItem; diff --git a/src/ui/common/Toast/index.tsx b/src/ui/common/Toast/index.tsx deleted file mode 100644 index 990d2c60..00000000 --- a/src/ui/common/Toast/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { toastsAtom } from '@/hooks/toast/atoms'; -import { Box, Center } from '@chakra-ui/react'; -import { useRecoilValue } from 'recoil'; -import ToastItem from './ToastItem'; - -const Toast = () => { - const toasts = useRecoilValue(toastsAtom); - - return ( - -
- {toasts.map(({ id, duration, message }) => ( - - {message} - - ))} -
-
- ); -}; - -export default Toast; diff --git a/src/utils/storage.tsx b/src/utils/storage.ts similarity index 100% rename from src/utils/storage.tsx rename to src/utils/storage.ts diff --git a/src/v1/base/BottomActionButton.tsx b/src/v1/base/BottomActionButton.tsx index dc030871..89ada09a 100644 --- a/src/v1/base/BottomActionButton.tsx +++ b/src/v1/base/BottomActionButton.tsx @@ -1,5 +1,6 @@ import { ComponentPropsWithoutRef } from 'react'; -import Button from './Button'; + +import Button from '@/v1/base/Button'; type BottomActionButtonProps = Omit< ComponentPropsWithoutRef, diff --git a/src/v1/base/Drawer.tsx b/src/v1/base/Drawer.tsx index 2ba799dc..6953142d 100644 --- a/src/v1/base/Drawer.tsx +++ b/src/v1/base/Drawer.tsx @@ -7,10 +7,10 @@ import { } from 'react'; import { Dialog, Transition } from '@headlessui/react'; -import { IconClose } from '@public/icons'; import useRemoveVerticalScroll from '@/hooks/useRemoveVerticalScroll'; -import Button from './Button'; +import { IconClose } from '@public/icons'; +import Button from '@/v1/base/Button'; interface DrawerProps { isOpen: boolean; diff --git a/src/v1/base/FloatingButton.tsx b/src/v1/base/FloatingButton.tsx index 541b370d..d753f7b5 100644 --- a/src/v1/base/FloatingButton.tsx +++ b/src/v1/base/FloatingButton.tsx @@ -2,7 +2,7 @@ import { ComponentPropsWithoutRef } from 'react'; import { IconPlus } from '@public/icons'; -import Portal from './Portal'; +import Portal from '@/v1/base/Portal'; interface FloatingButtonProps extends ComponentPropsWithoutRef<'button'> { position?: string; diff --git a/src/v1/base/LoginBottomActionButton.tsx b/src/v1/base/LoginBottomActionButton.tsx index 16f1a6ee..2bb560c7 100644 --- a/src/v1/base/LoginBottomActionButton.tsx +++ b/src/v1/base/LoginBottomActionButton.tsx @@ -1,7 +1,8 @@ import Link from 'next/link'; -import { KAKAO_LOGIN_URL } from '@/constants/url'; -import BottomActionButton from './BottomActionButton'; +import { KAKAO_LOGIN_URL } from '@/constants'; + +import BottomActionButton from '@/v1/base/BottomActionButton'; const LoginBottomActionButton = () => ( diff --git a/src/v1/base/Menu.tsx b/src/v1/base/Menu.tsx index 3518fb60..98c17099 100644 --- a/src/v1/base/Menu.tsx +++ b/src/v1/base/Menu.tsx @@ -12,7 +12,7 @@ import { import { IconHamburger } from '@public/icons'; import useOutsideClickRef from '@/hooks/useOutsideClickRef'; -import BottomSheet from './BottomSheet'; +import BottomSheet from '@/v1/base/BottomSheet'; type MenuContextValue = { isOpen: boolean; diff --git a/src/v1/base/ShareButton.tsx b/src/v1/base/ShareButton.tsx index b1a05bfa..b06cd875 100644 --- a/src/v1/base/ShareButton.tsx +++ b/src/v1/base/ShareButton.tsx @@ -1,5 +1,6 @@ +import useToast from '@/v1/base/Toast/useToast'; + import { IconShare } from '@public/icons'; -import useToast from './Toast/useToast'; const ShareButton = () => { const { show: showToast } = useToast(); diff --git a/src/v1/base/TextArea.tsx b/src/v1/base/TextArea.tsx index 78a9760d..617b10e0 100644 --- a/src/v1/base/TextArea.tsx +++ b/src/v1/base/TextArea.tsx @@ -12,8 +12,8 @@ import { Children, } from 'react'; -import ErrorMessage from './ErrorMessage'; -import InputLength from './InputLength'; +import ErrorMessage from '@/v1/base/ErrorMessage'; +import InputLength from '@/v1/base/InputLength'; interface BaseTextAreaProps extends TextareaHTMLAttributes { diff --git a/src/v1/base/Toast/ToastItem.tsx b/src/v1/base/Toast/ToastItem.tsx index 23f40d70..439569e0 100644 --- a/src/v1/base/Toast/ToastItem.tsx +++ b/src/v1/base/Toast/ToastItem.tsx @@ -1,5 +1,6 @@ +import type { ToastOption } from '@/v1/base/Toast/types'; + import { IconSuccess, IconWarning, IconError } from '@public/icons'; -import { ToastOption } from './types'; const ICONS = { success: , diff --git a/src/v1/base/Toast/ToastProvider.tsx b/src/v1/base/Toast/ToastProvider.tsx index 2360d6e0..887385c4 100644 --- a/src/v1/base/Toast/ToastProvider.tsx +++ b/src/v1/base/Toast/ToastProvider.tsx @@ -1,9 +1,9 @@ import { createContext, ReactNode, useMemo, useState } from 'react'; -import Portal from '@/v1/base/Portal'; +import type { ToastController, ToastOption } from '@/v1/base/Toast/types'; -import ToastItem from './ToastItem'; -import type { ToastController, ToastOption } from './types'; +import ToastItem from '@/v1/base/Toast/ToastItem'; +import Portal from '@/v1/base/Portal'; export const ToastContext = createContext({} as ToastController); diff --git a/src/v1/base/Toast/useToast.ts b/src/v1/base/Toast/useToast.ts index 375e4575..75589c77 100644 --- a/src/v1/base/Toast/useToast.ts +++ b/src/v1/base/Toast/useToast.ts @@ -1,7 +1,8 @@ import { useContext } from 'react'; -import { ToastContext } from './ToastProvider'; -import type { ToastOption } from './types'; +import type { ToastOption } from '@/v1/base/Toast/types'; + +import { ToastContext } from '@/v1/base/Toast/ToastProvider'; const useToast = () => { const toastController = useContext(ToastContext); diff --git a/src/v1/book/BookCover.tsx b/src/v1/book/BookCover.tsx index b99f295e..e19004b2 100644 --- a/src/v1/book/BookCover.tsx +++ b/src/v1/book/BookCover.tsx @@ -2,8 +2,7 @@ import { ComponentPropsWithoutRef, useState } from 'react'; import Image from 'next/image'; - -import { DATA_URL } from '@/constants/url'; +import { DATA_URL } from '@/constants'; type BookCoverSize = | 'xsmall' diff --git a/src/v1/bookGroup/DetailBookGroupCard.tsx b/src/v1/bookGroup/DetailBookGroupCard.tsx index 9951ec81..799c7a91 100644 --- a/src/v1/bookGroup/DetailBookGroupCard.tsx +++ b/src/v1/bookGroup/DetailBookGroupCard.tsx @@ -1,9 +1,10 @@ +import Link from 'next/link'; + +import { IconCalendar, IconMembers, IconComments } from '@public/icons'; +import BookGroupStatus from '@/v1/bookGroup/BookGroupStatus'; import Badge from '@/v1/base/Badge'; import Avatar from '@/v1/base/Avatar'; -import { IconCalendar, IconMembers, IconComments } from '@public/icons'; import BookCover from '@/v1/book/BookCover'; -import Link from 'next/link'; -import BookGroupStatus from './BookGroupStatus'; interface DetailBookGroupCardProps { title: string; diff --git a/src/v1/bookGroup/create/CreateBookGroupFunnel.tsx b/src/v1/bookGroup/create/CreateBookGroupFunnel.tsx index 2df059d2..3080b394 100644 --- a/src/v1/bookGroup/create/CreateBookGroupFunnel.tsx +++ b/src/v1/bookGroup/create/CreateBookGroupFunnel.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/navigation'; import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import type { CreateBookGroupFormValues } from './types'; +import type { CreateBookGroupFormValues } from '@/v1/bookGroup/create/types'; import useCreateBookGroupMutation from '@/queries/group/useCreateBookGroupMutation'; import { useFunnel } from '@/hooks/useFunnel'; @@ -20,7 +20,7 @@ import { SelectBookStep, SelectJoinTypeStep, SetUpDetailStep, -} from './steps'; +} from '@/v1/bookGroup/create/steps'; const FUNNEL_STEPS = [ 'SelectBook', diff --git a/src/v1/bookGroup/create/steps/EnterTitleStep/EnterTitleStep.tsx b/src/v1/bookGroup/create/steps/EnterTitleStep/EnterTitleStep.tsx index 1cdb5841..25a11026 100644 --- a/src/v1/bookGroup/create/steps/EnterTitleStep/EnterTitleStep.tsx +++ b/src/v1/bookGroup/create/steps/EnterTitleStep/EnterTitleStep.tsx @@ -1,12 +1,12 @@ import { useFormContext } from 'react-hook-form'; import type { MoveFunnelStepProps } from '@/v1/base/Funnel'; -import type { EnterTitleStepFormValues } from '../../types'; +import type { EnterTitleStepFormValues } from '@/v1/bookGroup/create/types'; import useRemoveVerticalScroll from '@/hooks/useRemoveVerticalScroll'; +import { TitleField } from '@/v1/bookGroup/create/steps/EnterTitleStep/fields'; import BottomActionButton from '@/v1/base/BottomActionButton'; -import { TitleField } from './fields'; const EnterTitleStep = ({ onNextStep }: MoveFunnelStepProps) => { const { diff --git a/src/v1/bookGroup/create/steps/SelectJoinTypeStep/SelectJoinTypeStep.tsx b/src/v1/bookGroup/create/steps/SelectJoinTypeStep/SelectJoinTypeStep.tsx index 181d367d..b98c8926 100644 --- a/src/v1/bookGroup/create/steps/SelectJoinTypeStep/SelectJoinTypeStep.tsx +++ b/src/v1/bookGroup/create/steps/SelectJoinTypeStep/SelectJoinTypeStep.tsx @@ -1,10 +1,13 @@ import { useFormContext } from 'react-hook-form'; import type { MoveFunnelStepProps } from '@/v1/base/Funnel'; -import type { SelectJoinTypeStepFormValues } from '../../types'; +import type { SelectJoinTypeStepFormValues } from '@/v1/bookGroup/create/types'; +import { + JoinPasswordFieldset, + JoinTypeFieldset, +} from '@/v1/bookGroup/create/steps/SelectJoinTypeStep/fields'; import BottomActionButton from '@/v1/base/BottomActionButton'; -import { JoinPasswordFieldset, JoinTypeFieldset } from './fields'; export type JoinTypeStepFieldName = keyof SelectJoinTypeStepFormValues; export type JoinTypeStepFieldProp = { name: JoinTypeStepFieldName }; diff --git a/src/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/JoinTypeFieldset.tsx b/src/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/JoinTypeFieldset.tsx index 2270e692..10691ff5 100644 --- a/src/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/JoinTypeFieldset.tsx +++ b/src/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/JoinTypeFieldset.tsx @@ -1,9 +1,9 @@ import { useFormContext } from 'react-hook-form'; -import type { SelectJoinTypeStepFormValues } from '../../../types'; -import type { JoinTypeStepFieldProp } from '../SelectJoinTypeStep'; +import type { SelectJoinTypeStepFormValues } from '@/v1/bookGroup/create/types'; +import type { JoinTypeStepFieldProp } from '@/v1/bookGroup/create/steps/SelectJoinTypeStep/SelectJoinTypeStep'; -import JoinTypeRadioCard from './JoinTypeRadioCard'; +import JoinTypeRadioCard from '@/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/JoinTypeRadioCard'; const JoinTypeFieldset = ({ children }: { children?: React.ReactNode }) => { return
{children}
; diff --git a/src/v1/bookGroup/detail/ShortMemberInfo.tsx b/src/v1/bookGroup/detail/ShortMemberInfo.tsx index 177611f0..a7c4e9c7 100644 --- a/src/v1/bookGroup/detail/ShortMemberInfo.tsx +++ b/src/v1/bookGroup/detail/ShortMemberInfo.tsx @@ -1,9 +1,10 @@ -import { IconArrowLeft } from '@public/icons'; -import Button from '@/v1/base/Button'; -import MemberItem from './MemberItem'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; +import { IconArrowLeft } from '@public/icons'; +import MemberItem from '@/v1/bookGroup/detail/MemberItem'; +import Button from '@/v1/base/Button'; + type Member = { id: number; profileImageSrc: string; diff --git a/src/v1/bookShelf/BookShelfCard.tsx b/src/v1/bookShelf/BookShelfCard.tsx index 88f58efa..04683ea7 100644 --- a/src/v1/bookShelf/BookShelfCard.tsx +++ b/src/v1/bookShelf/BookShelfCard.tsx @@ -1,5 +1,6 @@ -import { APIBookshelf } from '@/types/bookshelf'; -import BookShelf from './BookShelf'; +import type { APIBookshelf } from '@/types/bookshelf'; + +import BookShelf from '@/v1/bookShelf/BookShelf'; const BookShelfCard = ({ bookshelfName, diff --git a/src/v1/bookShelf/BookShelfRow.tsx b/src/v1/bookShelf/BookShelfRow.tsx index ad409907..7a5903ae 100644 --- a/src/v1/bookShelf/BookShelfRow.tsx +++ b/src/v1/bookShelf/BookShelfRow.tsx @@ -1,5 +1,6 @@ -import { APIBookshelf } from '@/types/bookshelf'; -import BookShelf from './BookShelf'; +import type { APIBookshelf } from '@/types/bookshelf'; + +import BookShelf from '@/v1/bookShelf/BookShelf'; const BookShelfRow = ({ books }: Pick) => { return ( diff --git a/src/v1/comment/BookCommentList.tsx b/src/v1/comment/BookCommentList.tsx index 21272514..25f5f4eb 100644 --- a/src/v1/comment/BookCommentList.tsx +++ b/src/v1/comment/BookCommentList.tsx @@ -1,12 +1,13 @@ -import { APIBookComment } from '@/types/book'; -import useToast from '@/v1/base/Toast/useToast'; +import type { APIBookComment } from '@/types/book'; import { useMyProfileId } from '@/queries/user/useMyProfileQuery'; import { useBookComments } from '@/queries/book/useBookCommentsQuery'; import usePatchBookCommentMutation from '@/queries/book/usePatchBookCommentMutation'; import useDeleteBookCommentMutation from '@/queries/book/useDeleteBookCommentMutation'; + +import useToast from '@/v1/base/Toast/useToast'; import { checkAuthentication } from '@/utils/helpers'; -import CommentList from './CommentList'; +import CommentList from '@/v1/comment/CommentList'; const BookCommentList = ({ bookId }: { bookId: number }) => { const isAuthenticated = checkAuthentication(); diff --git a/src/v1/comment/BookGroupCommentList.tsx b/src/v1/comment/BookGroupCommentList.tsx index 6da08a5c..6f8b011f 100644 --- a/src/v1/comment/BookGroupCommentList.tsx +++ b/src/v1/comment/BookGroupCommentList.tsx @@ -1,13 +1,14 @@ import { APIGroupComment } from '@/types/group'; -import useToast from '@/v1/base/Toast/useToast'; import { useBookGroupComments } from '@/queries/group/useBookGroupCommentsQuery'; import { useMyProfileId } from '@/queries/user/useMyProfileQuery'; import { useBookGroup } from '@/queries/group/useBookGroupQuery'; import usePatchBookGroupCommentMutation from '@/queries/group/usePatchBookGroupCommentMutation'; import useDeleteBookGroupCommentMutation from '@/queries/group/useDeleteBookGroupCommentMutation'; + +import useToast from '@/v1/base/Toast/useToast'; import { checkAuthentication } from '@/utils/helpers'; -import CommentList from './CommentList'; +import CommentList from '@/v1/comment/CommentList'; const BookGroupCommentList = ({ groupId }: { groupId: number }) => { const isAuthenticated = checkAuthentication(); diff --git a/src/v1/comment/CommentList.tsx b/src/v1/comment/CommentList.tsx index 714ad86d..f1f09b87 100644 --- a/src/v1/comment/CommentList.tsx +++ b/src/v1/comment/CommentList.tsx @@ -1,13 +1,14 @@ import { useMemo, useRef } from 'react'; import type { Writer } from '@/types/user'; + import useDisclosure from '@/hooks/useDisclosure'; +import EditCommentDrawer from '@/v1/comment/CommentDrawer'; import Avatar from '@/v1/base/Avatar'; import Menu from '@/v1/base/Menu'; import Button from '@/v1/base/Button'; import Modal from '@/v1/base/Modal'; -import EditCommentDrawer from './CommentDrawer'; type Comment = { id: number; diff --git a/src/v1/profile/bookShelf/MyProfileBookshelfContainer.tsx b/src/v1/profile/bookShelf/MyProfileBookshelfContainer.tsx index 5a18c31b..dc520279 100644 --- a/src/v1/profile/bookShelf/MyProfileBookshelfContainer.tsx +++ b/src/v1/profile/bookShelf/MyProfileBookshelfContainer.tsx @@ -1,6 +1,7 @@ -import ProfileBookshelfPresenter from './ProfileBookshelfPresenter'; import useMySummaryBookshelfQuery from '@/queries/bookshelf/useMySummaryBookShelfQuery'; +import ProfileBookshelfPresenter from '@/v1/profile/bookShelf/ProfileBookshelfPresenter'; + const MyProfileBookshelfContainer = () => { const { data } = useMySummaryBookshelfQuery(); diff --git a/src/v1/profile/bookShelf/ProfileBookShelf.tsx b/src/v1/profile/bookShelf/ProfileBookShelf.tsx index c8ea9a60..677f416a 100644 --- a/src/v1/profile/bookShelf/ProfileBookShelf.tsx +++ b/src/v1/profile/bookShelf/ProfileBookShelf.tsx @@ -1,7 +1,7 @@ import type { APIUser } from '@/types/user'; -import MyProfileBookshelfContainer from './MyProfileBookshelfContainer'; -import UserProfileBookshelfContainer from './UserProfileBookshelfContainer'; +import MyProfileBookshelfContainer from '@/v1/profile/bookShelf/MyProfileBookshelfContainer'; +import UserProfileBookshelfContainer from '@/v1/profile/bookShelf/UserProfileBookshelfContainer'; const ProfileBookShelf = ({ userId }: { userId: 'me' | APIUser['userId'] }) => { return userId === 'me' ? ( diff --git a/src/v1/profile/bookShelf/UserProfileBookshelfContainer.tsx b/src/v1/profile/bookShelf/UserProfileBookshelfContainer.tsx index 1a2ef0a9..cc074e46 100644 --- a/src/v1/profile/bookShelf/UserProfileBookshelfContainer.tsx +++ b/src/v1/profile/bookShelf/UserProfileBookshelfContainer.tsx @@ -1,7 +1,8 @@ -import ProfileBookshelfPresenter from './ProfileBookshelfPresenter'; import useUserSummaryBookshelfQuery from '@/queries/bookshelf/useUserSummaryBookShelfQuery'; import type { APIUser } from '@/types/user'; +import ProfileBookshelfPresenter from '@/v1/profile/bookShelf/ProfileBookshelfPresenter'; + const UserProfileBookshelfContainer = ({ userId, }: { diff --git a/src/v1/profile/group/ProfileGroup.tsx b/src/v1/profile/group/ProfileGroup.tsx index 93efe928..2db54d86 100644 --- a/src/v1/profile/group/ProfileGroup.tsx +++ b/src/v1/profile/group/ProfileGroup.tsx @@ -2,8 +2,8 @@ import SSRSafeSuspense from '@/components/SSRSafeSuspense'; import { APIUser } from '@/types/user'; +import ProfileGroupContainer from '@/v1/profile/group/ProfileGroupContainer'; import Skeleton from '@/v1/base/Skeleton'; -import ProfileGroupContainer from './ProfileGroupContainer'; const ProfileGroup = ({ userId }: { userId: 'me' | APIUser['userId'] }) => { return ( diff --git a/src/v1/profile/group/ProfileGroupContainer.tsx b/src/v1/profile/group/ProfileGroupContainer.tsx index cb033e86..0ddcd883 100644 --- a/src/v1/profile/group/ProfileGroupContainer.tsx +++ b/src/v1/profile/group/ProfileGroupContainer.tsx @@ -4,7 +4,7 @@ import type { APIUser } from '@/types/user'; import { checkAuthentication } from '@/utils/helpers'; -import ProfileGroupPresenter from './ProfileGroupPresenter'; +import ProfileGroupPresenter from '@/v1/profile/group/ProfileGroupPresenter'; const ProfileGroupContainer = ({ userId, diff --git a/src/v1/profile/info/MyProfileInfoContainer.tsx b/src/v1/profile/info/MyProfileInfoContainer.tsx index cfef3cdf..f266cc41 100644 --- a/src/v1/profile/info/MyProfileInfoContainer.tsx +++ b/src/v1/profile/info/MyProfileInfoContainer.tsx @@ -1,7 +1,9 @@ -import useMyProfileQuery from '@/queries/user/useMyProfileQuery'; -import { usePathname, useRouter } from 'next/navigation'; import { useEffect } from 'react'; -import ProfileInfoPresenter from './ProfileInfoPresenter'; +import { usePathname, useRouter } from 'next/navigation'; + +import useMyProfileQuery from '@/queries/user/useMyProfileQuery'; + +import ProfileInfoPresenter from '@/v1/profile/info/ProfileInfoPresenter'; const MyProfileContainer = () => { const { data } = useMyProfileQuery(); @@ -17,7 +19,7 @@ const MyProfileContainer = () => { if (!isSavedAdditionalInfo) replace(`${pathname}/add`); }, [data, pathname, replace]); - return ; + return ; }; export default MyProfileContainer; diff --git a/src/v1/profile/info/ProfileInfo.tsx b/src/v1/profile/info/ProfileInfo.tsx index c3107c75..c664f695 100644 --- a/src/v1/profile/info/ProfileInfo.tsx +++ b/src/v1/profile/info/ProfileInfo.tsx @@ -2,9 +2,9 @@ import type { APIUser } from '@/types/user'; import SSRSafeSuspense from '@/components/SSRSafeSuspense'; +import MyProfileContainer from '@/v1/profile/info/MyProfileInfoContainer'; +import UserProfileInfoContainer from '@/v1/profile/info/UserProfileInfoContainer'; import Skeleton from '@/v1/base/Skeleton'; -import MyProfileContainer from './MyProfileInfoContainer'; -import UserProfileInfoContainer from './UserProfileInfoContainer'; type ProfileInfoProps = { userId: 'me' | APIUser['userId']; diff --git a/src/v1/profile/info/UserProfileInfoContainer.tsx b/src/v1/profile/info/UserProfileInfoContainer.tsx index c92e3131..15faea50 100644 --- a/src/v1/profile/info/UserProfileInfoContainer.tsx +++ b/src/v1/profile/info/UserProfileInfoContainer.tsx @@ -1,7 +1,8 @@ import useUserProfileQuery from '@/queries/user/useUserProfileQuery'; -import ProfileInfoPresenter from './ProfileInfoPresenter'; import type { APIUser } from '@/types/user'; +import ProfileInfoPresenter from '@/v1/profile/info/ProfileInfoPresenter'; + const UserProfileInfoContainer = ({ userId, }: { @@ -9,7 +10,7 @@ const UserProfileInfoContainer = ({ }) => { const { data } = useUserProfileQuery(userId); - return ; + return ; }; export default UserProfileInfoContainer;