From 6c356d6cbbffa7493b57f30953a5a9ed1f65cdc0 Mon Sep 17 00:00:00 2001 From: Leejin-Yang Date: Sat, 14 Oct 2023 02:07:20 +0900 Subject: [PATCH 1/6] =?UTF-8?q?chore:=20=EB=A1=9C=EC=BB=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=99=98=EA=B2=BD=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/index.ts | 6 +++++- vite.config.ts | 21 +++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/constants/index.ts b/src/constants/index.ts index 776f2a8..558842c 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,14 +1,18 @@ -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 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 { diff --git a/vite.config.ts b/vite.config.ts index f725ec7..3742650 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,19 +1,16 @@ -import { defineConfig, loadEnv } from 'vite'; +import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; // https://vitejs.dev/config/ -export default defineConfig(({ mode }) => { - const env = loadEnv(mode, process.cwd(), ''); - return { - plugins: [react(), vanillaExtractPlugin()], - server: { - proxy: { - '/api': { - target: env.VITE_API_URL, - changeOrigin: true, - }, +export default defineConfig({ + plugins: [react(), vanillaExtractPlugin()], + server: { + proxy: { + '/api': { + target: 'http://localhost:8080', + changeOrigin: true, }, }, - }; + }, }); From 7af684db4d341f656806b5ce3ed5444c04a3399d Mon Sep 17 00:00:00 2001 From: Leejin-Yang Date: Sat, 14 Oct 2023 02:43:24 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EB=B0=B0=EB=84=88=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EC=9A=94=EC=B2=AD=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/banner.ts | 16 ++++++++++++++++ src/constants/index.ts | 13 +++++++++++++ src/hooks/queries/index.ts | 1 + src/hooks/queries/useBannerQuery.ts | 10 ++++++++++ src/mocks/browser.ts | 4 +++- src/mocks/data/banners.json | 17 +++++++++++++++++ src/mocks/handlers/bannerHandlers.ts | 9 +++++++++ src/mocks/handlers/index.ts | 1 + src/mocks/handlers/loginHandlers.ts | 5 +---- 9 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 src/apis/banner.ts create mode 100644 src/hooks/queries/useBannerQuery.ts create mode 100644 src/mocks/data/banners.json create mode 100644 src/mocks/handlers/bannerHandlers.ts 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 558842c..7ecd198 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -2,6 +2,10 @@ 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', @@ -47,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/mocks/handlers/loginHandlers.ts b/src/mocks/handlers/loginHandlers.ts index 89119a8..42e4bd1 100644 --- a/src/mocks/handlers/loginHandlers.ts +++ b/src/mocks/handlers/loginHandlers.ts @@ -10,10 +10,7 @@ export const loginHandlers = [ return res(ctx.status(200), ctx.cookie('mockSessionId', 'abc')); } - return res( - ctx.status(401), - ctx.json({ code: 401, message: 'Unauthorized - funeat' }) - ); + return res(ctx.json({ code: 401, message: 'Unauthorized - funeat' })); }), rest.get('/api/admin/logged-check', (req, res, ctx) => { From 77c3b9eefc307261fa00938e43cd202f34bff06f Mon Sep 17 00:00:00 2001 From: Leejin-Yang Date: Sat, 14 Oct 2023 02:44:02 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20BannerRow=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/BannerRow/BannerRow.tsx | 39 +++++++++++++++++++ .../components/BannerRow/bannerRow.css.ts | 19 +++++++++ .../Banners/components/BannerRow/index.ts | 3 ++ 3 files changed, 61 insertions(+) create mode 100644 src/pages/Banners/components/BannerRow/BannerRow.tsx create mode 100644 src/pages/Banners/components/BannerRow/bannerRow.css.ts create mode 100644 src/pages/Banners/components/BannerRow/index.ts diff --git a/src/pages/Banners/components/BannerRow/BannerRow.tsx b/src/pages/Banners/components/BannerRow/BannerRow.tsx new file mode 100644 index 0000000..a84282d --- /dev/null +++ b/src/pages/Banners/components/BannerRow/BannerRow.tsx @@ -0,0 +1,39 @@ +import { Banner } from '../../../../apis/banner'; +import { td } from '../../../../components/Table/table.css'; + +import { bannerImage, bannerLink, deleteButton } from './bannerRow.css'; + +interface BannerRowProps { + banner: Banner; +} + +const BannerRow = ({ banner }: BannerRowProps) => { + const { id, image, link } = banner; + + // TODO + + return ( + + {id} + + {`배너 + + + + {link} + + + + + + + ); +}; + +export default BannerRow; diff --git a/src/pages/Banners/components/BannerRow/bannerRow.css.ts b/src/pages/Banners/components/BannerRow/bannerRow.css.ts new file mode 100644 index 0000000..fa4fabc --- /dev/null +++ b/src/pages/Banners/components/BannerRow/bannerRow.css.ts @@ -0,0 +1,19 @@ +import { style } from '@vanilla-extract/css'; + +export const bannerImage = style({ + width: '100%', + height: 'auto', +}); + +export const bannerLink = style({ + textDecoration: 'underline', +}); + +export const deleteButton = style({ + width: 60, + height: 45, + lineHeight: '45px', + fontSize: 16, + border: '1px solid #ccc', + borderRadius: 8, +}); diff --git a/src/pages/Banners/components/BannerRow/index.ts b/src/pages/Banners/components/BannerRow/index.ts new file mode 100644 index 0000000..042be33 --- /dev/null +++ b/src/pages/Banners/components/BannerRow/index.ts @@ -0,0 +1,3 @@ +import BannerRow from './BannerRow'; + +export default BannerRow; From 4cb9395541f8549750b25c9fc13b709abb12dfbe Mon Sep 17 00:00:00 2001 From: Leejin-Yang Date: Sat, 14 Oct 2023 02:44:17 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EB=B0=B0=EB=84=88=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Banners/Banners.tsx | 55 ++++++++++++++++++++++++++++++ src/pages/Banners/banners.css.ts | 57 ++++++++++++++++++++++++++++++++ src/pages/Banners/index.ts | 3 ++ src/pages/index.tsx | 4 ++- 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/pages/Banners/Banners.tsx create mode 100644 src/pages/Banners/banners.css.ts create mode 100644 src/pages/Banners/index.ts diff --git a/src/pages/Banners/Banners.tsx b/src/pages/Banners/Banners.tsx new file mode 100644 index 0000000..44c3f63 --- /dev/null +++ b/src/pages/Banners/Banners.tsx @@ -0,0 +1,55 @@ +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(); + + if (!banners) { + return null; + } + + return ( + <> +
+

배너

+ +
+
+

+ 총 {banners.length.toLocaleString('ko-KR')}개의 상품이 검색되었습니다. +

+ + + + + {banners.map((banner) => ( + + ))} + +
+
+ + ); +}; + +export default Banners; diff --git a/src/pages/Banners/banners.css.ts b/src/pages/Banners/banners.css.ts new file mode 100644 index 0000000..81f6f72 --- /dev/null +++ b/src/pages/Banners/banners.css.ts @@ -0,0 +1,57 @@ +import { style } from '@vanilla-extract/css'; + +export const container = style({ + maxWidth: 1200, + margin: '0 auto', +}); + +export const titleWrapper = style([ + container, + { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + height: 60, + padding: '0 20px', + }, +]); + +export const title = style({ + height: 60, + lineHeight: '60px', + fontSize: 28, + fontWeight: 700, +}); + +export const searchSection = style([ + container, + { + padding: '0 20px', + marginTop: 20, + }, +]); + +export const addButton = style({ + width: 120, + height: 45, + lineHeight: '45px', + fontSize: 16, + border: '1px solid #ccc', + borderRadius: 8, +}); + +export const tableWrapper = style([ + container, + { + width: '100%', + padding: '0 20px', + marginTop: 20, + }, +]); + +export const tableTitle = style({ + height: 60, + lineHeight: '60px', + fontSize: 18, + fontWeight: 500, +}); diff --git a/src/pages/Banners/index.ts b/src/pages/Banners/index.ts new file mode 100644 index 0000000..91c8892 --- /dev/null +++ b/src/pages/Banners/index.ts @@ -0,0 +1,3 @@ +import Banners from './Banners'; + +export default Banners; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 06761ec..2418390 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,15 +1,16 @@ import { createBrowserRouter } from 'react-router-dom'; import Layout from './Layout'; +import AuthLayout from './Layout/AuthLayout'; import Home from './Home'; import Products from './Products'; import Reviews from './Reviews'; +import Banners from './Banners'; import { ROUTE } from '../constants'; import PageProvider from '../contexts/PageContext'; import ProductSearchQueryProvider from './Products/contexts/ProductSearchQueryContext'; import ReviewSearchQueryProvider from './Reviews/contexts/ReviewSearchQueryContext'; -import AuthLayout from './Layout/AuthLayout'; const router = createBrowserRouter([ { @@ -46,6 +47,7 @@ const router = createBrowserRouter([ ), }, + { path: ROUTE.BANNER, element: }, ], }, ]); From ca740baa72260bc5f468d848e80f527026ccee50 Mon Sep 17 00:00:00 2001 From: Leejin-Yang Date: Sat, 14 Oct 2023 02:52:01 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20todo=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Banners/Banners.tsx | 2 ++ src/pages/Banners/components/BannerRow/BannerRow.tsx | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pages/Banners/Banners.tsx b/src/pages/Banners/Banners.tsx index 44c3f63..d0d08e1 100644 --- a/src/pages/Banners/Banners.tsx +++ b/src/pages/Banners/Banners.tsx @@ -22,6 +22,8 @@ const Banners = () => { const { data: banners } = useBannerQuery(); const { isOpen, onOpen, onClose } = useDisclosure(); + // TODO 배너 추가 모달 + if (!banners) { return null; } diff --git a/src/pages/Banners/components/BannerRow/BannerRow.tsx b/src/pages/Banners/components/BannerRow/BannerRow.tsx index a84282d..2a79eaf 100644 --- a/src/pages/Banners/components/BannerRow/BannerRow.tsx +++ b/src/pages/Banners/components/BannerRow/BannerRow.tsx @@ -10,7 +10,7 @@ interface BannerRowProps { const BannerRow = ({ banner }: BannerRowProps) => { const { id, image, link } = banner; - // TODO + // TODO 배너 삭제 클릭 이벤트 return ( @@ -24,11 +24,7 @@ const BannerRow = ({ banner }: BannerRowProps) => { - From bcdd9d5b6189d748ff9b675733fbf9baf9b6dc80 Mon Sep 17 00:00:00 2001 From: Leejin-Yang Date: Sat, 14 Oct 2023 02:54:10 +0900 Subject: [PATCH 6/6] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=AA=A9=20=ED=95=B8=EB=93=A4=EB=9F=AC=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mocks/handlers/loginHandlers.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mocks/handlers/loginHandlers.ts b/src/mocks/handlers/loginHandlers.ts index 42e4bd1..89119a8 100644 --- a/src/mocks/handlers/loginHandlers.ts +++ b/src/mocks/handlers/loginHandlers.ts @@ -10,7 +10,10 @@ export const loginHandlers = [ return res(ctx.status(200), ctx.cookie('mockSessionId', 'abc')); } - return res(ctx.json({ code: 401, message: 'Unauthorized - funeat' })); + return res( + ctx.status(401), + ctx.json({ code: 401, message: 'Unauthorized - funeat' }) + ); }), rest.get('/api/admin/logged-check', (req, res, ctx) => {