Compare commits

...

53 Commits

Author SHA1 Message Date
760153c700 인스턴스 조회 추가 2025-11-28 16:40:32 +09:00
8dae810e3a 인스턴스 조회 추가 2025-11-28 16:40:18 +09:00
ac9bcdda8b 로그인정보 만료시 비밀번호 초기화 추가,
랭킹 강제 초기화 버튼 추가,
랭킹 시스템 조회 및 수정
2025-11-28 16:39:39 +09:00
3264e94093 로그인 비밀번호 초기화 추가 2025-10-27 18:54:45 +09:00
b801552839 제작 아이템 조회
랭킹 점수 관리
2025-09-16 16:43:58 +09:00
3169055646 칼리움 요청 날짜처리 변경
랭킹 스케줄 추가
2025-09-15 16:40:04 +09:00
5d2e1918d1 비즈니스로그조회 시간 추가
메타데이터 로드 추가
아이템 백과사전 추가
칼리움 요청 날짜처리 변경
2025-09-15 16:39:15 +09:00
4407fdc6b6 컴포넌트 관련 변경
이미지 업로드 한글명칭 불가 처리
유저 조회 부분 수정
2025-09-15 16:37:46 +09:00
b01c5cd410 아이템 조회 아이템ID 조건 추가 2025-09-15 16:25:12 +09:00
63b3704e89 히스토리 조회 관련 수정 2025-09-15 16:24:41 +09:00
f78a4912a6 event > rewardEvent 변경
월드이벤트(event) 추가
2025-09-15 16:23:48 +09:00
e25bcdc86e 선택 드랍다운 넓이 수정
아이템 백과사전 추가
2025-09-04 10:37:50 +09:00
5143b45610 모달 스크롤 추가
detailGrid 수정
전투이벤트, 랜드경매, 이벤트, 메일 상세 수정
랜드경매 예약종료일 제거
2025-08-09 09:50:14 +09:00
f4b629df52 경제지표 재화 보유
경제지표 아이템 보유
게임로그 스냅샷
히스토리 비즈니스로그 기준 변경
2025-08-04 17:40:37 +09:00
2ba8594e6b 칼리움 완료처리 파라미터 변경
우편 상세 확인시 페이지 처리
조회조건 변경시 페이지 초기화
랜드경매 메시지 제거
2025-07-28 14:13:01 +09:00
d3470e3d03 nginx 파일 제한 증가 2025-07-21 16:33:02 +09:00
99943c0b19 퀘스트 강제 완료
경제지표 재화 헤더 스타일 변경
2025-07-18 15:18:45 +09:00
26114c9a9b 코드 정리 2025-07-17 14:40:07 +09:00
952701f68b 게임로그 유저생성 로그 조회
게임로그 유저로그인 로그 조회
2025-07-17 14:38:07 +09:00
7041d4a649 유저 지표 잔존율 생성 2025-07-16 18:39:30 +09:00
7fa9abcad4 탭 모션 적용 2025-07-16 18:38:38 +09:00
943b146496 마이홈 리스트형식으로 변경 2025-07-14 13:53:14 +09:00
88585c1b24 전투이벤트 최대 진행시간 예외처리 2025-07-13 11:29:31 +09:00
991462c0d7 게임로그 아이템
게임로그 재화(아이템) 추가
2025-07-13 11:29:04 +09:00
bab594918e datepicker 옵션 변경 2025-07-07 14:26:11 +09:00
c4099c0cf0 메뉴 앤트디자인 메뉴로 교체
헤더 Breadcrumb 추가, profile 수정
2025-07-07 14:25:02 +09:00
0d8fb7b327 전투이벤트 진행시간 추가
진행시간 기준 종료시간 계산
2025-07-01 18:05:59 +09:00
d4db33bcf0 detailGrid 탭 추가 2025-07-01 14:41:07 +09:00
38dac99278 비즈니스로그 타입 예외처리 2025-07-01 14:40:41 +09:00
28094e1c48 배너 detailGrid 적용
배너 수정 및 삭제
2025-07-01 14:40:13 +09:00
67c048a11d 메뉴배너 detailGrid 적용 2025-06-27 09:28:13 +09:00
f6a0319701 메뉴배너 detailGrid 적용 2025-06-27 09:28:06 +09:00
0368bf77e7 antBUtton 생성
topButton 이미지 변경
엑셀 버튼 조정
tab 컨트론 생성
detailGrid, layout 생성
modal motion 적용
2025-06-27 09:25:41 +09:00
b2b579ead1 전투시스템 스케줄러 game mode 방식 변경 2025-06-19 18:54:15 +09:00
495243a1a3 경제지표 재화 추가
게임 로그 재화지표 export api 추가
경제지표 재화 상세 > 재화 로그 페이지 이동 처리
2025-06-16 15:45:36 +09:00
7993f37e26 qa 주소 변경 2025-06-16 15:43:20 +09:00
93a7c087e1 style, utils 수정 2025-06-12 14:16:46 +09:00
38fa323db6 Log currency 관련 API 호출 추가
components 정리에 따른 호출 위치 수정
excelExportButton에 functionName 추가
게임 로그조회 수정
2025-06-12 14:16:26 +09:00
6f9f0307ac component 정리
currencyLogSearchBar 생성
currencyLogCOntent 생성
excelExportButton api호출 방식 수정
2025-06-12 14:08:11 +09:00
dc7934d906 비즈니스 로그 조회 및 파일 다운 수정 2025-06-04 15:19:08 +09:00
9d06246aba 칼리움 요청 실패 표시 및 클릭시 페이지 이동 처리 2025-05-23 15:58:04 +09:00
1532793cc1 LogView 리팩토링
LogDetailModal 생성
화면별 히스토리(LogDetailModal) 적용
2025-05-22 14:55:37 +09:00
64bf449de7 이용자 제재 해지 사유 입력 2025-05-15 17:50:01 +09:00
9be5bb388a data 정보 수정
우편 내용 복사 기능
우편 코드 정리
유저 인벤토리 아이템 삭제 제거
2025-05-15 17:49:45 +09:00
d219a128bc api result 부분 수정 2025-05-15 17:43:20 +09:00
ce2f3db35c 닉네임 변경 처리 수정 2025-05-12 17:06:11 +09:00
b5efab7755 조회조건 필터 랜더링에 이슈로 컴포넌트 분리작업 2025-05-12 10:46:13 +09:00
1cc20b65e2 이용자제재 수정 2025-05-12 10:44:35 +09:00
0b85232969 전투이벤트 등록 알림 비동기에따른 state 초기화 부분 수정 2025-05-12 10:43:57 +09:00
4291d7976c 이용자제재 엑셀 업로드 처리 수정
이용자제재 상세 표시 부분 수정
2025-05-12 10:43:24 +09:00
065c081a85 조회조건 스켈레톤 2025-05-01 07:04:36 +09:00
fa290b64ec api 공통 모듈생성
search, api 공통 모듈 생성
공통모듈 화면 별 반영
2025-05-01 07:04:14 +09:00
f8d5b2197d 안쓰는부분 삭제 2025-05-01 07:02:25 +09:00
248 changed files with 21839 additions and 8866 deletions

2
.gitignore vendored
View File

@@ -32,3 +32,5 @@ yarn-error.log*
/.idea/misc.xml
/.idea/modules.xml
/.idea/vcs.xml
/.idea/git_toolbox_blame.xml
/.idea/git_toolbox_prj.xml

View File

@@ -3,6 +3,10 @@ server {
listen [::]:8080;
server_name localhost;
client_max_body_size 100M;
client_body_timeout 300s;
client_header_timeout 300s;
location / {
root /usr/share/nginx/admintool;
index index.html index.htm;
@@ -16,6 +20,11 @@ server {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_request_buffering off;
}
error_page 500 502 503 504 /50x.html;

View File

@@ -3,6 +3,10 @@ server {
listen [::]:8080;
server_name localhost;
client_max_body_size 100M;
client_body_timeout 300s;
client_header_timeout 300s;
location / {
root /usr/share/nginx/admintool;
index index.html index.htm;
@@ -16,6 +20,11 @@ server {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_request_buffering off;
}
error_page 500 502 503 504 /50x.html;

View File

@@ -3,6 +3,10 @@ server {
listen [::]:8080;
server_name localhost;
client_max_body_size 100M;
client_body_timeout 300s;
client_header_timeout 300s;
location / {
root /usr/share/nginx/admintool;
index index.html index.htm;
@@ -11,11 +15,16 @@ server {
# api reverse proxy
location /api/ {
proxy_pass http://172.40.129.180:23450;
proxy_pass http://172.24.128.231:23450;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_request_buffering off;
}
error_page 500 502 503 504 /50x.html;

View File

@@ -3,6 +3,10 @@ server {
listen [::]:8080;
server_name localhost;
client_max_body_size 100M;
client_body_timeout 300s;
client_header_timeout 300s;
location / {
root /usr/share/nginx/admintool;
index index.html index.htm;
@@ -16,6 +20,11 @@ server {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_request_buffering off;
}
error_page 500 502 503 504 /50x.html;

1744
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,13 +3,17 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^5.6.1",
"@hookform/resolvers": "^3.2.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"antd": "^5.26.1",
"axios": "^1.4.0",
"date-fns": "^2.30.0",
"dayjs": "^1.11.13",
"dotenv-cli": "^7.4.2",
"framer-motion": "^12.19.1",
"i18next": "^23.15.1",
"lodash": "^4.17.21",
"react": "^18.2.0",

View File

@@ -13,20 +13,31 @@ import {
LogView,
} from './pages/UserManage';
import { EconomicIndex, UserIndex } from './pages/IndexManage';
import { LandInfoView, CryptView, GameLogView, UserView, BusinessLogView, } from './pages/DataManage';
import {
LandInfoView,
GameLogView,
UserView,
BusinessLogView,
MetaItemView,
RankManage,
MetaCraftingView,
RankInfoView,
MetaInstanceView
} from './pages/DataManage';
import {
Board,
Event,
EventRegist,
RewardEvent,
RewardEventRegist,
Items,
Mail,
MailRegist,
ReportList,
UserBlock,
UserBlockRegist,
WhiteList,
LandAuction,
BattleEvent, MenuBanner, MenuBannerRegist,
BattleEvent,
MenuBanner, MenuBannerRegist, Ranking,
Event
} from './pages/ServiceManage';
const RouteInfo = () => {
@@ -59,24 +70,29 @@ const RouteInfo = () => {
<Route path="userview" element={<UserView />} />
<Route path="landview" element={<LandInfoView />} />
<Route path="gamelogview" element={<GameLogView />} />
<Route path="cryptview" element={<CryptView />} />
<Route path="businesslogview" element={<BusinessLogView />} />
<Route path="itemdictionary" element={<MetaItemView />} />
<Route path="craftdictionary" element={<MetaCraftingView />} />
<Route path="rankmanage" element={<RankManage />} />
<Route path="rankview" element={<RankInfoView />} />
<Route path="instancedictionary" element={<MetaInstanceView />} />
</Route>
<Route path="/servicemanage">
<Route path="board" element={<Board />} />
<Route path="whitelist" element={<WhiteList />} />
<Route path="mail" element={<Mail />} />
<Route path="mail/mailregist" element={<MailRegist />} />
<Route path="userblock" element={<UserBlock />} />
<Route path="userblock/userblockregist" element={<UserBlockRegist />} />
<Route path="reportlist" element={<ReportList />} />
<Route path="items" element={<Items />} />
<Route path="event" element={<Event />} />
<Route path="event/eventregist" element={<EventRegist />} />
<Route path="rewardevent" element={<RewardEvent />} />
<Route path="rewardevent/eventregist" element={<RewardEventRegist />} />
<Route path="landauction" element={<LandAuction />} />
<Route path="battleevent" element={<BattleEvent />} />
<Route path="menubanner" element={<MenuBanner />} />
<Route path="menubanner/menubannerregist" element={<MenuBannerRegist />} />
<Route path="ranking" element={<Ranking />} />
<Route path="event" element={<Event />} />
</Route>
</Route>
</Routes>

View File

@@ -1,79 +0,0 @@
import { Route, Routes } from 'react-router-dom';
import { Layout, LoginLayout, MainLayout } from './components/common/Layout';
import LoginBg from './assets/img/login-bg.png';
import { Login } from './pages/Login';
import LoginFail from './pages/LoginFail';
import { AccountEdit, AccountRegist, PasswordReset } from './pages/Account';
import Main from './pages/Main';
import {
AdminView,
AuthSetting,
AuthSettingUpdate,
CaliumRequest,
CaliumRequestRegist,
LogView,
} from './pages/UserManage';
import { EconomicIndex, UserIndex } from './pages/IndexManage';
import { ContentsView, CryptView, GameLogView, UserView } from './pages/DataManage';
import {
Board,
Event,
EventRegist,
Items,
Mail,
MailRegist,
ReportList,
UserBlock,
UserBlockRegist,
WhiteList,
} from './pages/ServiceManage';
const RouteInfo = () => {
return (
<Routes>
<Route element={<LoginLayout $bgimg={LoginBg} $padding="50px" />}>
<Route path="/" element={<Login />} />
<Route path="/fail" element={<LoginFail />} />
<Route path="/account/regist" element={<AccountRegist />} />
<Route path="/account/pwdreset" element={<PasswordReset />} />
<Route path="/account/edit" element={<AccountEdit />} />
</Route>
<Route element={<MainLayout />}>
<Route path="/main" element={<Main />} />
</Route>
<Route element={<Layout />}>
<Route path="/usermanage/">
<Route path="adminview" element={<AdminView />} />
<Route path="logview" element={<LogView />} />
<Route path="authsetting" element={<AuthSetting />} />
<Route path="authsetting/:id" element={<AuthSettingUpdate />} />
<Route path="caliumrequest" element={<CaliumRequest />} />
</Route>
<Route path="/indexmanage">
<Route path="userindex" element={<UserIndex />} />
<Route path="economicindex" element={<EconomicIndex />} />
</Route>
<Route path="/datamanage">
<Route path="userview" element={<UserView />} />
<Route path="contentsview" element={<ContentsView />} />
<Route path="gamelogview" element={<GameLogView />} />
<Route path="cryptview" element={<CryptView />} />
</Route>
<Route path="/servicemanage">
<Route path="board" element={<Board />} />
<Route path="whitelist" element={<WhiteList />} />
<Route path="mail" element={<Mail />} />
<Route path="mail/mailregist" element={<MailRegist />} />
<Route path="userblock" element={<UserBlock />} />
<Route path="userblock/userblockregist" element={<UserBlockRegist />} />
<Route path="reportlist" element={<ReportList />} />
<Route path="items" element={<Items />} />
<Route path="event" element={<Event />} />
<Route path="event/eventregist" element={<EventRegist />} />
</Route>
</Route>
</Routes>
)
}
export default RouteInfo;

View File

@@ -85,10 +85,10 @@ export const AdminDeleteUser = async (token, params) => {
}
};
export const AdminChangePw = async (token, params) => {
export const AdminChangePw = async ( params) => {
try {
const res = await Axios.post('/api/v1/admin/init-password', params, {
headers: { Authorization: `Bearer ${token}` },
headers: { },
});
return res.data;

View File

@@ -129,4 +129,21 @@ export const BattleRewardView = async (token) => {
throw new Error('BattleRewardView Error', e);
}
}
};
export const GameModeView = async (token) => {
try {
const res = await Axios.get(
`/api/v1/battle/game-mode/list`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res.data.data.game_mode_list;
} catch (e) {
if (e instanceof Error) {
throw new Error('GameModeView Error', e);
}
}
};

View File

@@ -52,7 +52,7 @@ export const BlackListDelete = async (token, params) => {
data: { list: params },
});
return res;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('BlackListDelete', e);

132
src/apis/Dictionary.js Normal file
View File

@@ -0,0 +1,132 @@
//운영 정보 관리 - 백과사전 api 연결
import { Axios, responseFileDownload } from '../utils';
// 아이템 백과사전 조회
export const getItemDictionaryList = async (token, searchType, searchData, largeType, smallType, brand, gender, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/dictionary/item/list?search_type=${searchType}&search_data=${searchData}
&large_type=${largeType}&small_type=${smallType}&brand=${brand}&gender=${gender}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
console.error('getItemDictionaryList API error:', error);
throw error;
}
};
export const ItemDictionaryExport = async (token, params) => {
try {
await Axios.get(`/api/v1/dictionary/item/excel-export?search_type=${params.search_type}&search_data=${params.search_data}
&large_type=${params.large_type}&small_type=${params.small_type}&brand=${params.brand}&gender=${params.gender}
&lang=${params.lang}&task_id=${params.taskId}`, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob'
}).then(response => {
responseFileDownload(response, {
defaultFileName: 'itemDictionary'
});
});
} catch (e) {
if (e instanceof Error) {
throw new Error('ItemDictionaryExport Error', e);
}
}
};
export const getCraftingDictionaryList = async (token, searchType, searchData, smallType, recipeType, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/dictionary/craft/list?search_type=${searchType}&search_data=${searchData}
&small_type=${smallType}&recipe_type=${recipeType}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
console.error('getCraftingDictionaryList API error:', error);
throw error;
}
};
export const CraftingDictionaryExport = async (token, params) => {
try {
await Axios.get(`/api/v1/dictionary/craft/excel-export?search_type=${params.search_type}&search_data=${params.search_data}
&small_type=${params.small_type}&recipe_type=${params.recipe_type}
&lang=${params.lang}&task_id=${params.taskId}`, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob'
}).then(response => {
responseFileDownload(response, {
defaultFileName: 'craftingDictionary'
});
});
} catch (e) {
if (e instanceof Error) {
throw new Error('CraftingDictionaryExport Error', e);
}
}
};
export const getInstanceDictionaryList = async (token, searchType, searchData, contentsType, accessType, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/dictionary/instance/list?search_type=${searchType}&search_data=${searchData}
&contents_type=${contentsType}&access_type=${accessType}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
console.error('getInstanceDictionaryList API error:', error);
throw error;
}
};
export const InstanceDictionaryExport = async (token, params) => {
try {
await Axios.get(`/api/v1/dictionary/instance/excel-export?search_type=${params.search_type}&search_data=${params.search_data}
&contents_type=${params.contents_type}&access_type=${params.access_type}
&lang=${params.lang}&task_id=${params.taskId}`, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob'
}).then(response => {
responseFileDownload(response, {
defaultFileName: 'instanceDictionary'
});
});
} catch (e) {
if (e instanceof Error) {
throw new Error('InstanceDictionaryExport Error', e);
}
}
};
export const BrandView = async (token) => {
try {
const res = await Axios.get(
`/api/v1/dictionary/brand/list`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res.data.data.brand_list;
} catch (e) {
if (e instanceof Error) {
throw new Error('BrandView Error', e);
}
}
};

View File

@@ -1,13 +1,13 @@
//운영서비스 관리 - 이벤트 api 연결
//운영서비스 관리 - 통합 이벤트 api 연결
import { Axios } from '../utils';
// 이벤트 리스트 조회
export const EventView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
export const EventView = async (token, searchData, status, startDate, endDate, order, size, currentPage) => {
try {
const res = await Axios.get(
`/api/v1/event/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
&page_size=${size}`,
`/api/v1/world-event/list?search_data=${searchData}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`,
{
headers: { Authorization: `Bearer ${token}` },
},
@@ -24,11 +24,11 @@ export const EventView = async (token, title, content, status, startDate, endDat
// 이벤트 상세보기
export const EventDetailView = async (token, id) => {
try {
const res = await Axios.get(`/api/v1/event/detail/${id}`, {
const res = await Axios.get(`/api/v1/world-event/detail/${id}`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data.detail;
return res.data.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventDetailView Error', e);
@@ -39,11 +39,11 @@ export const EventDetailView = async (token, id) => {
// 이벤트 등록
export const EventSingleRegist = async (token, params) => {
try {
const res = await Axios.post(`/api/v1/event`, params, {
const res = await Axios.post(`/api/v1/world-event`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventSingleRegist Error', e);
@@ -51,14 +51,14 @@ export const EventSingleRegist = async (token, params) => {
}
};
// 우편 수정
// 이벤트 수정
export const EventModify = async (token, id, params) => {
try {
const res = await Axios.put(`/api/v1/event/${id}`, params, {
const res = await Axios.put(`/api/v1/world-event/${id}`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data.list;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventModify Error', e);
@@ -66,15 +66,14 @@ export const EventModify = async (token, id, params) => {
}
};
// 우편 삭제
export const EventDelete = async (token, params, id) => {
// 이벤트 삭제
export const EventDelete = async (token, id) => {
try {
const res = await Axios.delete(`/api/v1/event/delete`, {
headers: { Authorization: `Bearer ${token}` },
data: { list: params },
const res = await Axios.delete(`/api/v1/world-event/delete?id=${id}`, {
headers: { Authorization: `Bearer ${token}` }
});
return res.data.data.list;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventDelete Error', e);
@@ -82,17 +81,20 @@ export const EventDelete = async (token, params, id) => {
}
};
// 이벤트 우편 아이템 확인
export const EventIsItem = async (token, params) => {
// 이벤트 메타데이터 조회
export const EventActionView = async (token) => {
try {
const res = await Axios.post(`/api/v1/event/item`, params, {
headers: { Authorization: `Bearer ${token}` },
});
const res = await Axios.get(
`/api/v1/dictionary/event-action/list`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res;
return res.data.data.event_action_list;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventIsItem Error', e);
throw new Error('EventActionView Error', e);
}
}
};

View File

@@ -1,5 +1,4 @@
//사용자 관리 - 로그조회 api 연결
import { Axios } from '../utils';
export const LogViewList = async (token, searchType, searchKey, historyType, startDt, endDt, orderBy, size, currentPage) => {
@@ -13,7 +12,7 @@ export const LogViewList = async (token, searchType, searchKey, historyType, sta
},
);
return res.data.data;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('LogViewList Error', e);
@@ -34,3 +33,21 @@ export const LogviewDetail = async (token, id) => {
}
}
};
export const LogHistory = async (token, params) => {
try {
const res = await Axios.post(`/api/v1/history/change-list`, params, {
headers: { Authorization: `Bearer ${token}` },
});
if(res.data.result === 'ERROR'){
throw new Error('LogHistory Error', res.data.data.message);
}
return res.data.data.list;
} catch (e) {
if (e instanceof Error) {
throw new Error('LogHistory Error', e);
}
}
};

View File

@@ -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 {
@@ -62,10 +75,14 @@ export const userIndexExport = async (token, filename, sendDate, endDate) => {
};
// Retention
export const RetentionIndexView = async (token, start_dt, end_dt) => {
export const RetentionIndexView = async (token, startDate, endDate, order, size, currentPage) => {
try {
const res = await Axios.get(`/api/v1/indicators/retention/list?start_dt=${start_dt}&end_dt=${end_dt}`, {
headers: { Authorization: `Bearer ${token}` },
const res = await Axios.get(`/api/v1/indicators/retention/list?start_dt=${startDate}&end_dt=${endDate}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
});
return res.data.data;
@@ -183,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}&currency_type=${currency_type}`, {
const res = await Axios.get(`/api/v1/indicators/currency/list?start_dt=${start_dt}&end_dt=${end_dt}&currency_type=${currencyType}&delta_type=${deltaType}`, {
headers: { Authorization: `Bearer ${token}` },
});
@@ -198,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}&currency_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}` },
});
@@ -278,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);
}
}
};
@@ -320,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);
}
}
};

View File

@@ -2,26 +2,31 @@
import { Axios } from '../utils';
//아이템 리스트 조회
export const ItemListView = async (token, searchType, data, status, restore, order, size, currentPage) => {
export const ItemListAPI = async (token, params) => {
try {
const res = await Axios.get(
`/api/v1/items/list?search_type=${searchType ? searchType : ''}
&search_key=${data ? data : ''}
&orderby=${order}
&page_no=${currentPage}
&page_size=${size}
`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
// console.log(res.data.data);
const res = await Axios.post(`/api/v1/items/list`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('ItemAPI Error', e);
}
}
};
export const ItemDeleteAPI = async (token, params) => {
try {
const res = await Axios.delete(`/api/v1/items/delete`, {
headers: { Authorization: `Bearer ${token}` },
data: params,
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('ItemDelete Error', e);
}
}
};

View File

@@ -1,12 +1,13 @@
//운영 정보 관리 - 로그 api 연결
import { Axios } from '../utils';
import { Axios, responseFileDownload } from '../utils';
// 비즈니스 로그 조회
export const BusinessLogList = async (token, params) => {
try {
const res = await Axios.post(`/api/v1/log/generic/list`, params, {
headers: { Authorization: `Bearer ${token}` },
timeout: 600000
});
return res.data;
@@ -15,4 +16,298 @@ export const BusinessLogList = async (token, params) => {
throw new Error('BusinessLogList Error', e);
}
}
};
export const BusinessLogExport = async (token, params) => {
try {
await Axios.post(`/api/v1/log/generic/excel-export`, params, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob'
}).then(response => {
responseFileDownload(response, {
defaultFileName: 'businessLog'
});
});
} catch (e) {
if (e instanceof Error) {
throw new Error('BusinessLogExport Error', e);
}
}
};
export const getExcelProgress = async (token, taskId) => {
try {
const response = await Axios.get(`/api/v1/log/progress/${taskId}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
if (!response.data) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response;
} catch (error) {
console.error('Progress API error:', error);
throw error;
}
};
export const getCurrencyList = async (token, startDate, endDate, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/log/currency/list?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('getCurrencyList API error:', error);
throw error;
}
};
export const GameCurrencyLogExport = async (token, params, fileName) => {
try {
await Axios.post(`/api/v1/log/currency/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('GameCurrencyLogExport Error', e);
}
}
};
export const getCurrencyDetailList = async (token, searchType, searchData, tranId, logAction, currencyType, amountDeltaType, startDate, endDate, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/log/currency/detail/list?search_type=${searchType}&search_data=${searchData}&tran_id=${tranId}
&log_action=${logAction}&currency_type=${currencyType}&amount_delta_type=${amountDeltaType}&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('getCurrencyDetailList API error:', error);
throw error;
}
};
export const GameCurrencyDetailLogExport = async (token, params, fileName) => {
try {
await Axios.post(`/api/v1/log/currency/detail/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('GameCurrencyDetailLogExport Error', e);
}
}
};
export const getItemDetailList = async (token, searchType, searchData, itemId, tranId, logAction, itemLargeType, itemSmallType, countDeltaType, startDate, endDate, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/log/item/detail/list?search_type=${searchType}&search_data=${searchData}&tran_id=${tranId}&item_id=${itemId}
&log_action=${logAction}&item_large_type=${itemLargeType}&item_small_type=${itemSmallType}&count_delta_type=${countDeltaType}&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('getItemDetailList API error:', error);
throw error;
}
};
export const GameItemDetailLogExport = async (token, params, fileName) => {
try {
await Axios.post(`/api/v1/log/item/detail/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('GameItemDetailLogExport Error', e);
}
}
};
export const getCurrencyItemList = async (token, searchType, searchData, tranId, logAction, currencyType, amountDeltaType, startDate, endDate, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/log/currency-item/list?search_type=${searchType}&search_data=${searchData}&tran_id=${tranId}
&log_action=${logAction}&currency_type=${currencyType}&amount_delta_type=${amountDeltaType}&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('getItemDetailList API error:', error);
throw error;
}
};
export const GameCurrencyItemLogExport = async (token, params, fileName) => {
try {
await Axios.post(`/api/v1/log/currency-item/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('GameCurrencyItemLogExport Error', e);
}
}
};
export const getUserCreateList = async (token, searchType, searchData, startDate, endDate, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/log/user/create/list?search_type=${searchType}&search_data=${searchData}&start_dt=${startDate}&end_dt=${endDate}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
console.error('getUserCreateList API error:', error);
throw error;
}
};
export const getUserLoginDetailList = async (token, searchType, searchData, tranId, startDate, endDate, order, size, currentPage) => {
try {
const response = await Axios.get(`/api/v1/log/user/login/list?search_type=${searchType}&search_data=${searchData}&tran_id=${tranId}
&start_dt=${startDate}&end_dt=${endDate}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
console.error('getUserLoginDetailList API error:', error);
throw error;
}
};
export const GameUserCreateLogExport = async (token, params, fileName) => {
try {
await Axios.post(`/api/v1/log/user/create/excel-export`, params, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob',
timeout: 300000
}).then(response => {
responseFileDownload(response, {
defaultFileName: fileName
});
});
} catch (e) {
if (e instanceof Error) {
throw new Error('GameUserCreateLogExport Error', e);
}
}
};
export const GameUserLoginLogExport = async (token, params, fileName) => {
try {
await Axios.post(`/api/v1/log/user/login/excel-export`, params, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob',
timeout: 300000
}).then(response => {
responseFileDownload(response, {
defaultFileName: fileName
});
});
} catch (e) {
if (e instanceof Error) {
throw new Error('GameUserLoginLogExport Error', e);
}
}
};
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);
}
}
};

View File

@@ -72,7 +72,7 @@ export const MailModify = async (token, id, params) => {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data.list;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('MailModify Error', e);
@@ -88,7 +88,7 @@ export const MailDelete = async (token, params, id) => {
data: { list: params },
});
return res.data.data.list;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('MailDelete Error', e);
@@ -147,7 +147,7 @@ export const MailIsItem = async (token, params) => {
headers: { Authorization: `Bearer ${token}` },
});
return res;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('MailItemCheck Error', e);

View File

@@ -67,11 +67,10 @@ export const MenuBannerModify = async (token, id, params) => {
};
// 삭제
export const MenuBannerDelete = async (token, params) => {
export const MenuBannerDelete = async (token, id) => {
try {
const res = await Axios.delete(`/api/v1/menu/banner/delete`, {
headers: { Authorization: `Bearer ${token}` },
data: { list: params },
const res = await Axios.delete(`/api/v1/menu/banner/delete?id=${id}`, {
headers: { Authorization: `Bearer ${token}` }
});
return res.data;

View File

@@ -1,21 +0,0 @@
//AI api 연결
import { Axios } from '../utils';
export const AnalyzeAI = async (token, params) => {
try {
const res = await Axios.post('/api/v1/ai/analyze', params, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('analyzeAI Error', e);
}
}
};

217
src/apis/Rank.js Normal file
View File

@@ -0,0 +1,217 @@
//운영서비스 관리 - 랭킹 스케줄 api 연결
import { Axios } from '../utils';
// 리스트 조회
export const RankingScheduleView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
try {
const res = await Axios.get(
`/api/v1/rank/schedule/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res.data.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingScheduleView Error', e);
}
}
};
export const RankingScheduleSimpleView = async (token) => {
try {
const res = await Axios.get(
`/api/v1/rank/schedule/simple-list`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res.data.data.list;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingScheduleSimpleView Error', e);
}
}
};
export const RankingSnapshotView = async (token, guid) => {
try {
const res = await Axios.get(
`/api/v1/rank/snapshot/list?guid=${guid}`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res.data.data.ranking_snapshot_list;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingSnapshotView Error', e);
}
}
};
// 전투시스템 상세보기
export const RankingScheduleDetailView = async (token, id) => {
try {
const res = await Axios.get(`/api/v1/rank/schedule/detail/${id}`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingScheduleDetailView Error', e);
}
}
};
export const RankerListView = async (token, guid, snapshot) => {
try {
const res = await Axios.get(`/api/v1/rank/ranker/list?guid=${guid}&snapshot=${snapshot}`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankerListView Error', e);
}
}
};
// 랭킹스케줄 등록
export const RankingScheduleSingleRegist = async (token, params) => {
try {
const res = await Axios.post(`/api/v1/rank/schedule`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingScheduleSingleRegist Error', e);
}
}
};
// 랭킹스케줄 수정
export const RankingScheduleModify = async (token, id, params) => {
try {
const res = await Axios.put(`/api/v1/rank/schedule/${id}`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingScheduleModify Error', e);
}
}
};
// 랭킹스케줄 삭제
export const RankingScheduleDelete = async (token, id) => {
try {
const res = await Axios.delete(`/api/v1/rank/schedule/delete?id=${id}`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingScheduleDelete Error', e);
}
}
};
export const RankingDataView = async (token) => {
try {
const res = await Axios.get(
`/api/v1/dictionary/ranking/list`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res.data.data.ranking_list;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingDataView Error', e);
}
}
};
export const RankingInfoView = async (token, guid) => {
try {
const res = await Axios.get(`/api/v1/rank/info?guid=${guid}`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingInfoView Error', e);
}
}
};
export const RankerInfoModify = async (token, params) => {
try {
const res = await Axios.put(`/api/v1/rank/info`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankerInfoModify Error', e);
}
}
};
export const RankingUpdate = async (token, guid) => {
try {
const res = await Axios.put(`/api/v1/rank/ranking/${guid}`, {},{
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingUpdate Error', e);
}
}
};
export const RankingInit = async (token, guid) => {
try {
const res = await Axios.put(`/api/v1/rank/ranking/init/${guid}`, {},{
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingInit Error', e);
}
}
};
export const RankingSnapshot = async (token, guid) => {
try {
const res = await Axios.put(`/api/v1/rank/ranking/snapshot/${guid}`, {},{
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RankingSnapshot Error', e);
}
}
};

98
src/apis/RewardEvent.js Normal file
View File

@@ -0,0 +1,98 @@
//운영서비스 관리 - 이벤트 api 연결
import { Axios } from '../utils';
// 이벤트 리스트 조회
export const RewardEventView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
try {
const res = await Axios.get(
`/api/v1/event/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
&orderby=${order}&page_no=${currentPage}&page_size=${size}`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res.data.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RewardEventView Error', e);
}
}
};
// 이벤트 상세보기
export const RewardEventDetailView = async (token, id) => {
try {
const res = await Axios.get(`/api/v1/event/detail/${id}`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data.detail;
} catch (e) {
if (e instanceof Error) {
throw new Error('RewardEventDetailView Error', e);
}
}
};
// 이벤트 등록
export const RewardEventSingleRegist = async (token, params) => {
try {
const res = await Axios.post(`/api/v1/event`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res;
} catch (e) {
if (e instanceof Error) {
throw new Error('RewardEventSingleRegist Error', e);
}
}
};
// 우편 수정
export const RewardEventModify = async (token, id, params) => {
try {
const res = await Axios.put(`/api/v1/event/${id}`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('RewardEventModify Error', e);
}
}
};
// 우편 삭제
export const RewardEventDelete = async (token, params, id) => {
try {
const res = await Axios.delete(`/api/v1/event/delete`, {
headers: { Authorization: `Bearer ${token}` },
data: { list: params },
});
return res.data.data.list;
} catch (e) {
if (e instanceof Error) {
throw new Error('RewardEventDelete Error', e);
}
}
};
// 이벤트 우편 아이템 확인
export const EventIsItem = async (token, params) => {
try {
const res = await Axios.post(`/api/v1/event/item`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventIsItem Error', e);
}
}
};

View File

@@ -12,7 +12,7 @@ export const UserView = async (token, searchType, searchKey) => {
{ headers: { Authorization: `Bearer ${token}` } },
);
return res.data.data.result;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('UserView Error', e);
@@ -42,7 +42,7 @@ export const UserChangeNickName = async (token, params) => {
headers: { Authorization: `Bearer ${token}` },
});
return res;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('UserChangeNickName Error', e);
@@ -185,6 +185,21 @@ export const UserQuestView = async (token, guid) => {
}
};
//퀘스트 테스크 완료
export const UserQuestTaskComplete = async (token, params) => {
try {
const res = await Axios.post(`/api/v1/users/quest/task`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('UserQuestTaskComplete Error', e);
}
}
};
// 친구목록 조회
export const UserFriendListView = async (token, guid) => {
try {

View File

@@ -1,134 +0,0 @@
//운영서비스 관리 - 화이트 리스트 api 연결
import { Axios } from '../utils';
export const WhiteListData = async token => {
try {
const res = await Axios.get(`/api/v1/white-list/list`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data.list;
} catch (e) {
if (e instanceof Error) {
throw new Error('whiteList Error', e);
}
}
};
// 선택 삭제
export const WhiteListDelete = async (token, params) => {
try {
const res = await Axios.delete(`/api/v1/white-list`, {
headers: { Authorization: `Bearer ${token}` },
data: { list: params },
});
return res;
} catch (e) {
if (e instanceof Error) {
throw new Error('WhiteListDelete', e);
}
}
};
// 선택 승인
export const WhiteListAllow = async (token, params) => {
try {
const res = await Axios.patch(
`/api/v1/white-list`,
{ list: params },
{
headers: { Authorization: `Bearer ${token}` },
},
);
return res;
} catch (e) {
if (e instanceof Error) {
throw new Error('WhiteListAllow', e);
}
}
};
// 화이트 리스트 등록 (단일)
export const WhiteListRegist = async (token, params) => {
try {
const res = await Axios.post(`/api/v1/white-list`, params, {
headers: { Authorization: `Bearer ${token}` },
});
return res;
} catch (e) {
if (e instanceof Error) {
throw new Error('WhiteListRegist', e);
}
}
};
// 화이트리스트 엑셀 업로더
export const WhiteListExelUpload = async (token, file) => {
const exelFile = new FormData();
exelFile.append('file', file);
try {
const res = await Axios.post(`/api/v1/white-list/excel-upload`, exelFile, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${token}`,
},
});
return res;
} catch (e) {
if (e instanceof Error) {
throw new Error('WhiteListExelUpload', e);
}
}
};
// 화이트 리스트 등록(복수) -> 등록하는 것임
export const WhiteListMultiRegsit = async (token, file) => {
const exelFile = new FormData();
exelFile.append('file', file);
try {
const res = await Axios.post(`/api/v1/white-list/multiPost`, exelFile, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${token}`,
},
});
return res;
} catch (e) {
if (e instanceof Error) {
throw new Error('WhiteListMultiRegsit', e);
}
}
};
// 엑셀 다운로드
export const WhiteListExport = async (token, fileName) => {
try{
await Axios.get(`/api/v1/white-list/excelDownLoad`, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob',
}).then(response => {
const href = URL.createObjectURL(response.data);
const link = document.createElement('a');
const fileName = 'Caliverse_whitelist.xlsx';
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('WhiteListExport Error', e);
}
}
};

View File

@@ -1,16 +1,42 @@
import { createAPIModule } from '../utils/apiService';
import * as APIConfigs from '../assets/data/apis'
export * from './Admin';
export * from './Auth';
export * from './Group';
export * from './History';
export * from './Mail';
export * from './Notice';
export * from './WhiteList';
export * from './BlackList';
export * from './Users';
export * from './Indicators';
export * from './Item';
export * from './Event';
export * from './RewardEvent';
export * from './Calium';
export * from './Land';
export * from './Menu';
export * from './OpenAI';
// export * from './OpenAI';
export * from './Log';
export * from './Data';
export * from './Dictionary';
export * from './Rank';
export * from './Event';
const apiModules = {};
const allApis = {};
// 각 API 설정에 대해 모듈 생성
Object.entries(APIConfigs).forEach(([configName, config]) => {
const moduleName = configName.replace(/API$/, ''); // "userAPI" -> "user"
apiModules[moduleName] = createAPIModule(config);
// 모든 API 함수 추출해서 allApis에 복사
Object.entries(apiModules[moduleName]).forEach(([fnName, fn]) => {
allApis[fnName] = fn;
});
});
export const Modules = apiModules;
// export const ItemAPI = createAPIModule(itemAPIConfig);

View File

@@ -8,5 +8,13 @@ export const ONE_MINUTE_MS = 60000;
export const ONE_MINUTE_SECOND = 60;
export const AUCTION_MIN_MINUTE_TIME = 15; // 15분
export const IMAGE_MAX_SIZE = 5242880;
export const STORAGE_MAIL_COPY = 'copyMailData';
export const STORAGE_BUSINESS_LOG_SEARCH = 'businessLogSearchParam';
export const STORAGE_GAME_LOG_CURRENCY_SEARCH = 'gameLogCurrencySearchParam';
export const STORAGE_GAME_LOG_ITEM_SEARCH = 'gameLogItemSearchParam';
export const STORAGE_GAME_LOG_USER_CREATE_SEARCH = 'gameLogUserCreateSearchParam';
export const STORAGE_GAME_LOG_USER_LOGIN_SEARCH = 'gameLogUserLoginSearchParam';
export const LOG_ACTION_FAIL_CALIUM_ECHO = 'FailCaliumEchoSystem';
export const BATTLE_EVENT_OPERATION_TIME_WAIT_SECONDS = 300;
export { INITIAL_PAGE_SIZE, INITIAL_CURRENT_PAGE, INITIAL_PAGE_LIMIT };

View File

@@ -0,0 +1,18 @@
{
"baseUrl": "/api/v1/world-event",
"endpoints": {
"EventView": {
"method": "GET",
"url": "/list",
"dataPath": "data",
"paramFormat": "query"
},
"EventDetailView": {
"method": "GET",
"url": "/detail/:id",
"dataPath": "data.data",
"paramFormat": "query",
"paramMapping": ["id"]
}
}
}

View File

@@ -0,0 +1,18 @@
{
"baseUrl": "/api/v1/history",
"endpoints": {
"LogViewList": {
"method": "GET",
"url": "/list",
"dataPath": "data",
"paramFormat": "query"
},
"LogviewDetail": {
"method": "GET",
"url": "/detail/:id",
"dataPath": "data.data",
"paramFormat": "query",
"paramMapping": ["id"]
}
}
}

View File

@@ -0,0 +1,17 @@
import itemAPI from './itemAPI.json';
import menuBannerAPI from './menuBannerAPI.json';
import historyAPI from './historyAPI.json';
import eventAPI from './eventAPI.json';
import rankingAPI from './rankingAPI.json';
import metaCraftingAPI from './metaCraftingAPI.json';
import metaInstanceAPI from './metaInstanceAPI.json';
export {
itemAPI,
menuBannerAPI,
historyAPI,
eventAPI,
rankingAPI,
metaCraftingAPI,
metaInstanceAPI
};

View File

@@ -0,0 +1,17 @@
{
"baseUrl": "/api/v1/items",
"endpoints": {
"ItemList": {
"method": "POST",
"url": "/list",
"dataPath": "data",
"paramFormat": "body"
},
"ItemDelete": {
"method": "DELETE",
"url": "/delete",
"dataPath": "data",
"paramFormat": "body"
}
}
}

View File

@@ -0,0 +1,48 @@
{
"baseUrl": "/api/v1/menu",
"endpoints": {
"MenuBannerView": {
"method": "GET",
"url": "/banner/list",
"dataPath": "data.data",
"paramFormat": "query"
},
"MenuBannerDetailView": {
"method": "GET",
"url": "/banner/detail/:id",
"dataPath": "data.data",
"paramFormat": "query",
"paramMapping": ["id"]
},
"MenuBannerSingleRegist": {
"method": "POST",
"url": "/banner",
"dataPath": "data",
"paramFormat": "body"
},
"MenuBannerModify": {
"method": "PUT",
"url": "/banner/:id",
"dataPath": "data",
"paramFormat": "body"
},
"MenuBannerDelete": {
"method": "DELETE",
"url": "/banner/delete",
"dataPath": "data",
"paramFormat": "body"
},
"MenuImageUpload": {
"method": "POST",
"url": "/image-upload",
"dataPath": "data",
"paramFormat": "body"
},
"MenuImageDelete": {
"method": "DELETE",
"url": "/image-delete",
"dataPath": "data",
"paramFormat": "body"
}
}
}

View File

@@ -0,0 +1,11 @@
{
"baseUrl": "/api/v1/dictionary/craft",
"endpoints": {
"getCraftingDictionaryList": {
"method": "GET",
"url": "/list",
"dataPath": "data",
"paramFormat": "query"
}
}
}

View File

@@ -0,0 +1,11 @@
{
"baseUrl": "/api/v1/dictionary/instance",
"endpoints": {
"getInstanceDictionaryList": {
"method": "GET",
"url": "/list",
"dataPath": "data",
"paramFormat": "query"
}
}
}

View File

@@ -0,0 +1,18 @@
{
"baseUrl": "/api/v1/rank/schedule",
"endpoints": {
"RankingScheduleView": {
"method": "GET",
"url": "/list",
"dataPath": "data",
"paramFormat": "query"
},
"RankingScheduleDetailView": {
"method": "GET",
"url": "/detail/:id",
"dataPath": "data.data",
"paramFormat": "query",
"paramMapping": ["id"]
}
}
}

View File

@@ -5,6 +5,14 @@ export const benItems = [
"19010005"
];
export const historyBenField = [
"create_by",
"create_dt",
"update_by",
"update_dt",
"id"
]
export const HourList = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
export const MinuteList = [
@@ -58,11 +66,11 @@ export const STATUS_STYLES = {
color: 'white'
},
WAIT: {
background: '#DEBB46',
background: '#FAAD14',
color: 'black'
},
FAIL: {
background: '#D33B27',
background: '#ff4d4f',
color: 'white'
},
FINISH: {
@@ -70,11 +78,11 @@ export const STATUS_STYLES = {
color: 'black'
},
REJECT: {
background: '#D33B27',
background: '#ff4d4f',
color: 'white'
},
CANCEL: {
background: '#D33B27',
background: '#ff4d4f',
color: 'white'
},
RESV_START: {
@@ -98,7 +106,7 @@ export const STATUS_STYLES = {
color: 'white'
},
REGISTER: {
background: '#DEBB46',
background: '#FAAD14',
color: 'black'
},
STOP: {
@@ -113,4 +121,126 @@ export const STATUS_STYLES = {
background: '#4287f5',
color: 'white'
},
};
};
export const FieldLabels = {
// DynamoDB 필드
'attribFieldName': '속성 명',
'pk': '파티션 키',
'sk': '정렬 키',
'DocType': '문서 타입',
'CreatedDateTime': '생성 일시',
'UpdatedDateTime': '수정 일시',
'DeletedDateTime': '삭제 일시',
'RestoredDateTime': '복원 일시',
'INSERT': '등록',
'UPDATE': '수정',
'DELETE': '삭제',
//기본
'id': 'ID',
'userId': '사용자 ID',
'userIP': '사용자 IP',
'timestamp': '타임스탬프',
'message': '메시지',
'tranId': '트랜잭션 ID',
'group_id': '그룹 ID',
'update_dt': '수정 일시',
'updateDt': '수정 일시',
'update_by': '수정자',
'create_dt': '생성 일시',
'createDt': '생성 일시',
'create_by': '생성자',
'status': '상태',
'deleted': '삭제 여부',
'guid': 'GUID',
'meta_id': '메타 ID',
'start_dt': '시작일',
'end_dt': '종료일',
'base_dt': '기준일',
'title': '제목',
// 이벤트 필드 관련
'eventId': '이벤트 ID',
'eventName': '이벤트 명',
'event_name': '이벤트 명',
'repeatType': '반복 타입',
'repeat_type': '반복 타입',
'eventOperationTime': '진행 시간(분)',
'event_operation_time': '진행 시간(분)',
'eventStartDt': '이벤트 시작일',
'event_start_dt': '이벤트 시작일',
'eventEndDt': '이벤트 종료일',
'event_end_dt': '이벤트 종료일',
'roundTime': '라운드 시간(초)',
'round_time': '라운드 시간(초)',
'roundCount': '라운드 수',
'hotTime': '핫타임',
'configId': '설정 ID',
'rewardGroupId': '보상 그룹 ID',
'attrib_type': '속성 타입',
'event_id': '이벤트 ID',
'is_active': '활성화 여부',
'start_day': '시작일',
'start_hour': '시작 시간',
'start_min': '시작 분',
'end_date': '종료 일시',
'instance_id': '인스턴스 ID',
'once_period_type': '주기 타입',
'day_of_week_type': '요일 타입',
'ffa_config_data_id': 'FFA 설정 ID',
'ffa_reward_group_id': 'FFA 보상 그룹 ID',
'ffa_hot_time': 'FFA 핫타임',
'round_count': '라운드 수',
//dictionary
'max_count': '최대 보유 가능 수량',
'stack_max_count': '최대 스택 가능 수량',
'expire_type': '아이템 만료 타입',
'expire_start_dt': '만료 시작 시간',
'expire_end_dt': '만료 종료 시간',
'expire_time_sec': '만료 시간 연장 여부',
'user_tradable': '유저 간 거래 가능 여부',
'system_tradable': '상점에서 판매 가능 여부',
'throwable': '버리기 가능 여부',
'cart_buy': '상점에서 구매 가능 여부',
'rarity': '희귀도',
'default_attrib': '기본 속성',
'attrib_random_group': '랜덤 그룹',
'item_set': '아이템 세트',
'buff': '아이템 사용 시 획득 버프',
'dress_slot_type': '착용 부위',
'product_link': '제품 URL',
'prop_small_type': '제작 아이템 그룹',
'gacha_group_id': '랜덤박스 그룹 ID',
'ugq_action': 'UGQ 사용 가능 여부',
'linked_land': '연결된 랜드 ID',
//스케줄
'refresh_interval': '새로고침 주기',
'initialization_interval': '초기화 주기',
'snapshot_interval': '스냅샷 주기',
'event_action_id': '이벤트 액션 그룹',
'global_event_action_id': '이벤트 액션 그룹',
'personal_event_action_id': '개인제작 액션 그룹',
'max_point': '기여도 목표점수',
//메뉴
'image_list': '이미지 목록',
'is_link': '링크 여부',
'order_id': '정렬'
};
export const historyTables = {
userBlock: 'black_list',
landAuction: 'land_auction',
landOwnerChange: 'land_ownership_changes',
rewardEvent: 'event',
mail: 'mail',
notice: 'notice',
battleEvent: 'battle_event',
caliumRequest: 'calium_request',
menuBanner: 'menu_banner',
event: 'world_event',
rankingSchedule: 'ranking_schedule',
}

View File

@@ -1,4 +1,4 @@
export {authType, ivenTabType, modalTypes, TabList, tattooSlot, commonStatus, ViewTitleCountType, landAuctionStatusType} from './types'
export {authType, ivenTabType, modalTypes, tattooSlot, commonStatus, ViewTitleCountType, landAuctionStatusType} from './types'
export {
mailSendType,
mailType,
@@ -7,14 +7,14 @@ export {
adminLevelType,
logOption,
eventStatus,
wellType,
currencyItemCode,
blockStatus,
blockSanctions,
blockPeriod,
blockType,
caliumStatus,
landSize,
userSearchType,
userType,
landAuctionStatus,
landSearchType,
CurrencyType,
@@ -27,5 +27,7 @@ export {
opYNType,
opUserSessionType,
opMailType,
amountDeltaType,
TabUserList
} from './options'
export {benItems, MinuteList, HourList, caliumRequestInitData, STATUS_STYLES, months, PAGE_SIZE_OPTIONS, ORDER_OPTIONS} from './data'

View File

@@ -103,14 +103,6 @@ export const menuConfig = {
view: true,
authLevel: adminAuthLevel.NONE
},
cryptview: {
title: '크립토 조회',
permissions: {
read: authType.cryptoRead
},
view: true,
authLevel: adminAuthLevel.NONE
},
businesslogview: {
title: '비즈니스 로그 조회',
permissions: {
@@ -118,6 +110,47 @@ export const menuConfig = {
},
view: true,
authLevel: adminAuthLevel.NONE
},
itemdictionary: {
title: '아이템 백과사전 조회',
permissions: {
read: authType.itemDictionaryRead
},
view: true,
authLevel: adminAuthLevel.NONE
},
craftdictionary: {
title: '제작 아이템 조회',
permissions: {
read: authType.craftingDictionaryRead
},
view: true,
authLevel: adminAuthLevel.NONE
},
instancedictionary: {
title: '인스턴스 조회',
permissions: {
read: authType.instanceDictionaryRead
},
view: true,
authLevel: adminAuthLevel.NONE
},
rankmanage: {
title: '랭킹 점수 관리',
permissions: {
read: authType.rankManagerRead,
update: authType.rankManagerUpdate,
},
view: true,
authLevel: adminAuthLevel.NONE
},
rankview: {
title: '랭킹 시스템 조회',
permissions: {
read: authType.rankInfoRead,
},
view: true,
authLevel: adminAuthLevel.NONE
}
}
},
@@ -154,17 +187,17 @@ export const menuConfig = {
view: true,
authLevel: adminAuthLevel.NONE
},
reportlist: {
title: '신고내역',
permissions: {
read: authType.reportRead,
update: authType.reportUpdate,
delete: authType.reportDelete
},
view: true,
authLevel: adminAuthLevel.NONE
},
event: {
// reportlist: {
// title: '신고내역',
// permissions: {
// read: authType.reportRead,
// update: authType.reportUpdate,
// delete: authType.reportDelete
// },
// view: true,
// authLevel: adminAuthLevel.NONE
// },
rewardevent: {
title: '보상 이벤트 관리',
permissions: {
read: authType.eventRead,
@@ -194,6 +227,16 @@ export const menuConfig = {
view: true,
authLevel: adminAuthLevel.NONE
},
items: {
title: '아이템 관리',
permissions: {
read: authType.itemRead,
update: authType.itemUpdate,
delete: authType.itemDelete
},
view: true,
authLevel: adminAuthLevel.NONE
},
menubanner: {
title: '메뉴 배너 관리',
permissions: {
@@ -204,6 +247,26 @@ export const menuConfig = {
view: true,
authLevel: adminAuthLevel.NONE
},
ranking: {
title: '랭킹 스케줄러',
permissions: {
read: authType.rankingRead,
update: authType.rankingUpdate,
delete: authType.rankingDelete
},
view: true,
authLevel: adminAuthLevel.NONE
},
event: {
title: '통합 이벤트 관리',
permissions: {
read: authType.worldEventRead,
update: authType.worldEventUpdate,
delete: authType.worldEventDelete
},
view: true,
authLevel: adminAuthLevel.NONE
},
}
}
};

View File

@@ -4,6 +4,52 @@ export const languageType = [
{ value: 'JA', name: '일본어' },
];
export const TabUserList = [
{ value: 'BASIC', name: '기본정보' },
{ value: 'AVATAR', name: '아바타' },
{ value: 'CLOTH', name: '의상' },
{ value: 'TOOL', name: '도구' },
{ value: 'INVENTORY', name: '인벤토리' },
{ value: 'MAIL', name: '우편' },
{ value: 'MYHOME', name: '마이홈' },
{ value: 'FRIEND', name: '친구목록' },
{ value: 'TATTOO', name: '타투' },
{ value: 'QUEST', name: '퀘스트' }
// { value: 'CLAIM', name: '클레임' }
];
export const TabGameLogList = [
{ value: 'CURRENCY', name: '재화 로그' },
{ value: 'ITEM', name: '아이템 로그' },
{ value: 'CURRENCYITEM', name: '재화(아이템) 로그' },
{ value: 'USERCREATE', name: '유저생성 로그' },
{ value: 'USERLOGIN', name: '유저로그인 로그' },
{ value: 'SNAPSHOT', name: '스냅샷 로그' },
];
export const TabEconomicIndexList = [
{ 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: '플레이타임' },
];
export const TabRankManageList = [
{ value: 'RANK', name: '랭킹 보드' },
// { value: 'RUN_RACE', name: '점프 러너 랭킹 보드' },
// { value: 'BATTLE_OBJECT', name: '컴뱃 존 랭킹 보드' }
];
export const mailSendType = [
{ value: 'ALL', name: '전체' },
{ value: 'RESERVE_SEND', name: '예약 발송' },
@@ -45,10 +91,10 @@ export const mailReceiveType = [
];
export const adminLevelType = [
{ value: '0', name: '없음' },
{ value: '1', name: 'GM' },
{ value: '2', name: 'Super GM' },
{ value: '3', name: 'Developer' },
{ value: 0, name: '없음' },
{ value: 1, name: 'GM' },
{ value: 2, name: 'Super GM' },
{ value: 3, name: 'Developer' },
]
export const eventStatus = [
@@ -81,7 +127,18 @@ export const landAuctionStatus = [
{ value: 'FAIL', name: '실패' },
];
export const wellType = [
export const questStatus = [
{ value: 'WAIT', name: '미완료' },
{ value: 'COMPLETE', name: '완료' },
{ value: 'RUNNING', name: '진행중' },
];
export const questCompleteStatusType = [
{ value: 0, name: '미완료' },
{ value: 1, name: '완료' }
]
export const currencyItemCode = [
{ value: '19010001', name: '골드' },
{ value: '19010002', name: '사파이어' },
{ value: '19010005', name: '루비' },
@@ -129,6 +186,7 @@ export const blockStatus = [
{ value: 'EXPIRATION', name: '기간만료' },
{ value: 'WAIT', name: '대기 중' },
{ value: 'FAIL', name: '실패' },
{ value: 'CANCEL', name: '해지' },
];
export const blockSanctions = [
@@ -156,11 +214,21 @@ export const blockPeriod = [
{ value: 'PERMANENT', name: '영구정지' },
];
export const userSearchType = [
export const userType = [
{ value: 'GUID', name: 'GUID' },
{ value: 'NAME', name: '닉네임' },
];
export const userType2 = [
{ value: 'GUID', name: 'GUID' },
{ value: 'NICKNAME', name: '닉네임' },
];
export const adminUserType = [
{ value: 'ID', name: 'ID(이메일)' },
{ value: 'NAME', name: '이름' },
];
export const userSearchType2 = [
{ value: 'GUID', name: 'GUID' },
{ value: 'NICKNAME', name: '닉네임' },
@@ -172,6 +240,17 @@ export const landSearchType = [
{ value: 'NAME', name: '랜드명' },
];
export const itemSearchType = [
{ value: 'ID', name: '아이템ID' },
{ value: 'NAME', name: '아이템명' },
];
export const instanceSearchType = [
{ value: 'ID', name: '인스턴스 ID' },
{ value: 'NAME', name: '인스턴스명' },
{ value: 'BUILDING', name: '빌딩 ID' },
];
export const blockType = [
{ value: '', name: '선택' },
{ value: 'Access_Restrictions', name: '접근 제한' },
@@ -194,6 +273,30 @@ export const CurrencyType = [
{value: 'Ruby', name: '루비' }
]
export const amountDeltaType = [
{value: 'Acquire', name: '획득' },
{value: 'Consume', name: '소모' },
{value: 'None', name: '' },
]
export const countDeltaType = [
{value: 'Acquire', name: '획득' },
{value: 'Consume', name: '소모' }
]
export const itemTypeLarge = [
{value: 'TOOL', name: '도구' },
{value: 'EXPENDABLE', name: '소모품' },
{value: 'TICKET', name: '티켓' },
{value: 'RAND_BOX', name: '랜덤 박스' },
{value: 'CLOTH', name: '의상' },
{value: 'AVATAR', name: '아바타' },
{value: 'PROP', name: '프랍(오브젝트)' },
{value: 'TATTOO', name: '타투' },
{value: 'CURRENCY', name: '재화' },
{value: 'SET_BOX', name: '세트 박스' }
]
export const battleEventStatus = [
{ value: 'ALL', name: '전체' },
{ value: 'WAIT', name: '대기' },
@@ -297,6 +400,294 @@ export const opMenuBannerStatus = [
{ value: 'FINISH', name: '만료' },
];
export const opItemStatus = [
{ value: 'ALL', name: '전체' },
{ value: 'ACTIVE', name: '활성' },
{ value: 'DEACTIVE', name: '비활성' },
];
export const opItemRestore = [
{ value: 'ALL', name: '전체' },
{ value: 'POSSIBLE', name: '가능' },
{ value: 'IMPOSSIBLE', name: '불가능' },
];
export const opPropSmallType = [
{ value: 'ALL', name: '전체' },
{ value: 'FURNITURE', name: 'FURNITURE' },
{ value: 'COOKING', name: 'COOKING' },
{ value: 'CLOTHES', name: 'CLOTHES' },
];
export const opPropRecipeType = [
{ value: 'ALL', name: '전체' },
{ value: 'Basic', name: '기본' },
{ value: 'Add', name: '등록 필요' },
];
export const opInstanceContentsType = [
{ value: 'ALL', name: '전체' },
{ value: 'Concert', name: 'Concert' },
{ value: 'Movie', name: 'Movie' },
{ value: 'Meeting', name: 'Meeting' },
{ value: 'MyHome', name: 'MyHome' },
{ value: 'Normal', name: 'Normal' },
];
export const opInstanceAccessType = [
{ value: 'ALL', name: '전체' },
{ value: 'Public', name: 'Public' },
{ value: 'Item', name: 'Item' },
{ value: 'Belong', name: 'Belong' },
];
export const opEquipType = [
{ value: 0, name: '미장착' },
{ value: 1, name: '의상장착' },
{ value: 2, name: '도구장착' },
{ value: 3, name: '타투장착' },
]
export const opItemLargeType = [
{ value: 'TOOL', name: '도구' },
{ value: 'EXPENDABLE', name: '소모품' },
{ value: 'TICKET', name: '티켓' },
{ value: 'RAND_BOX', name: '랜덤 박스' },
{ value: 'CLOTH', name: '의상' },
{ value: 'AVATAR', name: '아바타' },
{ value: 'PROP', name: '프랍' },
{ value: 'TATTOO', name: '타투' },
{ value: 'CURRENCY', name: '재화' },
{ value: 'PRODUCT', name: '제품' },
{ value: 'BEAUTY', name: '뷰티' },
{ value: 'SET_BOX', name: '세트 박스' },
]
export const opItemSmallType = [
{ value: 'HANDMIRROR', name: '손거울' },
{ value: 'LIGHTSTICK', name: '응원봉' },
{ value: 'FIRECRACKER', name: '폭죽총' },
{ value: 'LIGHTSABER', name: '광선검' },
{ value: 'REGISTER_ITEM_SOCIAL_ACTION', name: '소셜액션 등록형 아이템' },
{ value: 'REGISTER_ITEM_INTERIOR', name: '인테리어 등록형 아이템' },
{ value: 'SAIYAN_AURA', name: '초사이어인 오라' },
{ value: 'TICKET', name: '소모형 티켓' },
{ value: 'RANDOMBOX', name: '골드(재화) 가챠 랜덤 박스' },
{ value: 'SHIRT', name: '상의' },
{ value: 'DRESS', name: '드레스' },
{ value: 'OUTER', name: '겉옷' },
{ value: 'PANTS', name: '하의' },
{ value: 'GLOVES', name: '장갑' },
{ value: 'RING', name: '반지' },
{ value: 'BRACELET', name: '팔찌' },
{ value: 'BAG', name: '가방(크로스백)' },
{ value: 'BACKPACK', name: '가방(백팩)' },
{ value: 'CAP', name: '모자' },
{ value: 'MASK', name: '가면' },
{ value: 'GLASSES', name: '안경' },
{ value: 'EARRING', name: '귀걸이' },
{ value: 'NECKLACE', name: '목걸이' },
{ value: 'SHOES', name: '신발' },
{ value: 'SOCKS', name: '양말' },
{ value: 'ANKLET', name: '발찌' },
{ value: 'OFFICECHAIR', name: '의자' },
{ value: 'WALLMOUNTTV', name: '벽걸이TV' },
{ value: 'OUTDOORCHAIR', name: '야외용의자' },
{ value: 'TV', name: 'TV' },
{ value: 'VIGNETTE', name: '소품' },
{ value: 'COOKWARE', name: '조리도구' },
{ value: 'KITCHEN_TOOL', name: '부엌도구' },
{ value: 'LAPTOP', name: '노트북' },
{ value: 'OUTDOOR_GOODS', name: '캠핑도구' },
{ value: 'BED', name: '침대' },
{ value: 'DECO', name: '소형장식품' },
{ value: 'FURNITURE', name: '(러그)가구' },
{ value: 'MUSIC', name: '악기/음향' },
{ value: 'SHELF_S', name: '소형 선반(TV다이)' },
{ value: 'SHELF_L', name: '대형 선반(금고,책장)' },
{ value: 'SOFA_SINGLE', name: '1인용 소파' },
{ value: 'SOFA_COUCH', name: '카우치 소파' },
{ value: 'LIGHT_CEILING', name: '천정 조명' },
{ value: 'LIGHT_FLOOR', name: '스탠드 조명' },
{ value: 'LIGHT_TABLE', name: '탁상 조명' },
{ value: 'LIGHT_PENDENT', name: '팬던트 조명' },
{ value: 'TABLE_S', name: '소형 테이블' },
{ value: 'TABLE_L', name: '대형 테이블' },
{ value: 'TABLE_LIVINGROOM', name: '거실 테이블' },
{ value: 'TABLE_OFFICE', name: '사무용 테이블' },
{ value: 'LEISURE_APPLIANCE', name: '스포츠/여가' },
{ value: 'INDUCTION', name: '인덕션' },
{ value: 'MICROWAVE', name: '전자레인지' },
{ value: 'LARGE_APPLIANCE', name: '대형가전' },
{ value: 'COSMETIC', name: '화장품' },
{ value: 'CHEST', name: '앞면' },
{ value: 'LEFT_ARM', name: '왼팔' },
{ value: 'RIGHT_ARM', name: '오른팔' },
{ value: 'BACK', name: '후면' },
{ value: 'LEFT_LEG', name: '왼다리' },
{ value: 'RIGHT_LEG', name: '오른다리' },
{ value: 'CARTRIDGE', name: '속성 카트리지' },
{ value: 'BUFF_DRINK', name: '드링크(물약) 아이템' },
{ value: 'INTERPHONE', name: '인터폰' },
{ value: 'MEGAPHONE', name: '확성기' },
{ value: 'CURRENCY', name: 'CURRENCY' },
{ value: 'NFTLAND', name: 'NFTLAND' },
{ value: 'SUMMONSTONE', name: '소환석' },
{ value: 'GOLD', name: '골드' },
{ value: 'SAPPHIRE', name: '사파이어' },
{ value: 'CALIUM', name: '칼리움' },
{ value: 'BEAM', name: '빔' },
{ value: 'RUBY', name: '루비' },
{ value: 'LIGHT_LIMITED', name: '언리얼 라이트 사용 조명' },
{ value: 'SPEAKER', name: '재생 기능성 스피커' },
{ value: 'SETBOX', name: '세트박스' },
{ value: 'DRESS_SHOES', name: '드레스+신발' },
{ value: 'SHOULDERBAG', name: '숄더백' },
{ value: 'RECIPE', name: '레시피' }
]
export const opGender = [
{ value: 'MALE', name: '남성' },
{ value: 'FEMALE', name: '여성' },
]
export const opHistoryType = [
{ value: '', name: '전체' },
{ value: 'LOGIN_PERMITTED', name: '로그인 승인' },
{ value: 'ADMIN_INFO_UPDATE', name: '운영자 정보 수정' },
{ value: 'ADMIN_INFO_DELETE', name: '운영자 정보 삭제' },
{ value: 'PASSWORD_INIT', name: '비밀번호 초기화' },
{ value: 'USER_INFO_UPDATE', name: '유저 정보 변경' },
{ value: 'GROUP_AUTH_UPDATE', name: '그룹 권한 수정' },
{ value: 'GROUP_DELETE', name: '그룹 삭제' },
{ value: 'NOTICE_DELETE', name: '공지사항 삭제' },
{ value: 'NOTICE_ADD', name: '공지사항 등록' },
{ value: 'NOTICE_UPDATE', name: '공지사항 수정' },
{ value: 'NOTICE_SEND_FAIL', name: '공지사항 전송 실패' },
{ value: 'MAIL_DELETE', name: '우편 삭제' },
{ value: 'MAIL_ADD', name: '우편 등록' },
{ value: 'MAIL_UPDATE', name: '우편 수정' },
{ value: 'MAIL_SEND', name: '우편 전송' },
{ value: 'MAIL_SEND_FAIL', name: '우편 전송 실패' },
{ value: 'MAIL_ITEM_DELETE', name: '우편 아이템 삭제' },
{ value: 'MAIL_ITEM_UPDATE', name: '우편 아이템 수정' },
{ value: 'BLACKLIST_ADD', name: '유저 제재 등록' },
{ value: 'BLACKLIST_UPDATE', name: '유저 제재 수정' },
{ value: 'BLACKLIST_DELETE', name: '유저 제재 삭제' },
{ value: 'REPORT_DELETE', name: '신고내역 삭제' },
{ value: 'USER_ITEM_DELETE', name: '유저 아이템 삭제' },
{ value: 'SCHEDULE_MAIL_FAIL', name: '메일 스케줄 실패' },
{ value: 'SCHEDULE_NOTICE_FAIL', name: '공지 스케줄 실패' },
{ value: 'SCHEDULE_EVENT_FAIL', name: '이벤트 스케줄 실패' },
{ value: 'USER_MAIL_DELETE', name: '유저 메일 삭제' },
{ value: 'INVENTORY_ITEM_DELETE', name: '인벤토리 아이템 삭제' },
{ value: 'INVENTORY_ITEM_UPDATE', name: '인벤토리 아이템 수정' },
{ value: 'EVENT_ADD', name: '이벤트 등록' },
{ value: 'EVENT_UPDATE', name: '이벤트 수정' },
{ value: 'EVENT_DELETE', name: '이벤트 삭제' },
{ value: 'CALIUM_ADD', name: '칼리움 요청 등록' },
{ value: 'CALIUM_SAVE', name: '칼리움 저장' },
{ value: 'CALIUM_TRANSFER', name: '칼리움 전송' },
{ value: 'CALIUM_TOTAL_UPDATE', name: '칼리움 충전' },
{ value: 'LAND_AUCTION_ADD', name: '랜드경매 등록' },
{ value: 'LAND_AUCTION_UPDATE', name: '랜드경매 수정' },
{ value: 'LAND_AUCTION_DELETE', name: '랜드경매 삭제' },
{ value: 'BATTLE_EVENT_ADD', name: '전투시스템 이벤트 등록' },
{ value: 'BATTLE_EVENT_UPDATE', name: '전투시스템 이벤트 수정' },
{ value: 'BATTLE_EVENT_DELETE', name: '전투시스템 이벤트 삭제' },
{ value: 'LAND_OWNER_CHANGE_ADD', name: '랜드 소유권 변경 등록' },
{ value: 'LAND_OWNER_CHANGE_UPDATE', name: '랜드 소유권 변경 수정' },
{ value: 'LAND_OWNER_CHANGE_DELETE', name: '랜드 소유권 변경 예약 취소' },
{ value: 'LAND_OWNER_CHANGE_MAIL', name: '랜드 소유권 변경 우편' },
{ value: 'LAND_OWNED_INITIALIZE', name: '랜드 소유권 정보 초기화' },
{ value: 'LAND_DESC_INITIALIZE', name: '랜드 정보 초기화' },
{ value: 'LAND_AUCTION_INITIALIZE', name: '랜드 경매 초기화' },
{ value: 'MENU_BANNER_ADD', name: '메뉴 배너 등록' },
{ value: 'MENU_BANNER_UPDATE', name: '메뉴 배너 수정' },
{ value: 'MENU_BANNER_DELETE', name: '메뉴 배너 삭제' },
{ value: 'ITEM_UPDATE', name: '아이템 수정' },
{ value: 'ITEM_DELETE', name: '아이템 삭제' },
{ value: 'USER_ADMIN_AUTH_UPDATE', name: '유저 관리자 권한 수정' },
{ value: 'NICKNAME_REGISTRY_DELETE', name: '닉네임 레지스트리 삭제' },
{ value: 'NICKNAME_REGISTRY_ADD', name: '닉네임 레지스트리 등록' },
{ value: 'NICKNAME_UPDATE', name: '닉네임 수정' },
{ value: 'DATA_INIT_ADD', name: '데이터 초기화 등록' },
];
export const opDBType = [
{ value: '', name: '전체'},
{ value: 'dynamoDB', name: 'dynamoDB'},
{ 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: '실패' },
{ value: 'WAIT', name: '대기' },
{ value: 'END', name: '종료' },
{ value: 'RUNNING', name: '진행중' },
]
export const opRankingType = [
{ value: 'PIONEER', name: '개척자' },
{ value: 'RUNNER1', name: '점프러너 - 맵1' },
{ value: 'RUNNER2', name: '점프러너 - 맵2' },
{ value: 'RUNNER3', name: '점프러너 - 맵3' },
{ value: 'RUNNER4', name: '점프러너 - 맵4' },
{ value: 'BATTLE_FFA', name: '컴뱃존 - FFA' },
{ value: 'BATTLE_TEAM', name: '컴뱃존 - TEAM' },
{ value: 'EVENT_CONTRIBUTION', name: '월드 이벤트 - 기여도' },
{ value: 'EVENT_CRAFT', name: '월드 이벤트 - 개인 제작' },
]
// export const logAction = [
// { value: "None", name: "ALL" },
// { value: "AIChatDeleteCharacter", name: "NPC 삭제" },
@@ -593,6 +984,7 @@ export const opMenuBannerStatus = [
export const logAction = [
{ value: "None", name: "전체" },
{ value: "AdminToolQuestTaskForceComplete", name: "AdminToolQuestTaskForceComplete" },
{ value: "AIChatDeleteCharacter", name: "AIChatDeleteCharacter" },
{ value: "AIChatDeleteUser", name: "AIChatDeleteUser" },
{ value: "AIChatGetCharacter", name: "AIChatGetCharacter" },
@@ -619,12 +1011,20 @@ export const logAction = [
{ value: "BeaconCreate", name: "BeaconCreate" },
{ value: "BeaconEdit", name: "BeaconEdit" },
{ value: "BeaconSell", name: "BeaconSell" },
{ value: "BrokerApiAdmin", name: "BrokerApiAdmin" },
{ value: "BeaconShopRegisterItem", name: "BeaconShopRegisterItem" },
{ value: "BeaconShopReturnItem", name: "BeaconShopReturnItem" },
{ value: "BeaconShopPurchaseItem", name: "BeaconShopPurchaseItem" },
{ value: "BeaconShopReceivePaymentForSales", name: "BeaconShopReceivePaymentForSales" },
{ value: "BeaconShopSearchItem", name: "BeaconShopSearchItem" },
{ value: "BeaconShopUpdateDailyCount", name: "BeaconShopUpdateDailyCount" },
{ value: "BeaconShopDeleteRecord", name: "BeaconShopDeleteRecord" },
{ value: "BeaconShopDeactiveItems", name: "BeaconShopDeactiveItems" },
// { value: "BrokerApiAdmin", name: "BrokerApiAdmin" },
{ value: "BrokerApiPlanetAuth", name: "BrokerApiPlanetAuth" },
{ value: "BrokerApiUserExchangeOrderCompleted", name: "BrokerApiUserExchangeOrderCompleted" },
{ value: "BrokerApiUserExchangeOrderCreated", name: "BrokerApiUserExchangeOrderCreated" },
{ value: "BrokerApiUserSystemMailSend", name: "BrokerApiUserSystemMailSend" },
{ value: "BrokerApiUserEchoSystemRequest", name: "BrokerApiUserEchoSystemRequest" },
// { value: "BrokerApiUserSystemMailSend", name: "BrokerApiUserSystemMailSend" },
// { value: "BrokerApiUserEchoSystemRequest", name: "BrokerApiUserEchoSystemRequest" },
{ value: "BrokerApiUserLogin", name: "BrokerApiUserLogin" },
{ value: "BuffAdd", name: "BuffAdd" },
{ value: "BuffDelete", name: "BuffDelete" },
@@ -669,9 +1069,12 @@ export const logAction = [
{ value: "CheatCommandSendMail", name: "CheatCommandSendMail" },
{ value: "CheatCommandShopProductInit", name: "CheatCommandShopProductInit" },
{ value: "CheatCommandShopProductRenewal", name: "CheatCommandShopProductRenewal" },
{ value: "CheatCommandBeaconShopItemTimeChange", name: "CheatCommandBeaconShopItemTimeChange" },
{ value: "CheatCommandDailyLimitInit", name: "CheatCommandDailyLimitInit" },
{ value: "ClaimReward", name: "ClaimReward" },
{ value: "ConvertCalium", name: "ConvertCalium" },
{ value: "ConvertExchangeCalium", name: "ConvertExchangeCalium" },
{ value: "ContentsMove", name: "ContentsMove" },
{ value: "CraftFinish", name: "CraftFinish" },
{ value: "CraftHelp", name: "CraftHelp" },
{ value: "CraftRecipeRegister", name: "CraftRecipeRegister" },
@@ -698,6 +1101,12 @@ export const logAction = [
{ value: "FriendAdd", name: "FriendAdd" },
{ value: "FriendDelete", name: "FriendDelete" },
{ value: "GainLandProfit", name: "GainLandProfit" },
{ value: "GameModeObjectInteraction", name: "GameModeObjectInteraction" },
{ value: "GameModeObjectStateUpdate", name: "GameModeObjectStateUpdate" },
{ value: "GameModeUserDead", name: "GameModeUserDead" },
{ value: "GameModeUserRespawn", name: "GameModeUserRespawn" },
{ value: "GameModePenalty", name: "GameModePenalty" },
{ value: "GameModeAddMatchCount", name: "GameModeAddMatchCount" },
{ value: "InviteParty", name: "InviteParty" },
{ value: "ItemBuy", name: "ItemBuy" },
{ value: "ItemDestroy", name: "ItemDestroy" },
@@ -705,6 +1114,8 @@ export const logAction = [
{ value: "ItemTattooChangeAttribute", name: "ItemTattooChangeAttribute" },
{ value: "ItemTattooLevelUp", name: "ItemTattooLevelUp" },
{ value: "ItemUse", name: "ItemUse" },
{ value: "ItemDestroyByUser", name: "ItemDestroyByUser" },
{ value: "ItemDestoryByExpiration", name: "ItemDestoryByExpiration" },
{ value: "JoinInstance", name: "JoinInstance" },
{ value: "JoinParty", name: "JoinParty" },
{ value: "JoinPartyInstance", name: "JoinPartyInstance" },
@@ -726,12 +1137,21 @@ export const logAction = [
{ value: "MailRead", name: "MailRead" },
{ value: "MailSend", name: "MailSend" },
{ value: "MailTaken", name: "MailTaken" },
{ value: "MatchReserve", name: "MatchReserve" },
{ value: "MatchCancel", name: "MatchCancel" },
{ value: "MatchResult", name: "MatchResult" },
{ value: "MatchRoomUserJoin", name: "MatchRoomUserJoin" },
{ value: "MatchRoomUserQuit", name: "MatchRoomUserQuit" },
{ value: "MatchRoomCreate", name: "MatchRoomCreate" },
{ value: "MatchRoomUpdate", name: "MatchRoomUpdate" },
{ value: "MatchRoomDestroy", name: "MatchRoomDestroy" },
{ value: "ModifyLandInfo", name: "ModifyLandInfo" },
{ value: "MoneyChange", name: "MoneyChange" },
{ value: "MoveToBeacon", name: "MoveToBeacon" },
{ value: "ProductGive", name: "ProductGive" },
{ value: "ProductOpenFailed", name: "ProductOpenFailed" },
{ value: "ProductOpenSuccess", name: "ProductOpenSuccess" },
{ value: "QuestMailSend", name: "QuestMailSend" },
{ value: "QuestMainAssign", name: "QuestMainAssign" },
{ value: "QuestMainAbort", name: "QuestMainAbort" },
{ value: "QuestMainAssignByDialogue", name: "QuestMainAssignByDialogue" },
{ value: "QuestMainAssignForce", name: "QuestMainAssignForce" },
@@ -739,8 +1159,13 @@ export const logAction = [
{ value: "QuestMainRepeatTimeInit", name: "QuestMainRepeatTimeInit" },
{ value: "QuestMainRepeatTimeRefresh", name: "QuestMainRepeatTimeRefresh" },
{ value: "QuestMainReward", name: "QuestMainReward" },
{ value: "QuestMailSend", name: "QuestMailSend" },
{ value: "QuestMainTask", name: "QuestMainTask" },
{ value: "QuestTaskUpdate", name: "QuestTaskUpdate" },
{ value: "RankingStart", name: "RankingStart" },
{ value: "RankingFinish", name: "RankingFinish" },
{ value: "RankingScoreUpdate", name: "RankingScoreUpdate" },
{ value: "RankingEventActionScore", name: "RankingEventActionScore" },
{ value: "RefuseFriendRequest", name: "RefuseFriendRequest" },
{ value: "RenameFriendFolder", name: "RenameFriendFolder" },
{ value: "RenameMyhome", name: "RenameMyhome" },
@@ -750,6 +1175,11 @@ export const logAction = [
{ value: "ReplySummonParty", name: "ReplySummonParty" },
{ value: "ReservationEnterToServer", name: "ReservationEnterToServer" },
{ value: "RewardProp", name: "RewardProp" },
{ value: "RunRaceFinishReward", name: "RunRaceFinishReward" },
{ value: "RunRaceRespawnReward", name: "RunRaceRespawnReward" },
{ value: "RunRaceUnFinishReward", name: "RunRaceUnFinishReward" },
{ value: "RunRaceCheckPointAbusing", name: "RunRaceCheckPointAbusing" },
{ value: "RunRaceResultSummary", name: "RunRaceResultSummary" },
{ value: "SaveMyhome", name: "SaveMyhome" },
{ value: "SeasonPassBuyCharged", name: "SeasonPassBuyCharged" },
{ value: "SeasonPassStartNew", name: "SeasonPassStartNew" },
@@ -799,6 +1229,7 @@ export const logAction = [
{ value: "UpdateGameOption", name: "UpdateGameOption" },
{ value: "UpdateLanguage", name: "UpdateLanguage" },
{ value: "UpdateUgcNpcLike", name: "UpdateUgcNpcLike" },
{ value: "UpdateGameModePlayerRegulation", name: "UpdateGameModePlayerRegulation" },
{ value: "UserBlock", name: "UserBlock" },
{ value: "UserBlockCancel", name: "UserBlockCancel" },
{ value: "UserCreate", name: "UserCreate" },
@@ -806,76 +1237,16 @@ export const logAction = [
{ value: "UserLogout", name: "UserLogout" },
{ value: "UserLogoutSnapShot", name: "UserLogoutSnapShot" },
{ value: "UserReport", name: "UserReport" },
{ value: "WorldEventActionScore", name: "WorldEventActionScore" },
{ value: "Warp", name: "Warp" },
{ value: "igmApiLogin", name: "igmApiLogin" }
];
export const logDomain = [
{ value: "BASE", name: "전체" },
{ value: "AuthLogInOut", name: "AuthLogInOut" },
{ value: "GameLogInOut", name: "GameLogInOut" },
{ value: "UserCreate", name: "UserCreate" },
{ value: "User", name: "User" },
{ value: "UserInitial", name: "UserInitial" },
{ value: "CharacterCreate", name: "CharacterCreate" },
{ value: "Character", name: "Character" },
{ value: "Item", name: "Item" },
{ value: "Currency", name: "Currency" },
{ value: "Mail", name: "Mail" },
{ value: "MailStoragePeriodExpired", name: "MailStoragePeriodExpired" },
{ value: "MailProfile", name: "MailProfile" },
{ value: "Stage", name: "Stage" },
{ value: "ClaimReward", name: "ClaimReward" },
{ value: "QuestMain", name: "QuestMain" },
{ value: "QuestUgq", name: "QuestUgq" },
{ value: "QuestMail", name: "QuestMail" },
{ value: "SocialAction", name: "SocialAction" },
{ value: "MyHome", name: "MyHome" },
{ value: "Taxi", name: "Taxi" },
{ value: "RewardProp", name: "RewardProp" },
{ value: "Party", name: "Party" },
{ value: "PartyMember", name: "PartyMember" },
{ value: "PartyVote", name: "PartyVote" },
{ value: "PartyInstance", name: "PartyInstance" },
{ value: "EscapePosition", name: "EscapePosition" },
{ value: "UserBlock", name: "UserBlock" },
{ value: "Friend", name: "Friend" },
{ value: "UserReport", name: "UserReport" },
{ value: "TaskReservation", name: "TaskReservation" },
{ value: "SeasonPass", name: "SeasonPass" },
{ value: "PackageLastOrderRecode", name: "PackageLastOrderRecode" },
{ value: "PackageRepeat", name: "PackageRepeat" },
{ value: "PackageState", name: "PackageState" },
{ value: "Craft", name: "Craft" },
{ value: "CraftHelp", name: "CraftHelp" },
{ value: "Cart", name: "Cart" },
{ value: "Buff", name: "Buff" },
{ value: "UgqApi", name: "UgqApi" },
{ value: "AIChat", name: "AIChat" },
{ value: "Chat", name: "Chat" },
{ value: "Shop", name: "Shop" },
{ value: "Calium", name: "Calium" },
{ value: "CaliumEchoSystem", name: "CaliumEchoSystem" },
{ value: "CaliumStorageFail", name: "CaliumStorageFail" },
{ value: "Position", name: "Position" },
{ value: "Address", name: "Address" },
{ value: "BeaconCreate", name: "BeaconCreate" },
{ value: "Beacon", name: "Beacon" },
{ value: "CustomDefineUi", name: "CustomDefineUi" },
{ value: "Farming", name: "Farming" },
{ value: "FarmingReward", name: "FarmingReward" },
{ value: "RenewalShopProducts", name: "RenewalShopProducts" },
{ value: "CheatRenewalShopProducts", name: "CheatRenewalShopProducts" },
{ value: "ChangeDanceEntityState", name: "ChangeDanceEntityState" },
{ value: "Land", name: "Land" },
{ value: "Building", name: "Building" },
{ value: "SwitchingProp", name: "SwitchingProp" },
{ value: "LandAuction", name: "LandAuction" },
{ value: "LandAuctionActivity", name: "LandAuctionActivity" },
{ value: "LandAuctionBid", name: "LandAuctionBid" },
{ value: "LandAuctionBidPriceRefund", name: "LandAuctionBidPriceRefund" },
{ value: "BrokerApi", name: "BrokerApi" },
{ value: "Rental", name: "Rental" },
{ value: "AIChat", name: "AIChat" },
{ value: "AuthLogInOut", name: "AuthLogInOut" },
{ value: "BuildingProfit", name: "BuildingProfit" },
{ value: "BattleObjectInteraction", name: "BattleObjectInteraction" },
{ value: "BattleObjectStateUpdate", name: "BattleObjectStateUpdate" },
@@ -884,5 +1255,87 @@ export const logDomain = [
{ value: "BattleRoomJoin", name: "BattleRoomJoin" },
{ value: "BattleDead", name: "BattleDead" },
{ value: "BattleRound", name: "BattleRound" },
{ value: "BattleSnapshot", name: "BattleSnapshot" }
{ value: "BattleSnapshot", name: "BattleSnapshot" },
{ value: "BeaconCreate", name: "BeaconCreate" },
{ value: "Beacon", name: "Beacon" },
{ value: "BeaconShop", name: "BeaconShop" },
{ value: "BeaconShopSoldRecord", name: "BeaconShopSoldRecord" },
{ value: "BeaconShopSoldPrice", name: "BeaconShopSoldPrice" },
{ value: "BrokerApi", name: "BrokerApi" },
{ value: "Buff", name: "Buff" },
{ value: "Building", name: "Building" },
{ value: "Calium", name: "Calium" },
{ value: "CaliumEchoSystem", name: "CaliumEchoSystem" },
{ value: "CaliumStorageFail", name: "CaliumStorageFail" },
{ value: "Character", name: "Character" },
{ value: "CharacterCreate", name: "CharacterCreate" },
{ value: "Chat", name: "Chat" },
{ value: "CheatRenewalShopProducts", name: "CheatRenewalShopProducts" },
{ value: "ChangeDanceEntityState", name: "ChangeDanceEntityState" },
{ value: "ClaimReward", name: "ClaimReward" },
{ value: "Craft", name: "Craft" },
{ value: "CraftHelp", name: "CraftHelp" },
{ value: "Cart", name: "Cart" },
{ value: "Currency", name: "Currency" },
{ value: "CustomDefineUi", name: "CustomDefineUi" },
{ value: "EventActionScore", name: "EventActionScore" },
{ value: "EscapePosition", name: "EscapePosition" },
{ value: "Friend", name: "Friend" },
{ value: "Farming", name: "Farming" },
{ value: "FarmingReward", name: "FarmingReward" },
{ value: "GameLogInOut", name: "GameLogInOut" },
{ value: "GameObjectInteraction", name: "GameObjectInteraction" },
{ value: "GameModePenalty", name: "GameModePenalty" },
{ value: "GameModePlayRegulation", name: "GameModePlayRegulation" },
{ value: "Item", name: "Item" },
{ value: "IgmApi", name: "IgmApi" },
{ value: "MyHome", name: "MyHome" },
{ value: "Land", name: "Land" },
{ value: "LandAuction", name: "LandAuction" },
{ value: "LandAuctionActivity", name: "LandAuctionActivity" },
{ value: "LandAuctionBid", name: "LandAuctionBid" },
{ value: "LandAuctionBidPriceRefund", name: "LandAuctionBidPriceRefund" },
{ value: "Mail", name: "Mail" },
{ value: "MailStoragePeriodExpired", name: "MailStoragePeriodExpired" },
{ value: "MailProfile", name: "MailProfile" },
{ value: "MatchUser", name: "MatchUser" },
{ value: "MatchServerUser", name: "MatchServerUser" },
{ value: "MatchRoom", name: "MatchRoom" },
{ value: "Party", name: "Party" },
{ value: "PartyMember", name: "PartyMember" },
{ value: "PartyVote", name: "PartyVote" },
{ value: "PartyInstance", name: "PartyInstance" },
{ value: "Position", name: "Position" },
{ value: "PackageLastOrderRecode", name: "PackageLastOrderRecode" },
{ value: "PackageRepeat", name: "PackageRepeat" },
{ value: "PackageState", name: "PackageState" },
{ value: "PlanetProviderAuth", name: "PlanetProviderAuth" },
{ value: "PlanetUserAuth", name: "PlanetUserAuth" },
{ value: "PlanetItemExchange", name: "PlanetItemExchange" },
{ value: "QuestMain", name: "QuestMain" },
{ value: "QuestUgq", name: "QuestUgq" },
{ value: "QuestMail", name: "QuestMail" },
{ value: "Ranking", name: "Ranking" },
{ value: "Ranker", name: "Ranker" },
{ value: "RenewalShopProducts", name: "RenewalShopProducts" },
{ value: "Rental", name: "Rental" },
{ value: "RewardProp", name: "RewardProp" },
{ value: "RunRaceFinishReward", name: "RunRaceFinishReward" },
{ value: "RunRaceRespawnReward", name: "RunRaceRespawnReward" },
{ value: "RunRaceUnFinishReward", name: "RunRaceUnFinishReward" },
{ value: "RunRaceCheckPointAbusing", name: "RunRaceCheckPointAbusing" },
{ value: "RunRaceRewardSummary", name: "RunRaceRewardSummary" },
{ value: "Stage", name: "Stage" },
{ value: "SocialAction", name: "SocialAction" },
{ value: "SeasonPass", name: "SeasonPass" },
{ value: "Shop", name: "Shop" },
{ value: "SwitchingProp", name: "SwitchingProp" },
{ value: "Taxi", name: "Taxi" },
{ value: "TaskReservation", name: "TaskReservation" },
{ value: "UgqApi", name: "UgqApi" },
{ value: "User", name: "User" },
{ value: "UserCreate", name: "UserCreate" },
{ value: "UserInitial", name: "UserInitial" },
{ value: "UserBlock", name: "UserBlock" },
{ value: "UserReport", name: "UserReport" },
];

View File

@@ -0,0 +1,96 @@
{
"initialSearchParams": {
"searchType": "GUID",
"searchData": "",
"logAction": "None",
"logDomain": "BASE",
"tran_id": "",
"startDate": "",
"endDate": "",
"orderBy": "DESC",
"pageSize": 500,
"currentPage": 1
},
"searchFields": [
{
"type": "select",
"id": "searchType",
"optionsRef": "userSearchType2",
"col": 1,
"required": true
},
{
"type": "text",
"id": "searchData",
"placeholder": "대상 입력",
"width": "300px",
"col": 1,
"required": true
},
{
"type": "period",
"startDateId": "startDate",
"endDateId": "endDate",
"label": "조회 일자",
"col": 1
},
{
"type": "text",
"id": "searchContent",
"label": "우편 내용",
"placeholder": "우편 내용(공백으로 구분)",
"width": "300px",
"col": 1
},
{
"type": "select",
"id": "sendType",
"label": "발송 방식",
"optionsRef": "mailSendType",
"col": 2
},
{
"type": "select",
"id": "status",
"label": "발송 상태",
"optionsRef": "mailSendStatus",
"col": 2
},
{
"type": "select",
"id": "mailType",
"label": "우편 타입",
"optionsRef": "mailType",
"col": 2
},
{
"type": "select",
"id": "receiveType",
"label": "수신 대상",
"optionsRef": "mailReceiveType",
"col": 2
}
],
"apiInfo": {
"functionName": "MailView",
"loadOnMount": true,
"paramsMapping": [
"searchTitle",
"searchContent",
"sendType",
"status",
"mailType",
"receiveType",
{"param": "startDate", "transform": "toISOString"},
{"param": "endDate", "transform": "toISOString"},
"orderBy",
"pageSize",
"currentPage"
],
"pageField": "currentPage",
"pageSizeField": "pageSize",
"orderField": "orderBy"
}
}

View File

@@ -1,7 +1,6 @@
{
"initialSearchParams": {
"searchTitle": "",
"searchContent": "",
"searchData": "",
"status": "ALL",
"startDate": "",
"endDate": "",
@@ -13,51 +12,37 @@
"searchFields": [
{
"type": "text",
"id": "searchTitle",
"label": "우편 제목",
"id": "searchData",
"label": "제목",
"placeholder": "제목 입력",
"width": "300px",
"col": 1
},
{
"type": "period",
"startDateId": "startDate",
"endDateId": "endDate",
"label": "조회 일자",
"col": 1
},
{
"type": "text",
"id": "searchContent",
"label": "우편 내용",
"placeholder": "우편 내용(공백으로 구분)",
"width": "300px",
"col": 1
},
{
"type": "select",
"id": "status",
"label": "이벤트 상태",
"optionsRef": "eventStatus",
"col": 2
"label": "상태",
"optionsRef": "opMenuBannerStatus",
"col": 1
},
{
"type": "period",
"startDateId": "startDate",
"endDateId": "endDate",
"label": "기간",
"col": 1
}
],
"apiInfo": {
"functionName": "EventView",
"endpointName": "EventView",
"loadOnMount": true,
"paramsMapping": [
"searchTitle",
"searchContent",
"status",
"paramTransforms": [
{"param": "startDate", "transform": "toISOString"},
{"param": "endDate", "transform": "toISOString"},
"orderBy",
"pageSize",
"currentPage"
{"param": "endDate", "transform": "toISOString"}
],
"pageField": "currentPage",
"pageSizeField": "pageSize",
"pageField": "page_no",
"pageSizeField": "page_size",
"orderField": "orderBy"
}
}

View File

@@ -0,0 +1,119 @@
{
"id": "eventTable",
"selection": {
"type": "single",
"idField": "id"
},
"header": {
"countType": "total",
"orderType": "desc",
"pageType": "default",
"buttons": [
{
"id": "delete",
"text": "선택 삭제",
"theme": "line",
"disableWhen": "noSelection",
"requiredAuth": "worldEventDelete",
"action": "delete"
},
{
"id": "register",
"text": "통합 이벤트 등록",
"theme": "primary",
"requiredAuth": "worldEventUpdate",
"action": "regist"
}
]
},
"columns": [
{
"id": "checkbox",
"type": "checkbox",
"width": "40px",
"title": ""
},
{
"id": "row_num",
"type": "text",
"width": "70px",
"title": "번호"
},
{
"id": "status",
"type": "status",
"width": "120px",
"title": "상태",
"option_name": "opCommonStatus"
},
{
"id": "title",
"type": "text",
"title": "제목",
"width": "150px"
},
{
"id": "personal_event_action_id",
"type": "text",
"width": "150px",
"title": "개인제작 이벤트 모드"
},
{
"id": "global_event_action_id",
"type": "text",
"width": "150px",
"title": "기여도 이벤트 모드"
},
{
"id": "max_point",
"type": "text",
"width": "150px",
"title": "기여도 목표점수"
},
{
"id": "start_dt",
"type": "date",
"width": "220px",
"title": "시작일(KST)",
"format": {
"type": "function",
"name": "convertKTC"
}
},
{
"id": "end_dt",
"type": "date",
"width": "220px",
"title": "종료일(KST)",
"format": {
"type": "function",
"name": "convertKTC"
}
},
{
"id": "detail",
"type": "button",
"width": "120px",
"title": "상세보기",
"text": "상세보기",
"action": {
"type": "modal",
"target": "detailModal",
"dataParam": {
"id": "id"
}
}
},
{
"id": "history",
"type": "button",
"width": "120px",
"title": "히스토리",
"text": "히스토리"
}
],
"sort": {
"defaultColumn": "row_num",
"defaultDirection": "desc"
}
}

View File

@@ -0,0 +1,59 @@
{
"initialSearchParams": {
"searchType": "ID",
"searchData": "",
"historyType": "",
"startDate": "today",
"endDate": "today",
"orderBy": "DESC",
"pageSize": 50,
"currentPage": 1
},
"searchFields": [
{
"type": "select",
"id": "searchType",
"label": "대상",
"optionsRef": "adminUserType",
"col": 1
},
{
"type": "text",
"id": "searchData",
"placeholder": "대상 입력",
"width": "300px",
"col": 1
},
{
"type": "select",
"id": "historyType",
"label": "이력종류",
"optionsRef": "opHistoryType",
"col": 1
},
{
"type": "period",
"startDateId": "startDate",
"endDateId": "endDate",
"label": "기간",
"col": 2,
"width": "500px",
"format": "YYYY-MM-DD HH:mm:ss",
"showTime": true
}
],
"apiInfo": {
"endpointName": "LogViewList",
"loadOnMount": true,
"paramTransforms": [
{"param": "startDate", "transform": "toISOString"},
{"param": "endDate", "transform": "toISOString"}
],
"pageField": "page_no",
"pageSizeField": "page_size",
"orderField": "orderBy"
},
"paginationType": "front"
}

View File

@@ -0,0 +1,69 @@
{
"id": "historyTable",
"selection": {
"type": "single",
"idField": "id"
},
"header": {
"countType": "total",
"orderType": "desc",
"pageType": "default",
"buttons": []
},
"columns": [
{
"id": "logTime",
"type": "date",
"width": "200px",
"title": "일시(KST)",
"format": {
"type": "function",
"name": "convertKTC"
}
},
{
"id": "category",
"type": "option",
"width": "100px",
"title": "로그종류",
"option_name": "opLogCategory"
},
{
"id": "action",
"type": "option",
"width": "150px",
"title": "로그액션",
"option_name": "opLogAction"
},
{
"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",
"width": "120px",
"title": "상세보기",
"text": "상세보기"
}
],
"sort": {
"defaultColumn": "timestamp",
"defaultDirection": "desc"
}
}

View File

@@ -0,0 +1,39 @@
{
"initialSearchParams": {
"searchType": "GUID",
"searchData": "",
"orderBy": "DESC",
"pageSize": 50,
"currentPage": 1,
"lastEvaluatedKey": null
},
"searchFields": [
{
"type": "select",
"id": "searchType",
"label": "대상",
"optionsRef": "userType",
"col": 1,
"required": true
},
{
"type": "text",
"id": "searchData",
"placeholder": "대상 입력",
"width": "300px",
"col": 1,
"required": true
}
],
"apiInfo": {
"endpointName": "ItemList",
"loadOnMount": false,
"pageField": "page_no",
"pageSizeField": "page_size",
"orderField": "orderBy",
"lastPageKeyField": "pageKey"
},
"paginationType": "dynamodb",
"initSearch": false
}

View File

@@ -0,0 +1,90 @@
{
"id": "itemTable",
"selection": {
"type": "single",
"idField": "id"
},
"header": {
"countType": "total",
"orderType": "desc",
"pageType": "default",
"buttons": [
{
"id": "delete",
"text": "선택 삭제",
"theme": "line",
"disableWhen": "noSelection",
"requiredAuth": "itemDelete",
"action": "delete"
},
{
"id": "restore",
"text": "아이템 복구",
"theme": "line",
"disableWhen": "disable",
"requiredAuth": "itemDelete",
"action": "restore"
}
]
},
"columns": [
{
"id": "checkbox",
"type": "checkbox",
"width": "40px",
"title": ""
},
{
"id": "item_name",
"type": "text",
"width": "20%",
"title": "아이템명"
},
{
"id": "item_id",
"type": "text",
"width": "20%",
"title": "아이템 ID"
},
{
"id": "count",
"type": "text",
"width": "80px",
"title": "수량"
},
{
"id": "item_type",
"type": "option",
"width": "120px",
"title": "아이템 타입",
"option_name": "opItemType"
},
{
"id": "equip_type",
"type": "option",
"width": "80px",
"title": "장착",
"option_name": "opEquipType"
},
{
"id": "equiped_pos",
"type": "text",
"width": "80px",
"title": "슬롯"
},
{
"id": "create_dt",
"type": "date",
"width": "100px",
"title": "생성날짜(KST)",
"format": {
"type": "function",
"name": "convertKTC"
}
}
],
"sort": {
"defaultColumn": "row_num",
"defaultDirection": "desc"
}
}

View File

@@ -35,19 +35,14 @@
],
"apiInfo": {
"functionName": "MenuBannerView",
"endpointName": "MenuBannerView",
"loadOnMount": true,
"paramsMapping": [
"searchData",
"status",
"paramTransforms": [
{"param": "startDate", "transform": "toISOString"},
{"param": "endDate", "transform": "toISOString"},
"orderBy",
"pageSize",
"currentPage"
{"param": "endDate", "transform": "toISOString"}
],
"pageField": "currentPage",
"pageSizeField": "pageSize",
"pageField": "page_no",
"pageSizeField": "page_size",
"orderField": "orderBy"
}
}

View File

@@ -14,14 +14,14 @@
"text": "선택 삭제",
"theme": "line",
"disableWhen": "noSelection",
"requiredAuth": "battleEventDelete",
"requiredAuth": "menuBannerDelete",
"action": "delete"
},
{
"id": "register",
"text": "이미지 등록",
"theme": "primary",
"requiredAuth": "battleEventUpdate",
"requiredAuth": "menuBannerUpdate",
"action": "navigate",
"navigateTo": "/servicemanage/menubanner/menubannerregist"
}
@@ -40,6 +40,12 @@
"width": "70px",
"title": "번호"
},
{
"id": "order_id",
"type": "text",
"width": "70px",
"title": "순서"
},
{
"id": "status",
"type": "status",
@@ -94,10 +100,11 @@
}
},
{
"id": "update_by",
"type": "text",
"width": "150px",
"title": "히스토리"
"id": "history",
"type": "button",
"width": "120px",
"title": "히스토리",
"text": "히스토리"
}
],
"sort": {

View File

@@ -0,0 +1,49 @@
{
"initialSearchParams": {
"search_type": "ID",
"search_data": "",
"small_type": "ALL",
"recipe_type": "ALL",
"orderBy": "DESC",
"pageSize": 50,
"currentPage": 1
},
"searchFields": [
{
"type": "select",
"id": "search_type",
"optionsRef": "itemSearchType",
"col": 1
},
{
"type": "text",
"id": "search_data",
"placeholder": "아이템 입력",
"width": "300px",
"col": 1
},
{
"type": "select",
"id": "small_type",
"label": "제작 분류",
"optionsRef": "opPropSmallType",
"col": 1
},
{
"type": "select",
"id": "recipe_type",
"label": "레시피 필요 여부",
"optionsRef": "opPropRecipeType",
"col": 1
}
],
"apiInfo": {
"endpointName": "getCraftingDictionaryList",
"loadOnMount": true,
"pageField": "page_no",
"pageSizeField": "page_size",
"orderField": "orderBy"
}
}

View File

@@ -0,0 +1,49 @@
{
"initialSearchParams": {
"search_type": "ID",
"search_data": "",
"contents_type": "ALL",
"access_type": "ALL",
"orderBy": "DESC",
"pageSize": 50,
"currentPage": 1
},
"searchFields": [
{
"type": "select",
"id": "search_type",
"optionsRef": "instanceSearchType",
"col": 1
},
{
"type": "text",
"id": "search_data",
"placeholder": "인스턴스 입력",
"width": "300px",
"col": 1
},
{
"type": "select",
"id": "contents_type",
"label": "컨텐츠 타입",
"optionsRef": "opInstanceContentsType",
"col": 1
},
{
"type": "select",
"id": "access_type",
"label": "입장 방식",
"optionsRef": "opInstanceAccessType",
"col": 1
}
],
"apiInfo": {
"endpointName": "getInstanceDictionaryList",
"loadOnMount": true,
"pageField": "page_no",
"pageSizeField": "page_size",
"orderField": "orderBy"
}
}

View File

@@ -0,0 +1,48 @@
{
"initialSearchParams": {
"searchData": "",
"status": "ALL",
"startDate": "",
"endDate": "",
"orderBy": "DESC",
"pageSize": 50,
"currentPage": 1
},
"searchFields": [
{
"type": "text",
"id": "searchData",
"label": "제목",
"placeholder": "제목 입력",
"width": "300px",
"col": 1
},
{
"type": "select",
"id": "status",
"label": "상태",
"optionsRef": "opMenuBannerStatus",
"col": 1
},
{
"type": "period",
"startDateId": "startDate",
"endDateId": "endDate",
"label": "기간",
"col": 1
}
],
"apiInfo": {
"endpointName": "RankingScheduleView",
"loadOnMount": true,
"paramTransforms": [
{"param": "startDate", "transform": "toISOString"},
{"param": "endDate", "transform": "toISOString"}
],
"pageField": "page_no",
"pageSizeField": "page_size",
"orderField": "orderBy"
}
}

View File

@@ -0,0 +1,164 @@
{
"id": "rankingTable",
"selection": {
"type": "single",
"idField": "id"
},
"header": {
"countType": "total",
"orderType": "desc",
"pageType": "default",
"buttons": [
{
"id": "update",
"text": "강제 새로고침",
"theme": "line",
"disableWhen": "noSelection",
"requiredAuth": "rankingUpdate",
"action": "update"
},
{
"id": "init",
"text": "강제 초기화",
"theme": "line",
"disableWhen": "noSelection",
"requiredAuth": "rankingUpdate",
"action": "rankingInit"
},
{
"id": "snapshot",
"text": "강제 스냅샷",
"theme": "line",
"disableWhen": "noSelection",
"requiredAuth": "rankingUpdate",
"action": "rankingSnapshot"
},
{
"id": "delete",
"text": "선택 삭제",
"theme": "line",
"disableWhen": "noSelection",
"requiredAuth": "rankingDelete",
"action": "delete"
},
{
"id": "register",
"text": "랭킹 스케줄 등록",
"theme": "primary",
"requiredAuth": "rankingUpdate",
"action": "regist"
}
]
},
"columns": [
{
"id": "checkbox",
"type": "checkbox",
"width": "40px",
"title": ""
},
{
"id": "row_num",
"type": "text",
"width": "70px",
"title": "번호"
},
{
"id": "status",
"type": "status",
"width": "100px",
"title": "상태",
"option_name": "opCommonStatus"
},
{
"id": "title",
"type": "text",
"title": "제목"
},
{
"id": "meta_id",
"type": "text",
"title": "랭킹모드",
"width": "100px"
},
{
"id": "event_action_id",
"type": "text",
"title": "이벤트 액션 그룹",
"width": "150px"
},
{
"id": "start_dt",
"type": "date",
"width": "200px",
"title": "시작일(KST)",
"format": {
"type": "function",
"name": "convertKTC"
}
},
{
"id": "end_dt",
"type": "date",
"width": "200px",
"title": "종료일(KST)",
"format": {
"type": "function",
"name": "convertKTC"
}
},
{
"id": "base_dt",
"type": "date",
"width": "200px",
"title": "기준일(KST)",
"format": {
"type": "function",
"name": "convertKTC"
}
},
{
"id": "refresh_interval",
"type": "text",
"width": "120px",
"title": "새로고침 주기"
},
{
"id": "initialization_interval",
"type": "text",
"width": "120px",
"title": "초기화 주기"
},
{
"id": "snapshot_interval",
"type": "text",
"width": "120px",
"title": "스냅샷 주기"
},
{
"id": "detail",
"type": "button",
"width": "120px",
"title": "상세보기",
"text": "상세보기",
"action": {
"type": "modal",
"target": "detailModal",
"dataParam": {
"id": "id"
}
}
},
{
"id": "history",
"type": "button",
"width": "120px",
"title": "히스토리",
"text": "히스토리"
}
],
"sort": {
"defaultColumn": "row_num",
"defaultDirection": "desc"
}
}

View File

@@ -0,0 +1,63 @@
{
"initialSearchParams": {
"searchTitle": "",
"searchContent": "",
"status": "ALL",
"startDate": "",
"endDate": "",
"orderBy": "DESC",
"pageSize": 50,
"currentPage": 1
},
"searchFields": [
{
"type": "text",
"id": "searchTitle",
"label": "우편 제목",
"placeholder": "제목 입력",
"width": "300px",
"col": 1
},
{
"type": "period",
"startDateId": "startDate",
"endDateId": "endDate",
"label": "조회 일자",
"col": 1
},
{
"type": "text",
"id": "searchContent",
"label": "우편 내용",
"placeholder": "우편 내용(공백으로 구분)",
"width": "300px",
"col": 1
},
{
"type": "select",
"id": "status",
"label": "이벤트 상태",
"optionsRef": "eventStatus",
"col": 2
}
],
"apiInfo": {
"functionName": "RewardEventView",
"loadOnMount": true,
"paramsMapping": [
"searchTitle",
"searchContent",
"status",
{"param": "startDate", "transform": "toISOString"},
{"param": "endDate", "transform": "toISOString"},
"orderBy",
"pageSize",
"currentPage"
],
"pageField": "currentPage",
"pageSizeField": "pageSize",
"orderField": "orderBy"
}
}

View File

@@ -16,7 +16,7 @@
"type": "select",
"id": "searchType",
"label": "대상",
"optionsRef": "eventStatus",
"optionsRef": "userType",
"col": 1
},
{

View File

@@ -11,7 +11,7 @@
"buttons": [
{
"id": "delete",
"text": "선택 삭제",
"text": "선택 해지",
"theme": "line",
"disableWhen": "noSelection",
"requiredAuth": "blackListDelete",
@@ -73,12 +73,6 @@
"title": "제재 사유",
"option_name": "blockSanctions"
},
{
"id": "create_by",
"type": "text",
"width": "150px",
"title": "등록자"
},
{
"id": "detail",
"type": "button",
@@ -92,6 +86,13 @@
"id": "id"
}
}
},
{
"id": "history",
"type": "button",
"width": "120px",
"title": "히스토리",
"text": "히스토리"
}
],
"sort": {

View File

@@ -52,6 +52,18 @@ export const authType = {
menuBannerRead: 50,
menuBannerUpdate: 51,
menuBannerDelete: 52,
itemDictionaryRead: 53,
rankManagerRead: 54,
rankManagerUpdate: 55,
rankingRead: 56,
rankingUpdate: 57,
rankingDelete: 58,
worldEventRead: 59,
worldEventUpdate: 60,
worldEventDelete: 61,
craftingDictionaryRead: 62,
rankInfoRead: 63,
instanceDictionaryRead: 64,
levelReader: 999,
@@ -75,20 +87,6 @@ export const adminAuthLevel = {
DEVELOPER: "Developer",
}
export const TabList = [
{ title: '기본정보' },
{ title: '아바타' },
{ title: '의상' },
{ title: '도구' },
{ title: '인벤토리' },
{ title: '우편' },
{ title: '마이홈' },
{ title: '친구목록' },
{ title: '타투' },
{ title: '퀘스트' },
// { title: '클레임' },
];
export const ivenTabType = {
CLOTH: "cloth",
PROP: "prop",
@@ -119,6 +117,13 @@ export const commonStatus = {
delete: "DELETE",
reject: "REJECT",
complete: "COMPLETE",
cancel: "CANCEL"
}
export const customStatus = {
inprogress: "INPROGRESS",
expiration: "EXPIRATION",
}
export const ViewTitleCountType = {
@@ -151,4 +156,10 @@ export const currencyCodeTypes = {
sapphire: "19010002",
ruby: "19010005",
calium: "19010003"
}
}
export const languageNames = {
'Ko': '한국어',
'En': '영어',
'Ja': '일본어',
};

View File

@@ -1,198 +0,0 @@
import { styled } from 'styled-components';
import { useState } from 'react';
import Button from '../../components/common/button/Button';
import DatePicker, { registerLocale } from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import { getMonth, getYear } from 'date-fns';
import range from 'lodash/range';
import { TextInput, SelectInput, DatePickerWrapper, InputLabel, BtnWrapper } from '../../styles/Components';
const GoodsLogSearchBar = () => {
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const [selectData, setSelectData] = useState('default');
const years = range(1990, getYear(new Date()) + 1, 1);
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
const handleChange = e => {
setSelectData(e.target.value);
};
return (
<>
<SearchbarStyle2>
<SearchRow>
<SearchItem>
<InputLabel>조회 기간</InputLabel>
<DatePickerWrapper>
<InputGroup>
<DatePicker
selected={startDate}
onChange={date => setStartDate(date)}
className="datepicker"
placeholderText="검색기간 선택"
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
locale="ko"
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
</InputGroup>
<span>~</span>
<InputGroup>
<DatePicker
selected={endDate}
onChange={date => setEndDate(date)}
className="datepicker"
placeholderText="검색기간 선택"
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
minDate = {startDate}
locale="ko"
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
</InputGroup>
</DatePickerWrapper>
</SearchItem>
</SearchRow>
<SearchRow>
<SearchItem>
<InputLabel>조회 대상</InputLabel>
<TextInput type="text" placeholder="조회 대상 유저의 GUID를 입력하세요." width="600px" />
</SearchItem>
<BtnWrapper $gap="8px">
<Button theme="reset" />
<Button theme="gray" text="검색" />
</BtnWrapper>
</SearchRow>
</SearchbarStyle2>
</>
);
};
export default GoodsLogSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchbarStyle2 = styled(SearchbarStyle)`
flex-flow: column;
gap: 20px;
`;
const SearchRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px 0;
`;
const InputGroup = styled.div`
display: flex;
align-items: center;
gap: 5px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,168 @@
import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import {
CircularProgressWrapper,
FormWrapper,
TableStyle,
TableWrapper,
} from '../../styles/Components';
import { amountDeltaType, CurrencyType } from '../../assets/data';
import { useTranslation } from 'react-i18next';
import { numberFormatter } from '../../utils';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { CurrencyItemLogSearchBar, useCurrencyItemLogSearch } from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import Pagination from '../common/Pagination/Pagination';
import {
INITIAL_PAGE_LIMIT,
STORAGE_BUSINESS_LOG_SEARCH,
STORAGE_GAME_LOG_CURRENCY_SEARCH,
} from '../../assets/data/adminConstants';
import ExcelExportButton from '../common/button/ExcelExportButton';
import CircularProgress from '../common/CircularProgress';
import { GameCurrencyItemLogExport } from '../../apis';
import { AnimatedPageWrapper } from '../common/Layout';
const CurrencyItemLogContent = ({ active }) => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const [downloadState, setDownloadState] = useState({
loading: false,
progress: 0
});
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
handleOrderByChange,
handlePageSizeChange,
updateSearchParams
} = useCurrencyItemLogSearch(token, 500);
useEffect(() => {
if(active) {
// 세션 스토리지에서 복사된 메일 데이터 가져오기
const paramsData = sessionStorage.getItem(STORAGE_GAME_LOG_CURRENCY_SEARCH);
if (paramsData) {
const searchData = JSON.parse(paramsData);
handleSearch({
start_dt: new Date(searchData.start_dt),
end_dt: new Date(searchData.end_dt),
search_data: searchData.guid
});
// 사용 후 세션 스토리지 데이터 삭제
sessionStorage.removeItem(STORAGE_GAME_LOG_CURRENCY_SEARCH);
}
}
}, [active]);
const tableHeaders = useMemo(() => {
return [
// { id: 'logDay', label: '일자', width: '120px' },
{ id: 'logTime', label: '일시', width: '150px' },
{ id: 'accountId', label: 'account ID', width: '80px' },
{ id: 'userGuid', label: 'GUID', width: '200px' },
{ id: 'userNickname', label: '아바타명', width: '150px' },
{ id: 'tranId', label: '트랜잭션 ID', width: '200px' },
{ id: 'action', label: '액션', width: '150px' },
{ id: 'currencyType', label: '재화종류', width: '120px' },
{ id: 'amountDeltaType', label: '증감유형', width: '120px' },
{ id: 'deltaAmount', label: '수량', width: '80px' },
// { id: 'deltaAmount', label: '수량원본', width: '120px' },
{ id: 'currencyAmount', label: '잔량', width: '80px' },
{ id: 'itemId', label: '아이템ID', width: '150px' },
];
}, []);
if(!active) return null;
return (
<AnimatedPageWrapper>
<FormWrapper>
<CurrencyItemLogSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo orderType="asc" pageType="B" total={dataList?.total} total_all={dataList?.total_all}>
<ExcelExportButton
functionName="GameCurrencyItemLogExport"
params={searchParams}
fileName={t('FILE_GAME_LOG_CURRENCY_ITEM')}
onLoadingChange={setDownloadState}
dataSize={dataList?.total_all}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => (
<th key={header.id} width={header.width}>{header.label}</th>
))}
</tr>
</thead>
<tbody>
{dataList?.currency_item_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logTime}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
<td>{item.tranId}</td>
<td>{item.action}</td>
<td>{CurrencyType.find(data => data.value === item.currencyType)?.name}</td>
<td>{amountDeltaType.find(data => data.value === item.amountDeltaType)?.name}</td>
<td>{numberFormatter.formatCurrency(item.deltaAmount)}</td>
<td>{numberFormatter.formatCurrency(item.currencyAmount)}</td>
<td>{item.itemIDs}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{dataList?.currency_item_list &&
<Pagination
postsPerPage={searchParams.page_size}
totalPosts={dataList?.total_all}
setCurrentPage={handlePageChange}
currentPage={searchParams.page_no}
pageLimit={INITIAL_PAGE_LIMIT}
/>
}
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default CurrencyItemLogContent;

View File

@@ -0,0 +1,166 @@
import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import {
CircularProgressWrapper,
FormWrapper,
TableStyle,
TableWrapper,
} from '../../styles/Components';
import { amountDeltaType, CurrencyType } from '../../assets/data';
import { useTranslation } from 'react-i18next';
import { numberFormatter } from '../../utils';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { CurrencyLogSearchBar, useCurrencyLogSearch } from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import Pagination from '../common/Pagination/Pagination';
import {
INITIAL_PAGE_LIMIT,
STORAGE_BUSINESS_LOG_SEARCH,
STORAGE_GAME_LOG_CURRENCY_SEARCH,
} from '../../assets/data/adminConstants';
import ExcelExportButton from '../common/button/ExcelExportButton';
import CircularProgress from '../common/CircularProgress';
import { AnimatedPageWrapper } from '../common/Layout';
const CurrencyLogContent = ({ active }) => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const [downloadState, setDownloadState] = useState({
loading: false,
progress: 0
});
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
handleOrderByChange,
handlePageSizeChange,
updateSearchParams
} = useCurrencyLogSearch(token, 500);
useEffect(() => {
if(active) {
// 세션 스토리지에서 복사된 메일 데이터 가져오기
const paramsData = sessionStorage.getItem(STORAGE_GAME_LOG_CURRENCY_SEARCH);
if (paramsData) {
const searchData = JSON.parse(paramsData);
handleSearch({
start_dt: new Date(searchData.start_dt),
end_dt: new Date(searchData.end_dt),
search_data: searchData.guid
});
// 사용 후 세션 스토리지 데이터 삭제
sessionStorage.removeItem(STORAGE_GAME_LOG_CURRENCY_SEARCH);
}
}
}, [active]);
const tableHeaders = useMemo(() => {
return [
// { id: 'logDay', label: '일자', width: '120px' },
{ id: 'logTime', label: '일시', width: '120px' },
{ id: 'accountId', label: 'account ID', width: '80px' },
{ id: 'userGuid', label: 'GUID', width: '200px' },
{ id: 'userNickname', label: '아바타명', width: '150px' },
{ id: 'tranId', label: '트랜잭션 ID', width: '200px' },
{ id: 'action', label: '액션', width: '150px' },
{ id: 'currencyType', label: '재화종류', width: '120px' },
{ id: 'amountDeltaType', label: '증감유형', width: '120px' },
{ id: 'deltaAmount', label: '수량', width: '120px' },
// { id: 'deltaAmount', label: '수량원본', width: '120px' },
{ id: 'currencyAmount', label: '잔량', width: '120px' },
];
}, []);
if(!active) return null;
return (
<AnimatedPageWrapper>
<FormWrapper>
<CurrencyLogSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo orderType="asc" pageType="B" total={dataList?.total} total_all={dataList?.total_all}>
<ExcelExportButton
functionName="GameCurrencyDetailLogExport"
params={searchParams}
fileName={t('FILE_GAME_LOG_CURRENCY')}
onLoadingChange={setDownloadState}
dataSize={dataList?.total_all}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => (
<th key={header.id} width={header.width}>{header.label}</th>
))}
</tr>
</thead>
<tbody>
{dataList?.currency_detail_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logTime}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
<td>{item.tranId}</td>
<td>{item.action}</td>
<td>{CurrencyType.find(data => data.value === item.currencyType)?.name}</td>
<td>{amountDeltaType.find(data => data.value === item.amountDeltaType)?.name}</td>
<td>{numberFormatter.formatCurrency(item.deltaAmount)}</td>
{/*<td>{item.deltaAmount}</td>*/}
<td>{numberFormatter.formatCurrency(item.currencyAmount)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{dataList?.currency_detail_list &&
<Pagination
postsPerPage={searchParams.page_size}
totalPosts={dataList?.total_all}
setCurrentPage={handlePageChange}
currentPage={searchParams.page_no}
pageLimit={INITIAL_PAGE_LIMIT}
/>
}
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default CurrencyLogContent;

View File

@@ -0,0 +1,171 @@
import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import {
CircularProgressWrapper,
FormWrapper,
TableStyle,
TableWrapper,
} from '../../styles/Components';
import { amountDeltaType, CurrencyType } from '../../assets/data';
import { useTranslation } from 'react-i18next';
import { numberFormatter } from '../../utils';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { ItemLogSearchBar, useItemLogSearch } from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import Pagination from '../common/Pagination/Pagination';
import {
INITIAL_PAGE_LIMIT,
STORAGE_BUSINESS_LOG_SEARCH,
STORAGE_GAME_LOG_CURRENCY_SEARCH, STORAGE_GAME_LOG_ITEM_SEARCH,
} from '../../assets/data/adminConstants';
import ExcelExportButton from '../common/button/ExcelExportButton';
import CircularProgress from '../common/CircularProgress';
import { countDeltaType, itemTypeLarge } from '../../assets/data/options';
import { AnimatedPageWrapper } from '../common/Layout';
const CurrencyLogContent = ({ active }) => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const [downloadState, setDownloadState] = useState({
loading: false,
progress: 0
});
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
handleOrderByChange,
handlePageSizeChange,
updateSearchParams
} = useItemLogSearch(token, 500);
useEffect(() => {
if(active) {
// 세션 스토리지에서 복사된 메일 데이터 가져오기
const paramsData = sessionStorage.getItem(STORAGE_GAME_LOG_ITEM_SEARCH);
if (paramsData) {
const searchData = JSON.parse(paramsData);
handleSearch({
start_dt: new Date(searchData.start_dt),
end_dt: new Date(searchData.end_dt),
search_data: searchData.guid
});
// 사용 후 세션 스토리지 데이터 삭제
sessionStorage.removeItem(STORAGE_GAME_LOG_ITEM_SEARCH);
}
}
}, [active]);
const tableHeaders = useMemo(() => {
return [
// { id: 'logDay', label: '일자', width: '120px' },
{ id: 'logTime', label: '일시', width: '150px' },
{ id: 'accountId', label: 'account ID', width: '80px' },
{ id: 'userGuid', label: 'GUID', width: '200px' },
{ id: 'userNickname', label: '아바타명', width: '150px' },
{ id: 'tranId', label: '트랜잭션 ID', width: '200px' },
{ id: 'action', label: '액션', width: '120px' },
{ id: 'itemId', label: '아이템ID', width: '80px' },
{ id: 'itemName', label: '아이템명', width: '150px' },
{ id: 'itemTypeLarge', label: 'LargeType', width: '100px' },
{ id: 'itemTypeSmall', label: 'SmallType', width: '100px' },
{ id: 'countDeltaType', label: '증감유형', width: '80px' },
{ id: 'deltaCount', label: '수량', width: '80px' },
{ id: 'stackCount', label: '총량', width: '80px' },
];
}, []);
if(!active) return null;
return (
<AnimatedPageWrapper>
<FormWrapper>
<ItemLogSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo orderType="asc" pageType="B" total={dataList?.total} total_all={dataList?.total_all}>
<ExcelExportButton
functionName="GameItemDetailLogExport"
params={searchParams}
fileName={t('FILE_GAME_LOG_ITEM')}
onLoadingChange={setDownloadState}
dataSize={dataList?.total_all}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => (
<th key={header.id} width={header.width}>{header.label}</th>
))}
</tr>
</thead>
<tbody>
{dataList?.item_detail_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logTime}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
<td>{item.tranId}</td>
<td>{item.action}</td>
<td>{item.itemId}</td>
<td>{item.itemName}</td>
<td>{itemTypeLarge.find(data => data.value === item.itemTypeLarge)?.name || item.itemTypeLarge}</td>
<td>{item.itemTypeSmall}</td>
<td>{countDeltaType.find(data => data.value === item.countDeltaType)?.name || item.countDeltaType}</td>
<td>{numberFormatter.formatCurrency(item.deltaCount)}</td>
<td>{numberFormatter.formatCurrency(item.stackCount)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{dataList?.item_detail_list &&
<Pagination
postsPerPage={searchParams.page_size}
totalPosts={dataList?.total_all}
setCurrentPage={handlePageChange}
currentPage={searchParams.page_no}
pageLimit={INITIAL_PAGE_LIMIT}
/>
}
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default CurrencyLogContent;

View File

@@ -1,219 +0,0 @@
import { useState } from 'react';
import { styled } from 'styled-components';
import RadioInput from '../common/input/Radio';
import Button from '../common/button/Button';
import DatePicker, { registerLocale } from 'react-datepicker';
import { ko } from 'date-fns/esm/locale';
import 'react-datepicker/dist/react-datepicker.css';
import { getMonth, getYear } from 'date-fns';
import range from 'lodash/range';
import { TextInput, SelectInput, DatePickerWrapper, InputLabel, BtnWrapper } from '../../styles/Components';
const ItemLogSearchBar = () => {
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const [selectData, setSelectData] = useState('default');
const years = range(1990, getYear(new Date()) + 1, 1);
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
const handleChange = e => {
setSelectData(e.target.value);
};
return (
<>
<SearchbarStyle2>
<SearchRow>
<RadioGroup>
<RadioInput label="기본 조회" id="single" name="receiver" value="default" fontWeight="600" checked={selectData === 'default'} handleChange={handleChange} />
<RadioInput label="아이템 소유자 추적" id="multi" name="receiver" value="item" fontWeight="600" checked={selectData === 'item'} handleChange={handleChange} />
</RadioGroup>
</SearchRow>
<SearchRow>
<SearchItem>
<InputLabel>조회 기간</InputLabel>
<DatePickerWrapper>
<InputGroup>
<DatePicker
selected={startDate}
onChange={date => setStartDate(date)}
className="datepicker"
placeholderText="검색기간 선택"
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
locale="ko"
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
</InputGroup>
<span>~</span>
<InputGroup>
<DatePicker
selected={endDate}
onChange={date => setEndDate(date)}
className="datepicker"
placeholderText="검색기간 선택"
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
minDate = {startDate}
locale="ko"
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
</InputGroup>
</DatePickerWrapper>
</SearchItem>
</SearchRow>
<SearchRow>
{selectData === 'default' ? (
<SearchItem>
<InputLabel>조회 대상</InputLabel>
<TextInput type="text" placeholder="조회 대상 유저의 GUID를 입력하세요." width="600px" />
</SearchItem>
) : (
<SearchItem>
<InputLabel>아이템 ID</InputLabel>
<TextInput type="text" placeholder="아이템의 GUID를 입력하세요." width="600px" />
</SearchItem>
)}
<BtnWrapper $gap="8px">
<Button theme="reset" />
<Button theme="gray" text="검색" />
</BtnWrapper>
</SearchRow>
</SearchbarStyle2>
</>
);
};
export default ItemLogSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchbarStyle2 = styled(SearchbarStyle)`
flex-flow: column;
gap: 20px;
`;
const SearchRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px 0;
`;
const InputGroup = styled.div`
display: flex;
align-items: center;
gap: 5px;
`;
const RadioGroup = styled(InputGroup)`
gap: 30px;
height: 35px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -2,16 +2,19 @@ import { styled } from 'styled-components';
import { useEffect, useState } from 'react';
import { Title } from '../../styles/Components';
import { TextInput, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import { TextInput, BtnWrapper } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import Modal from '../../components/common/modal/Modal';
import { UserChangeNickName } from '../../apis';
import { alertTypes } from '../../assets/data/types';
import { useAlert } from '../../context/AlertProvider';
import { useLoading } from '../../context/LoadingProvider';
const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
let nickName = dataList.char_info && dataList.char_info.character_name;
const [modifyModal, setModifyModal] = useState('hidden');
const [completeModal, setCompleteModal] = useState('hidden');
const [completeText, setCompleteText] = useState('');
const [resultData, setResultData] = useState({
guid: '',
@@ -20,42 +23,42 @@ const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
});
useEffect(() => {
setResultData({ ...resultData, guid: dataList.user_info && dataList.user_info.aid, nickname: dataList.char_info && dataList.char_info.character_name });
setResultData({ ...resultData,
guid: dataList.user_info && dataList.user_info.aid,
nickname: dataList.char_info && dataList.char_info.character_name
});
}, [dataList]);
// 수정 모달창
const handleModifyModal = () => {
if (modifyModal === 'hidden') {
setModifyModal('view');
} else {
setModifyModal('hidden');
const handleSubmit = async (type, param = null) => {
switch (type) {
case "nicknameChangeSubmit":
showModal('NICKNAME_CHANGES_CONFIRM', {
type: alertTypes.confirm,
onConfirm: () => handleSubmit('nicknameChange')
});
break;
case "nicknameChange":
const token = sessionStorage.getItem('token');
await withLoading(async () => {
return await UserChangeNickName(token, resultData);
}).then(data =>{
console.log(data);
if(data.result === 'ERROR'){
showToast(data.data.message, {type: alertTypes.error});
}else{
showToast('NICKNAME_CHANGES_COMPLETE', {type: alertTypes.success});
}
}).catch(error => {
showToast(error, {type: alertTypes.error});
}).finally(() => {
handleClick();
});
break;
}
};
// 완료 모달창
const handleCompleteModal = () => {
if (completeModal === 'hidden') {
setCompleteModal('view');
} else {
setCompleteModal('hidden');
handleClick();
completeText === '변경이 완료되었습니다.' && window.location.reload();
}
};
// 수정
const handleModifyNotice = async () => {
const token = sessionStorage.getItem('token');
const message = await UserChangeNickName(token, resultData);
// console.log(message);
message.data.data.message !== '수정 하였습니다.' ? setCompleteText('변경 닉네임이 이미 존재합니다.\n다시 시도해주세요.') : setCompleteText('변경이 완료되었습니다.');
handleCompleteModal();
handleModifyModal();
};
}
return (
<>
@@ -82,29 +85,7 @@ const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
</PwSetTable>
<BtnWrapper $justify="center" $gap="10px">
<Button theme="line" text="취소" handleClick={handleClick} />
<Button theme="primary" text="변경하기" handleClick={handleModifyModal} />
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={modifyModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleModifyModal} />
</BtnWrapper>
<ModalText $align="center">닉네임을 변경하시겠습니까?</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleModifyModal} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleModifyNotice} />
</BtnWrapper>
</Modal>
{/* 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModal} />
</BtnWrapper>
<ModalText $align="center">{completeText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
<Button theme="primary" text="변경하기" handleClick={() => handleSubmit('nicknameChangeSubmit')} />
</BtnWrapper>
</Modal>
</>

View File

@@ -5,15 +5,31 @@ import { BtnWrapper, TableStyle } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import Modal from '../../components/common/modal/Modal';
import { useEffect, useState, Fragment } from 'react';
import { questStatus } from '../../assets/data/options';
import { commonStatus } from '../../assets/data';
import { useModal } from '../../hooks/hook';
import { alertTypes } from '../../assets/data/types';
import { useAlert } from '../../context/AlertProvider';
const QuestDetailModal = ({ detailPop, handleClick, detailQuest, handleQuestComplete }) => {
const { showModal } = useAlert();
const QuestDetailModal = ({ detailPop, handleClick, detailQuest }) => {
const [detailList, setDetailList] = useState([])
useEffect(() => {
Array.isArray(detailQuest) && setDetailList(detailQuest)
Array.isArray(detailQuest.detailQuest) && setDetailList(detailQuest.detailQuest)
}, [detailQuest])
// const questlist = [{ taskNo: detailQuest.task_no, taskName: detailQuest.quest_name, counter: detailQuest.counter, state: detailQuest.status }];
const handleQuestCompleteConfirm = (data) => {
const params = {...data, quest_key: detailQuest.quest_key}
showModal('QUEST_TASK_COMPLETE_CONFIRM',{
type: alertTypes.confirm,
onConfirm: () => {
handleQuestComplete(params);
}
});
}
return (
<>
<Modal $view={detailPop} min="480px">
@@ -25,7 +41,8 @@ const QuestDetailModal = ({ detailPop, handleClick, detailQuest }) => {
<th width="80">Task No</th>
<th>Task Name</th>
<th width="120">Counter</th>
<th width="120">State</th>
<th width="120">상태</th>
<th width="120">완료처리</th>
</tr>
</thead>
<tbody>
@@ -36,7 +53,10 @@ const QuestDetailModal = ({ detailPop, handleClick, detailQuest }) => {
<td>{el.task_no}</td>
<td>{el.quest_name}</td>
<td>{el.counter}</td>
<td>{el.status}</td>
<td>{questStatus.find(data => data.value === el.status)?.name}</td>
<td>
{ el.status === commonStatus.running && <Button text="완료" theme="line" handleClick={() => handleQuestCompleteConfirm(el)} />}
</td>
</tr>
</Fragment>
);

View File

@@ -0,0 +1,99 @@
import styled from 'styled-components';
import { useState, useEffect } from 'react';
import { UserToolView } from '../../apis/Users';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
const RankPioneerInfo = ({ userInfo }) => {
const [dataList, setDataList] = useState();
const [rowData, setRowData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, []);
useEffect(() => {
if(dataList && dataList.slot_list)
setRowData([
{ title: 'GUID', itemNo: dataList.slot_list.slot1?.tool_id, itemName: dataList?.slot_list.slot1?.tool_name },
{ title: '아바타명', itemNo: dataList.slot_list.slot2?.tool_id, itemName: dataList?.slot_list.slot2?.tool_name },
{ title: '점수', itemNo: dataList.slot_list.slot3?.tool_id, itemName: dataList?.slot_list.slot3?.tool_name },
])
}, [dataList])
const fetchData = async () => {
const token = sessionStorage.getItem('token');
await UserToolView(token, userInfo.guid).then(data => {
setDataList(data);
setLoading(false);
});
};
return (
loading ? <TableSkeleton width='30%' count={4} /> :
<>
<ToolWrapper>
<UserInfoTable $maxwidth="570px">
<colgroup>
<col width="120" />
<col width="30%" />
<col width="70%" />
</colgroup>
<tbody>
{rowData && rowData.map((el, idx) => {
return (
<tr key={idx}>
<th>{el.title}</th>
<td>{el.itemNo}</td>
<td>{el.itemName}</td>
</tr>
);
})}
</tbody>
</UserInfoTable>
</ToolWrapper>
</>
);
};
export default RankPioneerInfo;
const UserInfoTable = styled.table`
width: 100%;
max-width: ${props => props.$maxwidth || 'auto'};
font-size: 13px;
border-radius: 15px;
overflow: hidden;
tr:first-child {
th,
td {
border-top: 0;
}
}
th,
td {
height: 36px;
vertical-align: middle;
border-top: 1px solid #d9d9d9;
}
th {
width: 120px;
background: #888;
color: #fff;
font-weight: 700;
}
td {
background: #fff;
padding: 0 20px;
}
`;
const ToolWrapper = styled.div`
${UserInfoTable} {
td {
border-left: 1px solid #d9d9d9;
}
}
`;

View File

@@ -0,0 +1,84 @@
import React, { useRef } from 'react';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { RankInfoSearchBar, useRankInfoSearch } from '../searchBar';
import { useDataFetch } from '../../hooks/hook';
import { RankingScheduleSimpleView } from '../../apis';
import { FormWrapper, TableStyle, TableWrapper } from '../../styles/Components';
import { ExcelDownButton, ViewTableInfo } from '../common';
import { useTranslation } from 'react-i18next';
import { formatTimeFromSeconds } from '../../utils';
const RankingSnapshotInfo = () => {
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const { t } = useTranslation();
const {
config,
loading: dataLoading,
searchParams,
data: dataList,
handleSearch,
handleReset,
updateSearchParams,
snapshotData
} = useRankInfoSearch(token);
const {
data: rankingScheduleData
} = useDataFetch(() => RankingScheduleSimpleView(token), [token]);
return (
<>
<FormWrapper>
<RankInfoSearchBar
config={config}
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
scheduleData={rankingScheduleData}
snapshotData={snapshotData}
/>
</FormWrapper>
<ViewTableInfo>
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_RANKING_SNAPSHOT')} />
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<TableWrapper>
<TableStyle ref={tableRef}>
<caption></caption>
<thead>
<tr>
<th width="80">순위</th>
<th width="120">account ID</th>
<th width="200">GUID</th>
<th width="150">아바타명</th>
<th width="80">점수</th>
</tr>
</thead>
<tbody>
{dataList?.ranker_List?.map((rank, index) => (
<tr key={index}>
<td>{rank.rank}</td>
<td>{rank.account_id}</td>
<td>{rank.user_guid}</td>
<td>{rank.nickname}</td>
<td>{rank.score_type === "Time" ? formatTimeFromSeconds(rank.score) : rank.score}</td>
</tr>
))}
</tbody>
</TableStyle>
</TableWrapper>
}
</>
);
};
export default RankingSnapshotInfo;

View File

@@ -1,198 +0,0 @@
import { styled } from 'styled-components';
import { useState } from 'react';
import Button from '../../components/common/button/Button';
import DatePicker, { registerLocale } from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import { getMonth, getYear } from 'date-fns';
import range from 'lodash/range';
import { TextInput, SelectInput, DatePickerWrapper, InputLabel, BtnWrapper } from '../../styles/Components';
const TradeLogSerchBar = () => {
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const [selectData, setSelectData] = useState('default');
const years = range(1990, getYear(new Date()) + 1, 1);
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
const handleChange = e => {
setSelectData(e.target.value);
};
return (
<>
<SearchbarStyle2>
<SearchRow>
<SearchItem>
<InputLabel>조회 기간</InputLabel>
<DatePickerWrapper>
<InputGroup>
<DatePicker
selected={startDate}
onChange={date => setStartDate(date)}
className="datepicker"
placeholderText="검색기간 선택"
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
locale="ko"
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
</InputGroup>
<span>~</span>
<InputGroup>
<DatePicker
selected={endDate}
onChange={date => setEndDate(date)}
className="datepicker"
placeholderText="검색기간 선택"
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
minDate = {startDate}
locale="ko"
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
<SelectInput>
<option value="">00</option>
<option value="">01</option>
</SelectInput>
</InputGroup>
</DatePickerWrapper>
</SearchItem>
</SearchRow>
<SearchRow>
<SearchItem>
<InputLabel>조회 대상</InputLabel>
<TextInput type="text" placeholder="조회 대상 유저의 GUID를 입력하세요." width="600px" />
</SearchItem>
<BtnWrapper $gap="8px">
<Button theme="reset" />
<Button theme="gray" text="검색" />
</BtnWrapper>
</SearchRow>
</SearchbarStyle2>
</>
);
};
export default TradeLogSerchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchbarStyle2 = styled(SearchbarStyle)`
flex-flow: column;
gap: 20px;
`;
const SearchRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px 0;
`;
const InputGroup = styled.div`
display: flex;
align-items: center;
gap: 5px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -8,7 +8,9 @@ const UserAvatarInfo = ({ userInfo }) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData();
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, []);
const fetchData = async () => {

View File

@@ -0,0 +1,146 @@
import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import {
CircularProgressWrapper,
FormWrapper,
TableStyle,
TableWrapper,
} from '../../styles/Components';
import { useTranslation } from 'react-i18next';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { useUserCreateLogSearch, UserCreateLogSearchBar } from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import Pagination from '../common/Pagination/Pagination';
import {
INITIAL_PAGE_LIMIT,STORAGE_GAME_LOG_USER_CREATE_SEARCH,
} from '../../assets/data/adminConstants';
import ExcelExportButton from '../common/button/ExcelExportButton';
import CircularProgress from '../common/CircularProgress';
import { AnimatedPageWrapper } from '../common/Layout';
const UserCreateLogContent = ({ active }) => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const [downloadState, setDownloadState] = useState({
loading: false,
progress: 0
});
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
updateSearchParams
} = useUserCreateLogSearch(token, 500);
useEffect(() => {
if(active) {
// 세션 스토리지에서 복사된 메일 데이터 가져오기
const paramsData = sessionStorage.getItem(STORAGE_GAME_LOG_USER_CREATE_SEARCH);
if (paramsData) {
const searchData = JSON.parse(paramsData);
handleSearch({
start_dt: new Date(searchData.start_dt),
end_dt: new Date(searchData.end_dt),
search_data: searchData.guid
});
// 사용 후 세션 스토리지 데이터 삭제
sessionStorage.removeItem(STORAGE_GAME_LOG_USER_CREATE_SEARCH);
}
}
}, [active]);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '120px' },
{ id: 'accountId', label: 'account ID', width: '80px' },
{ id: 'userGuid', label: 'GUID', width: '200px' },
{ id: 'userNickname', label: '아바타명', width: '150px' },
{ id: 'createTime', label: '생성일시', width: '200px' },
];
}, []);
if(!active) return null;
return (
<AnimatedPageWrapper>
<FormWrapper>
<UserCreateLogSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo orderType="asc" pageType="B" total={dataList?.total} total_all={dataList?.total_all}>
<ExcelExportButton
functionName="GameUserCreateLogExport"
params={searchParams}
fileName={t('FILE_GAME_LOG_USER_CREATE')}
onLoadingChange={setDownloadState}
dataSize={dataList?.total_all}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => (
<th key={header.id} width={header.width}>{header.label}</th>
))}
</tr>
</thead>
<tbody>
{dataList?.user_create_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
<td>{item.createdTime}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{dataList?.user_create_list &&
<Pagination
postsPerPage={searchParams.page_size}
totalPosts={dataList?.total_all}
setCurrentPage={handlePageChange}
currentPage={searchParams.page_no}
pageLimit={INITIAL_PAGE_LIMIT}
/>
}
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default UserCreateLogContent;

View File

@@ -20,23 +20,25 @@ import { opUserSessionType } from '../../assets/data/options';
import Button from '../common/button/Button';
import { useModal } from '../../hooks/hook';
import { InitData } from '../../apis/Data';
import { useAlert } from '../../context/AlertProvider';
import { useLoading } from '../../context/LoadingProvider';
import { alertTypes } from '../../assets/data/types';
const UserDefaultInfo = ({ userInfo }) => {
const { t } = useTranslation();
const authInfo = useRecoilValue(authList);
const token = sessionStorage.getItem('token');
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
const {
modalState,
handleModalView,
handleModalClose
} = useModal({
userKick: 'hidden',
gmLevelChange: 'hidden',
pwChange: 'hidden'
});
const [alertMsg, setAlertMsg] = useState('');
const [dataList, setDataList] = useState({});
const [adminLevel, setAdminLevel] = useState('0');
const [loading, setLoading] = useState(true);
const [authDelete, setAuthDelete] = useState(false);
@@ -45,7 +47,9 @@ const UserDefaultInfo = ({ userInfo }) => {
}, [authInfo]);
useEffect(() => {
fetchData();
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, [userInfo]);
const fetchData = async () => {
@@ -61,48 +65,46 @@ const UserDefaultInfo = ({ userInfo }) => {
switch (type) {
case "gmLevelChangeSubmit":
setAdminLevel(param);
handleModalView('gmLevelChange');
showModal('USER_GM_CHANGE', {
type: alertTypes.confirm,
onConfirm: () => handleSubmit('gmLevelChange', param)
});
break;
case "userKickSubmit":
handleModalView('userKick');
showModal('USER_KICK_CONFIRM', {
type: alertTypes.confirm,
onConfirm: () => handleSubmit('userKick')
});
break;
case "gmLevelChange":
setLoading(true);
params.guid = userInfo.guid;
params.admin_level = adminLevel;
params.admin_level = param;
await UserChangeAdminLevel(token, params).then(data =>{
setAlertMsg(t('USER_GM_CHANGE_COMPLETE'))
await withLoading(async () => {
return await UserChangeAdminLevel(token, params);
}).then(data =>{
showToast('USER_GM_CHANGE_COMPLETE', {type: alertTypes.success});
}).catch(error => {
console.log(error);
showToast(error, {type: alertTypes.error});
}).finally(() => {
setLoading(false);
handleModalClose('gmLevelChange');
fetchData();
});
break;
case "userKick":
params.guid = userInfo.guid;
await UserKick(token, params).then((data) =>{
setAlertMsg(t('USER_KICK_COMPLETE'))
await withLoading(async () => {
return await UserKick(token, params);
}).then((data) =>{
showToast('USER_KICK_COMPLETE', {type: alertTypes.success});
}).catch(error => {
console.log(error);
showToast(error, {type: alertTypes.error});
}).finally(() => {
setLoading(false);
handleModalClose('userKick');
fetchData();
});
break;
case "registComplete":
handleModalClose('registComplete');
break;
case "warning":
setAlertMsg('');
break;
}
}
@@ -131,12 +133,13 @@ const UserDefaultInfo = ({ userInfo }) => {
<tr>
<th>접속상태</th>
<StatusCell>{dataList.user_session !== undefined && opUserSessionType.find(session => session.value === dataList.user_session)?.name}
{<Button theme={(dataList.user_session && authDelete) ? "line" : "disable"}
id={"user_session"}
name="kick"
text="KICK"
handleClick={() => handleSubmit('userKickSubmit')}
disabled={!dataList.user_session && !authDelete}
{<Button
theme={(dataList.user_session && authDelete) ? "line" : "disable"}
id={"user_session"}
name="kick"
text="KICK"
handleClick={() => handleSubmit('userKickSubmit')}
disabled={(!authDelete || !dataList.user_session)}
/>}
</StatusCell>
</tr>
@@ -163,7 +166,7 @@ const UserDefaultInfo = ({ userInfo }) => {
<tr>
<th>GM권한</th>
<td>
<SelectInput value={dataList.user_info && dataList.user_info.admin_level}
<SelectInput value={dataList?.user_info?.admin_level}
onChange={e => handleSubmit('gmLevelChangeSubmit', e.target.value)}
disabled={authInfo.auth_list && !authInfo.auth_list.some(auth => auth.id === authType.userSearchUpdate)} >
{adminLevelType.map((data, index) => (
@@ -190,10 +193,10 @@ const UserDefaultInfo = ({ userInfo }) => {
<td colSpan="3">
{dataList.char_info && dataList.char_info.character_name}
<EditButton
hidden={true}
// hidden={true}
onClick={e => {
e.preventDefault();
handleModalClose('pwChange');
handleModalView('pwChange');
}}></EditButton>
</td>
</tr>
@@ -216,28 +219,13 @@ const UserDefaultInfo = ({ userInfo }) => {
</tbody>
</UserInfoTable>
</div>
<NicknameChangeModal pwPop={modalState.pwChangeModal} handleClick={() => handleModalClose('pwChange')} dataList={dataList} />
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.gmLevelChangeModal}
modalText={t('USER_GM_CHANGE')}
handleSubmit={() => handleSubmit('gmLevelChange')}
handleCancel={() => handleModalClose('gmLevelChange')}
/>
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.userKickModal}
modalText={t('USER_KICK_CONFIRM')}
handleSubmit={() => handleSubmit('userKick')}
handleCancel={() => handleModalClose('userKick')}
/>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => setAlertMsg('')}
/>
<NicknameChangeModal
pwPop={modalState.pwChangeModal}
handleClick={() => {
handleModalClose('pwChange');
fetchData();
}}
dataList={dataList} />
</>
);
};

View File

@@ -9,7 +9,9 @@ const UserDressInfo = ({ userInfo }) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData();
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, []);
useEffect(() => {
@@ -91,12 +93,6 @@ const UserInfoTable = styled.table`
font-size: 13px;
border-radius: 15px;
overflow: hidden;
tr:first-child {
th,
td {
border-top: 0;
}
}
th,
td {
height: 36px;

View File

@@ -13,7 +13,9 @@ const UserFriendInfo = ({ userInfo }) => {
const [dataList, setDataList] = useState([]);
useEffect(() => {
fetchData();
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, []);
const fetchData = async () => {

View File

@@ -12,10 +12,16 @@ import { useRecoilValue } from 'recoil';
import { authList } from '../../store/authList';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { InfoSubTitle, UserDefaultTable, UserTableWrapper } from '../../styles/ModuleComponents';
import { useAlert } from '../../context/AlertProvider';
import { useLoading } from '../../context/LoadingProvider';
import { convertKTC, timeDiffMinute } from '../../utils';
import { alertTypes } from '../../assets/data/types';
const UserInventoryInfo = ({ userInfo }) => {
const { t } = useTranslation();
const authInfo = useRecoilValue(authList);
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
const [dataList, setDataList] = useState();
const [itemCount, setItemCount] = useState('');
@@ -28,8 +34,10 @@ const UserInventoryInfo = ({ userInfo }) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData();
}, []);
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, [userInfo]);
useEffect(() => {
setAuthDelete(authInfo.auth_list.some(auth => auth.id === 35));
@@ -152,6 +160,19 @@ const UserInventoryInfo = ({ userInfo }) => {
}
};
const handleAction = async (action, item = null) => {
switch (action) {
case "delete":
break;
case "deleteConfirm":
break;
default:
break;
}
};
const ConfirmChild = () => {
return(
<InputItem>
@@ -173,7 +194,7 @@ const UserInventoryInfo = ({ userInfo }) => {
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
<th width="60">삭제</th>
{/*<th width="60">삭제</th>*/}
</tr>
</thead>
<tbody>
@@ -184,9 +205,9 @@ const UserInventoryInfo = ({ userInfo }) => {
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
<td>
<Button theme={authDelete ? "line" : "disable"} id={el.item_guid} name="single" text="삭제" handleClick={e => {handleDeleteConfirmModal(el, type)}} disabled={!authDelete}/>
</td>
{/*<td>*/}
{/* <Button theme={authDelete ? "line" : "disable"} id={el.item_guid} name="single" text="삭제" handleClick={e => {handleDeleteConfirmModal(el, type)}} disabled={!authDelete}/>*/}
{/*</td>*/}
</tr>
);
})}
@@ -225,136 +246,6 @@ const UserInventoryInfo = ({ userInfo }) => {
modalText={t('DEL_COMPLETE')}
handleComplete={handleDeleteComplete}
/>
{/* <InfoSubTitle>의상 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">no.</th>
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.cloth.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper>
<InfoSubTitle>소품 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">no.</th>
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.prop.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper>
<InfoSubTitle>미용 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">no.</th>
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.beauty.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper>
<InfoSubTitle>타투 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">no.</th>
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.tattoo.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper>
<InfoSubTitle>기타 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">슬롯 no.</th>
<th width="320">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.etc.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper> */}
</>
);
};

View File

@@ -0,0 +1,159 @@
import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import {
CircularProgressWrapper,
FormWrapper,
TableStyle,
TableWrapper,
} from '../../styles/Components';
import { useTranslation } from 'react-i18next';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { useUserLoginLogSearch, UserLoginLogSearchBar } from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import Pagination from '../common/Pagination/Pagination';
import {
INITIAL_PAGE_LIMIT, STORAGE_GAME_LOG_USER_LOGIN_SEARCH,
} from '../../assets/data/adminConstants';
import ExcelExportButton from '../common/button/ExcelExportButton';
import CircularProgress from '../common/CircularProgress';
import { AnimatedPageWrapper } from '../common/Layout';
import { numberFormatter } from '../../utils';
const UserLoginLogContent = ({ active }) => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const [downloadState, setDownloadState] = useState({
loading: false,
progress: 0
});
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
handleOrderByChange,
handlePageSizeChange,
updateSearchParams
} = useUserLoginLogSearch(token, 500);
useEffect(() => {
if(active) {
// 세션 스토리지에서 복사된 메일 데이터 가져오기
const paramsData = sessionStorage.getItem(STORAGE_GAME_LOG_USER_LOGIN_SEARCH);
if (paramsData) {
const searchData = JSON.parse(paramsData);
handleSearch({
start_dt: new Date(searchData.start_dt),
end_dt: new Date(searchData.end_dt),
search_data: searchData.guid
});
// 사용 후 세션 스토리지 데이터 삭제
sessionStorage.removeItem(STORAGE_GAME_LOG_USER_LOGIN_SEARCH);
}
}
}, [active]);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '100px' },
{ id: 'accountId', label: 'account ID', width: '80px' },
{ id: 'userGuid', label: 'GUID', width: '180px' },
{ id: 'userNickname', label: '아바타명', width: '150px' },
{ id: 'tranId', label: '트랜잭션 ID', width: '200px' },
{ id: 'loginTime', label: '로그인시간', width: '150px' },
{ id: 'logoutTime', label: '로그아웃시간', width: '120px' },
{ id: 'serverType', label: '서버종류', width: '80px' },
{ id: 'languageType', label: '언어', width: '80px' },
{ id: 'playtime', label: '플레이시간(분)', width: '80px' },
];
}, []);
if(!active) return null;
return (
<AnimatedPageWrapper>
<FormWrapper>
<UserLoginLogSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo orderType="asc" pageType="B" total={dataList?.total} total_all={dataList?.total_all}>
<ExcelExportButton
functionName="GameUserLoginLogExport"
params={searchParams}
fileName={t('FILE_GAME_LOG_USER_LOGIN')}
onLoadingChange={setDownloadState}
dataSize={dataList?.total_all}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => (
<th key={header.id} width={header.width}>{header.label}</th>
))}
</tr>
</thead>
<tbody>
{dataList?.user_login_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
<td>{item.tranId}</td>
<td>{item.loginTime}</td>
<td>{item.logoutTime}</td>
<td>{item.serverType}</td>
<td>{item.languageType}</td>
<td>{numberFormatter.formatSecondToMinuts(item.playtime)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{dataList?.user_login_list &&
<Pagination
postsPerPage={searchParams.page_size}
totalPosts={dataList?.total_all}
setCurrentPage={handlePageChange}
currentPage={searchParams.page_no}
pageLimit={INITIAL_PAGE_LIMIT}
/>
}
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default UserLoginLogContent;

View File

@@ -1,36 +1,69 @@
import { useState, Fragment, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useState, Fragment, useEffect, memo } from 'react';
import styled from 'styled-components';
import CheckBox from '../../components/common/input/CheckBox';
import MailDetailModal from '../../components/DataManage/MailDetailModal';
import { SelectInput, TextInput } from '../../styles/Components';
import { UserMailDelete, UserMailItemDelete, UserMailView } from '../../apis';
import ConfirmModal from '../common/modal/ConfirmModal';
import CompletedModal from '../common/modal/CompletedModal';
import { useTranslation } from 'react-i18next';
import CustomConfirmModal from '../common/modal/CustomConfirmModal';
import DynamicModal from '../common/modal/DynamicModal';
import { authType, ivenTabType, opMailType } from '../../assets/data';
import { useRecoilValue } from 'recoil';
import { authList } from '../../store/authList';
import { convertKTC } from '../../utils';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { eventSearchType, opPickupType, opReadType, opYNType } from '../../assets/data/options';
import { opPickupType, opReadType, opYNType } from '../../assets/data/options';
import { useDynamoDBPagination, useModal } from '../../hooks/hook';
import { DynamoPagination } from '../common';
import { useLoading } from '../../context/LoadingProvider';
import { useAlert } from '../../context/AlertProvider';
import { alertTypes } from '../../assets/data/types';
const ConfirmChild = memo(({ maxCount }) => {
const { t } = useTranslation();
const [localCount, setLocalCount] = useState('1');
const {showToast} = useAlert();
const handleChange = (e) => {
let value = e.target.value;
if (value === '0' || value === '-0') {
value = '1';
} else if (value < 0) {
value = Math.abs(value).toString();
} else if(Number(value) > maxCount) {
showToast('DEL_COUNT_CHECK', {type: alertTypes.warning});
value = maxCount.toString();
}
setLocalCount(value);
};
return(
<InputItem>
<p>{t('DEL_COUNT_CONFIRM', {count: maxCount})}</p>
<TextInput
id="user-mail-item-count-input"
placeholder="수량"
type="number"
value={localCount}
onChange={handleChange}
width="200px"
/>
</InputItem>
);
});
const UserMailInfo = ({ userInfo }) => {
const token = sessionStorage.getItem('token');
const { t } = useTranslation();
const authInfo = useRecoilValue(authList);
const {withLoading} = useLoading();
const {showModal, showToast} = useAlert();
// 받은 우편, 보낸 우편
const [option, setOption] = useState('RECEIVE');
const [detail, setDetail] = useState({ title: '', content: '', item_list: [], mail_guid: '' });
const [deleteSelected, setDeleteSelected] = useState({});
const [itemUpdateCount, setItemUpdateCount] = useState('1');
const [authDelete, setAuthDelete] = useState(false);
const {
@@ -39,10 +72,7 @@ const UserMailInfo = ({ userInfo }) => {
handleModalClose
} = useModal({
detail: 'hidden',
deleteItem: 'hidden',
deleteSubmit: 'hidden',
deleteComplete: 'hidden',
deleteItemComplete: 'hidden'
deleteItem: 'hidden'
});
const fetchMailData = async (page, startKey) => {
@@ -77,32 +107,14 @@ const UserMailInfo = ({ userInfo }) => {
setDetail({ title: title, content: content, item_list: itmeList, mail_guid: mail_guid });
};
// 우편 아이템 삭제 개수 처리
const handleItemCount = e => {
if (e.target.value === '0' || e.target.value === '-0') {
setItemUpdateCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setItemUpdateCount(plusNum);
} else if(e.target.value > deleteSelected.count){
alert(t('DEL_COUNT_CHECK'));
setItemUpdateCount(deleteSelected.count);
} else {
setItemUpdateCount(e.target.value);
}
};
const handleModalSubmit = async (type, param = null) => {
let params;
let result;
switch (type) {
case "detail":
handleModalView('deleteSubmit');
break;
case "deleteItem":
setDeleteSelected(param);
handleModalView('deleteItem');
showModal('USER_MAIL_DEL_CONFIRM', {
type: alertTypes.confirm,
onConfirm: () => handleModalSubmit('deleteSubmit')
});
break;
case "deleteSubmit":
params = {}
@@ -110,58 +122,64 @@ const UserMailInfo = ({ userInfo }) => {
params.guid = userInfo.guid;
params.mail_guid = detail.mail_guid;
result = await UserMailDelete(token, params);
await withLoading(async () => {
return await UserMailDelete(token, params);
}).then(data => {
if(data.result === "SUCCESS") {
showToast('DEL_COMPLETE', {type: alertTypes.success});
}else{
showToast(data.data.message, {type: alertTypes.error});
}
}).catch(e => {
showToast(e, {type: alertTypes.error});
});
handleModalClose('deleteSubmit');
handleModalView('deleteComplete');
break;
case "deleteItem":
setDeleteSelected(param);
handleModalView('deleteItem');
break;
case "deleteItemSubmit":
//랜더링 문제로 dom에서 값을 찾아 가져온다.
const inputElement = document.getElementById("user-mail-item-count-input");
if(inputElement === null) showToast('INPUT_VALUE_ERROR',{type: alertTypes.error});
const count = inputElement.value;
params = {}
params.type = option;
params.guid = userInfo.guid;
params.mail_guid = detail.mail_guid;
params.item_id = deleteSelected.item_id;
params.parrent_count = deleteSelected.count;
params.count = itemUpdateCount;
params.count = count;
result = await UserMailItemDelete(token, params);
if(result.result === "SUCCESS"){
if(deleteSelected.count <= itemUpdateCount){
const item_idx = detail.item_list.findIndex(item => item.item_id === deleteSelected.item_id);
if(item_idx >= 0) {
detail.item_list.splice(item_idx, 1);
await withLoading(async () => {
return await UserMailItemDelete(token, params);
}).then(data => {
if(data.result === "SUCCESS") {
if(deleteSelected.count <= count){
const item_idx = detail.item_list.findIndex(item => item.item_id === params.item_id);
if(item_idx >= 0) {
detail.item_list.splice(item_idx, 1);
}
}else{
deleteSelected.count = deleteSelected.count - count;
}
showToast('DEL_ITEM_COMPLETE', {type: alertTypes.success});
}else{
deleteSelected.count = deleteSelected.count - itemUpdateCount;
showToast(data.data.message, {type: alertTypes.error});
}
}
handleModalClose('deleteItem');
handleModalView('deleteItemComplete');
break;
case "deleteComplete":
handleModalClose('deleteComplete');
handleModalClose('detail');
// const idx = dataList.mail_list.findIndex(mail => mail.mail_guid === detail.mail_guid);
// if(idx >= 0) {
// dataList.mail_list.splice(idx, 1);
// }
fetchPage(pagination.currentPage);
break;
case "deleteItemComplete":
handleModalClose('deleteItemComplete');
}).catch(e => {
showToast(e, {type: alertTypes.error});
}).finally(() => {
handleModalClose('deleteItem');
fetchPage(pagination.currentPage);
});
break;
}
}
const ConfirmChild = () => {
return(
<InputItem>
<p>{t('DEL_COUNT_CONFIRM', {count: deleteSelected.count})}</p>
<TextInput placeholder="수량" type="number" value={itemUpdateCount} onChange={e => handleItemCount(e)} width="200px" />
</InputItem>
);
}
return (
loading ? <TableSkeleton count={10}/> :
<>
@@ -198,12 +216,10 @@ const UserMailInfo = ({ userInfo }) => {
<th width="100">첨부 아이템</th>
<th width="80">수령</th>
<th width="80">시스템 우편</th>
{/* <th width="170">수령 일자</th> */}
</tr>
</thead>
<tbody>
{dataList && dataList.mail_list &&
dataList.mail_list.map((mail, idx) => {
{dataList?.mail_list?.map((mail, idx) => {
return (
<tr key={idx}>
<td>{convertKTC(mail.create_time)}</td>
@@ -222,9 +238,6 @@ const UserMailInfo = ({ userInfo }) => {
<td>{opYNType.find(type => type.value === (mail.item_list.length > 0)).name}</td>
<td>{opPickupType.find(type => type.value === mail.is_get_item).name}</td>
<td>{opYNType.find(type => type.value === mail.is_system_mail).name}</td>
{/* <td>
{mail.is_get_item_dt && String(new Date(new Date(mail.is_get_item_dt).setHours(new Date(mail.is_get_item_dt).getHours() + 9)).toLocaleString())}
</td> */}
</tr>
);
})}
@@ -272,7 +285,6 @@ const UserMailInfo = ({ userInfo }) => {
</>
)}
{/*상세*/}
<MailDetailModal
mailModal={modalState.detailModal}
@@ -283,32 +295,19 @@ const UserMailInfo = ({ userInfo }) => {
handleItemDelete={(param) => handleModalSubmit('deleteItem', param)}
authDelete={authDelete}
/>
{/*메일 삭제 모달*/}
<ConfirmModal
modalText={t('USER_MAIL_DEL_CONFIRM')}
view={modalState.deleteSubmitModal}
handleSubmit={() => handleModalSubmit('deleteSubmit')}
handleCancel={() => handleModalClose('deleteSubmit')}
handleClose={() => handleModalClose('deleteSubmit')}
/>
<CompletedModal
view={modalState.deleteCompleteModal}
modalText={t('DEL_COMPLETE')}
handleComplete={() => handleModalSubmit('deleteComplete')}
/>
{/*메일 아이템 삭제 모달*/}
<CustomConfirmModal
ChildView={ConfirmChild}
ChildView={() => (
<ConfirmChild
maxCount={deleteSelected.count}
/>
)}
view={modalState.deleteItemModal}
handleSubmit={() => handleModalSubmit('deleteItemSubmit')}
handleCancel={() => handleModalClose('deleteItem')}
handleClose={() => handleModalClose('deleteItem')}
/>
<CompletedModal
view={modalState.deleteItemCompleteModal}
modalText={t('DEL_ITEM_COMPLETE')}
handleComplete={() => handleModalSubmit('deleteItemComplete')}
/>
</>
);
};
@@ -397,31 +396,4 @@ const SelectContainer = styled.div`
select {
height: 30px;
}
`;
const PaginationButtons = styled.div`
display: flex;
align-items: center;
gap: 10px;
`;
const PaginationButton = styled.button`
background-color: ${props => props.disabled ? '#e0e0e0' : '#6c7eb7'};
color: ${props => props.disabled ? '#a0a0a0' : 'white'};
border: none;
padding: 6px 12px;
border-radius: 4px;
font-size: 12px;
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
transition: background-color 0.2s;
&:hover {
background-color: ${props => props.disabled ? '#e0e0e0' : '#5a6a9b'};
}
`;
const PageInfo = styled.div`
font-size: 12px;
font-weight: 500;
color: #666;
`;

View File

@@ -1,31 +1,38 @@
import styled from 'styled-components';
import Button from '../common/button/Button';
import { InfoSubTitle, UserDefaultTable, UserInfoTable, UserTableWrapper } from '../../styles/ModuleComponents';
import { useTranslation } from 'react-i18next';
import { useRecoilValue } from 'recoil';
import { authList } from '../../store/authList';
import { useEffect, useState } from 'react';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { UserInventoryView, UserMyhomeView } from '../../apis';
import { UserMyhomeView } from '../../apis';
import { SelectInput } from '../../styles/Components';
const UserMyHomeInfo = ({ userInfo }) => {
const { t } = useTranslation();
const [dataList, setDataList] = useState();
const [loading, setLoading] = useState(true);
const [selectedHome, setSelectedHome] = useState('');
useEffect(() => {
fetchData();
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, []);
const fetchData = async () => {
const token = sessionStorage.getItem('token');
await UserMyhomeView(token, userInfo.guid).then(data => {
setDataList(data.myhome_info);
setDataList(data);
if (data.myhome_info && data.myhome_info.length > 0) {
setSelectedHome(data.myhome_info[0].myhome_guid);
}
setLoading(false);
});
};
const handleHomeChange = (e) => {
setSelectedHome(e.target.value);
};
return (
loading ? <TableSkeleton count={15}/> :
dataList &&
@@ -34,7 +41,13 @@ const UserMyHomeInfo = ({ userInfo }) => {
<tbody>
<tr>
<th>마이 홈명</th>
<td>{dataList.myhome_name}</td>
<SelectInput onChange={handleHomeChange} value={selectedHome}>
{dataList.myhome_info && dataList.myhome_info.map((data, index) => (
<option key={index} value={data.myhome_guid}>
{data.myhome_name}
</option>
))}
</SelectInput>
</tr>
</tbody>
</UserInfoTable>
@@ -49,15 +62,19 @@ const UserMyHomeInfo = ({ userInfo }) => {
</tr>
</thead>
<tbody>
{dataList.prop_list && dataList.prop_list.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
</tr>
);
})}
{dataList.myhome_info.find(home => home.myhome_guid === selectedHome)?.prop_list?.map((el, idx) => (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
</tr>
))}
{(!dataList.myhome_info.find(home => home.myhome_guid === selectedHome)?.prop_list ||
dataList.myhome_info.find(home => home.myhome_guid === selectedHome)?.prop_list.length === 0) && (
<tr>
<td colSpan="3" style={{textAlign: 'center'}}>{t('TABLE_DATA_NOT_FOUND')}</td>
</tr>
)}
</tbody>
</UserDefaultTable>
</UserTableWrapper>

View File

@@ -3,18 +3,27 @@ import { useState, useEffect, Fragment } from 'react';
import styled from 'styled-components';
import Button from '../../components/common/button/Button';
import QuestDetailModal from '../../components/DataManage/QuestDetailModal';
import { UserQuestView } from '../../apis/Users';
import { UserQuestTaskComplete, UserQuestView } from '../../apis/Users';
import { convertKTC } from '../../utils';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { useAlert } from '../../context/AlertProvider';
import { CaliumCharge } from '../../apis';
import { alertTypes } from '../../assets/data/types';
import { useLoading } from '../../context/LoadingProvider';
import { questCompleteStatusType } from '../../assets/data/options';
const UserQuestInfo = ({ userInfo }) => {
const [detailPop, setDetailPop] = useState('hidden');
const [dataList, setDataList] = useState({});
const [detailQuest, setDetailQuest] = useState({});
const [loading, setLoading] = useState(true);
const { showModal, showToast } = useAlert();
const { withLoading } = useLoading();
useEffect(() => {
fetchData();
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, []);
const fetchData = async () => {
@@ -28,10 +37,30 @@ const UserQuestInfo = ({ userInfo }) => {
const handleClick = data => {
if (detailPop === 'hidden') {
setDetailPop('view');
setDetailQuest(data.detailQuest);
setDetailQuest(data);
} else setDetailPop('hidden');
};
const handleQuestComplete = async data => {
const token = sessionStorage.getItem('token');
await withLoading(async () => {
const params = {...data, guid: userInfo.guid};
return await UserQuestTaskComplete(token, params);
}).then(data => {
if (data.result === "SUCCESS") {
showToast('QUEST_TASK_COMPLETE', { type: alertTypes.success });
} else {
showToast(data.data.message, { type: alertTypes.error });
}
}).catch(error => {
showToast('API_FAIL', { type: alertTypes.error });
}).finally(() => {
handleClick();
fetchData();
});
};
return (
loading ? <TableSkeleton /> :
<>
@@ -57,7 +86,7 @@ const UserQuestInfo = ({ userInfo }) => {
<td>{el.quest_id}</td>
<td>{el.quest_name}</td>
<td>{el.quest_type}</td>
<td>{el.status}</td>
<td>{questCompleteStatusType.find(data => el.status === data.value).name || el.status}</td>
<td>{convertKTC(el.quest_assign_time, false)}</td>
<td>{convertKTC(el.quest_complete_time, false)}</td>
<td>
@@ -70,7 +99,7 @@ const UserQuestInfo = ({ userInfo }) => {
</tbody>
</QuestTable>
</UserTableWrapper>
<QuestDetailModal detailPop={detailPop} handleClick={handleClick} detailQuest={detailQuest} />
<QuestDetailModal detailPop={detailPop} handleClick={handleClick} detailQuest={detailQuest} handleQuestComplete={handleQuestComplete} />
</>
);
};

View File

@@ -0,0 +1,201 @@
import React, { useCallback, useEffect, useState } from 'react';
import NicknameChangeModal from '../../components/DataManage/NicknameChangeModal';
import { UserInfoView } from '../../apis/Users';
import { authType } from '../../assets/data';
import { useTranslation } from 'react-i18next';
import { useRecoilValue } from 'recoil';
import { authList } from '../../store/authList';
import { UserDefault } from '../../styles/ModuleComponents';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { UserInfoSkeleton } from '../Skeleton/UserInfoSkeleton';
import { useModal } from '../../hooks/hook';
import { useAlert } from '../../context/AlertProvider';
import { useLoading } from '../../context/LoadingProvider';
import { alertTypes } from '../../assets/data/types';
import { Button, Col, Descriptions, InputNumber, Row, Space, Table } from 'antd';
import { RankerInfoModify, RankingInfoView } from '../../apis';
import { InputItem, TextInput } from '../../styles/Components';
import CustomConfirmModal from '../common/modal/CustomConfirmModal';
import InputConfirmModal from '../common/modal/InputConfirmModal';
import { opRankingType } from '../../assets/data/options';
import { formatTimeFromSeconds } from '../../utils';
const UserRankInfo = ({ userInfo }) => {
const { t } = useTranslation();
const authInfo = useRecoilValue(authList);
const token = sessionStorage.getItem('token');
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
const {
modalState,
handleModalView,
handleModalClose
} = useModal({
valueChange: 'hidden'
});
const [dataList, setDataList] = useState({});
const [loading, setLoading] = useState(true);
const [authUpdate, setAuthUpdate] = useState(false);
const [selectRow, setSelectRow] = useState();
const [updateValue, setUpdateValue] = useState();
const [comment, setComment] = useState();
useEffect(() => {
setAuthUpdate(authInfo?.auth_list?.some(auth => auth.id === authType.rankManagerUpdate));
}, [authInfo]);
useEffect(() => {
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, [userInfo]);
const fetchData = async () => {
setLoading(true);
await RankingInfoView(token, userInfo.guid).then(data => {
setDataList(data.user_ranking_info);
}).catch(error => {
showToast(error, {type: alertTypes.error});
}).finally(() => {
setLoading(false);
});
};
const findAndFormatScore = (rankingType) => {
if (!dataList?.rankingItems) return '';
const item = dataList.rankingItems.find(item => item.rankingType === rankingType);
if (!item || item.score === null || item.score === undefined) return '';
// scoreType이 "Time"이면 시:분:초 형식으로 변환 (초 단위로 가정)
if (item.scoreType === "Time") {
return formatTimeFromSeconds(item.score);
}
// 그 외의 경우는 숫자 그대로 반환
return item.score.toString();
};
const columns = [
{
dataIndex: 'label',
width: 200,
onCell: () => ({
style: {
backgroundColor: '#f0f0f0',
fontWeight: 'bold'
}
})
},
{
dataIndex: 'content',
width: 300,
},
{
dataIndex: 'action',
},
];
const generateTableData = () => {
if (!dataList) return [];
const baseData = [
{
key: 'guid',
label: 'GUID',
content: userInfo?.guid || '',
action: null
},
{
key: 'nickname',
label: '아바타명',
content: dataList?.nickname || userInfo?.nickname || '',
action: null
}
];
// opRankingType을 기반으로 동적 생성
const rankingData = opRankingType.map(rankType => {
const formattedScore = findAndFormatScore(rankType.value);
const hasScore = formattedScore !== '';
return {
key: rankType.value.toLowerCase(),
label: rankType.name,
content: formattedScore,
action: authUpdate && hasScore ? (
<Button
type="primary"
onClick={() => handleSubmit('valueChange', rankType.value)}
>
수정
</Button>
) : null
};
});
return [...baseData, ...rankingData];
};
const data = generateTableData();
const handleSubmit = async (type, param = null) => {
let params = {};
switch (type) {
case "valueChange":
setSelectRow(param);
const comment = dataList.rankingItems.find(item => item.rankingType === param)?.scoreType === 'Time' ? '초단위로 입력해주세요.' : '점수';
setComment(comment);
handleModalView('valueChange');
break;
case "valueChangeConfirm":
const item = dataList.rankingItems.find(item => item.rankingType === selectRow);
params.guid = item.guid;
params.score = updateValue;
params.user_guid = userInfo.guid;
await withLoading(async () => {
return await RankerInfoModify(token, params);
}).then(data => {
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
fetchData();
}).catch(error => {
showToast(error, {type: alertTypes.error});
}).finally(() => {
handleModalClose('valueChange');
});
break;
}
}
return (
loading ? <TableSkeleton width='30%' count={5} /> :
<>
<div>
<UserDefault>
<Table
columns={columns}
dataSource={data}
pagination={false}
showHeader={false}
bordered
/>
</UserDefault>
</div>
<InputConfirmModal
inputType='number'
view={modalState.valueChangeModal}
handleSubmit={() => handleSubmit('valueChangeConfirm')}
handleCancel={() => handleModalClose('valueChange')}
handleClose={() => handleModalClose('valueChange')}
value={updateValue}
setValue={setUpdateValue}
inputText={t('UPDATE_VALUE_COMMENT',{comment:comment})}
/>
</>
);
};
export default UserRankInfo;

View File

@@ -1,57 +0,0 @@
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper } from '../../styles/Components';
const UserSearchBar = () => {
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>유저 조회</InputLabel>
<TextInput type="text" width="300px" placeholder="조회 대상 유저의 GUID를 입력하세요." />
</SearchItem>
<BtnWrapper $gap="8px">
<Button theme="reset" />
<Button
theme="primary"
text="검색"
handleClick={e => {
e.preventDefault();
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default UserSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,153 @@
import React, { Fragment, useMemo, useRef, useState } from 'react';
import {
CircularProgressWrapper,
FormWrapper,
TableStyle,
TableWrapper,
} from '../../styles/Components';
import { useTranslation } from 'react-i18next';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { UserSnapshotLogSearchBar, useUserSnapshotLogSearch } from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import Pagination from '../common/Pagination/Pagination';
import {
INITIAL_PAGE_LIMIT,
} from '../../assets/data/adminConstants';
import ExcelExportButton from '../common/button/ExcelExportButton';
import CircularProgress from '../common/CircularProgress';
import { AnimatedPageWrapper } from '../common/Layout';
import { numberFormatter } from '../../utils';
const UserSnapshotLogContent = ({ active }) => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const [downloadState, setDownloadState] = useState({
loading: false,
progress: 0
});
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
updateSearchParams
} = useUserSnapshotLogSearch(token, 500);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '80px' },
{ id: 'accountId', label: 'account ID', width: '80px' },
{ id: 'userGuid', label: 'GUID', width: '180px' },
{ id: 'userNickname', label: '아바타명', width: '150px' },
{ id: 'gold', label: '골드', width: '80px' },
{ id: 'sapphire', label: '사파이어', width: '80px' },
{ id: 'calium', label: '칼리움', width: '80px' },
{ id: 'ruby', label: '루비', width: '80px' },
{ id: 'item_13080002', label: '퀘스트 메달', width: '80px' },
{ id: 'item_13080004', label: '보급품 메달', width: '80px' },
{ id: 'item_13080005', label: '제작 메달', width: '80px' },
{ id: 'item_13080006', label: '에테론 315 포드', width: '80px' },
{ id: 'item_13080007', label: '에테론 316 포드', width: '80px' },
{ id: 'item_13080008', label: '에테론 317 포드', width: '80px' },
{ id: 'item_13080009', label: '에테론 318 포드', width: '80px' },
{ id: 'item_11570001', label: '강화잉크', width: '80px' },
{ id: 'item_11570002', label: '연성잉크', width: '80px' },
{ id: 'lastLogoutTime', label: '마지막 로그아웃 일자', width: '150px' },
];
}, []);
if(!active) return null;
return (
<AnimatedPageWrapper>
<FormWrapper>
<UserSnapshotLogSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo orderType="asc" pageType="B" total={dataList?.total} total_all={dataList?.total_all}>
<ExcelExportButton
functionName="GameUserSnapshotLogExport"
params={searchParams}
fileName={t('FILE_GAME_LOG_USER_SNAPSHOT')}
onLoadingChange={setDownloadState}
dataSize={dataList?.total_all}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => (
<th key={header.id} width={header.width}>{header.label}</th>
))}
</tr>
</thead>
<tbody>
{dataList?.snapshot_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
<td>{numberFormatter.formatCurrency(item.gold)}</td>
<td>{numberFormatter.formatCurrency(item.sapphire)}</td>
<td>{numberFormatter.formatCurrency(item.calium)}</td>
<td>{numberFormatter.formatCurrency(item.ruby)}</td>
<td>{item.item_13080002}</td>
<td>{item.item_13080004}</td>
<td>{item.item_13080005}</td>
<td>{item.item_13080006}</td>
<td>{item.item_13080007}</td>
<td>{item.item_13080008}</td>
<td>{item.item_13080009}</td>
<td>{item.item_11570001}</td>
<td>{item.item_11570002}</td>
<td>{item.lastLogoutTime}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{dataList?.snapshot_list &&
<Pagination
postsPerPage={searchParams.page_size}
totalPosts={dataList?.total_all}
setCurrentPage={handlePageChange}
currentPage={searchParams.page_no}
pageLimit={INITIAL_PAGE_LIMIT}
/>
}
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default UserSnapshotLogContent;

View File

@@ -4,12 +4,14 @@ import { useState, useEffect } from 'react';
import { UserTattooView } from '../../apis/Users';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
const UserTatttooInfo = ({ userInfo }) => {
const UserTattooInfo = ({ userInfo }) => {
const [dataList, setDataList] = useState();
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData();
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, []);
const fetchData = async () => {
@@ -60,7 +62,7 @@ const UserTatttooInfo = ({ userInfo }) => {
);
};
export default UserTatttooInfo;
export default UserTattooInfo;
const UserDefaultTable = styled.table`
border: 1px solid #e8eaec;

View File

@@ -9,7 +9,9 @@ const UserToolInfo = ({ userInfo }) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData();
if(userInfo && Object.keys(userInfo).length > 0) {
fetchData();
}
}, []);
useEffect(() => {

View File

@@ -0,0 +1,24 @@
export { default as UserDefaultInfo } from './UserDefaultInfo';
export { default as UserAvatarInfo } from './UserAvatarInfo';
export { default as UserDressInfo } from './UserDressInfo';
export { default as UserToolInfo } from './UserToolInfo';
export { default as UserInventoryInfo } from './UserInventoryInfo';
export { default as UserMailInfo } from './UserMailInfo';
export { default as UserMyHomeInfo } from './UserMyHomeInfo';
export { default as UserFriendInfo } from './UserFriendInfo';
export { default as UserTattooInfo } from './UserTattooInfo';
export { default as UserQuestInfo } from './UserQuestInfo';
export { default as UserClaimInfo } from './UserClaimInfo';
export { default as CurrencyLogContent } from './CurrencyLogContent';
export { default as ItemLogContent } from './ItemLogContent';
export { default as CurrencyItemLogContent } from './CurrencyItemLogContent';
export { default as UserLoginLogContent } from './UserLoginLogContent';
export { default as UserCreateLogContent } from './UserCreateLogContent';
export { default as UserSnapshotLogContent } from './UserSnapshotLogContent';
export { default as RankPioneerInfo } from './RankPioneerInfo';
export { default as LandDetailModal } from './LandDetailModal';
export { default as MailDetailModal } from './MailDetailModal';
export { default as QuestDetailModal } from './QuestDetailModal';
export { default as NicknameChangeModal } from './NicknameChangeModal';
export { default as UserRankInfo } from './UserRankInfo';
export { default as RankingSnapshotInfo } from './RankingSnapshotInfo';

View File

@@ -1,312 +1,279 @@
import { useEffect, useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import React, { Fragment, useMemo, useRef, useState } from 'react';
import { CurrencyIndexExport, CurrencyIndexView } from '../../apis';
import { SelectInput, TableStyle, TableInfo, ListOption, InputLabel } from '../../styles/Components';
import {
TableStyle,
FormWrapper,
TableWrapper, CircularProgressWrapper, TotalRow,
} from '../../styles/Components';
import CreditSeacrhBar from '../../components/IndexManage/CreditSearchBar';
import { uniqBy } from 'lodash';
import { sumBy } from 'lodash';
import { useCurrencyIndexSearch } from '../searchBar';
import { Button, TopButton, ViewTableInfo } from '../common';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { numberFormatter } from '../../utils';
import {STORAGE_GAME_LOG_CURRENCY_SEARCH, } from '../../assets/data/adminConstants';
import ExcelExportButton from '../common/button/ExcelExportButton';
import CircularProgress from '../common/CircularProgress';
import { useTranslation } from 'react-i18next';
import CurrencyUserIndexSearchBar from '../searchBar/CurrencyUserIndexSearchBar';
import { useNavigate } from 'react-router-dom';
import { AnimatedPageWrapper } from '../common/Layout';
const CreditContent = () => {
const { t } = useTranslation();
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 CURRENCY_LIST = [
{ "key": "Gold", "name": "골드" },
{ "key": "Sapphire", "name": "사파이어" },
{ "key": "Calium", "name": "칼리움" },
{ "key": "Onyxium", "name": "오닉시움" }
];
const navigate = useNavigate();
const tableRef = useRef(null);
const [downloadState, setDownloadState] = useState({
loading: false,
progress: 0
});
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
const [currencyType, setCurrencyType] = useState('Gold');
const [currencyText, setCurrencyText] = useState('골드');
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
updateSearchParams
} = useCurrencyIndexSearch(token);
const [dataList, setDataList] = useState([]);
const [routeData, setRouteData] = useState([]);
const tableHeaders = useMemo(() => {
return [
// 기본 컬럼 (rowSpan=2)
{ id: 'logDay', label: '일자', width: '100px', rowSpan: 2 },
{ id: 'accountId', label: 'account ID', width: '80px', rowSpan: 2 },
{ id: 'userGuid', label: 'GUID', width: '200px', rowSpan: 2 },
{ id: 'userNickname', label: '아바타명', width: '150px', rowSpan: 2 },
// 획득량 그룹 헤더 (첫 번째 행에만 표시)
{ id: 'acquired', label: '획득', width: '400px', colSpan: 5, groupHeader: true },
// 획득량 컬럼 (두 번째 행에만 표시)
{ id: 'sapphireAcquired', label: '사파이어', width: '80px', groupRow: true },
{ id: 'goldAcquired', label: '골드', width: '80px', groupRow: true },
{ id: 'caliumAcquired', label: '칼리움', width: '80px', groupRow: true },
{ id: 'beamAcquired', label: 'BEAM', width: '80px', groupRow: true },
{ id: 'rubyAcquired', label: '루비', width: '80px', groupRow: true },
// 소모량 그룹 헤더 (첫 번째 행에만 표시)
{ id: 'consumed', label: '소모', width: '400px', colSpan: 5, groupHeader: true },
// 소모량 컬럼 (두 번째 행에만 표시)
{ id: 'sapphireConsumed', label: '사파이어', width: '80px', groupRow: true },
{ id: 'goldConsumed', label: '골드', width: '80px', groupRow: true },
{ id: 'caliumConsumed', label: '칼리움', width: '80px', groupRow: true },
{ id: 'beamConsumed', label: 'BEAM', width: '80px', groupRow: true },
{ id: 'rubyConsumed', label: '루비', width: '80px', groupRow: true },
// 계 컬럼 (rowSpan=2)
{ id: 'sapphireNet', label: '사파이어 계', width: '80px', rowSpan: 2 },
{ id: 'goldNet', label: '골드 계', width: '80px', rowSpan: 2 },
{ id: 'caliumNet', label: '칼리움 계', width: '80px', rowSpan: 2 },
{ id: 'beamNet', label: 'BEAM 계', width: '80px', rowSpan: 2 },
{ id: 'rubyNet', label: '루비 계', width: '80px', rowSpan: 2 },
// 기타 컬럼 (rowSpan=2)
{ id: 'totalCurrencies', label: '활동 수', width: '80px', rowSpan: 2 },
{ id: 'detail', label: '상세', width: '100px', rowSpan: 2 }
];
}, []);
useEffect(() => {
fetchData(sendDate, finishDate, currencyType);
}, [currencyType]);
const totals = useMemo(() => {
if (!dataList?.currency_list?.length) return null;
const fetchData = async (startDate, endDate) => {
const newStartDate = new Date(startDate);
const newEndDate = new Date(endDate);
return dataList.currency_list.reduce((acc, item) => {
return {
sapphireAcquired: (acc.sapphireAcquired || 0) + (item.sapphireAcquired || 0),
sapphireConsumed: (acc.sapphireConsumed || 0) + (item.sapphireConsumed || 0),
goldAcquired: (acc.goldAcquired || 0) + (item.goldAcquired || 0),
goldConsumed: (acc.goldConsumed || 0) + (item.goldConsumed || 0),
caliumAcquired: (acc.caliumAcquired || 0) + (item.caliumAcquired || 0),
caliumConsumed: (acc.caliumConsumed || 0) + (item.caliumConsumed || 0),
beamAcquired: (acc.beamAcquired || 0) + (item.beamAcquired || 0),
beamConsumed: (acc.beamConsumed || 0) + (item.beamConsumed || 0),
rubyAcquired: (acc.rubyAcquired || 0) + (item.rubyAcquired || 0),
rubyConsumed: (acc.rubyConsumed || 0) + (item.rubyConsumed || 0),
sapphireNet: (acc.sapphireNet || 0) + (item.sapphireNet || 0),
goldNet: (acc.goldNet || 0) + (item.goldNet || 0),
caliumNet: (acc.caliumNet || 0) + (item.caliumNet || 0),
beamNet: (acc.beamNet || 0) + (item.beamNet || 0),
rubyNet: (acc.rubyNet || 0) + (item.rubyNet || 0),
totalCurrencies: (acc.totalCurrencies || 0) + (item.totalCurrencies || 0),
};
}, {});
}, [dataList?.currency_list]);
const startDateToLocal =
newStartDate.getFullYear() +
'-' +
(newStartDate.getMonth() + 1 < 9 ? '0' + (newStartDate.getMonth() + 1) : newStartDate.getMonth() + 1) +
'-' +
(newStartDate.getDate() < 9 ? '0' + newStartDate.getDate() : newStartDate.getDate());
const handleModalSubmit = async (type, param = null) => {
switch (type) {
case "detail":
const params = {
tab: "CURRENCY",
start_dt: (() => {
const date = new Date(param.logDay);
return date;
})(),
end_dt: (() => {
const date = new Date(param.logDay);
date.setDate(date.getDate() + 1);
return date;
})(),
guid: param.userGuid
};
const endDateToLocal =
newEndDate.getFullYear() +
'-' +
(newEndDate.getMonth() + 1 < 9 ? '0' + (newEndDate.getMonth() + 1) : newEndDate.getMonth() + 1) +
'-' +
(newEndDate.getDate() < 9 ? '0' + newEndDate.getDate() : newEndDate.getDate());
setDataList(await CurrencyIndexView(token, startDateToLocal, endDateToLocal, currencyType));
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
setRoutArray(await CurrencyIndexView(token, startDateToLocal, endDateToLocal, currencyType));
};
const handleCurrencyChange = e => {
let value = e.target.value;
setCurrencyType(value);
CURRENCY_LIST.filter(data => data.key === value).map(data => {
setCurrencyText(data.name);
});
};
//route data
const setRoutArray = async (data) => {
let routeArray = [];
let routeAcqArr = [];
let routeConArr = [];
//생산량 route
data.list && data.list[0].daily_data.filter(routeData => routeData.delta_type === 'ACQUIRE').map(routeData => {
routeData.data.map(routeData => {
if(!routeAcqArr.includes(routeData.route) ){
routeAcqArr.push(routeData.route);
}
})
});
routeArray.ACQUIRE = routeAcqArr;
//소진량 route
data.list && data.list[0].daily_data.filter(routeData => routeData.delta_type === 'CONSUME').map(routeData => {
routeData.data.map(routeData => {
if(!routeConArr.includes(routeData.route) ){
routeConArr.push(routeData.route);
}
})
});
routeArray.CONSUME = routeConArr;
setRouteData(routeArray);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Credit_Index.xlsx';
CurrencyIndexExport(token, fileName, sendDate, finishDate, currencyType);
};
// 복사한 데이터를 세션 스토리지에 저장
sessionStorage.setItem(STORAGE_GAME_LOG_CURRENCY_SEARCH, JSON.stringify(params));
navigate('/datamanage/gamelogview');
break;
}
}
return (
<>
<CreditSeacrhBar fetchData={fetchData} />
<TableInfo2>
<SelectInput onChange={handleCurrencyChange}>
{CURRENCY_LIST.map((item, index) => (
<option value={item.key} key={index}>
{item.name}
</option>
))}
</SelectInput>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo2>
<TableWrapper>
<EconomicTable>
<thead>
<tr>
<th colSpan="2" className="text-center" width="300">
Product
</th>
{dataList.list && uniqBy(dataList.list, 'date').map(data =>
<th width="160" key={data.date}>{data.date}</th>
<AnimatedPageWrapper>
<FormWrapper>
<CurrencyUserIndexSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo >
<ExcelExportButton
functionName="GameCurrencyLogExport"
params={searchParams}
fileName={t('FILE_CURRENCY_INDEX')}
onLoadingChange={setDownloadState}
dataSize={dataList?.length}
/>
{downloadState.loading && (
<CircularProgressWrapper>
<CircularProgress
progress={downloadState.progress}
size={36}
progressColor="#4A90E2"
/>
</CircularProgressWrapper>
)}
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{/* 첫 번째 행 - 기본 컬럼 + 그룹 헤더 + rowSpan=2 컬럼 */}
{tableHeaders.map(header => {
if (header.groupRow) return null; // 두 번째 행의 컬럼은 첫 번째 행에서 건너뜀
return (
<th
key={header.id}
width={header.width}
rowSpan={header.rowSpan}
colSpan={header.colSpan}
>
{header.label}
</th>
);
})}
</tr>
<tr>
{/* 두 번째 행 - 그룹 내 하위 컬럼만 */}
{tableHeaders.map(header => {
if (!header.groupRow) return null; // 첫 번째 행이나 rowSpan=2 컬럼은 두 번째 행에서 건너뜀
return (
<th key={header.id} width={header.width}>
{header.label}
</th>
);
})}
</tr>
</thead>
<tbody>
{totals && (
<TotalRow>
<td colSpan="4">합계</td>
{/* 획득 그룹 합계 */}
<td>{numberFormatter.formatCurrency(totals.sapphireAcquired)}</td>
<td>{numberFormatter.formatCurrency(totals.goldAcquired)}</td>
<td>{numberFormatter.formatCurrency(totals.caliumAcquired)}</td>
<td>{numberFormatter.formatCurrency(totals.beamAcquired)}</td>
<td>{numberFormatter.formatCurrency(totals.rubyAcquired)}</td>
{/* 소모 그룹 합계 */}
<td>{numberFormatter.formatCurrency(totals.sapphireConsumed)}</td>
<td>{numberFormatter.formatCurrency(totals.goldConsumed)}</td>
<td>{numberFormatter.formatCurrency(totals.caliumConsumed)}</td>
<td>{numberFormatter.formatCurrency(totals.beamConsumed)}</td>
<td>{numberFormatter.formatCurrency(totals.rubyConsumed)}</td>
{/* 계 합계 */}
<td>{numberFormatter.formatCurrency(totals.sapphireNet)}</td>
<td>{numberFormatter.formatCurrency(totals.goldNet)}</td>
<td>{numberFormatter.formatCurrency(totals.caliumNet)}</td>
<td>{numberFormatter.formatCurrency(totals.beamNet)}</td>
<td>{numberFormatter.formatCurrency(totals.rubyNet)}</td>
<td>{totals.totalCurrencies}</td>
<td>-</td>
</TotalRow>
)}
</tr>
</thead>
<tbody>
<tr>
<TableTitle colSpan="2">(Total) {currencyText} 생산량</TableTitle>
{dataList.list &&
dataList.list.map((data) =>
(data.total).filter(totalData => data.date === totalData.date && totalData.delta_type === 'ACQUIRE')
.map((totalData, index) => (
<TableData key={index}
$state={totalData.dif !== "" && totalData.dif !== "Infinity" ? "danger" : ""}>
{totalData.quantity}
{
totalData.dif !== "" && totalData.dif !== "Infinity"
? (<span>({totalData.dif})</span>)
: ("")
}
</TableData>
)
))
}
</tr>
<tr>
<TableTitle colSpan="2">(Total) {currencyText} 소진량</TableTitle>
{dataList.list &&
dataList.list.map((data) =>
(data.total).filter(totalData => data.date === totalData.date && totalData.delta_type === 'CONSUME')
.map((totalData, index) => (
<TableData key={index}
$state={totalData.dif !== "" && totalData.dif !== "Infinity" ? "danger" : ""}>
{totalData.quantity}
{
totalData.dif !== "" && totalData.dif !== "Infinity"
? (<span>({totalData.dif})</span>)
: ("")
}
</TableData>
)
))
}
</tr>
<tr>
<TableTitle colSpan="2">(Total) {currencyText} 보유량</TableTitle>
{dataList.list &&
dataList.list.map((data, index) => (
<TableData key={index}>
{sumBy(
data.total.filter(totalData => data.date === totalData.date && totalData.delta_type === 'ACQUIRE'),
'quantity',
) -
sumBy(
data.total.filter(totalData => data.date === totalData.date && totalData.delta_type === 'CONSUME'),
'quantity',
)}
</TableData>
))
}
</tr>
{/* 획득 GET */}
{
routeData.ACQUIRE && routeData.ACQUIRE.map((route, index) => (
<tr key={index}>
<TableTitle>GET</TableTitle>
<TableTitle>{route}</TableTitle>
{dataList.list &&
dataList.list.map((data) =>
data.daily_data.filter(dailyData => data.date === dailyData.date && dailyData.delta_type === 'ACQUIRE')
.map(dailyData => (dailyData.data).filter(routeData => data.date === routeData.date && routeData.route === route)
.map((routeData, i) => (
<TableData key={i} data={routeData.date}>{routeData.quantity}</TableData>
)))
)
}
</tr>
))
}
{/* 소진 USE CONSUME */}
{
routeData.CONSUME && routeData.CONSUME.map((route, index) => (
<tr key={index}>
<TableTitle>USE</TableTitle>
<TableTitle>{route}</TableTitle>
{dataList.list &&
dataList.list.map((data) =>
data.daily_data.filter(dailyData => data.date === dailyData.date && dailyData.delta_type === 'CONSUME')
.map(dailyData => (dailyData.data).filter(routeData => data.date === routeData.date && routeData.route === route)
.map((routeData, i) => (
<TableData key={i} data={routeData.date}>{routeData.quantity}</TableData>
)))
)
}
</tr>
))
}
</tbody>
</EconomicTable>
</TableWrapper>
</>
{dataList?.currency_list?.map((item, index) => (
<Fragment key={index}>
<tr>
{/* 기본 정보 */}
<td>{item.logDay}</td>
<td>{item.accountId}</td>
<td>{item.userGuid}</td>
<td>{item.userNickname}</td>
{/* 획득 그룹 */}
<td>{numberFormatter.formatCurrency(item.sapphireAcquired)}</td>
<td>{numberFormatter.formatCurrency(item.goldAcquired)}</td>
<td>{numberFormatter.formatCurrency(item.caliumAcquired)}</td>
<td>{numberFormatter.formatCurrency(item.beamAcquired)}</td>
<td>{numberFormatter.formatCurrency(item.rubyAcquired)}</td>
{/* 소모 그룹 */}
<td>{numberFormatter.formatCurrency(item.sapphireConsumed)}</td>
<td>{numberFormatter.formatCurrency(item.goldConsumed)}</td>
<td>{numberFormatter.formatCurrency(item.caliumConsumed)}</td>
<td>{numberFormatter.formatCurrency(item.beamConsumed)}</td>
<td>{numberFormatter.formatCurrency(item.rubyConsumed)}</td>
{/* 계 */}
<td>{numberFormatter.formatCurrency(item.sapphireNet)}</td>
<td>{numberFormatter.formatCurrency(item.goldNet)}</td>
<td>{numberFormatter.formatCurrency(item.caliumNet)}</td>
<td>{numberFormatter.formatCurrency(item.beamNet)}</td>
<td>{numberFormatter.formatCurrency(item.rubyNet)}</td>
<td>{item.totalCurrencies}</td>
<td>
<Button theme="line" text="상세보기"
handleClick={e => handleModalSubmit('detail', item)} />
</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default CreditContent;
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 TableInfo2 = styled(TableInfo)`
justify-content: space-between;
${InputLabel} {
font-size: 12px;
}
${SelectInput} {
width: auto;
min-width: 100px;
height: 24px;
}
`;
const TableDate = styled.th`
color: ${props => (props.$state === 'danger' ? '#d60000' : '#2c2c2c')};
`;
const TableData = styled.td`
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
`;
const perData = styled.span`
display: ${props => (props.$view === 'hidden' ? 'none' : 'block')};
`;
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;
}
}
`;

View File

@@ -0,0 +1,126 @@
import React, { Fragment, useMemo, useRef } from 'react';
import {
TableStyle,
FormWrapper,
TableWrapper, ListOption,
} from '../../styles/Components';
import { useCurrencyAcquireIndexSearch, CurrencyAcquireIndexSearchBar } from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { numberFormatter } from '../../utils';
import { useTranslation } from 'react-i18next';
import { AnimatedPageWrapper } from '../common/Layout';
import CSVDownloadButton from '../common/button/CsvDownButton';
const CurrencyAcquireContent = () => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
updateSearchParams
} = useCurrencyAcquireIndexSearch(token);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '100px' },
{ id: 'mail', label: '우편', width: '80px' },
{ id: 'ShopSell', label: '상점 판매', width: '80px' },
{ id: 'ShopPurchase', label: '상점 구매', width: '80px' },
{ id: 'seasonPass', label: '시즌 패스', width: '80px' },
{ id: 'claim', label: '클레임', width: '80px' },
{ id: 'quest', label: '퀘스트', width: '80px' },
{ id: 'ugq', label: 'UGQ', width: '80px' },
{ id: 'battleObject', label: '보급품 상자', width: '80px' },
{ id: 'randomBox', label: '랜덤박스', width: '80px' },
{ id: 'landRent', label: '랜드 임대', width: '80px' },
{ id: 'caliumExchange', label: '칼리움 교환소', width: '80px' },
{ id: 'caliumConverter', label: '칼리움 컨버터', width: '80px' },
{ id: 'beaconShop', label: '비컨 상점', width: '80px' },
{ id: 'etc', label: '기타', width: '80px' },
{ id: 'summary', label: '합계', width: '80px' },
];
}, []);
return (
<AnimatedPageWrapper>
<FormWrapper>
<CurrencyAcquireIndexSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo >
<ListOption>
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_CURRENCY_ACQUIRE')} />
</ListOption>
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => {
return (
<th
key={header.id}
width={header.width}
colSpan={header.colSpan}
>
{header.label}
</th>
);
})}
</tr>
</thead>
<tbody>
{dataList?.currency_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.MailTaken)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopSell)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.SeasonPassTakeReward)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ClaimReward)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.QuestMainReward || 0) + (item.actionSummary.QuestTaskUpdate || 0))}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.UgqAbort)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.RewardProp)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.GainLandProfit)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ConvertExchangeCalium)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ConvertCalium)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconSell || 0) + (item.actionSummary.BeaconShopReceivePaymentForSales || 0))}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.MoneyChange)}</td>
<td>{numberFormatter.formatCurrency(item.totalDeltaAmount)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default CurrencyAcquireContent;

View File

@@ -0,0 +1,109 @@
import React, { Fragment, useMemo, useRef } from 'react';
import {
TableStyle,
FormWrapper,
TableWrapper, ListOption
} from '../../styles/Components';
import {
AssetsIndexSearchBar, useAssetsIndexSearch,
} from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { numberFormatter } from '../../utils';
import { useTranslation } from 'react-i18next';
import { AnimatedPageWrapper } from '../common/Layout';
import CSVDownloadButton from '../common/button/CsvDownButton';
import DailyDashBoard from './DailyCaliumDashBoard';
const CurrencyAssetsContent = () => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
updateSearchParams
} = useAssetsIndexSearch(token);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '100px' },
{ id: 'userCount', label: '유저수', width: '100px' },
{ id: 'gold', label: '골드', width: '100px' },
{ id: 'sapphire', label: '사파이어', width: '100px' },
{ id: 'calium', label: '칼리움', width: '100px' },
{ id: 'ruby', label: '루비', width: '100px' }
];
}, []);
return (
<AnimatedPageWrapper>
<DailyDashBoard />
<FormWrapper>
<AssetsIndexSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo >
<ListOption>
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_ASSETS_CURRENCY')} />
</ListOption>
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => {
return (
<th
key={header.id}
width={header.width}
colSpan={header.colSpan}
>
{header.label}
</th>
);
})}
</tr>
</thead>
<tbody>
{dataList?.assets_list?.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{data.logDay}</td>
<td>{numberFormatter.formatCurrency(data.userCount)}</td>
<td>{numberFormatter.formatCurrency(data.gold)}</td>
<td>{numberFormatter.formatCurrency(data.sapphire)}</td>
<td>{numberFormatter.formatCurrency(data.calium)}</td>
<td>{numberFormatter.formatCurrency(data.ruby)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default CurrencyAssetsContent;

View File

@@ -0,0 +1,128 @@
import React, { Fragment, useMemo, useRef } from 'react';
import {
TableStyle,
FormWrapper,
TableWrapper, ListOption,
} from '../../styles/Components';
import {
useCurrencyConsumeIndexSearch, CurrencyConsumeIndexSearchBar,
} from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { numberFormatter } from '../../utils';
import { useTranslation } from 'react-i18next';
import { AnimatedPageWrapper } from '../common/Layout';
import CSVDownloadButton from '../common/button/CsvDownButton';
const CurrencyConsumeContent = () => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
updateSearchParams
} = useCurrencyConsumeIndexSearch(token);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '100px' },
{ id: 'itemBuy', label: '아이템 구매', width: '80px' },
{ id: 'ShopPurchase', label: '상점 구매', width: '80px' },
{ id: 'ShopRePurchase', label: '상점 재구매', width: '80px' },
{ id: 'beaconShop', label: '비컨상점', width: '80px' },
{ id: 'beacon', label: '비컨', width: '80px' },
{ id: 'taxi', label: '택시', width: '80px' },
{ id: 'farming', label: '파밍', width: '80px' },
{ id: 'seasonPass', label: '시즌 패스', width: '80px' },
{ id: 'caliumExchange', label: '칼리움 교환소', width: '80px' },
{ id: 'caliumConverter', label: '칼리움 컨버터', width: '80px' },
{ id: 'rent', label: '랜드 렌탈', width: '80px' },
{ id: 'landAuction', label: '랜드 경매', width: '80px' },
{ id: 'ugq', label: 'UGQ', width: '80px' },
{ id: 'etc', label: '기타', width: '80px' },
{ id: 'summary', label: '합계', width: '80px' },
];
}, []);
return (
<AnimatedPageWrapper>
<FormWrapper>
<CurrencyConsumeIndexSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo >
<ListOption>
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_CURRENCY_CONSUME')} />
</ListOption>
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => {
return (
<th
key={header.id}
width={header.width}
colSpan={header.colSpan}
>
{header.label}
</th>
);
})}
</tr>
</thead>
<tbody>
{dataList?.currency_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemBuy)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopRePurchase)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconShopRegisterItem || 0) + (item.actionSummary.BeaconShopPurchaseItem || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconCreate || 0) + (item.actionSummary.BeaconEdit || 0) + (item.actionSummary.BeaconAppearanceCustomize || 0))}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.TaxiMove)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.FarmingStart)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.SeasonPassBuyCharged)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ConvertExchangeCalium)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ConvertCalium)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.RentFloor)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.LandAuctionBid)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.UgqAssign)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.MoneyChange ||0) + (item.actionSummary.RenewalShopProducts ||0) + (item.actionSummary.CharacterAppearanceCustomize ||0))}</td>
<td>{numberFormatter.formatCurrency(item.totalDeltaAmount)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default CurrencyConsumeContent;

View File

@@ -1,110 +0,0 @@
import { useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { DailySearchBar } from '../../components/IndexManage/index';
import { DailyActiveUserExport, DailyActiveUserView } from '../../apis';
const PlayTimeContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// DAU 데이터
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
// await DailyActiveUserView(token, startDateToLocal, endDateToLocal).then(data => {
// console.log(data);
// setDataList(data);
// });
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Dau.xlsx';
DailyActiveUserExport(token, fileName, sendDate, finishDate);
};
return (
<>
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th rowSpan="1" width="45">
일자
</th>
<th colSpan="1" width="30">
DAU
</th>
{/*<th colSpan="1" width="30">*/}
{/* DALC*/}
{/*</th>*/}
<th colSpan="1" width="30">
DGLC
</th>
{/*<th colSpan="1" width="30">*/}
{/* MaxAU*/}
{/*</th>*/}
</tr>
</thead>
<tbody>
{dataList && (dataList || []).map((data, index) => (
<tr key={index}>
<td>{data.date}</td>
<td>{data.dau}</td>
{/*<td>{data.dalc}</td>*/}
<td>{data.dglc}</td>
{/*<td>{data.maxAu}</td>*/}
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default PlayTimeContent;

View File

@@ -0,0 +1,141 @@
import { useEffect, useState } from 'react';
import { dashboardCaliumIndex } from '../../apis';
import { styled } from 'styled-components';
import TitleArrow from '../../assets/img/icon/icon-title.png';
const DailyDashBoard = () => {
const [boardState, setBoardState] = useState('active');
const [totalData, setTotalData] = useState([]);
const handleBoard = () => {
if (boardState === 'active') {
setBoardState('inactive');
} else {
setBoardState('active');
}
};
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const token = sessionStorage.getItem('token');
await dashboardCaliumIndex(token).then(data => {
setTotalData(data.dashboard_calium);
});
};
const dashboardItems = [
{
title: '컨버터 칼리움 보유량',
value: totalData.total_calium || 0
},
{
title: '컨버터 변환 효율',
value: `${totalData.converter_rate || 0}%`
},
{
title: '인플레이션 가중치',
value: `${totalData.inflation_rate || 0}%`
},
{
title: '칼리움 누적 총량',
value: totalData.cumulative_calium || 0
}
];
return (
<DailyBoardWrapper>
{totalData &&
<DailyBoard>
<BoardTitle onClick={handleBoard} $state={boardState}>
Daily Dashboard
</BoardTitle>
<BoardInfo $state={boardState}>
<BoxWrapper>
{dashboardItems?.map((item, index) => (
<InfoItem key={index}>
<InfoTitle>{item.title}</InfoTitle>
<InfoValue>
{item.value}
</InfoValue>
</InfoItem>
))}
</BoxWrapper>
</BoardInfo>
</DailyBoard>
}
</DailyBoardWrapper>
);
};
export default DailyDashBoard;
const DailyBoardWrapper = styled.div`
padding-top: 20px;
border-top: 1px solid #000;
`;
const DailyBoard = styled.div`
background: #f6f6f6;
border-radius: 10px;
margin-bottom: 20px;
`;
const BoardTitle = styled.div`
font-size: 24px;
font-weight: 600;
line-height: 52px;
padding: 0 10px;
cursor: pointer;
&:after {
content: '';
display: inline-block;
width: 11px;
height: 52px;
margin-left: 10px;
background: url(${TitleArrow}) 50% 50% no-repeat;
position: absolute;
transform: ${props => (props.$state === 'active' ? 'rotate(0)' : 'rotate(180deg)')};
}
`;
const BoardInfo = styled.div`
padding: 20px;
border-top: 1px solid #d9d9d9;
display: ${props => (props.$state === 'active' ? 'block' : 'none')};
`;
const BoxWrapper = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px;
`;
const InfoItem = styled.div`
width: 18%;
background: #fff;
padding: 15px 20px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
border-radius: 15px;
`;
const InfoTitle = styled.div`
font-size: 20px;
font-weight: 600;
`;
const InfoValue = styled.div`
display: inline-flex;
flex-wrap: wrap;
margin: 5px 0;
gap: 5px 0;
align-items: center;
font-size: 18px;
font-weight: 600;
`;

View File

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

View File

@@ -1,111 +0,0 @@
import { Fragment, useEffect, useState } from 'react';
import Button from '../common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { DailySearchBar } from './index';
import { DailyMedalView } from '../../apis';
const DailyMedalContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await DailyMedalView(token, startDateToLocal, endDateToLocal));
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Daily_Medal.xlsx';
//DailyActiveUserExport(token, fileName, sendDate, finishDate);
};
return (
<>
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead >
<tr>
<th rowSpan="1" width="20">
일자
</th>
<th colSpan="1" width="30">
UserID
</th>
<th colSpan="1" width="30">
닉네임
</th>
<th colSpan="1" width="30">
Item ID
</th>
<th colSpan="1" width="30">
획득량
</th>
</tr>
</thead>
<tbody>
{(dataList || []).map((data, index) => (
<tr key={index}>
<td>{data.date}</td>
<td>{data.dau}</td>
<td>{data.dalc}</td>
<td>{data.dglc}</td>
<td>{data.maxAu}</td>
{Array.from({ length: 24 }, (_, i) => (
<td key={i}>{data['h' + i]}</td>
))}
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default DailyMedalContent;

View File

@@ -1,7 +1,7 @@
import { styled } from 'styled-components';
import { useEffect, useState } from 'react';
import DecoSearchBar from '../../components/IndexManage/DecoSearchBar';
import DecoSearchBar from '../searchBar/DecoSearchBar';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';

View File

@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import InstanceSearchBar from '../../components/IndexManage/InstanceSearchBar';
import InstanceSearchBar from '../searchBar/InstanceSearchBar';
import { InstanceIndexExport, InstanceIndexView } from '../../apis';
const InstanceContent = () => {

View File

@@ -0,0 +1,152 @@
import React, { Fragment, useMemo, useRef } from 'react';
import {
TableStyle,
FormWrapper,
TableWrapper, ListOption, TextInput, TableInfoContent, Notice,
} from '../../styles/Components';
import { ItemAcquireIndexSearchBar, useItemAcquireIndexSearch } from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { numberFormatter } from '../../utils';
import { useTranslation } from 'react-i18next';
import { AnimatedPageWrapper } from '../common/Layout';
import CSVDownloadButton from '../common/button/CsvDownButton';
const ItemAcquireContent = () => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
updateSearchParams
} = useItemAcquireIndexSearch(token);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '100px' },
{ id: 'mail', label: '우편', width: '80px' },
{ id: 'shopPurchase', label: '상점 구매', width: '80px' },
{ id: 'shopRePurchase', label: '상점 재구매', width: '80px' },
{ id: 'itemBuy', label: '아이템 구매', width: '80px' },
{ id: 'itemUse', label: '아이템 사용', width: '80px' },
{ id: 'seasonPass', label: '시즌 패스', width: '80px' },
{ id: 'claim', label: '클레임', width: '80px' },
{ id: 'quest', label: '퀘스트', width: '80px' },
{ id: 'ugq', label: 'UGQ', width: '80px' },
{ id: 'battleObject', label: '배틀맵', width: '80px' },
{ id: 'runRace', label: '런레이스', width: '80px' },
{ id: 'prop', label: '보급품 상자', width: '80px' },
{ id: 'randomBox', label: '랜덤박스', width: '80px' },
{ id: 'beacon', label: '비컨', width: '80px' },
{ id: 'beaconShop', label: '비컨 상점', width: '80px' },
{ id: 'myHome', label: '마이홈', width: '80px' },
{ id: 'craft', label: '크래프트', width: '80px' },
{ id: 'etc', label: '기타', width: '80px' },
{ id: 'summary', label: '합계', width: '80px' },
];
}, []);
return (
<AnimatedPageWrapper>
<FormWrapper>
<ItemAcquireIndexSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo >
{dataList?.item_list && dataList.item_list.length > 0 &&
<TableInfoContent>
<TextInput
type="text"
value={dataList.item_list[0].itemId}
width="100px"
readOnly
/>
<TextInput
type="text"
value={dataList.item_list[0].itemName}
width="150px"
readOnly
/>
<Notice $color='#F15F5F'>* 확인되지 않은 액션이 있을 있습니다</Notice>
</TableInfoContent>
}
<ListOption>
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_ITEM_ACQUIRE')} />
</ListOption>
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => {
return (
<th
key={header.id}
width={header.width}
colSpan={header.colSpan}
>
{header.label}
</th>
);
})}
</tr>
</thead>
<tbody>
{dataList?.item_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.MailTaken)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopPurchase)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopRePurchase)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemBuy)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemUse)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.SeasonPassTakeReward)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ClaimReward)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.QuestMainReward || 0) + (item.actionSummary.QuestTaskUpdate || 0) + (item.actionSummary.QuestMainTask || 0))}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.UgqAbort)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.BattleRoundStateUpdate || 0) + (item.actionSummary.BattlePodCombatOccupyReward || 0) + (item.actionSummary.BattleObjectInteraction || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.RunRaceFinishReward || 0) + (item.actionSummary.RunRaceRespawnReward || 0))}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.RewardProp)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconCreate || 0) + (item.actionSummary.BeaconEdit || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconShopPurchaseItem || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.DeleteMyhome || 0) + (item.actionSummary.SaveMyhome || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.CraftFinish || 0) + (item.actionSummary.CraftStop || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.CheatCommandItem || 0) + (item.actionSummary.CharacterAppearanceUpdate || 0)
+ (item.actionSummary.ItemTattooLevelUp || 0) + (item.actionSummary.UserCreate || 0) + (item.actionSummary.JoinInstance || 0))}</td>
<td>{numberFormatter.formatCurrency(item.totalDeltaCount)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default ItemAcquireContent;

View File

@@ -0,0 +1,119 @@
import React, { Fragment, useMemo, useRef } from 'react';
import {
TableStyle,
FormWrapper,
TableWrapper, ListOption
} from '../../styles/Components';
import {
AssetsIndexSearchBar, useAssetsIndexSearch,
} from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { numberFormatter } from '../../utils';
import { useTranslation } from 'react-i18next';
import { AnimatedPageWrapper } from '../common/Layout';
import CSVDownloadButton from '../common/button/CsvDownButton';
import DailyDashBoard from './DailyCaliumDashBoard';
const ItemAssetsContent = () => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
updateSearchParams
} = useAssetsIndexSearch(token);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '100px' },
{ id: 'userCount', label: '유저수', width: '100px' },
{ id: 'item_13080002', label: '퀘스트 메달', width: '100px' },
{ id: 'item_13080004', label: '보급품 메달', width: '100px' },
{ id: 'item_13080005', label: '제작 메달', width: '100px' },
{ id: 'item_13080006', label: '에테론 315 포드', width: '100px' },
{ id: 'item_13080007', label: '에테론 316 포드', width: '100px' },
{ id: 'item_13080008', label: '에테론 317 포드', width: '100px' },
{ id: 'item_13080009', label: '에테론 318 포드', width: '100px' },
{ id: 'item_11570001', label: '강화 잉크', width: '100px' },
{ id: 'item_11570002', label: '연성 잉크', width: '100px' }
];
}, []);
return (
<AnimatedPageWrapper>
<DailyDashBoard />
<FormWrapper>
<AssetsIndexSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo >
<ListOption>
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_ASSETS_ITEM')} />
</ListOption>
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => {
return (
<th
key={header.id}
width={header.width}
colSpan={header.colSpan}
>
{header.label}
</th>
);
})}
</tr>
</thead>
<tbody>
{dataList?.assets_list?.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{data.logDay}</td>
<td>{numberFormatter.formatCurrency(data.userCount)}</td>
<td>{numberFormatter.formatCurrency(data.item_13080002)}</td>
<td>{numberFormatter.formatCurrency(data.item_13080004)}</td>
<td>{numberFormatter.formatCurrency(data.item_13080005)}</td>
<td>{numberFormatter.formatCurrency(data.item_13080006)}</td>
<td>{numberFormatter.formatCurrency(data.item_13080007)}</td>
<td>{numberFormatter.formatCurrency(data.item_13080008)}</td>
<td>{numberFormatter.formatCurrency(data.item_13080009)}</td>
<td>{numberFormatter.formatCurrency(data.item_11570001)}</td>
<td>{numberFormatter.formatCurrency(data.item_11570002)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default ItemAssetsContent;

View File

@@ -0,0 +1,143 @@
import React, { Fragment, useMemo, useRef } from 'react';
import {
TableStyle,
FormWrapper,
TableWrapper, ListOption, TableInfoContent, TextInput, Label, Notice,
} from '../../styles/Components';
import {
ItemAcquireIndexSearchBar,
ItemConsumeIndexSearchBar,
useItemAcquireIndexSearch,
useItemConsumeIndexSearch,
} from '../searchBar';
import { TopButton, ViewTableInfo } from '../common';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { numberFormatter } from '../../utils';
import { useTranslation } from 'react-i18next';
import { AnimatedPageWrapper } from '../common/Layout';
import CSVDownloadButton from '../common/button/CsvDownButton';
import styled from 'styled-components';
const ItemConsumeContent = () => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const tableRef = useRef(null);
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
updateSearchParams
} = useItemConsumeIndexSearch(token);
const tableHeaders = useMemo(() => {
return [
{ id: 'logDay', label: '일자', width: '100px' },
{ id: 'shopSell', label: '상점 판매', width: '80px' },
{ id: 'itemUse', label: '아이템 사용', width: '80px' },
{ id: 'beaconShop', label: '비컨상점', width: '80px' },
{ id: 'beacon', label: '비컨', width: '80px' },
{ id: 'quest', label: '퀘스트', width: '80px' },
{ id: 'ugq', label: 'UGQ', width: '80px' },
{ id: 'randomBox', label: '랜덤박스', width: '80px' },
{ id: 'myHome', label: '마이홈', width: '80px' },
{ id: 'craft', label: '크래프트', width: '80px' },
{ id: 'etc', label: '기타', width: '80px' },
{ id: 'summary', label: '합계', width: '80px' },
];
}, []);
return (
<AnimatedPageWrapper>
<FormWrapper>
<ItemConsumeIndexSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo >
{dataList?.item_list && dataList.item_list.length > 0 &&
<TableInfoContent>
<TextInput
type="text"
value={dataList.item_list[0].itemId}
width="100px"
readOnly
/>
<TextInput
type="text"
value={dataList.item_list[0].itemName}
width="300px"
readOnly
/>
<Notice $color='#F15F5F'>* 확인되지 않은 액션이 있을 있습니다</Notice>
</TableInfoContent>
}
<ListOption>
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_ITEM_CONSUME')} />
</ListOption>
</ViewTableInfo>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
{tableHeaders.map(header => {
return (
<th
key={header.id}
width={header.width}
colSpan={header.colSpan}
>
{header.label}
</th>
);
})}
</tr>
</thead>
<tbody>
{dataList?.item_list?.map((item, index) => (
<Fragment key={index}>
<tr>
<td>{item.logDay}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ShopSell)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemUse)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconShopRegisterItem || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.BeaconEdit || 0) + (item.actionSummary.BeaconCreate || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.QuestTaskUpdate || 0))}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.UgqAbort)}</td>
<td>{numberFormatter.formatCurrency(item.actionSummary.ItemRandomBoxUse)}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.SaveMyhome || 0))}</td>
<td>{numberFormatter.formatCurrency((item.actionSummary.CraftStart || 0))}</td>
<td>{numberFormatter.formatCurrency(
(item.actionSummary.SummonParty || 0) + (item.actionSummary.ItemDestroy || 0) + (item.actionSummary.CreatePartyInstance || 0) + (item.actionSummary.ItemTattooChangeAttribute || 0)
+ (item.actionSummary.CheatCommandItem || 0) + (item.actionSummary.ItemDestoryByExpiration || 0) + (item.actionSummary.ItemDestroyByUser || 0) + (item.actionSummary.ItemTattooLevelUp || 0)
)}</td>
<td>{numberFormatter.formatCurrency(item.totalDeltaCount)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<TopButton />
</>
}
</AnimatedPageWrapper>
);
};
export default ItemConsumeContent;

View File

@@ -5,7 +5,7 @@ import Button from '../../components/common/button/Button';
import { SelectInput, TableStyle, TableInfo, ListOption, InputLabel, InputGroup } from '../../styles/Components';
import ItemSearchBar from '../../components/IndexManage/ItemSearchBar';
import ItemSearchBar from '../searchBar/ItemSearchBar';
import { ItemIndexExport, ItemIndexView } from '../../apis';
const ItemContent = () => {

View File

@@ -1,112 +0,0 @@
import { Fragment, useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { PlayTimeSearchBar } from '../../components/IndexManage/index';
import { PlaytimeIndexExport, PlaytimeIndexView } from '../../apis';
const PlayTimeContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// 이용자 지표 데이터
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await PlaytimeIndexView(token, startDateToLocal, endDateToLocal));
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_PlayTime_Index.xlsx';
PlaytimeIndexExport(token, fileName, sendDate, finishDate);
};
return (
<>
<PlayTimeSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th rowSpan="2" width="140">
일자
</th>
<th colSpan="4" width="520">
유저수
</th>
<th rowSpan="2" width="160">
누적 플레이타임()
</th>
<th rowSpan="2" width="160">
1인당 평균 플레이타임()
</th>
</tr>
<tr>
<th>30 이내</th>
<th>30 ~ 1시간</th>
<th>1시간 ~ 3시간</th>
<th>3시간 이상</th>
</tr>
</thead>
<tbody>
{dataList.playtime &&
dataList.playtime.map(time => (
<tr key={time.date}>
<td>{time.date}</td>
{time.user_cnt.map((cnt, index) => (
<td className="text-left" key={index}>
{cnt}
</td>
))}
<td className="text-left">{time.total_time}</td>
<td className="text-left">{time.average_time}</td>
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default PlayTimeContent;

View File

@@ -1,108 +1,76 @@
import { Fragment, useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import React, { Fragment, useRef } from 'react';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { RetentionSearchBar } from '../../components/IndexManage/index';
import { RetentionIndexExport, RetentionIndexView } from '../../apis';
import { AnimatedPageWrapper } from '../common/Layout';
import { useRetentionSearch, RetentionSearchBar } from '../searchBar';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { numberFormatter } from '../../utils';
import { useTranslation } from 'react-i18next';
import CSVDownloadButton from '../common/button/CsvDownButton';
const RetentionContent = () => {
const { t } = useTranslation();
const tableRef = useRef(null);
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(new Date(d.setDate(d.getDate() - 1)).setHours(24, 0, 0, 0));
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [retentionData, setRetention] = useState(1);
const {
searchParams,
loading: dataLoading,
data: dataList,
handleSearch,
handleReset,
updateSearchParams
} = useRetentionSearch(token);
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
const [excelBtn, setExcelBtn] = useState(true); //true 시 비활성화
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// Retention 지표 데이터
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 RetentionIndexView(token, startDateToLocal, endDateToLocal));
console.log(dataList);
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
setRetention(resultData.retention);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Retention_Index.xlsx';
if(!excelBtn){
RetentionIndexExport(token, fileName, sendDate, finishDate);
}
};
return (
<>
<RetentionSearchBar setResultData={setResultData} resultData={resultData}
handleSearch={handleSearch} fetchData={fetchData} setRetention={setRetention} setExcelBtn={setExcelBtn} />
<AnimatedPageWrapper>
<RetentionSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
<TableInfo>
<ListOption>
<Button
theme={excelBtn === true ? "disable" : "line"}
text="엑셀 다운로드"
disabled={handleXlsxExport}
handleClick={handleXlsxExport} />
<CSVDownloadButton tableRef={tableRef} fileName={t('FILE_INDEX_USER_RETENTION')} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead>
<tr>
{/* <th width="100">국가</th> */}
<th width="150">일자</th>
<th className="cell-nru">NRU</th>
{[...Array(Number(retentionData))].map((value, index) => {
return <th key={index}>{`D+${index + 1}`}</th>;
})}
</tr>
</thead>
<tbody>
{dataList.retention &&
dataList.retention.map(data => (
<tr className="cell-nru-th" key={data.date}>
<td>{data.date}</td>
{data['d-day'].map((day, index) => (
<td key={index}>{day.dif}</td>
))}
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
<IndexTableWrap>
<TableStyle ref={tableRef}>
<caption></caption>
<thead>
<tr>
<th>일자</th>
<th>NRU</th>
<th>D+1</th>
<th>D+7</th>
<th>D+30</th>
</tr>
</thead>
<tbody>
{dataList?.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{data.logDay}</td>
<td>{data.totalCreated}</td>
<td>{numberFormatter.formatPercent(data.d1_rate)}</td>
<td>{numberFormatter.formatPercent(data.d7_rate)}</td>
<td>{numberFormatter.formatPercent(data.d30_rate)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
}
</AnimatedPageWrapper>
);
};

View File

@@ -1,166 +0,0 @@
import { useEffect, useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper, AlertText } from '../../styles/Components';
const RetentionSearchBar = ({ resultData, setResultData, handleSearch, fetchData, setRetention, setExcelBtn }) => {
// 초기 날짜 세팅
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(new Date(d.setDate(d.getDate() - 1)).setHours(24, 0, 0, 0));
const [errorMessage, setErrorMessage] = useState('');
const [period, setPeriod] = useState(0);
// resultData에 임의 날짜 넣어주기
useEffect(() => {
setResultData({
send_dt: START_DATE,
end_dt: '',
retention: 0,
});
}, []);
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
const resultEndDate = new Date(resultSendData);
resultEndDate.setDate(resultEndDate.getDate() + Number(resultData.retention));
setResultData({ ...resultData, send_dt: resultSendData, end_dt: resultEndDate });
};
// // 발송 날짜 세팅 로직
// const handleEndDate = data => {
// const endDate = new Date(data);
// const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
// setResultData({ ...resultData, end_dt: resultSendData });
// };
// Retention 세팅 로직
const handleRetention = e => {
const value = e.target.value;
const resultEndDate = new Date(resultData.send_dt);
resultEndDate.setDate(resultEndDate.getDate() + Number(value));
setResultData({ ...resultData, end_dt: resultEndDate, retention: value });
setPeriod(value);
};
//Retention 범위 선택 후 disable 처리 로직
const handleSearchBtn = e => {
e.preventDefault();
if(period == 0) {
setErrorMessage("필수값을 선택하세요.");
return false;
} else {
setErrorMessage("");
setExcelBtn(false); //활성화
handleSearch(resultData.send_dt, resultData.end_dt);
}
}
const handleReset = e => {
e.preventDefault();
setResultData({ send_dt: START_DATE, end_dt: '', retention: 0 });
setRetention(1);
setErrorMessage("");
setPeriod(1);
setExcelBtn(true); //비활성화
fetchData(START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>집계 기준일</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자" selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자"
selectedDate={resultData.end_dt}
maxDate={new Date()}
readOnly={true}
disabled={true}
type="retention" />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<SearchItem>
<InputLabel>Retention 범위</InputLabel>
<SelectInput
onChange={e => handleRetention(e)} value={resultData.retention}>
<option value={0}>선택</option>
<option value={1}>D+1</option>
<option value={7}>D+7</option>
<option value={30}>D+30</option>
</SelectInput>
</SearchItem>
{/* 기획 보류 */}
{/* <SearchItem>
<InputLabel>조회 국가</InputLabel>
<SelectInput>
<option value="">ALL</option>
<option value="">KR</option>
<option value="">EN</option>
<option value="">JP</option>
<option value="">TH</option>
</SelectInput>
</SearchItem> */}
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} />
<Button
theme="search"
text="검색"
handleClick={handleSearchBtn}
/>
<AlertText>{errorMessage}</AlertText>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default RetentionSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -1,90 +0,0 @@
import { Fragment, useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { SegmentSearchBar } from '../../components/IndexManage/index';
import { SegmentIndexExport, SegmentIndexView } from '../../apis';
const SegmentContent = () => {
const token = sessionStorage.getItem('token');
const END_DATE = new Date();
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [finishDate, setFinishDate] = useState(END_DATE);
useEffect(() => {
fetchData(END_DATE);
}, []);
// Retention 지표 데이터
const fetchData = async endDate => {
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await SegmentIndexView(token, endDateToLocal));
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = end_dt => {
fetchData(end_dt);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Segment_Index.xlsx';
SegmentIndexExport(token, fileName, finishDate);
};
return (
<>
<SegmentSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th colSpan="1" width="200">
{dataList && dataList.start_dt} ~ {dataList && dataList.end_dt}
</th>
<th colSpan="2" width="400">
KIP
</th>
</tr>
<tr>
{/* <th>국가</th> */}
<th>세그먼트 분류</th>
<th>AU</th>
<th>AU Percentage by User Segment (%)</th>
</tr>
</thead>
<tbody>
{dataList && dataList.segment &&
dataList.segment.map((segment, index) => (
<tr key={index}>
{/* <td rowSpan="6">TH</td> */}
<td>{segment.type}</td>
<td>{segment.au}</td>
<td>{segment.dif}</td>
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default SegmentContent;

Some files were not shown because too many files have changed in this diff Show More