게임로그 유저생성 로그 조회

게임로그 유저로그인 로그 조회
This commit is contained in:
2025-07-17 14:38:07 +09:00
parent 7041d4a649
commit 952701f68b
11 changed files with 787 additions and 11 deletions

View File

@@ -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);
}
}
};

View File

@@ -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;

View File

@@ -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 = [

View File

@@ -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 (
<AnimatedPageWrapper>
<FormWrapper>
<UserCreateLogSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo orderType="asc" pageType="B" total={dataList?.total} total_all={dataList?.total_all}>
<ExcelExportButton
functionName="GameUserCreateLogExport"
params={searchParams}
fileName={t('FILE_GAME_LOG_USER_CREATE')}
onLoadingChange={setDownloadState}
dataSize={dataList?.total_all}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => (
<th key={header.id} width={header.width}>{header.label}</th>
))}
</tr>
</thead>
<tbody>
{dataList?.user_create_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
<td>{item.createdTime}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{dataList?.user_create_list &&
<Pagination
postsPerPage={searchParams.page_size}
totalPosts={dataList?.total_all}
setCurrentPage={handlePageChange}
currentPage={searchParams.page_no}
pageLimit={INITIAL_PAGE_LIMIT}
/>
}
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default UserCreateLogContent;

View File

@@ -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 (
<AnimatedPageWrapper>
<FormWrapper>
<UserLoginLogSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo orderType="asc" pageType="B" total={dataList?.total} total_all={dataList?.total_all}>
<ExcelExportButton
functionName="GameUserLoginLogExport"
params={searchParams}
fileName={t('FILE_GAME_LOG_USER_LOGIN')}
onLoadingChange={setDownloadState}
dataSize={dataList?.total_all}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => (
<th key={header.id} width={header.width}>{header.label}</th>
))}
</tr>
</thead>
<tbody>
{dataList?.user_login_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
<td>{item.tranId}</td>
<td>{item.loginTime}</td>
<td>{item.logoutTime}</td>
<td>{item.serverType}</td>
<td>{item.languageType}</td>
<td>{numberFormatter.formatSecondToMinuts(item.playtime)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{dataList?.user_login_list &&
<Pagination
postsPerPage={searchParams.page_size}
totalPosts={dataList?.total_all}
setCurrentPage={handlePageChange}
currentPage={searchParams.page_no}
pageLimit={INITIAL_PAGE_LIMIT}
/>
}
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default UserLoginLogContent;

View File

@@ -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 = [
<>
<InputGroup>
<SelectInput value={searchParams.search_type} onChange={e => onSearch({search_type: e.target.value }, false)}>
{userSearchType2.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput
type="text"
placeholder={searchParams.search_type === 'GUID' ? 'GUID ID 입력' : searchParams.search_type === 'NICKNAME' ? '아바타명 입력' :'Account ID 입력'}
value={searchParams.search_data}
width="260px"
onChange={e => onSearch({ search_data: e.target.value }, false)}
/>
</InputGroup>
</>,
<>
<InputLabel>일자</InputLabel>
<SearchPeriod
startDate={searchParams.start_dt}
handleStartDate={date => onSearch({ start_dt: date }, false)}
endDate={searchParams.end_dt}
handleEndDate={date => onSearch({ end_dt: date }, false)}
/>
</>
];
return <SearchBarLayout firstColumnData={searchList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
};
export default UserCreateLogSearchBar;

View File

@@ -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 = [
<>
<InputGroup>
<SelectInput value={searchParams.search_type} onChange={e => onSearch({search_type: e.target.value }, false)}>
{userSearchType2.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput
type="text"
placeholder={searchParams.search_type === 'GUID' ? 'GUID ID 입력' : searchParams.search_type === 'NICKNAME' ? '아바타명 입력' :'Account ID 입력'}
value={searchParams.search_data}
width="260px"
onChange={e => onSearch({ search_data: e.target.value }, false)}
/>
</InputGroup>
</>,
<>
<InputLabel>트랜잭션 ID</InputLabel>
<TextInput
type="text"
placeholder='트랜잭션 ID 입력'
value={searchParams.tran_id}
width="300px"
onChange={e => onSearch({ tran_id: e.target.value }, false)}
/>
</>,
];
const optionList = [
<>
<InputLabel>일자</InputLabel>
<SearchPeriod
startDate={searchParams.start_dt}
handleStartDate={date => onSearch({ start_dt: date }, false)}
endDate={searchParams.end_dt}
handleEndDate={date => onSearch({ end_dt: date }, false)}
/>
</>,
];
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
};
export default UserLoginLogSearchBar;

View File

@@ -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,
};

View File

@@ -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',

View File

@@ -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 = () => {
<CurrencyLogContent active={activeTab === 'CURRENCY'} />
<ItemLogContent active={activeTab === 'ITEM'} />
<CurrencyItemLogContent active={activeTab === 'CURRENCYITEM'} />
<UserCreateLogContent active={activeTab === 'USERCREATE'} />
<UserLoginLogContent active={activeTab === 'USERLOGIN'} />
</AnimatedPageWrapper>
);
};

View File

@@ -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';
}
}
};
/**