diff --git a/package.json b/package.json index 06cd96a..ba0869b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "eslint-plugin-react": "^7.33.2", "framer-motion": "^11.0.3", "lodash": "^4.17.21", + "lottie-react": "^2.4.0", "next": "14.0.3", "react": "^18", "react-dom": "^18", diff --git a/src/api/axiosInstance.ts b/src/api/axiosInstance.ts index ad0c8d9..f1798bd 100644 --- a/src/api/axiosInstance.ts +++ b/src/api/axiosInstance.ts @@ -27,6 +27,7 @@ axiosInstance.interceptors.response.use( const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { + /* originalRequest._retry = true; const refreshToken = @@ -49,6 +50,8 @@ axiosInstance.interceptors.response.use( console.error('Error refreshing token:', refreshError); throw refreshError; } + */ + window.location.href = '/login'; } return await Promise.reject(error); diff --git a/src/assets/icons/NotificationIcon.tsx b/src/assets/icons/NotificationIcon.tsx index d445745..8eb31ad 100644 --- a/src/assets/icons/NotificationIcon.tsx +++ b/src/assets/icons/NotificationIcon.tsx @@ -2,7 +2,7 @@ import React from 'react'; const NotificationIcon: React.FC> = (props) => ( - + { + return ( + // +
로딩중
+ ); +}; + +export default LottieComponent; diff --git a/src/components/atoms/index.ts b/src/components/atoms/index.ts index c7206d7..291e25c 100644 --- a/src/components/atoms/index.ts +++ b/src/components/atoms/index.ts @@ -16,3 +16,4 @@ export { default as RadioButtonField } from './RadioButtonField'; export { default as Input } from './Input'; export { default as MiniButton } from './MiniButton'; export { default as ExclamationAlertSpan } from './ExclamationAlertSpan'; +export { default as Lottie } from './Lottie'; diff --git a/src/components/molecules/Counter.tsx b/src/components/molecules/Counter.tsx index cb1111d..251de57 100644 --- a/src/components/molecules/Counter.tsx +++ b/src/components/molecules/Counter.tsx @@ -11,7 +11,7 @@ const Counter: React.FC = ({ - + diff --git a/src/components/organisms/FridgeInfoBox.tsx b/src/components/organisms/FridgeInfoBox.tsx index 1563ff6..3145a92 100644 --- a/src/components/organisms/FridgeInfoBox.tsx +++ b/src/components/organisms/FridgeInfoBox.tsx @@ -1,15 +1,19 @@ import { AngleIcon } from '@/assets/icons'; import { Button } from '../atoms'; import React from 'react'; +import { useRouter } from 'next/router'; const FridgeInfoBox: React.FC<{ + userName?: string; toggleIsOpenFridgeListModal: () => void; - toggleIsOpenIngredientAddModal?: () => void; -}> = ({ toggleIsOpenFridgeListModal, toggleIsOpenIngredientAddModal }) => { + isOkIngredientAdd?: boolean; +}> = ({ userName = '', toggleIsOpenFridgeListModal, isOkIngredientAdd }) => { + const router = useRouter(); + return (
-
홍길동님의
+
{userName}님의
- {toggleIsOpenIngredientAddModal && ( + {isOkIngredientAdd && (
diff --git a/src/components/organisms/FridgeListModal.tsx b/src/components/organisms/FridgeListModal.tsx index 60de454..201759b 100644 --- a/src/components/organisms/FridgeListModal.tsx +++ b/src/components/organisms/FridgeListModal.tsx @@ -3,12 +3,31 @@ import { PlusIcon, TrashcanIcon } from '@/assets/icons'; import React, { useState } from 'react'; import { FridgeListItem } from '../molecules'; +import useGetMyFridgeList from '@/hooks/queries/fridge/useGetFridgeList'; +import { useRouter } from 'next/router'; +import usePostFridge from '@/hooks/queries/fridge/usePostFridge'; const FridgeListModal: React.FC<{ isMyFridgeList?: boolean; }> = ({ isMyFridgeList }) => { - const [currentFridgeName, setCurrentFridgeName] = useState('기본 냉장고'); - const FRIDGE_NAME_LIST = ['기본 냉장고', '김치 냉장고', '주류 냉장고']; + const [currentFridge, setCurrentFridge] = useState({ + id: 1, + name: '기본 냉장고', + }); + + const router = useRouter(); + const fridgeList = useGetMyFridgeList(); + const fridgeMutation = usePostFridge(); + + const handleFridgeClick = (id: number) => { + void router.push(`?fridge-id=${id}`); + }; + + const handleNewFridgeClick = () => { + fridgeMutation.mutate({ + name: `내 냉장고 ${fridgeList ? fridgeList.length + 1 : 1}`, + }); + }; return ( @@ -19,18 +38,20 @@ const FridgeListModal: React.FC<{
- {FRIDGE_NAME_LIST.map((fridgeName) => ( + {fridgeList?.map(({ id, name }) => ( { - setCurrentFridgeName(fridgeName); + setCurrentFridge({ id, name }); }} /> ))} - - @@ -39,7 +60,13 @@ const FridgeListModal: React.FC<{ -
); diff --git a/src/components/organisms/IngredientAddModal.tsx b/src/components/organisms/IngredientAddModal.tsx index d416610..4bee752 100644 --- a/src/components/organisms/IngredientAddModal.tsx +++ b/src/components/organisms/IngredientAddModal.tsx @@ -2,19 +2,42 @@ import { BoxIcon, CalendarIcon, FreezerIcon, MemoIcon } from '@/assets/icons'; import { Button, Toggle } from '@/components/atoms'; import { Counter, IngredientAddItemContainer } from '../molecules'; import React, { useState } from 'react'; - -import { AppleIcon } from '../atoms/IngredientIcons'; import useCount from '@/hooks/useCount'; import useToast from '@/hooks/useToast'; import ModalContainer from '../atoms/ModalContainer'; +import { + useGetIngredientById, + usePostIngredient, +} from '@/hooks/queries/fridge'; +import Image from 'next/image'; +import type { PostIngredientBodyType } from '@/hooks/queries/fridge/usePostIngredient'; const IngredientAddModal: React.FC<{ + id: number; toggleIsOpenIngredientAddModal: () => void; -}> = ({ toggleIsOpenIngredientAddModal }) => { +}> = ({ id, toggleIsOpenIngredientAddModal }) => { const { showToast } = useToast(); + const onSuccess = () => { + toggleIsOpenIngredientAddModal(); + showToast('식자재 추가가 완료되었습니다.', 'success'); + }; + + const postIngredient = usePostIngredient(onSuccess); + + const [reqBody, setReqBody] = useState({ + refrigeratorId: 0, + ingredientId: 0, + name: '', + quantity: 0, + location: 'FREEZING', + memo: '', + addDate: '2024-01-12', + expirationDate: '2024-01-22', + isDeleted: true, + }); + const [isInFreezer, setIsInFreezer] = useState(false); - const [memoContent, setMemoContent] = useState(''); const { currentCount, handleIncreaseCount, handleDecreaseCount } = useCount(); const toggleIsInFreezer: () => void = () => { @@ -22,17 +45,22 @@ const IngredientAddModal: React.FC<{ }; const handleSubmit: () => void = () => { - console.log({ currentCount, isInFreezer, memoContent }); - toggleIsOpenIngredientAddModal(); - showToast('식자재 추가가 완료되었습니다.', 'success'); + postIngredient.mutate({ ...reqBody, quantity: currentCount }); }; + const data = useGetIngredientById(id); + return (
-
- -
사과
+
+ {data?.name +
{data?.name}
-
- 2024년 01월 12일 -
+ { + console.log(e.target.value); + setReqBody((prev) => ({ + ...prev, + addDate: e.target.value, + })); + }} + />
~
-
- 2024년 01월 6일 -
+ { + console.log(e.target.value); + setReqBody((prev) => ({ + ...prev, + expirationDate: e.target.value, + })); + }} + />
) => { - setMemoContent(e.target.value); + setReqBody((prev) => ({ ...prev, memo: e.target.value })); }} className="w-full p-[12px] rounded-[6px] body1-medium" placeholder="식자재 관련 정보를 입력해 주세요." diff --git a/src/components/templates/SuspenseFallback.tsx b/src/components/templates/SuspenseFallback.tsx index da0a170..69accd1 100644 --- a/src/components/templates/SuspenseFallback.tsx +++ b/src/components/templates/SuspenseFallback.tsx @@ -1,12 +1,11 @@ -import Image from 'next/image'; import React from 'react'; -import LottieGif from '@/assets/lottie.gif'; +import { Lottie } from '../atoms'; const SuspenseFallback: React.FC = () => { return (
- 로딩중 +
); diff --git a/src/hooks/queries/fridge/index.ts b/src/hooks/queries/fridge/index.ts index 8c602e7..6fffac2 100644 --- a/src/hooks/queries/fridge/index.ts +++ b/src/hooks/queries/fridge/index.ts @@ -1 +1,5 @@ export { default as useGetIngredientList } from './useGetIngredientList'; +export { default as useGetIngredientById } from './useGetIngredientById'; +export { default as usePostIngredient } from './usePostIngredient'; +export { default as useGetMyIngredients } from './useGetMyIngredients'; +export { default as useGetFridgeContentById } from './useGetFridgeContentById'; diff --git a/src/hooks/queries/fridge/useGetFridgeContentById.ts b/src/hooks/queries/fridge/useGetFridgeContentById.ts new file mode 100644 index 0000000..b489d02 --- /dev/null +++ b/src/hooks/queries/fridge/useGetFridgeContentById.ts @@ -0,0 +1,19 @@ +import { queryKeys } from '../queryKeys'; +import { useBaseQuery } from '../useBaseQuery'; + +interface FridgeContentType { + content: []; +} + +const useGetFridgeContentById = (id: string) => { + const { data } = useBaseQuery( + queryKeys.MY_FRIDGE_CONTENT(id), + `/ingrs/detail/refrig/${id}`, + ); + + if (!data?.data) return; + + return data?.data; +}; + +export default useGetFridgeContentById; diff --git a/src/hooks/queries/fridge/useGetFridgeList.ts b/src/hooks/queries/fridge/useGetFridgeList.ts new file mode 100644 index 0000000..5fa47c2 --- /dev/null +++ b/src/hooks/queries/fridge/useGetFridgeList.ts @@ -0,0 +1,18 @@ +import { queryKeys } from '../queryKeys'; +import { useBaseQuery } from '../useBaseQuery'; + +interface IngredientType { + id: number; + name: string; +} + +const useGetMyFridgeList = (id?: number) => { + const { data } = useBaseQuery( + queryKeys.INGREDIENTS(), + `/refrigs/${id ? `users/${id}` : 'my'}`, + ); + + return data?.data; +}; + +export default useGetMyFridgeList; diff --git a/src/hooks/queries/fridge/useGetIngredientById.ts b/src/hooks/queries/fridge/useGetIngredientById.ts new file mode 100644 index 0000000..289ef25 --- /dev/null +++ b/src/hooks/queries/fridge/useGetIngredientById.ts @@ -0,0 +1,23 @@ +import { queryKeys } from '../queryKeys'; +import { useBaseQuery } from '../useBaseQuery'; + +interface IngredientType { + ingredientId: 0; + category: string; + name: string; + iconImage: string; + expirationDays: 0; +} + +const useGetIngredientById = (id: number) => { + const { data } = useBaseQuery( + queryKeys.INGREDIENT_ID(id), + `/ingrs/${id}`, + ); + + if (!data?.data) return; + + return data?.data; +}; + +export default useGetIngredientById; diff --git a/src/hooks/queries/fridge/useGetIngredientList.ts b/src/hooks/queries/fridge/useGetIngredientList.ts index 63a83fb..8998fc1 100644 --- a/src/hooks/queries/fridge/useGetIngredientList.ts +++ b/src/hooks/queries/fridge/useGetIngredientList.ts @@ -1,11 +1,18 @@ -import type { IngredientType } from '@/types/fridge'; import { queryKeys } from '../queryKeys'; import { useBaseQuery } from '../useBaseQuery'; +interface IngredientType { + id: number; + name: string; + iconImage: string; +} + const useGetIngredientList = () => { - // const testApiEndpoint = 'https://jsonplaceholder.typicode.com/todos'; + const { data } = useBaseQuery< + Array<{ category: string; ingredientGroupList: IngredientType[] }> + >(queryKeys.INGREDIENT_LIST(), '/ingrs/category'); - return useBaseQuery(queryKeys.INGREDIENT(), '/regrigs/my'); + return data?.data; }; export default useGetIngredientList; diff --git a/src/hooks/queries/fridge/useGetMyIngredients.ts b/src/hooks/queries/fridge/useGetMyIngredients.ts new file mode 100644 index 0000000..83d52b3 --- /dev/null +++ b/src/hooks/queries/fridge/useGetMyIngredients.ts @@ -0,0 +1,19 @@ +import { queryKeys } from '../queryKeys'; +import { useBaseQuery } from '../useBaseQuery'; + +interface IngredientType { + id: number; + name: string; + iconImage: string; +} + +const useGetMyIngredients = () => { + const { data } = useBaseQuery( + queryKeys.INGREDIENTS(), + '/refridge/my', + ); + + return data?.data; +}; + +export default useGetMyIngredients; diff --git a/src/hooks/queries/fridge/usePostFridge.ts b/src/hooks/queries/fridge/usePostFridge.ts new file mode 100644 index 0000000..30f5774 --- /dev/null +++ b/src/hooks/queries/fridge/usePostFridge.ts @@ -0,0 +1,21 @@ +import { queryClient } from '@/pages/_app'; +import { queryKeys } from '../queryKeys'; +import { useBaseMutation } from '../useBaseMutation'; + +interface PostFridgeBodyType { + name: string; +} + +const usePostFridge = () => { + const onSuccess = (data: PostFridgeBodyType) => { + console.log(data); + void queryClient.invalidateQueries(); + }; + return useBaseMutation( + queryKeys.INGREDIENT_LIST, + `/refrigs`, + onSuccess, + ); +}; + +export default usePostFridge; diff --git a/src/hooks/queries/fridge/usePostIngredient.ts b/src/hooks/queries/fridge/usePostIngredient.ts new file mode 100644 index 0000000..cb7be2c --- /dev/null +++ b/src/hooks/queries/fridge/usePostIngredient.ts @@ -0,0 +1,28 @@ +import { useRouter } from 'next/router'; +import { queryKeys } from '../queryKeys'; +import { useBaseMutation } from '../useBaseMutation'; + +export interface PostIngredientBodyType { + refrigeratorId: number; + ingredientId: number; + name: string; + quantity: number; + location: 'FREEZING'; + memo: string; + addDate: string; + expirationDate: string; + isDeleted: true; +} + +const usePostIngredient = (fn?: () => void) => { + const router = useRouter(); + const onSuccess = () => { + void router.push('/fridge'); + }; + return useBaseMutation( + queryKeys.INGREDIENTS(), + `/ingrs/detail`, + onSuccess, + ); +}; +export default usePostIngredient; diff --git a/src/hooks/queries/login/usePostUser.ts b/src/hooks/queries/login/usePostUser.ts index 10534dd..9443d34 100644 --- a/src/hooks/queries/login/usePostUser.ts +++ b/src/hooks/queries/login/usePostUser.ts @@ -6,7 +6,7 @@ interface PostUserBodyType { nickName: string; kakaoId: number; kakaoEmail: string; - googleEmail: string; + googleEmail: string | null; profileImage: string; } diff --git a/src/hooks/queries/mypage/index.ts b/src/hooks/queries/mypage/index.ts new file mode 100644 index 0000000..9dc6662 --- /dev/null +++ b/src/hooks/queries/mypage/index.ts @@ -0,0 +1,3 @@ +export { default as useGetMe } from './useGetMe'; +export { default as useGetMyFriendsCount } from './useGetMyFriendsCount'; +export { default as useGetMyIngredientsCount } from './useGetMyIngredientsCount'; diff --git a/src/hooks/queries/mypage/useGetMe.ts b/src/hooks/queries/mypage/useGetMe.ts new file mode 100644 index 0000000..1b9c47e --- /dev/null +++ b/src/hooks/queries/mypage/useGetMe.ts @@ -0,0 +1,26 @@ +import type { ProfileEnum } from '@/types/common'; +import { queryKeys } from '../queryKeys'; +import { useBaseQuery } from '../useBaseQuery'; + +interface ResType { + nickName: string; + kakaoId: number; + kakaoEmail: string; + googleEmail: string | null; + profileImage: ProfileEnum; +} + +const useGetMe = () => { + const { data } = useBaseQuery(queryKeys.ME(), `/users/me`, true); + if (!data?.data) + return { + nickName: '', + kakaoId: 0, + kakaoEmail: '', + googleEmail: null, + profileImage: 'BLUE' as ProfileEnum, + }; + return data?.data; +}; + +export default useGetMe; diff --git a/src/hooks/queries/mypage/useGetMyFriendsCount.ts b/src/hooks/queries/mypage/useGetMyFriendsCount.ts new file mode 100644 index 0000000..83bf276 --- /dev/null +++ b/src/hooks/queries/mypage/useGetMyFriendsCount.ts @@ -0,0 +1,14 @@ +import { queryKeys } from '../queryKeys'; +import { useBaseQuery } from '../useBaseQuery'; + +const useGetMyFriendsCount = () => { + const { data } = useBaseQuery( + queryKeys.MY_FRIENDS_COUNT(), + `/friendship/count`, + true, + ); + + return data?.data; +}; + +export default useGetMyFriendsCount; diff --git a/src/hooks/queries/mypage/useGetMyIngredientsCount.ts b/src/hooks/queries/mypage/useGetMyIngredientsCount.ts new file mode 100644 index 0000000..2b4467a --- /dev/null +++ b/src/hooks/queries/mypage/useGetMyIngredientsCount.ts @@ -0,0 +1,14 @@ +import { queryKeys } from '../queryKeys'; +import { useBaseQuery } from '../useBaseQuery'; + +const useGetMyIngredientsCount = () => { + const { data } = useBaseQuery( + queryKeys.MY_FRIENDS_COUNT(), + `/ingrs/detail/count?day=5`, + true, + ); + + return data?.data; +}; + +export default useGetMyIngredientsCount; diff --git a/src/hooks/queries/queryKeys.ts b/src/hooks/queries/queryKeys.ts index e88a097..43571b3 100644 --- a/src/hooks/queries/queryKeys.ts +++ b/src/hooks/queries/queryKeys.ts @@ -1,9 +1,15 @@ import type { FriendshipSortType } from '@/types/friendship'; export const queryKeys = { - INGREDIENT: (id?: number) => (id ? ['ingredient', id] : ['ingredient']), + MY_FRIDGE_LIST: () => ['my_fridge_list'], + MY_FRIENDS_COUNT: () => ['my_friends_count'], + MY_FRIDGE_CONTENT: (id: string) => ['my_fridge', id], + INGREDIENT_LIST: () => ['ingredient_list'], + INGREDIENT_ID: (id: number) => ['ingredient', id], + INGREDIENTS: () => ['my-ingredient'], KAKAO: () => ['kakao'], SHARES: () => ['shares'], + ME: () => ['my-info'], FRIENDSHIPS: (sort: FriendshipSortType) => ['friendship', sort], } as const; diff --git a/src/hooks/queries/useBaseMutation.ts b/src/hooks/queries/useBaseMutation.ts index 15008ae..e865ce1 100644 --- a/src/hooks/queries/useBaseMutation.ts +++ b/src/hooks/queries/useBaseMutation.ts @@ -1,8 +1,13 @@ import axiosInstance from '@/api/axiosInstance'; import { useMutation } from '@tanstack/react-query'; -export const fetchData = async (url: string, body: T) => { - const response = await axiosInstance.post<{ data: T }>(url, body); +export const fetchData = async (url: string, body: T, method: string) => { + const response = await axiosInstance.request({ + method, + url, + data: body, + }); + return response.data; }; @@ -10,11 +15,12 @@ export const useBaseMutation = ( mutationKey: any, url: string, onSuccess: (any: any) => void, + method: 'POST' | 'PUT' | 'DELETE' = 'POST', ) => { return useMutation({ mutationKey, mutationFn: async (body: T) => { - const response = await fetchData(url, body); + const response = await fetchData(url, body, method); onSuccess(response.data); }, diff --git a/src/hooks/queries/useBaseQuery.ts b/src/hooks/queries/useBaseQuery.ts index 2fe6ddd..bfd805f 100644 --- a/src/hooks/queries/useBaseQuery.ts +++ b/src/hooks/queries/useBaseQuery.ts @@ -15,6 +15,7 @@ export const fetchData = async (url: string, isNotCatch: boolean) => { } } }; + export const useBaseQuery = ( queryKey: any, url: string, diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 3f8cb95..47f5788 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -21,9 +21,10 @@ const theme = extendTheme({ }, }); -const queryClient = new QueryClient({ +export const queryClient = new QueryClient({ defaultOptions: { queries: { + staleTime: 0, retry: false, refetchOnWindowFocus: false, }, diff --git a/src/pages/fridge/add/index.tsx b/src/pages/fridge/add/index.tsx new file mode 100644 index 0000000..f630de0 --- /dev/null +++ b/src/pages/fridge/add/index.tsx @@ -0,0 +1,102 @@ +import type { NextPage } from 'next'; +import { Header, IngredientAddModal } from '@/components/organisms'; +import { Container } from '../../../components/atoms'; +import { useState } from 'react'; +import { useGetIngredientList } from '@/hooks/queries/fridge'; +import Image from 'next/image'; +import { + Modal, + ModalOverlay, + ModalBody, + ModalContent, + useDisclosure, +} from '@chakra-ui/react'; + +const CATEGORIES = ['전체', '과일', '고기']; + +const FridgePage: NextPage = () => { + const [ingredientId, setIngredientId] = useState(null); + const { + isOpen: isOpenIngredientAddModal, + onOpen: onOpenIngredientAddModal, + onClose: onCloseIngredientAddModal, + } = useDisclosure(); + + const [currentCategory, setCurrentCategory] = useState('전체'); + + const data = useGetIngredientList(); + + return ( + <> + {isOpenIngredientAddModal && ( + + + + + + + + + )} +
+
+
+
+ {CATEGORIES.map((category) => ( +
{ + setCurrentCategory(category); + }} + className={`${category === currentCategory ? 'bg-primary2 text-white' : 'bg-white text-gray4'} cursor-pointer body1-semibold pt-[6px] pb-[6px] pl-[18px] pr-[18px] rounded-[20px]`} + > + {category} +
+ ))} +
+ {data?.map((items) => ( + + +
    + {items.ingredientGroupList.map((item) => ( +
  • { + setIngredientId(item.id); + onOpenIngredientAddModal(); + }} + className="flex flex-col items-center" + > + {item.name} +
    {item.name}
    +
  • + ))} +
+
+ ))} +
+
+ + ); +}; +export default FridgePage; diff --git a/src/pages/fridge/index.tsx b/src/pages/fridge/index.tsx index 25d05d9..07bdff0 100644 --- a/src/pages/fridge/index.tsx +++ b/src/pages/fridge/index.tsx @@ -3,7 +3,6 @@ import { FridgeBoard, FridgeInfoBox, FridgeListModal, - IngredientAddModal, } from '@/components/organisms'; import { type NextPage } from 'next'; import { @@ -13,22 +12,35 @@ import { ModalContent, useDisclosure, } from '@chakra-ui/react'; -import { useGetIngredientList } from '@/hooks/queries/fridge'; +import { useGetMe } from '@/hooks/queries/mypage'; +import { + useGetFridgeContentById, + useGetMyIngredients, +} from '@/hooks/queries/fridge'; const FridgePage: NextPage = () => { - const { - isOpen: isOpenIngredientAddModal, - onOpen: onOpenIngredientAddModal, - onClose: onCloseIngredientAddModal, - } = useDisclosure(); - const { isOpen: isOpenFridgeListModal, onOpen: onOpenFridgeListModal, onClose: onCloseFridgeListModal, } = useDisclosure(); - const { data } = useGetIngredientList(); + const { nickName } = useGetMe(); + + const urlParams = + typeof window !== 'undefined' + ? new URLSearchParams(window.location.search) + : null; + const fridgeId = urlParams?.get('fridge-id'); + + let data; + if (fridgeId) { + data = useGetFridgeContentById(fridgeId)?.content; + console.log(data); + return data; + } else { + data = useGetMyIngredients(); + } return ( <> @@ -52,36 +64,15 @@ const FridgePage: NextPage = () => { - - - - - - - -
- +
diff --git a/src/pages/mypage/index.tsx b/src/pages/mypage/index.tsx index a44407e..199d6ad 100644 --- a/src/pages/mypage/index.tsx +++ b/src/pages/mypage/index.tsx @@ -1,7 +1,6 @@ import { Button } from '@/components/atoms'; import { MyFridgeInfo } from '@/components/molecules'; import { NavWhiteBox } from '@/components/organisms'; -import ProfileImg from '@/assets/profile.png'; import Header from '@/components/organisms/Header'; import { type NextPage } from 'next'; import React from 'react'; @@ -16,6 +15,12 @@ import { QuestionIcon, SettingIcon, } from '@/assets/icons'; +import { + useGetMe, + useGetMyFriendsCount, + useGetMyIngredientsCount, +} from '@/hooks/queries/mypage'; +import { returnProfileImg } from '@/utils/returnProfileImg'; const GENERAGE_NAV_LIST = [ { @@ -43,14 +48,27 @@ const ETC_NAV_LIST = [ ]; const Mypage: NextPage = () => { + const data = useGetMe(); + const myFriendsCount = useGetMyFriendsCount(); + const myIngredientsCount = useGetMyIngredientsCount(); + return (
- 프로필 예시 - 닉네임 + {data?.profileImage && ( + 프로필 예시 + )} + + {data?.nickName ?? '닉네임을 입력해주세요.'} +
- +
- +
diff --git a/src/pages/mypage/profile/index.tsx b/src/pages/mypage/profile/index.tsx index e238f7f..08c431e 100644 --- a/src/pages/mypage/profile/index.tsx +++ b/src/pages/mypage/profile/index.tsx @@ -1,82 +1,95 @@ import { type NextPage } from 'next'; import Image from 'next/image'; -import ProfileImg from '@/assets/profile.png'; -import { Button, ExclamationAlertSpan } from '@/components/atoms'; +import { ExclamationAlertSpan } from '@/components/atoms'; import React, { useCallback, useState } from 'react'; +import type { FormEvent } from 'react'; import Header from '@/components/organisms/Header'; import { debounceFunction } from '@/utils/debounceUtil'; import usePostUser from '@/hooks/queries/login/usePostUser'; +import { useGetMe } from '@/hooks/queries/mypage'; +import type { ProfileEnum } from '@/types/common'; +import axiosInstance from '@/api/axiosInstance'; +import { returnProfileImg } from '@/utils/returnProfileImg'; -const PROPILES = [ +const PROFILES: Array<{ string: ProfileEnum; pointColor: string }> = [ { string: 'GREEN', - imgUrl: - 'https://mara-s3bucket.s3.ap-northeast-2.amazonaws.com/images/profiles/green-nor.svg', + pointColor: '#3CAA8D', }, { string: 'RED', - imgUrl: - 'https://mara-s3bucket.s3.ap-northeast-2.amazonaws.com/images/profiles/red-nor.svg', + pointColor: '#CB5D45', }, { string: 'BLUE', - imgUrl: - 'https://mara-s3bucket.s3.ap-northeast-2.amazonaws.com/images/profiles/blue-nor.svg', + pointColor: '#5C93D4', }, { string: 'YELLOW', - imgUrl: - 'https://mara-s3bucket.s3.ap-northeast-2.amazonaws.com/images/profiles/yellow-nor.svg', + pointColor: '#D5B02D', }, ]; -const FriendsListPage: NextPage = () => { - const [selectedImageSrc, setSelectedImageSrc] = useState(ProfileImg); +const ProfilePage: NextPage = () => { + const [selectedProfile, setSelectedProfile] = useState('BLUE'); const [nickname, setNickname] = useState(''); const [isNicknameAvailable, setIsNicknameAvailable] = useState(false); const [isNicknameChecked, setIsNicknameChecked] = useState(false); - const postUser = usePostUser(); + const MyInfo = useGetMe(); - const handleImageClick: (src: string) => void = (src) => { - // imgURL로 변경 - console.log('선택한이미지SRC', src); - setSelectedImageSrc(ProfileImg); - }; + if (MyInfo.nickName) { + setNickname(MyInfo.nickName); + } + + const postUser = usePostUser(); const handleNicknameChange: ( e: React.ChangeEvent, ) => void = (e) => { setNickname(e.target.value); setIsNicknameChecked(false); - void debouncedHandleNicknameChange(e.target.value); }; + const nickNameCheckResult = async (nickName: string) => { + try { + const res = await axiosInstance.get<{ + message: 'string'; + data: { + isDuplicated: boolean; + }; + }>(`/users/nickname/check?nickname=${nickName}`); + setIsNicknameChecked(true); + setIsNicknameAvailable(!res?.data?.data.isDuplicated); + } catch (error) { + console.error('Error checking nickname:', error); + } + }; + const debouncedHandleNicknameChange = useCallback( debounceFunction((currentNickname: string) => { - setIsNicknameChecked(true); - setIsNicknameAvailable(false); + void nickNameCheckResult(currentNickname); }, 1000), [], ); - const handleSumbit = () => { + const handleSumbit = (e: FormEvent) => { + e.preventDefault(); + const urlParams = typeof window !== 'undefined' ? new URLSearchParams(window.location.search) : null; - const kakaoId = Number(urlParams?.get('kakaoId')); + const kakaoId = urlParams?.get('kakaoId'); const kakaoEmail = urlParams?.get('kakaoEmail'); - if (!kakaoId || !kakaoEmail) return; - postUser.mutate({ nickName: nickname, - kakaoId, - kakaoEmail, - googleEmail: '', - profileImage: 'BLUE', + kakaoId: Number(kakaoId ?? MyInfo.kakaoId), + kakaoEmail: kakaoEmail ?? MyInfo.kakaoEmail, + googleEmail: null, + profileImage: selectedProfile, }); }; @@ -88,12 +101,15 @@ const FriendsListPage: NextPage = () => { > 프로필 이미지 -
+
{ ))}
- {PROPILES.map((profile) => ( + {PROFILES.map(({ string, pointColor }) => ( 프로필 이미지 { - handleImageClick(profile.string); + setSelectedProfile(string); }} /> ))}
+
-
); }; -export default FriendsListPage; +export default ProfilePage; diff --git a/src/stories/Lottie.stories.tsx b/src/stories/Lottie.stories.tsx new file mode 100644 index 0000000..57c288e --- /dev/null +++ b/src/stories/Lottie.stories.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Lottie } from '../components/atoms'; + +export default { + title: 'Lottie', + component: Lottie, +}; + +const Template: any = () => { + return ; +}; + +export const Default = Template.bind({}); +Default.args = {}; diff --git a/tailwind.config.ts b/tailwind.config.ts index d6b44df..ad2a129 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -12,12 +12,20 @@ const config: Config = { colors: { primary1: '#52C5A6', primary2: '#3CAA8D', - primary3: '#0C5E5A', + primary3: '#1E8A6F', + primary4: '#0C5E5A', point1: '#FFE377', point2: '#FF8436', point3: '#CA3F13', point4: '#FB2414', - gray1: '#F1F2F4', + state1: '#F7C0AE', + state2: '#CB5D45', + state3: '#B5DEF6', + state4: '#5C93D4', + state5: '#F4EDAC', + state6: '#D5B02D', + gray0: '#F1F2F4', + gray1: '#EEEFF2', gray2: '#E0E1E6', gray3: '#CCCFD7', gray4: '#B5B9C5', diff --git a/yarn.lock b/yarn.lock index 198fa33..5692d33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8682,6 +8682,18 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lottie-react@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/lottie-react/-/lottie-react-2.4.0.tgz#f7249eee2b1deee70457a2d142194fdf2456e4bd" + integrity sha512-pDJGj+AQlnlyHvOHFK7vLdsDcvbuqvwPZdMlJ360wrzGFurXeKPr8SiRCjLf3LrNYKANQtSsh5dz9UYQHuqx4w== + dependencies: + lottie-web "^5.10.2" + +lottie-web@^5.10.2: + version "5.12.2" + resolved "https://registry.yarnpkg.com/lottie-web/-/lottie-web-5.12.2.tgz#579ca9fe6d3fd9e352571edd3c0be162492f68e5" + integrity sha512-uvhvYPC8kGPjXT3MyKMrL3JitEAmDMp30lVkuq/590Mw9ok6pWcFCwXJveo0t5uqYw1UREQHofD+jVpdjBv8wg== + loupe@^2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"