경제지표 재화 보유
경제지표 아이템 보유 게임로그 스냅샷 히스토리 비즈니스로그 기준 변경
This commit is contained in:
@@ -36,6 +36,19 @@ export const userTotalIndex = async token => {
|
||||
}
|
||||
};
|
||||
|
||||
export const dashboardCaliumIndex = async token => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/dashboard/calium/converter`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('dashboardCaliumIndex', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 유저 지표 다운로드
|
||||
export const userIndexExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
@@ -187,10 +200,10 @@ export const PlaytimeIndexExport = async (token, filename, sendDate, endDate) =>
|
||||
|
||||
// 2. 경제 지표
|
||||
|
||||
// 재화 조회 (currency)
|
||||
export const CurrencyIndexView = async (token, start_dt, end_dt, currency_type) => {
|
||||
// 재화 획득 조회
|
||||
export const CurrencyAcquireIndexView = async (token, start_dt, end_dt, currencyType, deltaType) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/use?start_dt=${start_dt}&end_dt=${end_dt}¤cy_type=${currency_type}`, {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/list?start_dt=${start_dt}&end_dt=${end_dt}¤cy_type=${currencyType}&delta_type=${deltaType}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
@@ -202,75 +215,10 @@ export const CurrencyIndexView = async (token, start_dt, end_dt, currency_type)
|
||||
}
|
||||
};
|
||||
|
||||
// 재화 지표 다운로드
|
||||
export const CurrencyIndexExport = async (token, filename, sendDate, endDate, currencyType) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/currency/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}¤cy_type=${currencyType}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('CurrencyIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// VBP
|
||||
export const VbpIndexView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/vbp?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('VbpIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// VBP 다운로드
|
||||
export const VBPIndexExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/currency/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('VBPIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Item
|
||||
export const ItemIndexView = async (token, start_dt, end_dt) => {
|
||||
export const ItemIndexView = async (token, start_dt, end_dt, itemId, deltaType) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/item?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
const res = await Axios.get(`/api/v1/indicators/item/list?start_dt=${start_dt}&end_dt=${end_dt}&item_id=${itemId}&delta_type=${deltaType}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
@@ -282,27 +230,17 @@ export const ItemIndexView = async (token, start_dt, end_dt) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Item 다운로드
|
||||
export const ItemIndexExport = async (token, filename, sendDate, endDate) => {
|
||||
// Assets
|
||||
export const AssetsIndexView = async (token, start_dt, end_dt, itemId, deltaType) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/currency/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
const res = await Axios.get(`/api/v1/indicators/assets/list?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ItemIndexExport Error', e);
|
||||
throw new Error('AssetsIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -324,137 +262,4 @@ export const InstanceIndexView = async (token, data, start_dt, end_dt) => {
|
||||
throw new Error('InstanceIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Instance 다운로드
|
||||
export const InstanceIndexExport = async (token, filename, data, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(
|
||||
`/api/v1/indicators/currency/excel-down?file=${filename}&search_key=${data ? data : ''}
|
||||
&start_dt=${sendDate}&end_dt=${endDate}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
},
|
||||
).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('InstanceIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Clothes
|
||||
export const ClothesIndexView = async (token, data, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/clothes?search_key=${data ? data : ''}&start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ClothesIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Clothes 다운로드
|
||||
export const ClothesIndexExport = async (token, filename, data, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(
|
||||
`/api/v1/indicators/currency/excel-down?file=${filename}&search_key=${data ? data : ''}
|
||||
&start_dt=${sendDate}&end_dt=${endDate}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
},
|
||||
).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ClothesIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// DAU
|
||||
export const DailyActiveUserView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/dau/list?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.dau_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('DailyActiveUserView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// DAU 다운로드
|
||||
export const DailyActiveUserExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/dau/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('PlaytimeIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Daily Medal
|
||||
export const DailyMedalView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/daily-medal/list?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.daily_medal_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('DailyMedalView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -242,7 +242,6 @@ export const getUserLoginDetailList = async (token, searchType, searchData, tran
|
||||
|
||||
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',
|
||||
@@ -262,7 +261,6 @@ export const GameUserCreateLogExport = async (token, params, fileName) => {
|
||||
|
||||
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',
|
||||
@@ -278,4 +276,40 @@ export const GameUserLoginLogExport = async (token, params, fileName) => {
|
||||
throw new Error('GameUserLoginLogExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getUserSnapshotList = async (token, searchType, searchData, startDate, endDate, order, size, currentPage) => {
|
||||
try {
|
||||
const response = await Axios.get(`/api/v1/log/user/snapshot/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('getUserSnapshotList API error:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const GameUserSnapshotLogExport = async (token, params, fileName) => {
|
||||
try {
|
||||
await Axios.post(`/api/v1/log/user/snapshot/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('GameUserSnapshotLogExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -10,19 +10,22 @@ export const TabGameLogList = [
|
||||
{ value: 'CURRENCYITEM', name: '재화(아이템) 로그' },
|
||||
{ value: 'USERCREATE', name: '유저생성 로그' },
|
||||
{ value: 'USERLOGIN', name: '유저로그인 로그' },
|
||||
{ value: 'SNAPSHOT', name: '스냅샷 로그' },
|
||||
];
|
||||
|
||||
export const TabEconomicIndexList = [
|
||||
{ value: 'CURRENCY', name: '재화(유저)' },
|
||||
// { value: 'ITEM', name: '아이템' },
|
||||
// { value: 'VBP', name: 'VBP' },
|
||||
// { value: 'deco', name: '의상/타투' },
|
||||
// { value: 'instance', name: '인스턴스' },
|
||||
{ value: 'CURRENCY_ACQUIRE', name: '재화 획득' },
|
||||
{ value: 'CURRENCY_CONSUME', name: '재화 소모' },
|
||||
{ value: 'ITEM_ACQUIRE', name: '아이템 획득' },
|
||||
{ value: 'ITEM_CONSUME', name: '아이템 소모' },
|
||||
{ value: 'CURRENCY_ASSETS', name: '재화 보유' },
|
||||
{ value: 'ITEM_ASSETS', name: '아이템 보유' },
|
||||
];
|
||||
|
||||
export const TabUserIndexList = [
|
||||
{ value: 'USER', name: '이용자 지표' },
|
||||
{ value: 'RETENTION', name: '잔존율' },
|
||||
{ value: 'CURRENCY', name: '재화' },
|
||||
// { value: 'SEGMENT', name: 'Segment' },
|
||||
// { value: 'PLAYTIME', name: '플레이타임' },
|
||||
];
|
||||
@@ -468,6 +471,59 @@ export const opDBType = [
|
||||
{ value: 'MySql', name: 'MySql'},
|
||||
]
|
||||
|
||||
export const opLogCategory = [
|
||||
{ value: 'SCHEDULER', name: '스케줄러'},
|
||||
{ value: 'DYNAMODB', name: 'DynamoDB'},
|
||||
{ value: 'MARIADB', name: 'MariaDB'},
|
||||
{ value: 'MESSAGE_QUEUE', name: '메시지큐'},
|
||||
{ value: 'REDIS', name: 'Redis'},
|
||||
{ value: 'S3', name: 'S3'},
|
||||
{ value: 'BATCH_JOB', name: '배치잡'},
|
||||
]
|
||||
|
||||
export const opLogAction = [
|
||||
{ value: 'KICK_USER', name: '유저킥' },
|
||||
{ value: 'ADMIN_LEVEL', name: 'GM 레벨' },
|
||||
{ value: 'NICKNAME_CHANGE', name: '아바타명 변경' },
|
||||
{ value: 'MAIL_ITEM', name: '메일 아이템' },
|
||||
{ value: 'QUEST_TASK', name: '퀘스트 Task' },
|
||||
{ value: 'SCHEDULE_CLEANUP', name: '스케줄 캐시정리' },
|
||||
{ value: 'SCHEDULE_DATA_INIT', name: '스케줄 데이터 초기화' },
|
||||
{ value: 'SCHEDULE_LAND_OWNER_CHANGE', name: '스케줄 랜드 소유자 변경' },
|
||||
{ value: 'SCHEDULE_BLACK_LIST', name: '스케줄 이용자 제재' },
|
||||
{ value: 'SCHEDULE_NOTICE', name: '스케줄 인게임메시지' },
|
||||
{ value: 'SCHEDULE_MAIL', name: '스케줄 우편' },
|
||||
{ value: 'SCHEDULE_EVENT', name: '스케줄 이벤트' },
|
||||
{ value: 'SCHEDULE_BATTLE_EVENT', name: '스케줄 전투 이벤트' },
|
||||
{ value: 'SCHEDULE_LAND_AUCTION', name: '스케줄 랜드 경매' },
|
||||
{ value: 'BANNER', name: '메뉴 배너' },
|
||||
{ value: 'BATTLE_EVENT', name: '전투 이벤트' },
|
||||
{ value: 'BUILDING', name: '빌딩' },
|
||||
{ value: 'LAND_OWNER_CHANGE', name: '랜드 소유자 변경' },
|
||||
{ value: 'LAND_AUCTION', name: '랜드 경매' },
|
||||
{ value: 'GROUP', name: '그룹' },
|
||||
{ value: 'ADMIN', name: '운영자' },
|
||||
{ value: 'ADMIN_GROUP', name: '운영자 그룹' },
|
||||
{ value: 'ADMIN_DELETE', name: '운영자 삭제' },
|
||||
{ value: 'AUTH_ADMIN', name: '운영자 권한' },
|
||||
{ value: 'PASSWORD_INIT', name: '비밀번호 초기화' },
|
||||
{ value: 'PASSWORD_CHANGE', name: '비밀번호 변경' },
|
||||
{ value: 'BLACK_LIST', name: '이용자 제재' },
|
||||
{ value: 'CALIUM_REQUEST', name: '칼리움 요청' },
|
||||
{ value: 'EVENT', name: '이벤트' },
|
||||
{ value: 'MAIL', name: '우편' },
|
||||
{ value: 'NOTICE', name: '인게임메시지' },
|
||||
{ value: 'DATA_INIT', name: '데이터 초기화' },
|
||||
{ value: 'DATA', name: '데이터' },
|
||||
{ value: 'USER', name: '사용자' },
|
||||
{ value: 'ITEM', name: '아이템' }
|
||||
]
|
||||
|
||||
export const opCommonStatus = [
|
||||
{ value: 'SUCCESS', name: '성공' },
|
||||
{ value: 'FAIL', name: '실패' }
|
||||
]
|
||||
|
||||
// export const logAction = [
|
||||
// { value: "None", name: "ALL" },
|
||||
// { value: "AIChatDeleteCharacter", name: "NPC 삭제" },
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"id": "timestamp",
|
||||
"id": "logTime",
|
||||
"type": "date",
|
||||
"width": "200px",
|
||||
"title": "일시(KST)",
|
||||
@@ -22,25 +22,38 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dbType",
|
||||
"id": "category",
|
||||
"type": "option",
|
||||
"width": "100px",
|
||||
"title": "DB타입",
|
||||
"option_name": "opDBType"
|
||||
"title": "로그종류",
|
||||
"option_name": "opLogCategory"
|
||||
},
|
||||
{
|
||||
"id": "historyType",
|
||||
"id": "action",
|
||||
"type": "option",
|
||||
"width": "150px",
|
||||
"title": "이력종류",
|
||||
"option_name": "opHistoryType"
|
||||
"title": "로그액션",
|
||||
"option_name": "opLogAction"
|
||||
},
|
||||
{
|
||||
"id": "userId",
|
||||
"id": "status",
|
||||
"type": "option",
|
||||
"width": "100px",
|
||||
"title": "상태",
|
||||
"option_name": "opCommonStatus"
|
||||
},
|
||||
{
|
||||
"id": "worker",
|
||||
"type": "text",
|
||||
"width": "100px",
|
||||
"title": "작업자"
|
||||
},
|
||||
{
|
||||
"id": "message",
|
||||
"type": "text",
|
||||
"width": "100px",
|
||||
"title": "비고"
|
||||
},
|
||||
{
|
||||
"id": "detail",
|
||||
"type": "button",
|
||||
|
||||
153
src/components/DataManage/UserSnapshotLogContent.js
Normal file
153
src/components/DataManage/UserSnapshotLogContent.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import React, { Fragment, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
CircularProgressWrapper,
|
||||
FormWrapper,
|
||||
TableStyle,
|
||||
TableWrapper,
|
||||
} from '../../styles/Components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { UserSnapshotLogSearchBar, useUserSnapshotLogSearch } from '../searchBar';
|
||||
import { TopButton, ViewTableInfo } from '../common';
|
||||
import Pagination from '../common/Pagination/Pagination';
|
||||
import {
|
||||
INITIAL_PAGE_LIMIT,
|
||||
} 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 UserSnapshotLogContent = ({ 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,
|
||||
updateSearchParams
|
||||
} = useUserSnapshotLogSearch(token, 500);
|
||||
|
||||
const tableHeaders = useMemo(() => {
|
||||
return [
|
||||
{ id: 'logDay', label: '일자', width: '80px' },
|
||||
{ id: 'accountId', label: 'account ID', width: '80px' },
|
||||
{ id: 'userGuid', label: 'GUID', width: '180px' },
|
||||
{ id: 'userNickname', label: '아바타명', width: '150px' },
|
||||
{ id: 'gold', label: '골드', width: '80px' },
|
||||
{ id: 'sapphire', label: '사파이어', width: '80px' },
|
||||
{ id: 'calium', label: '칼리움', width: '80px' },
|
||||
{ id: 'ruby', label: '루비', width: '80px' },
|
||||
{ id: 'item_13080002', label: '퀘스트 메달', width: '80px' },
|
||||
{ id: 'item_13080004', label: '보급품 메달', width: '80px' },
|
||||
{ id: 'item_13080005', label: '제작 메달', width: '80px' },
|
||||
{ id: 'item_13080006', label: '에테론 315 포드', width: '80px' },
|
||||
{ id: 'item_13080007', label: '에테론 316 포드', width: '80px' },
|
||||
{ id: 'item_13080008', label: '에테론 317 포드', width: '80px' },
|
||||
{ id: 'item_13080009', label: '에테론 318 포드', width: '80px' },
|
||||
{ id: 'item_11570001', label: '강화잉크', width: '80px' },
|
||||
{ id: 'item_11570002', label: '연성잉크', width: '80px' },
|
||||
{ id: 'lastLogoutTime', label: '마지막 로그아웃 일자', width: '150px' },
|
||||
];
|
||||
}, []);
|
||||
|
||||
if(!active) return null;
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<FormWrapper>
|
||||
<UserSnapshotLogSearchBar
|
||||
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="GameUserSnapshotLogExport"
|
||||
params={searchParams}
|
||||
fileName={t('FILE_GAME_LOG_USER_SNAPSHOT')}
|
||||
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?.snapshot_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>{numberFormatter.formatCurrency(item.gold)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.sapphire)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.calium)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.ruby)}</td>
|
||||
<td>{item.item_13080002}</td>
|
||||
<td>{item.item_13080004}</td>
|
||||
<td>{item.item_13080005}</td>
|
||||
<td>{item.item_13080006}</td>
|
||||
<td>{item.item_13080007}</td>
|
||||
<td>{item.item_13080008}</td>
|
||||
<td>{item.item_13080009}</td>
|
||||
<td>{item.item_11570001}</td>
|
||||
<td>{item.item_11570002}</td>
|
||||
<td>{item.lastLogoutTime}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
{dataList?.snapshot_list &&
|
||||
<Pagination
|
||||
postsPerPage={searchParams.page_size}
|
||||
totalPosts={dataList?.total_all}
|
||||
setCurrentPage={handlePageChange}
|
||||
currentPage={searchParams.page_no}
|
||||
pageLimit={INITIAL_PAGE_LIMIT}
|
||||
/>
|
||||
}
|
||||
<TopButton />
|
||||
</>
|
||||
}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
export default UserSnapshotLogContent;
|
||||
@@ -14,7 +14,7 @@ import {STORAGE_GAME_LOG_CURRENCY_SEARCH, } from '../../assets/data/adminConstan
|
||||
import ExcelExportButton from '../common/button/ExcelExportButton';
|
||||
import CircularProgress from '../common/CircularProgress';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CurrencyIndexSearchBar from '../searchBar/CurrencyIndexSearchBar';
|
||||
import CurrencyUserIndexSearchBar from '../searchBar/CurrencyUserIndexSearchBar';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { AnimatedPageWrapper } from '../common/Layout';
|
||||
|
||||
@@ -132,7 +132,7 @@ const CreditContent = () => {
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<FormWrapper>
|
||||
<CurrencyIndexSearchBar
|
||||
<CurrencyUserIndexSearchBar
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
|
||||
126
src/components/IndexManage/CurrencyAcquireContent.js
Normal file
126
src/components/IndexManage/CurrencyAcquireContent.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import React, { Fragment, useMemo, useRef } from 'react';
|
||||
|
||||
import {
|
||||
TableStyle,
|
||||
FormWrapper,
|
||||
TableWrapper, ListOption,
|
||||
} from '../../styles/Components';
|
||||
|
||||
import { useCurrencyAcquireIndexSearch, CurrencyAcquireIndexSearchBar } from '../searchBar';
|
||||
import { TopButton, ViewTableInfo } from '../common';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { numberFormatter } from '../../utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AnimatedPageWrapper } from '../common/Layout';
|
||||
import CSVDownloadButton from '../common/button/CsvDownButton';
|
||||
|
||||
const CurrencyAcquireContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const {
|
||||
searchParams,
|
||||
loading: dataLoading,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
updateSearchParams
|
||||
} = useCurrencyAcquireIndexSearch(token);
|
||||
|
||||
const tableHeaders = useMemo(() => {
|
||||
return [
|
||||
{ id: 'logDay', label: '일자', width: '100px' },
|
||||
{ id: 'mail', label: '우편', width: '80px' },
|
||||
{ id: 'ShopSell', label: '상점 판매', width: '80px' },
|
||||
{ id: 'ShopPurchase', label: '상점 구매', width: '80px' },
|
||||
{ id: 'seasonPass', label: '시즌 패스', width: '80px' },
|
||||
{ id: 'claim', label: '클레임', width: '80px' },
|
||||
{ id: 'quest', label: '퀘스트', width: '80px' },
|
||||
{ id: 'ugq', label: 'UGQ', width: '80px' },
|
||||
{ id: 'battleObject', label: '보급품 상자', width: '80px' },
|
||||
{ id: 'randomBox', label: '랜덤박스', width: '80px' },
|
||||
{ id: 'landRent', label: '랜드 임대', width: '80px' },
|
||||
{ id: 'caliumExchange', label: '칼리움 교환소', width: '80px' },
|
||||
{ id: 'caliumConverter', label: '칼리움 컨버터', width: '80px' },
|
||||
{ id: 'beaconShop', label: '비컨 상점', width: '80px' },
|
||||
{ id: 'etc', label: '기타', width: '80px' },
|
||||
{ id: 'summary', label: '합계', width: '80px' },
|
||||
];
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<FormWrapper>
|
||||
<CurrencyAcquireIndexSearchBar
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
handleSearch(newParams);
|
||||
} else {
|
||||
updateSearchParams(newParams);
|
||||
}
|
||||
}}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
</FormWrapper>
|
||||
<ViewTableInfo >
|
||||
<ListOption>
|
||||
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_CURRENCY_ACQUIRE')} />
|
||||
</ListOption>
|
||||
</ViewTableInfo>
|
||||
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||
<>
|
||||
<TableWrapper>
|
||||
<TableStyle ref={tableRef}>
|
||||
<thead>
|
||||
<tr>
|
||||
{tableHeaders.map(header => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
width={header.width}
|
||||
colSpan={header.colSpan}
|
||||
>
|
||||
{header.label}
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{dataList?.currency_list?.map((item, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{item.logDay}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.MailTaken)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopSell)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.SeasonPassTakeReward)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ClaimReward)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.QuestMainReward || 0) + (item.actionSummary.QuestTaskUpdate || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.UgqAbort)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.RewardProp)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.GainLandProfit)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ConvertExchangeCalium)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ConvertCalium)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconSell || 0) + (item.actionSummary.BeaconShopReceivePaymentForSales || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.MoneyChange)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.totalDeltaAmount)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<TopButton />
|
||||
</>
|
||||
}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default CurrencyAcquireContent;
|
||||
109
src/components/IndexManage/CurrencyAssetsContent.js
Normal file
109
src/components/IndexManage/CurrencyAssetsContent.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import React, { Fragment, useMemo, useRef } from 'react';
|
||||
|
||||
import {
|
||||
TableStyle,
|
||||
FormWrapper,
|
||||
TableWrapper, ListOption
|
||||
} from '../../styles/Components';
|
||||
|
||||
import {
|
||||
AssetsIndexSearchBar, useAssetsIndexSearch,
|
||||
} from '../searchBar';
|
||||
import { TopButton, ViewTableInfo } from '../common';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { numberFormatter } from '../../utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AnimatedPageWrapper } from '../common/Layout';
|
||||
import CSVDownloadButton from '../common/button/CsvDownButton';
|
||||
import DailyDashBoard from './DailyCaliumDashBoard';
|
||||
|
||||
const CurrencyAssetsContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const {
|
||||
searchParams,
|
||||
loading: dataLoading,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
updateSearchParams
|
||||
} = useAssetsIndexSearch(token);
|
||||
|
||||
const tableHeaders = useMemo(() => {
|
||||
return [
|
||||
{ id: 'logDay', label: '일자', width: '100px' },
|
||||
{ id: 'userCount', label: '유저수', width: '100px' },
|
||||
{ id: 'gold', label: '골드', width: '100px' },
|
||||
{ id: 'sapphire', label: '사파이어', width: '100px' },
|
||||
{ id: 'calium', label: '칼리움', width: '100px' },
|
||||
{ id: 'ruby', label: '루비', width: '100px' }
|
||||
];
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<DailyDashBoard />
|
||||
<FormWrapper>
|
||||
<AssetsIndexSearchBar
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
handleSearch(newParams);
|
||||
} else {
|
||||
updateSearchParams(newParams);
|
||||
}
|
||||
}}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
</FormWrapper>
|
||||
<ViewTableInfo >
|
||||
<ListOption>
|
||||
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_ASSETS_CURRENCY')} />
|
||||
</ListOption>
|
||||
</ViewTableInfo>
|
||||
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||
<>
|
||||
<TableWrapper>
|
||||
<TableStyle ref={tableRef}>
|
||||
<thead>
|
||||
<tr>
|
||||
{tableHeaders.map(header => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
width={header.width}
|
||||
colSpan={header.colSpan}
|
||||
>
|
||||
{header.label}
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{dataList?.assets_list?.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.logDay}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.userCount)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.gold)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.sapphire)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.calium)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.ruby)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<TopButton />
|
||||
</>
|
||||
}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default CurrencyAssetsContent;
|
||||
128
src/components/IndexManage/CurrencyConsumeContent.js
Normal file
128
src/components/IndexManage/CurrencyConsumeContent.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import React, { Fragment, useMemo, useRef } from 'react';
|
||||
|
||||
import {
|
||||
TableStyle,
|
||||
FormWrapper,
|
||||
TableWrapper, ListOption,
|
||||
} from '../../styles/Components';
|
||||
|
||||
import {
|
||||
useCurrencyConsumeIndexSearch, CurrencyConsumeIndexSearchBar,
|
||||
} from '../searchBar';
|
||||
import { TopButton, ViewTableInfo } from '../common';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { numberFormatter } from '../../utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AnimatedPageWrapper } from '../common/Layout';
|
||||
import CSVDownloadButton from '../common/button/CsvDownButton';
|
||||
|
||||
const CurrencyConsumeContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const {
|
||||
searchParams,
|
||||
loading: dataLoading,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
updateSearchParams
|
||||
} = useCurrencyConsumeIndexSearch(token);
|
||||
|
||||
const tableHeaders = useMemo(() => {
|
||||
return [
|
||||
{ id: 'logDay', label: '일자', width: '100px' },
|
||||
{ id: 'itemBuy', label: '아이템 구매', width: '80px' },
|
||||
{ id: 'ShopPurchase', label: '상점 구매', width: '80px' },
|
||||
{ id: 'ShopRePurchase', label: '상점 재구매', width: '80px' },
|
||||
{ id: 'beaconShop', label: '비컨상점', width: '80px' },
|
||||
{ id: 'beacon', label: '비컨', width: '80px' },
|
||||
{ id: 'taxi', label: '택시', width: '80px' },
|
||||
{ id: 'farming', label: '파밍', width: '80px' },
|
||||
{ id: 'seasonPass', label: '시즌 패스', width: '80px' },
|
||||
{ id: 'caliumExchange', label: '칼리움 교환소', width: '80px' },
|
||||
{ id: 'caliumConverter', label: '칼리움 컨버터', width: '80px' },
|
||||
{ id: 'rent', label: '랜드 렌탈', width: '80px' },
|
||||
{ id: 'landAuction', label: '랜드 경매', width: '80px' },
|
||||
{ id: 'ugq', label: 'UGQ', width: '80px' },
|
||||
{ id: 'etc', label: '기타', width: '80px' },
|
||||
{ id: 'summary', label: '합계', width: '80px' },
|
||||
];
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<FormWrapper>
|
||||
<CurrencyConsumeIndexSearchBar
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
handleSearch(newParams);
|
||||
} else {
|
||||
updateSearchParams(newParams);
|
||||
}
|
||||
}}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
</FormWrapper>
|
||||
<ViewTableInfo >
|
||||
<ListOption>
|
||||
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_CURRENCY_CONSUME')} />
|
||||
</ListOption>
|
||||
</ViewTableInfo>
|
||||
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||
<>
|
||||
<TableWrapper>
|
||||
<TableStyle ref={tableRef}>
|
||||
<thead>
|
||||
<tr>
|
||||
{tableHeaders.map(header => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
width={header.width}
|
||||
colSpan={header.colSpan}
|
||||
>
|
||||
{header.label}
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{dataList?.currency_list?.map((item, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{item.logDay}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemBuy)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopRePurchase)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconShopRegisterItem || 0) + (item.actionSummary.BeaconShopPurchaseItem || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconCreate || 0) + (item.actionSummary.BeaconEdit || 0) + (item.actionSummary.BeaconAppearanceCustomize || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.TaxiMove)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.FarmingStart)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.SeasonPassBuyCharged)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ConvertExchangeCalium)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ConvertCalium)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.RentFloor)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.LandAuctionBid)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.UgqAssign)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.MoneyChange ||0) + (item.actionSummary.RenewalShopProducts ||0) + (item.actionSummary.CharacterAppearanceCustomize ||0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.totalDeltaAmount)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<TopButton />
|
||||
</>
|
||||
}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default CurrencyConsumeContent;
|
||||
@@ -1,110 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { DailySearchBar } from '../../components/IndexManage/index';
|
||||
import { DailyActiveUserExport, DailyActiveUserView } from '../../apis';
|
||||
|
||||
const PlayTimeContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// DAU 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
// await DailyActiveUserView(token, startDateToLocal, endDateToLocal).then(data => {
|
||||
// console.log(data);
|
||||
// setDataList(data);
|
||||
// });
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Dau.xlsx';
|
||||
DailyActiveUserExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan="1" width="45">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
DAU
|
||||
</th>
|
||||
{/*<th colSpan="1" width="30">*/}
|
||||
{/* DALC*/}
|
||||
{/*</th>*/}
|
||||
<th colSpan="1" width="30">
|
||||
DGLC
|
||||
</th>
|
||||
{/*<th colSpan="1" width="30">*/}
|
||||
{/* MaxAU*/}
|
||||
{/*</th>*/}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{dataList && (dataList || []).map((data, index) => (
|
||||
<tr key={index}>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.dau}</td>
|
||||
{/*<td>{data.dalc}</td>*/}
|
||||
<td>{data.dglc}</td>
|
||||
{/*<td>{data.maxAu}</td>*/}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTimeContent;
|
||||
|
||||
141
src/components/IndexManage/DailyCaliumDashBoard.js
Normal file
141
src/components/IndexManage/DailyCaliumDashBoard.js
Normal file
@@ -0,0 +1,141 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { dashboardCaliumIndex } from '../../apis';
|
||||
|
||||
import { styled } from 'styled-components';
|
||||
import TitleArrow from '../../assets/img/icon/icon-title.png';
|
||||
|
||||
const DailyDashBoard = () => {
|
||||
const [boardState, setBoardState] = useState('active');
|
||||
const [totalData, setTotalData] = useState([]);
|
||||
|
||||
const handleBoard = () => {
|
||||
if (boardState === 'active') {
|
||||
setBoardState('inactive');
|
||||
} else {
|
||||
setBoardState('active');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await dashboardCaliumIndex(token).then(data => {
|
||||
setTotalData(data.dashboard_calium);
|
||||
});
|
||||
};
|
||||
|
||||
const dashboardItems = [
|
||||
{
|
||||
title: '컨버터 칼리움 보유량',
|
||||
value: totalData.total_calium || 0
|
||||
},
|
||||
{
|
||||
title: '컨버터 변환 효율',
|
||||
value: `${totalData.converter_rate || 0}%`
|
||||
},
|
||||
{
|
||||
title: '인플레이션 가중치',
|
||||
value: `${totalData.inflation_rate || 0}%`
|
||||
},
|
||||
{
|
||||
title: '칼리움 누적 총량',
|
||||
value: totalData.cumulative_calium || 0
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<DailyBoardWrapper>
|
||||
{totalData &&
|
||||
<DailyBoard>
|
||||
<BoardTitle onClick={handleBoard} $state={boardState}>
|
||||
Daily Dashboard
|
||||
</BoardTitle>
|
||||
<BoardInfo $state={boardState}>
|
||||
<BoxWrapper>
|
||||
{dashboardItems?.map((item, index) => (
|
||||
<InfoItem key={index}>
|
||||
<InfoTitle>{item.title}</InfoTitle>
|
||||
<InfoValue>
|
||||
{item.value}
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
))}
|
||||
</BoxWrapper>
|
||||
</BoardInfo>
|
||||
</DailyBoard>
|
||||
}
|
||||
</DailyBoardWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default DailyDashBoard;
|
||||
|
||||
const DailyBoardWrapper = styled.div`
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #000;
|
||||
`;
|
||||
|
||||
const DailyBoard = styled.div`
|
||||
background: #f6f6f6;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const BoardTitle = styled.div`
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 52px;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
&:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 11px;
|
||||
height: 52px;
|
||||
margin-left: 10px;
|
||||
background: url(${TitleArrow}) 50% 50% no-repeat;
|
||||
position: absolute;
|
||||
transform: ${props => (props.$state === 'active' ? 'rotate(0)' : 'rotate(180deg)')};
|
||||
}
|
||||
`;
|
||||
|
||||
const BoardInfo = styled.div`
|
||||
padding: 20px;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
display: ${props => (props.$state === 'active' ? 'block' : 'none')};
|
||||
`;
|
||||
|
||||
const BoxWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
`;
|
||||
|
||||
const InfoItem = styled.div`
|
||||
width: 18%;
|
||||
background: #fff;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 15px;
|
||||
`;
|
||||
|
||||
const InfoTitle = styled.div`
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const InfoValue = styled.div`
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 5px 0;
|
||||
gap: 5px 0;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
`;
|
||||
@@ -26,7 +26,6 @@ const DailyDashBoard = ({ content }) => {
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await userTotalIndex(token).then(data => {
|
||||
console.log(data);
|
||||
setTotalData(data.dashboard);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { DailySearchBar } from './index';
|
||||
import { DailyMedalView } from '../../apis';
|
||||
|
||||
const DailyMedalContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await DailyMedalView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Daily_Medal.xlsx';
|
||||
//DailyActiveUserExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead >
|
||||
<tr>
|
||||
<th rowSpan="1" width="20">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
UserID
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
닉네임
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
Item ID
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
획득량
|
||||
</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{(dataList || []).map((data, index) => (
|
||||
<tr key={index}>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.dau}</td>
|
||||
<td>{data.dalc}</td>
|
||||
<td>{data.dglc}</td>
|
||||
<td>{data.maxAu}</td>
|
||||
{Array.from({ length: 24 }, (_, i) => (
|
||||
<td key={i}>{data['h' + i]}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DailyMedalContent;
|
||||
|
||||
152
src/components/IndexManage/ItemAcquireContent.js
Normal file
152
src/components/IndexManage/ItemAcquireContent.js
Normal file
@@ -0,0 +1,152 @@
|
||||
import React, { Fragment, useMemo, useRef } from 'react';
|
||||
|
||||
import {
|
||||
TableStyle,
|
||||
FormWrapper,
|
||||
TableWrapper, ListOption, TextInput, TableInfoContent, Notice,
|
||||
} from '../../styles/Components';
|
||||
|
||||
import { ItemAcquireIndexSearchBar, useItemAcquireIndexSearch } from '../searchBar';
|
||||
import { TopButton, ViewTableInfo } from '../common';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { numberFormatter } from '../../utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AnimatedPageWrapper } from '../common/Layout';
|
||||
import CSVDownloadButton from '../common/button/CsvDownButton';
|
||||
|
||||
const ItemAcquireContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const {
|
||||
searchParams,
|
||||
loading: dataLoading,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
updateSearchParams
|
||||
} = useItemAcquireIndexSearch(token);
|
||||
|
||||
const tableHeaders = useMemo(() => {
|
||||
return [
|
||||
{ id: 'logDay', label: '일자', width: '100px' },
|
||||
{ id: 'mail', label: '우편', width: '80px' },
|
||||
{ id: 'shopPurchase', label: '상점 구매', width: '80px' },
|
||||
{ id: 'shopRePurchase', label: '상점 재구매', width: '80px' },
|
||||
{ id: 'itemBuy', label: '아이템 구매', width: '80px' },
|
||||
{ id: 'itemUse', label: '아이템 사용', width: '80px' },
|
||||
{ id: 'seasonPass', label: '시즌 패스', width: '80px' },
|
||||
{ id: 'claim', label: '클레임', width: '80px' },
|
||||
{ id: 'quest', label: '퀘스트', width: '80px' },
|
||||
{ id: 'ugq', label: 'UGQ', width: '80px' },
|
||||
{ id: 'battleObject', label: '배틀맵', width: '80px' },
|
||||
{ id: 'runRace', label: '런레이스', width: '80px' },
|
||||
{ id: 'prop', label: '보급품 상자', width: '80px' },
|
||||
{ id: 'randomBox', label: '랜덤박스', width: '80px' },
|
||||
{ id: 'beacon', label: '비컨', width: '80px' },
|
||||
{ id: 'beaconShop', label: '비컨 상점', width: '80px' },
|
||||
{ id: 'myHome', label: '마이홈', width: '80px' },
|
||||
{ id: 'craft', label: '크래프트', width: '80px' },
|
||||
{ id: 'etc', label: '기타', width: '80px' },
|
||||
{ id: 'summary', label: '합계', width: '80px' },
|
||||
];
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<FormWrapper>
|
||||
<ItemAcquireIndexSearchBar
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
handleSearch(newParams);
|
||||
} else {
|
||||
updateSearchParams(newParams);
|
||||
}
|
||||
}}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
</FormWrapper>
|
||||
<ViewTableInfo >
|
||||
{dataList?.item_list && dataList.item_list.length > 0 &&
|
||||
<TableInfoContent>
|
||||
<TextInput
|
||||
type="text"
|
||||
value={dataList.item_list[0].itemId}
|
||||
width="100px"
|
||||
readOnly
|
||||
/>
|
||||
<TextInput
|
||||
type="text"
|
||||
value={dataList.item_list[0].itemName}
|
||||
width="150px"
|
||||
readOnly
|
||||
/>
|
||||
<Notice $color='#F15F5F'>* 확인되지 않은 액션이 있을 수 있습니다</Notice>
|
||||
</TableInfoContent>
|
||||
}
|
||||
<ListOption>
|
||||
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_ITEM_ACQUIRE')} />
|
||||
</ListOption>
|
||||
</ViewTableInfo>
|
||||
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||
<>
|
||||
<TableWrapper>
|
||||
<TableStyle ref={tableRef}>
|
||||
<thead>
|
||||
<tr>
|
||||
{tableHeaders.map(header => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
width={header.width}
|
||||
colSpan={header.colSpan}
|
||||
>
|
||||
{header.label}
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{dataList?.item_list?.map((item, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{item.logDay}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.MailTaken)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopRePurchase)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemBuy)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemUse)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.SeasonPassTakeReward)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ClaimReward)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.QuestMainReward || 0) + (item.actionSummary.QuestTaskUpdate || 0) + (item.actionSummary.QuestMainTask || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.UgqAbort)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.BattleRoundStateUpdate || 0) + (item.actionSummary.BattlePodCombatOccupyReward || 0) + (item.actionSummary.BattleObjectInteraction || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.RunRaceFinishReward || 0) + (item.actionSummary.RunRaceRespawnReward || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.RewardProp)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconCreate || 0) + (item.actionSummary.BeaconEdit || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconShopPurchaseItem || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.DeleteMyhome || 0) + (item.actionSummary.SaveMyhome || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.CraftFinish || 0) + (item.actionSummary.CraftStop || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.CheatCommandItem || 0) + (item.actionSummary.CharacterAppearanceUpdate || 0)
|
||||
+ (item.actionSummary.ItemTattooLevelUp || 0) + (item.actionSummary.UserCreate || 0) + (item.actionSummary.JoinInstance || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.totalDeltaCount)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<TopButton />
|
||||
</>
|
||||
}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemAcquireContent;
|
||||
119
src/components/IndexManage/ItemAssetsContent.js
Normal file
119
src/components/IndexManage/ItemAssetsContent.js
Normal file
@@ -0,0 +1,119 @@
|
||||
import React, { Fragment, useMemo, useRef } from 'react';
|
||||
|
||||
import {
|
||||
TableStyle,
|
||||
FormWrapper,
|
||||
TableWrapper, ListOption
|
||||
} from '../../styles/Components';
|
||||
|
||||
import {
|
||||
AssetsIndexSearchBar, useAssetsIndexSearch,
|
||||
} from '../searchBar';
|
||||
import { TopButton, ViewTableInfo } from '../common';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { numberFormatter } from '../../utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AnimatedPageWrapper } from '../common/Layout';
|
||||
import CSVDownloadButton from '../common/button/CsvDownButton';
|
||||
import DailyDashBoard from './DailyCaliumDashBoard';
|
||||
|
||||
const ItemAssetsContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const {
|
||||
searchParams,
|
||||
loading: dataLoading,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
updateSearchParams
|
||||
} = useAssetsIndexSearch(token);
|
||||
|
||||
const tableHeaders = useMemo(() => {
|
||||
return [
|
||||
{ id: 'logDay', label: '일자', width: '100px' },
|
||||
{ id: 'userCount', label: '유저수', width: '100px' },
|
||||
{ id: 'item_13080002', label: '퀘스트 메달', width: '100px' },
|
||||
{ id: 'item_13080004', label: '보급품 메달', width: '100px' },
|
||||
{ id: 'item_13080005', label: '제작 메달', width: '100px' },
|
||||
{ id: 'item_13080006', label: '에테론 315 포드', width: '100px' },
|
||||
{ id: 'item_13080007', label: '에테론 316 포드', width: '100px' },
|
||||
{ id: 'item_13080008', label: '에테론 317 포드', width: '100px' },
|
||||
{ id: 'item_13080009', label: '에테론 318 포드', width: '100px' },
|
||||
{ id: 'item_11570001', label: '강화 잉크', width: '100px' },
|
||||
{ id: 'item_11570002', label: '연성 잉크', width: '100px' }
|
||||
];
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<DailyDashBoard />
|
||||
<FormWrapper>
|
||||
<AssetsIndexSearchBar
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
handleSearch(newParams);
|
||||
} else {
|
||||
updateSearchParams(newParams);
|
||||
}
|
||||
}}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
</FormWrapper>
|
||||
<ViewTableInfo >
|
||||
<ListOption>
|
||||
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_ASSETS_ITEM')} />
|
||||
</ListOption>
|
||||
</ViewTableInfo>
|
||||
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||
<>
|
||||
<TableWrapper>
|
||||
<TableStyle ref={tableRef}>
|
||||
<thead>
|
||||
<tr>
|
||||
{tableHeaders.map(header => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
width={header.width}
|
||||
colSpan={header.colSpan}
|
||||
>
|
||||
{header.label}
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{dataList?.assets_list?.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.logDay}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.userCount)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_13080002)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_13080004)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_13080005)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_13080006)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_13080007)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_13080008)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_13080009)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_11570001)}</td>
|
||||
<td>{numberFormatter.formatCurrency(data.item_11570002)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<TopButton />
|
||||
</>
|
||||
}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemAssetsContent;
|
||||
143
src/components/IndexManage/ItemConsumeContent.js
Normal file
143
src/components/IndexManage/ItemConsumeContent.js
Normal file
@@ -0,0 +1,143 @@
|
||||
import React, { Fragment, useMemo, useRef } from 'react';
|
||||
|
||||
import {
|
||||
TableStyle,
|
||||
FormWrapper,
|
||||
TableWrapper, ListOption, TableInfoContent, TextInput, Label, Notice,
|
||||
} from '../../styles/Components';
|
||||
|
||||
import {
|
||||
ItemAcquireIndexSearchBar,
|
||||
ItemConsumeIndexSearchBar,
|
||||
useItemAcquireIndexSearch,
|
||||
useItemConsumeIndexSearch,
|
||||
} from '../searchBar';
|
||||
import { TopButton, ViewTableInfo } from '../common';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { numberFormatter } from '../../utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AnimatedPageWrapper } from '../common/Layout';
|
||||
import CSVDownloadButton from '../common/button/CsvDownButton';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const ItemConsumeContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const {
|
||||
searchParams,
|
||||
loading: dataLoading,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
updateSearchParams
|
||||
} = useItemConsumeIndexSearch(token);
|
||||
|
||||
const tableHeaders = useMemo(() => {
|
||||
return [
|
||||
{ id: 'logDay', label: '일자', width: '100px' },
|
||||
{ id: 'shopSell', label: '상점 판매', width: '80px' },
|
||||
{ id: 'itemUse', label: '아이템 사용', width: '80px' },
|
||||
{ id: 'beaconShop', label: '비컨상점', width: '80px' },
|
||||
{ id: 'beacon', label: '비컨', width: '80px' },
|
||||
{ id: 'quest', label: '퀘스트', width: '80px' },
|
||||
{ id: 'ugq', label: 'UGQ', width: '80px' },
|
||||
{ id: 'randomBox', label: '랜덤박스', width: '80px' },
|
||||
{ id: 'myHome', label: '마이홈', width: '80px' },
|
||||
{ id: 'craft', label: '크래프트', width: '80px' },
|
||||
{ id: 'etc', label: '기타', width: '80px' },
|
||||
{ id: 'summary', label: '합계', width: '80px' },
|
||||
];
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<FormWrapper>
|
||||
<ItemConsumeIndexSearchBar
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
handleSearch(newParams);
|
||||
} else {
|
||||
updateSearchParams(newParams);
|
||||
}
|
||||
}}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
</FormWrapper>
|
||||
<ViewTableInfo >
|
||||
{dataList?.item_list && dataList.item_list.length > 0 &&
|
||||
<TableInfoContent>
|
||||
<TextInput
|
||||
type="text"
|
||||
value={dataList.item_list[0].itemId}
|
||||
width="100px"
|
||||
readOnly
|
||||
/>
|
||||
<TextInput
|
||||
type="text"
|
||||
value={dataList.item_list[0].itemName}
|
||||
width="300px"
|
||||
readOnly
|
||||
/>
|
||||
<Notice $color='#F15F5F'>* 확인되지 않은 액션이 있을 수 있습니다</Notice>
|
||||
</TableInfoContent>
|
||||
}
|
||||
<ListOption>
|
||||
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_ITEM_CONSUME')} />
|
||||
</ListOption>
|
||||
</ViewTableInfo>
|
||||
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||
<>
|
||||
<TableWrapper>
|
||||
<TableStyle ref={tableRef}>
|
||||
<thead>
|
||||
<tr>
|
||||
{tableHeaders.map(header => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
width={header.width}
|
||||
colSpan={header.colSpan}
|
||||
>
|
||||
{header.label}
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{dataList?.item_list?.map((item, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{item.logDay}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopSell)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemUse)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconShopRegisterItem || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconEdit || 0) + (item.actionSummary.BeaconCreate || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.QuestTaskUpdate || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.UgqAbort)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.SaveMyhome || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency((item.actionSummary.CraftStart || 0))}</td>
|
||||
<td>{numberFormatter.formatCurrency(
|
||||
(item.actionSummary.SummonParty || 0) + (item.actionSummary.ItemDestroy || 0) + (item.actionSummary.CreatePartyInstance || 0) + (item.actionSummary.ItemTattooChangeAttribute || 0)
|
||||
+ (item.actionSummary.CheatCommandItem || 0) + (item.actionSummary.ItemDestoryByExpiration || 0) + (item.actionSummary.ItemDestroyByUser || 0) + (item.actionSummary.ItemTattooLevelUp || 0)
|
||||
)}</td>
|
||||
<td>{numberFormatter.formatCurrency(item.totalDeltaCount)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<TopButton />
|
||||
</>
|
||||
}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemConsumeContent;
|
||||
@@ -1,112 +0,0 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { PlayTimeSearchBar } from '../../components/IndexManage/index';
|
||||
import { PlaytimeIndexExport, PlaytimeIndexView } from '../../apis';
|
||||
|
||||
const PlayTimeContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// 이용자 지표 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await PlaytimeIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_PlayTime_Index.xlsx';
|
||||
PlaytimeIndexExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PlayTimeSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan="2" width="140">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="4" width="520">
|
||||
유저수
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
총 누적 플레이타임(분)
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
1인당 평균 플레이타임(분)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>30분 이내</th>
|
||||
<th>30분 ~ 1시간</th>
|
||||
<th>1시간 ~ 3시간</th>
|
||||
<th>3시간 이상</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.playtime &&
|
||||
dataList.playtime.map(time => (
|
||||
<tr key={time.date}>
|
||||
<td>{time.date}</td>
|
||||
{time.user_cnt.map((cnt, index) => (
|
||||
<td className="text-left" key={index}>
|
||||
{cnt}
|
||||
</td>
|
||||
))}
|
||||
<td className="text-left">{time.total_time}</td>
|
||||
<td className="text-left">{time.average_time}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTimeContent;
|
||||
@@ -5,8 +5,8 @@ import { AnimatedPageWrapper } from '../common/Layout';
|
||||
import { useRetentionSearch, RetentionSearchBar } from '../searchBar';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { numberFormatter } from '../../utils';
|
||||
import { ExcelDownButton } from '../common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CSVDownloadButton from '../common/button/CsvDownButton';
|
||||
|
||||
const RetentionContent = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -38,7 +38,7 @@ const RetentionContent = () => {
|
||||
/>
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_INDEX_USER_RETENTION')} />
|
||||
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_USER_RETENTION')} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { SegmentSearchBar } from '../../components/IndexManage/index';
|
||||
import { SegmentIndexExport, SegmentIndexView } from '../../apis';
|
||||
|
||||
const SegmentContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(END_DATE);
|
||||
}, []);
|
||||
|
||||
// Retention 지표 데이터
|
||||
const fetchData = async endDate => {
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await SegmentIndexView(token, endDateToLocal));
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = end_dt => {
|
||||
fetchData(end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Segment_Index.xlsx';
|
||||
SegmentIndexExport(token, fileName, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SegmentSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="1" width="200">
|
||||
{dataList && dataList.start_dt} ~ {dataList && dataList.end_dt}
|
||||
</th>
|
||||
<th colSpan="2" width="400">
|
||||
KIP
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* <th>국가</th> */}
|
||||
<th>세그먼트 분류</th>
|
||||
<th>AU</th>
|
||||
<th>AU Percentage by User Segment (%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList && dataList.segment &&
|
||||
dataList.segment.map((segment, index) => (
|
||||
<tr key={index}>
|
||||
{/* <td rowSpan="6">TH</td> */}
|
||||
<td>{segment.type}</td>
|
||||
<td>{segment.au}</td>
|
||||
<td>{segment.dif}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SegmentContent;
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { DailyDashBoard } from '../../components/IndexManage/index';
|
||||
|
||||
import { Title, TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { UserIndexSearchBar, DailyDashBoard } from '../../components/IndexManage/index';
|
||||
|
||||
import { userIndexView, userIndexExport } from '../../apis';
|
||||
import Loading from '../common/Loading';
|
||||
import { userIndexView } from '../../apis';
|
||||
import { ExcelDownButton } from '../common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatStringDate } from '../../utils';
|
||||
import { AnimatedPageWrapper } from '../common/Layout';
|
||||
import { UserIndexSearchBar } from '../searchBar';
|
||||
|
||||
const UserContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
import VBPSearchBar from '../searchBar/VBPSearchBar';
|
||||
import { VBPIndexExport, VbpIndexView } from '../../apis';
|
||||
|
||||
const VBPContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await VbpIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_VBP_Index.xlsx';
|
||||
VBPIndexExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<VBPSearchBar fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<EconomicTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="2" className="text-center" width="300">
|
||||
Product
|
||||
</th>
|
||||
<th width="160">2023-08-07</th>
|
||||
<th width="160">2023-08-08</th>
|
||||
<th width="160">2023-08-09</th>
|
||||
<th width="160">2023-08-10</th>
|
||||
<th width="160">2023-08-11</th>
|
||||
<th width="160">2023-08-12</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 생산량</TableTitle>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 소진량</TableTitle>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 보유량</TableTitle>
|
||||
<TableData>3.2M</TableData>
|
||||
<TableData>3.3M</TableData>
|
||||
<TableData>3.3M</TableData>
|
||||
<TableData>3.4M</TableData>
|
||||
<TableData>3.5M</TableData>
|
||||
<TableData>3.5M</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">GET</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">USE</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default VBPContent;
|
||||
|
||||
const TableWrapper = styled.div`
|
||||
width: 100%;
|
||||
min-width: 680px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
${TableStyle} {
|
||||
width: 100%;
|
||||
min-width: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
tbody {
|
||||
tr:nth-child(1),
|
||||
tr:nth-child(2),
|
||||
tr:nth-child(3) {
|
||||
background: #f5fcff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1,27 +1,17 @@
|
||||
import UserIndexSearchBar from "../searchBar/UserIndexSearchBar";
|
||||
import RetentionSearchBar from "../searchBar/RetentionSearchBar";
|
||||
import SegmentSearchBar from "../searchBar/SegmentSearchBar";
|
||||
import DailyDashBoard from "./DailyDashBoard";
|
||||
import PlayTimeSearchBar from "../searchBar/PlayTimeSearchBar";
|
||||
import UserContent from "./UserContent";
|
||||
import PlayTimeContent from "./PlayTimeContent";
|
||||
import RetentionContent from "./RetentionContent";
|
||||
import SegmentContent from "./SegmentContent";
|
||||
import DailyActiveUserContent from "./DailyActiveUserContent";
|
||||
import DailyMedalContent from "./DailyMedalContent";
|
||||
import DailySearchBar from "../searchBar/DailySearchBar";
|
||||
import CurrencyConsumeContent from "./CurrencyConsumeContent";
|
||||
import CurrencyAcquireContent from "./CurrencyAcquireContent";
|
||||
import ItemAcquireContent from "./ItemAcquireContent";
|
||||
import ItemConsumeContent from "./ItemConsumeContent";
|
||||
|
||||
export {
|
||||
UserIndexSearchBar,
|
||||
RetentionSearchBar,
|
||||
SegmentSearchBar,
|
||||
DailyDashBoard,
|
||||
PlayTimeSearchBar,
|
||||
DailyDashBoard,
|
||||
UserContent,
|
||||
SegmentContent,
|
||||
RetentionContent,
|
||||
PlayTimeContent,
|
||||
DailySearchBar,
|
||||
DailyActiveUserContent,
|
||||
DailyMedalContent,
|
||||
CurrencyConsumeContent,
|
||||
CurrencyAcquireContent,
|
||||
ItemAcquireContent,
|
||||
ItemConsumeContent
|
||||
};
|
||||
390
src/components/common/button/CsvDownButton.js
Normal file
390
src/components/common/button/CsvDownButton.js
Normal file
@@ -0,0 +1,390 @@
|
||||
import { ExcelDownButton } from '../../../styles/ModuleComponents';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
const CSVDownloadButton = ({ tableRef, data, fileName = 'download.csv', onLoadingChange }) => {
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const [lastProgress, setLastProgress] = useState(0);
|
||||
|
||||
// 타임아웃 감지 및 처리
|
||||
useEffect(() => {
|
||||
let timeoutTimer;
|
||||
|
||||
if (isDownloading && lastProgress >= 95) {
|
||||
// 최종 단계에서 타임아웃 감지 타이머 설정
|
||||
timeoutTimer = setTimeout(() => {
|
||||
// 진행 상태가 여전히 변하지 않았다면 타임아웃으로 간주
|
||||
if (isDownloading && lastProgress >= 95) {
|
||||
console.log("CSV download timeout detected, completing process");
|
||||
setIsDownloading(false);
|
||||
if (onLoadingChange) {
|
||||
onLoadingChange({ loading: false, progress: 100 });
|
||||
}
|
||||
}
|
||||
}, 10000); // 10초 타임아웃 (CSV는 Excel보다 빠르므로 시간 단축)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (timeoutTimer) clearTimeout(timeoutTimer);
|
||||
};
|
||||
}, [isDownloading, lastProgress, onLoadingChange]);
|
||||
|
||||
const flattenObject = (obj, prefix = '') => {
|
||||
return Object.keys(obj).reduce((acc, key) => {
|
||||
const prefixedKey = prefix ? `${prefix}.${key}` : key;
|
||||
|
||||
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
|
||||
Object.assign(acc, flattenObject(obj[key], prefixedKey));
|
||||
} else if (Array.isArray(obj[key])) {
|
||||
// 배열은 JSON 문자열로 변환
|
||||
acc[prefixedKey] = JSON.stringify(obj[key]);
|
||||
} else {
|
||||
acc[prefixedKey] = obj[key];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const updateLoadingState = (newProgress) => {
|
||||
setLastProgress(newProgress);
|
||||
if (onLoadingChange && typeof onLoadingChange === 'function') {
|
||||
onLoadingChange({loading: true, progress: newProgress});
|
||||
}
|
||||
};
|
||||
|
||||
// CSV 문자열 이스케이프 처리
|
||||
const escapeCSVField = (field) => {
|
||||
if (field === null || field === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const str = String(field);
|
||||
|
||||
// 쉼표, 따옴표, 줄바꿈이 있는 경우 따옴표로 감싸고 내부 따옴표는 두 개로 변환
|
||||
if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes('\r')) {
|
||||
return '"' + str.replace(/"/g, '""') + '"';
|
||||
}
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
// 배열을 CSV 행으로 변환
|
||||
const arrayToCSVRow = (array) => {
|
||||
return array.map(escapeCSVField).join(',');
|
||||
};
|
||||
|
||||
const downloadTableCSV = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!tableRef || !tableRef.current) {
|
||||
reject(new Error('테이블 참조가 없습니다.'));
|
||||
return;
|
||||
}
|
||||
|
||||
updateLoadingState(10);
|
||||
|
||||
// 메인 스레드에서 데이터 추출
|
||||
const tableElement = tableRef.current;
|
||||
const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
|
||||
const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
||||
|
||||
// 일반 행만 포함 (상세 행 제외)
|
||||
const normalBodyRows = Array.from(bodyRows).filter(row => {
|
||||
const hasTdWithColspan = Array.from(row.cells).some(cell => cell.hasAttribute('colspan'));
|
||||
return !hasTdWithColspan;
|
||||
});
|
||||
|
||||
// 헤더 데이터 추출
|
||||
const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);
|
||||
|
||||
updateLoadingState(30);
|
||||
|
||||
// 바디 데이터 추출
|
||||
const bodyData = normalBodyRows.map(row =>
|
||||
Array.from(row.cells).map(cell => cell.textContent)
|
||||
);
|
||||
|
||||
updateLoadingState(50);
|
||||
|
||||
// CSV 생성을 비동기로 처리
|
||||
setTimeout(() => {
|
||||
try {
|
||||
// CSV 문자열 생성
|
||||
let csvContent = '';
|
||||
|
||||
// 헤더 추가
|
||||
csvContent += arrayToCSVRow(headers) + '\n';
|
||||
|
||||
updateLoadingState(70);
|
||||
|
||||
// 데이터 행들을 청크로 처리
|
||||
const chunkSize = 1000;
|
||||
let currentIndex = 0;
|
||||
|
||||
function processDataChunk() {
|
||||
const end = Math.min(currentIndex + chunkSize, bodyData.length);
|
||||
|
||||
for (let i = currentIndex; i < end; i++) {
|
||||
csvContent += arrayToCSVRow(bodyData[i]) + '\n';
|
||||
}
|
||||
|
||||
currentIndex = end;
|
||||
const progress = 70 + Math.floor((currentIndex / bodyData.length) * 20);
|
||||
updateLoadingState(progress);
|
||||
|
||||
if (currentIndex < bodyData.length) {
|
||||
// 아직 처리할 데이터가 남아있으면 다음 청크 처리 예약
|
||||
setTimeout(processDataChunk, 0);
|
||||
} else {
|
||||
// 모든 데이터 처리 완료 후 다운로드
|
||||
finishCSVDownload(csvContent);
|
||||
}
|
||||
}
|
||||
|
||||
function finishCSVDownload(csvContent) {
|
||||
try {
|
||||
updateLoadingState(95);
|
||||
|
||||
// BOM 추가 (한글 깨짐 방지)
|
||||
const BOM = '\uFEFF';
|
||||
const csvWithBOM = BOM + csvContent;
|
||||
|
||||
// Blob 생성 및 다운로드
|
||||
const blob = new Blob([csvWithBOM], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
|
||||
if (link.download !== undefined) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', fileName);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
updateLoadingState(100);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 첫 번째 청크 처리 시작
|
||||
processDataChunk();
|
||||
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const chunkArray = (array, chunkSize) => {
|
||||
const chunks = [];
|
||||
for (let i = 0; i < array.length; i += chunkSize) {
|
||||
chunks.push(array.slice(i, i + chunkSize));
|
||||
}
|
||||
return chunks;
|
||||
};
|
||||
|
||||
const downloadDataCSV = async () => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if (!data || data.length === 0) {
|
||||
reject(new Error('다운로드할 데이터가 없습니다.'));
|
||||
return;
|
||||
}
|
||||
|
||||
updateLoadingState(5);
|
||||
|
||||
// 데이터 플랫 변환 과정을 더 작은 청크로 나누기
|
||||
const dataChunkSize = 2000;
|
||||
const dataChunks = chunkArray(data, dataChunkSize);
|
||||
let flattenedData = [];
|
||||
|
||||
for (let i = 0; i < dataChunks.length; i++) {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
// 청크 내 아이템들을 플랫하게 변환
|
||||
const chunkData = dataChunks[i].map(item => {
|
||||
// 기본 필드
|
||||
const baseData = {
|
||||
'logTime': item.logTime,
|
||||
'GUID': item.userGuid === 'None' ? '' : item.userGuid,
|
||||
'Nickname': item.userNickname === 'None' ? '' : item.userNickname,
|
||||
'Account ID': item.accountId === 'None' ? '' : item.accountId,
|
||||
'Action': item.action,
|
||||
'Domain': item.domain === 'None' ? '' : item.domain,
|
||||
'Tran ID': item.tranId
|
||||
};
|
||||
|
||||
// Actor 데이터 플랫하게 추가
|
||||
const actorData = item.header && item.header.Actor ?
|
||||
flattenObject(item.header.Actor, 'Actor') : {};
|
||||
|
||||
// Infos 데이터 플랫하게 추가
|
||||
let infosData = {};
|
||||
if (item.body && item.body.Infos && Array.isArray(item.body.Infos)) {
|
||||
item.body.Infos.forEach((info) => {
|
||||
infosData = {
|
||||
...infosData,
|
||||
...flattenObject(info, `Info`)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...baseData,
|
||||
...actorData,
|
||||
...infosData
|
||||
};
|
||||
});
|
||||
|
||||
flattenedData = [...flattenedData, ...chunkData];
|
||||
const progress = 5 + Math.floor((i + 1) / dataChunks.length * 10);
|
||||
updateLoadingState(progress);
|
||||
resolve();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
// 모든 항목의 모든 키 수집하여 헤더 생성
|
||||
const allKeys = new Set();
|
||||
|
||||
// 헤더 수집도 청크로 나누기
|
||||
for (let i = 0; i < flattenedData.length; i += dataChunkSize) {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
const end = Math.min(i + dataChunkSize, flattenedData.length);
|
||||
for (let j = i; j < end; j++) {
|
||||
Object.keys(flattenedData[j]).forEach(key => allKeys.add(key));
|
||||
}
|
||||
const progress = 15 + Math.floor((i + dataChunkSize) / flattenedData.length * 5);
|
||||
updateLoadingState(progress);
|
||||
resolve();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
const headers = Array.from(allKeys);
|
||||
updateLoadingState(25);
|
||||
|
||||
// CSV 생성
|
||||
let csvContent = '';
|
||||
|
||||
// 헤더 추가
|
||||
csvContent += arrayToCSVRow(headers) + '\n';
|
||||
updateLoadingState(30);
|
||||
|
||||
// 청크로 데이터 나누기
|
||||
const chunkSize = 1000;
|
||||
const rowChunks = chunkArray(flattenedData, chunkSize);
|
||||
|
||||
// 각 청크 처리
|
||||
let processedCount = 0;
|
||||
for (const chunk of rowChunks) {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
// 각 청크의 데이터를 CSV 행으로 변환
|
||||
chunk.forEach(item => {
|
||||
const row = headers.map(header => {
|
||||
const value = item[header] !== undefined ? item[header] : '';
|
||||
return value;
|
||||
});
|
||||
csvContent += arrayToCSVRow(row) + '\n';
|
||||
});
|
||||
|
||||
processedCount += chunk.length;
|
||||
|
||||
// 진행률 계산 및 콜백 호출
|
||||
const newProgress = Math.min(90, Math.round((processedCount / flattenedData.length) * 60) + 30);
|
||||
updateLoadingState(newProgress);
|
||||
|
||||
resolve();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
// 메모리 정리를 위한 가비지 컬렉션 힌트
|
||||
if (processedCount % (chunkSize * 5) === 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
}
|
||||
}
|
||||
|
||||
updateLoadingState(95);
|
||||
|
||||
// 파일 다운로드
|
||||
setTimeout(() => {
|
||||
try {
|
||||
// BOM 추가 (한글 깨짐 방지)
|
||||
const BOM = '\uFEFF';
|
||||
const csvWithBOM = BOM + csvContent;
|
||||
|
||||
// Blob 생성 및 다운로드
|
||||
const blob = new Blob([csvWithBOM], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
|
||||
if (link.download !== undefined) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', fileName);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
updateLoadingState(100);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDownload = useCallback(async () => {
|
||||
if (isDownloading) return; // 이미 다운로드 중이면 중복 실행 방지
|
||||
|
||||
setIsDownloading(true);
|
||||
setLastProgress(0);
|
||||
if (onLoadingChange) onLoadingChange({loading: true, progress: 0});
|
||||
|
||||
try {
|
||||
if (tableRef) {
|
||||
await downloadTableCSV();
|
||||
} else if (data) {
|
||||
await downloadDataCSV();
|
||||
} else {
|
||||
alert('유효한 데이터 소스가 없습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('CSV download failed:', error);
|
||||
alert('CSV 다운로드 중 오류가 발생했습니다.');
|
||||
} finally {
|
||||
// 다운로드 완료 후 짧은 지연 시간을 두어 100% 상태를 잠시 보여줌
|
||||
setTimeout(() => {
|
||||
setIsDownloading(false);
|
||||
if (onLoadingChange) onLoadingChange({loading: false, progress: 100});
|
||||
}, 500);
|
||||
}
|
||||
}, [tableRef, data, fileName, isDownloading, onLoadingChange]);
|
||||
|
||||
return (
|
||||
<ExcelDownButton onClick={handleDownload} disabled={isDownloading}>
|
||||
{isDownloading ? '다운로드 중...' : '엑셀 다운로드'}
|
||||
</ExcelDownButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default CSVDownloadButton;
|
||||
@@ -97,20 +97,20 @@ const LogDetailModal = ({ detailView,
|
||||
const allChangedItems = [];
|
||||
|
||||
changedData.forEach((item, itemIndex) => {
|
||||
if (item.changed && Array.isArray(item.changed)) {
|
||||
item.changed.forEach((changedItem) => {
|
||||
if (item.domain.changed && Array.isArray(item.domain.changed)) {
|
||||
item.domain.changed.forEach((changedItem) => {
|
||||
allChangedItems.push({
|
||||
...changedItem,
|
||||
// 어떤 데이터 항목에서 온 것인지 구분하기 위한 정보 추가
|
||||
sourceIndex: itemIndex,
|
||||
sourceInfo: {
|
||||
dbType: item.dbType,
|
||||
timestamp: item.timestamp,
|
||||
operationType: item.operationType,
|
||||
logTime: item.logTime,
|
||||
operationType: item.domain.operationType,
|
||||
historyType: item.historyType,
|
||||
tableName: item.tableName,
|
||||
tranId: item.tranId,
|
||||
userId: item.userId
|
||||
worker: item.worker
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -164,8 +164,8 @@ const LogDetailModal = ({ detailView,
|
||||
<td>{item.fieldName}</td>
|
||||
<td>{formatValue(item.newValue)}</td>
|
||||
<td>{formatValue(item.oldValue)}</td>
|
||||
<td>{item.sourceInfo.userId}</td>
|
||||
<td>{convertKTC(item.sourceInfo.timestamp, false)}</td>
|
||||
<td>{item.sourceInfo.worker}</td>
|
||||
<td>{convertKTC(item.sourceInfo.logTime, false)}</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
|
||||
150
src/components/searchBar/AssetsIndexSearchBar.js
Normal file
150
src/components/searchBar/AssetsIndexSearchBar.js
Normal file
@@ -0,0 +1,150 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { InputLabel, TextInput } from '../../styles/Components';
|
||||
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { AssetsIndexView, CurrencyAcquireIndexView, ItemIndexView } from '../../apis';
|
||||
|
||||
export const useAssetsIndexSearch = (token, initialPageSize) => {
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
start_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 1);
|
||||
return date;
|
||||
})(),
|
||||
end_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate());
|
||||
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 AssetsIndexView(
|
||||
token,
|
||||
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);
|
||||
return result;
|
||||
} 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, updateSearchParams, fetchData]);
|
||||
|
||||
const handleReset = useCallback(async () => {
|
||||
const now = new Date();
|
||||
now.setDate(now.getDate() - 1);
|
||||
const resetParams = {
|
||||
start_dt: now,
|
||||
end_dt: new Date(),
|
||||
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 AssetsIndexSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
|
||||
onSearch(searchParams, true);
|
||||
};
|
||||
|
||||
const searchList = [
|
||||
<>
|
||||
<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 AssetsIndexSearchBar;
|
||||
165
src/components/searchBar/CurrencyAcquireIndexSearchBar.js
Normal file
165
src/components/searchBar/CurrencyAcquireIndexSearchBar.js
Normal file
@@ -0,0 +1,165 @@
|
||||
import { InputLabel, SelectInput } from '../../styles/Components';
|
||||
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { CurrencyType } from '../../assets/data';
|
||||
import { CurrencyAcquireIndexView } from '../../apis';
|
||||
|
||||
export const useCurrencyAcquireIndexSearch = (token, initialPageSize) => {
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
start_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 1);
|
||||
return date;
|
||||
})(),
|
||||
end_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate());
|
||||
return date;
|
||||
})(),
|
||||
currency_type: 'Gold',
|
||||
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 CurrencyAcquireIndexView(
|
||||
token,
|
||||
params.start_dt.toISOString(),
|
||||
params.end_dt.toISOString(),
|
||||
params.currency_type,
|
||||
"Acquire",
|
||||
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);
|
||||
return result;
|
||||
} 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 = {
|
||||
start_dt: now,
|
||||
end_dt: new Date(),
|
||||
currency_type: 'Gold',
|
||||
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 CurrencyAcquireIndexSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
|
||||
onSearch(searchParams, true);
|
||||
};
|
||||
|
||||
const searchList = [
|
||||
<>
|
||||
<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)}
|
||||
/>
|
||||
</>,
|
||||
<>
|
||||
<InputLabel>재화종류</InputLabel>
|
||||
<SelectInput value={searchParams.currency_type} onChange={e => onSearch({ currency_type: e.target.value }, false)} >
|
||||
{CurrencyType.map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</>,
|
||||
];
|
||||
|
||||
return <SearchBarLayout firstColumnData={searchList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
export default CurrencyAcquireIndexSearchBar;
|
||||
165
src/components/searchBar/CurrencyConsumeIndexSearchBar.js
Normal file
165
src/components/searchBar/CurrencyConsumeIndexSearchBar.js
Normal file
@@ -0,0 +1,165 @@
|
||||
import { InputLabel, SelectInput } from '../../styles/Components';
|
||||
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { CurrencyType } from '../../assets/data';
|
||||
import { CurrencyAcquireIndexView } from '../../apis';
|
||||
|
||||
export const useCurrencyConsumeIndexSearch = (token, initialPageSize) => {
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
start_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 1);
|
||||
return date;
|
||||
})(),
|
||||
end_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate());
|
||||
return date;
|
||||
})(),
|
||||
currency_type: 'Gold',
|
||||
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 CurrencyAcquireIndexView(
|
||||
token,
|
||||
params.start_dt.toISOString(),
|
||||
params.end_dt.toISOString(),
|
||||
params.currency_type,
|
||||
"Consume",
|
||||
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);
|
||||
return result;
|
||||
} 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 = {
|
||||
start_dt: now,
|
||||
end_dt: new Date(),
|
||||
currency_type: 'Gold',
|
||||
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 CurrencyConsumeIndexSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
|
||||
onSearch(searchParams, true);
|
||||
};
|
||||
|
||||
const searchList = [
|
||||
<>
|
||||
<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)}
|
||||
/>
|
||||
</>,
|
||||
<>
|
||||
<InputLabel>재화종류</InputLabel>
|
||||
<SelectInput value={searchParams.currency_type} onChange={e => onSearch({ currency_type: e.target.value }, false)} >
|
||||
{CurrencyType.map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</>,
|
||||
];
|
||||
|
||||
return <SearchBarLayout firstColumnData={searchList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
export default CurrencyConsumeIndexSearchBar;
|
||||
@@ -125,7 +125,7 @@ export const useCurrencyIndexSearch = (token, initialPageSize) => {
|
||||
};
|
||||
};
|
||||
|
||||
const CurrencyIndexSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
const CurrencyUserIndexSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -147,4 +147,4 @@ const CurrencyIndexSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
return <SearchBarLayout firstColumnData={searchList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
export default CurrencyIndexSearchBar;
|
||||
export default CurrencyUserIndexSearchBar;
|
||||
171
src/components/searchBar/ItemAcquireIndexSearchBar.js
Normal file
171
src/components/searchBar/ItemAcquireIndexSearchBar.js
Normal file
@@ -0,0 +1,171 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { InputLabel, TextInput } from '../../styles/Components';
|
||||
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { CurrencyAcquireIndexView, ItemIndexView } from '../../apis';
|
||||
|
||||
export const useItemAcquireIndexSearch = (token, initialPageSize) => {
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
start_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 1);
|
||||
return date;
|
||||
})(),
|
||||
end_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate());
|
||||
return date;
|
||||
})(),
|
||||
item_id: '',
|
||||
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 ItemIndexView(
|
||||
token,
|
||||
params.start_dt.toISOString(),
|
||||
params.end_dt.toISOString(),
|
||||
params.item_id,
|
||||
"Acquire",
|
||||
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);
|
||||
return result;
|
||||
} 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 = {
|
||||
start_dt: now,
|
||||
end_dt: new Date(),
|
||||
item_id: '',
|
||||
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 ItemAcquireIndexSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
|
||||
if(searchParams.item_id.length === 0 || searchParams.item_id === ''){
|
||||
showToast('ITEM_ID_EMPTY_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
onSearch(searchParams, true);
|
||||
};
|
||||
|
||||
const searchList = [
|
||||
<>
|
||||
<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)}
|
||||
/>
|
||||
</>,
|
||||
<>
|
||||
<InputLabel>아이템 ID</InputLabel>
|
||||
<TextInput
|
||||
type="text"
|
||||
placeholder={'아이템 ID 입력'}
|
||||
value={searchParams.item_id}
|
||||
width="300px"
|
||||
onChange={e => onSearch({ item_id: e.target.value }, false)}
|
||||
/>
|
||||
</>,
|
||||
];
|
||||
|
||||
return <SearchBarLayout firstColumnData={searchList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
export default ItemAcquireIndexSearchBar;
|
||||
171
src/components/searchBar/ItemConsumeIndexSearchBar.js
Normal file
171
src/components/searchBar/ItemConsumeIndexSearchBar.js
Normal file
@@ -0,0 +1,171 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { InputLabel, TextInput } from '../../styles/Components';
|
||||
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { CurrencyAcquireIndexView, ItemIndexView } from '../../apis';
|
||||
|
||||
export const useItemConsumeIndexSearch = (token, initialPageSize) => {
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
start_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 1);
|
||||
return date;
|
||||
})(),
|
||||
end_dt: (() => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate());
|
||||
return date;
|
||||
})(),
|
||||
item_id: '',
|
||||
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 ItemIndexView(
|
||||
token,
|
||||
params.start_dt.toISOString(),
|
||||
params.end_dt.toISOString(),
|
||||
params.item_id,
|
||||
"Consume",
|
||||
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);
|
||||
return result;
|
||||
} 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 = {
|
||||
start_dt: now,
|
||||
end_dt: new Date(),
|
||||
item_id: '',
|
||||
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 ItemConsumeIndexSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
|
||||
if(searchParams.item_id.length === 0 || searchParams.item_id === ''){
|
||||
showToast('ITEM_ID_EMPTY_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
onSearch(searchParams, true);
|
||||
};
|
||||
|
||||
const searchList = [
|
||||
<>
|
||||
<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)}
|
||||
/>
|
||||
</>,
|
||||
<>
|
||||
<InputLabel>아이템 ID</InputLabel>
|
||||
<TextInput
|
||||
type="text"
|
||||
placeholder={'아이템 ID 입력'}
|
||||
value={searchParams.item_id}
|
||||
width="300px"
|
||||
onChange={e => onSearch({ item_id: e.target.value }, false)}
|
||||
/>
|
||||
</>,
|
||||
];
|
||||
|
||||
return <SearchBarLayout firstColumnData={searchList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
export default ItemConsumeIndexSearchBar;
|
||||
175
src/components/searchBar/UserSnapshotLogSearchBar.js
Normal file
175
src/components/searchBar/UserSnapshotLogSearchBar.js
Normal 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 { getUserLoginDetailList, getUserSnapshotList } from '../../apis';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
|
||||
export const useUserSnapshotLogSearch = (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 getUserSnapshotList(
|
||||
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);
|
||||
}
|
||||
}, [showToast, 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 UserSnapshotLogSearchBar = ({ 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 UserSnapshotLogSearchBar;
|
||||
@@ -27,9 +27,15 @@ import BusinessLogSearchBar, { useBusinessLogSearch } from './BusinessLogSearchB
|
||||
import CurrencyLogSearchBar, { useCurrencyLogSearch } from './CurrencyLogSearchBar';
|
||||
import ItemLogSearchBar, { useItemLogSearch } from './ItemLogSearchBar';
|
||||
import CurrencyItemLogSearchBar, { useCurrencyItemLogSearch } from './CurrencyItemLogSearchBar';
|
||||
import CurrencyIndexSearchBar, { useCurrencyIndexSearch } from './CurrencyIndexSearchBar';
|
||||
import CurrencyUserIndexSearchBar, { useCurrencyIndexSearch } from './CurrencyUserIndexSearchBar';
|
||||
import CurrencyAcquireIndexSearchBar, { useCurrencyAcquireIndexSearch } from './CurrencyAcquireIndexSearchBar';
|
||||
import CurrencyConsumeIndexSearchBar, { useCurrencyConsumeIndexSearch } from './CurrencyConsumeIndexSearchBar';
|
||||
import ItemAcquireIndexSearchBar, { useItemAcquireIndexSearch } from './ItemAcquireIndexSearchBar';
|
||||
import ItemConsumeIndexSearchBar, { useItemConsumeIndexSearch } from './ItemConsumeIndexSearchBar';
|
||||
import UserCreateLogSearchBar, { useUserCreateLogSearch } from './UserCreateLogSearchBar';
|
||||
import UserLoginLogSearchBar, { useUserLoginLogSearch } from './UserLoginLogSearchBar';
|
||||
import UserSnapshotLogSearchBar, { useUserSnapshotLogSearch } from './UserSnapshotLogSearchBar';
|
||||
import AssetsIndexSearchBar, { useAssetsIndexSearch } from './AssetsIndexSearchBar';
|
||||
import LandAuctionSearchBar from './LandAuctionSearchBar';
|
||||
import CaliumRequestSearchBar from './CaliumRequestSearchBar';
|
||||
|
||||
@@ -66,15 +72,27 @@ export {
|
||||
useCurrencyLogSearch,
|
||||
LandAuctionSearchBar,
|
||||
CaliumRequestSearchBar,
|
||||
CurrencyIndexSearchBar,
|
||||
CurrencyUserIndexSearchBar,
|
||||
useCurrencyIndexSearch,
|
||||
useItemLogSearch,
|
||||
ItemLogSearchBar,
|
||||
CurrencyItemLogSearchBar,
|
||||
useCurrencyItemLogSearch,
|
||||
UserCreateLogSearchBar,
|
||||
useUserCreateLogSearch,
|
||||
UserLoginLogSearchBar,
|
||||
useUserLoginLogSearch,
|
||||
useItemLogSearch,
|
||||
ItemLogSearchBar,
|
||||
CurrencyItemLogSearchBar,
|
||||
useCurrencyItemLogSearch,
|
||||
UserCreateLogSearchBar,
|
||||
useUserCreateLogSearch,
|
||||
UserLoginLogSearchBar,
|
||||
useUserLoginLogSearch,
|
||||
CurrencyAcquireIndexSearchBar,
|
||||
useCurrencyAcquireIndexSearch,
|
||||
CurrencyConsumeIndexSearchBar,
|
||||
useCurrencyConsumeIndexSearch,
|
||||
ItemAcquireIndexSearchBar,
|
||||
useItemAcquireIndexSearch,
|
||||
ItemConsumeIndexSearchBar,
|
||||
useItemConsumeIndexSearch,
|
||||
UserSnapshotLogSearchBar,
|
||||
useUserSnapshotLogSearch,
|
||||
AssetsIndexSearchBar,
|
||||
useAssetsIndexSearch
|
||||
};
|
||||
|
||||
|
||||
12
src/i18n.js
12
src/i18n.js
@@ -56,6 +56,7 @@ const resources = {
|
||||
DOWNLOAD_FAIL: '다운이 실패하였습니다.',
|
||||
DELETE_STATUS_ONLY_WAIT: '대기상태의 데이터만 삭제가 가능합니다.',
|
||||
TABLE_DATA_NOT_FOUND: '데이터가 없습니다.',
|
||||
ITEM_ID_EMPTY_WARNING: '아이템 아이디를 입력해주세요.',
|
||||
//user
|
||||
NICKNAME_CHANGES_CONFIRM: '닉네임을 변경하시겠습니까?',
|
||||
NICKNAME_CHANGES_COMPLETE: '닉네임 변경이 완료되었습니다.',
|
||||
@@ -164,14 +165,21 @@ const resources = {
|
||||
FILE_SIZE_OVER_ERROR: "파일의 사이즈가 5MB를 초과하였습니다.",
|
||||
//파일명칭
|
||||
FILE_INDEX_USER_CONTENT: 'Caliverse_User_Index.xlsx',
|
||||
FILE_INDEX_USER_RETENTION: 'Caliverse_Retention.xlsx',
|
||||
FILE_INDEX_USER_RETENTION: 'Caliverse_Retention.csv',
|
||||
FILE_INDEX_CURRENCY_ACQUIRE: 'Caliverse_Currency_Acquire.csv',
|
||||
FILE_INDEX_CURRENCY_CONSUME: 'Caliverse_Currency_Consume.csv',
|
||||
FILE_INDEX_ITEM_ACQUIRE: 'Caliverse_Item_Acquire.csv',
|
||||
FILE_INDEX_ITEM_CONSUME: 'Caliverse_Item_Consume.csv',
|
||||
FILE_INDEX_ASSETS_CURRENCY: 'Caliverse_Assets_Currency.csv',
|
||||
FILE_INDEX_ASSETS_ITEM: 'Caliverse_Assets_Item.csv',
|
||||
FILE_CALIUM_REQUEST: 'Caliverse_Calium_Request.xlsx',
|
||||
FILE_LAND_AUCTION: 'Caliverse_Land_Auction.xlsx',
|
||||
FILE_BUSINESS_LOG: 'Caliverse_Log.xlsx',
|
||||
FILE_BUSINESS_LOG: 'Caliverse_Log',
|
||||
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_USER_SNAPSHOT: 'Caliverse_Game_Log_User_Snapshot',
|
||||
FILE_GAME_LOG_ITEM: 'Caliverse_Game_Log_Item',
|
||||
FILE_GAME_LOG_CURRENCY_ITEM: 'Caliverse_Game_Log_Currecy_Item',
|
||||
FILE_CURRENCY_INDEX: 'Caliverse_Currency_Index',
|
||||
|
||||
@@ -20,6 +20,7 @@ import ItemLogContent from '../../components/DataManage/ItemLogContent';
|
||||
import CurrencyItemLogContent from '../../components/DataManage/CurrencyItemLogContent';
|
||||
import UserCreateLogContent from '../../components/DataManage/UserCreateLogContent';
|
||||
import UserLoginLogContent from '../../components/DataManage/UserLoginLogContent';
|
||||
import UserSnapshotLogContent from '../../components/DataManage/UserSnapshotLogContent';
|
||||
|
||||
registerLocale('ko', ko);
|
||||
|
||||
@@ -62,6 +63,7 @@ const GameLogView = () => {
|
||||
<CurrencyItemLogContent active={activeTab === 'CURRENCYITEM'} />
|
||||
<UserCreateLogContent active={activeTab === 'USERCREATE'} />
|
||||
<UserLoginLogContent active={activeTab === 'USERLOGIN'} />
|
||||
<UserSnapshotLogContent active={activeTab === 'SNAPSHOT'} />
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,29 +1,19 @@
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { AnimatedPageWrapper } from '../../components/common/Layout'
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { Title, BtnWrapper, ButtonClose, ModalText, TabItem, TabScroll, TabWrapper } from '../../styles/Components';
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { authList } from '../../store/authList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import CreditContent from '../../components/IndexManage/CreditContent';
|
||||
import VBPContent from '../../components/IndexManage/VBPContent';
|
||||
import ItemContent from '../../components/IndexManage/ItemContent';
|
||||
import InstanceContent from '../../components/IndexManage/InstanceContent';
|
||||
import DecoContent from '../../components/IndexManage/DecoContent';
|
||||
import { Title, TabItem, TabScroll, TabWrapper } from '../../styles/Components';
|
||||
import { withAuth } from '../../hooks/hook';
|
||||
import { authType } from '../../assets/data';
|
||||
import { TabEconomicIndexList, TabGameLogList } from '../../assets/data/options';
|
||||
import { TabEconomicIndexList } from '../../assets/data/options';
|
||||
import CurrencyAcquireContent from '../../components/IndexManage/CurrencyAcquireContent';
|
||||
import CurrencyConsumeContent from '../../components/IndexManage/CurrencyConsumeContent';
|
||||
import ItemAcquireContent from '../../components/IndexManage/ItemAcquireContent';
|
||||
import ItemConsumeContent from '../../components/IndexManage/ItemConsumeContent';
|
||||
import CurrencyAssetsContent from '../../components/IndexManage/CurrencyAssetsContent';
|
||||
import ItemAssetsContent from '../../components/IndexManage/ItemAssetsContent';
|
||||
|
||||
const EconomicIndex = () => {
|
||||
const [activeTab, setActiveTab] = useState('CURRENCY');
|
||||
const [activeTab, setActiveTab] = useState('CURRENCY_ACQUIRE');
|
||||
|
||||
const handleTab = (e, content) => {
|
||||
e.preventDefault();
|
||||
@@ -45,11 +35,12 @@ const EconomicIndex = () => {
|
||||
})}
|
||||
</TabWrapper>
|
||||
</TabScroll>
|
||||
{activeTab === 'CURRENCY' && <CreditContent />}
|
||||
{/*{activeTab === 'vbp' && <VBPContent />}*/}
|
||||
{/*{activeTab === 'item' && <ItemContent />}*/}
|
||||
{/*{activeTab === 'instance' && <InstanceContent />}*/}
|
||||
{/*{activeTab === 'deco' && <DecoContent />}*/}
|
||||
{activeTab === 'CURRENCY_ACQUIRE' && <CurrencyAcquireContent />}
|
||||
{activeTab === 'CURRENCY_CONSUME' && <CurrencyConsumeContent />}
|
||||
{activeTab === 'ITEM_ACQUIRE' && <ItemAcquireContent />}
|
||||
{activeTab === 'ITEM_CONSUME' && <ItemConsumeContent />}
|
||||
{activeTab === 'CURRENCY_ASSETS' && <CurrencyAssetsContent />}
|
||||
{activeTab === 'ITEM_ASSETS' && <ItemAssetsContent />}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,10 +4,11 @@ import { Link } from 'react-router-dom';
|
||||
import { AnimatedPageWrapper } from '../../components/common/Layout';
|
||||
import { Title } from '../../styles/Components';
|
||||
|
||||
import { UserContent, SegmentContent, PlayTimeContent, RetentionContent, DailyActiveUserContent, DailyMedalContent } from '../../components/IndexManage/index';
|
||||
import { UserContent, RetentionContent } from '../../components/IndexManage/index';
|
||||
import { authType } from '../../assets/data';
|
||||
import { withAuth } from '../../hooks/hook';
|
||||
import { TabUserIndexList } from '../../assets/data/options';
|
||||
import CreditContent from '../../components/IndexManage/CreditContent';
|
||||
|
||||
const UserIndex = () => {
|
||||
const [activeTab, setActiveTab] = useState('USER');
|
||||
@@ -32,12 +33,9 @@ const UserIndex = () => {
|
||||
})}
|
||||
</TabWrapper>
|
||||
|
||||
{/*{activeTab === 'DAU' && <DailyActiveUserContent />}*/}
|
||||
{activeTab === 'USER' && <UserContent />}
|
||||
{activeTab === 'RETENTION' && <RetentionContent />}
|
||||
{activeTab === 'SEGMENT' && <SegmentContent />}
|
||||
{activeTab === 'PLAYTIME' && <PlayTimeContent />}
|
||||
{/*{activeTab === '메달' && <DailyMedalContent />}*/}
|
||||
{activeTab === 'CURRENCY' && <CreditContent />}
|
||||
|
||||
|
||||
</AnimatedPageWrapper>
|
||||
|
||||
@@ -15,14 +15,14 @@ import { Title, FormWrapper, TableStyle, TableWrapper, PopupMessage } from '../.
|
||||
import {
|
||||
StatusWapper,
|
||||
ChargeBtn,
|
||||
StatusLabel, TitleItemLabel, TitleItemValue, TitleItem,
|
||||
StatusLabel,
|
||||
} from '../../styles/ModuleComponents';
|
||||
import {Button, ExcelDownButton, Pagination, ViewTableInfo} from '../../components/common';
|
||||
import { convertKTC, truncateText } from '../../utils';
|
||||
import { CaliumRequestRegistModal } from '../../components/UserManage';
|
||||
import { CaliumCharge, LogHistory } from '../../apis';
|
||||
import { useModal, withAuth } from '../../hooks/hook';
|
||||
import { CommonSearchBar, useCommonSearch } from '../../components/ServiceManage';
|
||||
import { CommonSearchBar } from '../../components/ServiceManage';
|
||||
import {
|
||||
INITIAL_PAGE_LIMIT,
|
||||
LOG_ACTION_FAIL_CALIUM_ECHO,
|
||||
@@ -35,7 +35,6 @@ import useCommonSearchOld from '../../hooks/useCommonSearchOld';
|
||||
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
||||
import { historyTables } from '../../assets/data/data';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { logAction } from '../../assets/data/options';
|
||||
|
||||
const CaliumRequest = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
@@ -43,7 +43,7 @@ const LogView = () => {
|
||||
switch (action) {
|
||||
case "detail":
|
||||
handleModalView('detail');
|
||||
setDetailData(item.data);
|
||||
setDetailData(item.domain.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
import { Fragment, useState, useEffect } from 'react';
|
||||
|
||||
import {LogViewSearchBar} from '../../components/ServiceManage';
|
||||
import { Title, FormWrapper, SelectInput, TableInfo, ListCount, ListOption, TableStyle, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import Pagination from '../../components/common/Pagination/Pagination';
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
|
||||
import LogViewModal from '../../components/UserManage/LogViewModal';
|
||||
|
||||
import { LogViewList } from '../../apis';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { authList } from '../../store/authList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { logOption } from '../../assets/data';
|
||||
import { convertKTC } from '../../utils';
|
||||
|
||||
const LogView = () => {
|
||||
const navigate = useNavigate();
|
||||
const userInfo = useRecoilValue(authList);
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
const [stateModal, setStateModal] = useState('hidden');
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [detailData, setDetailData] = useState('');
|
||||
const [searchData, setSearchData] = useState({});
|
||||
const [orderBy, setOrderBy] = useState('DESC');
|
||||
const [pageSize, setPageSize] = useState('50');
|
||||
|
||||
const handleButtonClick = content => {
|
||||
setStateModal('view');
|
||||
setDetailData(content);
|
||||
};
|
||||
|
||||
const handleModal = () => {
|
||||
if (stateModal === 'hidden') {
|
||||
setStateModal('view');
|
||||
} else {
|
||||
setStateModal('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
const handleOrderBy = e => {
|
||||
const order = e.target.value;
|
||||
setOrderBy(order);
|
||||
fetchData(
|
||||
searchData.searchOption,
|
||||
searchData.data,
|
||||
searchData.logOption,
|
||||
searchData.startDate && new Date(searchData.startDate).toISOString(),
|
||||
searchData.endDate && new Date(searchData.endDate).toISOString(),
|
||||
order,
|
||||
pageSize,
|
||||
);
|
||||
};
|
||||
|
||||
const handlePageSize = e => {
|
||||
const size = e.target.value;
|
||||
setPageSize(size);
|
||||
setCurrentPage(1);
|
||||
fetchData(
|
||||
searchData.searchOption,
|
||||
searchData.data,
|
||||
searchData.logOption,
|
||||
searchData.startDate && new Date(searchData.startDate).toISOString(),
|
||||
searchData.endDate && new Date(searchData.endDate).toISOString(),
|
||||
orderBy,
|
||||
size,
|
||||
1,
|
||||
);
|
||||
};
|
||||
|
||||
const fetchData = async (searchType, searchKey, historyType, startDt, endDt, order, size) => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
setDataList(await LogViewList(token, searchType, searchKey, historyType, startDt, endDt, order ? order : orderBy, size ? size : pageSize, currentPage));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(
|
||||
searchData.searchOption,
|
||||
searchData.data,
|
||||
searchData.logOption,
|
||||
searchData.startDate && new Date(searchData.startDate).toISOString(),
|
||||
searchData.endDate && new Date(searchData.endDate).toISOString(),
|
||||
orderBy,
|
||||
pageSize,
|
||||
);
|
||||
}, [currentPage]);
|
||||
|
||||
const handleSearch = (searchType, searchKey, historyType, startDt, endDt) => {
|
||||
fetchData(searchType, searchKey, historyType, startDt, endDt);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 5) ? (
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={'view'}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={() => navigate(-1)} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">
|
||||
해당 메뉴에 대한 조회 권한이 없습니다.
|
||||
<br />
|
||||
권한 등급을 변경 후 다시 이용해주세요.
|
||||
</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={() => navigate(-1)} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
) : (
|
||||
<>
|
||||
<Title>사용 이력 조회</Title>
|
||||
<FormWrapper action="" $flow="row">
|
||||
<LogViewSearchBar handleSearch={handleSearch} resultData={setSearchData} />
|
||||
</FormWrapper>
|
||||
<TableInfo>
|
||||
<ListCount>
|
||||
총 : {dataList && dataList.total} 건 / {dataList && dataList.total_all} 건
|
||||
</ListCount>
|
||||
<ListOption>
|
||||
<SelectInput className="input-select" onChange={e => handleOrderBy(e)}>
|
||||
<option value="DESC">최신일자</option>
|
||||
<option value="ASC">오래된순</option>
|
||||
</SelectInput>
|
||||
<SelectInput className="input-select" onChange={e => handlePageSize(e)}>
|
||||
<option value="50">50개</option>
|
||||
<option value="100">100개</option>
|
||||
</SelectInput>
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="200">일시</th>
|
||||
<th width="200">이름</th>
|
||||
<th width="50%">ID</th>
|
||||
<th width="30%">사용 이력</th>
|
||||
{/* <th width="25%">상세 이력</th> */}
|
||||
<th width="150">상세 보기</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.list &&
|
||||
dataList.list.map((history, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{convertKTC(history.create_dt)}</td>
|
||||
<td>{history.name}</td>
|
||||
<td>{history.mail}</td>
|
||||
<td>{logOption.map(data => data.value === history.history_type && data.name)}</td>
|
||||
<td>
|
||||
<Button theme="line" text="JSON INFO" handleClick={() => handleButtonClick(history.content)} />
|
||||
</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
|
||||
|
||||
<LogViewModal stateModal={stateModal} handleModal={handleModal} data={detailData} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogView;
|
||||
@@ -162,6 +162,14 @@ export const InputItem = styled.div`
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
export const Notice = styled.span`
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
color: ${props => props.$color || '#999'} !important;
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
`;
|
||||
|
||||
export const BtnWrapper = styled.div`
|
||||
width: ${props => props.width};
|
||||
display: flex;
|
||||
@@ -240,6 +248,20 @@ export const ListCount = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
export const TableInfoContent = styled.div`
|
||||
position: absolute;
|
||||
display: flex;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
color: #686868;
|
||||
gap: 20px;
|
||||
span {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
|
||||
export const HeaderPaginationContainer = styled.div`
|
||||
position: absolute;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user