From 1c88d34d38d8c70c39688f65f1b3a8647fbb2c23 Mon Sep 17 00:00:00 2001 From: Chung-il Jung Date: Sat, 31 Aug 2024 16:31:36 +0900 Subject: [PATCH] refactor: eslint flat config --- .eslintrc.json | 39 -------- bun.lockb | Bin 174250 -> 189074 bytes eslint.config.mjs | 88 +++++++++++++++++++ package.json | 16 ++-- src/App.tsx | 4 +- src/api/ApiController.ts | 10 ++- src/api/Auth.ts | 5 +- src/api/Lecture.ts | 10 ++- src/api/Major.ts | 9 +- src/api/Notice.ts | 8 +- src/api/User.ts | 12 ++- src/api/etc.ts | 8 +- src/api/index.ts | 2 +- src/app/recoilStore.ts | 2 +- src/components/AsyncBoundary.tsx | 3 +- src/components/ErrorFrame.tsx | 1 + src/components/Etc/Button.tsx | 2 +- src/components/Etc/Modal.tsx | 4 +- src/components/Etc/RangeInput.tsx | 3 +- src/components/Etc/ScrollButton.tsx | 3 +- src/components/Etc/Spinner.tsx | 2 +- src/components/EvaluationDetail.tsx | 4 +- src/components/Lecture/IsTestInfo.tsx | 6 +- src/components/Lecture/LectureCard.tsx | 2 +- src/components/Lecture/LectureContainer.tsx | 2 +- src/components/Lecture/LectureInfoBox.tsx | 6 +- src/components/List/EvaluationList.tsx | 12 +-- src/components/List/LectureList.tsx | 2 +- src/components/List/SearchEvaluationList.tsx | 6 +- src/components/List/SearchTestInfoList.tsx | 2 +- src/components/List/TestInfoList.tsx | 20 +++-- src/components/Major/MajorSearch.tsx | 4 +- src/components/Major/MajorSelect.tsx | 4 +- src/components/Nav.tsx | 2 +- src/components/OptionSelect.tsx | 12 +-- src/components/RouteChangeTracker.ts | 2 +- src/components/SemesterSelect.tsx | 5 +- src/components/UserInfo/UserAccount.tsx | 2 +- src/components/UserInfo/UserInfo.tsx | 10 ++- src/components/UserInfo/UserPoint.tsx | 2 +- src/components/Write/WriteEvaluation.tsx | 4 +- src/components/Write/WriteTestInfo.tsx | 14 +-- src/components/index.ts | 24 ++--- src/hooks/useFavoriteMajor.ts | 8 +- src/hooks/useLectureQuery.ts | 17 ++-- src/hooks/useSearch.ts | 4 +- src/hooks/useUserQuery.ts | 10 ++- src/hooks/useWriteEvaluation.ts | 11 ++- src/main.tsx | 5 +- src/pages/Exit.tsx | 5 +- src/pages/IdSearch.tsx | 2 +- src/pages/LectureInfo.tsx | 4 +- src/pages/Login.tsx | 3 +- src/pages/Main.tsx | 1 + src/pages/MyInfo.tsx | 4 +- src/pages/MyPosting.tsx | 2 +- src/pages/PwSearch.tsx | 2 +- src/pages/ResetPassword.tsx | 2 +- src/pages/SignUp.tsx | 13 +-- src/types/user.ts | 2 +- src/utils/fakeData.ts | 1 + src/utils/loginStorage.ts | 2 + 62 files changed, 305 insertions(+), 171 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 eslint.config.mjs diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 54a2e19e..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "env": { - "node": true, - "browser": true, - "es2021": true - }, - "settings": { - "react": { - "version": "detect" - } - }, - "extends": [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended", - "plugin:import/recommended", - "plugin:jsx-a11y/recommended", - "prettier" - ], - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": ["react"], - "rules": { - "react/jsx-uses-react": "off", - "react/react-in-jsx-scope": "off", - "import/no-unresolved": "off", - "react/prop-types": "off", - "import/no-named-as-default": "off", - "jsx-a11y/label-has-associated-control": "off", - "eqeqeq": "error", - "no-undef": "off", - "no-unused-vars": "off" - } -} diff --git a/bun.lockb b/bun.lockb index 4abc8b45ca92d0c79f9dd96ec31dbd280d1a2d12..631423c645deeb19ec8a0106c01b0eef2afd6d50 100755 GIT binary patch delta 45276 zcmeFacUTi!_cojfLPKRGg!u9((V- zH;NUk*n7d=doO(V%1kuRdCvL0<$AC0uQL~S?!DG-Yp-1=$&j^`1~ZQs&Twv;)z8&a zwQ0|%_%)4;OZ)HaWcPDqI+m2%R{q`^hBasP~<~FyaaMRFbA13rY9u~j~*lv1?%$}Q{od-q6dpaaS6i* zQdaDQY)3#YGoEHikS7Or=I=#1Bt}72!xk)(3&9+}B`=PmE8Dbs98KbQd!I zRN32MD+HGG9& zc1&t&TIPsYQ2?x?@*yR}!?KT%O&~v3>s6xcYeIqwFjag51t6F$K`GSYS17$cxV0%? z!8yp*kWq-*Lx&+#1p@`ogn>!+H5ZAHCHsV#NYnzn2TX>gS?~tOfn_QvrXhkXT7%lC zrHjGjvZ-J)WHgvE#(`lQT%S5TVPvc*16J5TP6AWD0br_lNObD(QAr}v)&{%>>p@SR z$&5}+Add}3CMv%VSVbBAZF!5DfRQ#kHYH_vic@Tcw;i9cz7T&FGI_|}UL?X__EE@` zKW0dD!Y~x9F}PvFUxqjhPKb^(gG>haG~x{^08_pY8wqzMvOgl8N_hw-bBCtJrpCa$1x-aF^htI^ zGd{g9m^x?@^u&?P`F>c&Ue&cxT|l2vDM`^u!{agCVp9jE$0rOD^>X1;`-5p%I16kh zuvp+1&U|TC!PJXK1l}U>0)g`dP8T?uXH|BvplAc8Zf_#6vA~SLubg-{+!XkPzc2Y-(vJJrK(d@?_ z{4gj7(>Po!@Ki8{UUs4&hk~gh4>0vpQ?Lx&f#G{M5Pd`C4+WC}v8f61NvbrFXttk7 zgprV)0j2_b32Xu;k0`)Y;Is~W1+^4>0glj{LN5W+0J!eYkGEm5nIln$D6}I#9x*o4 z(qadRKKSzK21lo+B_lp1Ha*ojesHF!b|=1~J78K2TXg0}*jdO_@B<;Es0-Unudz`; z06%PFQ%9u4#)?FxY_y)KY604Zsh*t=rh#w?nQDX6Q{tV5riw%((vvb{h7XFZmh<}K zh_8kC{a~tev%rqQd@F3gG&xp4PqS*C!11Y0>MfvGS3cczUa!g?H6%V}h)5)gPmRrh zRiax+Kn85-#`nXB=(HhD@J%i<7(hQp;LRbtoGi%QLituF#EwitF`|x;DZRVEZNX&N zpxD?E*a#^J;&nyYPvz`r{RYyTJ$Qr9u}kz#Rp-KZDK<4ZJvJp%q*+A%cNY&sUw(A@ zqtj{hy#r$*&3*?rlH0r)-nzeTe`XPCD^=OwBKZVW7eC ze0*YTN?feFNTl(1YHZ@j*c6c{1{K>PT@x^^o>?$}48a&kON&P)?90&!sUmY2MCn9e zbDCc-;GcR>oa@IA*NpzWMV$EQZ8q(oc zlo<{GQy&Kj8QOxWMXq2PJoUj;krYfB+a>e*^Dvb19RO2BbHUWI0-*srFdnFa49L_# zZ!j6&NMK_y)pM;s{7*|)8Cpy&+=DS-4%Q4R%^tr9dNQC8Oa-dIR6&ZMzc`X_q4y}h zkH7X6T#k6^bEgbG-xM$z{6Uas49Mgyc?+f%Jr)?QO^r>X#lk5udW1+c26}42ATYJG z2bf$tXn0IoN_FOeOY|z(O`<5FpeK7r6S&(VQ_;v^!>op zOy6w2v>uRYB>94=8SY?ec1{joUKN-|+$Ave*D)~l*Shh1J<%DlPBFt%RI!bOj22*O z<_1(uBVx=%zCb3IFW@s|DljoVDLyeiHEn3>@TAsAn<%8~1tvoV#STozC>JHB;suui zdg@1eur-*2j{L0N!_rXcU-ux*-k{mTHv94hDD!y3s8BNW0rWIdG^>ZF5P!XZ?~e&c zM-6!~h4-?nbsF!PyUR zxDmK6cp?g>5#|o2#h?%dHU_()g)}x$soE3pirVn-+1swkPBUytZCl#|Q{Q(oewBIj z@rfAU%dcj%yKvv`O#T7a5#AruY8qy;MeOI=k@^-U4n{c*dz{=BQfG*cSy}4h-IoLn#8v%5U%ng>Rs2#i|JDmfH^p*?u@j6eo+TdsGxAaB ziSPl>tG~{O{#assrN-Vvk8Ik#E7?*}r}YrsNcXT)HP#+!=F;W+=ibI|->h9p$D@{8Nt-n9d{n5tEqKK;GPk+vBbTF7*_5S^}h9*na+*8z-L|J#Wo=_e; z?#ZZ>sJp8)wcN5ZuWQh*mRNZsr(rw2F9F+pExItwcQ)TrA#N{bD=g(q4lA`%NUw=S zqBiI_UAD;6Q*5lm7Ffx}U3J(BD><`~m0By9S8RZ_LhPo?7Ff%fB(@x?)vUCEg1OHI zG*CzaB_fdzd&<&>nZ}kwTh2;t6cT9-k;sn?Zr~#hticx8$eBvE+(sd`(PIs4<>DAU zHn6#z+0N$MD#SYaY=y0y8O=)V6sR)5P9c4+FA}waQ95jqm8aNN%2wFPnKV{vuaNG? z#FrzAA$ysxY`(oh+`)jYu$N1hV$iikl!!I5_mtd%m+)0&u~G+A3!N`tww)*W(Lo`0m$4NNa^^5A zZLE+ssU;G5A%PBCY3a#ivH6V^lGD(&Wee?nWCmF8+^I-R3q3ZpZ*w-=QePSat-rdW zP&-fQDoDS20xc_Vq7Zx5VGSI~>j91mX&IInJ~^5$tzpDhh}ObKawFEDsa!f4%62HR z2Dw0*jv4Ezk;Dh;vIWiLk`GXNQ-4XFu|~QgTFi!8dy4zjV=GX?GAO+@d8A^j1)VjL zIKr4UXeO5yLP?bvvX$1JQbQ~U)K|K!5gMU@6aYzWl_U?64|!N}ND~tOG+_gsj8g1J ztwpFUE3@}u9g3dW6ApPt0A?abkf?`D5-0p$BNLK&G%8DL(6>>68)ys zL@LW4q4pGFirM_O3g!)4-c}(^X(keNM@}@t)>CpAQXtJ%ajoWTK|8rLvAIapNga)L zOSeIy0gFL|MoM7`%^e9_>EX%5viWiavw$s^E2MW3)1EIB{pH36C=|>HHeaETB)eeL zu!RaANijk}>X0D{;X@I>Ld$-IULgdNki#1rY!`JX=U1rwSI7u!3m+TxE41WS=*h2; zTT4y4OoTe|w)_f7Fh+Tu@>i$`A*`aX;Nq{4d25ZX@2}7jO-S}0A^c_QVyQF1m~GDb zHkH=I`0V{3O00jfQP9$?|DBYyXa6>Bb1X_d{wwV`X#4)Bc7(Jhl=1(mM6v!Dd(r=; zT>4P_D|0`)cw6XuCJq>OD|I#{P#{Rc%6j@pTVk39B3@rTMrT0^S4*M&yz~|PO0JF)xAkQ!0_Bo@et7?cCa1&+ zLpEGJO~)hDo$8={1)G1lQXy{Ffi(z{i`RBw1B2wuH?}-TA@%nciMk;jCJ$Ci7E%{T z{KoSD60KP}6eqUp$W{c)rF}YTmXXTlUiuI`)G265KFj8JRfz37u@znA(s0Z`Y7?1I1hu)%gd;(AIpu!md{gGHhfD{JN>U4synj9Po39!PwxXtBfuD;84W?9m$` zxQmv>AWz96NWN@gpbvAKm4+*%?!mk#vCd%CNrFU%=&GgFkc5>!#8diEkZQ1%F<$y; zyU^euPw5~?)E(-LUb+|(Wy6kxI6d?s)(2WWQGxew2}g`!kZ1u!T#T14g!XJ9Mj363 z)Dl<>-+Do!!m;C{FVX~w9|wB{3A>5CC-ae&Mk%EIL-~;+W-A+dN>)K~WrKV8NG~HK zlmkQRVF;3h4cysN+^sts*h?M&GQVn(7AV}0CKIME!l#k!3ZVD+;D+aS=I2+hkE)B)R zBDbM1jG+mTs1NnjJ~#%63gauPhQwD(J=m@%zdP__PX$S6C30u|psI=k_y`7+iON5sE2?+yBke)*N-AY9t%{VKPdrC(^@j&;=CiG_u`pd<02C^0X<&w7pY1$QH+IhwB zreZtq>7@$+cLG=kS0aRyQY#NOhf85yJfDEt5J7<`0;tAjDIW4k=8X`yN7Q9iK{~z(%CD zSh514?&@-y4bd1wE99UdtieEqE_7Yg`5z)gn*iF6q3H@Q@y$S5=}<`2L9i^sOBX^A zd#a(2_1+jbrO;vHMv1fv>x|?F8Wwyps)1dUQ3P2rz;2>J7=MtF*Ejbba3VNeENo_a`0mO#Q#g(LJbHF_Gtiy`3@ zilOoyAsoEXQm@gxtJFOuNrTi%?e5i@kod-E)*wwTaTz0wJ!vjNLg#vVO3py?qWx8> zHx_^HWa(oc{)AYn1b z%H}lgS0Pw3r$XYV3eEAOkZ7RPP_H-3Rk%S>mqLrmPZe7+N-mDfW(_jr(kHZ2*R~qutb~zeXQe9IZ(V&4HcAk=Fkg)J3`bZtdYu0i~6$^=EV-n^ zWZa69=cStvB8N%Uhb)~uevrZ`Bhg!sx*$$py=kn1M8@KXiHUv>Qfo*C>U?JT{MyNH zo`WILMu&Xpl9iBfVnvgwgC0R@1&Oc0vVc#?@7cW}(PmML>XXfc6hQfK)n)xGo6l5A zb)gGngF}3zZKm+H;ZSAiDK?nO7L1dNgQv0;pi5I(1C?CtP{;;?QVZDvm0VI%NT+7p zO^bu3u?E?4>HBHFh8Ru=ji<8(P{vHx%t0D4o2Jt{UXmA34q#>3K9V6bXvESjc?m*; zNGFXi;s>BaJvf&^qM^zkv#vs-w!<&|v5wE=eU0A2bUy}(mQnt=T|JWxoFEssXW4=Y z@_H=GpZghVJg!gW<*cuz!^|O4UBvWb3w?YfUVmWFvB9=Jl5y(LDNNzR2w^Wkti>#m zC`oN;9zs3XLb;FhIzsJHV_kJmSkLC`ug@Aa@RUVEB1>zawt8%lgNt1wga~4wF+jF7 z5!ewdVzVZhs&o)=CJ7fY$!;Xz(q>)A7%%F0v|3%7H<&JBs>KJObhs^0%dkg%5mW83 zU40Sj1A+LAOPvtgv-%>Yguwtcqz6EkHdA~UKow$^s;|FeipRpGzKAJb6fdfo zePNOLFQx+e0F)31M0JHYH4f96%Sb1ei|(uKyLQC;??yLaA{Plj~OjN zm@2*@$l9z6-5o*CvnQ{h0`3YC#MJV80A+Xt(51~f?2CLG73F#k5WfKEBBtE0NWeu* z@vlk1rOlM?EuaT{0q7#8^j}H9MNG~AK>{uHDzG%Lgq?H5SS{^6J%|s0u2N` zF$E3rLLbbXl{gpX%0q!Y30rm4Ao|IZBLQL1H@Focp>56F-1)f(oF)BJegkp!eTbK(3Blp zsIM-(P)MQ8G~H(ldSVK4f~?J?nj`3m$-+Ouq?#}2iD}d>7Gy1M12rE(aZ7{*{}a=2 zUqB+KVB%`K|wAiC9YzcofIw+B1(k_Vp_M$ z!IbfYpeLr_NkJy2;3&WriLiN$gdKG2nj+3?k;c-F#bg0g4~k?T*Opi zA3@(&kYm7P&=4?HJWSvuFc~&nkW;{P5$n+Sqwi^xMVUf^v0(g(#tHiIB;X>Zbdv-w z5cI^PpC-t}RN-`iX9#*?(ieeA#o|SW#{X;tsDODw#C(c?yhzY56Xca(Dqxi$uN8QM zz?%i$2ByKg4~#$20ld(jRZfDyRQ^fG>XYpm1jvB%V2ZdT$n^O;>2HCl;@eQ5Nr2EBc{|gb6VKfp@nk>AKJWgOuI%0|+j~DVpo)Di8ZU}k3Aa4MZ zXEuTHC)!Fc0+W8bAgeS1V)Y4Ch&TYIV2L0TQ-wzbnV7of1egpxDex&URd7b&N^ot+ zcffT0g;i<=WvD^|D)5nz@rfY61XBgCz;qFlE8l@h^-<9OpD@+;S)H#NA0Wk7D5#=u z0)H3yhdLo5a5WM@=^F!-zKOt&;D7HH|Girj{d>14x+BbqyEG?o5!0Nx2hikw1kk0; z|K2VBd$%b1_imBDYov`u`!10t3(2IPLoWi;-t?!S|MzZ@_s_p~i~rs&{(HCh@7?0R zcZ>htE&ji~TWt8hakseb%i`}fSd(4KZN962==;`Hu6r`#^`w-{PCDa<8O8N)wxX^0 z_^loN_I3&?dDMTr$-Zi4+pE*efYmeB-Pd`2{>G-8ft?E9H+^$D#cJtv$>x{avbMPD zuse1r+0WZ$tZb)}@nNHOMzE`Q$k+pr+Oc1@N3fAQWo+UuC8J>X?22IZcgfhkJCuwc zYqUFp-3=*yx030=mO>h`TgE!TH3Ate*T#vg*44$0V4kYZW$!*J7K8Jl%j$;7eckd8xgI-+Fa*~}ww(-9ea6VfoY z=~1}psEjQ$`tB|fhYIjVDug>QmL;H@QeUOq_@8f9SakTHal1XKsKza-*xLk>E z%*-uE`^wQiNTb+*3bd~R?W<5Snd}!xpDScRA9~$5HoNDOAcKe3pKfi}#A2SRf13@H z{NImUYV}d(JMzY2lQyy)Zlhm}y`rqX`RD!Y=9in^unE1(7T;St`lju&Sy~-2hFx<4 zZaM)somAr4kf@Vz(@9y-&wd_yy|ye*DoX!obGz`uq23Q`c@|C@^+&%OcbB^PEcdcm z9<*kzS(m5n25sthS=#%vfi%ndti501ma_>4f9}0xs8xlEm7apPPr=)#lz6mf52W3v zWU8H87K}CRT{|Fs(3km5PTlgXleJ-s#j$Kd6SpyAZ+?H$(k$`LjNwfeJ~%Y7uYPay zl!e2Yd3tX%ugeB+3LomDdVZ01??{#o=sq$zCE$_QpEn^PH76F0hyh#%&K z?A1yPgGvm8b4q49n|}_&;2eg*IbS}R`iV4A5$k;(BjP+p#CavdvQN%OFn_Rq7b2Kh z>|DIgX5ZkIV*@TmFmu=?c%93B!RtIW^il-#C%Xo(^I7I{1it4Vh1Z4b7Q8NErB@=D z#cVWQm#};Ax|B7#8o?}MngVVT7+t~wkyY^U0beirn(W6 zwy(msX`QYk&o{kg@wBbO3f=rhy&72!=C0(%EDl_5n>Wz!>-s63t;>2$_P^G0o4T&J;#*UeOVH=PDZ9Gy)(uH_Gl z|LEx;%5_frP91*7QwO{)ybLxySVZYo3}M`a~Dta3DE!PxNBG5L1yATix1!L ztP-rthGpxtt$WhZZB)0J&GMIx^2ttFG+CT5;zCrX8ky~b3f=tjG9gFbgb7dICgul?%zG~Zoj>~e{}k^ zwA$r6YmB&F!#mFPt5YqPr*XJG4vp;%-|LFS_L>_&@bOUX}at z8DFlL+_`~1JJDRaZBGu`FzCP;m8HRfXT4q@2^;WrbHv;xwd~tR4%@r1G9!M`qHP;z z?yWwfHzhasYw_^!O%^X)bxv33{mR0Ovbl|)(TCEED4U*+6m8TL3f4{LQg-LxJ2&~p z%K>$kud3a7@XmslW-W6<#yw~$4N-aQ%Q^mOZu>)CkL8zp7GCSn-0n@ojb9DdQfE$C{Y_!Bub=!}ub^wLaqv3H+|1FT zTvH?a(53-#3E3C(6SMsz6<(t!H|#Rt<&UwwqwlONqjFXFlp`C3Zq_dN;byOc-b2Ul z?QIg?x&P?p>zgUE{Y@UvYRZb^1a;qvI3bW?s0M z((>mMd+?j(bA5SQG`pDm<@4C#%Q9 zboHp=E5SPQ={9ZavYJodc+=eH1efWuMKSVc`NT3w=d}A3byvlxTzf2@W)?QFe12|b zyein>{POAbzGS;Hh6T@$F5291OYa@S+E$dOs0-FrH=DkzqPcYMRdo3Ac2HNll34Gn zrPd}VM_*fZKyjx$wejBVgUbx3?kHx8KlBR^3%ulRaqGn>d;9RFonrSqC|I*`_U*G~ zvT1Z9%HSP88dy92W5hl?n|t}(nwy?MOXqLMcy+>I$HuYGpRO5uf8$TXYlprUy|Z9y zEiDV$=aGAU)y_lDKCf#gZBlbtD@CQ^Qoo2JVd~H1so-oByi;Q>Soh=hej5vQUoO8D z{qx}Z$Wfxe^R0u!rTQN8#+CV%sl2M*g+-o;8ndl#N0%S|y=QKnI_}_!&|SBF9vf3@ zV&|>rlO!Dli^;{ivm4hdKKuAt<+{{hx6SV572U6IkLvm0T>Yj&A2%M@?z?cH z%kuoYjjuGEo;iQ_JpY0Ru2nx0?wGACx_qctVxY2q_o z_Bn8q<)+q8BURVkM}3Qp9PZpLq(@w>(X-D6ggx4}c6HsYg9B|{dZaYzw0LL7`k6~V zr#s%Xu2dV$Z#R3`FL!W4xFb{5{jjWb;Ommb#~a^?VF$M9-f^s>zg6i1la_s(bnbP0 zZ(0Z2pZk3OxGEYE5uByh`>Xzith53Cx$hj7jyYD@!R(c>R-yZ}jWyn^zi7kJ`%8Yt zUmCL6!+!6zE6uh72NO6CEZ z{2J|hjrKu$#M-?<`yl1MQ8G{1N=O-R(7v}y<{6vw7VUeB_Cb2Vy1hgDAkBKG#3NRB zA?3Y8``#<@WMa{KwC_FI2k9N__W|vLwBm!3`M|z`H0J}__fg4wVwZeG`#z$5pOnlO zHuMwP2WbbSZ!Gf}?fZoGeO59**e#GEKcjtLl;Ua;JMc?SR{skc_(jR!8{=QmKuGCd zm5dHs3Ten!H1L~}k+8|%(7%P5vZC-f9UjOmgBf~%L z?Agc9$9BVbi+v-;M1=d8Y+jRY?V(qx&z|~Wrs_0!#0HPE9}I7q_8a`Y>BzTPhJEA< zhYeUZ)9A~FTA5>NezHk&{j4KylzK6G<=AS!$CgJ&jo5m`w24uF(`nak{Lr3jwY7~q zv97LB)xoS)HHXw(did1W^jg8;X^)x?TQ#;_$6Zs0#TRT*1hhZU)x~?avlZIn?vK>5Yxk zx;Oh?k@EJWjdb+x)eCR_8Du!P)yEy9eQiB=)~?;;YsB-#8!pN-wdR@;!*;*hll7{W zsVtYjj~&`+HLMPLjOSR1}&e6pR6!Ew%)mY3zMy5pHI0w zs<+j}8@+l44QTzo-_A=x*5*kiR>mvtH1)gM>*T??)0VpQ*6U_6(YVEin(WPLGu4u; zb-S66((2P26^&nwyYgUFvP@@t3(=S#&OwXnEC@P#vua_^HhYt;)dB5G>Ls>X`+7;` z^8NbOJ7PmFZ8&m!hmV$ZCfe4u8*5XezVo>{?G?3k7Wv5|JuF|W`jl>eF>>`Tt6|^o zOzwEMZSG_~8EL1cMSFzV z<(_`n)@W>*tdH($>v?a!=@-|Sxjn--vZz7$%^?>>-_JU<`NH}4O`DR~+}TAOGsCv$ z8e`)yE$d9Rtt+o_Is4V=fLR@`OtE+F_4wPC+fQU;>&&a@qO@LovLfRxmvZyYeubmC z)1et@Q;+VQ_ocpKqU@Ee&zI!Xkkqji$@{r>49pu?x@JuJfW+U5^NO}g)E-75Lahq&6ly+&-Ga^pqKIU5STg&bJguIbXZ&34HrnryGoGS6Jw zyl3pUDRSY&e?+Sn+s7RC zmw&$eG%l`0Rvg^w&SB-fG=sL@fgegYc`Pi99UNAp=h41XlNV~4XQ^%8+l?3N>PUN! zAG_n&MTLv)`SX1`)aW~$@n36N95vUmq1U78w4=2RyP8X5^~Ge#mw0IYr}yhxHuUy?-+V^G=XjSW?TKNnZQhcI<%$S{?bF8x`5cSN2^(=D zq4|$iWdn3o%?id|_U$y>`La%UuA_x{|IKYjpytql^0ZN(&?-1CJsK;E$lHD ztON7@Y{$KclU++sx$Z2i;ndGVG$ zd(YhHnP&FD_|=fQ`*+_mj|#|aGONM;sYZu$(}UlPxuv)@r2d*?Hd)Tx4>Hg6;H9$l zU8a1UG$Uu4d8(gNzeg28HdFUm554dAy=88@>tCJRM(HNsuQ6}`<&R(T4xKaYnz{Hz zPC@fS4*kTF-X-?YGLH@knoD=eunw(cweA<3^f@?Y*4LYUGv;4wc}f=bA|jyp+f=(e z&r9*{i(Ic5nZKy{zLnmjGhZrK_iOy<=Xe*7Gxxp>Y;bdAJ#K?8%nKY;aC~cp$@X%8ye)Rd;P z^55xMH22jquc5YiPseQAH+4k&O3&L_f1FVc`O>z7-$Z55a{quP9=VS$cAIXVT9!B| zW~BLzgsj;k?e_U478y^uG&Sx-ru6E%TRqLEg>ii)j2SD#3CMwU`x(KR)nKrJ|5P$f z*t-yqli0CZ$uwn)sw21zJ%}G5HlO9kL~xG!jBM5lM#(tMdIRwaL@u0x?7}T!pvaR# zArV92#)XQZ@G^j62Ps-|j1Cl!NfE09MQd&gDdreLVWJC#Cl{>?MVFdT93h1_XC#5* zGbz#~Q221Aq*yJ3!l4Ef?YQI`P(;>(;vy*&oShyN`n93R)q}#1t0cv4Qnc2Gq63$s z55I4L?BKoP(d89kxO(?o?OKL)qXAFfz21O_rDucqy1d1J`=)p0ypmc15MeBM{#Be$FpcrBa#RF2ra&E>@m{~zF%NU9{?k*{glcJ*u6!Bb< z2^1ODP<$Z8FwU<&6pjs`SWzE}MD7hKu8<85>qJhY@m>sL6OXbnnB@Z3&jpn zq;iZo6pu*}YYs&^w}ljQ?4U5QfMOIEZ2?6Wdnk^OB9k++gyJ(P(k-DF!C2^0&s{VQKRga0!2KArWH~L`X4jr#NUm9!qE~m~@cdeyR{bh7vsn+eAcA=zs z)s~Gz;tzH5ukJqfdZg~znp5s~D=`|J)YIQ|+n8<46+h=<^Vlyw0ak8zsZmcW(QgL$My@jzHTs2wad!*?XH02&a135LvptIFFN9&=d^oe+Ns`!H@}uH%X!p% zl*NgQ{=NN2AO9$^auSVSoiyWn#k37Ocg=XH(`*v|J#YS|dWE+0TJ~sTFlXYEPmc@4 zwW1~*2x<8}=!ombz(23|?*FZuezr$LgF~AP?z}LsYGpNesM#@_&9A%z58Xa>cE#6R=ZuPfOscROz3kK2=G)91S6*KKfU(^;b6k6)mXp%b3txZUJ>%T> zU8V~kRxbRI?DOG$THd2~F*TR&8cj@>~ zk)C(se)JA8n%1{(%l02aYd#y+x#az-75mJ`ohmQA7dcrsGGo}y>X3r|!y26bvHnHv zM~%1xWZtL&lVYt`zj(iQ%YZR8hkqJ&tGC&5cdHU{-0azJmn@&TylMAI;g6k6pMUb< zCI!3AwP|JgIXtG{`{0;P$KG4^YeOe`&FOxPwt0uM&vrU?=V3j+)nnh^$qK!7tZsxf z@AC@FwLWzmQ)XOnH4a$OaKQV1=CN~UR-j z_RgM0*{741_!NpJ9v;PiS)cFRb=u}FS>5U2y&CO%y#Cqz_yDtufjhFZ29}Oi?%By6 zzWQ#uv&(GjPsgM7Em2)NyKHhJyP$d}RYmQly6N5Nu`)Q%taR&UdW=ESZq2h0)HS+U zZHF&#O7K6i>+_t;B}Ln(f8O0ukyN#G-0bDW$L;2=%I|#7qtmHcFZMl;S|gqBHgAtt z{SGb@jgHi;oYrx^#cllvt#UUozn<(fYD7Vbg}6%8I9TZ6}>sA6tJ( zar(>YIYH~@zFgY(h{~eT_T+65=RS^G_wr5MjT^dlU-|XTS=os2mws9WZ{(UbWg?hO zTn^D@?kdq1&aD|}E0<5Sjk`;8_k?9nADlX1?pfcg?Si+>mG4?TD43&b*sgqZiG5zx>K(VGJ=QwurIr{ldylpc zE065G`0&9b&*G_ChLmwqSJZNdiv~r}C!3l}x6o+TiHwle_>~^1?n!mBzdvDLF=Tk;GziRI@@xg`0cFFTKf}l`lfir0{Qr z^V5W;jONlk@S;Gms=D#WGrE0NCq%y-z4^d|&`1l_k+)fml~HYFhwWO9T57YVt+C&s zh-~HHxwR(DvM9S`ac;eN*`TkDI&D5S9S`{`@K5G+ne<)Jt(faIxDX$vrnrIiu6{m@ zSciGXMa$8LJGmk`GlludSu2=sCJmkV|0YS_BGvo{?buy83g$M$JlGZO$Mj~n&0U$c zR4(_uJ)|@l*8~#v6cR?{+PIpEni^l);OhRc5@PMt9(nYA#$zF8j z|8eFga2?VYi@E*n8DrMg%9g6wPo>E;|6f(o8?|uw?|>a-n*Y=5(nrYnO}q=~6U3CE z!GB~B;aPiBt@-DyeA9o5srVa|+CcM9HTUFl8=NZW=$nC*mmVK{ zM*&>B1RXuNf{!TF*KV~ApRdUvYTo~*HI4Gr0yGm|^S~KB;cx_z)JlGH=0EegDpX7l zpiy3W2!+0sL&hHvbo6`(J?=rqYaUReN4Lr~pYPGrYLwX+r~>FJ5p*U9-vcO));g*W z<3#<%YKr5(bD{oEuA;|~Nk@iKd1k;ffYKiobQyH=!S%;EM#SCN%rxS@uVK>c8$e|P z*aCI{^>{;oo{^wup4W-EwQHHiDtacfJ3u{Mg1Azk3^)WF2B^2E0Mx_Nfa$;tpa_@= zu)r)}HoyUMfVsdt;7?#aumD&HEC!YUOMzv;a(bF;1p+I9)xa8HEkF<93<6?-!N3rJ zMh=aXVL$>f92fy411SKF7tOGftElkpf%tLcmX~@TY#QOa-gS)9--w=fakz# zfQH$7;3Mz}Fa&A>GN2YfpSIDkqYts@^CtReXb-R#ppmx`7zzvn5`f{r2p}0q0WhCM zSOY}Sz(5)?F#tW9(i&(Bv;*7#cfb*71kmHtG&1PnoO8f=-~wz! zCrI!dcmccwUIDLxH^5uq3gC-$W56xI&4A{B3(Bg1?gVfMI0_sCjstEmrX|n{XbpHU z+`^-I;3RMgI1S7J zN`a+79*_?V0%C!|Kog(?;16^JIsu)5Ecoce?|Rza@|`3b^L zfoH&T;2v-vpoi9|kNo^1qZ6%9lBaKJ;1vDpSr zqsK6lfp3WX4*USB0b0Kqpa!4^NC6|DE>I6J2I>Q5fH`0R*aCKdJ z5DNIim`=3pb^(+?5D*G<2Rm3<2VRC?E(p55q11dVmy=0Q8N**T5U#EkMtw zy+Yclh${gm0kl|hkCH3-@={0yL(K)crupbm6ZLfm1n4dhxt1K@?p{Zq> z7EueRRWy>Q5mexL;39AZxDH$cs8R~w0m$fEz)j#T@Cfyb9twav)Qi{~@GIa2KpWm` z;63mK_y&9hXfY%ge*{QRGI2E^X5e8!2cV6WwhgMo0IUztR-!ovs?@H4f+{x#*9EBW zHT_x&J%wq-s|VOZCXbNItsv8fm?i)nCF%p3GOBCmofgq83qNQfM+!geO35QUFw$Hy{Vv0c`)ojTDS|8w1*q@( z1LRt=k}?xhp_+^|(Ece*qnzS2Ws*Tu1`SdgloUr{O&S$dOo_-%l#nc>7ShP3kxHJR z*7Sn`eStoLAyk3JwVKMbWeN`m5&;_Z@qngMsxt{dqg4N))EGg7OFJoerVLng@Xa>*>gsD~20U9vm znIM1$QCDCZK(m1Qmc|K<5p6GNTB`8~F_lN%LS+hGRH+vdDzFHkek3EPRf~Z{fLgZz zpgmv_Fdv{+(SW24M$@W4p^pdRfY|`GZXS3pFbCj-u*NWI^aASth3WubU~gp7PT-6< zO_xxCB>){P)=%q3c3fdGW8Mw9)jtFkvAjsK$+zz& z%8K}w>i@ARvPD+SZ#f}`a=P-jALfYB{JPWcmdC<>~zGmc6O(>zTo;I z>rfH=LpErBmI(=3IeR#}LyH))Tl2$Bh;c(s!{YMJm0S4bgvV`O|c-dUEd82+nAXm%$dWLIyMw%z1Ch5j z^7@qo&lx_#IZljvVGn$n%+)JpS~BCf&O~|Km{P_Z-;kIEsWr_I%H_M-+3n9ai(Qe+ z)44Tu08Ir;#1yVvy)(42bpm4GSBg2q{Xhwp*N~tN68P-Qb-MR@gb@Rml`iz>L&Ltj3zDhxSyt<(FrAswNh?!DosHAz^sWOmddoTB-9hRb012D2D!f7!5k{<>YM6&%HUd7al>k@(H;4hZqYh&2KdKVS8pj z?ceu`m=SCgc}uvw!;HIS9-4s@boLlCpRe(g!v@!3#u$IHJPC2M8UtKjx(lm%_!3R zy0oRe&Z5x8tsbZ=!3^^dX@01B`rOV#-de78K@2=dP6@BgojlICGk3Xas`NEyUXGct zoeM69Bvev-ijgpfxg1iRevi5PmDrd!J_{j+3Gx;(Q(E^s>!b1 z`t@A?3HU{;*xaiXuL)W-5M31@3vzUh;jk(RI zF#@&S&HLHypNGXi`15Z&kk%l42~>6Fe_R@crInUb?&KN9;-6MOjG%muISU_{aJ4PP z=3E=h0ZYvfwQGK}QOq=>JqZ`ztTwdki!?um9w(AlK6SimjJ~9e$lcj>nHj&|9=bbq zcH;DwH7JJP?DudB;3Z4V53SEQusr49$72W83DkQz@5Xw6?TR|gC+^2t#!m3-|H=T( zFUgl|&TiiztmApXG;C^nxWvl;4FiOJG-G_ZiPi`7kruW-EM{LUlmXs$QNQuE8~)0S)v|B-bj z9O;C`Z80~U(yigvoJW80HR-|Ktwr^$xzFb@=mv0p#$t0z&9BT)*c^MNnRmB6>Z)9w zJ#kCI^}m3;Te&xHQIdk&Z~-;qx82nn?5&;aYR7b6u^7o)@~5Ld++8GK26Nw#OI+WU zbG?X-w23XhAo&z5z2R>6r4;GukcXD|b6FQrptl{rLViq|J>J-0WkV!z@zVq#7; zd6$9|t@(!)eH~|g8CFi=?99dH`1WWhB=};sso2c&VPpP4^K9stNzyhR zi;xR8(;oGa%P|$3>UxNDnsD=x6#3s=VN4khjxiJ0=a?&uHx4*HSKzLHZuk#wI1&h1 zbzJ#EHI-}B9wNSI9S_kY&hRSIFW^co#HL)^tBjSUFJ`VO7Ulr~F^2Z};n!ecOa9ax z!IfTROm$j`BDum}x#+9IP-{jy_x>s~#4I5;HI*j+ytd~aSIBf;8w<+;*XtTH)DnjU z^^d)3ZWoLh?-u+aCy2X!jWIWhgeZdvrn6gQ-rVrxsWI2^I@6Hv_WHWmQ7>@O*HPsv zZVbqBkCXa8dS=J(X}h{rjl_q#3}Tn zJD1GdKKgeIIKqUI^Feabnr;1ks!d30kKM3Cct-J2!qJ_Whk8Ux>LrVQ?q6+1E;c$1LDZQQ2FB z1Ow}Tm)5p1{`fn=5stYD-(2M^K~^u24x2^xLQB`Z=byy%`khYf!iC&K1I=9crB`O^ z|779Dk;i_ga28_Lb&BtsqG&SzcZ@%`fC}%01T<7Cm(;O7?D(YH?*vUfxN|o#^F2KH zTZMhb_3QSXK6kt>gF6#9x;Y8uY;H026A(&78Z0Q;q4Qq8^j%!}^LLu5h@rz?jkI%v z5>CGp|BhKJARZoS_zoMJG)tOxRJLWOB1T}i0MguHtSQ)ZE_nSMe z)%=R;-)Wo>Lp471bI||L!T<2@7$x^guy+{JG(wu0IaX2QMt*Sooo2d_JM32RlF9FL zEPlsqKnxwl6nde9Q}W6O{f;?`7&@$3kM71i7<)1Mcg#I5`8HEO9`;rXY4TG3Y;rR% zK=wP0=J)6|zaA`Rgu6oZXLQ;($-iR)G5u^&qvpn16T`{x;B!mNb-d#u=kFm7N4b77 zn7VVidex(Q4$(Wr(7l!B_mb5y>ZQCIF?7FZQfJG)(bJRW{Lb^wM%GgBZn>LxJ#9M(yG#*900kFkhvg(i|jH48rtttsXIre-X3zWiwU^~2Lb52Ed#cWhTWY)` z=F5FYUaS2`LBk^Hi}&Z?q1`Q!LU=pmxG(2gg=OXvQqb@)KVBrgnY(!%QqZ=7w>bWJ z;5^{6s<3>2K-N0QTHNB#Yd+y-fyjy_gzm#7eteAPXRr|?G}pwBJBezo{`qj^=r{Ww zTK(TcP;EITfdYlo+Z^q>j>xpmEo+OyaXW|eYO7v;Tvy7QffNmpqK%s}%p&LZdbMHd zTfW(daX?H@i!PNmZ?={Hj)7tG?=j7o3*52)ud-_oh^pH9bG8BsD2Sp19F-CE5;`!B zGHO=^H8d3!d?FgmKm(%kaKHp3!Ll-6ftE|X=IiU`qk9c|NSU8$dF|=V>dVT^)bdqW zW?E`y-*2CN_P_w+?eYi1K6|aT*K4n}_G1p;D5m%ntbO_NSN^|o0Ae=r+_iGJSojWE zlDhRq=A3f$z+60Zeo6hmRdQKs5^cufn+1)az27Ry5l2S|Wh}e@sondIzxy>)I6NpB zPLb&>N|4?oY0z0^y^=zookew~dlfU>OFhqF17XxCO2?;ikXD=nG#K4}PKjYHN7MhE zQzBI_285!X7)`5&Rp&8c z`Zzj_Pvs=tIIoP2*gamPegBJ7J11;jepA;45NgPA0W4Wlsr&-)2BlKX1xTKhO7HRZ z_*A;h+oIp7CZ&pz2dptaWqKj2tb0Hhps+=mYpf4|fOA~B= zpMU6YY3EblM-Q*!p}{PdBio58_CSZvefPd~zAM*yKDsc_&}qUTlkIhX+^ll^qDiDB z&R5aHZ9Vws1H0b~j_12|{lNmf;5K@=tS!^5laBcA{1gyrt#; zkQ`gdgiR>{tb-5NmWAfFVeS3r-lyFWtGsz1rL<$AU6Bb|Wr(CrQ3<_|7Yq)A5_yh5 z^oz`(jqR9`_0OO>OjI^LgCf7j#6=Lg`+LQzx`5dRhMDx`g6LlAfE=b|;R(+-GVNBN z%}w{~_N?T^Uv#b1X!qgyXGI3>1FeWnI*nePidN5dJRHz&1;!7=O@q(uv5yfNKqZ^LYDWoKOXeC&&aQINH|q@^zfCz zQCpod?)^}aRW*kkXr~#MKx3Spg0JG!Zl@~V&a+c2gCqAN;VS4`2? z4q=X76JFbt(sRKmo#Fmiq&Vmnh^RdP!RwTOFRnhFwkJSrM_c)3g~dUZA3<)AgGT>| zVUiuRkhilO^zUFOb~Z4%A$NY7w)60^n#XiT@o&z{9CXqTj6UL^4{iaY+Og&`hT5Pr zV@qDpD|XY5u|Pn1%DrlbgEAB#yy2j6fMiD0u50>aqyyy;}sdcN1eH|XI_J+tU2AZ!Bw!S&f;X#B8wZ+`ft4gs5u*LxN{ z`&P*DmMeHk!f~OtoGj`ByQ`&u;KBBCV5eC#KfXD(9^&yVstIBpTgDqaeI%KElSP)+ zEYt^NgdvO!hD(OVr0MnjAN3k6&8wBp>hBfRJI^;LYXqW|^>EP9-<41wM9D_(qtoD| zxlgEpIJIieIjvn%wXW4!vGt-IvE@4{@n`5%_q#@j)K+2PZV!@!NKcb(9hZh(Rl?c3 zIaK)z2x3yyT!WGx%@rPG7>L(1j$HO&y^^H-Rk>6i1o?l>rBj#!Hh%T79heO`^Vjdj ztgddOVQ6(gJM{2i(PHxarRUbDJQr}2@C4$ZSU#Ew5L{`S*ZJGJzP9{F9ReB>qiKp4 zHZbb8aI-4g-dKiDh?$-&=h7XVT&h8OLcb=SpwGuU(D?JC=@Cd;@jucuGQ_{j*I_0Z zXLK9d^5H2RpukVn11BcFeCSzSfsp+x_@fa$TidREZQhF46ZIa%T`_@W7;HjXGN%vO ze8|N)oW&rS0S0_0)CW;b`qus%@GbG&!*D$CN;lU-n~~CQx`8z!QdpgsI<-J}SWUq3 z{L)AF@C|6aN@_G6)E5EC)#1AR{LN!$miN$v#(pYShX|_y_NNo!ymAQX)b%>yvq0+R z&W6z-#pDY0fyTkEn zM9RIbbk;50(DFp=U5~Im#+o)CN;uJ8H!irBMu44V zCQL@M=?G6SKKjnO{b@VC(M5$oI&~dTn%eCV7_FRVa;bOeKUm0>_>D4GDs-M7X z__D+L$DYpPe3lAHw0cm61kXy#cn71JOKG1EWR{hU6h`_VTu}y;Wu-L47pdHf{Q=p? zN?s-GtI= zfT}X;-3tAB)}$G&m`%M&*&cmB{q=|j{d0EbXS{R7xo_WDEgSOBxy z9$6@AnFTdN7mpjXg`YL@y$r}5ULiDc@81h{ecsjA54#+E@r`Ju&p01cxNy-g7zwwVnK;oN%aGb;nDhh<%CIW&N`*ruHPyTAqfqO9EK-AM1P+!N< zzCadkYr2TTHX6TXX2<{mzPwx3>!a{zxd4C&M2#e)|zALENGOuksF#jt1*od7NqQ9o2zga>~0?M0$cZ+Cgk zimXI@p+5|NvTExL8Bv75$@+@Tsy_d)SPD&f?v<*6zEAJb ztp~?$?I4ve>!pT;ml&@i;>$V<3~5`|kPvxLP|$7!Z7Cf^XI zpN@B;ln|!M-BQ~MEY?FH`%BfkS(~bV8$h4We5^k6F*0}NK|Qzk2_9CpMduaHj5M+j z^w@)o>2e5*VNWb3wLQCAy$%d+tXHeczaF-EKHrw&W`%Tk8Ir3#FuY7(y3(GF6NmSG zy}>j|?)ns`;83ie^eya)X1Bkw$!#(8Um~*ONmE#e|KPR^%%O@JfS$97!u-yK7w5aC zo!+Lg%~7-;o`>WWQKsluF%4d}%KJk*WAuTp9*+v$vg4ZsZ6IEHd)w+mAff$VE8%nn zzu1%F_<~@!P;7a@oZk2RYrr`_ruf^s?)%5C-gXs3K_HG3I#jGi&+jU$o_c2PujtWF z^PR=Gorx95bDffAb=c?SGEvEkU%mQw-`$O{8F z?6^lzd;6};%K4{rf}~+++_yMu=x3n0=ai1V{0@;p99*d0O{t$ugnK`k5Pnr~Rx$hX zQ^?ppwB;-oeVQv!!YL)9(cy{JaIf&2@&Ej2{-w{t;=!Jt!Mhp70O~--vmT3Aha7E$TtW~ztHJW7RFXDr(2xHj^!fo4?f%~ zdSa`$@y@^)u6g{tv79U&<;9Z@zFHt^Ku7yif2`5$;HOP`>qJ^;G9c%=~B zkhA~LrK-6nG{L!j4B`%q&D$Lqi(ws5P2j#?`HHny%3nMwT8r-POW{tCPOi7IQ?5!S5A$zjz~t>&l0F7$yh+i!a~ka=fZ?LN>Qu^TQwDhL6h<=F@Ci@Nhig^-kYa6bP%RBlUKz@n0v79=-i7Xz)nXX8_(eLy6(Ya@uVw zzP2_r9xqtg4{V;H8r~DaIh?1(^_zhDNB7BV1W&CC2Oa({b^Yro>TuH+Rh!|$Xr?(@ z^cc#CV8iIS2xbkxF;##s9uWV5Qrh-*8vi0ber}$oD-o=xf1 zVG*?#hO=P`WesPMv}-u?qtql80S5Au*w#SBS{pn81v-k7;Or zKZ*5p_XbhoWZA0$KRJ=LCZ8!RnD}2U&G0X(iDBIYso4`*5by2bCJ;)QQ&{f?5z10}e7 zH7YR;nwAMHklvieRPiixBk1|0<7Vo}Y!)%1u^CI^%yCVgnYCLpT;t8S56|Reie=r> zyn7vpdU}?dV6MwiG}DO#$dY2SqbSd;)ddBPl6fx29CLPIKAh7@&t$T&ie`r9j0%iq zIv>a@&MPT&nP)r8iks;!&MpLUzN5gA>vW;a;TJ_B%gZCNenK$P>K@V7iVU;EcTUkv z2da(T>zFCaLsBt+Afou=j>^(h+YC1zDlb7nMMI|}0<4+NN+@gwn_jzO20O-Tp@;(^ z1QYrud9cs~y|EQJ$$G|e9K|JO{&gEon%*9y8mbLc8a6or+R}rWG!D9WplKY)QQd&1 zhF0ah4UBBoj+iMtoh56oNQvta2xexoc6i}#LN<${fS#v1d|k2s#^ z1G9Dn&T;dK%?0!Fv*@&)b*7YuSnIX`)*m`u=KgeQAd6}*&hgPB{(y%2;f1fT*OELI z-cCF0=ftw|3gY;k3MxuRSP5Fnf?MD|1x3!(EgS1$S&xYV+8%AUr8Sp`_CVw=A!a8R z%-!%rv3j6rvmemfCkKSCb%y14z+$uMr+c7;s&v)?FLNHc4US?Q&f2%YSH*;LR;^`i zgBqnS+On2)2yOI`C$&CSQV0RJXak(@!*u4N-PP=_+Sx8v-ih{@kY1plCqv+p)!i`? zX!}y(bmotQ&A%)M=41lSE-c8&%QZV(E=O77%z20`^Jf-j7vAqIj&nInTxDc(AO_p) zEJ_)mnTsB`qnhl;{X2@{oGh62vA9R^ql^qd_7jj8){mCVWT8McRRR3z`&dvrjV!dK zeVXBwLVg4Zzd+Oj=$Tb)2A?!H4H;`=CRoqQ293UA6s|SaFP64!072dx8`n?})4kj{ zj|Erbz5L%-oa@Z@y8HUhk%`=^*7eqS1_kfo^8V;N!hW^D(P!O^SZ87Qsu|f;bIL?a@+*Lb8A^O|?n)#oLQqAafkqrx8GIe^RB>Scl(hI>9Crh9s+f_Kni1cd zkdi=v;d`w^+72hG7>Y>QU)b*QE1&6d;}=fs|!jE_l?g? z8{D7c##WK_-*J=;9ul9LLLRDzN;%YkHxg9QrmF1Xci^cyRCN6Tt`;eoP zxy!(lXI_F<1Dyv-^?UY>Pw5B6s==?2sTktfJ0-r)1MoBy6>G=_b*;e#N)@X^F$LRG zWDvcn6c|}k_CO`2;VS4kI+2?!#uzxqcXO3XpR6VOWh*EOT`pf4lm;jca-tp{@;D3<3k{t4RbsJ$O^&CxoM!_{gU>7UftSpm zRp=g28pCykj)Zd@hCO#ZDCwIW7V+{ngqd##$l0E)`*kT3()gOXxi zshHMOv^R3K*cK@VYhot4mdJ5FVvLbZj!AQl!~D*D(@YMCn|fKvz>Fl%&qrZ%W%v77}FcwT?3FO)Fh| z&%Vo*3KJFiq^@#9T_DH$oO=v%ns*N&rwN_~-WoKryFAXXJIM{3^^g~W#h}#Ab)~)O z;LSPAryL|`F&(57FaV_<+=c>bpa>Nx5RQYVa4t~j3MGFMs6FKOU;y>>4m?2m%poT| znV@8FH&9xUT7gnI1WHC?PM^eV9RyS%DKkDJBYp^18$9*oTyI(K+ehx91LSa7t^i5~ zy@6pAC`Ul4{0>kGjE$fav9m#`p?pxP_iK_YZv&p{g@Wc#MO!4OXIfCI;NMSf;I-o7 zZJ=cNGKGqu)KDBKEdUXqwxGf2xgBU`V(Os844T05zE}lvGQb3sJo>4FtiQMe{7)J2 zX>tz_fKo6GQ3@OfC4PIlT+s&00vVhKN{SrO2pKXGlzNnJkjfzrU44wd!w2c?iq0HpzI3rhYE8YZ_BpPlI0Gc6-0 zag$PUB`EbX6Ae?$8;y_^P5~taV?aq^YEu8C)TGR;hc#F<~Hr zic2L$_#Aol$<-SlNIlqdCq6{Lka$jtq%UNlD7d%;MHUKs{UwpHqW4 zK*XoS_e%U8Jh^-VD0yTAD3zOx^0a9U1to+3gkp+-d{BGPn=sH7)CWDJp{oi?kp%Ba zL$-dBtmomiJ#DtUsJNfEwYREikypL#(VdfDG_ia#!r5od_o%&Xd3f2!7a z_r}vx?;YtGe*Veyurs$@PE6S8lPzn2r&I_vmy;(dt1M7OGX zeu$Vu_)ezyDMqKNn<9mzuUG&0FhN62l zo#u@p$LYjJQ$zS>;+krDVXl!_R87a5iEh>PXf>(2UYm!@7k}7QPTX82NZ4vD7FE~r zTG6eBUK0bSqPA-YKTTXyLoZy=ibXYa+8{)7C~9z`Z;c>LCb&l8`YItpL3wd)O`Y(h zyjWCIr>TVrZjmR|ZcSuoV=pGz$+FW$^&}kj9 z>V!dPNPX6pM`V(=N@B4~kkG!8xVDZ?J0HS82+N7S^@EMU1gYf(qsn5Dhfbr%6s|`j zsLe(S5u7WC1Du0|)s@BMYC7#5%2w-W8)H^9mAK8Vf{npY^A*Hm=OC>uCIk(cf#{2_ zMT5h9m+jTe1{WeZ`=pu@Dp-p}UOH`a%)cl}BlTf6xHy&LAB$_e^?VEQKE5Z3Za#Y9 zu8p|XN2jfZ)fMLuYPnXBaL!hA_to+4Vv?_3n~V8}bxrZ`Yj6!Ej>f2?=;o*A=Zi^x zdf~LaxYkd{j}!0v>9xOMxgyue!}0-KYjDz}&_+9QT(qphD@Z#RTuX4}XaI!|j-tE2 zPLqsPuD2ADC&e}Xdfr65?~ee&ia`z$D2BB&!Qo_!CN`s=XTa&e8Ic;kfp|Yaug%2L zRT2%__24L;HRMjdwzwuxug$2+aS4(}^hSFI9EAjo_6^e7V4)+8oLKA~#J3mM1nD(f zAZsA54+_!PR3i&1H3F$n(X&Pfzg=7ttmh5H`@wo`yXtaWqP1%1Bse9qU4yhI!I9>2 zl4b|-em%XmPYu~RwCx$BT?US(3QC}-9~4e*?{)6{7f-Pr|0*JYjk>TV|R{g11&sxK*PW_r+KWs zPASMaaez;dHr#{bnn!9jemm=vz()5SI6dhJhj)Ul_r zG!We)q;)PruW96sa1v7^LNqgxik4DWOH%GoC(8zvq<%xHxm3=?m*XO(RJ)SYf|As& zl2jc(wOm$7>erH#L0vUB8mT6-EhVW7C8;V1WjVJOQjMf?t4mT3OHw`oYHpUAGW`Q7 z{L3|f_pK1|(pqTx1WQrpFQk7ZXiX4zv;cfrbQz>y7R6#}`U%puUzDxdU29U0<8a{k zO2Gz5lfIDl=&dzDG{%1+B(2$y{$0UYNc;S)Y-k9_CH_si1k&zbO5?O9n9N@Z(O7p# z|K8ZI|5(-`?4Mkd1!?S;jX4=>jzEfSIXFb9q!ZW1>rA_12Gf)lFa*Zp=33GaXl6s& zNlfh!A_VHiwb44wQ%vIwnys3I2##wn&D#Q`+E5F$_3$;sH7)c)Mgy^^g-*EEKy+`Z z)B0c{D>K_Yh)))iTI#i1AZvwMm`~Uaghq%maPm&o4jip**p#q{PH!Y8x6)~kKqxOW zv=L|qA5d^0oz(n#4(b{6| zAkA1P+XZQ&!3B$t5<(4-XhNH%_ApZL3i+vJ5MNPD>Y(RaifcOPwM($zkq0%>%5w!A zwGTyDe{EWn>;y4E+V%=(DD6TbIJu+Hs67piv`8Di)(ra^4SwuzXN8xDnRJf+GjW`~GZ&GnDd9gCh-c$qMb{dh%8l z4UXzzi$FL}Y$q0V(`olWNDjpQhH!ocjz$tELAWBYy{t@bs6RNlS>NbjV=&}T_#M?A zgQK3KujrP02XSq0on|_wdk5*fDs&Xx`{)Fl&SG*Oon`=5$R>2Q)fON{8Zq};25DY` z!$}u&x@H$B7uN@dX!{^V5hIn*E-B$CelCHduA)2PK|<09i_u`N2H>qbLw)WFpRRrnit^OiR=BboM7$iB9+*Mlsq>##|9hpz}h0& zyap-RBFt*dP2!}APFSHMDWy$Bigsf81ho-dY5Du$bdo8x5+qYcZ=&dTE7>EW(!6q$3+EL&T6eu}B7o@$bl$6hkjj_`xPDHfK z21g-*XlWfJobMyLXXrGJeGwUw<2oWGE26<(2QHFgTk`^`aA^Ve$IjOT3UDz6*A4}T zAi$U$L<&c(nju=_WHrvHL?dt*HC#N+O%~T?>9mg_AO+Ht(=_NuJ3RIl%{Zi@rN&Mn z)rOp>sgXh{I9V8!BD&+kY!?Iw15_sa98<}El%16-77fyAFGJvuY)mt!Ai=!9m^@gg zZP8!uvb=dr1BVq3jvL?}BwXq*x@YUO&S`QHOhKH>27;44hDBO)0bEPTIj-p?D`<9* zken_i55Z0YA?cF>N#i+ydP`mqk&@+@ZJIh6QsChNF&`-$nXz8%r<7y~9a_0em@akS zUrh;pGsU&TbebIyNI|Ey$dY3~S}rx6!3EQ9pcRqoC>epqv=6{_kT|kGVjwx`QRfiN zJfvWpcZgsyNL)KYr;Qn;uFJ(ZsE-CmA%&@mE4J+@lBYMu)sH+eXfn3}*A6A%UR=HS zWMjVvCmAD5%odY#bi%D{acz!H8#Y9abFDPDhk?Vv)ICId2&qP>gH5P+u<=mU0mapW zG=srmmS%-$HzO5}LWpK8{Keqpc)`_yrsXj51x>8wNZ}xcNcQy$(p~{aiebEakk)0m ztQmW#XOPf!xVUzdPSB4Ki$La$5Zy=Xw5LbNJ(Kntjp;~orDy99O)I4Qq~Xp(id0Im zCL9{tOOuXNf62(JNVOGH z!$Y*bljLcMn-EokEHc57$(7JTWx!jEcdA7Vm~KZ78LWZS9+zC+IaQD%2tZYU%yLm^ zHBkDLrRBia1c=rK=tGov4`T2sOAWw#1C;Lvkp8*=eL!=hh6BJ*K}4yNX!H1^LHw@|3wB>SSS^j zDLH;QKpt5MP(!N#`Vb|Dvv^r!1`i{;<+McUa1X;2$Wt^cX<& zq>^8jQiFv6(Nju3QEK?S!k486kX=&bIckC^DY&d;5Tzbp1*pOefIej@8FX8c%9QHg zQFx-n-&N>6MNX9T7RkIAJIhM)!2?D92$Y8Ig`6)@((_W`iBh|76uvAa`FnuMe*nmC ziBTxED^TW_G)GdXQ8I{9lAbJ~SgNGRiBbbr3SXAmKwd|Y|L>_11ONMqX!ZZ9VWqzE z%o3+ldhq|7=KMDel5Ki?xq>DthL)u?wtW;iQ5vdbg(phMehOcfLX^X$;F~JY(^ym{ zoxZ-J)If%kpQ+>%CHX)_K1h+5rL-)Nhv`Fv^TCrSo=4@#niiu}K!RCJM2o+#;AqVP+2EUJ`ViZ5QYn(LfH*{g^HT}@vK zrQ{k#zE+VFrE|qjP^z~Z)C=?xC@rSXK=F@zPG1V8s6H++Lqz9I7IFlS5kDE{mz0qO`!t6@!0D(DJIpR$zX zwIC-2^vo=IBupvyPn62*kWWP-@I`zBg*F70;-3=u$2Gwhc_JE=3bs)Awv>qvQF2{J zB|lD~oj|DrT@>04)C_zoD1H8kQu(xUXrB~jDHR8S5dB`AWA*W1trmFMgG5` z)Zkd99#P`QmqY)lp?nC4PEhDXr65GoRZ7HeNxF2R9qQjJB?_ef@ydzZ^zW6De4zRFO6lJ#rGKxK{=HJ7 zD=NCs`TLa;?%~pc`L`>jE!)?BFbp@XT_Jn-fq=uYI|4V}HM;O?`ncV0=cmuPd*!>! z*Nzo%O?NK%VEtm$wl=vlqZW)ioHYID6*uGeH+Eg9^x(7QpUqs)Z+ZEXu{M0fcfUTU zy87LbKdu%{_*~;k!$w9cDr{f8dv|!N>0uu4G_#hQl&^8*(z62lVLRq8f1WwP%JY`F zO_j|_H(vI-D13YrwIytqPr2|~TMJIMyE42^!;1A5H9E1|b?F}llUF>kGG2Aj$7B6_ z%cQwa?k|sQxA5Ms>MIsppEk>6_>MY}b>DpQddscx$nyGJOI*L#Ry_7w6dxiU+7~PK z{moQN-5Z7Hu($7x6>ayKicZMWi%Gx5iU+}M{V9r%5baPhd%vkTVqX* zDvsSB#WxlU!Ce3sa3G3rD&`%C74v^L6>o!UCi?y!D+d2zD$f2rijNYngS!W=@gGrm z5O>BOvEtl=receOQG6?L-oaQg@{p`<)u2Hg5XQG8qREx6T(O~tNM;8G~b{ z2~+Nl>Kfznc3D)RUGM2{4WACIW_loSmA|=lNauwYxJ>`>$Sybk%K7E(){%of`~0+g z;9t}B9cCLErS_k5^@9QbG1LlSw1CuHNr8f6NKrmdz(E?=fgXkM@UejC%dvV@&5&+AD2L z{ycl`_^TBA<~J(ce7f|xqj{Hl+tWM^BZAEOUBgPibDzvUfsPg3E@AjDVfZga@#Dpd z;PNkH_%BEC6T}IZG5l9B{NN^u^{&M7lf@bMo+94A5-m=-YAd$58ii-^=Uv6%U&G*o zn;|y4hR2((*^2A0Me(Be_F61ION{v|mY*%I!S@`IzaGmoF%I8z#m)GhCu(oR;z{#( ze19iy$M*u!;$|%Wy_kgWh2ma(FB0u;#qx{A0r*}b9>Vuh(dBk5zf2s0@8x14zE_BD zcVhXKVjjL%i5Ky`TJ*ge+hR@G0q8x*&;7k)OWPiUGwZ#rFnDF(mN)z?n$Bu+Z29V6 zt4x2qdCIyCUD^!x_-Xrs7JluUoiE?rwP)u8AAFp&z8>RW7I3bj=RMmT?dG(PJozKVx$k_Cs0YW@K3nNFJT;x2e!`Okuqxv;A(j;aq(?O8X;AU^b$z$} z9)5Xn@y4n5x_c>l>0Z}*<+h`w-XQA{UtRy{HtXd%|7>3QeFOX7xoGF%ZCH7HZxhS3 z$mI@+6}k-cf8M5B)62)L6SD8OtkZmPm+MWh85$XFxnp?#6a5T{e*$@8Ls^4QeD3#i z%=aH|dcQM#Irq%6N*&hue$MFjfcrhG;X3P_xkh(qo;%q1yNz!?oBwgPs&VAwVVBMy zaBHT0zWBEV|8(a_H>St)yI(k%W`h|X%e#rf9vCsAoeFo*+tMy}J(o3rbDc+=N z1vM}jC}^YyHXna>i%U`p~COx4T8IT5)6A)OQtn_8(GZ z=l8=S*Hj70Uec)NVw#6|5E+PA)89o#YaW9 zIWKNhYku!-rQglxq`1s&7k=`mmW5|xrktGJ%k=uf-rde^_G!{$U@g!6#_?;fY$#kG zV|V&pgUw?%Kl3f9_S&P?@&>~s>jbISTgqCuIdSno)9ydqKD;5e)6@4R+V|^=e;>Pd zdhfC0R}Za}bt^Wqa9pgoG9#x=z@&Bg4ozS7$WK|YuFYh>^24$h?sU99XoaGf#_OlD zivPS2Kh({t((RBw7O~s6?5lst&3(jx)9&-*O}x3SsTM~Lj5)X{l-s+(pw+>FSt0;auTDkY?W*^UixKcBwt?O#AjB4t<8iS%jTDzxQ?O!dch2X6ahX&`QETh^S9lx{@A*CiA2z9))r(V?cT|05Iv~*A zwSo7fC2p?VqMB=_%(GsmD9(k_g0hOQm|j{J%spRi)HVLroL`+9HOOB#uDC*z9&Hb$ zuMDeVz9cU^r{LVpqKyq-?e3biD5_0$izfq4&pEK6`rAoIf}9eHNN*0^rbdFE;7~sX z1#wXcBduQctyMMV{E^r}6}{h&uCq7J+wjh;N?p$-k1S|l7h3IXhjoqi&wSqcbo}o5 zzlP}h=(zir_cw{$zQnrjImKXF6zGW%^@E>fo(A+9V?OL*(ba0TvciL`w)gU^RxqO3 zot#L&x>lox{vIH-ul_7B#Mb+V@RmM#uXfbD1NtS_XMXU za3h{X@q5KX;If}$_kJ41?-PeS#c8P+r=_P+Ee@!i1U0zV*@Ca&+i-pFeMQsGF1X}< zvdfdBgI0-a1Ir~X%++|-KmTlG^WW!Yl|OWS?$Rk;1D17LQO~=1ldvk+6I!vj`R@wF zW5u>&KyeiRhnQE4ZTA^YmEaDEzR$4jKF6u@SrmUnybkUjxW><;_+#RX=Qwq}Fcn{d zJ0V8Az{dO1R9x{QiZ2w4!My?3{$&(@T3qrHr><9~qUKc;e^!ing;UpSoOi*U7x~vX zb-gha6JJO17sbuswt=&H6UAQ=_eI~2=-(&w z?_(7IK)ep_9=OJzqWDMRj8Ev_XY>!;6EWg*?CeM${reon7mLN<-teZg+w)QU^Vv)I zShkwyO__!V|B}V<5OfqE*h+%ej29p`Cl+G^fvq_NTS?%|cv}b#k|5C*0vEQK1lbl4SlK~P zoyFThP`eTY2S`wpS=d8xfdm8XA*jXnk|4h_1T`HXaAN};APBaE;4BF|n2RF>_ee0- z5du$ENP@Xm5Ck|u;LY-!Ac(Yv;5G?-nQs*c-jHB+6$t9G>m*oh13_bF2m;s)X9zml zLhzCVK`f#w1jcp{tf&e>JyuMDZ6s*#0zn8{;sQZmdk8euAP8eI)gZ8SfM6>L^o*|# z!9fxvR)-*hZ6-msBLr48AZW)681Lr zV-R_Q^I_c@EbrLr^v>fImTtIZ*Z1nD6W4>Y%Y9s0?U?N{&!s8$MloRn4_cb73}5tm zI=^dBjO&wOfj8y^U2J20D1%)I;BDE82sAW>6-S_2(-49g zET$m@Z%D9}1R~=bL9jX)g2YA;%x0TO(6Jr_R*hjSD{HJ@aD_fo=9f>cZSU9fsYjc- z>3g4^o|8&i0&6pjcYSGH?DBziIX=@h7a3Zyl~RT4iTHY z*~fY?_5k$q^O!{wG_z}@cgC1XD-GCwwlfr_jEzJ?OITqf8p;miO{2RcPH*SzV`aMQ#kur8T@4Qe z3U}=Mtv0SL-oJcfRF7W|b{w^KzQLb28txf8TNW^By%XH9qdqzX?5q zr(UZsV|mS>wzdxT-)@F>Rxsb@5L_U^?B)=xV%JHKuZN&9)(w6Qn?VyhIGi`lIs55~ zQF_$JU%I}!xX<;MKhieLeS4tac+08L$sUc)H{Cnt%P^Yj+q-bMAOAPfB26xU0z?`9c9(KXy|f0NUz6p?go7Mg2HJ2 zlA)OHWJx^R)Rs3EiX97f_2oYa8fx=*wzC88jT!K$13yVqhzBw%u;LE5okzv@#PY^g zuj*s<@xRBUKeyqV zOClJsOwzF+g*R4;sWNNzFLd-wN%Eq$H_9mqT?-ffRr~>f@92^jd#OB0ow=<>)b}-b%yjFSl8!OAW^@`9E{5VCnL6KR3zoN)CDl%*ER{_e~ z1R2>yk7nNhC~u2W&KBvLitHyvW(WQbU_*bYWve25LtVn>vB5|9A@%7w zs)67J0rV7EOQ03d8i)b(s22`I01beKKqH_r&ng||U2|F?-5Q7i+5l~Vc0hZe0}u;z z1mXa?@6j3P0(1qs0o{Qf00mJ3&=a70X7uP>ZNLq12Pk$ZR_M=7`T%tSe|jn{0Es|= zf`%USdjY%zUIDLxH^5uqJ@5he43IzRA;S9rjUf#WxtUx@fwm3!1=s=5Z7Uk)9=@dn9x_V=F+>N4Fq*0f|6wAPGnY`T-P(SQDh+>kQfjpvhkqpa;tW zfgk{j9sOr4>1nkpp!A$xcc2Fl4U<}>g^agwYU%-#KoZ!7JtdO(@ zYyexpj>VtgqjD-CSsAbdLZEp!uow6Zpc(%Ma1b~I%m#i176CNzX`*)nx&u7`7a$DK z0eT=DhyWS@4FUXzFzI>8G3Y)m2%l*Mp$9GLPp@VJLx6N(0Pqq8UIPhrSwIGm0;B@(0QzB|E?g4;>_&PIuou_|?57p{01}6RBfv4> zEI_w|&I1>KKY`1@72qmx3%Cv30qz3#fcpU5n|c5|1n3zCV|1cCU;>x{=71Go4g7-q zXy6Fi=MsQ;pe@h@5TL*S-~f8ubObOGps7HQqppE`DM0@(XCP=AK#!p(0kr`Wpeo9_ z05t$J@a6!W5DI})z;8rRKj#d-&I0Fvra&an42S}vfm|q9h6WCx!XLmv-~!|qfj@yZ zz$Jk8!#V&h`aQwd0Qw?LPx#Uf(-@##g`O04N8SMdH}INrc}NTfo&!{va1`lzz!ZRX zi5tL8;1)nD|83wdz->kg_|)DlE&7cqP1{&KQ1TMR&17H~_(`BN(P{1`17^TZ$SI(A z0F!~%K&t|~)4UmPK-(&-bA}Jj2}g>ej+O`tYnmonfWn;I)EtNaDD*=CI*^bBMBy!I zgQhgqqbU%sP|~I9p}JIu$`iE$!U2@YDQS?#r2+7*hNwbAKsAsWr4K2kai#I403$y& z0>S|@fQ(if$CfIMJ4Qju6KDq@Ur+`qrh=62089cXYAFavG5L=gQ}fA~via0-j8dk& zLfeCqaqR%oL2V2LNQabXj07p^%&wp1E9a2ml$QW>0lEX-fUZCfARZV33#)FaXE|vH-*>7Az#P0r-U*29V-0z-V9;K=Kj5 za9|{m2awWSfbysu)uA+D0x%933*-aifr$XgC{4~;1Iz%Xa^)~9*o-r{MP`1UH=!>B z)9J{eu1o_~vzq7m&e~s)oC0iT6VCJc9GU=EfwzDbflu~b0%#^1gXaNSb}oW{2+(Rk z%g<*JC*Tu6i}6d~1@IVn02Bdtfjhu$;5u*%I0GC54g$YXH{+1_1Na@-2kZs*0J{Mc zkY*?ubpY58Q~?eFM}fnD9dHCV2^5fvt0tweGcfV|`eP-E4B z+CVM96`L7Ey-l~Q@NL6WJ4kuIuBanT4= zX){N1EsZ}ZC)X+&l3Z0vmWBW{cFh2CEg4BliIP&a9tGOB(iG%nb=uM?kCaf9>Xl4N ze_2R&DU~AwsfQHV6sY70>P>eT&<*IS7($&7MKSh08plqWZ!S^Vm zOjSTeP;iwkOKzoU8ISUmPt%cxM2&hfwlfd}kTFz-I?)Blp&op#OjXhqc{I<-#fbp9 zz8BCF2n1;A1_RZBZU6;u0uT@M0J;ODoAi+mN)zRkG>plSTthve9+x%bYo5xG+eqFY z=m*G$n9rYSCHoafg)9X~14aVmVp=6f0Kk0DzQ| zyDWfAU@)Kt&`_kQS3>}b7xGLXKrs{yWCJt{XlN-;C`QV9N$scujlb#|Qb^-MN>w+~ zSd+qG0F5IVLA{y?!~)d2@c``s6M%6*4;W4XNt=_}sj-l^1!90)fI2q@G-ouvMge&M zrB%zQ*Q7XKp`@@2P&VHddFqgmLJ|BbU?wmXm;y|O98Q+bHZ#Ca2c`ie19D0-S3*b% zRseJeqJz_Xq~`(*7!7_DKnE+TJO{E7ptC_|0n{^+Q<Pa3D=q-D6j%Z*1{MN~06IHS!3m&r>QarYhcw{_rLgTYrdQ-+e-y#!68NAtG#@`D_-n_0zqB@)c0o0><(`iKr0OSZ_Ci})v9J}I*tN$ zz3SHUsw+)va$CsHV?A#^O}9V}j1PqF9M<&?UspK5vzd2zckOY-i1IA{E;RNQWSj1- zo@jRNoMF1acYuU6(Ji&|pmSSoDnDx0)<9_V^YVj2t{$859KR*I-{l?giz(7}ysQmB zm~DhonX@$XCofB)x{kjYNH=10yNDffi@JBcAn=NL+;Ic;=`QbwUz&aIp{_lPA|fkf zBk%F{N?K40R$(zuc}v3puK=8Q7=It7!yI_wfoKyEyfQGA3(Jo~&=MpfCF8%MBO>Ves*K8Mf^{??y7u3Y#r%6#{#hyOn4Vc$s-ab%$yUduyJsh8DP4XJ;Qy)@^)9wFbajVv9Z zZ}^lAc*t96)nQcJH<>Lf{?dJd5&{ALti~Vl^li_bXZmN@S?mL6%Ln$?&tcVR**OCQUS%Rg8>&gqP zwE<>}T%J9D&O7s#tVMId4mM))+u>o2VIU5JY~@XJ-}T9JQm555l)6Uy5I31^qyp+& zoTl$wp0VrIAG@RinBi*QEC1cEhnd++-sP)?)Ynk$*_7L$O1s9VNsqjBPG&hT|BHqc zC);9=z59~y!`El6;CfyeqNesZqhg3Myq@{>{M=y^)MKfj^;#ODy0E5@iYvYUqH=)x zhAQu{$p$svUfM17(U1D5zItlGkCP_*nrLXV#V$@cEm-Pn=9rFx)36dUjcCC9S$Uf?RR-EUC6I@?)6uyasf zPt`Y|`%3RRk7lD$Y-H4EJ!bkAJN$iT*5EDg=b*l~DrDl)%YH6z_oASmbb#V)*tECs zsfmkxig?w3&M3?BE32b`kG!+bW1E`^)&|hIj9r0FzBjXY2NPtuGvqH(w;DWpr}fZs zE!xj3Kwa#U{@9)#RAW8fVgGuCY?>C0_x0>~tk&}k$xbPDx-;<|_6GI6R|x}ZgoRfy z3q}E7`9#*0T}Nxe%^K_jX;R;V_3%x?mF|^|{ZSCpi+VhgdB2BQ-Pn@Gf>l*&J=s-0 zI@!NTeY7U3+Qd@3;FgmgpiO=M)q~_=OR@UMJT=(tnpJaI`pj2g>KJW&-qSuF0Oi(g4)TV5@l#F9eA5k-% zeQqY$u$CWjs;bQTLFAymn(M7aV!u!R$L2|%!rJ7|d9Xz#b-rEBhm+?4GyKH&W%>B} z+X>u~H>rcmER2^ktMHk(^Y8$R13|9MoH|3Utp8YKS+W<>ni# z>)YL;es*b&>FlnULh5V94jQ#)P41=sR+{fWdj=+4~9|G7dUhs2JUqZIS(z;UDY4b)I@cuLeI6Ku~zJg#Kj8u3a4w@MMnE8(%hc7Pt zR9Z%TwV7eosa`3^p9`fqBUlb9$8#U`Sr@ymvd_Lt^VVSG8+UiD3FGxHqdMh<#eyR?k@F1L19 zcP|X5 zznN)ij{3yV_IA0$Hkz+~W6nR(Nxxp@GM|QmeZd8d5X=kx^sK6ZV5?je*gDFu2;*|C zHE~WYokkxLc;(ub8>weKj09Up^<`vE)vj0Hqph|bb4xk%&eSun2GF%Y&o-cBp!y!P zh2wTKT>Y`)N~I*OMbtN?d7Bw0IM3KpgdA*zSc$ludiDWzg+qF_E&}wdp7|J~)th>G zNwqsTLwjZHrXNs)cDcH^g|BCSp(GxK9$}1WW5J$S2-eQHJa!3}uO0nTwmd(4qAy(u zD~9@pv!hU|?2JOA@HtwT+ZpBQn0-C}Qr6~{hmBF5F1j%=S;%ofPFTzIAx>j|3zf}9 ztolz2XDzAjdX%8cmioR??d8f^n; zXU-bIz75K&ug+6n1cdU~uV`i3hjNb4q|1+4`nG!t9d5Ct(WTwDuhN<`r}9D{$32bY zO*Yl7O2XP%39SU4bfPu9QI9PyFGS=zTEs&G@!E}LK}QQyLMG2iu*u5-Q?HGFV#g>fd#%v7i=OpRurD6CjC>tqFE zUO3c(tuqz;@CfoXQ=tW4m3dY~H`Ev9b>bIXYVvv!ZX`&@6X;yhntiG$*s`G&1$#&J z{djvS-d)plORr+7w{#@JoAlVGis+8|hP~|VFM~Aon^D-2&#^2iiX7EN^Gdh0V$z zaQZTp3y@E94{(&B+bu1UR_0KFS9nVIN(wu!J=yHV zB3veqM5xl_QeUQKW&3>{YvGs`FJDdeH;ZF z4Q66~9DC>v`Rq7WH5dbe$XFi7c2Uudacom<&>eB?nJva!eW9T3*n{(SbzF0^)H&DV zShyWZ7R9k1E-*`d1>r>-=2a=T_bsI)niD#)sZ`Ru6AOhK9o3f?21XC{@LKf53neL# zG3#n~Vo%8+RMK@~BVn`9suMG}htJhlCa!*#J-*{S7dq*p;*yesb>es@whnb2Z!2{* zm9n-i{;r7{9asQfcVd5`gkad2J@y6tDq5Jii{LLLbY^?%$vsj=fyQx@1IjCrpj=9VNzFi9K{^f7hs-s+;h3@Py3fk-H~SgSB)tZSxYA@ zp=!9Oji_2xUa8B9P}os@r{<3AqL>R!KI#>ZA;K|Bs#bv!20i3;imUfx(9))phLw6- z^?+Fq)*mGU2ce`Ekz5qNK@*m>@@%R1bC6?!oC-HWilW3BrbioHrW}xGPC8eQm2a=(ckW>Jml86Nc+b@|4PyS z*ovB|hJ!kvO5}H9nYlkqRTm6(0eRS!`Ba0uyI?u6g{ziUoV$JClpW!v1ES2gZ)#GP zHMND;v2(D#lr$udrIfHQoJ)MEs!_v64Pc>0NnuHe+Kk$PbG_&I{5LTx z`1WDlYQZoyPF2G&^r}uZvdVN>8gJD$Wk^eGmu(t%g2IDUH< zRV$Sll#-s-mHgrczse?BX|TDL*?f+~vE!6mMn{=k)l7A)X-B8sOEp`a7)tl$HBAi& zb*n40vz+VAs(RquslEf()cQ@Qr<4Cu_iFU|A8&-&k$KWFKs7{d_-nap;J5M>x#KrZ zV=y2-jqSnVAn+SIB?3czkFJMJy4a=dkjF~MpswshTj8HW?m!w_tP`x*_&2;gMdN?6 zU5zPqxBrLoJ?N;R4u*1^`FewrPbQ^PSY08%8YV~e?Yl6LzJYn@rid{uLzlEy#t1H2IhYN{ zwamBX84if*)y}5D>@ai*y9TpQs1c~XrTM<$@dq}Qho4dg3N_UCI6v&wsr{AEJ&ll4 z564inCSNJ}3B?^)F0Pmy)i*#-@+h=l9?;59(SZZ2a-}4cxgJtZxx(wgOu)OsYM5tp zTvcr9|M9xU@hD2#qNH!RANSttKJE&d@uBN&AI@|ryILPpGom9~h$|pY)qjxvOqTP^-(qSHY}w zR@!i+!?wE6Bn)M>>kHA!@zIt|tuNRIsxR5@uyX74dWN%qR)z{QSAAc)N1N@7TDuNy zrsTj>#k+yuSj6!Ix%vj}@}XgQ8~N#Lq`LU+LtWmby<3Su)-6QvcU0fKojAF2$jl>K z@OXeUmI#?U!`TMBwM_nrru62E96`2@>Px*tU03^>H<-ReF%v_D3+-N^h-{+~Y;Gv- zHCf?F9C!S`Q^A^D2^Cy~F(c-L!Ld_DF#9kRQ`ZXBt;$x$=7$L_mEVWv=mf`al`Gi> zzMR#5ffsV+Yl5HNR{3*!?!s*_Q2L=JfV+tt{92NGWQ%iieR8wWr8(L>wy+TItr% zpri$>)l_IKgpFbSn+o-n9O*YBL9HbHFlkv|`ekhy9&)F9p=l$<#urz)ACudnU%kHl zbCleu!`D%ux(5#>O23`e|DS(5Q(xfzyJO`~-`_W*TZ>XJ{kX~F*oH`iiuxZ<)8?Kq z8<`)?Py)Xx&@)Xxk7G9?h3MAmOX2Iz>^XGr_lt4pOUE-=^b?c%+V}?{@$rZJa8s9- zJNj{qQ(r7!V0+;5$&;Vx&IWDlzQOoUt!!j7OiSq(NjrzHn>Xhz^W`htvcIa?2}-zp z(;uEb%J1MZsj0q!UYi#Bdr{2%9k`)~feVCfs~rT6KKC31=5h$X)NN}nc(HrU1)o}m zxY-qkMM7PqvP7(Mt5c+VCkKIxtvQ)t8;T!P{x+(J$Y6Uj9KWAxel~m!pK) z+N>eziJ3h!lF|!)h!&a|^8N)c+X(ZG_<(}xorJh@tm-5|!`k)~EP4Mq-2`8mKi^Hz zv%}qmAa=j2;K1r7;LE3*u$1k&C>XLUZ3IWQAPZkM-Gwbb_Yhi^E6DFDY_7=rv-%mr zdyplUgv#tnreIRgK2vCb|G>FWaFHwT$`Tw9uvvYEvb;fpO~K17VY~_NTQDLIrvdhS zo=}x79*?7K>(PP{J3m%1XP=QW@I(Z#;(0B=rS#)(FFGSl5+8aKY(40{+utFk5_Fs8O)~oFE!7;gaB4ka|hD yDX=!l@Ib~@VFz>RgfZ=L3AQb`ESMMcyCwt+#wC%u=DN_dyfhEwpeT&v=l&n@6I84K diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..160515cd --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,88 @@ +import pluginJs from '@eslint/js'; +import tseslint from '@typescript-eslint/eslint-plugin'; +import parser from '@typescript-eslint/parser'; +import pluginImport from 'eslint-plugin-import'; +import pluginJsxA11y from 'eslint-plugin-jsx-a11y'; +import pluginReact from 'eslint-plugin-react'; +import pluginReactHooks from 'eslint-plugin-react-hooks'; +import pluginSimpleImportSort from 'eslint-plugin-simple-import-sort'; +import globals from 'globals'; + +const commonRules = { + ...pluginJs.configs.recommended.rules, + ...tseslint.configs.recommended.rules, + ...pluginReact.configs.recommended.rules, + ...pluginImport.configs.rules, + ...pluginReactHooks.configs.recommended.rules, + ...pluginJsxA11y.configs.recommended.rules, + 'react/jsx-uses-react': 'off', + 'react/react-in-jsx-scope': 'off', + 'import/no-unresolved': 'off', + 'react/prop-types': 'off', + 'import/no-named-as-default': 'off', + 'import/no-named-as-default-member': 'off', + 'jsx-a11y/label-has-associated-control': 'off', + eqeqeq: 'error', + 'no-undef': 'off', + 'no-unused-vars': 'off', + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', +}; + +export default [ + { + files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'], + languageOptions: { + globals: globals.browser, + parser: parser, + parserOptions: { + ecmaVersion: 2021, + sourceType: 'module', + }, + }, + settings: { + react: { + version: 'detect', + }, + }, + plugins: { + '@typescript-eslint': tseslint, + react: pluginReact, + 'react-hooks': pluginReactHooks, + import: pluginImport, + 'jsx-a11y': pluginJsxA11y, + 'simple-import-sort': pluginSimpleImportSort, + }, + rules: { + ...commonRules, + }, + }, + //! ts override (지우면 ts와 충돌) + { + files: ['**/*.{ts,tsx}'], + languageOptions: { + parser: parser, + parserOptions: { + ecmaVersion: 2021, + sourceType: 'module', + project: ['./tsconfig.json'], + }, + }, + rules: { + ...commonRules, + //TODO: 추후 제거 예정 (변경 시 현재 동작에 영향이 가는 것을 고려해야함) + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + disallowTypeAnnotations: false, + }, + ], + 'padding-line-between-statements': [ + 'error', + { blankLine: 'always', prev: '*', next: 'return' }, + ], + }, + }, +]; diff --git a/package.json b/package.json index 503e97eb..f0b40542 100644 --- a/package.json +++ b/package.json @@ -26,20 +26,26 @@ "compile": "tsc" }, "devDependencies": { - "@types/node": "^18.19.47", - "@types/react": "^18.3.4", + "@eslint/js": "^9.9.1", + "@types/node": "^22.5.1", + "@types/react": "^18.3.5", "@types/react-dom": "^18.3.0", "@types/react-star-ratings": "^2.3.3", + "@typescript-eslint/eslint-plugin": "^8.3.0", + "@typescript-eslint/parser": "^8.3.0", "@vitejs/plugin-react": "^4.3.1", - "eslint": "^8.57.0", - "eslint-config-prettier": "^8.10.0", + "eslint": "^9.9.1", + "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.9.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.35.0", "eslint-plugin-react-hooks": "^4.6.2", - "prettier": "^2.8.8", + "eslint-plugin-simple-import-sort": "^12.1.1", + "globals": "^15.9.0", + "prettier": "^3.3.3", "typescript": "^4.9.5", + "typescript-eslint": "^8.3.0", "vite": "^5.4.2" } } diff --git a/src/App.tsx b/src/App.tsx index d1696951..1b903c63 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ -import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; import { Footer, Nav, ScrollButton } from 'components'; +import RouteChangeTracker from 'components/RouteChangeTracker'; import { BadGateway, BanReason, @@ -20,7 +20,7 @@ import { Search, SignUp, } from 'pages'; -import RouteChangeTracker from 'components/RouteChangeTracker'; +import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; const App = () => { return ( diff --git a/src/api/ApiController.ts b/src/api/ApiController.ts index 08de3091..56082299 100644 --- a/src/api/ApiController.ts +++ b/src/api/ApiController.ts @@ -3,6 +3,7 @@ import axios from 'axios'; import jwtDecode, { type JwtPayload } from 'jwt-decode'; import { useRecoilState } from 'recoil'; import { isLoginStorage } from 'utils/loginStorage'; + import { logout, refresh } from './etc'; const PROXY_URL = window.location.hostname === 'localhost' ? '/api' : '/proxy'; @@ -19,6 +20,7 @@ const JwtInterceptors = () => { if (!token) return false; const tokenInfo = jwtDecode(token); if (tokenInfo.exp && tokenInfo.exp <= Date.now() / 1000) return false; + return true; }; //토큰 리프레시 @@ -29,6 +31,7 @@ const JwtInterceptors = () => { throw new Error(`Response status is ${res?.status}`); } else { setToken(res.data.AccessToken); + return res; } } catch (error) { @@ -57,8 +60,9 @@ const JwtInterceptors = () => { }, function (error) { alert('해당 요청이 정상적으로 이루어지지 않았어요.\n 다시 시도해주세요.'); + return Promise.reject(error); - } + }, ); instance.interceptors.response.use( @@ -69,9 +73,11 @@ const JwtInterceptors = () => { if (error.response.status === 502) { location.href = '/502'; } + return Promise.reject(error); - } + }, ); + return { instance }; }; diff --git a/src/api/Auth.ts b/src/api/Auth.ts index a71da833..704c8110 100644 --- a/src/api/Auth.ts +++ b/src/api/Auth.ts @@ -1,7 +1,7 @@ import { useNavigate } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; -import { AxiosResponseSuccess } from 'types/common'; -import { +import type { AxiosResponseSuccess } from 'types/common'; +import type { FindPassword, ResetPassword, ResponseUserCheckID, @@ -12,6 +12,7 @@ import { UserLoginResponse, } from 'types/user'; import { removeStorage, setStorage } from 'utils/loginStorage'; + import { tokenState } from '../app/recoilStore'; import JwtInterceptors from './ApiController'; diff --git a/src/api/Lecture.ts b/src/api/Lecture.ts index fe13d387..f5216fda 100644 --- a/src/api/Lecture.ts +++ b/src/api/Lecture.ts @@ -1,8 +1,9 @@ -import { AxiosError } from 'axios'; -import { ExamPostsResponse } from 'types/exam'; +import type { AxiosError } from 'axios'; +import type { Review } from 'types/evaluate'; +import type { ExamPostsResponse } from 'types/exam'; import type { LectureDetailItem, MainLecture } from 'types/lecture'; + import JwtInterceptors from './ApiController'; -import { Review } from 'types/evaluate'; const Lecture = () => { const { instance } = JwtInterceptors(); @@ -13,6 +14,7 @@ const Lecture = () => { const data: MainLecture = await instance.get( `/lecture/all/?option=${lecture}&page=${page}&majorType=${majorType}` ); + return data; } catch (error) { const axiosError = error as AxiosError; @@ -60,6 +62,7 @@ const Lecture = () => { const { data } = await instance.get( `/evaluate-posts/?lectureId=${selectId}&page=${pageParam}` ); + return { data, isLast: data.length < 10, @@ -77,6 +80,7 @@ const Lecture = () => { const data: ExamPostsResponse = await instance.get( `/exam-posts/?lectureId=${selectId}&page=${pageParam}` ); + return { data, isLast: data.data.length < 10, diff --git a/src/api/Major.ts b/src/api/Major.ts index 7ec939cf..4659a12d 100644 --- a/src/api/Major.ts +++ b/src/api/Major.ts @@ -1,6 +1,7 @@ -import { AxiosError } from 'axios'; +import type { AxiosError } from 'axios'; +import type { VersionCheckSuccess } from 'types/common'; + import JwtInterceptors from './ApiController'; -import { VersionCheckSuccess } from 'types/common'; const Major = () => { const { instance } = JwtInterceptors(); @@ -9,6 +10,7 @@ const Major = () => { const version = async () => { try { const res: VersionCheckSuccess = await instance.get('/suwiki/version'); + return res; } catch (error) { const axiosError = error as AxiosError; @@ -20,6 +22,7 @@ const Major = () => { const type = async () => { try { const res = await instance.get('/suwiki/majorType'); + return res; } catch (error) { const axiosError = error as AxiosError; @@ -31,6 +34,7 @@ const Major = () => { const searchFavorite = async () => { try { const res = await instance.get('/user/favorite-major'); + return res; } catch (error) { const axiosError = error as AxiosError; @@ -42,6 +46,7 @@ const Major = () => { const favoriting = async (majorType: string) => { try { const res = await instance.post('/user/favorite-major', { majorType }); + return res; } catch (error) { const axiosError = error as AxiosError; diff --git a/src/api/Notice.ts b/src/api/Notice.ts index 22b667e2..45e0bcb6 100644 --- a/src/api/Notice.ts +++ b/src/api/Notice.ts @@ -1,6 +1,7 @@ +import type { AxiosError } from 'axios'; + +import type { NoticeDetail,NoticeItem } from '../types/notice'; import JwtInterceptors from './ApiController'; -import type { NoticeItem, NoticeDetail } from '../types/notice'; -import { AxiosError } from 'axios'; const Notices = () => { const { instance } = JwtInterceptors(); @@ -8,6 +9,7 @@ const Notices = () => { const list = async (pageParam = 1) => { try { const res = await instance.get(`/notice/all?page=${pageParam}`); + return { data: res, nextPage: pageParam + 1, @@ -23,12 +25,14 @@ const Notices = () => { const detail = async (notice: string) => { try { const res = await instance.get(`/notice/?noticeId=${notice}`); + return res; } catch (error) { const axiosError = error as AxiosError; alert(axiosError.message); } }; + return { list, detail }; }; diff --git a/src/api/User.ts b/src/api/User.ts index 7c0b9a0a..82466145 100644 --- a/src/api/User.ts +++ b/src/api/User.ts @@ -1,5 +1,7 @@ -import { AxiosError } from 'axios'; +import type { AxiosError } from 'axios'; import type { AxiosResponseSuccess } from 'types/common'; +import type { Review } from 'types/evaluate'; +import type { MyExam } from 'types/exam'; import type { BlacklistInfo, EvaluatePostCreate, @@ -12,10 +14,9 @@ import type { RestrictionInfo, UserProfileInfo, } from 'types/user'; + import { queryClient } from '../main'; import JwtInterceptors from './ApiController'; -import { Review } from 'types/evaluate'; -import { MyExam } from 'types/exam'; const User = () => { const { instance } = JwtInterceptors(); @@ -23,6 +24,7 @@ const User = () => { const info = async () => { try { const data: UserProfileInfo = await instance.get('/user/my-page'); + return data; } catch (error) { const axiosError = error as AxiosError; @@ -34,6 +36,7 @@ const User = () => { const evaluateList = async (pageParam = 1) => { try { const { data } = await instance.get(`/evaluate-posts/written/?page=${pageParam}`); + return { data, isLast: data.length < 10, @@ -49,6 +52,7 @@ const User = () => { const examInfoList = async (pageParam = 1) => { try { const { data } = await instance.get(`/exam-posts/written/?page=${pageParam}`); + return { data, isLast: data.length < 10, @@ -74,6 +78,7 @@ const User = () => { const banList = async () => { try { const data: BlacklistInfo[] = await instance.get('user/blacklist-reason'); + return data; } catch (error) { const axiosError = error as AxiosError; @@ -85,6 +90,7 @@ const User = () => { const resList = async () => { try { const data: RestrictionInfo[] = await instance.get('user/restricted-reason'); + return data; } catch (error) { const axiosError = error as AxiosError; diff --git a/src/api/etc.ts b/src/api/etc.ts index 48ba7604..d59ccf4b 100644 --- a/src/api/etc.ts +++ b/src/api/etc.ts @@ -1,11 +1,13 @@ -import axios, { AxiosError } from 'axios'; -import { ClientRefresh } from 'types/user'; +import type { AxiosError } from 'axios'; +import axios from 'axios'; +import type { ClientRefresh } from 'types/user'; import { removeStorage } from 'utils/loginStorage'; // 전공 선택 의존성때문에 따로 빼놓은 것 export const type = async (Authorization: string) => { try { const { data } = await axios.get(`/suwiki/majorType`, { headers: { Authorization } }); + return data; } catch (error) { const axiosError = error as AxiosError; @@ -18,6 +20,7 @@ export const searchFavorite = async (Authorization: string) => { const { data } = await axios.get(`/user/favorite-major`, { headers: { Authorization }, }); + return data; } catch (error) { const axiosError = error as AxiosError; @@ -43,6 +46,7 @@ export const logout = async () => { export const refresh = () => { try { const res = axios.post(`/user/client-refresh`); + return res; } catch (error) { const axiosError = error as AxiosError; diff --git a/src/api/index.ts b/src/api/index.ts index d0bc3da1..31d954fe 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,6 +1,6 @@ +export { default as ApiController } from './ApiController'; export { default as Auth } from './Auth'; export { default as Lecture } from './Lecture'; export { default as Major } from './Major'; export { default as Notice } from './Notice'; export { default as User } from './User'; -export { default as ApiController } from './ApiController'; diff --git a/src/app/recoilStore.ts b/src/app/recoilStore.ts index acb760cf..edd67336 100644 --- a/src/app/recoilStore.ts +++ b/src/app/recoilStore.ts @@ -1,5 +1,5 @@ import { atom } from 'recoil'; -import { Review } from 'types/evaluate'; +import type { Review } from 'types/evaluate'; export const lectureState = atom({ key: 'lectureState', diff --git a/src/components/AsyncBoundary.tsx b/src/components/AsyncBoundary.tsx index a351f3e5..f74334a7 100644 --- a/src/components/AsyncBoundary.tsx +++ b/src/components/AsyncBoundary.tsx @@ -1,4 +1,5 @@ -import { PropsWithChildren, Suspense, type ComponentProps } from 'react'; +import type {PropsWithChildren} from 'react'; +import { type ComponentProps, Suspense } from 'react'; import { ErrorBoundary, type FallbackProps } from 'react-error-boundary'; type ErrorBoundaryProps = ComponentProps; diff --git a/src/components/ErrorFrame.tsx b/src/components/ErrorFrame.tsx index 83c8e643..f4b1217f 100644 --- a/src/components/ErrorFrame.tsx +++ b/src/components/ErrorFrame.tsx @@ -10,6 +10,7 @@ interface ErrorFrameProps { const ErrorFrame = ({ mainMsg, subMsg }: ErrorFrameProps) => { const navigate = useNavigate(); + return ( diff --git a/src/components/Etc/Button.tsx b/src/components/Etc/Button.tsx index 107d33e4..51a144f9 100644 --- a/src/components/Etc/Button.tsx +++ b/src/components/Etc/Button.tsx @@ -1,5 +1,5 @@ import styled from '@emotion/styled'; -import { PropsWithChildren } from 'react'; +import type { PropsWithChildren } from 'react'; interface ButtonProps { onClick?: () => void; diff --git a/src/components/Etc/Modal.tsx b/src/components/Etc/Modal.tsx index 3e102ebe..6a98a807 100644 --- a/src/components/Etc/Modal.tsx +++ b/src/components/Etc/Modal.tsx @@ -1,5 +1,6 @@ import styled from '@emotion/styled'; -import { PropsWithChildren, useEffect } from 'react'; +import type { PropsWithChildren} from 'react'; +import { useEffect } from 'react'; interface ModalProps { isOpen: boolean; @@ -14,6 +15,7 @@ const Modal = ({ isOpen, onRequestClose, children }: PropsWithChildren window.removeEventListener('keydown', onKeyPress); }, [onRequestClose]); diff --git a/src/components/Etc/RangeInput.tsx b/src/components/Etc/RangeInput.tsx index 680c7c22..06d5af01 100644 --- a/src/components/Etc/RangeInput.tsx +++ b/src/components/Etc/RangeInput.tsx @@ -1,5 +1,5 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import styled from '@emotion/styled'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; interface RangeInputProps { min?: number; @@ -46,6 +46,7 @@ const RangeInput = ({ inputElement.addEventListener('mousedown', handleDown); inputElement.addEventListener('mouseup', handleUpAndLeave); inputElement.addEventListener('mouseleave', handleUpAndLeave); + return () => { inputElement.removeEventListener('mousemove', changeInputProgressPercentStyle); inputElement.removeEventListener('mousedown', handleDown); diff --git a/src/components/Etc/ScrollButton.tsx b/src/components/Etc/ScrollButton.tsx index 34de15de..6347c9f5 100644 --- a/src/components/Etc/ScrollButton.tsx +++ b/src/components/Etc/ScrollButton.tsx @@ -1,5 +1,5 @@ -import { useState, useEffect } from 'react'; import styled from '@emotion/styled'; +import { useEffect,useState } from 'react'; const ScrollButton = () => { const [ScrollY, setScrollY] = useState(0); @@ -19,6 +19,7 @@ const ScrollButton = () => { useEffect(() => { window.addEventListener('scroll', handleFollow); + return () => { window.removeEventListener('scroll', handleFollow); }; diff --git a/src/components/Etc/Spinner.tsx b/src/components/Etc/Spinner.tsx index 3be0f1ec..3956aac7 100644 --- a/src/components/Etc/Spinner.tsx +++ b/src/components/Etc/Spinner.tsx @@ -1,5 +1,5 @@ -import styled from '@emotion/styled'; import { keyframes } from '@emotion/react'; +import styled from '@emotion/styled'; const Spinner = ({ id }: { id?: string }) => { return ( diff --git a/src/components/EvaluationDetail.tsx b/src/components/EvaluationDetail.tsx index 61b37b3f..b46818d0 100644 --- a/src/components/EvaluationDetail.tsx +++ b/src/components/EvaluationDetail.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; -import { SetNumber, SetTeamNumber } from 'types/common'; -import { Review } from 'types/evaluate'; +import type { SetNumber, SetTeamNumber } from 'types/common'; +import type { Review } from 'types/evaluate'; import { floatFix } from 'utils/floatFix'; const team = { diff --git a/src/components/Lecture/IsTestInfo.tsx b/src/components/Lecture/IsTestInfo.tsx index 1a3cc1fc..8db11eb4 100644 --- a/src/components/Lecture/IsTestInfo.tsx +++ b/src/components/Lecture/IsTestInfo.tsx @@ -1,9 +1,9 @@ import styled from '@emotion/styled'; -import { Button, Spinner, SearchTestInfoList } from 'components'; +import { User } from 'api'; +import { Button, SearchTestInfoList,Spinner } from 'components'; import { fakeEvaluationList } from 'constants/placeholderData'; -import { isLoginStorage } from 'utils/loginStorage'; import useLectureQuery from 'hooks/useLectureQuery'; -import { User } from 'api'; +import { isLoginStorage } from 'utils/loginStorage'; interface IsTestInfoProps { selectId: string; diff --git a/src/components/Lecture/LectureCard.tsx b/src/components/Lecture/LectureCard.tsx index 90b34931..fa914a46 100644 --- a/src/components/Lecture/LectureCard.tsx +++ b/src/components/Lecture/LectureCard.tsx @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import StarRatings from 'react-star-ratings'; -import { LectureDetailItem } from 'types/lecture'; +import type { LectureDetailItem } from 'types/lecture'; import { floatFix } from 'utils/floatFix'; import { subStr } from 'utils/subString'; diff --git a/src/components/Lecture/LectureContainer.tsx b/src/components/Lecture/LectureContainer.tsx index 48889e13..8f762d4d 100644 --- a/src/components/Lecture/LectureContainer.tsx +++ b/src/components/Lecture/LectureContainer.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; -import { FlexWrap } from 'styles/common'; import { LectureCard } from 'components'; +import { FlexWrap } from 'styles/common'; import type { LectureDetailItem } from 'types/lecture'; const LectureContainer = ({ data }: { data?: LectureDetailItem[] }) => { diff --git a/src/components/Lecture/LectureInfoBox.tsx b/src/components/Lecture/LectureInfoBox.tsx index 2f87cd16..5cc48db0 100644 --- a/src/components/Lecture/LectureInfoBox.tsx +++ b/src/components/Lecture/LectureInfoBox.tsx @@ -1,9 +1,9 @@ import styled from '@emotion/styled'; -import { useNavigate } from 'react-router-dom'; import { Button } from 'components'; +import { useNavigate } from 'react-router-dom'; +import type { SetNumber, SetTeamNumber } from 'types/common'; +import type { LectureDetailItem } from 'types/lecture'; import { floatFix } from 'utils/floatFix'; -import { LectureDetailItem } from 'types/lecture'; -import { SetNumber, SetTeamNumber } from 'types/common'; interface LectureInfoBoxProps { current: LectureDetailItem; diff --git a/src/components/List/EvaluationList.tsx b/src/components/List/EvaluationList.tsx index 55125772..0318d397 100644 --- a/src/components/List/EvaluationList.tsx +++ b/src/components/List/EvaluationList.tsx @@ -1,12 +1,12 @@ import styled from '@emotion/styled'; -import StarRatings from 'react-star-ratings'; -import { useState } from 'react'; -import { subStr } from 'utils/subString'; -import { EvaluationDetail, WriteEvaluation, Spinner, Modal } from 'components'; +import { User } from 'api'; +import { EvaluationDetail, Modal,Spinner, WriteEvaluation } from 'components'; import useUserQuery from 'hooks/useUserQuery'; +import { useState } from 'react'; +import StarRatings from 'react-star-ratings'; +import type { Review } from 'types/evaluate'; import { floatFix } from 'utils/floatFix'; -import { User } from 'api'; -import { Review } from 'types/evaluate'; +import { subStr } from 'utils/subString'; const EvaluationList = () => { const { EvaluationList } = useUserQuery(); diff --git a/src/components/List/LectureList.tsx b/src/components/List/LectureList.tsx index ef9cc3f2..87f00851 100644 --- a/src/components/List/LectureList.tsx +++ b/src/components/List/LectureList.tsx @@ -2,7 +2,7 @@ import { LectureContainer } from 'components'; import { fakeLectureList } from 'constants/placeholderData'; import useLectureQuery from 'hooks/useLectureQuery'; import { FlexWrap } from 'styles/common'; -import { MainLecture } from 'types/lecture'; +import type { MainLecture } from 'types/lecture'; interface LectureListProps { pages: diff --git a/src/components/List/SearchEvaluationList.tsx b/src/components/List/SearchEvaluationList.tsx index bcc1f6cd..ecb60266 100644 --- a/src/components/List/SearchEvaluationList.tsx +++ b/src/components/List/SearchEvaluationList.tsx @@ -1,12 +1,12 @@ import styled from '@emotion/styled'; -import StarRatings from 'react-star-ratings'; -import { useState, Fragment } from 'react'; import { User } from 'api'; import { EvaluationDetail, Spinner } from 'components'; import { fakeEvaluationList } from 'constants/placeholderData'; import useLectureQuery from 'hooks/useLectureQuery'; +import { Fragment,useState } from 'react'; +import StarRatings from 'react-star-ratings'; +import type { Review } from 'types/evaluate'; import { floatFix } from 'utils/floatFix'; -import { Review } from 'types/evaluate'; export const FakeList = () => { return ( diff --git a/src/components/List/SearchTestInfoList.tsx b/src/components/List/SearchTestInfoList.tsx index 8df95557..ca0616f7 100644 --- a/src/components/List/SearchTestInfoList.tsx +++ b/src/components/List/SearchTestInfoList.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; import { User } from 'api'; -import { ExamPost } from 'types/exam'; +import type { ExamPost } from 'types/exam'; interface SearchTestInfoListProps { page: ExamPost[]; diff --git a/src/components/List/TestInfoList.tsx b/src/components/List/TestInfoList.tsx index 1c9b0693..895e492a 100644 --- a/src/components/List/TestInfoList.tsx +++ b/src/components/List/TestInfoList.tsx @@ -1,11 +1,12 @@ import styled from '@emotion/styled'; -import { useState } from 'react'; -import { WriteTestInfo, Spinner, Modal } from 'components'; -import { subStr } from 'utils/subString'; -import useUserQuery from 'hooks/useUserQuery'; import { User } from 'api'; +import { Modal, Spinner, WriteTestInfo } from 'components'; +import useUserQuery from 'hooks/useUserQuery'; +import { useState } from 'react'; import type { MyExam } from 'types/exam'; -import { ExamDiff } from './SearchTestInfoList'; +import { subStr } from 'utils/subString'; + +import type { ExamDiff } from './SearchTestInfoList'; const TestInfoList = () => { const { TestInfoList } = useUserQuery(); @@ -22,9 +23,7 @@ const TestInfoList = () => { data.pages.map((page) => { return ( - {page?.data.map((row) => ( - - ))} + {page?.data.map((row) => )} ); }) @@ -43,7 +42,10 @@ export const TestInfoCard = ({ row }: { row: MyExam }) => { let mobileTitle = subStr(row.lectureName, 14); const onDelete = () => { - window.confirm('시험정보를 삭제하시겠습니까?') && deleteExamInfo(row.id.toString()); + const isConfirmed = window.confirm('시험정보를 삭제하시겠습니까?'); + if (isConfirmed) { + deleteExamInfo(row.id.toString()); + } }; const examDifficultySet = row.examDifficulty; diff --git a/src/components/Major/MajorSearch.tsx b/src/components/Major/MajorSearch.tsx index ca67c983..4efb8aaa 100644 --- a/src/components/Major/MajorSearch.tsx +++ b/src/components/Major/MajorSearch.tsx @@ -1,8 +1,8 @@ import styled from '@emotion/styled'; -import * as styles from '@mui/material/styles'; -import { Fragment, useState } from 'react'; import { TextField } from '@mui/material'; +import * as styles from '@mui/material/styles'; import useFavoriteMajor from 'hooks/useFavoriteMajor'; +import { Fragment, useState } from 'react'; const MajorSearch = ({ setModalIsOpen, diff --git a/src/components/Major/MajorSelect.tsx b/src/components/Major/MajorSelect.tsx index 30d2430f..a9a6f28b 100644 --- a/src/components/Major/MajorSelect.tsx +++ b/src/components/Major/MajorSelect.tsx @@ -1,8 +1,8 @@ +import { MajorSearch, Modal } from 'components'; import { useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { MajorSearch, Modal } from 'components'; -import { subStr } from 'utils/subString'; import { Arrows, OptionBox, SelectedOption, SelectedOption_M } from 'styles/common'; +import { subStr } from 'utils/subString'; const MajorSelect = () => { const [searchParams] = useSearchParams(); diff --git a/src/components/Nav.tsx b/src/components/Nav.tsx index ec204c14..d312d6c2 100644 --- a/src/components/Nav.tsx +++ b/src/components/Nav.tsx @@ -1,8 +1,8 @@ import styled from '@emotion/styled'; +import { logout } from 'api/etc'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { isLoginStorage } from 'utils/loginStorage'; -import { logout } from 'api/etc'; const Nav = () => { const navigate = useNavigate(); diff --git a/src/components/OptionSelect.tsx b/src/components/OptionSelect.tsx index 136cd3da..fcf99caa 100644 --- a/src/components/OptionSelect.tsx +++ b/src/components/OptionSelect.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { Arrows, Option, OptionBox, Options, SelectedOption } from 'styles/common'; -import { SortOption } from 'types/common'; +import type { SortOption } from 'types/common'; interface OptionSelectProps { list: SortOption[]; @@ -21,9 +21,11 @@ const OptionSelect = ({ list, itemTitle, location }: OptionSelectProps) => { const isMain = location === 'main'; const handleSelect = (option: string) => { - isMain - ? navigate(`/?option=${option}&majorType=${majorType}`) - : navigate(`/search?q=${searchValue}&option=${option}&majorType=${majorType}`); + if (isMain) { + navigate(`/?option=${option}&majorType=${majorType}`); + } else { + navigate(`/search?q=${searchValue}&option=${option}&majorType=${majorType}`); + } }; return ( @@ -32,7 +34,7 @@ const OptionSelect = ({ list, itemTitle, location }: OptionSelectProps) => { select={select} icon={isMain ? selectedOption?.icon : undefined} onClick={(e) => { - e.stopPropagation; + e.stopPropagation(); onSelect(!select); }} > diff --git a/src/components/RouteChangeTracker.ts b/src/components/RouteChangeTracker.ts index 2cfc0162..bcdff6c2 100644 --- a/src/components/RouteChangeTracker.ts +++ b/src/components/RouteChangeTracker.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -import { useLocation } from 'react-router-dom'; import { initialize, pageview } from 'react-ga'; +import { useLocation } from 'react-router-dom'; const RouteChangeTracker = () => { const location = useLocation(); diff --git a/src/components/SemesterSelect.tsx b/src/components/SemesterSelect.tsx index b8b17ffc..72294c7b 100644 --- a/src/components/SemesterSelect.tsx +++ b/src/components/SemesterSelect.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { OptionBox, SelectedOption, Arrows, Options, Option } from 'styles/common'; +import { Arrows, Option, OptionBox, Options, SelectedOption } from 'styles/common'; interface SemesterSelectProps { list: string[]; @@ -9,12 +9,13 @@ interface SemesterSelectProps { const SemesterSelect = ({ list, selected, setSelect }: SemesterSelectProps) => { const [modal, setModal] = useState(false); + return ( { - e.stopPropagation; + e.stopPropagation(); setModal(!modal); }} > diff --git a/src/components/UserInfo/UserAccount.tsx b/src/components/UserInfo/UserAccount.tsx index 52e1211d..660022e5 100644 --- a/src/components/UserInfo/UserAccount.tsx +++ b/src/components/UserInfo/UserAccount.tsx @@ -1,5 +1,5 @@ import styled from '@emotion/styled'; -import { UserEmail, UserId } from 'types/user'; +import type { UserEmail, UserId } from 'types/user'; interface UserAccountProps extends UserId, UserEmail { isLogin: boolean; diff --git a/src/components/UserInfo/UserInfo.tsx b/src/components/UserInfo/UserInfo.tsx index 3d1875df..5e48a3fa 100644 --- a/src/components/UserInfo/UserInfo.tsx +++ b/src/components/UserInfo/UserInfo.tsx @@ -1,9 +1,9 @@ import styled from '@emotion/styled'; -import { useNavigate } from 'react-router-dom'; import { UserAccount, UserPoint } from 'components'; import { fakeUserInfo } from 'constants/placeholderData'; +import { useNavigate } from 'react-router-dom'; +import type { UserProfileInfo } from 'types/user'; import { isLoginStorage } from 'utils/loginStorage'; -import { UserProfileInfo } from 'types/user'; interface UserInfoProps { my: UserProfileInfo; @@ -14,7 +14,11 @@ const UserInfo = ({ my }: UserInfoProps) => { const isLogin = isLoginStorage(); const handleNavigate = () => { - isLogin ? navigate('/myposting') : navigate('/login'); + if (isLogin) { + navigate('/myposting'); + } else { + navigate('/login'); + } }; const optionSlice = (start: number, end: number) => { diff --git a/src/components/UserInfo/UserPoint.tsx b/src/components/UserInfo/UserPoint.tsx index 03153711..73544c6a 100644 --- a/src/components/UserInfo/UserPoint.tsx +++ b/src/components/UserInfo/UserPoint.tsx @@ -1,5 +1,5 @@ import styled from '@emotion/styled'; -import { UserProfileInfo } from 'types/user'; +import type { UserProfileInfo } from 'types/user'; interface UserPointProps { my: UserProfileInfo; diff --git a/src/components/Write/WriteEvaluation.tsx b/src/components/Write/WriteEvaluation.tsx index 1c21bfa9..c137d347 100644 --- a/src/components/Write/WriteEvaluation.tsx +++ b/src/components/Write/WriteEvaluation.tsx @@ -1,5 +1,4 @@ import styled from '@emotion/styled'; -import { Fragment } from 'react'; import SemesterSelect from 'components/SemesterSelect'; import { EvaluationSelectOptions, @@ -7,6 +6,7 @@ import { semesters, } from 'constants/placeholderData'; import useWriteEvaluation from 'hooks/useWriteEvaluation'; +import { Fragment } from 'react'; import type { Review } from 'types/evaluate'; export interface WriteEvaluationProps { @@ -29,6 +29,7 @@ const WriteEvaluation = ({ setModalIsOpen, row, type }: WriteEvaluationProps) => onChangeLectureOptions, onEvaluate, } = useWriteEvaluation({ setModalIsOpen, row, type }); + return ( @@ -61,6 +62,7 @@ const WriteEvaluation = ({ setModalIsOpen, row, type }: WriteEvaluationProps) => {EvaluationSliderOptions.map(({ id, name }) => { const { state, Slider } = SliderOptions[id as SliderId]; + return ( diff --git a/src/components/Write/WriteTestInfo.tsx b/src/components/Write/WriteTestInfo.tsx index b11b1fc7..1250bc91 100644 --- a/src/components/Write/WriteTestInfo.tsx +++ b/src/components/Write/WriteTestInfo.tsx @@ -1,9 +1,9 @@ import styled from '@emotion/styled'; -import { useState } from 'react'; -import { useMutation } from 'react-query'; import { User } from 'api'; import SemesterSelect from 'components/SemesterSelect'; import { ExamSelectOptions, examTypes, semesters } from 'constants/placeholderData'; +import { useState } from 'react'; +import { useMutation } from 'react-query'; import type { MyExam } from 'types/exam'; type WriteTestProps = 'examDifficulty' | 'examInfo'; @@ -50,7 +50,7 @@ const WriteTestInfo = ({ setModalIsOpen, row, type }: WriteTestInfoProps) => { examType, examDifficulty: examOptions.examDifficulty, content, - }) + }), ); const examInfoUpdate = useMutation(() => @@ -60,7 +60,7 @@ const WriteTestInfo = ({ setModalIsOpen, row, type }: WriteTestInfoProps) => { examType, examDifficulty: examOptions.examDifficulty, content, - }) + }), ); const onTest = () => { @@ -70,7 +70,11 @@ const WriteTestInfo = ({ setModalIsOpen, row, type }: WriteTestInfoProps) => { if (examOptions.examInfo.length === 0) return alert('시험유형(란)을 선택해주세요'); if (content.length < 30 || content.length > 1000) return alert('최소 30자 이상 최대 1000자 이내로 입력해주세요'); - type === 'update' ? examInfoUpdate.mutate() : examWriting.mutate(); + if (type === 'update') { + examInfoUpdate.mutate(); + } else { + examWriting.mutate(); + } setModalIsOpen(false); }; diff --git a/src/components/index.ts b/src/components/index.ts index 4753f771..d953d1b7 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,37 +1,31 @@ +export { default as BanFrame } from './BanFrame'; +export { default as ErrorFrame } from './ErrorFrame'; export { default as Button } from './Etc/Button'; +export { default as Modal } from './Etc/Modal'; export { default as RangeInput } from './Etc/RangeInput'; export { default as ScrollButton } from './Etc/ScrollButton'; export { default as Spinner } from './Etc/Spinner'; -export { default as Modal } from './Etc/Modal'; - +export { default as EvaluationDetail } from './EvaluationDetail'; +export { default as Footer } from './Footer'; export { default as IsTestInfo } from './Lecture/IsTestInfo'; export { default as LectureCard } from './Lecture/LectureCard'; export { default as LectureContainer } from './Lecture/LectureContainer'; export { default as LectureDetail } from './Lecture/LectureDetail'; export { default as LectureInfoBox } from './Lecture/LectureInfoBox'; export { default as LectureSearch } from './Lecture/LectureSearch'; - export { default as EvaluationList } from './List/EvaluationList'; export { default as LectureList } from './List/LectureList'; export { default as MainList } from './List/MainList'; export { default as SearchEvaluationList } from './List/SearchEvaluationList'; export { default as SearchTestInfoList } from './List/SearchTestInfoList'; export { default as TestInfoList } from './List/TestInfoList'; - -export { default as MajorSelect } from './Major/MajorSelect'; +export { default as MainBanner } from './MainBanner'; export { default as MajorSearch } from './Major/MajorSearch'; - +export { default as MajorSelect } from './Major/MajorSelect'; +export { default as Nav } from './Nav'; +export { default as OptionSelect } from './OptionSelect'; export { default as UserAccount } from './UserInfo/UserAccount'; export { default as UserInfo } from './UserInfo/UserInfo'; export { default as UserPoint } from './UserInfo/UserPoint'; - export { default as WriteEvaluation } from './Write/WriteEvaluation'; export { default as WriteTestInfo } from './Write/WriteTestInfo'; - -export { default as BanFrame } from './BanFrame'; -export { default as ErrorFrame } from './ErrorFrame'; -export { default as EvaluationDetail } from './EvaluationDetail'; -export { default as Footer } from './Footer'; -export { default as Nav } from './Nav'; -export { default as OptionSelect } from './OptionSelect'; -export { default as MainBanner } from './MainBanner'; diff --git a/src/hooks/useFavoriteMajor.ts b/src/hooks/useFavoriteMajor.ts index fd32409a..6bc6980a 100644 --- a/src/hooks/useFavoriteMajor.ts +++ b/src/hooks/useFavoriteMajor.ts @@ -26,6 +26,7 @@ const useFavoriteMajor = (setModalIsOpen: React.Dispatch { const { data: getMainLecture } = useQuery( ['main', option, major], () => lecture.main(option, 1, major), - { keepPreviousData: true, suspense: true } + { keepPreviousData: true, suspense: true }, ); // 검색 쿼리(key: 검색어,정렬,전공) @@ -43,16 +43,18 @@ const useLectureQuery = () => { { getNextPageParam: (lastPage) => { if (lastPage && !lastPage.isLast) return lastPage.nextPage; + return undefined; }, keepPreviousData: true, - } + }, ); useEffect(() => { if (inView) { getNextSearch(); } }, [inView, getNextSearch]); + return { data, searchLoading, nextLoading, value, ref }; }; @@ -86,8 +88,9 @@ const useLectureQuery = () => { totalAvg: 0, }); }, - } + }, ); + return { data, isLoading, isLogin: isLogin }; }; @@ -100,19 +103,21 @@ const useLectureQuery = () => { { getNextPageParam: (lastPage) => { if (lastPage && !lastPage.isLast) return lastPage.nextPage; + return undefined; }, onSuccess: (data) => setWritten(data.pages[0]!.data), cacheTime: CACHE_TIME.MINUTE_0, staleTime: CACHE_TIME.MINUTE_0, enabled: isLogin, - } + }, ); useEffect(() => { if (inView && isLogin) { fetchNextPage(); } }, [inView, fetchNextPage]); + return { data, isFetchingNextPage, isLoading, ref }; }; @@ -125,19 +130,21 @@ const useLectureQuery = () => { { getNextPageParam: (lastPage) => { if (lastPage && !lastPage.isLast) return lastPage.nextPage; + return undefined; }, onSuccess: (data) => setWritten(data.pages[0]!.data), cacheTime: CACHE_TIME.MINUTE_0, staleTime: CACHE_TIME.MINUTE_0, enabled: isLogin, - } + }, ); useEffect(() => { if (inView && isLogin) { fetchNextPage(); } }, [inView, fetchNextPage]); + return { data, isFetchingNextPage, isLoading, ref }; }; diff --git a/src/hooks/useSearch.ts b/src/hooks/useSearch.ts index 04628855..f5ba757c 100644 --- a/src/hooks/useSearch.ts +++ b/src/hooks/useSearch.ts @@ -1,4 +1,5 @@ -import { KeyboardEvent, useEffect, useRef } from 'react'; +import type { KeyboardEvent} from 'react'; +import { useEffect, useRef } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; interface SearchHook { @@ -23,6 +24,7 @@ const useSearch: SearchHook = (key = 'q') => { const inputValue = inputRef.current?.value.trim() || ''; if (inputValue.length < 2) { alert('두 글자 이상 입력해주세요'); + return; } navigate(`/search?q=${inputValue}&option=${option}&majorType=${majorType}`); diff --git a/src/hooks/useUserQuery.ts b/src/hooks/useUserQuery.ts index 276bb64a..88fe8bd4 100644 --- a/src/hooks/useUserQuery.ts +++ b/src/hooks/useUserQuery.ts @@ -1,9 +1,9 @@ -import { useInfiniteQuery } from 'react-query'; import { User } from 'api'; -import { isLoginStorage } from 'utils/loginStorage'; +import { CACHE_TIME } from 'constants/cacheTime'; import { useEffect } from 'react'; import { useInView } from 'react-intersection-observer'; -import { CACHE_TIME } from 'constants/cacheTime'; +import { useInfiniteQuery } from 'react-query'; +import { isLoginStorage } from 'utils/loginStorage'; const useUserQuery = () => { const user = User(); @@ -16,6 +16,7 @@ const useUserQuery = () => { { getNextPageParam: (lastPage) => { if (lastPage && !lastPage.isLast) return lastPage.nextPage; + return undefined; }, enabled: isLoginStorage(), @@ -28,6 +29,7 @@ const useUserQuery = () => { fetchNextPage(); } }, [inView, fetchNextPage]); + return { data, isLoading, isFetchingNextPage, ref }; }; @@ -40,6 +42,7 @@ const useUserQuery = () => { { getNextPageParam: (lastPage) => { if (lastPage && !lastPage.isLast) return lastPage.nextPage; + return undefined; }, enabled: isLoginStorage(), @@ -52,6 +55,7 @@ const useUserQuery = () => { fetchNextPage(); } }, [inView, fetchNextPage]); + return { data, isLoading, isFetchingNextPage, ref }; }; diff --git a/src/hooks/useWriteEvaluation.ts b/src/hooks/useWriteEvaluation.ts index 318d91ca..8cc53518 100644 --- a/src/hooks/useWriteEvaluation.ts +++ b/src/hooks/useWriteEvaluation.ts @@ -49,11 +49,16 @@ const useWriteEvaluation = ({ setModalIsOpen, row, type }: WriteEvaluationProps) if (lectureOptions.difficulty === undefined) return alert('학점(란)을 선택해주세요'); if (content.length < 30 || content.length > 1000) return alert('최소 30자 이상 최대 1000자 이내로 입력해주세요'); - type === 'update' - ? updateEvaluation(row.id.toString(), data) - : writeEvaluation(row.id.toString(), data); + + if (type === 'update') { + updateEvaluation(row.id.toString(), data); + } else { + writeEvaluation(row.id.toString(), data); + } + setModalIsOpen(false); }; + return { SliderOptions, lectureOptions, diff --git a/src/main.tsx b/src/main.tsx index c1f84fb5..db9f93ba 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,16 +1,17 @@ import './styles/globalStyle.css'; import axios from 'axios'; +import AsyncBoundary from 'components/AsyncBoundary'; import { CACHE_TIME } from 'constants/cacheTime'; +import { BadGateway } from 'pages'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { initialize } from 'react-ga'; import { QueryClient, QueryClientProvider } from 'react-query'; import { ReactQueryDevtools } from 'react-query/devtools'; import { RecoilRoot } from 'recoil'; + import App from './App'; -import AsyncBoundary from 'components/AsyncBoundary'; -import { BadGateway } from 'pages'; initialize('G-KG7KQ8K3GP'); export const queryClient = new QueryClient({ diff --git a/src/pages/Exit.tsx b/src/pages/Exit.tsx index e378e99c..05c00655 100644 --- a/src/pages/Exit.tsx +++ b/src/pages/Exit.tsx @@ -1,9 +1,10 @@ import { Auth } from 'api'; import { Button } from 'components'; import { CssTextField } from 'components/Etc/CssTextField'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import type { SubmitHandler} from 'react-hook-form'; +import { useForm } from 'react-hook-form'; import { AuthWrapper, Container, Img, Sub, Title } from 'styles/common'; -import { UserLogin } from 'types/user'; +import type { UserLogin } from 'types/user'; const Exit = () => { const { quit } = Auth(); diff --git a/src/pages/IdSearch.tsx b/src/pages/IdSearch.tsx index 84fbd3fd..650cdbf7 100644 --- a/src/pages/IdSearch.tsx +++ b/src/pages/IdSearch.tsx @@ -2,7 +2,7 @@ import { Auth } from 'api'; import { Button } from 'components'; import { CssTextField } from 'components/Etc/CssTextField'; import { Loader } from 'components/Etc/Spinner'; -import { useForm, type SubmitHandler } from 'react-hook-form'; +import { type SubmitHandler,useForm } from 'react-hook-form'; import { AuthWrapper, Container, Img, Sub, Title } from 'styles/common'; import type { UserEmail } from 'types/user'; import { validateEmail } from 'utils/validate'; diff --git a/src/pages/LectureInfo.tsx b/src/pages/LectureInfo.tsx index 00790ee8..4bd74882 100644 --- a/src/pages/LectureInfo.tsx +++ b/src/pages/LectureInfo.tsx @@ -66,8 +66,8 @@ const LectureInfo = () => { !isLogin ? alert('로그인해 주세요') : !written - ? setModalIsOpen(true) - : alert(`이미 작성한 ${check}가 있습니다`) + ? setModalIsOpen(true) + : alert(`이미 작성한 ${check}가 있습니다`) } /> diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 6547cbe5..875056ce 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,7 +1,8 @@ import styled from '@emotion/styled'; import { Auth } from 'api'; import { CssTextField } from 'components/Etc/CssTextField'; -import { FieldValues, SubmitHandler, useForm } from 'react-hook-form'; +import type { FieldValues, SubmitHandler} from 'react-hook-form'; +import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { AuthWrapper, Container, Img } from 'styles/common'; diff --git a/src/pages/Main.tsx b/src/pages/Main.tsx index 2c2551da..8dd0c31e 100644 --- a/src/pages/Main.tsx +++ b/src/pages/Main.tsx @@ -2,6 +2,7 @@ import styled from '@emotion/styled'; import { versionCheck } from 'app/versionCheck'; import { LectureSearch, MainBanner, MainList, MajorSelect, OptionSelect } from 'components'; import { useNavigate } from 'react-router-dom'; + import { sortOptions } from '../constants/placeholderData'; const Main = () => { diff --git a/src/pages/MyInfo.tsx b/src/pages/MyInfo.tsx index 3b920ac5..2435f268 100644 --- a/src/pages/MyInfo.tsx +++ b/src/pages/MyInfo.tsx @@ -1,9 +1,9 @@ -import { useQuery } from 'react-query'; import { User } from 'api'; import { UserInfo } from 'components'; +import { CACHE_TIME } from 'constants/cacheTime'; import { fakeUserInfo } from 'constants/placeholderData'; +import { useQuery } from 'react-query'; import { isLoginStorage } from 'utils/loginStorage'; -import { CACHE_TIME } from 'constants/cacheTime'; const MyInfo = () => { const user = User(); diff --git a/src/pages/MyPosting.tsx b/src/pages/MyPosting.tsx index 42de355e..f01cf2cf 100644 --- a/src/pages/MyPosting.tsx +++ b/src/pages/MyPosting.tsx @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; +import { Button, EvaluationList, TestInfoList } from 'components'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Button, EvaluationList, TestInfoList } from 'components'; import { isLoginStorage } from 'utils/loginStorage'; const MyPosting = () => { diff --git a/src/pages/PwSearch.tsx b/src/pages/PwSearch.tsx index 7395b99b..d68786f3 100644 --- a/src/pages/PwSearch.tsx +++ b/src/pages/PwSearch.tsx @@ -2,7 +2,7 @@ import { Auth } from 'api'; import { Button } from 'components'; import { CssTextField } from 'components/Etc/CssTextField'; import { Loader } from 'components/Etc/Spinner'; -import { useForm, type SubmitHandler } from 'react-hook-form'; +import { type SubmitHandler,useForm } from 'react-hook-form'; import { AuthWrapper, Container, Img, Sub, Title } from 'styles/common'; import type { UserFindPw } from 'types/user'; import { validateEmail } from 'utils/validate'; diff --git a/src/pages/ResetPassword.tsx b/src/pages/ResetPassword.tsx index 1e07a843..8b1ff2ce 100644 --- a/src/pages/ResetPassword.tsx +++ b/src/pages/ResetPassword.tsx @@ -1,7 +1,7 @@ import { Auth } from 'api'; import { Button } from 'components'; import { CssTextField } from 'components/Etc/CssTextField'; -import { useForm, type SubmitHandler } from 'react-hook-form'; +import { type SubmitHandler,useForm } from 'react-hook-form'; import { AuthWrapper, Checking, Container, Img, Sub, Title } from 'styles/common'; import type { ResetPassword as ResetPasswordType } from 'types/user'; import { validatePassword } from 'utils/validate'; diff --git a/src/pages/SignUp.tsx b/src/pages/SignUp.tsx index 0cc9cfbd..ee600f0e 100644 --- a/src/pages/SignUp.tsx +++ b/src/pages/SignUp.tsx @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { Auth } from 'api'; import { CssTextField } from 'components/Etc/CssTextField'; import { useEffect, useState } from 'react'; -import { useForm, type SubmitHandler } from 'react-hook-form'; +import { type SubmitHandler, useForm } from 'react-hook-form'; import { AuthWrapper, Button, Checking, Container, Img } from 'styles/common'; import type { UserJoin } from 'types/user'; import { @@ -32,14 +32,17 @@ const SignUp = () => { // 체크박스 전체선택시 모두선택 체크박스 활성화시키기 const handleCheck = (e: React.ChangeEvent) => { const targetName = e.target.name as CheckList; - e.target.checked - ? setCheckList([...checkList, targetName]) - : setCheckList(checkList.filter((el) => el !== targetName)); + setCheckList((prevCheckList) => + e.target.checked + ? [...prevCheckList, targetName] + : prevCheckList.filter((item) => item !== targetName), + ); }; // 전체체크 선택시 전체 선택 or 전체해제 const checkAll = (e: React.ChangeEvent) => { - e.target.checked ? setCheckList(['terms', 'privacy']) : setCheckList([]); + const isChecked = e.target.checked; + setCheckList(isChecked ? ['terms', 'privacy'] : []); }; const handleIdCheck = () => checkId(setIdCheck, { loginId: formValues.loginId }); diff --git a/src/types/user.ts b/src/types/user.ts index a2350200..88b001cc 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -60,7 +60,7 @@ export interface EvaluatePostBase { content: string; } -export interface EvaluatePostUpdate extends EvaluatePostBase {} +export type EvaluatePostUpdate = EvaluatePostBase; export interface EvaluatePostCreate extends EvaluatePostBase { lectureName: string; diff --git a/src/utils/fakeData.ts b/src/utils/fakeData.ts index 8ac4dc39..104e279c 100644 --- a/src/utils/fakeData.ts +++ b/src/utils/fakeData.ts @@ -5,6 +5,7 @@ const createFakeData = (count: number, generator: Generator): T[] => { for (let i = 0; i < data.length; i++) { data[i] = generator(i); } + return data; }; diff --git a/src/utils/loginStorage.ts b/src/utils/loginStorage.ts index 23302146..81e3a012 100644 --- a/src/utils/loginStorage.ts +++ b/src/utils/loginStorage.ts @@ -2,6 +2,7 @@ const storage = localStorage; export const isLoginStorage = () => { const state = localStorage.login; + return !!state; }; @@ -12,6 +13,7 @@ export const getStorage = (key: string, defaultValue = undefined) => { return storedValue || defaultValue; } catch (error) { console.error(error); + return defaultValue; } };