diff --git a/src/App.tsx b/src/App.tsx index 5831ac1..d6ffe36 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,50 @@ import { CssBaseline } from '@mui/material'; import 'common.css'; +import { useState } from 'react'; + +import { memberTokenStore } from '@store/memberTokenStore.ts'; + +import { stationMockData } from '@mock'; + +import { useFetchStations } from '@hook/stations/useFetchStations.ts'; + import ToastContainer from '@common/Toast/ToastContainer'; +import LoginModalContent from '@component/Login'; import ModalContainer from '@component/ModalContainer'; import NavigationContainer from '@component/Navigation'; import AdminTable from '@component/Table'; import { MENU_LIST } from '@constant'; +import { getColumnsByTitle, stationColumns } from '@type'; + + function App() { + const [tableTitle, setTableTitle] = useState('충전소 관리'); + const [tableColumn, setTableColumn] = useState(stationColumns); + const [tableData, setTableData] = useState(stationMockData); + const token = memberTokenStore.getState(); + const { lastPage, stationSummaryList } = useFetchStations(token, 1); + if (token === '') return ; + + const setTable = (title: string) => { + setTableTitle(title); + setTableData(stationSummaryList); + setTableColumn(getColumnsByTitle(title)); + }; + // const [page, setPage] = useState(1); + return ( <> - - + + ); } -export default App; +export default App; \ No newline at end of file diff --git a/src/components/Navigation/Navigation.tsx b/src/components/Navigation/Navigation.tsx index d11955d..94982ac 100644 --- a/src/components/Navigation/Navigation.tsx +++ b/src/components/Navigation/Navigation.tsx @@ -31,6 +31,7 @@ const headerTitleCss = css` xs: none; sm: block; } + flex-grow: 1; margin-left: 4px; diff --git a/src/components/Navigation/NavigationContainer.tsx b/src/components/Navigation/NavigationContainer.tsx index 71db38d..8ad170d 100644 --- a/src/components/Navigation/NavigationContainer.tsx +++ b/src/components/Navigation/NavigationContainer.tsx @@ -6,12 +6,13 @@ import type { Menu } from '@type'; interface NavigationContainerProps { title: string; menus: readonly Menu[]; + tableHandler: (title: string) => void; } -function NavigationContainer({ title, menus }: NavigationContainerProps) { +function NavigationContainer({ title, menus, tableHandler }: NavigationContainerProps) { return ( - + ); } diff --git a/src/components/Navigation/NavigationMenu.stories.tsx b/src/components/Navigation/NavigationMenu.stories.tsx index 2da5764..ec4a9da 100644 --- a/src/components/Navigation/NavigationMenu.stories.tsx +++ b/src/components/Navigation/NavigationMenu.stories.tsx @@ -26,7 +26,7 @@ export default meta; export const Default = () => { return ( - + ); }; diff --git a/src/components/Navigation/NavigationMenu.tsx b/src/components/Navigation/NavigationMenu.tsx index c792af7..ce9ba49 100644 --- a/src/components/Navigation/NavigationMenu.tsx +++ b/src/components/Navigation/NavigationMenu.tsx @@ -17,9 +17,10 @@ import type { Menu } from '@type'; interface NavigationProps { menus: readonly Menu[]; + onClick: (title: string) => void; } -export const NavigationMenu = ({ menus }: NavigationProps) => { +export const NavigationMenu = ({ menus, onClick }: NavigationProps) => { const [memberToken, setMemberToken] = useExternalState(memberTokenStore); const { openModal } = modalActions; const { showToast } = toastActions; @@ -32,12 +33,11 @@ export const NavigationMenu = ({ menus }: NavigationProps) => { const isLoggedIn = memberToken !== ''; - const handleClickMenu = (menu: Menu) => { - if (menu === '간편 로그인' && !isLoggedIn) { + const handleClickMenu = () => { + if (!isLoggedIn) { openModal(, 500); } - - if (menu === '간편 로그인' && isLoggedIn) { + if (isLoggedIn) { logout(); } }; @@ -48,13 +48,16 @@ export const NavigationMenu = ({ menus }: NavigationProps) => { showToast('로그아웃 되었습니다', 'success'); }; - return menus.map((menu, index) => ( - - )); + return ( + <> + {menus.map((menu, index) => ( + + ))} + {' '} + + ); }; diff --git a/src/components/Table/AdminTable.stories.tsx b/src/components/Table/AdminTable.stories.tsx index 246cb87..104d0fb 100644 --- a/src/components/Table/AdminTable.stories.tsx +++ b/src/components/Table/AdminTable.stories.tsx @@ -1,23 +1,32 @@ import type { Meta } from '@storybook/react'; +import { stationMockData } from '@mock'; + +import type { StationDetails } from '@type'; +import { stationColumns } from '@type'; + import type { TableProps } from './AdminTable'; import AdminTable from './AdminTable'; +type Props = TableProps; + const meta = { component: AdminTable, tags: ['autodocs'], args: { title: '전체 충전소', + data: stationMockData, + columns: stationColumns, }, argTypes: { title: { description: '페이지 제목을 변경할 수 있습니다.', }, }, -} satisfies Meta; +} as Meta; export default meta; -export const Default = (args: TableProps) => { +export const Default = (args: Props) => { return ; }; diff --git a/src/components/Table/AdminTable.tsx b/src/components/Table/AdminTable.tsx index 4cc9302..2a4e985 100644 --- a/src/components/Table/AdminTable.tsx +++ b/src/components/Table/AdminTable.tsx @@ -5,34 +5,27 @@ import Paper from '@mui/material/Paper'; import Table from '@mui/material/Table'; import TableContainer from '@mui/material/TableContainer'; -import { memberTokenStore } from '@store/memberTokenStore'; - -import { useFetchStations } from '@hook/stations/useFetchStations'; - -import { STATION_DETAILS_CATEGORY_LIST } from '@constant'; +import type { ColumnType } from '@type'; import AdminTableBody from './AdminTableBody'; import AdminTableHead from './AdminTableHead'; import AdminTablePagination from './AdminTablePagination'; -export interface TableProps { +export interface TableProps { + data: T[]; + columns: ColumnType[]; title: string; + lastPage: number; } -function AdminTable({ title }: TableProps) { - const token = memberTokenStore.getState(); - // const [page, setPage] = useState(1); - const { lastPage, stationSummaryList } = useFetchStations(token, 1); - - if (token === '') return

로그인이 필요합니다

; - +function AdminTable({ data, columns, title, lastPage }: TableProps) { return ( {title} - - + +
@@ -54,4 +47,4 @@ const boxShadowCss = css` 0 1px 3px 0 rgba(0, 0, 0, 0.015); `; -export default AdminTable; +export default AdminTable; \ No newline at end of file diff --git a/src/components/Table/AdminTableBody.tsx b/src/components/Table/AdminTableBody.tsx index 41b3ffb..c8f6e21 100644 --- a/src/components/Table/AdminTableBody.tsx +++ b/src/components/Table/AdminTableBody.tsx @@ -3,107 +3,52 @@ import TableBody from '@mui/material/TableBody'; import TableCell from '@mui/material/TableCell'; import TableRow from '@mui/material/TableRow'; -import { modalActions } from '@store/modalStateStore'; +import { modalActions } from '@store/modalStateStore.ts'; import { stationEditMock } from '@mock'; import Form from '@component/Form'; -import { ROWS_PER_PAGE } from '@constant'; +import type { ColumnType } from '@type'; -import type { StationProps } from '@type'; - -interface Props { - elements: StationProps[]; +interface TableRowsProps { + data: Array; + columns: Array>; } -// TODO: 하드 코딩 없앨 것 -function AdminTableBody({ elements }: Props) { +const AdminTableBody = ({ data, columns }: TableRowsProps) => { const { openModal } = modalActions; - const handleOpenModal = () => { + function handleOpenModal() { openModal(
); - }; - - const emptyRows = ROWS_PER_PAGE - Math.min(ROWS_PER_PAGE, elements.length); - - return ( - - {elements.map((element) => ( - - - {element.stationId} - - - {element.stationName} - - - {element.stationName} - - - {element.address} - - - {element.detailLocation} - - - {element.companyName} - - - {element.contact} - - - {String(element.isParkingFree)} - - - {String(element.isPrivate)} - - - {element.latitude} - - - {element.longitude} - - - {element.operationTime} - - - {String(element.privateReason)} - - - {String(element.stationState)} - - - ))} - - {emptyRows > 0 && ( - - - - )} - - ); -} + } + + const rows = data.map((row, index) => { + return ( + + {columns.map((column, index2) => { + return ( + + {`${row[column.key]}`} + + ); + })} + + ); + }); + return {rows}; +}; const tableItemSizeCss = css` - min-width: 200px; height: 76px; `; const tableItemCommonCss = css` border-bottom: 0; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; text-align: center; `; - export default AdminTableBody; diff --git a/src/components/Table/AdminTableHead.stories.tsx b/src/components/Table/AdminTableHead.stories.tsx index 0b242c4..13c13ff 100644 --- a/src/components/Table/AdminTableHead.stories.tsx +++ b/src/components/Table/AdminTableHead.stories.tsx @@ -1,6 +1,6 @@ import type { Meta } from '@storybook/react'; -import { STATION_DETAILS_CATEGORY_LIST } from '@constant'; +import { stationColumns } from '@type'; import AdminTableHead from './AdminTableHead'; @@ -8,10 +8,10 @@ const meta = { component: AdminTableHead, tags: ['autodocs'], args: { - categoryList: STATION_DETAILS_CATEGORY_LIST, + columns: stationColumns, }, argTypes: { - categoryList: { + columns: { control: { type: 'array', }, @@ -23,5 +23,5 @@ const meta = { export default meta; export const Default = () => { - return ; + return ; }; diff --git a/src/components/Table/AdminTableHead.tsx b/src/components/Table/AdminTableHead.tsx index 69f0ca1..a1d2461 100644 --- a/src/components/Table/AdminTableHead.tsx +++ b/src/components/Table/AdminTableHead.tsx @@ -3,25 +3,25 @@ import TableCell from '@mui/material/TableCell'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; -import type { StationCategoryValues } from '@type'; +import type { ColumnType } from '@type'; -interface Props { - categoryList: readonly StationCategoryValues[]; +interface TableHeaderProps { + columns: Array>; } -function AdminTableHead({ categoryList }: Props) { +const AdminTableHead = ({ columns }: TableHeaderProps) => { return ( - {categoryList.map((category, index) => ( + {columns.map((category, index) => ( - {category} + {category.header} ))} ); -} +}; const tableHeadCss = css` white-space: pre; diff --git a/src/constants/index.ts b/src/constants/index.ts index 383b53c..fb2ba6d 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -14,9 +14,7 @@ export const STATION_DETAILS_CATEGORIES = { stationState: '충전소 기타 정보', } as const; -export const STATION_DETAILS_CATEGORY_LIST = Object.values(STATION_DETAILS_CATEGORIES); - -export const ROWS_PER_PAGE = 10; +export const ROWS_PER_PAGE = 5; export const LAST_PAGE = 12; export const MENU_LIST = [ @@ -24,5 +22,4 @@ export const MENU_LIST = [ '충전소 관리', '충전소 제보 관리', '충전기 신고 관리', - '간편 로그인', ] as const; diff --git a/src/hooks/stations/useFetchStations.ts b/src/hooks/stations/useFetchStations.ts index f12fcbb..7b7de75 100644 --- a/src/hooks/stations/useFetchStations.ts +++ b/src/hooks/stations/useFetchStations.ts @@ -5,16 +5,16 @@ import { useEffect, useState } from 'react'; import { ROWS_PER_PAGE } from '@constant'; import { BASE_URL } from '@constant/url'; -import type { StationProps } from '@type'; +import type { StationDetails } from '@type'; interface StationsResponse { lastPage: number; - elements: StationProps[]; + elements: StationDetails[]; } export const useFetchStations = (token: string, page: number) => { const [lastPage, setLastPage] = useState(1); - const [stationSummaryList, setStationSummaryList] = useState([]); + const [stationSummaryList, setStationSummaryList] = useState([]); const config = { headers: { @@ -44,4 +44,4 @@ export const useFetchStations = (token: string, page: number) => { }, []); return { lastPage, stationSummaryList }; -}; +}; \ No newline at end of file diff --git a/src/mocks/index.ts b/src/mocks/index.ts index 9d2045b..5768922 100644 --- a/src/mocks/index.ts +++ b/src/mocks/index.ts @@ -1,6 +1,6 @@ import { ROWS_PER_PAGE } from '../constants'; -const mock = { +const stationMock = { stationId: 'HA_1234', stationName: '충전소', address: '서울시 강남구 테헤란로 411', @@ -31,4 +31,44 @@ export const stationEditMock = { stationState: 'null', }; -export const elements = Array.from({ length: ROWS_PER_PAGE }, () => mock); +const misinformationMock = { + id: 1, + memberId: 2, + isChecked: false, +}; + +const faultReportMock = { + id: 1, + memberId: 2, + stationId: 3, +}; + +const memberMock = { + id: 1, + email: 'b**ster@gmail.com', + role: 'ADMIN', +}; + +export const faultReportMockData = Array.from({ length: ROWS_PER_PAGE }, () => faultReportMock); +export const memberMockData = Array.from({ length: ROWS_PER_PAGE }, () => memberMock); + +export const stationMockData = Array.from({ length: ROWS_PER_PAGE }, () => stationMock); +export const misinformnationMockData = Array.from( + { length: ROWS_PER_PAGE }, + () => misinformationMock +); + +export function getMockData(title: string): Array { + switch (title) { + case '충전소 관리': + return stationMockData as Array; + case '충전소 제보 관리': + return misinformnationMockData as Array; + case '충전기 신고 관리': + return faultReportMockData as Array; + case '회원 관리': + return memberMockData as Array; + default: + return []; + } +} diff --git a/src/types/index.ts b/src/types/index.ts index 9ab7b6c..4778fbf 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,74 @@ import type { MENU_LIST, STATION_DETAILS_CATEGORIES } from '@constant'; -export interface StationProps { +export interface ColumnType { + key: K; + header: string; + width?: number; +} + +interface FaultReportDetails { + id: number; + stationId: string; + memberId: number; +} + +interface MemberDetails { + id: number; + email: number; + role: string; +} + +const memberColumns: ColumnType[] = [ + { + key: 'id', + header: 'ID', + }, + { + key: 'email', + header: 'E-MAIL', + }, + { + key: 'role', + header: '회원 권한', + }, +]; +const faultReportColumns: ColumnType[] = [ + { + key: 'id', + header: 'ID', + }, + { + key: 'stationId', + header: '충전소 ID', + }, + { + key: 'memberId', + header: '회원 ID', + }, +]; + +type MisinformationDetails = { + id: number; + memberId: number; + isChecked: boolean; +}; + +const misinformationColumns: ColumnType[] = [ + { + key: 'id', + header: 'ID', + }, + { + key: 'memberId', + header: '회원 ID', + }, + { + key: 'isChecked', + header: '확인 여부', + }, +]; + +export interface StationDetails { stationId: string; stationName: string; companyName: string; @@ -16,6 +84,76 @@ export interface StationProps { longitude: number; } +export const stationColumns: ColumnType[] = [ + { + key: 'stationId', + header: 'ID', + }, + { + key: 'stationName', + header: '충전소 이름', + }, + { + key: 'companyName', + header: '회사 이름', + }, + { + key: 'contact', + header: '연락처', + }, + { + key: 'detailLocation', + header: '상세 위치', + }, + { + key: 'isParkingFree', + header: '주차 무료', + }, + { + key: 'isPrivate', + header: '외부인 제한 충전소', + }, + { + key: 'operationTime', + header: '운영 시간', + }, + { + key: 'privateReason', + header: '외부인 제한 이유', + }, + { + key: 'stationState', + header: '충전소 기타 정보', + }, + { + key: 'address', + header: '주소', + }, + { + key: 'latitude', + header: '위도', + }, + { + key: 'longitude', + header: '경도', + }, +]; + +export function getColumnsByTitle(title: string): ColumnType[] { + switch (title) { + case '충전소 관리': + return stationColumns as ColumnType[]; + case '충전소 제보 관리': + return misinformationColumns as ColumnType[]; + case '충전기 신고 관리': + return faultReportColumns as ColumnType[]; + case '회원 관리': + return memberColumns as ColumnType[]; + default: + return []; + } +} + export type Menu = (typeof MENU_LIST)[number]; export type StationCategoryKeys = keyof typeof STATION_DETAILS_CATEGORIES; @@ -24,4 +162,8 @@ export type StationCategoryValues = (typeof STATION_DETAILS_CATEGORIES)[StationC type ExcludeID = T extends 'ID' ? never : T; export type StationCategoryValuesWithoutID = ExcludeID; -export interface StationEditProps extends Omit {} +export interface StationEditProps extends Omit {} + +export interface ModalElementsProps { + data: StationDetails[]; +}