From 952701f68b781c1844b597245d09584bd1727252 Mon Sep 17 00:00:00 2001 From: bcjang Date: Thu, 17 Jul 2025 14:38:07 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B2=8C=EC=9E=84=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EC=83=9D=EC=84=B1=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B2=8C=EC=9E=84=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/Log.js | 75 +++++++ src/assets/data/adminConstants.js | 2 + src/assets/data/options.js | 3 +- .../DataManage/UserCreateLogContent.js | 149 ++++++++++++++ .../DataManage/UserLoginLogContent.js | 159 +++++++++++++++ .../searchBar/UserCreateLogSearchBar.js | 175 ++++++++++++++++ .../searchBar/UserLoginLogSearchBar.js | 191 ++++++++++++++++++ src/components/searchBar/index.js | 6 + src/i18n.js | 2 + src/pages/DataManage/GameLogView.js | 15 +- src/utils/common.js | 21 ++ 11 files changed, 787 insertions(+), 11 deletions(-) create mode 100644 src/components/DataManage/UserCreateLogContent.js create mode 100644 src/components/DataManage/UserLoginLogContent.js create mode 100644 src/components/searchBar/UserCreateLogSearchBar.js create mode 100644 src/components/searchBar/UserLoginLogSearchBar.js diff --git a/src/apis/Log.js b/src/apis/Log.js index 02ed93f..bf569ca 100644 --- a/src/apis/Log.js +++ b/src/apis/Log.js @@ -203,4 +203,79 @@ export const GameCurrencyItemLogExport = async (token, params, fileName) => { throw new Error('GameCurrencyItemLogExport Error', e); } } +}; + +export const getUserCreateList = async (token, searchType, searchData, startDate, endDate, order, size, currentPage) => { + try { + const response = await Axios.get(`/api/v1/log/user/create/list?search_type=${searchType}&search_data=${searchData}&start_dt=${startDate}&end_dt=${endDate} + &orderby=${order}&page_no=${currentPage}&page_size=${size}`, { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }); + + return response.data; + } catch (error) { + console.error('getUserCreateList API error:', error); + throw error; + } +}; + +export const getUserLoginDetailList = async (token, searchType, searchData, tranId, startDate, endDate, order, size, currentPage) => { + try { + const response = await Axios.get(`/api/v1/log/user/login/list?search_type=${searchType}&search_data=${searchData}&tran_id=${tranId} + &start_dt=${startDate}&end_dt=${endDate} + &orderby=${order}&page_no=${currentPage}&page_size=${size}`, { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }); + + return response.data; + } catch (error) { + console.error('getUserLoginDetailList API error:', error); + throw error; + } +}; + +export const GameUserCreateLogExport = async (token, params, fileName) => { + try { + console.log(params); + await Axios.post(`/api/v1/log/user/create/excel-export`, params, { + headers: { Authorization: `Bearer ${token}` }, + responseType: 'blob', + timeout: 300000 + }).then(response => { + + responseFileDownload(response, { + defaultFileName: fileName + }); + }); + } catch (e) { + if (e instanceof Error) { + throw new Error('GameUserCreateLogExport Error', e); + } + } +}; + +export const GameUserLoginLogExport = async (token, params, fileName) => { + try { + console.log(params); + await Axios.post(`/api/v1/log/user/login/excel-export`, params, { + headers: { Authorization: `Bearer ${token}` }, + responseType: 'blob', + timeout: 300000 + }).then(response => { + + responseFileDownload(response, { + defaultFileName: fileName + }); + }); + } catch (e) { + if (e instanceof Error) { + throw new Error('GameUserLoginLogExport Error', e); + } + } }; \ No newline at end of file diff --git a/src/assets/data/adminConstants.js b/src/assets/data/adminConstants.js index b1cee6a..4c71b27 100644 --- a/src/assets/data/adminConstants.js +++ b/src/assets/data/adminConstants.js @@ -12,6 +12,8 @@ export const STORAGE_MAIL_COPY = 'copyMailData'; export const STORAGE_BUSINESS_LOG_SEARCH = 'businessLogSearchParam'; export const STORAGE_GAME_LOG_CURRENCY_SEARCH = 'gameLogCurrencySearchParam'; export const STORAGE_GAME_LOG_ITEM_SEARCH = 'gameLogItemSearchParam'; +export const STORAGE_GAME_LOG_USER_CREATE_SEARCH = 'gameLogUserCreateSearchParam'; +export const STORAGE_GAME_LOG_USER_LOGIN_SEARCH = 'gameLogUserLoginSearchParam'; export const LOG_ACTION_FAIL_CALIUM_ECHO = 'FailCaliumEchoSystem'; export const BATTLE_EVENT_OPERATION_TIME_WAIT_SECONDS = 300; diff --git a/src/assets/data/options.js b/src/assets/data/options.js index f36a676..f0d7647 100644 --- a/src/assets/data/options.js +++ b/src/assets/data/options.js @@ -8,7 +8,8 @@ export const TabGameLogList = [ { value: 'CURRENCY', name: '재화 로그' }, { value: 'ITEM', name: '아이템 로그' }, { value: 'CURRENCYITEM', name: '재화(아이템) 로그' }, - // { value: 'TRADE', name: '거래 로그' }, + { value: 'USERCREATE', name: '유저생성 로그' }, + { value: 'USERLOGIN', name: '유저로그인 로그' }, ]; export const TabEconomicIndexList = [ diff --git a/src/components/DataManage/UserCreateLogContent.js b/src/components/DataManage/UserCreateLogContent.js new file mode 100644 index 0000000..b7be27a --- /dev/null +++ b/src/components/DataManage/UserCreateLogContent.js @@ -0,0 +1,149 @@ +import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react'; + +import { + CircularProgressWrapper, + FormWrapper, + TableStyle, + TableWrapper, +} from '../../styles/Components'; +import { useTranslation } from 'react-i18next'; +import { TableSkeleton } from '../Skeleton/TableSkeleton'; +import { useUserCreateLogSearch, UserCreateLogSearchBar } from '../searchBar'; +import { TopButton, ViewTableInfo } from '../common'; +import Pagination from '../common/Pagination/Pagination'; +import { + INITIAL_PAGE_LIMIT,STORAGE_GAME_LOG_USER_CREATE_SEARCH, +} from '../../assets/data/adminConstants'; +import ExcelExportButton from '../common/button/ExcelExportButton'; +import CircularProgress from '../common/CircularProgress'; +import { AnimatedPageWrapper } from '../common/Layout'; +import { GameUserCreateLogExport } from '../../apis'; + +const UserCreateLogContent = ({ active }) => { + const { t } = useTranslation(); + const token = sessionStorage.getItem('token'); + const tableRef = useRef(null); + const [downloadState, setDownloadState] = useState({ + loading: false, + progress: 0 + }); + + const { + searchParams, + loading: dataLoading, + data: dataList, + handleSearch, + handleReset, + handlePageChange, + handleOrderByChange, + handlePageSizeChange, + updateSearchParams + } = useUserCreateLogSearch(token, 500); + + useEffect(() => { + if(active) { + // 세션 스토리지에서 복사된 메일 데이터 가져오기 + const paramsData = sessionStorage.getItem(STORAGE_GAME_LOG_USER_CREATE_SEARCH); + + if (paramsData) { + const searchData = JSON.parse(paramsData); + + handleSearch({ + start_dt: new Date(searchData.start_dt), + end_dt: new Date(searchData.end_dt), + search_data: searchData.guid + }); + + // 사용 후 세션 스토리지 데이터 삭제 + sessionStorage.removeItem(STORAGE_GAME_LOG_USER_CREATE_SEARCH); + } + } + }, [active]); + + const tableHeaders = useMemo(() => { + return [ + { id: 'logDay', label: '일자', width: '120px' }, + { id: 'accountId', label: 'account ID', width: '80px' }, + { id: 'userGuid', label: 'GUID', width: '200px' }, + { id: 'userNickname', label: '아바타명', width: '150px' }, + { id: 'createTime', label: '생성일시', width: '200px' }, + ]; + }, []); + + if(!active) return null; + + return ( + + + { + if (executeSearch) { + handleSearch(newParams); + } else { + updateSearchParams(newParams); + } + }} + onReset={handleReset} + /> + + + + {downloadState.loading && ( + + + + )} + + {dataLoading ? : + <> + + + + + {tableHeaders.map(header => ( + {header.label} + ))} + + + + {dataList?.user_create_list?.map((item, index) => ( + + + {item.logDay} + {item.accountId} + {item.userGuid} + {item.userNickname} + {item.createdTime} + + + ))} + + + + {dataList?.user_create_list && + + } + + + } + + ); +}; +export default UserCreateLogContent; \ No newline at end of file diff --git a/src/components/DataManage/UserLoginLogContent.js b/src/components/DataManage/UserLoginLogContent.js new file mode 100644 index 0000000..9dacd77 --- /dev/null +++ b/src/components/DataManage/UserLoginLogContent.js @@ -0,0 +1,159 @@ +import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react'; + +import { + CircularProgressWrapper, + FormWrapper, + TableStyle, + TableWrapper, +} from '../../styles/Components'; +import { useTranslation } from 'react-i18next'; +import { TableSkeleton } from '../Skeleton/TableSkeleton'; +import { useUserLoginLogSearch, UserLoginLogSearchBar } from '../searchBar'; +import { TopButton, ViewTableInfo } from '../common'; +import Pagination from '../common/Pagination/Pagination'; +import { + INITIAL_PAGE_LIMIT, STORAGE_GAME_LOG_USER_LOGIN_SEARCH, +} from '../../assets/data/adminConstants'; +import ExcelExportButton from '../common/button/ExcelExportButton'; +import CircularProgress from '../common/CircularProgress'; +import { AnimatedPageWrapper } from '../common/Layout'; +import { numberFormatter } from '../../utils'; + +const UserLoginLogContent = ({ active }) => { + const { t } = useTranslation(); + const token = sessionStorage.getItem('token'); + const tableRef = useRef(null); + const [downloadState, setDownloadState] = useState({ + loading: false, + progress: 0 + }); + + const { + searchParams, + loading: dataLoading, + data: dataList, + handleSearch, + handleReset, + handlePageChange, + handleOrderByChange, + handlePageSizeChange, + updateSearchParams + } = useUserLoginLogSearch(token, 500); + + useEffect(() => { + if(active) { + // 세션 스토리지에서 복사된 메일 데이터 가져오기 + const paramsData = sessionStorage.getItem(STORAGE_GAME_LOG_USER_LOGIN_SEARCH); + + if (paramsData) { + const searchData = JSON.parse(paramsData); + + handleSearch({ + start_dt: new Date(searchData.start_dt), + end_dt: new Date(searchData.end_dt), + search_data: searchData.guid + }); + + // 사용 후 세션 스토리지 데이터 삭제 + sessionStorage.removeItem(STORAGE_GAME_LOG_USER_LOGIN_SEARCH); + } + } + }, [active]); + + const tableHeaders = useMemo(() => { + return [ + { id: 'logDay', label: '일자', width: '100px' }, + { id: 'accountId', label: 'account ID', width: '80px' }, + { id: 'userGuid', label: 'GUID', width: '180px' }, + { id: 'userNickname', label: '아바타명', width: '150px' }, + { id: 'tranId', label: '트랜잭션 ID', width: '200px' }, + { id: 'loginTime', label: '로그인시간', width: '150px' }, + { id: 'logoutTime', label: '로그아웃시간', width: '120px' }, + { id: 'serverType', label: '서버종류', width: '80px' }, + { id: 'languageType', label: '언어', width: '80px' }, + { id: 'playtime', label: '플레이시간(분)', width: '80px' }, + ]; + }, []); + + if(!active) return null; + + return ( + + + { + if (executeSearch) { + handleSearch(newParams); + } else { + updateSearchParams(newParams); + } + }} + onReset={handleReset} + /> + + + + {downloadState.loading && ( + + + + )} + + {dataLoading ? : + <> + + + + + {tableHeaders.map(header => ( + {header.label} + ))} + + + + {dataList?.user_login_list?.map((item, index) => ( + + + {item.logDay} + {item.accountId} + {item.userGuid} + {item.userNickname} + {item.tranId} + {item.loginTime} + {item.logoutTime} + {item.serverType} + {item.languageType} + {numberFormatter.formatSecondToMinuts(item.playtime)} + + + ))} + + + + {dataList?.user_login_list && + + } + + + } + + ); +}; +export default UserLoginLogContent; \ No newline at end of file diff --git a/src/components/searchBar/UserCreateLogSearchBar.js b/src/components/searchBar/UserCreateLogSearchBar.js new file mode 100644 index 0000000..3a4a7e6 --- /dev/null +++ b/src/components/searchBar/UserCreateLogSearchBar.js @@ -0,0 +1,175 @@ +import { TextInput, InputLabel, SelectInput, InputGroup } from '../../styles/Components'; +import { SearchBarLayout, SearchPeriod } from '../common/SearchBar'; +import { useCallback, useEffect, useState } from 'react'; +import { userSearchType2 } from '../../assets/data/options'; +import { getUserCreateList } from '../../apis'; +import { useAlert } from '../../context/AlertProvider'; +import { alertTypes } from '../../assets/data/types'; + +export const useUserCreateLogSearch = (token, initialPageSize) => { + const {showToast} = useAlert(); + + const [searchParams, setSearchParams] = useState({ + search_type: 'GUID', + search_data: '', + start_dt: (() => { + const date = new Date(); + date.setDate(date.getDate() - 1); + return date; + })(), + end_dt: (() => { + const date = new Date(); + date.setDate(date.getDate() - 1); + return date; + })(), + order_by: 'ASC', + page_size: initialPageSize, + page_no: 1 + }); + + const [loading, setLoading] = useState(false); + const [data, setData] = useState(null); + + useEffect(() => { + // const initialLoad = async () => { + // await fetchData(searchParams); + // }; + // + // initialLoad(); + }, [token]); + + const fetchData = useCallback(async (params) => { + if (!token) return; + + try { + setLoading(true); + const result = await getUserCreateList( + token, + params.search_type, + params.search_data, + params.start_dt.toISOString(), + params.end_dt.toISOString(), + params.order_by, + params.page_size, + params.page_no + ); + if(result.result === "ERROR_LOG_MEMORY_LIMIT"){ + showToast('LOG_MEMORY_LIMIT_WARNING', {type: alertTypes.error}); + }else if(result.result === "ERROR_MONGODB_QUERY"){ + showToast('LOG_MONGGDB_QUERY_WARNING', {type: alertTypes.error}); + }else if(result.result === "ERROR"){ + showToast(result.result, {type: alertTypes.error}); + } + setData(result.data); + return result.data; + } catch (error) { + showToast('error', {type: alertTypes.error}); + throw error; + } finally { + setLoading(false); + } + }, [token]); + + const updateSearchParams = useCallback((newParams) => { + setSearchParams(prev => ({ + ...prev, + ...newParams + })); + }, []); + + const handleSearch = useCallback(async (newParams = {}, executeSearch = true) => { + const updatedParams = { + ...searchParams, + ...newParams, + page_no: newParams.page_no || 1 // Reset to first page on new search + }; + updateSearchParams(updatedParams); + + if (executeSearch) { + return await fetchData(updatedParams); + } + return null; + }, [searchParams, fetchData]); + + const handleReset = useCallback(async () => { + const now = new Date(); + now.setDate(now.getDate() - 1); + const resetParams = { + search_type: 'GUID', + search_data: '', + start_dt: now, + end_dt: now, + order_by: 'ASC', + page_size: initialPageSize, + page_no: 1 + }; + setSearchParams(resetParams); + return await fetchData(resetParams); + }, [initialPageSize, fetchData]); + + const handlePageChange = useCallback(async (newPage) => { + return await handleSearch({ page_no: newPage }, true); + }, [handleSearch]); + + const handlePageSizeChange = useCallback(async (newSize) => { + return await handleSearch({ page_size: newSize, page_no: 1 }, true); + }, [handleSearch]); + + const handleOrderByChange = useCallback(async (newOrder) => { + return await handleSearch({ order_by: newOrder }, true); + }, [handleSearch]); + + return { + searchParams, + loading, + data, + handleSearch, + handleReset, + handlePageChange, + handlePageSizeChange, + handleOrderByChange, + updateSearchParams + }; +}; + +const UserCreateLogSearchBar = ({ searchParams, onSearch, onReset }) => { + const handleSubmit = event => { + event.preventDefault(); + + onSearch(searchParams, true); + }; + + const searchList = [ + <> + + onSearch({search_type: e.target.value }, false)}> + {userSearchType2.map((data, index) => ( + + ))} + + onSearch({ search_data: e.target.value }, false)} + /> + + , + <> + 일자 + onSearch({ start_dt: date }, false)} + endDate={searchParams.end_dt} + handleEndDate={date => onSearch({ end_dt: date }, false)} + /> + + ]; + + return ; +}; + +export default UserCreateLogSearchBar; \ No newline at end of file diff --git a/src/components/searchBar/UserLoginLogSearchBar.js b/src/components/searchBar/UserLoginLogSearchBar.js new file mode 100644 index 0000000..9f2be73 --- /dev/null +++ b/src/components/searchBar/UserLoginLogSearchBar.js @@ -0,0 +1,191 @@ +import { TextInput, InputLabel, SelectInput, InputGroup } from '../../styles/Components'; +import { SearchBarLayout, SearchPeriod } from '../common/SearchBar'; +import { useCallback, useEffect, useState } from 'react'; +import { userSearchType2 } from '../../assets/data/options'; +import { getUserLoginDetailList } from '../../apis'; +import { useAlert } from '../../context/AlertProvider'; +import { alertTypes } from '../../assets/data/types'; + +export const useUserLoginLogSearch = (token, initialPageSize) => { + const {showToast} = useAlert(); + + const [searchParams, setSearchParams] = useState({ + search_type: 'GUID', + search_data: '', + tran_id: '', + start_dt: (() => { + const date = new Date(); + date.setDate(date.getDate() - 1); + return date; + })(), + end_dt: (() => { + const date = new Date(); + date.setDate(date.getDate() - 1); + return date; + })(), + order_by: 'ASC', + page_size: initialPageSize, + page_no: 1 + }); + + const [loading, setLoading] = useState(false); + const [data, setData] = useState(null); + + useEffect(() => { + // const initialLoad = async () => { + // await fetchData(searchParams); + // }; + // + // initialLoad(); + }, [token]); + + const fetchData = useCallback(async (params) => { + if (!token) return; + + try { + setLoading(true); + const result = await getUserLoginDetailList( + token, + params.search_type, + params.search_data, + params.tran_id, + params.start_dt.toISOString(), + params.end_dt.toISOString(), + params.order_by, + params.page_size, + params.page_no + ); + if(result.result === "ERROR_LOG_MEMORY_LIMIT"){ + showToast('LOG_MEMORY_LIMIT_WARNING', {type: alertTypes.error}); + }else if(result.result === "ERROR_MONGODB_QUERY"){ + showToast('LOG_MONGGDB_QUERY_WARNING', {type: alertTypes.error}); + }else if(result.result === "ERROR"){ + showToast(result.result, {type: alertTypes.error}); + } + setData(result.data); + return result.data; + } catch (error) { + showToast('error', {type: alertTypes.error}); + throw error; + } finally { + setLoading(false); + } + }, [token]); + + const updateSearchParams = useCallback((newParams) => { + setSearchParams(prev => ({ + ...prev, + ...newParams + })); + }, []); + + const handleSearch = useCallback(async (newParams = {}, executeSearch = true) => { + const updatedParams = { + ...searchParams, + ...newParams, + page_no: newParams.page_no || 1 // Reset to first page on new search + }; + updateSearchParams(updatedParams); + + if (executeSearch) { + return await fetchData(updatedParams); + } + return null; + }, [searchParams, fetchData]); + + const handleReset = useCallback(async () => { + const now = new Date(); + now.setDate(now.getDate() - 1); + const resetParams = { + search_type: 'GUID', + search_data: '', + tran_id: '', + start_dt: now, + end_dt: now, + order_by: 'ASC', + page_size: initialPageSize, + page_no: 1 + }; + setSearchParams(resetParams); + return await fetchData(resetParams); + }, [initialPageSize, fetchData]); + + const handlePageChange = useCallback(async (newPage) => { + return await handleSearch({ page_no: newPage }, true); + }, [handleSearch]); + + const handlePageSizeChange = useCallback(async (newSize) => { + return await handleSearch({ page_size: newSize, page_no: 1 }, true); + }, [handleSearch]); + + const handleOrderByChange = useCallback(async (newOrder) => { + return await handleSearch({ order_by: newOrder }, true); + }, [handleSearch]); + + return { + searchParams, + loading, + data, + handleSearch, + handleReset, + handlePageChange, + handlePageSizeChange, + handleOrderByChange, + updateSearchParams + }; +}; + +const UserLoginLogSearchBar = ({ searchParams, onSearch, onReset }) => { + const handleSubmit = event => { + event.preventDefault(); + + onSearch(searchParams, true); + }; + + const searchList = [ + <> + + onSearch({search_type: e.target.value }, false)}> + {userSearchType2.map((data, index) => ( + + ))} + + onSearch({ search_data: e.target.value }, false)} + /> + + , + <> + 트랜잭션 ID + onSearch({ tran_id: e.target.value }, false)} + /> + , + ]; + + const optionList = [ + <> + 일자 + onSearch({ start_dt: date }, false)} + endDate={searchParams.end_dt} + handleEndDate={date => onSearch({ end_dt: date }, false)} + /> + , + ]; + + return ; +}; + +export default UserLoginLogSearchBar; \ No newline at end of file diff --git a/src/components/searchBar/index.js b/src/components/searchBar/index.js index 0e39bc3..1d3c05c 100644 --- a/src/components/searchBar/index.js +++ b/src/components/searchBar/index.js @@ -28,6 +28,8 @@ import CurrencyLogSearchBar, { useCurrencyLogSearch } from './CurrencyLogSearchB import ItemLogSearchBar, { useItemLogSearch } from './ItemLogSearchBar'; import CurrencyItemLogSearchBar, { useCurrencyItemLogSearch } from './CurrencyItemLogSearchBar'; import CurrencyIndexSearchBar, { useCurrencyIndexSearch } from './CurrencyIndexSearchBar'; +import UserCreateLogSearchBar, { useUserCreateLogSearch } from './UserCreateLogSearchBar'; +import UserLoginLogSearchBar, { useUserLoginLogSearch } from './UserLoginLogSearchBar'; import LandAuctionSearchBar from './LandAuctionSearchBar'; import CaliumRequestSearchBar from './CaliumRequestSearchBar'; @@ -70,5 +72,9 @@ export { ItemLogSearchBar, CurrencyItemLogSearchBar, useCurrencyItemLogSearch, + UserCreateLogSearchBar, + useUserCreateLogSearch, + UserLoginLogSearchBar, + useUserLoginLogSearch, }; diff --git a/src/i18n.js b/src/i18n.js index c454d12..fc27bd6 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -169,6 +169,8 @@ const resources = { FILE_BUSINESS_LOG: 'Caliverse_Log.xlsx', FILE_BATTLE_EVENT: 'Caliverse_Battle_Event.xlsx', FILE_GAME_LOG_CURRENCY: 'Caliverse_Game_Log_Currency', + FILE_GAME_LOG_USER_CREATE: 'Caliverse_Game_Log_User_Create', + FILE_GAME_LOG_USER_LOGIN: 'Caliverse_Game_Log_User_Login', FILE_GAME_LOG_ITEM: 'Caliverse_Game_Log_Item', FILE_GAME_LOG_CURRENCY_ITEM: 'Caliverse_Game_Log_Currecy_Item', FILE_CURRENCY_INDEX: 'Caliverse_Currency_Index', diff --git a/src/pages/DataManage/GameLogView.js b/src/pages/DataManage/GameLogView.js index 662949f..2719b21 100644 --- a/src/pages/DataManage/GameLogView.js +++ b/src/pages/DataManage/GameLogView.js @@ -1,8 +1,6 @@ -import { Fragment, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { AnimatedPageWrapper } from '../../components/common/Layout' -import Button from '../../components/common/button/Button'; -import Pagination from '../../components/common/Pagination/Pagination'; import { registerLocale } from 'react-datepicker'; import { ko } from 'date-fns/esm/locale'; @@ -10,14 +8,8 @@ import 'react-datepicker/dist/react-datepicker.css'; import { Title, - SelectInput, - TableStyle, - TableInfo, - ListCount, - ListOption, TabScroll, TabItem, TabWrapper, } from '../../styles/Components'; -import { styled } from 'styled-components'; import { withAuth } from '../../hooks/hook'; import { authType } from '../../assets/data'; @@ -26,6 +18,8 @@ import CurrencyLogContent from '../../components/DataManage/CurrencyLogContent'; import { STORAGE_GAME_LOG_CURRENCY_SEARCH } from '../../assets/data/adminConstants'; import ItemLogContent from '../../components/DataManage/ItemLogContent'; import CurrencyItemLogContent from '../../components/DataManage/CurrencyItemLogContent'; +import UserCreateLogContent from '../../components/DataManage/UserCreateLogContent'; +import UserLoginLogContent from '../../components/DataManage/UserLoginLogContent'; registerLocale('ko', ko); @@ -39,7 +33,6 @@ const GameLogView = () => { const searchData = JSON.parse(paramsData); setActiveTab(searchData.tab); - console.log(searchData); } }, []); @@ -67,6 +60,8 @@ const GameLogView = () => { + + ); }; diff --git a/src/utils/common.js b/src/utils/common.js index 76baf87..3e86fea 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -134,8 +134,29 @@ export const numberFormatter = { console.error('Currency formatting error:', e); return '0%'; } + }, + formatSecondToMinuts: (seconds) => { + if (seconds === null || seconds === undefined) return '0:00'; + + try { + const num = typeof seconds === 'string' ? parseFloat(seconds) : seconds; + if (isNaN(num)) return '0:00'; + + // 총 초를 분과 초로 변환 + const minutes = Math.floor(num / 60); + const remainingSeconds = Math.floor(num % 60); + + // 초가 10보다 작으면 앞에 0을 붙여 두 자리로 표시 + const formattedSeconds = remainingSeconds < 10 ? `0${remainingSeconds}` : remainingSeconds; + + return `${minutes}:${formattedSeconds}`; + } catch (e) { + console.error('Seconds to minutes formatting error:', e); + return '0:00'; + } } + }; /**