diff --git a/src/apis/banner.ts b/src/apis/banner.ts new file mode 100644 index 0000000..fadc8c0 --- /dev/null +++ b/src/apis/banner.ts @@ -0,0 +1,16 @@ +import { API_SERVICE_URL } from '../constants'; +import { fetchApi } from './fetchApi'; + +export interface Banner { + id: number; + image: string; + link: string; +} + +export const getBanners = async () => { + const response = await fetchApi(`${API_SERVICE_URL}/banners`, { + method: 'GET', + }); + const data: Banner[] = await response.json(); + return data; +}; diff --git a/src/constants/index.ts b/src/constants/index.ts index 776f2a8..7ecd198 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,14 +1,22 @@ -export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; +export const API_BASE_URL = import.meta.env.PROD + ? import.meta.env.VITE_API_BASE_URL + : '/api/admin'; + +export const API_SERVICE_URL = import.meta.env.PROD + ? import.meta.env.VITE_API_SERVICE_URL + : '/api'; export const ROUTE = { HOME: '/', PRODUCT: '/products', REVIEW: '/reviews', + BANNER: '/banners', }; export const ROUTES = [ { path: ROUTE.PRODUCT, name: '상품' }, { path: ROUTE.REVIEW, name: '리뷰' }, + { path: ROUTE.BANNER, name: '배너' }, ]; export interface Column { @@ -43,3 +51,12 @@ export const PRODUCT_SEARCH_COLUMNS: Column[] = [ { id: 1, name: '아이디', align: 'right' }, { id: 2, name: '상품명' }, ]; + +export const BANNER_COLUMNS_WIDTH = [10, 40, 40, 10]; + +export const BANNER_COLUMNS: Column[] = [ + { id: 1, name: '아이디', align: 'right' }, + { id: 2, name: '이미지', align: 'center' }, + { id: 3, name: '링크' }, + { id: 4, name: '', align: 'center' }, +]; diff --git a/src/hooks/queries/index.ts b/src/hooks/queries/index.ts index d708098..e14b723 100644 --- a/src/hooks/queries/index.ts +++ b/src/hooks/queries/index.ts @@ -2,6 +2,7 @@ export * from './useProductQuery'; export * from './useCategoryQuery'; export * from './useReviewQuery'; export * from './useLoginQuery'; +export * from './useBannerQuery'; export * from './useLoginMutation'; export * from './useProductMutation'; diff --git a/src/hooks/queries/useBannerQuery.ts b/src/hooks/queries/useBannerQuery.ts new file mode 100644 index 0000000..720621d --- /dev/null +++ b/src/hooks/queries/useBannerQuery.ts @@ -0,0 +1,10 @@ +import { useQuery } from '@tanstack/react-query'; + +import { getBanners } from '../../apis/banner'; + +export const useBannerQuery = () => { + return useQuery({ + queryKey: ['banners'], + queryFn: () => getBanners(), + }); +}; diff --git a/src/mocks/browser.ts b/src/mocks/browser.ts index 2a3d53f..ca6e970 100644 --- a/src/mocks/browser.ts +++ b/src/mocks/browser.ts @@ -5,11 +5,13 @@ import { productHandlers, reviewHandlers, loginHandlers, + bannerHandlers, } from './handlers'; export const worker = setupWorker( ...categoryHandlers, ...productHandlers, ...reviewHandlers, - ...loginHandlers + ...loginHandlers, + ...bannerHandlers ); diff --git a/src/mocks/data/banners.json b/src/mocks/data/banners.json new file mode 100644 index 0000000..346810d --- /dev/null +++ b/src/mocks/data/banners.json @@ -0,0 +1,17 @@ +[ + { + "id": 3, + "image": "https://image.funeat.site/prod/banner.png", + "link": "https://funeat.site" + }, + { + "id": 2, + "image": "https://image.funeat.site/prod/banner.png", + "link": "https://funeat.site/products/food" + }, + { + "id": 1, + "image": "https://image.funeat.site/prod/banner.png", + "link": "https://funeat.site/recipes" + } +] diff --git a/src/mocks/handlers/bannerHandlers.ts b/src/mocks/handlers/bannerHandlers.ts new file mode 100644 index 0000000..c742bd6 --- /dev/null +++ b/src/mocks/handlers/bannerHandlers.ts @@ -0,0 +1,9 @@ +import { rest } from 'msw'; + +import banners from '../data/banners.json'; + +export const bannerHandlers = [ + rest.get('/api/banners', (_, res, ctx) => { + return res(ctx.status(200), ctx.json(banners)); + }), +]; diff --git a/src/mocks/handlers/index.ts b/src/mocks/handlers/index.ts index a8ab793..6f05565 100644 --- a/src/mocks/handlers/index.ts +++ b/src/mocks/handlers/index.ts @@ -2,3 +2,4 @@ export * from './categoryHandlers'; export * from './productHandlers'; export * from './reviewHandlers'; export * from './loginHandlers'; +export * from './bannerHandlers'; diff --git a/src/pages/Banners/Banners.tsx b/src/pages/Banners/Banners.tsx new file mode 100644 index 0000000..d0d08e1 --- /dev/null +++ b/src/pages/Banners/Banners.tsx @@ -0,0 +1,57 @@ +import BannerRow from './components/BannerRow'; + +import { BANNER_COLUMNS, BANNER_COLUMNS_WIDTH } from '../../constants'; +import { + Colgroup, + Table, + TableBody, + TableHeader, +} from '../../components/Table'; +import { useDisclosure } from '../../hooks'; +import { useBannerQuery } from '../../hooks/queries'; + +import { + addButton, + tableTitle, + tableWrapper, + title, + titleWrapper, +} from './banners.css'; + +const Banners = () => { + const { data: banners } = useBannerQuery(); + const { isOpen, onOpen, onClose } = useDisclosure(); + + // TODO 배너 추가 모달 + + if (!banners) { + return null; + } + + return ( + <> +