diff --git a/src/apis/Indicators.js b/src/apis/Indicators.js
index 27f24d3..e623724 100644
--- a/src/apis/Indicators.js
+++ b/src/apis/Indicators.js
@@ -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);
- }
- }
};
\ No newline at end of file
diff --git a/src/apis/Log.js b/src/apis/Log.js
index bf569ca..4f2ceea 100644
--- a/src/apis/Log.js
+++ b/src/apis/Log.js
@@ -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);
+ }
+ }
};
\ No newline at end of file
diff --git a/src/assets/data/options.js b/src/assets/data/options.js
index 13c7e64..ce078bb 100644
--- a/src/assets/data/options.js
+++ b/src/assets/data/options.js
@@ -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 삭제" },
diff --git a/src/assets/data/pages/historyTable.json b/src/assets/data/pages/historyTable.json
index 458ce7e..3aad83f 100644
--- a/src/assets/data/pages/historyTable.json
+++ b/src/assets/data/pages/historyTable.json
@@ -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",
diff --git a/src/components/DataManage/UserSnapshotLogContent.js b/src/components/DataManage/UserSnapshotLogContent.js
new file mode 100644
index 0000000..301d133
--- /dev/null
+++ b/src/components/DataManage/UserSnapshotLogContent.js
@@ -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 (
+
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
+
+
+ {downloadState.loading && (
+
+
+
+ )}
+
+ {dataLoading ? :
+ <>
+
+
+
+
+ {tableHeaders.map(header => (
+ | {header.label} |
+ ))}
+
+
+
+ {dataList?.snapshot_list?.map((item, index) => (
+
+
+ | {item.logDay} |
+ {item.accountId} |
+ {item.userGuid} |
+ {item.userNickname} |
+ {numberFormatter.formatCurrency(item.gold)} |
+ {numberFormatter.formatCurrency(item.sapphire)} |
+ {numberFormatter.formatCurrency(item.calium)} |
+ {numberFormatter.formatCurrency(item.ruby)} |
+ {item.item_13080002} |
+ {item.item_13080004} |
+ {item.item_13080005} |
+ {item.item_13080006} |
+ {item.item_13080007} |
+ {item.item_13080008} |
+ {item.item_13080009} |
+ {item.item_11570001} |
+ {item.item_11570002} |
+ {item.lastLogoutTime} |
+
+
+ ))}
+
+
+
+ {dataList?.snapshot_list &&
+
+ }
+
+ >
+ }
+
+ );
+};
+export default UserSnapshotLogContent;
\ No newline at end of file
diff --git a/src/components/IndexManage/CreditContent.js b/src/components/IndexManage/CreditContent.js
index 80185f9..f91bdd2 100644
--- a/src/components/IndexManage/CreditContent.js
+++ b/src/components/IndexManage/CreditContent.js
@@ -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 (
- {
if (executeSearch) {
diff --git a/src/components/IndexManage/CurrencyAcquireContent.js b/src/components/IndexManage/CurrencyAcquireContent.js
new file mode 100644
index 0000000..0a26c40
--- /dev/null
+++ b/src/components/IndexManage/CurrencyAcquireContent.js
@@ -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 (
+
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
+
+
+
+
+
+ {dataLoading ? :
+ <>
+
+
+
+
+ {tableHeaders.map(header => {
+ return (
+ |
+ {header.label}
+ |
+ );
+ })}
+
+
+
+
+ {dataList?.currency_list?.map((item, index) => (
+
+
+ | {item.logDay} |
+ {numberFormatter.formatCurrency(item.actionSummary.MailTaken)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ShopSell)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)} |
+ {numberFormatter.formatCurrency(item.actionSummary.SeasonPassTakeReward)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ClaimReward)} |
+ {numberFormatter.formatCurrency((item.actionSummary.QuestMainReward || 0) + (item.actionSummary.QuestTaskUpdate || 0))} |
+ {numberFormatter.formatCurrency(item.actionSummary.UgqAbort)} |
+ {numberFormatter.formatCurrency(item.actionSummary.RewardProp)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)} |
+ {numberFormatter.formatCurrency(item.actionSummary.GainLandProfit)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ConvertExchangeCalium)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ConvertCalium)} |
+ {numberFormatter.formatCurrency((item.actionSummary.BeaconSell || 0) + (item.actionSummary.BeaconShopReceivePaymentForSales || 0))} |
+ {numberFormatter.formatCurrency(item.actionSummary.MoneyChange)} |
+ {numberFormatter.formatCurrency(item.totalDeltaAmount)} |
+
+
+ ))}
+
+
+
+
+ >
+ }
+
+ );
+};
+
+export default CurrencyAcquireContent;
diff --git a/src/components/IndexManage/CurrencyAssetsContent.js b/src/components/IndexManage/CurrencyAssetsContent.js
new file mode 100644
index 0000000..e813b1f
--- /dev/null
+++ b/src/components/IndexManage/CurrencyAssetsContent.js
@@ -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 (
+
+
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
+
+
+
+
+
+ {dataLoading ? :
+ <>
+
+
+
+
+ {tableHeaders.map(header => {
+ return (
+ |
+ {header.label}
+ |
+ );
+ })}
+
+
+
+
+ {dataList?.assets_list?.map((data, index) => (
+
+
+ | {data.logDay} |
+ {numberFormatter.formatCurrency(data.userCount)} |
+ {numberFormatter.formatCurrency(data.gold)} |
+ {numberFormatter.formatCurrency(data.sapphire)} |
+ {numberFormatter.formatCurrency(data.calium)} |
+ {numberFormatter.formatCurrency(data.ruby)} |
+
+
+ ))}
+
+
+
+
+ >
+ }
+
+ );
+};
+
+export default CurrencyAssetsContent;
diff --git a/src/components/IndexManage/CurrencyConsumeContent.js b/src/components/IndexManage/CurrencyConsumeContent.js
new file mode 100644
index 0000000..b6280f4
--- /dev/null
+++ b/src/components/IndexManage/CurrencyConsumeContent.js
@@ -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 (
+
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
+
+
+
+
+
+ {dataLoading ? :
+ <>
+
+
+
+
+ {tableHeaders.map(header => {
+ return (
+ |
+ {header.label}
+ |
+ );
+ })}
+
+
+
+
+ {dataList?.currency_list?.map((item, index) => (
+
+
+ | {item.logDay} |
+ {numberFormatter.formatCurrency(item.actionSummary.ItemBuy)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ShopRePurchase)} |
+ {numberFormatter.formatCurrency((item.actionSummary.BeaconShopRegisterItem || 0) + (item.actionSummary.BeaconShopPurchaseItem || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.BeaconCreate || 0) + (item.actionSummary.BeaconEdit || 0) + (item.actionSummary.BeaconAppearanceCustomize || 0))} |
+ {numberFormatter.formatCurrency(item.actionSummary.TaxiMove)} |
+ {numberFormatter.formatCurrency(item.actionSummary.FarmingStart)} |
+ {numberFormatter.formatCurrency(item.actionSummary.SeasonPassBuyCharged)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ConvertExchangeCalium)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ConvertCalium)} |
+ {numberFormatter.formatCurrency(item.actionSummary.RentFloor)} |
+ {numberFormatter.formatCurrency(item.actionSummary.LandAuctionBid)} |
+ {numberFormatter.formatCurrency(item.actionSummary.UgqAssign)} |
+ {numberFormatter.formatCurrency((item.actionSummary.MoneyChange ||0) + (item.actionSummary.RenewalShopProducts ||0) + (item.actionSummary.CharacterAppearanceCustomize ||0))} |
+ {numberFormatter.formatCurrency(item.totalDeltaAmount)} |
+
+
+ ))}
+
+
+
+
+ >
+ }
+
+ );
+};
+
+export default CurrencyConsumeContent;
diff --git a/src/components/IndexManage/DailyActiveUserContent.js b/src/components/IndexManage/DailyActiveUserContent.js
deleted file mode 100644
index 566d421..0000000
--- a/src/components/IndexManage/DailyActiveUserContent.js
+++ /dev/null
@@ -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 (
- <>
-
-
-
-
-
-
-
-
-
-
-
- |
- 일자
- |
-
- DAU
- |
- {/**/}
- {/* DALC*/}
- {/* | */}
-
- DGLC
- |
- {/**/}
- {/* MaxAU*/}
- {/* | */}
-
-
-
-
- {dataList && (dataList || []).map((data, index) => (
-
- | {data.date} |
- {data.dau} |
- {/*{data.dalc} | */}
- {data.dglc} |
- {/*{data.maxAu} | */}
-
- ))}
-
-
-
- >
- );
-};
-
-export default PlayTimeContent;
-
diff --git a/src/components/IndexManage/DailyCaliumDashBoard.js b/src/components/IndexManage/DailyCaliumDashBoard.js
new file mode 100644
index 0000000..a2bb09e
--- /dev/null
+++ b/src/components/IndexManage/DailyCaliumDashBoard.js
@@ -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 (
+
+ {totalData &&
+
+
+ Daily Dashboard
+
+
+
+ {dashboardItems?.map((item, index) => (
+
+ {item.title}
+
+ {item.value}
+
+
+ ))}
+
+
+
+ }
+
+ );
+};
+
+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;
+`;
diff --git a/src/components/IndexManage/DailyDashBoard.js b/src/components/IndexManage/DailyDashBoard.js
index e50e792..4693728 100644
--- a/src/components/IndexManage/DailyDashBoard.js
+++ b/src/components/IndexManage/DailyDashBoard.js
@@ -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);
});
};
diff --git a/src/components/IndexManage/DailyMedalContent.js b/src/components/IndexManage/DailyMedalContent.js
deleted file mode 100644
index 7c1d4e9..0000000
--- a/src/components/IndexManage/DailyMedalContent.js
+++ /dev/null
@@ -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 (
- <>
-
-
-
-
-
-
-
-
-
-
-
- |
- 일자
- |
-
- UserID
- |
-
- 닉네임
- |
-
- Item ID
- |
-
- 획득량
- |
-
-
-
-
-
- {(dataList || []).map((data, index) => (
-
- | {data.date} |
- {data.dau} |
- {data.dalc} |
- {data.dglc} |
- {data.maxAu} |
- {Array.from({ length: 24 }, (_, i) => (
- {data['h' + i]} |
- ))}
-
- ))}
-
-
-
-
- >
- );
-};
-
-export default DailyMedalContent;
-
diff --git a/src/components/IndexManage/ItemAcquireContent.js b/src/components/IndexManage/ItemAcquireContent.js
new file mode 100644
index 0000000..b8445ca
--- /dev/null
+++ b/src/components/IndexManage/ItemAcquireContent.js
@@ -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 (
+
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
+
+ {dataList?.item_list && dataList.item_list.length > 0 &&
+
+
+
+ * 확인되지 않은 액션이 있을 수 있습니다
+
+ }
+
+
+
+
+ {dataLoading ? :
+ <>
+
+
+
+
+ {tableHeaders.map(header => {
+ return (
+ |
+ {header.label}
+ |
+ );
+ })}
+
+
+
+
+ {dataList?.item_list?.map((item, index) => (
+
+
+ | {item.logDay} |
+ {numberFormatter.formatCurrency(item.actionSummary.MailTaken)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ShopRePurchase)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ItemBuy)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ItemUse)} |
+ {numberFormatter.formatCurrency(item.actionSummary.SeasonPassTakeReward)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ClaimReward)} |
+ {numberFormatter.formatCurrency((item.actionSummary.QuestMainReward || 0) + (item.actionSummary.QuestTaskUpdate || 0) + (item.actionSummary.QuestMainTask || 0))} |
+ {numberFormatter.formatCurrency(item.actionSummary.UgqAbort)} |
+ {numberFormatter.formatCurrency((item.actionSummary.BattleRoundStateUpdate || 0) + (item.actionSummary.BattlePodCombatOccupyReward || 0) + (item.actionSummary.BattleObjectInteraction || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.RunRaceFinishReward || 0) + (item.actionSummary.RunRaceRespawnReward || 0))} |
+ {numberFormatter.formatCurrency(item.actionSummary.RewardProp)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)} |
+ {numberFormatter.formatCurrency((item.actionSummary.BeaconCreate || 0) + (item.actionSummary.BeaconEdit || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.BeaconShopPurchaseItem || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.DeleteMyhome || 0) + (item.actionSummary.SaveMyhome || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.CraftFinish || 0) + (item.actionSummary.CraftStop || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.CheatCommandItem || 0) + (item.actionSummary.CharacterAppearanceUpdate || 0)
+ + (item.actionSummary.ItemTattooLevelUp || 0) + (item.actionSummary.UserCreate || 0) + (item.actionSummary.JoinInstance || 0))} |
+ {numberFormatter.formatCurrency(item.totalDeltaCount)} |
+
+
+ ))}
+
+
+
+
+ >
+ }
+
+ );
+};
+
+export default ItemAcquireContent;
diff --git a/src/components/IndexManage/ItemAssetsContent.js b/src/components/IndexManage/ItemAssetsContent.js
new file mode 100644
index 0000000..25e5e1b
--- /dev/null
+++ b/src/components/IndexManage/ItemAssetsContent.js
@@ -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 (
+
+
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
+
+
+
+
+
+ {dataLoading ? :
+ <>
+
+
+
+
+ {tableHeaders.map(header => {
+ return (
+ |
+ {header.label}
+ |
+ );
+ })}
+
+
+
+
+ {dataList?.assets_list?.map((data, index) => (
+
+
+ | {data.logDay} |
+ {numberFormatter.formatCurrency(data.userCount)} |
+ {numberFormatter.formatCurrency(data.item_13080002)} |
+ {numberFormatter.formatCurrency(data.item_13080004)} |
+ {numberFormatter.formatCurrency(data.item_13080005)} |
+ {numberFormatter.formatCurrency(data.item_13080006)} |
+ {numberFormatter.formatCurrency(data.item_13080007)} |
+ {numberFormatter.formatCurrency(data.item_13080008)} |
+ {numberFormatter.formatCurrency(data.item_13080009)} |
+ {numberFormatter.formatCurrency(data.item_11570001)} |
+ {numberFormatter.formatCurrency(data.item_11570002)} |
+
+
+ ))}
+
+
+
+
+ >
+ }
+
+ );
+};
+
+export default ItemAssetsContent;
diff --git a/src/components/IndexManage/ItemConsumeContent.js b/src/components/IndexManage/ItemConsumeContent.js
new file mode 100644
index 0000000..79dc3ba
--- /dev/null
+++ b/src/components/IndexManage/ItemConsumeContent.js
@@ -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 (
+
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
+
+ {dataList?.item_list && dataList.item_list.length > 0 &&
+
+
+
+ * 확인되지 않은 액션이 있을 수 있습니다
+
+ }
+
+
+
+
+ {dataLoading ? :
+ <>
+
+
+
+
+ {tableHeaders.map(header => {
+ return (
+ |
+ {header.label}
+ |
+ );
+ })}
+
+
+
+
+ {dataList?.item_list?.map((item, index) => (
+
+
+ | {item.logDay} |
+ {numberFormatter.formatCurrency(item.actionSummary.ShopSell)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ItemUse)} |
+ {numberFormatter.formatCurrency((item.actionSummary.BeaconShopRegisterItem || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.BeaconEdit || 0) + (item.actionSummary.BeaconCreate || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.QuestTaskUpdate || 0))} |
+ {numberFormatter.formatCurrency(item.actionSummary.UgqAbort)} |
+ {numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)} |
+ {numberFormatter.formatCurrency((item.actionSummary.SaveMyhome || 0))} |
+ {numberFormatter.formatCurrency((item.actionSummary.CraftStart || 0))} |
+ {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)
+ )} |
+ {numberFormatter.formatCurrency(item.totalDeltaCount)} |
+
+
+ ))}
+
+
+
+
+ >
+ }
+
+ );
+};
+
+export default ItemConsumeContent;
diff --git a/src/components/IndexManage/PlayTimeContent.js b/src/components/IndexManage/PlayTimeContent.js
deleted file mode 100644
index f6af9c2..0000000
--- a/src/components/IndexManage/PlayTimeContent.js
+++ /dev/null
@@ -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 (
- <>
-
-
-
-
-
-
-
-
-
-
-
- |
- 일자
- |
-
- 유저수
- |
-
- 총 누적 플레이타임(분)
- |
-
- 1인당 평균 플레이타임(분)
- |
-
-
- | 30분 이내 |
- 30분 ~ 1시간 |
- 1시간 ~ 3시간 |
- 3시간 이상 |
-
-
-
- {dataList.playtime &&
- dataList.playtime.map(time => (
-
- | {time.date} |
- {time.user_cnt.map((cnt, index) => (
-
- {cnt}
- |
- ))}
- {time.total_time} |
- {time.average_time} |
-
- ))}
-
-
-
- >
- );
-};
-
-export default PlayTimeContent;
diff --git a/src/components/IndexManage/RetentionContent.js b/src/components/IndexManage/RetentionContent.js
index 4711ead..d1540c3 100644
--- a/src/components/IndexManage/RetentionContent.js
+++ b/src/components/IndexManage/RetentionContent.js
@@ -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 = () => {
/>
-
+
{dataLoading ? :
diff --git a/src/components/IndexManage/SegmentContent.js b/src/components/IndexManage/SegmentContent.js
deleted file mode 100644
index f4773fc..0000000
--- a/src/components/IndexManage/SegmentContent.js
+++ /dev/null
@@ -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 (
- <>
-
-
-
-
-
-
-
-
-
-
-
- |
- {dataList && dataList.start_dt} ~ {dataList && dataList.end_dt}
- |
-
- KIP
- |
-
-
- {/* | 국가 | */}
- 세그먼트 분류 |
- AU |
- AU Percentage by User Segment (%) |
-
-
-
- {dataList && dataList.segment &&
- dataList.segment.map((segment, index) => (
-
- {/* | TH | */}
- {segment.type} |
- {segment.au} |
- {segment.dif} |
-
- ))}
-
-
-
- >
- );
-};
-
-export default SegmentContent;
diff --git a/src/components/IndexManage/UserContent.js b/src/components/IndexManage/UserContent.js
index 8700f8b..69c56b0 100644
--- a/src/components/IndexManage/UserContent.js
+++ b/src/components/IndexManage/UserContent.js
@@ -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');
diff --git a/src/components/IndexManage/VBPContent.js b/src/components/IndexManage/VBPContent.js
deleted file mode 100644
index 0acff4d..0000000
--- a/src/components/IndexManage/VBPContent.js
+++ /dev/null
@@ -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 (
- <>
-
-
-
-
-
-
-
-
-
-
- |
- Product
- |
- 2023-08-07 |
- 2023-08-08 |
- 2023-08-09 |
- 2023-08-10 |
- 2023-08-11 |
- 2023-08-12 |
-
-
-
-
- (Total) VBP 생산량
- 500000
- 500000
- 500000
- 500000
- 500000
- 500000
-
-
- (Total) VBP 소진량
- 490000
- 490000
- 490000
- 490000
- 490000
- 490000
-
-
- (Total) VBP 보유량
- 3.2M
- 3.3M
- 3.3M
- 3.4M
- 3.5M
- 3.5M
-
-
- GET
- 퀘스트 보상
- 150000
- 150000
- 150000
- 150000
- 150000
- 150000
-
-
- 시즌패스 보상
- 150000
- 150000
- 150000
- 150000
- 150000
- 150000
-
-
- USE
- 퀘스트 보상
- 150000
- 150000
- 150000
- 150000
- 150000
- 150000
-
-
- 시즌패스 보상
- 150000
- 150000
- 150000
- 150000
- 150000
- 150000
-
-
- {/* {mokupData.map((data, index) => (
-
-
- | {data.date} |
- {data.name} |
- {data.trader} |
- {data.id} |
- {data.key} |
-
-
- ))} */}
-
-
-
- >
- );
-};
-
-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;
- }
- }
-`;
diff --git a/src/components/IndexManage/index.js b/src/components/IndexManage/index.js
index fff52a6..fafba48 100644
--- a/src/components/IndexManage/index.js
+++ b/src/components/IndexManage/index.js
@@ -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
};
\ No newline at end of file
diff --git a/src/components/common/button/CsvDownButton.js b/src/components/common/button/CsvDownButton.js
new file mode 100644
index 0000000..1f42067
--- /dev/null
+++ b/src/components/common/button/CsvDownButton.js
@@ -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 (
+
+ {isDownloading ? '다운로드 중...' : '엑셀 다운로드'}
+
+ );
+};
+
+export default CSVDownloadButton;
\ No newline at end of file
diff --git a/src/components/common/modal/LogDetailModal.js b/src/components/common/modal/LogDetailModal.js
index 6570ed7..5443bc3 100644
--- a/src/components/common/modal/LogDetailModal.js
+++ b/src/components/common/modal/LogDetailModal.js
@@ -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,
{item.fieldName} |
{formatValue(item.newValue)} |
{formatValue(item.oldValue)} |
- {item.sourceInfo.userId} |
- {convertKTC(item.sourceInfo.timestamp, false)} |
+ {item.sourceInfo.worker} |
+ {convertKTC(item.sourceInfo.logTime, false)} |
)
})}
diff --git a/src/components/searchBar/AssetsIndexSearchBar.js b/src/components/searchBar/AssetsIndexSearchBar.js
new file mode 100644
index 0000000..c35e4bd
--- /dev/null
+++ b/src/components/searchBar/AssetsIndexSearchBar.js
@@ -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 = [
+ <>
+ 일자
+ onSearch({ start_dt: date }, false)}
+ endDate={searchParams.end_dt}
+ handleEndDate={date => onSearch({ end_dt: date }, false)}
+ />
+ >
+ ];
+
+ return ;
+};
+
+export default AssetsIndexSearchBar;
\ No newline at end of file
diff --git a/src/components/searchBar/CurrencyAcquireIndexSearchBar.js b/src/components/searchBar/CurrencyAcquireIndexSearchBar.js
new file mode 100644
index 0000000..a9122ab
--- /dev/null
+++ b/src/components/searchBar/CurrencyAcquireIndexSearchBar.js
@@ -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 = [
+ <>
+ 일자
+ onSearch({ start_dt: date }, false)}
+ endDate={searchParams.end_dt}
+ handleEndDate={date => onSearch({ end_dt: date }, false)}
+ />
+ >,
+ <>
+ 재화종류
+ onSearch({ currency_type: e.target.value }, false)} >
+ {CurrencyType.map((data, index) => (
+
+ ))}
+
+ >,
+ ];
+
+ return ;
+};
+
+export default CurrencyAcquireIndexSearchBar;
\ No newline at end of file
diff --git a/src/components/searchBar/CurrencyConsumeIndexSearchBar.js b/src/components/searchBar/CurrencyConsumeIndexSearchBar.js
new file mode 100644
index 0000000..15ffb78
--- /dev/null
+++ b/src/components/searchBar/CurrencyConsumeIndexSearchBar.js
@@ -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 = [
+ <>
+ 일자
+ onSearch({ start_dt: date }, false)}
+ endDate={searchParams.end_dt}
+ handleEndDate={date => onSearch({ end_dt: date }, false)}
+ />
+ >,
+ <>
+ 재화종류
+ onSearch({ currency_type: e.target.value }, false)} >
+ {CurrencyType.map((data, index) => (
+
+ ))}
+
+ >,
+ ];
+
+ return ;
+};
+
+export default CurrencyConsumeIndexSearchBar;
\ No newline at end of file
diff --git a/src/components/searchBar/CurrencyIndexSearchBar.js b/src/components/searchBar/CurrencyUserIndexSearchBar.js
similarity index 96%
rename from src/components/searchBar/CurrencyIndexSearchBar.js
rename to src/components/searchBar/CurrencyUserIndexSearchBar.js
index ba4c9d7..307316f 100644
--- a/src/components/searchBar/CurrencyIndexSearchBar.js
+++ b/src/components/searchBar/CurrencyUserIndexSearchBar.js
@@ -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 ;
};
-export default CurrencyIndexSearchBar;
\ No newline at end of file
+export default CurrencyUserIndexSearchBar;
\ No newline at end of file
diff --git a/src/components/searchBar/ItemAcquireIndexSearchBar.js b/src/components/searchBar/ItemAcquireIndexSearchBar.js
new file mode 100644
index 0000000..ca988f3
--- /dev/null
+++ b/src/components/searchBar/ItemAcquireIndexSearchBar.js
@@ -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 = [
+ <>
+ 일자
+ onSearch({ start_dt: date }, false)}
+ endDate={searchParams.end_dt}
+ handleEndDate={date => onSearch({ end_dt: date }, false)}
+ />
+ >,
+ <>
+ 아이템 ID
+ onSearch({ item_id: e.target.value }, false)}
+ />
+ >,
+ ];
+
+ return ;
+};
+
+export default ItemAcquireIndexSearchBar;
\ No newline at end of file
diff --git a/src/components/searchBar/ItemConsumeIndexSearchBar.js b/src/components/searchBar/ItemConsumeIndexSearchBar.js
new file mode 100644
index 0000000..1aab5fd
--- /dev/null
+++ b/src/components/searchBar/ItemConsumeIndexSearchBar.js
@@ -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 = [
+ <>
+ 일자
+ onSearch({ start_dt: date }, false)}
+ endDate={searchParams.end_dt}
+ handleEndDate={date => onSearch({ end_dt: date }, false)}
+ />
+ >,
+ <>
+ 아이템 ID
+ onSearch({ item_id: e.target.value }, false)}
+ />
+ >,
+ ];
+
+ return ;
+};
+
+export default ItemConsumeIndexSearchBar;
\ No newline at end of file
diff --git a/src/components/searchBar/UserSnapshotLogSearchBar.js b/src/components/searchBar/UserSnapshotLogSearchBar.js
new file mode 100644
index 0000000..780698a
--- /dev/null
+++ b/src/components/searchBar/UserSnapshotLogSearchBar.js
@@ -0,0 +1,175 @@
+import { TextInput, InputLabel, SelectInput, InputGroup } from '../../styles/Components';
+import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
+import { useCallback, useEffect, useState } from 'react';
+import { userSearchType2 } from '../../assets/data/options';
+import { 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 = [
+ <>
+
+ onSearch({search_type: e.target.value }, false)}>
+ {userSearchType2.map((data, index) => (
+
+ ))}
+
+ onSearch({ search_data: e.target.value }, false)}
+ />
+
+ >,
+ <>
+ 일자
+ onSearch({ start_dt: date }, false)}
+ endDate={searchParams.end_dt}
+ handleEndDate={date => onSearch({ end_dt: date }, false)}
+ />
+ >,
+ ];
+
+ return ;
+};
+
+export default UserSnapshotLogSearchBar;
\ No newline at end of file
diff --git a/src/components/searchBar/index.js b/src/components/searchBar/index.js
index 1d3c05c..dcd7a4b 100644
--- a/src/components/searchBar/index.js
+++ b/src/components/searchBar/index.js
@@ -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
};
diff --git a/src/i18n.js b/src/i18n.js
index 21fc704..72855fd 100644
--- a/src/i18n.js
+++ b/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',
diff --git a/src/pages/DataManage/GameLogView.js b/src/pages/DataManage/GameLogView.js
index 2719b21..31f3914 100644
--- a/src/pages/DataManage/GameLogView.js
+++ b/src/pages/DataManage/GameLogView.js
@@ -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 = () => {
+
);
};
diff --git a/src/pages/IndexManage/EconomicIndex.js b/src/pages/IndexManage/EconomicIndex.js
index da1d3c5..8bfaf6f 100644
--- a/src/pages/IndexManage/EconomicIndex.js
+++ b/src/pages/IndexManage/EconomicIndex.js
@@ -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 = () => {
})}
- {activeTab === 'CURRENCY' && }
- {/*{activeTab === 'vbp' && }*/}
- {/*{activeTab === 'item' && }*/}
- {/*{activeTab === 'instance' && }*/}
- {/*{activeTab === 'deco' && }*/}
+ {activeTab === 'CURRENCY_ACQUIRE' && }
+ {activeTab === 'CURRENCY_CONSUME' && }
+ {activeTab === 'ITEM_ACQUIRE' && }
+ {activeTab === 'ITEM_CONSUME' && }
+ {activeTab === 'CURRENCY_ASSETS' && }
+ {activeTab === 'ITEM_ASSETS' && }
);
};
diff --git a/src/pages/IndexManage/UserIndex.js b/src/pages/IndexManage/UserIndex.js
index bcc88ce..2ecce0c 100644
--- a/src/pages/IndexManage/UserIndex.js
+++ b/src/pages/IndexManage/UserIndex.js
@@ -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 = () => {
})}
- {/*{activeTab === 'DAU' && }*/}
{activeTab === 'USER' && }
{activeTab === 'RETENTION' && }
- {activeTab === 'SEGMENT' && }
- {activeTab === 'PLAYTIME' && }
- {/*{activeTab === '메달' && }*/}
+ {activeTab === 'CURRENCY' && }
diff --git a/src/pages/UserManage/CaliumRequest.js b/src/pages/UserManage/CaliumRequest.js
index 215a5c8..8ba1410 100644
--- a/src/pages/UserManage/CaliumRequest.js
+++ b/src/pages/UserManage/CaliumRequest.js
@@ -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');
diff --git a/src/pages/UserManage/LogView.js b/src/pages/UserManage/LogView.js
index 88e0ed2..19179ee 100644
--- a/src/pages/UserManage/LogView.js
+++ b/src/pages/UserManage/LogView.js
@@ -43,7 +43,7 @@ const LogView = () => {
switch (action) {
case "detail":
handleModalView('detail');
- setDetailData(item.data);
+ setDetailData(item.domain.data);
break;
default:
diff --git a/src/pages/UserManage/LogView_bak.js b/src/pages/UserManage/LogView_bak.js
deleted file mode 100644
index 4d71d9c..0000000
--- a/src/pages/UserManage/LogView_bak.js
+++ /dev/null
@@ -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) ? (
-
-
- navigate(-1)} />
-
-
- 해당 메뉴에 대한 조회 권한이 없습니다.
-
- 권한 등급을 변경 후 다시 이용해주세요.
-
-
-
-
- ) : (
- <>
-
사용 이력 조회
-
-
-
-
-
- 총 : {dataList && dataList.total} 건 / {dataList && dataList.total_all} 건
-
-
- handleOrderBy(e)}>
-
-
-
- handlePageSize(e)}>
-
-
-
-
-
-
-
-
-
- | 일시 |
- 이름 |
- ID |
- 사용 이력 |
- {/* 상세 이력 | */}
- 상세 보기 |
-
-
-
- {dataList.list &&
- dataList.list.map((history, index) => (
-
-
- | {convertKTC(history.create_dt)} |
- {history.name} |
- {history.mail} |
- {logOption.map(data => data.value === history.history_type && data.name)} |
-
- |
-
-
- ))}
-
-
-
-
-
- >
- )}
- >
- );
-};
-
-export default LogView;
diff --git a/src/styles/Components.js b/src/styles/Components.js
index 34dddae..51cdab8 100644
--- a/src/styles/Components.js
+++ b/src/styles/Components.js
@@ -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;