diff --git a/components/admin/announcement/AnnounceEdit.tsx b/components/admin/announcement/AnnounceEdit.tsx index 29081bd01..6cb143410 100644 --- a/components/admin/announcement/AnnounceEdit.tsx +++ b/components/admin/announcement/AnnounceEdit.tsx @@ -32,6 +32,15 @@ export default function AnnounceEdit() { resetHandler(); }, []); + const resetHandler = async () => { + try { + const res = await instance.get('/pingpong/announcement'); + setContent(res?.data.content); + } catch (e) { + alert(e); + } + }; + if (!user) return null; const currentUserId = user.intraId; @@ -77,15 +86,6 @@ export default function AnnounceEdit() { } }; - const resetHandler = async () => { - try { - const res = await instance.get('/pingpong/announcement'); - setContent(res?.data.content); - } catch (e) { - alert(e); - } - }; - return (
diff --git a/components/admin/coin/CoinPolicyHistory.tsx b/components/admin/coin/CoinPolicyHistory.tsx index e129137f1..58e9bce16 100644 --- a/components/admin/coin/CoinPolicyHistory.tsx +++ b/components/admin/coin/CoinPolicyHistory.tsx @@ -1,4 +1,5 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useState } from 'react'; +import { useQuery } from 'react-query'; import { useSetRecoilState } from 'recoil'; import { Paper, @@ -8,10 +9,7 @@ import { TableContainer, TableRow, } from '@mui/material'; -import { - IcoinPolicyHistory, - IcoinPolicyHistoryTable, -} from 'types/admin/adminCoinTypes'; +import { IcoinPolicyHistory } from 'types/admin/adminCoinTypes'; import { instanceInManage } from 'utils/axios'; import { dateToStringShort } from 'utils/handleTime'; import { toastState } from 'utils/recoil/toast'; @@ -34,47 +32,43 @@ const coinPolicyHistoryTableTitle: { [key: string]: string } = { }; function CoinPolicyHistory() { - const [coinPolicyHistoryData, setCoinPolicyHistoryData] = - useState({ - coinPolicyList: [], - totalPage: 0, - currentPage: 0, - }); const [currentPage, setCurrentPage] = useState(1); const setSnackBar = useSetRecoilState(toastState); - const getCoinPolicyHistoryHandler = useCallback(async () => { - try { - const res = await instanceInManage.get( - `/coinpolicy?page=${currentPage}&size=5` - ); - setCoinPolicyHistoryData({ - coinPolicyList: res.data.coinPolicyList.map( - (coinPolicyHistory: IcoinPolicyHistory) => { + const getApi = async (currentPage: number) => { + const res = await instanceInManage.get( + `/coinpolicy?page=${currentPage}&size=5` + ); + return res.data; + }; + + const { data, isError } = useQuery( + ['coinPolicyHistory', currentPage], + () => getApi(currentPage), + { + keepPreviousData: true, + select: (data) => ({ + coinPolicyList: data.coinPolicyList?.map( + (coinPolicy: IcoinPolicyHistory) => { return { - ...coinPolicyHistory, - createdAt: dateToStringShort( - new Date(coinPolicyHistory.createdAt) - ), + ...coinPolicy, + createdAt: dateToStringShort(new Date(coinPolicy.createdAt)), }; } ), - totalPage: res.data.totalPage, - currentPage: currentPage, - }); - } catch (e: unknown) { - setSnackBar({ - toastName: 'get coinpolicyhistory', - severity: 'error', - message: 'API 요청에 문제가 발생했습니다.', - clicked: true, - }); + totalPage: data.totalPage, + }), } - }, [currentPage]); + ); - useEffect(() => { - getCoinPolicyHistoryHandler(); - }, [currentPage]); + if (isError) { + setSnackBar({ + toastName: 'get coinpolicyhistory', + severity: 'error', + message: 'API 요청에 문제가 발생했습니다.', + clicked: true, + }); + } return ( <> @@ -85,8 +79,8 @@ function CoinPolicyHistory() { table={coinPolicyHistoryTableTitle} /> - {coinPolicyHistoryData.coinPolicyList.length > 0 ? ( - coinPolicyHistoryData.coinPolicyList.map( + {data?.coinPolicyList?.length > 0 ? ( + data?.coinPolicyList?.map( (coinPolicyHistory: IcoinPolicyHistory) => (
{ setCurrentPage(pageNumber); }} diff --git a/components/error/Error.tsx b/components/error/Error.tsx index 116e8c2f6..592a09686 100644 --- a/components/error/Error.tsx +++ b/components/error/Error.tsx @@ -1,9 +1,17 @@ +import { useEffect } from 'react'; +import { useResetRecoilState } from 'recoil'; import useErrorPage from 'hooks/error/useErrorPage'; import styles from 'styles/Error.module.scss'; import ErrorEmoji from '/public/image/error_face.svg'; +import { modalState } from 'utils/recoil/modal'; export default function ErrorPage() { const { error, goHome } = useErrorPage(); + const resetModal = useResetRecoilState(modalState); + + useEffect(() => { + resetModal(); + }, []); return (
diff --git a/components/modal/admin/AdminEditCoinPolicy.tsx b/components/modal/admin/AdminEditCoinPolicy.tsx index 83476e4a3..fe7c1ac06 100644 --- a/components/modal/admin/AdminEditCoinPolicy.tsx +++ b/components/modal/admin/AdminEditCoinPolicy.tsx @@ -1,3 +1,5 @@ +import { AxiosResponse } from 'axios'; +import { useMutation, useQueryClient } from 'react-query'; import { useSetRecoilState } from 'recoil'; import { IcoinPolicy } from 'types/admin/adminCoinTypes'; import { instanceInManage } from 'utils/axios'; @@ -10,24 +12,35 @@ export default function AdminEditCoinPolicyModal(props: IcoinPolicy) { const setModal = useSetRecoilState(modalState); const setSnackBar = useSetRecoilState(toastState); - const editCoinPolicyHandler = async () => { - try { - await instanceInManage.post(`/coinpolicy`, props); - setSnackBar({ - toastName: 'edit coinpolicy', - severity: 'success', - message: '새로운 재화 정책이 등록되었습니다.', - clicked: true, - }); - } catch (e: unknown) { - setSnackBar({ - toastName: 'edit coinpolicy', - severity: 'error', - message: 'API 요청에 문제가 발생했습니다.', - clicked: true, - }); + const queryClient = useQueryClient(); + + const { mutate } = useMutation( + (data: IcoinPolicy): Promise => { + return instanceInManage.post(`/coinpolicy`, data); } - setModal({ modalName: null }); + ); + + const editCoinPolicyHandler = () => { + mutate(props, { + onSuccess: () => { + setSnackBar({ + toastName: 'edit coinpolicy', + severity: 'success', + message: '새로운 재화 정책이 등록되었습니다.', + clicked: true, + }); + queryClient.invalidateQueries({ queryKey: 'coinPolicyHistory' }); + setModal({ modalName: null }); + }, + onError: () => { + setSnackBar({ + toastName: 'edit coinpolicy', + severity: 'error', + message: 'API 요청에 문제가 발생했습니다.', + clicked: true, + }); + }, + }); }; return ( @@ -63,10 +76,7 @@ export default function AdminEditCoinPolicyModal(props: IcoinPolicy) {
-
)}
-

⚠ 선물한 아이템은 환불 및 취소가 불가합니다 ⚠

+

⚠ 선물은 환불 및 취소가 불가합니다 ⚠

diff --git a/components/mode/modeWraps/StoreModeWrap.tsx b/components/mode/modeWraps/StoreModeWrap.tsx index c5347dc87..d1d2e7b58 100644 --- a/components/mode/modeWraps/StoreModeWrap.tsx +++ b/components/mode/modeWraps/StoreModeWrap.tsx @@ -1,4 +1,3 @@ -import Image from 'next/image'; import { Dispatch, SetStateAction } from 'react'; import { useSetRecoilState } from 'recoil'; import { Modal } from 'types/modalTypes'; diff --git a/components/store/InventoryList.tsx b/components/store/InventoryList.tsx index ae2c29185..c1837a7d3 100644 --- a/components/store/InventoryList.tsx +++ b/components/store/InventoryList.tsx @@ -7,7 +7,7 @@ import { InvetoryItem } from 'components/store/InventoryItem'; import styles from 'styles/store/Inventory.module.scss'; function fetchInventoryData(page: number) { - if (page === null) page = 0; + if (page === null) page = 1; return instance.get(`/pingpong/items?page=${page}&size=${8}`).then((res) => { return res.data; }); diff --git a/constants/store/purchaseAlertMessage.ts b/constants/store/purchaseAlertMessage.ts new file mode 100644 index 000000000..fe6602ba8 --- /dev/null +++ b/constants/store/purchaseAlertMessage.ts @@ -0,0 +1,20 @@ +const SAD_EMOJI = '(。•́︿•̀。)' + ' '; +const HAPPY_EMOJI = '¡¡¡( •̀ ᴗ •́ )و!!!' + ' '; + +export const PURCHASE_ALERT_MESSAGE = { + COMMON: { + ITEM_ERROR: SAD_EMOJI + '해당 아이템이 없습니다.', + OUTDATED_ERROR: SAD_EMOJI + '지금은 구매할 수 없는 아이템입니다.', + COIN_ERROR: SAD_EMOJI + 'GG코인이 부족합니다.', + KAKAO_USER_ERROR: SAD_EMOJI + '카카오 유저는 상점을 이용할 수 없습니다.', + }, + BUY: { + SUCCESS: HAPPY_EMOJI + '구매 완료', + USER_ERROR: 'USER NOT FOUND', + }, + GIFT: { + EMPTY_RECIPIENT: SAD_EMOJI + '선물할 유저를 선택해주세요.', + RECIPIENT_ERROR: SAD_EMOJI + '선물하려는 유저가 없습니다', + KAKAO_RECIPIENT_ERROR: SAD_EMOJI + '카카오 유저에게는 선물할 수 없습니다.', + }, +}; diff --git a/pages/store.tsx b/pages/store.tsx index f8f55ccfe..a5e772019 100644 --- a/pages/store.tsx +++ b/pages/store.tsx @@ -1,45 +1,39 @@ -import { useEffect, useState } from 'react'; -import { useRecoilState, useResetRecoilState } from 'recoil'; +import { useState } from 'react'; +import { useQuery } from 'react-query'; +import { useSetRecoilState } from 'recoil'; import { StoreMode } from 'types/storeTypes'; import { ICoin } from 'types/userTypes'; -import { updateCoinState } from 'utils/recoil/updateCoin'; +import { instance } from 'utils/axios'; +import { errorState } from 'utils/recoil/error'; import { StoreModeWrap } from 'components/mode/modeWraps/StoreModeWrap'; import { InventoryList } from 'components/store/InventoryList'; import ItemsList from 'components/store/purchase/ItemsList'; -import useAxiosGet from 'hooks/useAxiosGet'; import styles from 'styles/store/StoreContainer.module.scss'; export default function Store() { const [mode, setMode] = useState('BUY'); - const [coin, setCoin] = useState({ coin: 0 }); - const [reloadCoin, updateCoin] = useRecoilState(updateCoinState); - const resetUpdateCoinState = useResetRecoilState(updateCoinState); + const dummyCoin = { coin: 0 }; // data가 undefined일 때 대비 + const setError = useSetRecoilState(errorState); - const getCoin = useAxiosGet({ - url: '/pingpong/users/coin', - setState: setCoin, - err: 'JY01', - type: 'setError', - }); - - useEffect(() => { - getCoin(); - return () => { - resetUpdateCoinState(); - }; - }, []); + const { data, isError } = useQuery( + 'coin', + () => instance.get('/pingpong/users/coin').then((res) => res.data), + { retry: 1 } + ); - useEffect(() => { - if (reloadCoin) { - getCoin(); - updateCoin(false); - } - }, [reloadCoin]); + if (isError) { + setError('JY01'); + } + if (!data) return null; return (

GG Store

- +
{mode === 'BUY' ? : }
diff --git a/styles/Layout/Header.module.scss b/styles/Layout/Header.module.scss index 067599d6e..07b7a27f3 100644 --- a/styles/Layout/Header.module.scss +++ b/styles/Layout/Header.module.scss @@ -82,7 +82,7 @@ .notiCount { font-family: $common-en-font; - font-size: 0.25rem; + font-size: 0.7rem; font-weight: 400; } diff --git a/styles/admin/modal/AdminNoti.module.scss b/styles/admin/modal/AdminNoti.module.scss index 1e8699a51..8629c2954 100644 --- a/styles/admin/modal/AdminNoti.module.scss +++ b/styles/admin/modal/AdminNoti.module.scss @@ -77,6 +77,7 @@ display: flex; width: 430px; height: 202px; + font-size: 1rem; resize: none; background: #f9fafb; border: 0.5px solid #ced4da; @@ -128,7 +129,7 @@ .adminSearchBar { display: flex; width: 275px; - height: 33px; + height: 40px; cursor: text; background: white; border: 0.5px solid #ced4da; @@ -138,20 +139,20 @@ input { all: unset; width: calc(100% - 4rem); - padding: 0.5rem 0.8rem; margin-left: 0.5rem; + font-size: 0.8rem; color: black; } .icons { span { display: flex; - height: 33px; + height: 40px; margin-left: 30px; font-size: 1.2rem; align-items: center; } .reset { - height: 33px; + height: 40px; color: gray; cursor: pointer; } @@ -160,6 +161,7 @@ z-index: 999; width: 100%; padding: 0 0.8rem 0.5rem 0.5rem; + font-size: 0.8rem; color: black; background: white; border: 1px solid #ced4da; diff --git a/styles/common.scss b/styles/common.scss index e4a96710e..d8b94e577 100644 --- a/styles/common.scss +++ b/styles/common.scss @@ -281,7 +281,6 @@ $vip-background: linear-gradient( position: relative; width: $size; height: $size; - background: linear-gradient(to right bottom, $g1-top, $g1-bottom); border-radius: 50%; img { border-radius: 50%; diff --git a/styles/modal/match/MatchManualModal.module.scss b/styles/modal/match/MatchManualModal.module.scss index d0979e286..e6b70d2b2 100644 --- a/styles/modal/match/MatchManualModal.module.scss +++ b/styles/modal/match/MatchManualModal.module.scss @@ -4,11 +4,14 @@ @include modalContainer('DARKPURPLE'); width: 80vw; max-width: 20rem; + justify-content: flex-start; + padding-top: 2rem; + padding-bottom: 2rem; } .title { @include modalTitle; // font-family, font-size, font-weight, letter-spacing - margin: 2.75rem 0; + margin-bottom: 2rem; font-size: 2.25rem; color: #f8f8ff; text-shadow: 0px 0px 12px #ffffff; @@ -16,13 +19,12 @@ .matchRadioBoxWrap { box-sizing: border-box; - width: 100%; + width: 90%; } .ruleList { width: 100%; - height: 60vh; - max-height: 285px; + height: 45vh; padding: 1rem; margin: 0 0.6rem 1.75rem; overflow-y: scroll; @@ -30,8 +32,8 @@ font-weight: 700; color: black; list-style: none; - background: #ffffff; - border-radius: 0px 0px 10px 10px; + background: #fdebf8; + border-radius: 10px; > li { margin-top: 1.2rem; @@ -64,9 +66,11 @@ margin-left: 0.8rem; font-size: 0.9rem; font-weight: 400; + word-break: keep-all; > li { margin: 0.4rem 0; + line-height: 150%; white-space: pre-wrap; list-style: square; } diff --git a/styles/modal/store/CoinHistoryDetails.module.scss b/styles/modal/store/CoinHistoryDetails.module.scss index c4f2352ae..f3100a6a3 100644 --- a/styles/modal/store/CoinHistoryDetails.module.scss +++ b/styles/modal/store/CoinHistoryDetails.module.scss @@ -24,6 +24,7 @@ .content { .history { color: black; + word-break: keep-all; } .date { font-size: 0.8rem; diff --git a/styles/modal/store/StoreManualModal.module.scss b/styles/modal/store/StoreManualModal.module.scss index 8873d8f97..0030f2407 100644 --- a/styles/modal/store/StoreManualModal.module.scss +++ b/styles/modal/store/StoreManualModal.module.scss @@ -4,11 +4,14 @@ @include modalContainer('DARKPURPLE'); width: 80vw; max-width: 20rem; + justify-content: flex-start; + padding-top: 2rem; + padding-bottom: 2rem; } .title { @include modalTitle; // font-family, font-size, font-weight, letter-spacing - margin: 2.75rem 0; + margin-bottom: 2rem; font-size: 2.25rem; color: #f8f8ff; text-shadow: 0px 0px 12px #ffffff; @@ -21,8 +24,7 @@ .ruleList { width: 100%; - height: 60vh; - max-height: 285px; + height: 45vh; padding: 1rem; margin: 0 0.6rem 1.75rem; overflow-y: scroll; @@ -30,8 +32,8 @@ font-weight: 700; color: black; list-style: none; - background: #ffffff; - border-radius: 0px 0px 10px 10px; + background: #fdebf8; + border-radius: 10px; > li { margin-top: 1.2rem; @@ -41,8 +43,9 @@ .ruleTitle { display: flex; - align-items: center; + word-break: keep-all; vertical-align: middle; + align-items: center; &.withIcon { :first-child { min-width: 1.5rem; @@ -64,16 +67,18 @@ margin-left: 0.8rem; font-size: 0.9rem; font-weight: 400; + word-break: keep-all; > li { margin: 0.4rem 0; + line-height: 130%; white-space: pre-wrap; list-style: square; } .ruleContent { padding-left: 0.5rem; line-height: 170%; - list-style-type: '-'; + list-style-type: '- '; } } diff --git a/types/admin/adminCoinTypes.ts b/types/admin/adminCoinTypes.ts index 21363864e..453e342f6 100644 --- a/types/admin/adminCoinTypes.ts +++ b/types/admin/adminCoinTypes.ts @@ -14,9 +14,3 @@ export interface IcoinPolicyHistory { rankLose: number; createdAt: Date; } - -export interface IcoinPolicyHistoryTable { - coinPolicyList: Array; - totalPage: number; - currentPage: number; -} diff --git a/utils/recoil/updateCoin.ts b/utils/recoil/updateCoin.ts deleted file mode 100644 index e1dc2bc45..000000000 --- a/utils/recoil/updateCoin.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { atom } from 'recoil'; -import { v1 } from 'uuid'; - -export const updateCoinState = atom({ - key: `updateCoinState/${v1()}`, - default: false, -});