조민수 조민수 05-06
toast Message + board example Add
@f3c190500628b7c460e87a5b62ea75b356118edb
src/App.tsx
--- src/App.tsx
+++ src/App.tsx
@@ -3,6 +3,7 @@
 import { UserListPage } from './user/UserListPage';
 import {AdminLayout} from "./admin/layout/AdminLayout.tsx";
 import {AdminRoute} from "./admin/route/AdminRoute.tsx";
+import {ToastContainer} from "react-toastify";
 
 type Skin = 'admin' | 'user';
 
@@ -62,6 +63,7 @@
           <UserListPage />
         </UserLayout>
       )}
+      <ToastContainer position="bottom-right" autoClose={3000} />
     </>
   );
 }
 
src/admin/component/EmptyRow.tsx (added)
+++ src/admin/component/EmptyRow.tsx
@@ -0,0 +1,15 @@
+export function EmptyRow({colSpan}: any) {
+    return (
+        <div className="table table_type_cols">
+            <table>
+                <tbody>
+                <tr>
+                    <td colSpan={colSpan} style={{textAlign: "center"}}>
+                        데이터가 없습니다.
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+    );
+}(No newline at end of file)
 
src/admin/component/ImageTable.tsx (added)
+++ src/admin/component/ImageTable.tsx
@@ -0,0 +1,46 @@
+interface ImageTableProps {
+    list: any[];
+}
+
+export const ImageTable = ({list = []}: ImageTableProps) => {
+    if (!list || list.length === 0) return (
+        <ul className="gallery_list">
+            <div className="board1_btn w100per" style={{textAlign: "center"}}>
+                데이터가 없습니다.
+            </div>
+        </ul>
+    );
+
+    return (
+        <ul className="gallery_list">
+            {list.map((item) => (
+                <li key={item.mazId}>
+                    <a href="#" data-mainzone-id={item.mazId}>
+
+                        {item.useYn === "Y" ? (
+                            <span className="status primary">사용</span>
+                        ) : (
+                            <span className="status gray">미사용</span>
+                        )}
+
+                        <div className="images_area">
+                            <img
+                                src={`/uss/ion/pwm/getImage.do?atchFileId=${item.mainzoneImageFile}`}
+                                alt=""
+                            />
+                        </div>
+
+                        <div className="list_content">
+                            <b className="list_title">{item.mazNm}</b>
+                            <ul className="list_info">
+                                <li>작성자 {item.registerId}</li>
+                                <li>노출순서 {item.sort}</li>
+                                <li>{item.regdt}</li>
+                            </ul>
+                        </div>
+                    </a>
+                </li>
+            ))}
+        </ul>
+    );
+}(No newline at end of file)
 
src/admin/component/PageHeader.tsx (added)
+++ src/admin/component/PageHeader.tsx
@@ -0,0 +1,39 @@
+interface PageHeaderProps {
+    title: string;
+    breadcrumb: any[];
+    homeUrl?: string;
+}
+
+export const PageHeader = ({title, breadcrumb, homeUrl}: PageHeaderProps) => {
+    return (
+        <div className="content_title">
+            <div className="left">
+                <h3>{title}</h3>
+            </div>
+            <div className="right">
+                <ol className="breadcrumb">
+                    <li>
+                        <a href={homeUrl || "/cmm/main/mainPage.do"} className="home">
+                            <i></i>
+                        </a>
+                    </li>
+                    {breadcrumb.map((item, idx) => {
+                        const isLast = idx === breadcrumb.length - 1;
+
+                        return (
+                            <li key={idx}>
+                                {isLast ? (
+                                    <strong className="current_location">
+                                        {item.label}
+                                    </strong>
+                                ) : (
+                                    <a href={item.url || "#"}>{item.label}</a>
+                                )}
+                            </li>
+                        );
+                    })}
+                </ol>
+            </div>
+        </div>
+    );
+}
 
src/admin/component/SearchBar.tsx (added)
+++ src/admin/component/SearchBar.tsx
@@ -0,0 +1,65 @@
+import type {ChangeEvent} from 'react'
+
+interface SearchBarProps {
+    searchSortCnd: string
+    searchKeyword: string
+    options: { value: string; label: string }[]
+    onChange: (
+        name: string,
+        value: string
+    ) => void
+    onSearch: () => void
+}
+
+export const SearchBar = ({
+                              searchSortCnd,
+                              searchKeyword,
+                              options,
+                              onChange,
+                              onSearch
+                          }: SearchBarProps) => {
+    const handleChange = (event: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
+        onChange(event.target.name, event.target.value);
+    }
+
+    return (
+        <>
+            <select
+                id="searchCnd"
+                name="searchSortCnd"
+                className="search_select"
+                value={searchSortCnd}
+                onChange={handleChange}
+            >
+                <option value="">전체</option>
+                {options.map((option) => (
+                    <option key={option.value} value={option.value}>
+                        {option.label}
+                    </option>
+                ))}
+            </select>
+
+            <div className="search_type input_type">
+                <input
+                    type="text"
+                    id="searchWrd"
+                    name="searchKeyword"
+                    className="input search_input"
+                    value={searchKeyword}
+                    onChange={handleChange}
+                    maxLength={20}
+                    placeholder="검색어를 입력하세요"
+                />
+            </div>
+
+            <button
+                type="button"
+                className="btn btn_search"
+                onClick={onSearch}
+            >
+                검색
+            </button>
+        </>
+    )
+}
+
 
src/admin/component/pagination/PageSizeSelector.tsx (added)
+++ src/admin/component/pagination/PageSizeSelector.tsx
@@ -0,0 +1,27 @@
+import type {ChangeEvent} from "react";
+
+interface PageSizeSelectorProps {
+    value: number;
+    options: { value: string; label: string; }[];
+    onChange: (e: ChangeEvent<HTMLSelectElement>) => void;
+}
+
+export const PageSizeSelector = ({value, options, onChange}: PageSizeSelectorProps) => {
+    return (
+        <>
+            <select
+                id="pageUnit"
+                name="pageUnit"
+                className="search_select"
+                value={String(value)}
+                onChange={onChange}
+            >
+                {options.map((option) => (
+                    <option key={option.value} value={option.value}>
+                        {option.label}
+                    </option>
+                ))}
+            </select>
+        </>
+    )
+};
src/admin/feature/board/api/boardApi.ts
--- src/admin/feature/board/api/boardApi.ts
+++ src/admin/feature/board/api/boardApi.ts
@@ -1,7 +1,8 @@
-import type {BoardSearchParams, BoardListItem} from "../model/board.types.ts";
 import {apiClient} from "../../../../api/apiClient.ts";
 import type {PageResponse} from "../../../../type/pageResponse.ts";
+import type {SearchParams} from "../../../../type/searchParams.ts";
+import type {BoardListItem} from "../type/board.types.ts";
 
-export async function fetchBoardList(params: BoardSearchParams) {
+export async function fetchBoardList(params: SearchParams) {
     return apiClient.get<PageResponse<BoardListItem>>('/cop/bbs/list.do', params);
 }
 
src/admin/feature/board/components/BoardListSearchForm.tsx (added)
+++ src/admin/feature/board/components/BoardListSearchForm.tsx
@@ -0,0 +1,84 @@
+import {useState, type ChangeEvent} from 'react'
+import {SearchBar} from "../../../component/SearchBar.tsx";
+import type {SearchParams} from "../../../../type/searchParams.ts";
+import {PageSizeSelector} from "../../../component/pagination/PageSizeSelector.tsx";
+
+interface BoardListSearchFormProps {
+    totalCount: number
+    searchParams: SearchParams
+    onChange: (params: SearchParams) => void
+}
+
+const searchOptions = [
+    {value: '0', label: '게시판명/연결메뉴'},
+    {value: '1', label: '게시판유형'},
+]
+
+const pageSizeOptions = [
+    {value: '10', label: '10건씩'},
+    {value: '20', label: '20건씩'},
+    {value: '30', label: '30건씩'},
+]
+
+export function BoardListSearchForm({
+                                        totalCount,
+                                        searchParams,
+                                        onChange
+                                    }: BoardListSearchFormProps) {
+    const [draftParams, setDraftParams] = useState({
+        searchSortCnd: searchParams.searchSortCnd,
+        searchKeyword: searchParams.searchKeyword,
+    });
+
+    const handleChange = (
+        name: string,
+        value: string
+    ) => {
+        setDraftParams((prev) => ({
+            ...prev,
+            [name]: value,
+        }));
+    };
+
+    const handleSearch = () => {
+        onChange({
+            ...searchParams,
+            ...draftParams,
+            pageIndex: 1,
+        });
+    };
+
+    const handlePageChange = (event: ChangeEvent<HTMLSelectElement>) => {
+        const {name, value} = event.target
+        onChange({
+            ...searchParams,
+            [name]: Number(value),
+            pageIndex: 1,
+        })
+    }
+
+    return (
+        <div className="search_area">
+            <div className="search_left">
+                <p className="total_number">
+                    게시물 <b>{totalCount}</b>
+                </p>
+            </div>
+
+            <div className="search_right">
+                <SearchBar
+                    searchSortCnd={draftParams.searchSortCnd}
+                    searchKeyword={draftParams.searchKeyword}
+                    options={searchOptions}
+                    onChange={handleChange}
+                    onSearch={handleSearch}
+                />
+                <PageSizeSelector
+                    value={searchParams.pageUnit}
+                    options={pageSizeOptions}
+                    onChange={handlePageChange}
+                />
+            </div>
+        </div>
+    )
+}
 
src/admin/feature/board/components/BoardListTable.tsx (added)
+++ src/admin/feature/board/components/BoardListTable.tsx
@@ -0,0 +1,41 @@
+import type {BoardListItem} from "../type/board.types.ts";
+import type {SearchParams} from "../../../../type/searchParams.ts";
+import {EmptyRow} from "../../../component/EmptyRow.tsx";
+import {BoardListTableHeader} from "./BoardListTableHeader.tsx";
+import {BoardListTableRow} from "./BoardListTableRow.tsx";
+
+interface BoardListTableProps {
+    items: BoardListItem[]
+    params: SearchParams
+    onChange: (params: SearchParams) => void
+    totalCount: number
+    currentPage: number
+    recordPerPage: number
+}
+
+export function BoardListTable({items, params, onChange, totalCount, currentPage, recordPerPage}: BoardListTableProps) {
+    if (!items.length) {
+        return <EmptyRow colSpan={9}/>
+    }
+
+    return (
+        <div className="table table_type_cols">
+            <table>
+                <BoardListTableHeader params={params} onChange={onChange}/>
+                <tbody>
+                {items.map((item, index) => (
+                    <BoardListTableRow
+                        key={item.bbsId}
+                        item={item}
+                        index={index}
+                        searchParams={params}
+                        totalCount={totalCount}
+                        currentPage={currentPage}
+                        recordPerPage={recordPerPage}
+                    />
+                ))}
+                </tbody>
+            </table>
+        </div>
+    )
+}
 
src/admin/feature/board/components/BoardListTableHeader.tsx (added)
+++ src/admin/feature/board/components/BoardListTableHeader.tsx
@@ -0,0 +1,111 @@
+import type {SearchParams} from "../../../../type/searchParams.ts";
+
+interface BoardListTableHeaderProps {
+    params: SearchParams;
+    onChange: (params: SearchParams) => void
+}
+
+export function BoardListTableHeader({params, onChange}: BoardListTableHeaderProps) {
+    const handleSort = (field: string) => {
+
+        const nextOrder =
+            params.searchSortCnd === field &&
+            params.searchSortOrd === 'ASC'
+                ? 'DESC'
+                : 'ASC';
+
+        onChange({
+            ...params,
+            searchSortCnd: field,
+            searchSortOrd: nextOrder,
+            pageIndex: 1,
+        });
+    };
+
+    const getSortIcon = (field: string) => {
+
+        if (params.searchSortCnd !== field) {
+            return '-';
+        }
+
+        return params.searchSortOrd === 'ASC'
+            ? '▲'
+            : '▼';
+    };
+
+    return (
+        <>
+            <colgroup>
+                <col style={{width: '6%'}}/>
+                <col style={{width: '18%'}}/>
+                <col style={{width: '18%'}}/>
+                <col style={{width: '10%'}}/>
+                <col style={{width: '10%'}}/>
+                <col style={{width: '10%'}}/>
+                <col style={{width: '6%'}}/>
+                <col style={{width: '20%'}}/>
+            </colgroup>
+
+            <thead>
+            <tr>
+                <th>번호</th>
+                <th>
+                    게시판명
+                    <button
+                        className={`sort sortBtn ${params.searchSortCnd === 'BBS_NM' ? 'active' : ''}`}
+                        onClick={() => handleSort('BBS_NM')}
+                    >
+                        {getSortIcon('BBS_NM')}
+                    </button>
+                </th>
+                <th>
+                    연결 메뉴
+                    <button
+                        className={`sort sortBtn ${params.searchSortCnd === 'MENU_NM' ? 'active' : ''}`}
+                        onClick={() => handleSort('MENU_NM')}
+                    >
+                        {getSortIcon('MENU_NM')}
+                    </button>
+                </th>
+                <th>
+                    댓글 / 글수
+                    <button
+                        className={`sort sortBtn ${params.searchSortCnd === 'TOTCNT' ? 'active' : ''}`}
+                        onClick={() => handleSort('TOTCNT')}
+                    >
+                        {getSortIcon('TOTCNT')}
+                    </button>
+                </th>
+                <th>
+                    게시판유형
+                    <button
+                        className={`sort sortBtn ${params.searchSortCnd === 'BBS_TY_CODE_NM' ? 'active' : ''}`}
+                        onClick={() => handleSort('BBS_TY_CODE_NM')}
+                    >
+                        {getSortIcon('BBS_TY_CODE_NM')}
+                    </button>
+                </th>
+                <th>
+                    생성일
+                    <button
+                        className={`sort sortBtn ${params.searchSortCnd === 'FRST_REGIST_PNTTM' ? 'active' : ''}`}
+                        onClick={() => handleSort('FRST_REGIST_PNTTM')}
+                    >
+                        {getSortIcon('FRST_REGIST_PNTTM')}
+                    </button>
+                </th>
+                <th>
+                    사용여부
+                    <button
+                        className={`sort sortBtn ${params.searchSortCnd === 'USE_AT' ? 'active' : ''}`}
+                        onClick={() => handleSort('USE_AT')}
+                    >
+                        {getSortIcon('USE_AT')}
+                    </button>
+                </th>
+                <th>게시판 관리</th>
+            </tr>
+            </thead>
+        </>
+    )
+}
 
src/admin/feature/board/components/BoardListTableRow.tsx (added)
+++ src/admin/feature/board/components/BoardListTableRow.tsx
@@ -0,0 +1,44 @@
+import type {BoardListItem} from "../type/board.types.ts";
+import type {SearchParams} from "../../../../type/searchParams.ts";
+
+interface BoardListTableRowProps {
+    item: BoardListItem
+    index: number
+    searchParams: SearchParams
+    totalCount: number
+    currentPage: number
+    recordPerPage: number
+}
+
+export function BoardListTableRow({item, index, searchParams, totalCount, currentPage, recordPerPage}: BoardListTableRowProps) {
+    const rowNumber = searchParams.searchSortOrd === 'DESC'
+        ? totalCount - (currentPage - 1) * recordPerPage - index
+        : (currentPage - 1) * recordPerPage + (index + 1)
+
+    return (
+        <tr>
+            <td>{rowNumber}</td>
+            <td>{item.bbsNm}</td>
+            <td>{item.menuNm}</td>
+            <td>
+                {item.newCnt}/{item.totCnt}
+            </td>
+            <td>{item.bbsTyCodeNm}</td>
+            <td>{item.frstRegisterPnttm}</td>
+            <td>
+                {item.useAt === 'Y' ? (
+                    <span className="status text blue">사용</span>
+                ) : (
+                    <span className="status text gray">미사용</span>
+                )}
+            </td>
+            <td>
+                <div className="btn_wrap center">
+                    <button className="btn line primary small">수정</button>
+                    <button className="btn line lightgray small">게시판보기</button>
+                    <button className="btn line lightgray small">템플릿</button>
+                </div>
+            </td>
+        </tr>
+    )
+}
src/admin/feature/board/hook/useBoardList.ts
--- src/admin/feature/board/hook/useBoardList.ts
+++ src/admin/feature/board/hook/useBoardList.ts
@@ -1,13 +1,21 @@
-import type {BoardSearchParams} from "../model/board.types.ts";
-
 import {keepPreviousData, useQuery} from "@tanstack/react-query";
 import {fetchBoardList} from "../api/boardApi.ts";
+import type {SearchParams} from "../../../../type/searchParams.ts";
 
-export function useBoardListQuery(searchParams: BoardSearchParams) {
+export function useBoardListQuery(searchParams: SearchParams) {
 
-    return useQuery({
+    const query = useQuery({
         queryKey: ['boardList', searchParams],
         queryFn: () => fetchBoardList(searchParams),
         placeholderData: keepPreviousData,
     });
+
+    return {
+        list: query.data?.list ?? [],
+        totalCount: query.data?.totalCount ?? 0,
+        currentPage: query.data?.currentPage ?? 0,
+        recordPerPage: query.data?.recordPerPage ?? 0,
+        isLoading: query.isLoading,
+        error: query.error,
+    }
 }
 
src/admin/feature/board/model/board.types.ts (deleted)
--- src/admin/feature/board/model/board.types.ts
@@ -1,18 +0,0 @@
-export interface BoardSearchParams {
-    searchCondition: string
-    searchSortOrder: string
-    searchKeyword: string
-    pageUnit: number,
-    pageIndex: number
-}
-
-export interface BoardListItem {
-    bbsId: string
-    bbsNm: string
-    menuNm: string
-    newCnt: number
-    totCnt: number
-    bbsTyCodeNm: string
-    frstRegisterPnttm: string
-    useAt: 'Y' | 'N'
-}(No newline at end of file)
src/admin/feature/board/page/BoardListPage.tsx
--- src/admin/feature/board/page/BoardListPage.tsx
+++ src/admin/feature/board/page/BoardListPage.tsx
@@ -1,7 +1,73 @@
+import {useBoardListQuery} from "../hook/useBoardList.ts";
+import {PageHeader} from "../../../component/PageHeader.tsx";
+import {useEffect, useRef, useState} from "react";
+import type {SearchParams} from "../../../../type/searchParams.ts";
+import {BoardListSearchForm} from "../components/BoardListSearchForm.tsx";
+import {BoardListTable} from "../components/BoardListTable.tsx";
+import {toast} from "react-toastify";
+
+const initSearchParam: SearchParams = {
+    pageIndex: 1,
+    pageUnit: 10,
+    searchKeyword: "",
+    searchSortCnd: "BBS_NM",
+    searchSortOrd: "ASC"
+}
+
 export const BoardListPage = () => {
+    const [searchParams, setSearchParams] = useState<SearchParams>(initSearchParam);
+    const {
+        list,
+        totalCount,
+        currentPage,
+        recordPerPage,
+        isLoading,
+        error
+    } = useBoardListQuery(searchParams);
+
+    const toastId = useRef<string | number | null>(null);
+
+    useEffect(() => {
+        if (isLoading) {
+            toastId.current = toast.info("Loading...");
+        }
+
+        if (!isLoading && toastId.current) {
+            toast.dismiss(toastId.current);
+            toast.success("로드 완료!");
+        }
+
+        if (error && toastId.current) {
+            toast.dismiss(toastId.current);
+            toast.error(error.message);
+        }
+    }, [isLoading, error]);
+
+    const title = '게시판 관리'
+    const breadcrumb = [{label: '게시판 관리'}]
+    const homeUrl = '#'
+
     return (
-        <div>
-            <h2>게시판 관리</h2>
-        </div>
-    );
+        <>
+            <PageHeader
+                title={title}
+                breadcrumb={breadcrumb}
+                homeUrl={homeUrl}
+            />
+            <BoardListSearchForm
+                totalCount={totalCount}
+                searchParams={searchParams}
+                onChange={setSearchParams}
+            />
+            {isLoading && <p>Loading...</p>}
+            <BoardListTable
+                items={list}
+                params={searchParams}
+                onChange={setSearchParams}
+                totalCount={totalCount}
+                currentPage={currentPage}
+                recordPerPage={recordPerPage}
+            />
+        </>
+    )
 };
 
src/admin/feature/board/type/board.types.ts (added)
+++ src/admin/feature/board/type/board.types.ts
@@ -0,0 +1,10 @@
+export interface BoardListItem {
+    bbsId: string
+    bbsNm: string
+    menuNm: string
+    newCnt: number
+    totCnt: number
+    bbsTyCodeNm: string
+    frstRegisterPnttm: string
+    useAt: 'Y' | 'N'
+}(No newline at end of file)
src/admin/hook/useMenuList.ts
--- src/admin/hook/useMenuList.ts
+++ src/admin/hook/useMenuList.ts
@@ -16,7 +16,6 @@
         queryKey: ['menuList'],
         queryFn: fetchMenuList,
         select: (data) => {
-            console.log(data);
             const headMenuList =
                 (data.head ?? [])
                     .map(toMenuItem)
@@ -35,8 +34,6 @@
 
         staleTime: 1000 * 60 * 10,
     });
-
-    console.log(query);
 
     return {
         headMenuList: query.data?.headMenuList ?? [],
src/admin/layout/AdminLayout.tsx
--- src/admin/layout/AdminLayout.tsx
+++ src/admin/layout/AdminLayout.tsx
@@ -1,16 +1,18 @@
 import type {ReactNode} from "react";
 import {AdminSideBar} from "./AdminSideBar.tsx";
+import {AdminTopBar} from "./AdminTopBar.tsx";
 
 type AdminLayoutProps = {
     children: ReactNode;
 }
 
-
 export function AdminLayout({children}: AdminLayoutProps) {
     return (
         <div className="wrap">
             <AdminSideBar/>
+
             <div className="container sub">
+                <AdminTopBar/>
                 <div className="content_wrap">
                     {children}
                 </div>
src/admin/layout/AdminSideBar.tsx
--- src/admin/layout/AdminSideBar.tsx
+++ src/admin/layout/AdminSideBar.tsx
@@ -1,8 +1,16 @@
-import { MenuList } from '../component/menu/MenuList.tsx';
-import { useMenuList } from '../hook/useMenuList.ts';
+import {useEffect} from 'react';
+import {toast} from 'react-toastify';
+import {MenuList} from '../component/menu/MenuList.tsx';
+import {useMenuList} from '../hook/useMenuList.ts';
 
 export const AdminSideBar = () => {
-  const { headMenuList, menuList, isLoading, errorMessage } = useMenuList();
+  const {headMenuList, menuList, isLoading, errorMessage} = useMenuList();
+
+  useEffect(() => {
+    if (errorMessage) {
+      toast.error(errorMessage);
+    }
+  }, [errorMessage]);
 
   return (
     <div className="menu_wrap">
@@ -10,8 +18,7 @@
         <a href="/">DashBoard</a>
       </h1>
       <nav className="menu">
-        {isLoading && <p style={{ padding: '0 20px', color: '#fff' }}>메뉴 로딩중...</p>}
-        {errorMessage && <p style={{ padding: '0 20px', color: '#ffd1d1' }}>{errorMessage}</p>}
+        {isLoading && <p style={{padding: '0 20px', color: '#fff'}}>메뉴 로딩중...</p>}
         <MenuList headMenuList={headMenuList} menuList={menuList} />
       </nav>
     </div>
 
src/admin/layout/AdminTopBar.tsx (added)
+++ src/admin/layout/AdminTopBar.tsx
@@ -0,0 +1,55 @@
+export const AdminTopBar = () => {
+    return (
+        <div className="top_util">
+            <ul className="user_util">
+                <li className="final_date">
+                    <i></i>
+                    <p>최종접속일시 :</p>
+                    <span></span>
+                </li>
+
+                <li className="ip">
+                    <i></i>
+                    <p>IP</p>
+                </li>
+
+                <li className="time_out">
+                    <i></i>
+
+                    <p>
+                        로그인 타임아웃 :{" "}
+                        <span className="view_timer" id="ViewTimer">
+                            {/*<font color="red"></font>*/}
+                        </span>
+                    </p>
+
+                    <button
+                        type="button"
+                        className="btn_extend"
+                        onClick={() => {}}
+                    >
+                        연장
+                    </button>
+                </li>
+            </ul>
+
+            <div className="user_info">
+                <div className="area_right">
+                    <ul className="user_info_ul">
+                        <li>
+                            {}({})
+                        </li>
+                    </ul>
+
+                    <button
+                        type="button"
+                        className="btn btn_logout"
+                        onClick={() => {}}
+                    >
+                        로그아웃
+                    </button>
+                </div>
+            </div>
+        </div>
+    );
+}(No newline at end of file)
src/main.tsx
--- src/main.tsx
+++ src/main.tsx
@@ -3,6 +3,7 @@
 import {BrowserRouter} from "react-router-dom";
 import App from './App';
 import './styles/app.css';
+import 'react-toastify/dist/ReactToastify.css';
 
 const queryClient = new QueryClient();
 
src/styles/app.css
--- src/styles/app.css
+++ src/styles/app.css
@@ -1,7 +1,7 @@
 .skin_switcher {
   position: fixed;
   z-index: 99999;
-  right: 16px;
+  left: 16px;
   bottom: 16px;
   display: flex;
   gap: 6px;
 
src/type/searchParams.ts (added)
+++ src/type/searchParams.ts
@@ -0,0 +1,8 @@
+export interface SearchParams {
+    searchSortCnd: string
+    searchSortCnd: string
+    searchSortOrd: string
+    searchKeyword: string
+    pageUnit: number
+    pageIndex: number
+}
Add a comment
List