From 9edaa735870781c86c163c1667feac8d0a674bad Mon Sep 17 00:00:00 2001 From: jinhojang6 Date: Fri, 27 Sep 2024 00:40:01 +0900 Subject: [PATCH] feat: implement react query --- apis/operators/useGetOperators.ts | 31 +++ common/api.ts | 55 ++++++ package.json | 2 + .../Dashboard/OperatorGrid/OperatorGrid.tsx | 74 +++++--- .../Dashboard/DashboardContainer.tsx | 6 +- src/pages/_app.tsx | 177 +++++++++--------- yarn.lock | 94 +++++++++- 7 files changed, 323 insertions(+), 116 deletions(-) create mode 100644 apis/operators/useGetOperators.ts create mode 100644 common/api.ts diff --git a/apis/operators/useGetOperators.ts b/apis/operators/useGetOperators.ts new file mode 100644 index 0000000..be53ab2 --- /dev/null +++ b/apis/operators/useGetOperators.ts @@ -0,0 +1,31 @@ +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { api } from '../../common/api' + +const useQueryOptions = { + refetchOnWindowFocus: false, + staleTime: 60 * 1000, + retry: 1, +} + +export const fetchData = async () => { + return await api.get('/operators').then((res) => res.data) +} + +const useGetOperators = () => { + const queryKey = ['getOperators'] + const queryClient = useQueryClient() + + const updateCache = (newData: any) => { + queryClient.setQueryData(queryKey, newData) + } + + const response = useQuery({ + queryKey: queryKey, + queryFn: fetchData, + ...useQueryOptions, + }) + + return { ...response, updateCache } +} + +export default useGetOperators diff --git a/common/api.ts b/common/api.ts new file mode 100644 index 0000000..ba59cae --- /dev/null +++ b/common/api.ts @@ -0,0 +1,55 @@ +import axios, { AxiosError } from 'axios' + +export const API_BASE = 'https://exit-test-567058b69f45.herokuapp.com/api' + +const api = axios.create({ + baseURL: API_BASE, + withCredentials: true, +}) + +api.interceptors.request.use( + async (config) => { + return config + }, + (error) => { + Promise.reject(error) + }, +) + +api.interceptors.response.use( + (response) => { + return response + }, + async (error: AxiosError) => { + const originalRequest = error.config + + if (error.response?.status === 401) { + try { + const refreshToken = await localStorage.getItem('refreshToken') + await api + .post('/token/refresh', { + refreshToken, + }) + .then(async (res) => { + api.defaults.headers.common['Authorization'] = + 'Bearer ' + res.data.token?.accessToken + return await axios(originalRequest as any) + }) + } catch (e) { + // localStorage.setItem('refreshToken', ''); + // deleteCookie('refresh-token'); + } + } + + if (error.response?.status === 429) { + return Promise.reject(error) + } + return Promise.reject(error) + }, +) + +api.defaults.withCredentials = true + +export const fetcher = (url: string) => api.get(url).then((res) => res) + +export { api } diff --git a/package.json b/package.json index 6ce9d58..ffd7350 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@mdx-js/loader": "^2.3.0", "@mdx-js/react": "^2.3.0", "@next/mdx": "^13.5.5", + "@tanstack/react-query": "^5.56.2", "@types/mdx": "^2.0.8", "@vercel/og": "^0.5.4", "axios": "^1.4.0", @@ -44,6 +45,7 @@ "remark-gfm": "3.0.1" }, "devDependencies": { + "@tanstack/eslint-plugin-query": "^5.57.2", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/src/components/Dashboard/OperatorGrid/OperatorGrid.tsx b/src/components/Dashboard/OperatorGrid/OperatorGrid.tsx index 7122b15..6457376 100644 --- a/src/components/Dashboard/OperatorGrid/OperatorGrid.tsx +++ b/src/components/Dashboard/OperatorGrid/OperatorGrid.tsx @@ -12,7 +12,9 @@ interface Operator { isPinned: boolean } -interface OperatorGridProps {} +interface OperatorGridProps { + isLoading: boolean +} const operators: Operator[] = [ { @@ -73,7 +75,9 @@ const operators: Operator[] = [ }, ] -const OperatorGrid: React.FC = () => { +const OperatorGrid: React.FC = ({ + isLoading, +}: OperatorGridProps) => { return (
@@ -107,32 +111,38 @@ const OperatorGrid: React.FC = () => { - {operators.map((operator) => ( - - - - - - {operator.name} - - - {operator.pointsPerHour} rp - - - - - {operator.isStaked ? 'Unstake' : 'Stake'} - - - {operator.isPinned ? ( - Pinned - ) : ( - Unpinned - )} - - - - ))} + {isLoading + ? Array.from({ length: 8 }).map((_, index) => ( + + + + )) + : operators.map((operator) => ( + + + + + + {operator.name} + + + {operator.pointsPerHour} rp + + + + + {operator.isStaked ? 'Unstake' : 'Stake'} + + + {operator.isPinned ? ( + Pinned + ) : ( + Unpinned + )} + + + + ))} ) @@ -288,4 +298,12 @@ const ActionButton = styled.button<{ isStaked: boolean }>` cursor: pointer; ` +const Placeholder = styled.div` + width: 100%; + aspect-ratio: 1; + background-color: var(--grey-900); + border-radius: 8px; + opacity: 0.5; +` + export default OperatorGrid diff --git a/src/containers/Dashboard/DashboardContainer.tsx b/src/containers/Dashboard/DashboardContainer.tsx index cd4b408..4f73418 100644 --- a/src/containers/Dashboard/DashboardContainer.tsx +++ b/src/containers/Dashboard/DashboardContainer.tsx @@ -4,6 +4,7 @@ import { ProgressBar } from '@/components/Dashboard/ProgressBar' import { breakpoints } from '@/configs/ui.configs' import styled from '@emotion/styled' import React from 'react' +import useGetOperators from '../../../apis/operators/useGetOperators' export type DashboardPageProps = React.DetailedHTMLProps< React.HTMLAttributes, @@ -14,6 +15,9 @@ const DashboardContainer: React.FC = ({ children, ...props }) => { + const { data, isLoading } = useGetOperators() + console.log(data) + return ( @@ -22,7 +26,7 @@ const DashboardContainer: React.FC = ({ - + diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 9d66175..9913c3c 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,5 +1,6 @@ import { DefaultLayout } from '@/layouts/DefaultLayout' import { css, Global } from '@emotion/react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { NextComponentType, NextPageContext } from 'next' import type { AppProps } from 'next/app' import Head from 'next/head' @@ -18,6 +19,8 @@ type AppLayoutProps

= AppProps & { Component: NextLayoutComponentType } +const queryClient = new QueryClient() + export default function App({ Component, pageProps }: AppLayoutProps) { const getLayout = Component.getLayout || @@ -25,93 +28,95 @@ export default function App({ Component, pageProps }: AppLayoutProps) { return ( - - Logos Ordianals - + + Logos Ordianals + + + - - - {getLayout()} + {getLayout()} + ) } diff --git a/yarn.lock b/yarn.lock index 1a9003a..2ff48d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -235,7 +235,7 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== -"@eslint-community/eslint-utils@^4.2.0": +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== @@ -473,6 +473,25 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" +"@tanstack/eslint-plugin-query@^5.57.2": + version "5.57.2" + resolved "https://registry.yarnpkg.com/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.57.2.tgz#7f47055f3855a94b65c8b94e20864c5d60a59070" + integrity sha512-JEvKhL4cRV0vMwvjg8LSYE7tiaxr1zzBEgNC9n/vexrUKkLeM7rxt4iIXMYphMGzuTYdSBYBo5CQy1Iio7LhSA== + dependencies: + "@typescript-eslint/utils" "^8.3.0" + +"@tanstack/query-core@5.56.2": + version "5.56.2" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.56.2.tgz#2def2fb0290cd2836bbb08afb0c175595bb8109b" + integrity sha512-gor0RI3/R5rVV3gXfddh1MM+hgl0Z4G7tj6Xxpq6p2I03NGPaJ8dITY9Gz05zYYb/EJq9vPas/T4wn9EaDPd4Q== + +"@tanstack/react-query@^5.56.2": + version "5.56.2" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.56.2.tgz#3a0241b9d010910905382f5e99160997b8795f91" + integrity sha512-SR0GzHVo6yzhN72pnRhkEFRAHMsUo5ZPzAxfTMvUxFIDVS6W9LYUp6nXW3fcHVdg0ZJl8opSH85jqahvm6DSVg== + dependencies: + "@tanstack/query-core" "5.56.2" + "@types/acorn@^4.0.0": version "4.0.6" resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" @@ -619,11 +638,24 @@ "@typescript-eslint/types" "7.2.0" "@typescript-eslint/visitor-keys" "7.2.0" +"@typescript-eslint/scope-manager@8.7.0": + version "8.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.7.0.tgz#90ee7bf9bc982b9260b93347c01a8bc2b595e0b8" + integrity sha512-87rC0k3ZlDOuz82zzXRtQ7Akv3GKhHs0ti4YcbAJtaomllXoSO8hi7Ix3ccEvCd824dy9aIX+j3d2UMAfCtVpg== + dependencies: + "@typescript-eslint/types" "8.7.0" + "@typescript-eslint/visitor-keys" "8.7.0" + "@typescript-eslint/types@7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.2.0.tgz#0feb685f16de320e8520f13cca30779c8b7c403f" integrity sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA== +"@typescript-eslint/types@8.7.0": + version "8.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.7.0.tgz#21d987201c07b69ce7ddc03451d7196e5445ad19" + integrity sha512-LLt4BLHFwSfASHSF2K29SZ+ZCsbQOM+LuarPjRUuHm+Qd09hSe3GCeaQbcCr+Mik+0QFRmep/FyZBO6fJ64U3w== + "@typescript-eslint/typescript-estree@7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz#5beda2876c4137f8440c5a84b4f0370828682556" @@ -638,6 +670,30 @@ semver "^7.5.4" ts-api-utils "^1.0.1" +"@typescript-eslint/typescript-estree@8.7.0": + version "8.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.7.0.tgz#6c7db6baa4380b937fa81466c546d052f362d0e8" + integrity sha512-MC8nmcGHsmfAKxwnluTQpNqceniT8SteVwd2voYlmiSWGOtjvGXdPl17dYu2797GVscK30Z04WRM28CrKS9WOg== + dependencies: + "@typescript-eslint/types" "8.7.0" + "@typescript-eslint/visitor-keys" "8.7.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/utils@^8.3.0": + version "8.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.7.0.tgz#cef3f70708b5b5fd7ed8672fc14714472bd8a011" + integrity sha512-ZbdUdwsl2X/s3CiyAu3gOlfQzpbuG3nTWKPoIvAu1pu5r8viiJvv2NPN2AqArL35NCYtw/lrPPfM4gxrMLNLPw== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.7.0" + "@typescript-eslint/types" "8.7.0" + "@typescript-eslint/typescript-estree" "8.7.0" + "@typescript-eslint/visitor-keys@7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz#5035f177752538a5750cca1af6044b633610bf9e" @@ -646,6 +702,14 @@ "@typescript-eslint/types" "7.2.0" eslint-visitor-keys "^3.4.1" +"@typescript-eslint/visitor-keys@8.7.0": + version "8.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.7.0.tgz#5e46f1777f9d69360a883c1a56ac3c511c9659a8" + integrity sha512-b1tx0orFCCh/THWPQa2ZwWzvOeyzzp36vkJYOpVg0u8UVOIsfVrnuC9FqAw9gRKn+rG2VmWQ/zDJZzkxUnj/XQ== + dependencies: + "@typescript-eslint/types" "8.7.0" + eslint-visitor-keys "^3.4.3" + "@ungap/structured-clone@^1.0.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" @@ -1712,6 +1776,17 @@ fast-glob@^3.2.9, fast-glob@^3.3.1: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -3596,6 +3671,13 @@ minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -4286,6 +4368,11 @@ semver@^7.5.4: dependencies: lru-cache "^6.0.0" +semver@^7.6.0: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + set-function-name@^2.0.0, set-function-name@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" @@ -4633,6 +4720,11 @@ ts-api-utils@^1.0.1: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + ts-easing@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec"