From 23ce6afed5d9e307da1c02a5b5723b696a17ab4f Mon Sep 17 00:00:00 2001 From: "Ahyeon, Jung" <75254185+a-honey@users.noreply.github.com> Date: Fri, 23 Feb 2024 01:51:44 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=82=B4,=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EB=83=89=EC=9E=A5=EA=B3=A0=20API=20=EC=97=B0=EB=8F=99=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 로티 gif -> json 및 react-lottie 설치 * feat: 마이페이지 나의 정보 api 추가 * feat: 내 냉장고에 이름 추가 * feat: 식자재 추가 마크업 * fix: 식자재 추가시 페이지 이동 및 다시 돌아와서 모달 오픈 * feat: 재료 선택 후 모달에서 정보 불러오기 * feat: 식자재 추가 뮤테이션 추가 * feat: 내 냉장고 정보 불러오기 * feat: 냉장고 목록 조회 api * feat: 마이페이지 식자재, 친구 count api 추가 * fix: 콘솔에러 수정 * feat: 스타일 추가 * feat: 스타일 추가시 border 변경 * feat: 프로필 업데이트 api 추가 * fix: 현재 프로필일때만 테두리 * feat: 냉장고 ID 식자재 가져오기 api 추가 * feat: 내 냉장고 추가 api 연결 * fix: 식자재 추가 모달 식자재 추가 페이지 내로 위치 이동 * fix: 미변경 에러 수정 * feat: 식자재 추가 api 연결 * fix: 뮤테이션 메서드 추가 * fix: 냉장고 리스트 길이에 따라 추가 * fix: 프로필 이미지 수정 * fix: 닉네임 중복 체크 수정 * fix: 콘솔 에러 수정 --- package.json | 1 + src/api/axiosInstance.ts | 3 + src/assets/icons/NotificationIcon.tsx | 2 +- src/assets/lottie.gif | Bin 18313 -> 0 bytes src/assets/lottie.json | 317 ++++++++++++++++++ src/components/atoms/Lottie.tsx | 12 + src/components/atoms/index.ts | 1 + src/components/molecules/Counter.tsx | 2 +- src/components/organisms/FridgeInfoBox.tsx | 16 +- src/components/organisms/FridgeListModal.tsx | 47 ++- .../organisms/IngredientAddModal.tsx | 84 ++++- src/components/templates/SuspenseFallback.tsx | 5 +- src/hooks/queries/fridge/index.ts | 4 + .../queries/fridge/useGetFridgeContentById.ts | 19 ++ src/hooks/queries/fridge/useGetFridgeList.ts | 18 + .../queries/fridge/useGetIngredientById.ts | 23 ++ .../queries/fridge/useGetIngredientList.ts | 13 +- .../queries/fridge/useGetMyIngredients.ts | 19 ++ src/hooks/queries/fridge/usePostFridge.ts | 21 ++ src/hooks/queries/fridge/usePostIngredient.ts | 28 ++ src/hooks/queries/login/usePostUser.ts | 2 +- src/hooks/queries/mypage/index.ts | 3 + src/hooks/queries/mypage/useGetMe.ts | 26 ++ .../queries/mypage/useGetMyFriendsCount.ts | 14 + .../mypage/useGetMyIngredientsCount.ts | 14 + src/hooks/queries/queryKeys.ts | 8 +- src/hooks/queries/useBaseMutation.ts | 12 +- src/hooks/queries/useBaseQuery.ts | 1 + src/pages/_app.tsx | 3 +- src/pages/fridge/add/index.tsx | 102 ++++++ src/pages/fridge/index.tsx | 57 ++-- src/pages/mypage/index.tsx | 28 +- src/pages/mypage/profile/index.tsx | 111 +++--- src/stories/Lottie.stories.tsx | 14 + tailwind.config.ts | 12 +- yarn.lock | 12 + 36 files changed, 923 insertions(+), 131 deletions(-) delete mode 100644 src/assets/lottie.gif create mode 100644 src/assets/lottie.json create mode 100644 src/components/atoms/Lottie.tsx create mode 100644 src/hooks/queries/fridge/useGetFridgeContentById.ts create mode 100644 src/hooks/queries/fridge/useGetFridgeList.ts create mode 100644 src/hooks/queries/fridge/useGetIngredientById.ts create mode 100644 src/hooks/queries/fridge/useGetMyIngredients.ts create mode 100644 src/hooks/queries/fridge/usePostFridge.ts create mode 100644 src/hooks/queries/fridge/usePostIngredient.ts create mode 100644 src/hooks/queries/mypage/index.ts create mode 100644 src/hooks/queries/mypage/useGetMe.ts create mode 100644 src/hooks/queries/mypage/useGetMyFriendsCount.ts create mode 100644 src/hooks/queries/mypage/useGetMyIngredientsCount.ts create mode 100644 src/pages/fridge/add/index.tsx create mode 100644 src/stories/Lottie.stories.tsx 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) => ( - + R4k{cC@6>tlTa#RX(DtoPFEf)X6+1bCd z&pu}}ZF-QOe@r2;0C)ocz$f7A`hyXdk3?QRvgGQ?gsUghZ&ob7S($aadhMO+ynD6z z_iDF2xV-H_!=AS5WtQ8A+wYv}YA)|;KHuARp||bIvyLC1bvE|*-hI*g=+*O|244O0 z;oYbIe*L!s{~;A{`9us244WeoNCL-==0Tv95iW1h7|8n5%lZv`rb+I9N%9|p=6`?9 z|1TB5eFFS(oM64qsKVKeh-lE4*5y)NzuFaia^J-b%<)OB4JW_5yoD=T&zb(i{wv!& z=X@V=<%a`Ti+GEF+%^5w!RtHymjBdx<8`BA=46@DDp{fQroI_PzIdLf_lq^O(OC6g=cOP>md$;+dc3q_wKsr zc@teSfV_v_sn)!?;XV1qi+1OdPuIQQr$UY2)mMi7khFC#4?fxL!I%Fu z=pz$@>sQIRq{z;y!P7l?*pv>FXKNSY;n zr79J}ksYDcXLl-XMlbc5FFW=4rrmfXG*}j+zE#E|PzUGp1?V#05QV`k=xnT7{-o{t zxyt8{3@V~?Xm!<#4iSX<=Vute4SbP*xO8+527}_rb)~#oGM)r$%~fU%gm(>F#nh#B zkwJ%X%^_z?yKfUl>dxmO<;onp;Alw#BI<3%;uWK_lzaiq4kvbMU20VXW6sFv8`WoC zJ~n+fV{d59*m%_fwv4OA z?8JIVr7q>Lh9LCKtbVIZ(lD3=+_5z-(A`SFL zZI||&*YG_!|K^Z}((LRO94Eo^d;~Z5N%N84>qGi`eU8{TQ~yJ#^`sF|qF^Isyc^C? zJXTH4vkR3)@C;zXR$>$mG5rvT#RFbCWJgMm8uSQRA(Qhl6^?!*!?-VA3GOCEk0}`J zS3bmzn186opygjiCCwNXtKGp}p5s_z(<h_kBz02CZ1D@T+2T>(d!ABK4q~CxH3@$M%aUNynY?4opSw#)pHfptaPk_>lp!$<@JKYDsWZUWFhoxhHKrZLVVGm8Og?q- zIas%N>dZ4>3!W4`wUx&$O*wsF(m3Yzi79D0Xl|-$lU%WYf^wMH)BOG|l!F>FwR&xl zN~yN<<3}Gwit6CUXpDMGS!5}yg5wt%9iTmY+Z z`@*CG#hrtr$_&{=gdNWesY(N|`Seszeib*SVXMtJ9HNzLu3>!Tp-cn}B-?V_5Sj5= z&MiDPM9l+MA*j7g0W#eC7*!jD?F?OL>_D5**ks8OWodTa5A-0(RnSom&b6FT`m2jAp)nQ^z|Tf<2@RH+-M(wP8k z9;?K8&18L{Xr&76ow-gCP^vh^@e3;s)fzRw*qmzFbkjwo#?o6E(c;!o;^;LW-18oZ zI5@o)TIQZ`3nJk43lq_4GQQBxE6>7bYs0mG)7m`KDhgM(2>oV9u{L|i**{?LyLcY= z=i>RxVT4aT;-ZS$;OK~Ea&$n>1>5l??lnInTu-5ohYbp_Q-g<{IhmFpgrCunnEfqQ zG_VCh1BlB{`3y%lKP|tuqZ;LwDm5tj5q$Epk+Iwt{2rxBd8CUU5wj%ECW_5D8goX| ziet(1$|lAD*roolYYb76_pfsgF&uC=W9lFjL=H2{KgOV&l6#-r)a_kcPssRXm~0Wr zrT-pcmj>(=zB#svir`t2y()B?Nhd({PqPR(VR|#4kj%4apx${#$VRMFu5%me#@#UL zkBs;ojj>-eH@>r{pm98%G3T7agr0{p{1C`s=WFkIZ2)Y~BjN14c3Do=k-sJP#mUZC za%%8zSH&}348l9#(ZgkV3--Zyysfv+91e*vc1}EsMECOo3eF)^+br|7TuAaC2(pU( z`rF5_eNK88|6B`}oGhoL@n2;ir(&>@=L=X?^mwJ){=`BEo+Eeu;JkAD@D@I|_#QCCK`r>|NTcUr0 ze3KT;%~P`MLd`oxa5sjYte}itos_K_7Iv}80Gw0eIk!diA0EnpmMeD--IT|i`;bR} zaIR@x`1y68Xgf3|fhXhmch>W3L*YB&--$K8*Xjvp&;Nx)#VK+fR&GOe6~DKw$Z zr||hgiu?_AWvQ|`8iqLq`__|VDyW$W2Py^Vv4s5w%$qcI=>ptGhAt`a`^PL1(KfMD zO+()&$$^^3;NK`EU=>%i(AKi0I+Rdoo}5KHiHs z+W$=z|1aC&uY5U2T_ZEw&(e3e2#|9i%x~sZcBnxG!hTu9j8^8)-J3 zm(EQZl}kq3PBc?zzv@2+d6#t=TvSTc)g2;n!~&TNTfJpkOVwzJ5%bdnrzm|yf2y*- zr{lXVOv_8b`%7I{xA>J#5`KEy>pL4JZGC(%(>_hROGqpBpke8rVtxi~IyWMU2r$|p zoRyL}lsGO&uv&?Y#(YmxBXDD2xc~$*mb9QhNWy*t;RXwsWCY zxaX1yNWtUxi8$YO%fz(MD_d6(XvL+9U2?I7lB9g=+q8LJ*9ab-}mg>=<`$0ciNzdk}r(OQ!`Ggo zqx3VSR=gG1kYeJ`K--1YZ+`9?0LB=r9pPmh`5SX2cK!IMgpEADay^?TYtZ=#k(|Bm zNz(q+0Q!73U8C_+jx__=&hNXbP5gS&@n~$Yz7SI8(1v=L1}n1{P%hcont|ESiwlI! z%aKe8AMUmp$s9dU>6;E>XbG)mgY|DlF~zwk>}1Cn%mjeOa`w3LLcR z^WMvCVh&Jd!fcJHH^Oh6It^>_whaRK0;}GTNei!UgKffa2#P@p-U>SeBg$lu;As{! zNN8YFH*huRtpilj?dx;JKgl^63b z@E*|{@+A!m^L%9nEja2cG7;54<^kCYzqNy}J^V&i7pY~vaxB=X8=6xE61+|Qt7X{$ z9tzV|SqyDwY#o;(ocV(KAB)}u4J=eH@3gAX==i6vLD3zuZ`t{4=01!D#f@s7Az!9E zx0Z2lEi`_p6nN$$zV#T5X!N+I1rO05uh2~%ttB?z08us zOe&92I>Z1V+E%ZzM~T&VtPcNmAT9OHTDB zXju2TexM$&u)EYZsnqppPO;@)cd@cmSzp8WxVAxQsKw=PaL#S8j1_|06Gy6V?1EH} zS$^t@%~OWvKBuAWZ;G{%?_Xov@s7B@!S@=x4Rf;Rrzz!p%5x`Ag~V80{~UmEx-4gW z0#}J~ltu@#C1wMPDn(=hR1_LqlH+7(Q#+3Gm=8e74W)tlth}Ol*QhWc39;lvtf$BE zb_x_{VAsIPD}^naXkDJ{Ep{gln&kX(MNz^7^y#e|ZU!ET6+Mc~HDNwZ!MkHAOfi2= zxb2(WFpY}HO~s?Em!Z-$CE=Tj&0ZC(zSXAfzEJUYL|X z)ukXP>g|I^d4^*TA$DisHA&O)hv*EubkC)QqKoJh^4Vk5W|;M6l=ymQsX@*W%5d1#VZkdXpg7)G^{Cc?v89QPguN}lQFWo|DE8n@ic9s z#Ho0ow$sKxmMhnS*zOG9@=(vo@66an%!tM+xzo9}J}l&yD?Oj}eglj5RC5;0Fai|M zv+HK1r91+#66#zSP97!!dZw4}4a|Id_a5rygwsX{Q!!yrh~tOHGU$X-U94Jltf@4p zP?<0<)iuejWZ|3BUGvvFo{Z-TylRWLChWhzWN~2Oiwf5ujxJA|H#M|g2~V_*joj)G zStR%t@YE~Ugk<u1vW4eh>eU-Vkt7x%xR& zJ7%y(vOMXo-H@J}#|JIyH_rzz7Nq6O47hhm>LZ^Z z>0uO|XnC>K!Uyy1e3k{6&m8V)`pz|-sy7MhiQms17C&NQos{4xoRO*TeT*q&Zgqt(o8c{~~)H~)V3Gz!`#(js@Yv;V%pHTfHWQYOC#@%uc zl5UU;eoaA!ckUMJXR8;=4vkvbB*ayfD;HzO8tfMn1;w^l+Yzx5U%#K*V~b@=a!pXb z%SxFLbZf9pa2XkuI`qlYmap{m`(b$xFg)3eKnH~z z>dL6Z#oKtJ=l<52Md!tFal`!a-NmtmiQPLA{&bT5lXv_x^i#e7$AmN?I9MwKG5Rr1 zXiupb^}XYI)xnu|pd9FdP&RdP;w(a;+=xnOnp8WjbN$g{_8a#?|>N4%(LgD8`!rRlG` zicCu4*Ylj_w7RH)5RZdRVVFx-+_&Y=3Z!-jd}|NhO7f@J8&*$DFBc@hPThQMQJljL zICxdL68vh2L)12Q3W9t)=j$uC&;%+K0V%x0UH@-5uYmuy-~TJyHOXIYKwKcKe#``K zt`w=gzuw;Vvlpg>%PHeYe#Nr7dNQ_~u!jgf7uMhz)mb=&z5}bV^N=cBhN{lMm`9r; zVRvyJk?}a)Wt%hZ!?M`hvxQLeX@bJu%qucu!m5o$>ulWlZ<_K zEA)G6zJ3F&-#!~=o;f7I&>~EG*G zI2C9v-V_>S1$#3&XyJBxICVS1t!>}!Dl6t@1)OtC+ua#`SC{$hub9k{Y)^WCAj zrP6q}Vojka&VL zA!WNwU(N@K2+1LMImaID2bSd0nQI*)wW38iJDh?f*U#!;O{sH~ELi7bY$VQ`)VWiu ze6BpD5_OtomS-IRVa!u)-bz$*O=Xl4MQbomdw=T}*)-ZjKTF{9M639~`^k(l z#-fH@0!%D7IuA-W@r8_p+(lvzF2MxU*HpCv#3?e@;1kbqvy->IW!{SX5r3GS0b}I5 zx7y&5PX55yx2DL~peKLR*CXlyB)qS%OJw7R=Yp(E)>fFtLky5%$N%y^{}krG&sTpH znJ09>B<8pkyANAElT#oenEC$aS_HsAZq0^OaT*W@d6f~js-iE@?L0|x<(Rq)dEm50 zU|uPzj$k)qvs4kYxB+&br5OOmU%bHjJf87uH5aHON7|du=pK zd2;{fOD;G{jxhWz$-?(9{p0!H^dD7zaH1jMiy@c_BAmGrnG)@pD+?ldDGGr_QyOle zQTBQK99(MrEto!;J2clRsc=sYOBn>B9d?DY*E_sj2`bPN%tJRa(s;(zFDzDqx6xM^ z@&jJ4Gzcd-45#JeTy*=zbY5fIHuA|OtnG(qvgWOk`jmrf;oa^LMdT?4XF)e_KOt`!ZXCl^z=WUqjMs&Sop9JJR)-hExykG6DI zdtK#A!_C66yG9G8XF?$im6FSx|cJf|b-rv-PZ?;2;;vmIB6H7S4AoOag*|CAR52 z!8d>#bzdMmLJp?3r%YpkrU&j%9>W&sedyhq!qwEx%Bb72Lsey5=!4~FI`it`aA+I!Ev?$mLj^nty!*! zjZ&-F!(um%O%tG3xidNqXU0F?z!@=FL3Q~dK6}rcIRVdRpH8}WEb@@1=GMz5bk&tr z2ajzXzu>f8z{^VwZ%5Ud>+nys0K=WL{!C)V~o7#@1Ar>};) z=D~?!@!aSkz&DUS?t~^7sTu5BH*0w_VDv*U3>B&=DTecivaXlrSt#Cd+B)%Euu$dozU@uv>{j6WMD=zWLE2RA!U+G4=+$IPy$|xs-nWgf!Bk zeAeNT?x*knCSwb(ZN^dk*_WxEJx)Povr4A+oM3os`5>N8-7RKZ)0mRct(S~2k?Czf zza15OKlx)!!h^&o?&27+jm&djo;t0kSm+*LRa36(nM&0Lr%pu7=9(4(W7-PY3j3Kl zBnkG@?px~r|PKDnQd79IaLp2@<$*zTU|ntLL0$y%pLK2K--kYFsC>%R)Z;K*E==Q*FQc{qsR z(V(kHy>fy~rI?>Ix#i*s*ksQSl~(I=WJd&J6Ghix;VX}@XaXsR!0t(ualwrIzC^MuBDFpWpiS0 z)P>^RPgiYNJl?O-I@&4KRp4Qnvv2@WOHBDP2>~kt| z3f|oHdM<_>R%kVwI~lat9blz03$`$3FQL~~2;y*w0}{CWw8k-c(0#u^j4lYKCfaK} zT$HrACN@w_>u-&}G^&6rCXuF<0NacXi>KKgy~IWZ*1letYnvi5LSa5T*!e^kfGb94 zYVH@3H)%|XGGXKVL_b^oq8&DKO+%NFo;Dm5u_XS+lI7ihs=uuJEX{9J{!1-xV>)w& z&rvJTmrLgEsCf2ZTS~|IQLLtC5nEFx$eOjUN4K41JCc|RL^%Hf_i{WlSq?_m=TG1T zG$T-~mgo>w^>nrf$N}x;eF_)D66-nNmKy_xSBcrjqfcCtPOM0|gNk4JDzl{`;USCu zYI6#rr5A}0{_=x{(Rxh zS)3;ZnUdJuCF<0&VsZxF`PUmE0vTvaKw{)lF{f-5RkM&n(Ehv ziJ3+s4dcVRFl!{Kwo{PZi8%h19ZQIV9=Bk-$p!=TwNL{j#KFbX^s?TvZqd!H%mv>r z8@J!M@;HsVXt!OYTnT6XD+zY#FIHcF%qz@Km+pMs`Ji%H%VzbAqGJ(TRz7LFxU;CQ zZBy1SAFoSSi(sQ_%=w3(breKz_Xxk{QirI>PeYG42_ zrQ=F9mieBe_YW1d9?mOSnfNS^3bvG1?p(e4WU&^qOOy{u{%LUKLLn-%rzCTm#dGJT zD}kN-R1}5_;+C?OnI$44(X_%9#tufDY1^lyQb9qDE&qqFtg>L;o7SBs9fZOD56 z@ibG~FUwc>N)R(3gsXFDH@EY2vh2%iU8nA90W8Sn5|{Kco!p=??Ie0_L>|Ow0HRHK zs-Fvxtgf)!I4zx2DQKx|a@eB)tCj(m$ok#ilnSyzJSU-nG#h#Gq{0dJNNX@7_(rFI zWx(ioO_i)RyAS0q+xhAanLL6xneUP*qS$!tH;O5;X$FiWP8Tg8@2b3? zjZ3sj>0f28onLbSOdZ#=SBM+EzhnW?%YL8e=qUTv`OxTkK>T`m`M&wXJQ^*+6Qk6~ z0&tbsun~>n31U&=Fk_lv#Gyi%@30>c*I<_H1X%jhi-ln0fCMwu@iL-GOq93@$Aqu= zd=+New~)1wwI3zB;lYLO-H$hD(?bi4n6&PHF;#v28=L>q_xoR`k4XMK4>!DNZ@ZB3 zJ`^1y<<(zsHf)nGp(BsGi8kZEGA-j(R%KiQ+ui`{MyG< zU;9(OqveyKLs!WYbNl*@wa;u=8~09iK7YIKk!w2hvsq4tO}=}-nMo_wW|1Nd63{l0 zszcz>r=!;ybG(fz6c2b1P6`MEMV;H}f@v(Zl!{8qk}eeAn_c9%fIUJzRD7GZ1Xwt# zn{f+$hZg3Pz2thKX_KAR(a0htn;o`b@zf46YjVE<%|9O3DF@vSKM>6ho!?a`(+vL; zYkSS?FYKV?pHpP+S4+p_*3btDeMDxvxh{wO?tq?eMqo$Wu&9QZI;D#0HY-5-c3drX zo$vBh*1@XfJbT>0?kc4VRI%N~o~h6Ydp4iin}qzj0IPIOZ{V3_Dl^%YM=}>cz1f)2 zA&nA_B`uF$JhJh8fk>sMyPZE0?m1Yh0b0qh1X;;{lKY&#d0iebD0pS}LjWR;iuom% zp-2hqN2zO{yUa@tmyE|v>&rA{~+ zs%5TUQ93nFZpj+@{R9^wO4p#>*h|YpHpgGDw9d{rE*JEz_{FdhVrju3MnwPq2zKWo z17yiR*i{&@s<*4?f8pr#MRnDGnF8FwK@q$An=CRi(+4-Yn%A#^A$W+6T5L5eSwq2Bi+F~use@=xWy!u9DDRBC_-4LX#baE|FcPF&J%#dXSz=M08 zPMFq;$SCvbXn=I|fU^)CTp&OOCg7Y`g*X-8T0`*X-GWIxt3m#9N??reLEK|!0g4|y z8)g*uIG|inRDSM?uGOUwieQfbcWb($vmN5h01D?xRjyA-q=FDmnsEQy5TD-8ZIYnj zT?Jv`iCx>J@rSMWjsEhk_5L)n8RCzFNwf7ab!ryjyqT+9jAz5c0vcjOkP7mUD@IBo z_==rBC%Ej)GZ_0BN4>vw^+UiW$acD%Hp0i8)AhSe#s%c+Wsj|~pBUw7qJzUarZNUN zeTp#-@iU%z&~?%!UUA%nENFJZ%j2dpb!Q?$+DJ5W77tK9dTKL5+ants_ysjiAE>j2 z3G(PT*`o}c_zgP)H*?->jE#5MqlGMP^BxpCq)=;RP1PRo?)(M`%wYCd{+PD-?ec&6 ze+EsgVcDiGeiaXEB;uF;wy~#NcHg*FWv%>(;P|VS-Q#?Wzt{E)m0o`geluNjNw!>Rvq5lNjR81P zeM9MI_sXX_N6xDgQ|zb?Ci$Ce@g2O4cQ{+=ntPQXVmS1yaml`q8fjkDoZ%~KB;d<1 z$J4Cd8RZ^9I>24@QVj+_Z<7HrNZSgCYCZ2XzEFawhEc2DXX)e{?ChyHnL+ug(s3w{ zuWhXyd_VB=$)lSWE`0g~z@BCV?T}#R|HUrTA4k!D*);zao%eIit;;2j?BrSu1`NZ` z)2G<_p~QYWjMysSmO|H2EU#+a8c1|&+w?IU-CHn0LubOKqsmg0$-1!xcE|YCww@S< z^Gh(iS_-hJ=wc_n0qVHy8(lo)Z<*!a!V?)gOipCtu8eregXU;n8 { + 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"