Compare commits
22 Commits
4bc8b34c26
...
1598fa93b6
| Author | SHA1 | Date | |
|---|---|---|---|
| 1598fa93b6 | |||
| 24eb59d937 | |||
| a74376108a | |||
| 80a3c0ab8a | |||
| f290f0dbf0 | |||
| 9221a06a8e | |||
| 2c693b2503 | |||
| 73f8448b24 | |||
| ffdfad1223 | |||
| 8bd7e8325d | |||
| daf4c62aa7 | |||
| a333f55f81 | |||
| 894eb17fd8 | |||
| cddd8e6333 | |||
| a0087a1e29 | |||
| f2d7c87f38 | |||
| 5d9b6871fb | |||
| 3efd663f0d | |||
| 04adce4aaf | |||
| 2330bbc393 | |||
| 2b2cec3d10 | |||
| 24e09a65bc |
@@ -9,11 +9,11 @@ import {
|
|||||||
AdminView,
|
AdminView,
|
||||||
AuthSetting,
|
AuthSetting,
|
||||||
AuthSettingUpdate,
|
AuthSettingUpdate,
|
||||||
CaliumRequest,
|
CaliumRequest, DataInitView,
|
||||||
LogView,
|
LogView,
|
||||||
} from './pages/UserManage';
|
} from './pages/UserManage';
|
||||||
import { EconomicIndex, UserIndex } from './pages/IndexManage';
|
import { EconomicIndex, UserIndex } from './pages/IndexManage';
|
||||||
import { LandView, CryptView, GameLogView, UserView } from './pages/DataManage';
|
import { LandInfoView, CryptView, GameLogView, UserView, BusinessLogView, } from './pages/DataManage';
|
||||||
import {
|
import {
|
||||||
Board,
|
Board,
|
||||||
Event,
|
Event,
|
||||||
@@ -49,6 +49,7 @@ const RouteInfo = () => {
|
|||||||
<Route path="authsetting" element={<AuthSetting />} />
|
<Route path="authsetting" element={<AuthSetting />} />
|
||||||
<Route path="authsetting/:id" element={<AuthSettingUpdate />} />
|
<Route path="authsetting/:id" element={<AuthSettingUpdate />} />
|
||||||
<Route path="caliumrequest" element={<CaliumRequest />} />
|
<Route path="caliumrequest" element={<CaliumRequest />} />
|
||||||
|
<Route path="datainit" element={<DataInitView />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/indexmanage">
|
<Route path="/indexmanage">
|
||||||
<Route path="userindex" element={<UserIndex />} />
|
<Route path="userindex" element={<UserIndex />} />
|
||||||
@@ -56,9 +57,10 @@ const RouteInfo = () => {
|
|||||||
</Route>
|
</Route>
|
||||||
<Route path="/datamanage">
|
<Route path="/datamanage">
|
||||||
<Route path="userview" element={<UserView />} />
|
<Route path="userview" element={<UserView />} />
|
||||||
<Route path="landview" element={<LandView />} />
|
<Route path="landview" element={<LandInfoView />} />
|
||||||
<Route path="gamelogview" element={<GameLogView />} />
|
<Route path="gamelogview" element={<GameLogView />} />
|
||||||
<Route path="cryptview" element={<CryptView />} />
|
<Route path="cryptview" element={<CryptView />} />
|
||||||
|
<Route path="businesslogview" element={<BusinessLogView />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/servicemanage">
|
<Route path="/servicemanage">
|
||||||
<Route path="board" element={<Board />} />
|
<Route path="board" element={<Board />} />
|
||||||
|
|||||||
32
src/apis/Data.js
Normal file
32
src/apis/Data.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//사용자 관리 - 데이터 api 연결
|
||||||
|
|
||||||
|
import { Axios } from '../utils';
|
||||||
|
|
||||||
|
|
||||||
|
export const InitData = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post('/api/v1/data/init-data', params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('InitData Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InitHistoryList = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post(`/api/v1/data/init-list`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('InitHistoryList Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -21,6 +21,24 @@ export const LandAuctionView = async (token, landType, landData, userType, userD
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const LandInfoData = async (token, landType, landData, landSize, category, status, startDate, endDate, order, size, currentPage) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(
|
||||||
|
`/api/v1/land/info?land_type=${landType}&land_data=${landData}&land_size=${landSize}&category=${category}&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('LandInfoData Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 랜드 경매 상세보기
|
// 랜드 경매 상세보기
|
||||||
export const LandAuctionDetailView = async (token, id) => {
|
export const LandAuctionDetailView = async (token, id) => {
|
||||||
try {
|
try {
|
||||||
@@ -51,6 +69,21 @@ export const LandAuctionSingleRegist = async (token, params) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 랜드 소유권 변경 등록
|
||||||
|
export const LandOwnedChangesRegist = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post(`/api/v1/land/change`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('LandAuctionSingleRegist Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 랜드 경매 수정
|
// 랜드 경매 수정
|
||||||
export const LandAuctionModify = async (token, id, params) => {
|
export const LandAuctionModify = async (token, id, params) => {
|
||||||
try {
|
try {
|
||||||
@@ -66,6 +99,21 @@ export const LandAuctionModify = async (token, id, params) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 랜드 소유권 변경 수정
|
||||||
|
export const LandOwnedChangesModify = async (token, id, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.put(`/api/v1/land/change/${id}`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('LandAuctionModify Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 랜드 경매 삭제
|
// 랜드 경매 삭제
|
||||||
export const LandAuctionDelete = async (token, params, id) => {
|
export const LandAuctionDelete = async (token, params, id) => {
|
||||||
try {
|
try {
|
||||||
@@ -82,6 +130,22 @@ export const LandAuctionDelete = async (token, params, id) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 랜드 소유권 변경 예약 삭제
|
||||||
|
export const LandOwnerChangesDelete = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.delete(`/api/v1/land/change/delete`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
data: params,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('LandAuctionDelete Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const LandView = async (token) => {
|
export const LandView = async (token) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.get(
|
const res = await Axios.get(
|
||||||
|
|||||||
18
src/apis/Log.js
Normal file
18
src/apis/Log.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//운영 정보 관리 - 로그 api 연결
|
||||||
|
|
||||||
|
import { Axios } from '../utils';
|
||||||
|
|
||||||
|
// 비즈니스 로그 조회
|
||||||
|
export const BusinessLogList = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post(`/api/v1/log/generic/list`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('BusinessLogList Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -65,6 +65,20 @@ export const UserChangeAdminLevel = async (token, params) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const UserKick = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.put('/api/v1/users/user-kick', params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('UserKick Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 아바타 조회
|
// 아바타 조회
|
||||||
export const UserAvatarView = async (token, guid) => {
|
export const UserAvatarView = async (token, guid) => {
|
||||||
try {
|
try {
|
||||||
@@ -187,9 +201,9 @@ export const UserFriendListView = async (token, guid) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 우편 조회
|
// 우편 조회
|
||||||
export const UserMailView = async (token, guid, option) => {
|
export const UserMailView = async (token, params) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.get(`/api/v1/users/mail?guid=${guid}&type=${option}`, {
|
const res = await Axios.post(`/api/v1/users/mail`, params, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ export {
|
|||||||
landAuctionStatus,
|
landAuctionStatus,
|
||||||
landSearchType,
|
landSearchType,
|
||||||
CurrencyType,
|
CurrencyType,
|
||||||
languageType
|
languageType,
|
||||||
|
opLandCategoryType,
|
||||||
|
opLandOwnedType,
|
||||||
|
opSuccessType,
|
||||||
|
opPickupType,
|
||||||
|
opReadType,
|
||||||
|
opYNType,
|
||||||
|
opUserSessionType,
|
||||||
|
opMailType,
|
||||||
} from './options'
|
} from './options'
|
||||||
export {benItems, MinuteList, HourList, caliumRequestInitData, STATUS_STYLES, months} from './data'
|
export {benItems, MinuteList, HourList, caliumRequestInitData, STATUS_STYLES, months} from './data'
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { authType } from './types';
|
import { adminAuthLevel, authType } from './types';
|
||||||
|
|
||||||
export const menuConfig = {
|
export const menuConfig = {
|
||||||
usermanage: {
|
usermanage: {
|
||||||
@@ -11,13 +11,17 @@ export const menuConfig = {
|
|||||||
confirm: authType.adminSearchConfirm,
|
confirm: authType.adminSearchConfirm,
|
||||||
update: authType.adminSearchUpdate,
|
update: authType.adminSearchUpdate,
|
||||||
delete: authType.adminSearchDelete
|
delete: authType.adminSearchDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
logview: {
|
logview: {
|
||||||
title: '사용 이력 조회',
|
title: '사용 이력 조회',
|
||||||
permissions: {
|
permissions: {
|
||||||
read: authType.adminLogSearchRead
|
read: authType.adminLogSearchRead
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
authsetting: {
|
authsetting: {
|
||||||
title: '권한 설정',
|
title: '권한 설정',
|
||||||
@@ -25,14 +29,25 @@ export const menuConfig = {
|
|||||||
read: authType.authoritySettingRead,
|
read: authType.authoritySettingRead,
|
||||||
update: authType.authoritySettingUpdate,
|
update: authType.authoritySettingUpdate,
|
||||||
delete: authType.authoritySettingDelete
|
delete: authType.authoritySettingDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
caliumrequest: {
|
caliumrequest: {
|
||||||
title: '칼리움 요청',
|
title: '칼리움 요청',
|
||||||
permissions: {
|
permissions: {
|
||||||
read: authType.caliumRequestRead,
|
read: authType.caliumRequestRead,
|
||||||
update: authType.caliumRequestUpdate
|
update: authType.caliumRequestUpdate
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
|
},
|
||||||
|
datainit: {
|
||||||
|
title: '데이터 초기화',
|
||||||
|
permissions: {},
|
||||||
|
view: false,
|
||||||
|
test: true,
|
||||||
|
authLevel: adminAuthLevel.MASTER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -43,13 +58,17 @@ export const menuConfig = {
|
|||||||
title: '유저 지표',
|
title: '유저 지표',
|
||||||
permissions: {
|
permissions: {
|
||||||
read: authType.userIndicatorsRead
|
read: authType.userIndicatorsRead
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
economicindex: {
|
economicindex: {
|
||||||
title: '경제 지표',
|
title: '경제 지표',
|
||||||
permissions: {
|
permissions: {
|
||||||
read: authType.economicIndicatorsRead
|
read: authType.economicIndicatorsRead
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -62,27 +81,43 @@ export const menuConfig = {
|
|||||||
read: authType.userSearchRead,
|
read: authType.userSearchRead,
|
||||||
update: authType.userSearchUpdate,
|
update: authType.userSearchUpdate,
|
||||||
delete: authType.userSearchDelete
|
delete: authType.userSearchDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
landview: {
|
landview: {
|
||||||
title: '랜드 조회',
|
title: '랜드 정보 조회',
|
||||||
permissions: {
|
permissions: {
|
||||||
read: authType.landRead,
|
read: authType.landRead,
|
||||||
update: authType.landUpdate,
|
update: authType.landUpdate,
|
||||||
delete: authType.landDelete
|
delete: authType.landDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
gamelogview: {
|
gamelogview: {
|
||||||
title: '게임 로그 조회',
|
title: '게임 로그 조회',
|
||||||
permissions: {
|
permissions: {
|
||||||
read: authType.gameLogRead
|
read: authType.gameLogRead
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
cryptview: {
|
cryptview: {
|
||||||
title: '크립토 조회',
|
title: '크립토 조회',
|
||||||
permissions: {
|
permissions: {
|
||||||
read: authType.cryptoRead
|
read: authType.cryptoRead
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
|
},
|
||||||
|
businesslogview: {
|
||||||
|
title: '비즈니스 로그 조회',
|
||||||
|
permissions: {
|
||||||
|
read: authType.businessLogRead
|
||||||
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -95,7 +130,9 @@ export const menuConfig = {
|
|||||||
read: authType.inGameRead,
|
read: authType.inGameRead,
|
||||||
update: authType.inGameUpdate,
|
update: authType.inGameUpdate,
|
||||||
delete: authType.inGameDelete
|
delete: authType.inGameDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
mail: {
|
mail: {
|
||||||
title: '우편',
|
title: '우편',
|
||||||
@@ -103,7 +140,9 @@ export const menuConfig = {
|
|||||||
read: authType.mailRead,
|
read: authType.mailRead,
|
||||||
update: authType.mailUpdate,
|
update: authType.mailUpdate,
|
||||||
delete: authType.mailDelete
|
delete: authType.mailDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
userblock: {
|
userblock: {
|
||||||
title: '이용자 제재',
|
title: '이용자 제재',
|
||||||
@@ -111,7 +150,9 @@ export const menuConfig = {
|
|||||||
read: authType.blackListRead,
|
read: authType.blackListRead,
|
||||||
update: authType.blackListUpdate,
|
update: authType.blackListUpdate,
|
||||||
delete: authType.blackListDelete
|
delete: authType.blackListDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
reportlist: {
|
reportlist: {
|
||||||
title: '신고내역',
|
title: '신고내역',
|
||||||
@@ -119,7 +160,9 @@ export const menuConfig = {
|
|||||||
read: authType.reportRead,
|
read: authType.reportRead,
|
||||||
update: authType.reportUpdate,
|
update: authType.reportUpdate,
|
||||||
delete: authType.reportDelete
|
delete: authType.reportDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
event: {
|
event: {
|
||||||
title: '보상 이벤트 관리',
|
title: '보상 이벤트 관리',
|
||||||
@@ -127,7 +170,9 @@ export const menuConfig = {
|
|||||||
read: authType.eventRead,
|
read: authType.eventRead,
|
||||||
update: authType.eventUpdate,
|
update: authType.eventUpdate,
|
||||||
delete: authType.eventDelete
|
delete: authType.eventDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
landauction: {
|
landauction: {
|
||||||
title: '랜드 경매 관리',
|
title: '랜드 경매 관리',
|
||||||
@@ -135,7 +180,9 @@ export const menuConfig = {
|
|||||||
read: authType.landAuctionRead,
|
read: authType.landAuctionRead,
|
||||||
update: authType.landAuctionUpdate,
|
update: authType.landAuctionUpdate,
|
||||||
delete: authType.landAuctionDelete
|
delete: authType.landAuctionDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
battleevent: {
|
battleevent: {
|
||||||
title: '전투시스템 타입 스케줄러',
|
title: '전투시스템 타입 스케줄러',
|
||||||
@@ -143,7 +190,9 @@ export const menuConfig = {
|
|||||||
read: authType.battleEventRead,
|
read: authType.battleEventRead,
|
||||||
update: authType.battleEventUpdate,
|
update: authType.battleEventUpdate,
|
||||||
delete: authType.battleEventDelete
|
delete: authType.battleEventDelete
|
||||||
}
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,12 @@ export const userSearchType = [
|
|||||||
{ value: 'NAME', name: '닉네임' },
|
{ value: 'NAME', name: '닉네임' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const userSearchType2 = [
|
||||||
|
{ value: 'GUID', name: 'GUID' },
|
||||||
|
{ value: 'NICKNAME', name: '닉네임' },
|
||||||
|
{ value: 'ACCOUNT', name: 'Account ID' },
|
||||||
|
];
|
||||||
|
|
||||||
export const landSearchType = [
|
export const landSearchType = [
|
||||||
{ value: 'ID', name: '랜드ID' },
|
{ value: 'ID', name: '랜드ID' },
|
||||||
{ value: 'NAME', name: '랜드명' },
|
{ value: 'NAME', name: '랜드명' },
|
||||||
@@ -206,3 +212,658 @@ export const eventSearchType = [
|
|||||||
|
|
||||||
export const battleEventRoundCount = [1,2,3,4,8,12,16];
|
export const battleEventRoundCount = [1,2,3,4,8,12,16];
|
||||||
export const battleEventHotTime = [1,2,3,4,5,6,7,8];
|
export const battleEventHotTime = [1,2,3,4,5,6,7,8];
|
||||||
|
|
||||||
|
export const opLandOwnedType = [
|
||||||
|
{ value: true, name: '가능' },
|
||||||
|
{ value: false, name: '불가능' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opLandCategoryType = [
|
||||||
|
{ value: 'ALL', name: '전체' },
|
||||||
|
{ value: 'public', name: '공공 임대형' },
|
||||||
|
{ value: 'auction', name: '경매' },
|
||||||
|
{ value: 'event', name: '이벤트 지급' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opLandInfoStatusType = [
|
||||||
|
{ value: 'ALL', name: '전체' },
|
||||||
|
{ value: 'NONE', name: '' },
|
||||||
|
{ value: 'OWNED', name: '지급 완료' },
|
||||||
|
{ value: 'OWNED_WAIT', name: '지급 예약' },
|
||||||
|
{ value: 'AUCTION_END', name: '경매 완료' },
|
||||||
|
{ value: 'AUCTION_WAIT', name: '경매 대기' },
|
||||||
|
{ value: 'AUCTION_RUNNING', name: '경매 진행' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opInitDataType = [
|
||||||
|
{ value: 'None', name: '' },
|
||||||
|
{ value: 'LandAuction', name: '랜드경매' },
|
||||||
|
{ value: 'LandOwned', name: '랜드소유자' },
|
||||||
|
{ value: 'LandDesc', name: '랜드문구' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opSuccessType = [
|
||||||
|
{ value: true, name: '성공' },
|
||||||
|
{ value: false, name: '실패' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opUserSessionType = [
|
||||||
|
{ value: true, name: '접속중' },
|
||||||
|
{ value: false, name: '미접속' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opYNType = [
|
||||||
|
{ value: true, name: 'Y' },
|
||||||
|
{ value: false, name: 'N' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opReadType = [
|
||||||
|
{ value: true, name: '확인' },
|
||||||
|
{ value: false, name: '미확인' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opPickupType = [
|
||||||
|
{ value: true, name: '수령' },
|
||||||
|
{ value: false, name: '미수령' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opMailType = [
|
||||||
|
{ value: 'RECEIVE', name: '받은 우편' },
|
||||||
|
{ value: 'SEND', name: '보낸 우편' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const opInputType = [
|
||||||
|
{ value: 'String', name: '문자열' },
|
||||||
|
{ value: 'Number', name: '숫자' },
|
||||||
|
{ value: 'Boolean', name: '부울' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// export const logAction = [
|
||||||
|
// { value: "None", name: "ALL" },
|
||||||
|
// { value: "AIChatDeleteCharacter", name: "NPC 삭제" },
|
||||||
|
// { value: "AIChatDeleteUser", name: "유저 삭제" },
|
||||||
|
// { value: "AIChatGetCharacter", name: "NPC 조회" },
|
||||||
|
// { value: "AIChatIncentiveMarking", name: "인센티브 획득 마킹" },
|
||||||
|
// { value: "AIChatIncentiveSearch", name: "인센티브 조회" },
|
||||||
|
// { value: "AIChatJwtIssue", name: "Jwt 토큰 발행" },
|
||||||
|
// { value: "AIChatJwtVerify", name: "Jwt 토큰 확인" },
|
||||||
|
// { value: "AIChatPointCharge", name: "포인트 충전" },
|
||||||
|
// { value: "AIChatPointChargeVerify", name: "포인트 충전 확인" },
|
||||||
|
// { value: "AIChatRegisterCharacter", name: "NPC 등록" },
|
||||||
|
// { value: "AIChatRegisterUser", name: "유저 등록" },
|
||||||
|
// { value: "AIChatUpdateCharacter", name: "NPC 정보 업데이트" },
|
||||||
|
// { value: "BanParty", name: "파티 추방" },
|
||||||
|
// { value: "BattleInstanceJoin", name: "배틀 인스턴스 조인" },
|
||||||
|
// { value: "BattleInstanceSnapshotCreate", name: "배틀 인스턴스 스냅샷 생성" },
|
||||||
|
// { value: "BattleInstanceSnapshotSave", name: "배틀 인스턴스 스냅샷 저장" },
|
||||||
|
// { value: "BattleObjectInteraction", name: "배틀 오브젝트 상호작용" },
|
||||||
|
// { value: "BattleObjectStateUpdate", name: "배틀 오브젝트 상태 변경" },
|
||||||
|
// { value: "BattlePodCombatOccupyReward", name: "포드 컴뱃 소유 보상" },
|
||||||
|
// { value: "BattleRoundStateUpdate", name: "배틀 라운드 스테이트 업데이트" },
|
||||||
|
// { value: "BattleUserDead", name: "유저 데드" },
|
||||||
|
// { value: "BattleUserRespawn", name: "배틀 리스폰" },
|
||||||
|
// { value: "BeaconAppearanceCustomize", name: "비컨 외형 커스터마이징" },
|
||||||
|
// { value: "BeaconCreate", name: "비컨 생성" },
|
||||||
|
// { value: "BeaconEdit", name: "비컨 편집" },
|
||||||
|
// { value: "BeaconSell", name: "비컨 매각" },
|
||||||
|
// { value: "BrokerApiAdmin", name: "BrokerApi 어드민 재화 지급" },
|
||||||
|
// { value: "BrokerApiPlanetAuth", name: "BrokerApi Planet 인증" },
|
||||||
|
// { value: "BrokerApiUserExchangeComplete", name: "BrokerApi 재화 교환 완료" },
|
||||||
|
// { value: "BrokerApiUserExchangeOrder", name: "BrokerApi 재화 교환 주문" },
|
||||||
|
// { value: "BrokerApiUserLogin", name: "BrokerApi 유저 로그인" },
|
||||||
|
// { value: "BuffAdd", name: "버프 추가" },
|
||||||
|
// { value: "BuffDelete", name: "버프 제거" },
|
||||||
|
// { value: "CaliumSyncEchoSystem", name: "칼리움 동기화 처리" },
|
||||||
|
// { value: "CancelFriendRequest", name: "친구요청 취소" },
|
||||||
|
// { value: "CartAdd", name: "장바구니 추가" },
|
||||||
|
// { value: "CartDelete", name: "장바구니 삭제" },
|
||||||
|
// { value: "CartPurchase", name: "장바구니 구매" },
|
||||||
|
// { value: "ChangeConvertCaliumInfo", name: "칼리움 정보 변환" },
|
||||||
|
// { value: "CharacterAppearanceCustomize", name: "캐리터 외형 커스터마이징" },
|
||||||
|
// { value: "CharacterAppearanceUpdate", name: "캐릭터 외형 갱신" },
|
||||||
|
// { value: "CharacterCreate", name: "캐릭터 생성(자동 생성)" },
|
||||||
|
// { value: "CharacterLoading", name: "캐릭터 로딩" },
|
||||||
|
// { value: "ChatChannel", name: "채널 채팅" },
|
||||||
|
// { value: "ChatNormal", name: "노말 채팅" },
|
||||||
|
// { value: "ChatNotice", name: "전서버 채팅" },
|
||||||
|
// { value: "ChatParty", name: "파티 채팅" },
|
||||||
|
// { value: "ChatWhisper", name: "귓속말 채팅" },
|
||||||
|
// { value: "CheatAllCraftFinish", name: "치트로 인한 모든 제작 시간 단축" },
|
||||||
|
// { value: "CheatCommandChangeNickName", name: "치트로 인한 캐릭터 명 변경" },
|
||||||
|
// { value: "CheatCommandCharacterInit", name: "치트로 인한 캐릭터 초기화" },
|
||||||
|
// { value: "CheatCommandClaimReset", name: "치트로 인한 클레임 리워드 리셋" },
|
||||||
|
// { value: "CheatCommandClaimUpdate", name: "치트로 인한 클레임 리워드 대기시간 단축" },
|
||||||
|
// { value: "CheatCommandCraftHelpInit", name: "치트로 인한 제작 헬프 초기화" },
|
||||||
|
// { value: "CheatCommandDeleteQuest", name: "치트로 인한 퀘스트 삭제" },
|
||||||
|
// { value: "CheatCommandGainLand", name: "치트로 인한 랜드 획득" },
|
||||||
|
// { value: "CheatCommandIncreaseExp", name: "치트로 인한 시즌 패스 경험치 증가" },
|
||||||
|
// { value: "CheatCommandItem", name: "치트로 인한 아이템 추가 삭제" },
|
||||||
|
// { value: "CheatCommandItemLevelUp", name: "치트로 인한 아이템 레벨 업" },
|
||||||
|
// { value: "CheatCommandLandAuctionBlindSet", name: "치트로 인한 랜드 경매 블라이인드 입찰 설정" },
|
||||||
|
// { value: "CheatCommandLandAuctionCanel", name: "치트로 인한 랜드 경매 취소" },
|
||||||
|
// { value: "CheatCommandLandAuctionReserve", name: "치트로 인한 랜드 경매 예약" },
|
||||||
|
// { value: "CheatCommandLandAuctionStart", name: "치트로 인한 랜드 경매 시작" },
|
||||||
|
// { value: "CheatCommandPackageSend", name: "치트로 인한 패키지 메일 전송" },
|
||||||
|
// { value: "CheatCommandQuestAccept", name: "치트로 인한 퀘스트 할당" },
|
||||||
|
// { value: "CheatCommandQuestComplete", name: "치트로 인한 퀘스트 완료" },
|
||||||
|
// { value: "CheatCommandRegisterCraftRecipe", name: "치트로 인한 레시피 등록" },
|
||||||
|
// { value: "CheatCommandResetAllQuest", name: "치트로 인한 퀘스트 리셋" },
|
||||||
|
// { value: "CheatCommandResetEscapePosition", name: "치트로 인한 탈출시간 리셋" },
|
||||||
|
// { value: "CheatCommandResetMailCount", name: "치트로 인한 메일 횟수 제한 초기화" },
|
||||||
|
// { value: "CheatCommandSeasonPassInit", name: "치트로 인한 시즌 패스 초기화" },
|
||||||
|
// { value: "CheatCommandSendMail", name: "치트로 인한 메일 발송" },
|
||||||
|
// { value: "CheatCommandShopProductInit", name: "치트로 인한 상점 품목 초기화" },
|
||||||
|
// { value: "CheatCommandShopProductRenewal", name: "치트로 인한 상점 갱신 전송" },
|
||||||
|
// { value: "ClaimReward", name: "클레임 리워드 이벤트 보상" },
|
||||||
|
// { value: "ConvertCalium", name: "칼리움 컨버터 변환" },
|
||||||
|
// { value: "ConvertExchangeCalium", name: "칼리움 교환소 변환" },
|
||||||
|
// { value: "CraftFinish", name: "제작 완료" },
|
||||||
|
// { value: "CraftHelp", name: "제작 도움" },
|
||||||
|
// { value: "CraftRecipeRegister", name: "제작 레시피 추가" },
|
||||||
|
// { value: "CraftStart", name: "제작 시작" },
|
||||||
|
// { value: "CraftStop", name: "제작 취소" },
|
||||||
|
// { value: "CreateCaliumContent", name: "칼리움 컨텐츠 생성" },
|
||||||
|
// { value: "CreateParty", name: "파티 생성" },
|
||||||
|
// { value: "CreatePartyInstance", name: "파티 던전 생성" },
|
||||||
|
// { value: "DailyQuestCheck", name: "데일리 퀘스트 체크" },
|
||||||
|
// { value: "DanceEntityStateEnd", name: "캐릭터 엔티티 스테이트 댄스 종료" },
|
||||||
|
// { value: "DanceEntityStateStart", name: "캐릭터 엔티티 스테이트 댄스 시작" },
|
||||||
|
// { value: "DeleteMyhome", name: "마이홈 삭제" },
|
||||||
|
// { value: "DestroyParty", name: "파티 파괴" },
|
||||||
|
// { value: "EndPartyVote", name: "파티 투표 종료" },
|
||||||
|
// { value: "EnterMyhome", name: "마이홈 입장" },
|
||||||
|
// { value: "EnterMyhomeEditRoom", name: "마이홈 에디트 룸 입장" },
|
||||||
|
// { value: "FailCaliumEchoSystem", name: "칼리움 에코시스템 실패" },
|
||||||
|
// { value: "FailCaliumStorageRollBack", name: "칼리움 컨버터 롤백 실패" },
|
||||||
|
// { value: "FarmingCancel", name: "파밍 취소" },
|
||||||
|
// { value: "FarmingComplete", name: "파밍 완료" },
|
||||||
|
// { value: "FarmingIncompletedReward", name: "파밍 미완료 보상" },
|
||||||
|
// { value: "FarmingStart", name: "파밍 시작" },
|
||||||
|
// { value: "FillupCalium", name: "칼리움 총량 누적" },
|
||||||
|
// { value: "FriendAdd", name: "친구추가" },
|
||||||
|
// { value: "FriendDelete", name: "친구삭세" },
|
||||||
|
// { value: "GainLandProfit", name: "랜드 수익 획득" },
|
||||||
|
// { value: "InviteParty", name: "파티 초대" },
|
||||||
|
// { value: "ItemBuy", name: "아이템 구매" },
|
||||||
|
// { value: "ItemDestroy", name: "아이템 제거" },
|
||||||
|
// { value: "ItemRandomBoxUse", name: "아이템 랜덤박스 사용" },
|
||||||
|
// { value: "ItemTattooChangeAttribute", name: "타투 아이템 속성변환" },
|
||||||
|
// { value: "ItemTattooLevelUp", name: "타투 아이템 강화" },
|
||||||
|
// { value: "ItemUse", name: "아이템 사용" },
|
||||||
|
// { value: "JoinInstance", name: "인스턴스 입장" },
|
||||||
|
// { value: "JoinParty", name: "파티 가입" },
|
||||||
|
// { value: "JoinPartyInstance", name: "파티 인스턴스 입장" },
|
||||||
|
// { value: "KickFriendsFromMyhome", name: "마이홈에서 친구 내쫒기" },
|
||||||
|
// { value: "LandAuctionActivity", name: "랜드 경매 활성화" },
|
||||||
|
// { value: "LandAuctionBid", name: "랜드 경매 입찰" },
|
||||||
|
// { value: "LandAuctionBidPriceRefund", name: "랜드 경매 입찰금 환급" },
|
||||||
|
// { value: "LandAuctionCheck", name: "랜드 경매 체크" },
|
||||||
|
// { value: "LeaveInstance", name: "인스턴스 퇴장" },
|
||||||
|
// { value: "LeaveParty", name: "파티 탈퇴" },
|
||||||
|
// { value: "LeavePartyInstance", name: "파티 인스턴스 퇴장" },
|
||||||
|
// { value: "LoginToGame", name: "게임 로그인" },
|
||||||
|
// { value: "LoginToGameSnapShot", name: "게임 로그인 스냅샷" },
|
||||||
|
// { value: "LoginToUserAuth", name: "계정 로그인" },
|
||||||
|
// { value: "MailAiChatIncentivePoint", name: "AI Chat 인센티브 우편 지급" },
|
||||||
|
// { value: "MailDestroy", name: "우편 삭제" },
|
||||||
|
// { value: "MailGetSystemMail", name: "시스템 우편 받기" },
|
||||||
|
// { value: "MailInitSendCount", name: "우편 보내기 기회 초기화" },
|
||||||
|
// { value: "MailRead", name: "우편 읽기" },
|
||||||
|
// { value: "MailSend", name: "우편 발송" },
|
||||||
|
// { value: "MailTaken", name: "우편 첨부 수령" },
|
||||||
|
// { value: "ModifyLandInfo", name: "랜드 정보 수정" },
|
||||||
|
// { value: "MoneyChange", name: "재화 변경" },
|
||||||
|
// { value: "ProductGive", name: "결제 상품 지급" },
|
||||||
|
// { value: "ProductOpenFailed", name: "결제 상품 오픈 실패" },
|
||||||
|
// { value: "ProductOpenSuccess", name: "결제 상품 오픈 성공" },
|
||||||
|
// { value: "QuestMailSend", name: "퀘스트 우편 발송" },
|
||||||
|
// { value: "QuestMainAbort", name: "퀘스트 메인 포기" },
|
||||||
|
// { value: "QuestMainAssignByDialogue", name: "대화를 통한 퀘스트 메인 수락" },
|
||||||
|
// { value: "QuestMainAssignForce", name: "퀘스트 메인 강제 수락" },
|
||||||
|
// { value: "QuestMainRefuse", name: "퀘스트 메인 수락 거절" },
|
||||||
|
// { value: "QuestMainRepeatTimeInit", name: "반복가능 퀘스트 리프레시 타임 초기화" },
|
||||||
|
// { value: "QuestMainRepeatTimeRefresh", name: "반복가능 퀘스트 리프레시 타임 갱신" },
|
||||||
|
// { value: "QuestMainReward", name: "퀘스트 메인 보상" },
|
||||||
|
// { value: "QuestMainTask", name: "퀘스트 메인 태스크관련" },
|
||||||
|
// { value: "QuestTaskUpdate", name: "퀘스트 태스크 업데이트" },
|
||||||
|
// { value: "RefuseFriendRequest", name: "친구요청 거절" },
|
||||||
|
// { value: "RenameFriendFolder", name: "친구 폴더면 수정" },
|
||||||
|
// { value: "RenameMyhome", name: "마이홈 이름 변경" },
|
||||||
|
// { value: "RenewalShopProducts", name: "사용자 요청에 의한 상픔 리스트 갱심" },
|
||||||
|
// { value: "RentFloor", name: "빌딩 층 임대" },
|
||||||
|
// { value: "ReplyInviteParty", name: "파티 초대 응답" },
|
||||||
|
// { value: "ReplySummonParty", name: "파티 맴버 소환 응답" },
|
||||||
|
// { value: "ReservationEnterToServer", name: "서버 이동 예약" },
|
||||||
|
// { value: "RewardProp", name: "리워드 프랍" },
|
||||||
|
// { value: "SaveMyhome", name: "마이홈 저장" },
|
||||||
|
// { value: "SeasonPassBuyCharged", name: "시즌 패스 유료 구입" },
|
||||||
|
// { value: "SeasonPassStartNew", name: "새로운 시즌 패스 시작" },
|
||||||
|
// { value: "SeasonPassTakeReward", name: "시즌 패스 보상 획득" },
|
||||||
|
// { value: "SendFriendRequest", name: "친구 신청" },
|
||||||
|
// { value: "ShopChangeProductTradingMeter", name: "상품 리스트 갱신" },
|
||||||
|
// { value: "ShopGetProductTradingMeter", name: "상품 리스트 조회" },
|
||||||
|
// { value: "ShopGetRePurchase", name: "판매한 상품 리스트 조회" },
|
||||||
|
// { value: "ShopPurchase", name: "상품 구매" },
|
||||||
|
// { value: "ShopRePurchase", name: "판매한 상품 재구매" },
|
||||||
|
// { value: "ShopSell", name: "상품 판매" },
|
||||||
|
// { value: "StageConcertStart", name: "콘서트 시작" },
|
||||||
|
// { value: "StageEnter", name: "스테이지 입장" },
|
||||||
|
// { value: "StageExit", name: "스테이지 퇴장" },
|
||||||
|
// { value: "StartPartyVote", name: "파티 투표 시작" },
|
||||||
|
// { value: "SummonParty", name: "파티 맴버 소환" },
|
||||||
|
// { value: "SwitchingProp", name: "데일리 퀘스트 체크" },
|
||||||
|
// { value: "TaskReservationComplete", name: "Task Reservation complete" },
|
||||||
|
// { value: "TaxiMove", name: "택시 이동" },
|
||||||
|
// { value: "TestBusinessLog", name: "테스트 비지니스 로그 전송" },
|
||||||
|
// { value: "TestUserCreate", name: "테스트 계정으로 생성" },
|
||||||
|
// { value: "TestUserInitial", name: "테스트 계정으로 초기화" },
|
||||||
|
// { value: "TestWriteNoticeChat", name: "테스트 공지사항 추가" },
|
||||||
|
// { value: "TestWriteSystemMail", name: "테스트 시스템 메일 추가" },
|
||||||
|
// { value: "UgqAbort", name: "Ugq 포기" },
|
||||||
|
// { value: "UgqApiAddSlot", name: "UgqApi 슬롯 추가" },
|
||||||
|
// { value: "UgqApiAdminLogin", name: "UgqApi 어드민 로그인" },
|
||||||
|
// { value: "UgqApiChangeState", name: "UgqApi Ugq 상태 변경" },
|
||||||
|
// { value: "UgqApiCreatorPoint", name: "UgqApi CreatorPoint 증감" },
|
||||||
|
// { value: "UgqApiLogin", name: "UgqApi 로그인" },
|
||||||
|
// { value: "UgqApiLogout", name: "UgqApi 로그아웃" },
|
||||||
|
// { value: "UgqApiQuestCraete", name: "UgqApi 퀘스트 생성" },
|
||||||
|
// { value: "UgqAssign", name: "Ugq 수락" },
|
||||||
|
// { value: "UgqDailyRewardCountRefresh", name: "Ugq 데일리 보상 리프레시" },
|
||||||
|
// { value: "UgqDeregisterBookmark", name: "Ugq 북마크 해제" },
|
||||||
|
// { value: "UgqDeregisterLike", name: "Ugq 좋아요 해제" },
|
||||||
|
// { value: "UgqReAssign", name: "Ugq 재수락" },
|
||||||
|
// { value: "UgqRegisterBookmark", name: "Ugq 북마크 등록" },
|
||||||
|
// { value: "UgqRegisterLike", name: "Ugq 좋아요 등록" },
|
||||||
|
// { value: "UgqTestAbort", name: "Test Ugq 포기" },
|
||||||
|
// { value: "UgqTestAssign", name: "Test Ugq 수락" },
|
||||||
|
// { value: "UgqTestDelete", name: "Test Ugq 삭제" },
|
||||||
|
// { value: "UpdateBeaconAppearanceCustomize", name: "비컨 외형 커스터마이징" },
|
||||||
|
// { value: "UpdateCharacterProfile", name: "캐릭터 프로필 업데이트" },
|
||||||
|
// { value: "UpdateCustomDefineUi", name: "커스텀 UI 업데이트" },
|
||||||
|
// { value: "UpdateEscape", name: "유저 탈출" },
|
||||||
|
// { value: "UpdateGameOption", name: "게임 옵션 업데이트" },
|
||||||
|
// { value: "UpdateLanguage", name: "유저 언어 업데이트" },
|
||||||
|
// { value: "UpdateUgcNpcLike", name: "NPC Like 업데이트" },
|
||||||
|
// { value: "UserBlock", name: "유저 차단" },
|
||||||
|
// { value: "UserBlockCancel", name: "유저 차단 취소" },
|
||||||
|
// { value: "UserCreate", name: "유저 생성" },
|
||||||
|
// { value: "UserLoading", name: "유저 로딩" },
|
||||||
|
// { value: "UserLogout", name: "유저 로그아웃" },
|
||||||
|
// { value: "UserLogoutSnapShot", name: "게임 로그아웃 스냅샷" },
|
||||||
|
// { value: "UserReport", name: "유저 신고" },
|
||||||
|
// { value: "Warp", name: "워프" },
|
||||||
|
// { value: "igmApiLogin", name: "igmApi 로그인" }
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// export const logDomain = [
|
||||||
|
// { value: "BASE", name: "전체" },
|
||||||
|
// { value: "AuthLogInOut", name: "인증 로그인/인증 로그아웃" },
|
||||||
|
// { value: "GameLogInOut", name: "게임 로그인/게임 로그아웃" },
|
||||||
|
// { value: "UserCreate", name: "유저 생성" },
|
||||||
|
// { value: "User", name: "유저" },
|
||||||
|
// { value: "UserInitial", name: "유저 초기화" },
|
||||||
|
// { value: "CharacterCreate", name: "캐릭터 생성" },
|
||||||
|
// { value: "Character", name: "캐릭터" },
|
||||||
|
// { value: "Item", name: "아이템" },
|
||||||
|
// { value: "Currency", name: "재화" },
|
||||||
|
// { value: "Mail", name: "우편" },
|
||||||
|
// { value: "MailStoragePeriodExpired", name: "메일 보관 기간 만료 삭제" },
|
||||||
|
// { value: "MailProfile", name: "우편 제한 개요" },
|
||||||
|
// { value: "Stage", name: "스테이지" },
|
||||||
|
// { value: "ClaimReward", name: "클레임 리워드" },
|
||||||
|
// { value: "QuestMain", name: "퀘스트 메인" },
|
||||||
|
// { value: "QuestUgq", name: "퀘스트 Ugq" },
|
||||||
|
// { value: "QuestMail", name: "퀘스트 메일" },
|
||||||
|
// { value: "SocialAction", name: "소셜 액션" },
|
||||||
|
// { value: "MyHome", name: "마이홈" },
|
||||||
|
// { value: "Taxi", name: "택시" },
|
||||||
|
// { value: "RewardProp", name: "리워드 프랍" },
|
||||||
|
// { value: "Party", name: "파티" },
|
||||||
|
// { value: "PartyMember", name: "파티 맴버" },
|
||||||
|
// { value: "PartyVote", name: "파티 투표" },
|
||||||
|
// { value: "PartyInstance", name: "파티 인스턴스" },
|
||||||
|
// { value: "EscapePosition", name: "고립탈출" },
|
||||||
|
// { value: "UserBlock", name: "유저 차단" },
|
||||||
|
// { value: "Friend", name: "친구" },
|
||||||
|
// { value: "UserReport", name: "유저 신고" },
|
||||||
|
// { value: "TaskReservation", name: "처리못한 예약 테스크" },
|
||||||
|
// { value: "SeasonPass", name: "시즌 패스" },
|
||||||
|
// { value: "PackageLastOrderRecode", name: "패키지 마지막 획득 기록" },
|
||||||
|
// { value: "PackageRepeat", name: "패키지 연속 지급" },
|
||||||
|
// { value: "PackageState", name: "패키지 상태" },
|
||||||
|
// { value: "Craft", name: "제작" },
|
||||||
|
// { value: "CraftHelp", name: "제작 도움" },
|
||||||
|
// { value: "Cart", name: "카트" },
|
||||||
|
// { value: "Buff", name: "버프" },
|
||||||
|
// { value: "UgqApi", name: "UgqApi" },
|
||||||
|
// { value: "AIChat", name: "AI채팅" },
|
||||||
|
// { value: "Chat", name: "채팅" },
|
||||||
|
// { value: "Shop", name: "상점" },
|
||||||
|
// { value: "Calium", name: "칼리움" },
|
||||||
|
// { value: "CaliumEchoSystem", name: "칼리움 에코 시스템" },
|
||||||
|
// { value: "CaliumStorageFail", name: "칼리움 저장 실패" },
|
||||||
|
// { value: "Position", name: "위치" },
|
||||||
|
// { value: "Address", name: "주소" },
|
||||||
|
// { value: "BeaconCreate", name: "비컨 생성" },
|
||||||
|
// { value: "Beacon", name: "비컨" },
|
||||||
|
// { value: "CustomDefineUi", name: "CustomDefineUi" },
|
||||||
|
// { value: "Farming", name: "파밍" },
|
||||||
|
// { value: "FarmingReward", name: "파밍 보상" },
|
||||||
|
// { value: "RenewalShopProducts", name: "상점 리뉴얼" },
|
||||||
|
// { value: "CheatRenewalShopProducts", name: "상점 리뉴얼 치트" },
|
||||||
|
// { value: "ChangeDanceEntityState", name: "댄스 엔티티 상태 변경" },
|
||||||
|
// { value: "Land", name: "랜드" },
|
||||||
|
// { value: "Building", name: "빌딩" },
|
||||||
|
// { value: "SwitchingProp", name: "스위칭프랍" },
|
||||||
|
// { value: "LandAuction", name: "랜드 경매" },
|
||||||
|
// { value: "LandAuctionActivity", name: "랜드 경매 활성화" },
|
||||||
|
// { value: "LandAuctionBid", name: "랜드 경매 입찰" },
|
||||||
|
// { value: "LandAuctionBidPriceRefund", name: "랜드 경매 입찰금 환급" },
|
||||||
|
// { value: "BrokerApi", name: "BrokerApi" },
|
||||||
|
// { value: "Rental", name: "랜탈" },
|
||||||
|
// { value: "BuildingProfit", name: "빌딩 수익" },
|
||||||
|
// { value: "BattleObjectInteraction", name: "전투 오브젝트 인터렉션" },
|
||||||
|
// { value: "BattleObjectStateUpdate", name: "전투 오브젝트 상태 업데이트" },
|
||||||
|
// { value: "BattleReward", name: "전투 보상" },
|
||||||
|
// { value: "BattleRespawn", name: "전투 리스폰" },
|
||||||
|
// { value: "BattleRoomJoin", name: "전투 입장" },
|
||||||
|
// { value: "BattleDead", name: "전투 죽음" },
|
||||||
|
// { value: "BattleRound", name: "전투 라운드" },
|
||||||
|
// { value: "BattleSnapshot", name: "전투 스냅샷" }
|
||||||
|
// ];
|
||||||
|
|
||||||
|
export const logAction = [
|
||||||
|
{ value: "None", name: "전체" },
|
||||||
|
{ value: "AIChatDeleteCharacter", name: "AIChatDeleteCharacter" },
|
||||||
|
{ value: "AIChatDeleteUser", name: "AIChatDeleteUser" },
|
||||||
|
{ value: "AIChatGetCharacter", name: "AIChatGetCharacter" },
|
||||||
|
{ value: "AIChatIncentiveMarking", name: "AIChatIncentiveMarking" },
|
||||||
|
{ value: "AIChatIncentiveSearch", name: "AIChatIncentiveSearch" },
|
||||||
|
{ value: "AIChatJwtIssue", name: "AIChatJwtIssue" },
|
||||||
|
{ value: "AIChatJwtVerify", name: "AIChatJwtVerify" },
|
||||||
|
{ value: "AIChatPointCharge", name: "AIChatPointCharge" },
|
||||||
|
{ value: "AIChatPointChargeVerify", name: "AIChatPointChargeVerify" },
|
||||||
|
{ value: "AIChatRegisterCharacter", name: "AIChatRegisterCharacter" },
|
||||||
|
{ value: "AIChatRegisterUser", name: "AIChatRegisterUser" },
|
||||||
|
{ value: "AIChatUpdateCharacter", name: "AIChatUpdateCharacter" },
|
||||||
|
{ value: "BanParty", name: "BanParty" },
|
||||||
|
{ value: "BattleInstanceJoin", name: "BattleInstanceJoin" },
|
||||||
|
{ value: "BattleInstanceSnapshotCreate", name: "BattleInstanceSnapshotCreate" },
|
||||||
|
{ value: "BattleInstanceSnapshotSave", name: "BattleInstanceSnapshotSave" },
|
||||||
|
{ value: "BattleObjectInteraction", name: "BattleObjectInteraction" },
|
||||||
|
{ value: "BattleObjectStateUpdate", name: "BattleObjectStateUpdate" },
|
||||||
|
{ value: "BattlePodCombatOccupyReward", name: "BattlePodCombatOccupyReward" },
|
||||||
|
{ value: "BattleRoundStateUpdate", name: "BattleRoundStateUpdate" },
|
||||||
|
{ value: "BattleUserDead", name: "BattleUserDead" },
|
||||||
|
{ value: "BattleUserRespawn", name: "BattleUserRespawn" },
|
||||||
|
{ value: "BeaconAppearanceCustomize", name: "BeaconAppearanceCustomize" },
|
||||||
|
{ value: "BeaconCreate", name: "BeaconCreate" },
|
||||||
|
{ value: "BeaconEdit", name: "BeaconEdit" },
|
||||||
|
{ value: "BeaconSell", name: "BeaconSell" },
|
||||||
|
{ 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: "BrokerApiUserLogin", name: "BrokerApiUserLogin" },
|
||||||
|
{ value: "BuffAdd", name: "BuffAdd" },
|
||||||
|
{ value: "BuffDelete", name: "BuffDelete" },
|
||||||
|
{ value: "CaliumSyncEchoSystem", name: "CaliumSyncEchoSystem" },
|
||||||
|
{ value: "CancelFriendRequest", name: "CancelFriendRequest" },
|
||||||
|
{ value: "CartAdd", name: "CartAdd" },
|
||||||
|
{ value: "CartDelete", name: "CartDelete" },
|
||||||
|
{ value: "CartPurchase", name: "CartPurchase" },
|
||||||
|
{ value: "ChangeConvertCaliumInfo", name: "ChangeConvertCaliumInfo" },
|
||||||
|
{ value: "CharacterAppearanceCustomize", name: "CharacterAppearanceCustomize" },
|
||||||
|
{ value: "CharacterAppearanceUpdate", name: "CharacterAppearanceUpdate" },
|
||||||
|
{ value: "CharacterCreate", name: "CharacterCreate" },
|
||||||
|
{ value: "CharacterLoading", name: "CharacterLoading" },
|
||||||
|
{ value: "ChatChannel", name: "ChatChannel" },
|
||||||
|
{ value: "ChatNormal", name: "ChatNormal" },
|
||||||
|
{ value: "ChatNotice", name: "ChatNotice" },
|
||||||
|
{ value: "ChatParty", name: "ChatParty" },
|
||||||
|
{ value: "ChatWhisper", name: "ChatWhisper" },
|
||||||
|
{ value: "CheatAllCraftFinish", name: "CheatAllCraftFinish" },
|
||||||
|
{ value: "CheatCommandChangeNickName", name: "CheatCommandChangeNickName" },
|
||||||
|
{ value: "CheatCommandCharacterInit", name: "CheatCommandCharacterInit" },
|
||||||
|
{ value: "CheatCommandClaimReset", name: "CheatCommandClaimReset" },
|
||||||
|
{ value: "CheatCommandClaimUpdate", name: "CheatCommandClaimUpdate" },
|
||||||
|
{ value: "CheatCommandCraftHelpInit", name: "CheatCommandCraftHelpInit" },
|
||||||
|
{ value: "CheatCommandDeleteQuest", name: "CheatCommandDeleteQuest" },
|
||||||
|
{ value: "CheatCommandGainLand", name: "CheatCommandGainLand" },
|
||||||
|
{ value: "CheatCommandIncreaseExp", name: "CheatCommandIncreaseExp" },
|
||||||
|
{ value: "CheatCommandItem", name: "CheatCommandItem" },
|
||||||
|
{ value: "CheatCommandItemLevelUp", name: "CheatCommandItemLevelUp" },
|
||||||
|
{ value: "CheatCommandLandAuctionBlindSet", name: "CheatCommandLandAuctionBlindSet" },
|
||||||
|
{ value: "CheatCommandLandAuctionCanel", name: "CheatCommandLandAuctionCanel" },
|
||||||
|
{ value: "CheatCommandLandAuctionReserve", name: "CheatCommandLandAuctionReserve" },
|
||||||
|
{ value: "CheatCommandLandAuctionStart", name: "CheatCommandLandAuctionStart" },
|
||||||
|
{ value: "CheatCommandPackageSend", name: "CheatCommandPackageSend" },
|
||||||
|
{ value: "CheatCommandQuestAccept", name: "CheatCommandQuestAccept" },
|
||||||
|
{ value: "CheatCommandQuestComplete", name: "CheatCommandQuestComplete" },
|
||||||
|
{ value: "CheatCommandRegisterCraftRecipe", name: "CheatCommandRegisterCraftRecipe" },
|
||||||
|
{ value: "CheatCommandResetAllQuest", name: "CheatCommandResetAllQuest" },
|
||||||
|
{ value: "CheatCommandResetEscapePosition", name: "CheatCommandResetEscapePosition" },
|
||||||
|
{ value: "CheatCommandResetMailCount", name: "CheatCommandResetMailCount" },
|
||||||
|
{ value: "CheatCommandSeasonPassInit", name: "CheatCommandSeasonPassInit" },
|
||||||
|
{ value: "CheatCommandSendMail", name: "CheatCommandSendMail" },
|
||||||
|
{ value: "CheatCommandShopProductInit", name: "CheatCommandShopProductInit" },
|
||||||
|
{ value: "CheatCommandShopProductRenewal", name: "CheatCommandShopProductRenewal" },
|
||||||
|
{ value: "ClaimReward", name: "ClaimReward" },
|
||||||
|
{ value: "ConvertCalium", name: "ConvertCalium" },
|
||||||
|
{ value: "ConvertExchangeCalium", name: "ConvertExchangeCalium" },
|
||||||
|
{ value: "CraftFinish", name: "CraftFinish" },
|
||||||
|
{ value: "CraftHelp", name: "CraftHelp" },
|
||||||
|
{ value: "CraftRecipeRegister", name: "CraftRecipeRegister" },
|
||||||
|
{ value: "CraftStart", name: "CraftStart" },
|
||||||
|
{ value: "CraftStop", name: "CraftStop" },
|
||||||
|
{ value: "CreateCaliumContent", name: "CreateCaliumContent" },
|
||||||
|
{ value: "CreateParty", name: "CreateParty" },
|
||||||
|
{ value: "CreatePartyInstance", name: "CreatePartyInstance" },
|
||||||
|
{ value: "DailyQuestCheck", name: "DailyQuestCheck" },
|
||||||
|
{ value: "DanceEntityStateEnd", name: "DanceEntityStateEnd" },
|
||||||
|
{ value: "DanceEntityStateStart", name: "DanceEntityStateStart" },
|
||||||
|
{ value: "DeleteMyhome", name: "DeleteMyhome" },
|
||||||
|
{ value: "DestroyParty", name: "DestroyParty" },
|
||||||
|
{ value: "EndPartyVote", name: "EndPartyVote" },
|
||||||
|
{ value: "EnterMyhome", name: "EnterMyhome" },
|
||||||
|
{ value: "EnterMyhomeEditRoom", name: "EnterMyhomeEditRoom" },
|
||||||
|
{ value: "FailCaliumEchoSystem", name: "FailCaliumEchoSystem" },
|
||||||
|
{ value: "FailCaliumStorageRollBack", name: "FailCaliumStorageRollBack" },
|
||||||
|
{ value: "FarmingCancel", name: "FarmingCancel" },
|
||||||
|
{ value: "FarmingComplete", name: "FarmingComplete" },
|
||||||
|
{ value: "FarmingIncompletedReward", name: "FarmingIncompletedReward" },
|
||||||
|
{ value: "FarmingStart", name: "FarmingStart" },
|
||||||
|
{ value: "FillupCalium", name: "FillupCalium" },
|
||||||
|
{ value: "FriendAdd", name: "FriendAdd" },
|
||||||
|
{ value: "FriendDelete", name: "FriendDelete" },
|
||||||
|
{ value: "GainLandProfit", name: "GainLandProfit" },
|
||||||
|
{ value: "InviteParty", name: "InviteParty" },
|
||||||
|
{ value: "ItemBuy", name: "ItemBuy" },
|
||||||
|
{ value: "ItemDestroy", name: "ItemDestroy" },
|
||||||
|
{ value: "ItemRandomBoxUse", name: "ItemRandomBoxUse" },
|
||||||
|
{ value: "ItemTattooChangeAttribute", name: "ItemTattooChangeAttribute" },
|
||||||
|
{ value: "ItemTattooLevelUp", name: "ItemTattooLevelUp" },
|
||||||
|
{ value: "ItemUse", name: "ItemUse" },
|
||||||
|
{ value: "JoinInstance", name: "JoinInstance" },
|
||||||
|
{ value: "JoinParty", name: "JoinParty" },
|
||||||
|
{ value: "JoinPartyInstance", name: "JoinPartyInstance" },
|
||||||
|
{ value: "KickFriendsFromMyhome", name: "KickFriendsFromMyhome" },
|
||||||
|
{ value: "LandAuctionActivity", name: "LandAuctionActivity" },
|
||||||
|
{ value: "LandAuctionBid", name: "LandAuctionBid" },
|
||||||
|
{ value: "LandAuctionBidPriceRefund", name: "LandAuctionBidPriceRefund" },
|
||||||
|
{ value: "LandAuctionCheck", name: "LandAuctionCheck" },
|
||||||
|
{ value: "LeaveInstance", name: "LeaveInstance" },
|
||||||
|
{ value: "LeaveParty", name: "LeaveParty" },
|
||||||
|
{ value: "LeavePartyInstance", name: "LeavePartyInstance" },
|
||||||
|
{ value: "LoginToGame", name: "LoginToGame" },
|
||||||
|
{ value: "LoginToGameSnapShot", name: "LoginToGameSnapShot" },
|
||||||
|
{ value: "LoginToUserAuth", name: "LoginToUserAuth" },
|
||||||
|
{ value: "MailAiChatIncentivePoint", name: "MailAiChatIncentivePoint" },
|
||||||
|
{ value: "MailDestroy", name: "MailDestroy" },
|
||||||
|
{ value: "MailGetSystemMail", name: "MailGetSystemMail" },
|
||||||
|
{ value: "MailInitSendCount", name: "MailInitSendCount" },
|
||||||
|
{ value: "MailRead", name: "MailRead" },
|
||||||
|
{ value: "MailSend", name: "MailSend" },
|
||||||
|
{ value: "MailTaken", name: "MailTaken" },
|
||||||
|
{ value: "ModifyLandInfo", name: "ModifyLandInfo" },
|
||||||
|
{ value: "MoneyChange", name: "MoneyChange" },
|
||||||
|
{ value: "ProductGive", name: "ProductGive" },
|
||||||
|
{ value: "ProductOpenFailed", name: "ProductOpenFailed" },
|
||||||
|
{ value: "ProductOpenSuccess", name: "ProductOpenSuccess" },
|
||||||
|
{ value: "QuestMailSend", name: "QuestMailSend" },
|
||||||
|
{ value: "QuestMainAbort", name: "QuestMainAbort" },
|
||||||
|
{ value: "QuestMainAssignByDialogue", name: "QuestMainAssignByDialogue" },
|
||||||
|
{ value: "QuestMainAssignForce", name: "QuestMainAssignForce" },
|
||||||
|
{ value: "QuestMainRefuse", name: "QuestMainRefuse" },
|
||||||
|
{ value: "QuestMainRepeatTimeInit", name: "QuestMainRepeatTimeInit" },
|
||||||
|
{ value: "QuestMainRepeatTimeRefresh", name: "QuestMainRepeatTimeRefresh" },
|
||||||
|
{ value: "QuestMainReward", name: "QuestMainReward" },
|
||||||
|
{ value: "QuestMainTask", name: "QuestMainTask" },
|
||||||
|
{ value: "QuestTaskUpdate", name: "QuestTaskUpdate" },
|
||||||
|
{ value: "RefuseFriendRequest", name: "RefuseFriendRequest" },
|
||||||
|
{ value: "RenameFriendFolder", name: "RenameFriendFolder" },
|
||||||
|
{ value: "RenameMyhome", name: "RenameMyhome" },
|
||||||
|
{ value: "RenewalShopProducts", name: "RenewalShopProducts" },
|
||||||
|
{ value: "RentFloor", name: "RentFloor" },
|
||||||
|
{ value: "ReplyInviteParty", name: "ReplyInviteParty" },
|
||||||
|
{ value: "ReplySummonParty", name: "ReplySummonParty" },
|
||||||
|
{ value: "ReservationEnterToServer", name: "ReservationEnterToServer" },
|
||||||
|
{ value: "RewardProp", name: "RewardProp" },
|
||||||
|
{ value: "SaveMyhome", name: "SaveMyhome" },
|
||||||
|
{ value: "SeasonPassBuyCharged", name: "SeasonPassBuyCharged" },
|
||||||
|
{ value: "SeasonPassStartNew", name: "SeasonPassStartNew" },
|
||||||
|
{ value: "SeasonPassTakeReward", name: "SeasonPassTakeReward" },
|
||||||
|
{ value: "SendFriendRequest", name: "SendFriendRequest" },
|
||||||
|
{ value: "ShopChangeProductTradingMeter", name: "ShopChangeProductTradingMeter" },
|
||||||
|
{ value: "ShopGetProductTradingMeter", name: "ShopGetProductTradingMeter" },
|
||||||
|
{ value: "ShopGetRePurchase", name: "ShopGetRePurchase" },
|
||||||
|
{ value: "ShopPurchase", name: "ShopPurchase" },
|
||||||
|
{ value: "ShopRePurchase", name: "ShopRePurchase" },
|
||||||
|
{ value: "ShopSell", name: "ShopSell" },
|
||||||
|
{ value: "StageConcertStart", name: "StageConcertStart" },
|
||||||
|
{ value: "StageEnter", name: "StageEnter" },
|
||||||
|
{ value: "StageExit", name: "StageExit" },
|
||||||
|
{ value: "StartPartyVote", name: "StartPartyVote" },
|
||||||
|
{ value: "SummonParty", name: "SummonParty" },
|
||||||
|
{ value: "SwitchingProp", name: "SwitchingProp" },
|
||||||
|
{ value: "TaskReservationComplete", name: "TaskReservationComplete" },
|
||||||
|
{ value: "TaxiMove", name: "TaxiMove" },
|
||||||
|
{ value: "TestBusinessLog", name: "TestBusinessLog" },
|
||||||
|
{ value: "TestUserCreate", name: "TestUserCreate" },
|
||||||
|
{ value: "TestUserInitial", name: "TestUserInitial" },
|
||||||
|
{ value: "TestWriteNoticeChat", name: "TestWriteNoticeChat" },
|
||||||
|
{ value: "TestWriteSystemMail", name: "TestWriteSystemMail" },
|
||||||
|
{ value: "UgqAbort", name: "UgqAbort" },
|
||||||
|
{ value: "UgqApiAddSlot", name: "UgqApiAddSlot" },
|
||||||
|
{ value: "UgqApiAdminLogin", name: "UgqApiAdminLogin" },
|
||||||
|
{ value: "UgqApiChangeState", name: "UgqApiChangeState" },
|
||||||
|
{ value: "UgqApiCreatorPoint", name: "UgqApiCreatorPoint" },
|
||||||
|
{ value: "UgqApiLogin", name: "UgqApiLogin" },
|
||||||
|
{ value: "UgqApiLogout", name: "UgqApiLogout" },
|
||||||
|
{ value: "UgqApiQuestCraete", name: "UgqApiQuestCraete" },
|
||||||
|
{ value: "UgqAssign", name: "UgqAssign" },
|
||||||
|
{ value: "UgqDailyRewardCountRefresh", name: "UgqDailyRewardCountRefresh" },
|
||||||
|
{ value: "UgqDeregisterBookmark", name: "UgqDeregisterBookmark" },
|
||||||
|
{ value: "UgqDeregisterLike", name: "UgqDeregisterLike" },
|
||||||
|
{ value: "UgqReAssign", name: "UgqReAssign" },
|
||||||
|
{ value: "UgqRegisterBookmark", name: "UgqRegisterBookmark" },
|
||||||
|
{ value: "UgqRegisterLike", name: "UgqRegisterLike" },
|
||||||
|
{ value: "UgqTestAbort", name: "UgqTestAbort" },
|
||||||
|
{ value: "UgqTestAssign", name: "UgqTestAssign" },
|
||||||
|
{ value: "UgqTestDelete", name: "UgqTestDelete" },
|
||||||
|
{ value: "UpdateBeaconAppearanceCustomize", name: "UpdateBeaconAppearanceCustomize" },
|
||||||
|
{ value: "UpdateCharacterProfile", name: "UpdateCharacterProfile" },
|
||||||
|
{ value: "UpdateCustomDefineUi", name: "UpdateCustomDefineUi" },
|
||||||
|
{ value: "UpdateEscape", name: "UpdateEscape" },
|
||||||
|
{ value: "UpdateGameOption", name: "UpdateGameOption" },
|
||||||
|
{ value: "UpdateLanguage", name: "UpdateLanguage" },
|
||||||
|
{ value: "UpdateUgcNpcLike", name: "UpdateUgcNpcLike" },
|
||||||
|
{ value: "UserBlock", name: "UserBlock" },
|
||||||
|
{ value: "UserBlockCancel", name: "UserBlockCancel" },
|
||||||
|
{ value: "UserCreate", name: "UserCreate" },
|
||||||
|
{ value: "UserLoading", name: "UserLoading" },
|
||||||
|
{ value: "UserLogout", name: "UserLogout" },
|
||||||
|
{ value: "UserLogoutSnapShot", name: "UserLogoutSnapShot" },
|
||||||
|
{ value: "UserReport", name: "UserReport" },
|
||||||
|
{ 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: "BuildingProfit", name: "BuildingProfit" },
|
||||||
|
{ value: "BattleObjectInteraction", name: "BattleObjectInteraction" },
|
||||||
|
{ value: "BattleObjectStateUpdate", name: "BattleObjectStateUpdate" },
|
||||||
|
{ value: "BattleReward", name: "BattleReward" },
|
||||||
|
{ value: "BattleRespawn", name: "BattleRespawn" },
|
||||||
|
{ value: "BattleRoomJoin", name: "BattleRoomJoin" },
|
||||||
|
{ value: "BattleDead", name: "BattleDead" },
|
||||||
|
{ value: "BattleRound", name: "BattleRound" },
|
||||||
|
{ value: "BattleSnapshot", name: "BattleSnapshot" }
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export const authType = {
|
export const authType = {
|
||||||
|
none: 0,
|
||||||
adminSearchRead: 1,
|
adminSearchRead: 1,
|
||||||
adminSearchConfirm: 2,
|
adminSearchConfirm: 2,
|
||||||
adminSearchUpdate: 3,
|
adminSearchUpdate: 3,
|
||||||
@@ -46,9 +47,23 @@ export const authType = {
|
|||||||
landDelete: 45,
|
landDelete: 45,
|
||||||
battleEventRead: 46,
|
battleEventRead: 46,
|
||||||
battleEventUpdate: 47,
|
battleEventUpdate: 47,
|
||||||
battleEventDelete: 48
|
battleEventDelete: 48,
|
||||||
|
businessLogRead: 49,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
levelReader: 999,
|
||||||
|
levelMaster: 9999,
|
||||||
|
levelDeveloper: 99999,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const adminAuthLevel = {
|
||||||
|
NONE: "None",
|
||||||
|
READER: "Reader",
|
||||||
|
MASTER: "Master",
|
||||||
|
DEVELOPER: "Developer",
|
||||||
|
}
|
||||||
|
|
||||||
export const TabList = [
|
export const TabList = [
|
||||||
{ title: '기본정보' },
|
{ title: '기본정보' },
|
||||||
{ title: '아바타' },
|
{ title: '아바타' },
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Profile from '../../assets/img/datamanage/img-profile.png';
|
import Profile from '../../assets/img/datamanage/img-profile.png';
|
||||||
import NicknameChangeModal from '../../components/DataManage/NicknameChangeModal';
|
import NicknameChangeModal from '../../components/DataManage/NicknameChangeModal';
|
||||||
import EditIcon from '../../assets/img/icon/icon-edit.png';
|
import EditIcon from '../../assets/img/icon/icon-edit.png';
|
||||||
import { UserChangeAdminLevel, UserInfoView } from '../../apis/Users';
|
import { UserChangeAdminLevel, UserInfoView, UserKick } from '../../apis/Users';
|
||||||
import { SelectInput } from '../../styles/Components';
|
import { SelectInput } from '../../styles/Components';
|
||||||
import { adminLevelType, authType, modalTypes } from '../../assets/data';
|
import { adminLevelType, authType, modalTypes } from '../../assets/data';
|
||||||
import DynamicModal from '../common/modal/DynamicModal';
|
import DynamicModal from '../common/modal/DynamicModal';
|
||||||
@@ -16,53 +16,94 @@ import { convertKTC } from '../../utils';
|
|||||||
import { EditButton, ProfileWrapper, UserDefault, UserInfoTable } from '../../styles/ModuleComponents';
|
import { EditButton, ProfileWrapper, UserDefault, UserInfoTable } from '../../styles/ModuleComponents';
|
||||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||||
import { UserInfoSkeleton } from '../Skeleton/UserInfoSkeleton';
|
import { UserInfoSkeleton } from '../Skeleton/UserInfoSkeleton';
|
||||||
|
import { opUserSessionType } from '../../assets/data/options';
|
||||||
|
import Button from '../common/button/Button';
|
||||||
|
import { useModal } from '../../hooks/hook';
|
||||||
|
import { InitData } from '../../apis/Data';
|
||||||
|
|
||||||
const UserDefaultInfo = ({ userInfo }) => {
|
const UserDefaultInfo = ({ userInfo }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const authInfo = useRecoilValue(authList);
|
const authInfo = useRecoilValue(authList);
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
const [pwPop, setPwPop] = useState('hidden');
|
const {
|
||||||
const [gmModal, setGmModal] = useState('hidden');
|
modalState,
|
||||||
|
handleModalView,
|
||||||
|
handleModalClose
|
||||||
|
} = useModal({
|
||||||
|
userKick: 'hidden',
|
||||||
|
gmLevelChange: 'hidden',
|
||||||
|
pwChange: 'hidden'
|
||||||
|
});
|
||||||
|
const [alertMsg, setAlertMsg] = useState('');
|
||||||
const [dataList, setDataList] = useState({});
|
const [dataList, setDataList] = useState({});
|
||||||
const [adminLevel, setAdminLevel] = useState('0');
|
const [adminLevel, setAdminLevel] = useState('0');
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [authDelete, setAuthDelete] = useState(false);
|
||||||
|
|
||||||
const handleClick = () => {
|
useEffect(() => {
|
||||||
if (pwPop === 'hidden') setPwPop('view');
|
setAuthDelete(authInfo?.auth_list?.some(auth => auth.id === authType.userSearchDelete));
|
||||||
else setPwPop('hidden');
|
}, [authInfo]);
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [userInfo]);
|
}, [userInfo]);
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const token = sessionStorage.getItem('token');
|
setLoading(true);
|
||||||
await UserInfoView(token, userInfo.guid).then(data => {
|
await UserInfoView(token, userInfo.guid).then(data => {
|
||||||
setDataList(data);
|
setDataList(data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGMChange = (e) =>{
|
const handleSubmit = async (type, param = null) => {
|
||||||
setAdminLevel(e.target.value);
|
|
||||||
setGmModal('view');
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
const token = sessionStorage.getItem('token');
|
|
||||||
let params = {};
|
let params = {};
|
||||||
params.guid = userInfo.guid;
|
|
||||||
params.admin_level = adminLevel;
|
|
||||||
|
|
||||||
await UserChangeAdminLevel(token, params);
|
switch (type) {
|
||||||
|
case "gmLevelChangeSubmit":
|
||||||
|
setAdminLevel(param);
|
||||||
|
|
||||||
handleCancel();
|
handleModalView('gmLevelChange');
|
||||||
await fetchData();
|
break;
|
||||||
}
|
case "userKickSubmit":
|
||||||
|
handleModalView('userKick');
|
||||||
|
break;
|
||||||
|
case "gmLevelChange":
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
const handleCancel = () => {
|
params.guid = userInfo.guid;
|
||||||
setGmModal('hidden');
|
params.admin_level = adminLevel;
|
||||||
|
|
||||||
|
await UserChangeAdminLevel(token, params).then(data =>{
|
||||||
|
setAlertMsg(t('USER_GM_CHANGE_COMPLETE'))
|
||||||
|
}).catch(error => {
|
||||||
|
console.log(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'))
|
||||||
|
}).catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
handleModalClose('userKick');
|
||||||
|
fetchData();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "registComplete":
|
||||||
|
handleModalClose('registComplete');
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
setAlertMsg('');
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -76,11 +117,11 @@ const UserDefaultInfo = ({ userInfo }) => {
|
|||||||
<UserInfoTable $maxwidth="530px">
|
<UserInfoTable $maxwidth="530px">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>AID(GUID)</th>
|
<th>GUID</th>
|
||||||
<td>{dataList.user_info && dataList.user_info.aid}</td>
|
<td>{dataList.user_info && dataList.user_info.aid}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>계정 ID</th>
|
<th>Account ID</th>
|
||||||
<td>{dataList.user_info && dataList.user_info.user_id}</td>
|
<td>{dataList.user_info && dataList.user_info.user_id}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -88,12 +129,16 @@ const UserDefaultInfo = ({ userInfo }) => {
|
|||||||
<td>{dataList.user_info && dataList.user_info.nation}</td>
|
<td>{dataList.user_info && dataList.user_info.nation}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>멤버십</th>
|
<th>접속상태</th>
|
||||||
<td>{dataList.user_info && dataList.user_info.membership}</td>
|
<StatusCell>{dataList.user_session !== undefined && opUserSessionType.find(session => session.value === dataList.user_session)?.name}
|
||||||
</tr>
|
{<Button theme={(dataList.user_session && authDelete) ? "line" : "disable"}
|
||||||
<tr>
|
id={"user_session"}
|
||||||
<th>친구 추천코드</th>
|
name="kick"
|
||||||
<td>{dataList.user_info && dataList.user_info.friend_code}</td>
|
text="KICK"
|
||||||
|
handleClick={() => handleSubmit('userKickSubmit')}
|
||||||
|
disabled={!dataList.user_session && !authDelete}
|
||||||
|
/>}
|
||||||
|
</StatusCell>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>계정 생성일</th>
|
<th>계정 생성일</th>
|
||||||
@@ -103,10 +148,7 @@ const UserDefaultInfo = ({ userInfo }) => {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>최근 접속일자</th>
|
<th>최근 접속일자</th>
|
||||||
<td>
|
<td>{dataList.user_info && convertKTC(dataList.user_info.access_dt)}</td>
|
||||||
{/*{dataList.user_info && String(new Date(new Date(dataList.user_info.access_dt).setHours(new Date(dataList.user_info.access_dt).getHours() + 9)).toLocaleString())}*/}
|
|
||||||
{dataList.user_info && convertKTC(dataList.user_info.access_dt)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>최근 종료일자</th>
|
<th>최근 종료일자</th>
|
||||||
@@ -121,7 +163,9 @@ const UserDefaultInfo = ({ userInfo }) => {
|
|||||||
<tr>
|
<tr>
|
||||||
<th>GM권한</th>
|
<th>GM권한</th>
|
||||||
<td>
|
<td>
|
||||||
<SelectInput value={dataList.user_info && dataList.user_info.admin_level} onChange={(e) => handleGMChange(e)} disabled={authInfo.auth_list && !authInfo.auth_list.some(auth => auth.id === authType.userSearchUpdate)} >
|
<SelectInput value={dataList.user_info && 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) => (
|
{adminLevelType.map((data, index) => (
|
||||||
<option key={index} value={data.value}>
|
<option key={index} value={data.value}>
|
||||||
{data.name}
|
{data.name}
|
||||||
@@ -149,7 +193,7 @@ const UserDefaultInfo = ({ userInfo }) => {
|
|||||||
hidden={true}
|
hidden={true}
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleClick();
|
handleModalClose('pwChange');
|
||||||
}}></EditButton>
|
}}></EditButton>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -172,15 +216,36 @@ const UserDefaultInfo = ({ userInfo }) => {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</UserInfoTable>
|
</UserInfoTable>
|
||||||
</div>
|
</div>
|
||||||
<NicknameChangeModal pwPop={pwPop} handleClick={handleClick} dataList={dataList} />
|
<NicknameChangeModal pwPop={modalState.pwChangeModal} handleClick={() => handleModalClose('pwChange')} dataList={dataList} />
|
||||||
<DynamicModal
|
<DynamicModal
|
||||||
modalType={modalTypes.childOkCancel}
|
modalType={modalTypes.confirmOkCancel}
|
||||||
view={gmModal}
|
view={modalState.gmLevelChangeModal}
|
||||||
modalText={t('USER_GM_CHANGE')}
|
modalText={t('USER_GM_CHANGE')}
|
||||||
handleCancel={handleCancel}
|
handleSubmit={() => handleSubmit('gmLevelChange')}
|
||||||
handleSubmit={handleSubmit}
|
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('')}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default UserDefaultInfo;
|
export default UserDefaultInfo;
|
||||||
|
|
||||||
|
const StatusCell = styled.td`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
`;
|
||||||
@@ -12,53 +12,67 @@ import CompletedModal from '../common/modal/CompletedModal';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import CustomConfirmModal from '../common/modal/CustomConfirmModal';
|
import CustomConfirmModal from '../common/modal/CustomConfirmModal';
|
||||||
import DynamicModal from '../common/modal/DynamicModal';
|
import DynamicModal from '../common/modal/DynamicModal';
|
||||||
import { authType, ivenTabType } from '../../assets/data';
|
import { authType, ivenTabType, opMailType } from '../../assets/data';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { authList } from '../../store/authList';
|
import { authList } from '../../store/authList';
|
||||||
import { convertKTC } from '../../utils';
|
import { convertKTC } from '../../utils';
|
||||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||||
|
import { eventSearchType, opPickupType, opReadType, opYNType } from '../../assets/data/options';
|
||||||
|
import { useDynamoDBPagination, useModal } from '../../hooks/hook';
|
||||||
|
import { DynamoPagination } from '../common';
|
||||||
|
|
||||||
const UserMailInfo = ({ userInfo }) => {
|
const UserMailInfo = ({ userInfo }) => {
|
||||||
const token = sessionStorage.getItem('token');
|
const token = sessionStorage.getItem('token');
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const authInfo = useRecoilValue(authList);
|
const authInfo = useRecoilValue(authList);
|
||||||
|
|
||||||
//데이터 리스트
|
|
||||||
const [dataList, setDataList] = useState([]);
|
|
||||||
// 받은 우편, 보낸 우편
|
// 받은 우편, 보낸 우편
|
||||||
const [option, setOption] = useState('RECEIVE');
|
const [option, setOption] = useState('RECEIVE');
|
||||||
// 상세 정보
|
|
||||||
const [detail, setDetail] = useState({ title: '', content: '', item_list: [], mail_guid: '' });
|
const [detail, setDetail] = useState({ title: '', content: '', item_list: [], mail_guid: '' });
|
||||||
const [deleteSelected, setDeleteSelected] = useState({});
|
const [deleteSelected, setDeleteSelected] = useState({});
|
||||||
const [itemUpdateCount, setItemUpdateCount] = useState('1');
|
const [itemUpdateCount, setItemUpdateCount] = useState('1');
|
||||||
const [authDelete, setAuthDelete] = useState(false);
|
const [authDelete, setAuthDelete] = useState(false);
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
const [modalState, setModalState] = useState({
|
const {
|
||||||
detailModal: 'hidden',
|
modalState,
|
||||||
deleteItemModal: 'hidden',
|
handleModalView,
|
||||||
deleteSubmitModal: 'hidden',
|
handleModalClose
|
||||||
deleteCompleteModal: 'hidden',
|
} = useModal({
|
||||||
deleteItemCompleteModal: 'hidden'
|
detail: 'hidden',
|
||||||
|
deleteItem: 'hidden',
|
||||||
|
deleteSubmit: 'hidden',
|
||||||
|
deleteComplete: 'hidden',
|
||||||
|
deleteItemComplete: 'hidden'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 받은 우편, 보낸 우편 option 에 따른 data fetch
|
const fetchMailData = async (page, startKey) => {
|
||||||
|
const params = {
|
||||||
|
mail_type: option,
|
||||||
|
guid: userInfo.guid,
|
||||||
|
page_key: startKey
|
||||||
|
};
|
||||||
|
return await UserMailView(token, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: dataList,
|
||||||
|
loading,
|
||||||
|
pagination,
|
||||||
|
fetchPage,
|
||||||
|
goToNextPage,
|
||||||
|
goToPrevPage,
|
||||||
|
resetPagination
|
||||||
|
} = useDynamoDBPagination(fetchMailData);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData(option);
|
resetPagination();
|
||||||
|
fetchPage(1);
|
||||||
}, [option]);
|
}, [option]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAuthDelete(authInfo.auth_list.some(auth => auth.id === authType.userSearchDelete));
|
setAuthDelete(authInfo.auth_list.some(auth => auth.id === authType.userSearchDelete));
|
||||||
}, [authInfo]);
|
}, [authInfo]);
|
||||||
|
|
||||||
const fetchData = async option => {
|
|
||||||
await UserMailView(token, userInfo.guid, option).then(data =>{
|
|
||||||
setDataList(data);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 상세 모달 value 세팅
|
|
||||||
const handleDetail = (title, content, itmeList, mail_guid) => {
|
const handleDetail = (title, content, itmeList, mail_guid) => {
|
||||||
setDetail({ title: title, content: content, item_list: itmeList, mail_guid: mail_guid });
|
setDetail({ title: title, content: content, item_list: itmeList, mail_guid: mail_guid });
|
||||||
};
|
};
|
||||||
@@ -79,20 +93,6 @@ const UserMailInfo = ({ userInfo }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalView = (type) => {
|
|
||||||
setModalState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
[`${type}Modal`]: 'view',
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleModalClose = (type) => {
|
|
||||||
setModalState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
[`${type}Modal`]: 'hidden',
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleModalSubmit = async (type, param = null) => {
|
const handleModalSubmit = async (type, param = null) => {
|
||||||
let params;
|
let params;
|
||||||
let result;
|
let result;
|
||||||
@@ -145,7 +145,7 @@ const UserMailInfo = ({ userInfo }) => {
|
|||||||
// if(idx >= 0) {
|
// if(idx >= 0) {
|
||||||
// dataList.mail_list.splice(idx, 1);
|
// dataList.mail_list.splice(idx, 1);
|
||||||
// }
|
// }
|
||||||
fetchData(option);
|
fetchPage(pagination.currentPage);
|
||||||
break;
|
break;
|
||||||
case "deleteItemComplete":
|
case "deleteItemComplete":
|
||||||
handleModalClose('deleteItemComplete');
|
handleModalClose('deleteItemComplete');
|
||||||
@@ -165,21 +165,28 @@ const UserMailInfo = ({ userInfo }) => {
|
|||||||
return (
|
return (
|
||||||
loading ? <TableSkeleton count={10}/> :
|
loading ? <TableSkeleton count={10}/> :
|
||||||
<>
|
<>
|
||||||
<SelectWrapper>
|
<SelectContainer>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
value={option}
|
value={option}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
setOption(e.target.value);
|
setOption(e.target.value);
|
||||||
}}>
|
}}>
|
||||||
<option value="RECEIVE">받은 우편</option>
|
{opMailType.map((data, index) => (
|
||||||
<option value="SEND">보낸 우편</option>
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</SelectInput>
|
</SelectInput>
|
||||||
</SelectWrapper>
|
|
||||||
|
<DynamoPagination
|
||||||
|
pagination={pagination}
|
||||||
|
onNextPage={goToNextPage}
|
||||||
|
onPrevPage={goToPrevPage}
|
||||||
|
/>
|
||||||
|
</SelectContainer>
|
||||||
|
|
||||||
{option === 'RECEIVE' && (
|
{option === 'RECEIVE' && (
|
||||||
<>
|
<>
|
||||||
{/* <CheckWrapper>
|
|
||||||
<CheckBox id="viewDelReceiveMail" label="삭제 우편 보기" />
|
|
||||||
</CheckWrapper> */}
|
|
||||||
<UserTableWrapper>
|
<UserTableWrapper>
|
||||||
<UserDefaultTable>
|
<UserDefaultTable>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -195,7 +202,7 @@ const UserMailInfo = ({ userInfo }) => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{dataList.mail_list &&
|
{dataList && dataList.mail_list &&
|
||||||
dataList.mail_list.map((mail, idx) => {
|
dataList.mail_list.map((mail, idx) => {
|
||||||
return (
|
return (
|
||||||
<tr key={idx}>
|
<tr key={idx}>
|
||||||
@@ -211,10 +218,10 @@ const UserMailInfo = ({ userInfo }) => {
|
|||||||
{mail.title}
|
{mail.title}
|
||||||
</MailLink>
|
</MailLink>
|
||||||
</td>
|
</td>
|
||||||
<td>{mail.status === true ? '확인' : '미확인'}</td>
|
<td>{opReadType.find(type => type.value === mail.status).name}</td>
|
||||||
<td>{mail.item_list.length > 0 ? 'Y' : 'N'}</td>
|
<td>{opYNType.find(type => type.value === (mail.item_list.length > 0)).name}</td>
|
||||||
<td>{mail.is_get_item === true ? '수령함' : '미수령함'}</td>
|
<td>{opPickupType.find(type => type.value === mail.is_get_item).name}</td>
|
||||||
<td>{mail.is_system_mail === true ? 'Y' : 'N'}</td>
|
<td>{opYNType.find(type => type.value === mail.is_system_mail).name}</td>
|
||||||
{/* <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())}
|
{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> */}
|
</td> */}
|
||||||
@@ -228,9 +235,6 @@ const UserMailInfo = ({ userInfo }) => {
|
|||||||
)}
|
)}
|
||||||
{option === 'SEND' && (
|
{option === 'SEND' && (
|
||||||
<>
|
<>
|
||||||
{/* <CheckWrapper>
|
|
||||||
<CheckBox id="viewDelSendMail" label="삭제 우편 보기" />
|
|
||||||
</CheckWrapper> */}
|
|
||||||
<UserTableWrapper>
|
<UserTableWrapper>
|
||||||
<UserDefaultTable>
|
<UserDefaultTable>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -242,7 +246,7 @@ const UserMailInfo = ({ userInfo }) => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{dataList.mail_list &&
|
{dataList && dataList.mail_list &&
|
||||||
dataList.mail_list.map((mail, idx) => {
|
dataList.mail_list.map((mail, idx) => {
|
||||||
return (
|
return (
|
||||||
<tr key={idx}>
|
<tr key={idx}>
|
||||||
@@ -267,6 +271,8 @@ const UserMailInfo = ({ userInfo }) => {
|
|||||||
</UserTableWrapper>
|
</UserTableWrapper>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{/*상세*/}
|
{/*상세*/}
|
||||||
<MailDetailModal
|
<MailDetailModal
|
||||||
mailModal={modalState.detailModal}
|
mailModal={modalState.detailModal}
|
||||||
@@ -365,22 +371,7 @@ const UserTableWrapper = styled.div`
|
|||||||
const MailLink = styled.div`
|
const MailLink = styled.div`
|
||||||
color: #61a2d0;
|
color: #61a2d0;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
`;
|
cursor: pointer;
|
||||||
|
|
||||||
const SelectWrapper = styled.div`
|
|
||||||
select {
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
margin-bottom: 10px;
|
|
||||||
`;
|
|
||||||
const CheckWrapper = styled.div`
|
|
||||||
text-align: right;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: -40px;
|
|
||||||
height: 30px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 700;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const InputItem = styled.div`
|
const InputItem = styled.div`
|
||||||
@@ -396,3 +387,41 @@ const InputItem = styled.div`
|
|||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const SelectContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
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;
|
||||||
|
`;
|
||||||
@@ -1,25 +1,43 @@
|
|||||||
import BoardInfoModal from './modal/BoardInfoModal';
|
import BoardInfoModal from './modal/BoardInfoModal';
|
||||||
import BoardRegistModal from './modal/BoardRegistModal';
|
import BoardRegistModal from './modal/BoardRegistModal';
|
||||||
import MailDetailModal from './modal/MailDetailModal';
|
import MailDetailModal from './modal/MailDetailModal';
|
||||||
import MailListSearchBar from './searchBar/MailListSearchBar';
|
import LandAuctionModal from './modal/LandAuctionModal'
|
||||||
|
import BattleEventModal from './modal/BattleEventModal'
|
||||||
import ReportListAnswerModal from './modal/ReportListAnswerModal';
|
import ReportListAnswerModal from './modal/ReportListAnswerModal';
|
||||||
import ReportListDetailModal from './modal/ReportListDetailModal';
|
import ReportListDetailModal from './modal/ReportListDetailModal';
|
||||||
import ReportListSearchBar from './searchBar/ReportListSearchBar';
|
|
||||||
import UserBlockDetailModal from './modal/UserBlockDetailModal';
|
import UserBlockDetailModal from './modal/UserBlockDetailModal';
|
||||||
|
import OwnerChangeModal from './modal/OwnerChangeModal';
|
||||||
|
//searchbar
|
||||||
|
import SearchFilter from './searchBar/SearchFilter';
|
||||||
|
import ReportListSearchBar from './searchBar/ReportListSearchBar';
|
||||||
import UserBlockSearchBar from './searchBar/UserBlockSearchBar';
|
import UserBlockSearchBar from './searchBar/UserBlockSearchBar';
|
||||||
import WhiteListSearchBar from './WhiteListRegistBar';
|
|
||||||
import ReportListSummary from './ReportListSummary';
|
|
||||||
import ItemsSearchBar from './searchBar/ItemsSearchBar';
|
import ItemsSearchBar from './searchBar/ItemsSearchBar';
|
||||||
import EventListSearchBar from './searchBar/EventListSearchBar';
|
import EventListSearchBar from './searchBar/EventListSearchBar';
|
||||||
import LandAuctionSearchBar from './searchBar/LandAuctionSearchBar'
|
import LandAuctionSearchBar from './searchBar/LandAuctionSearchBar'
|
||||||
import LandAuctionModal from './modal/LandAuctionModal'
|
import MailListSearchBar from './searchBar/MailListSearchBar';
|
||||||
import BattleEventModal from './modal/BattleEventModal'
|
import LandInfoSearchBar from './searchBar/LandInfoSearchBar';
|
||||||
|
import BusinessLogSearchBar from './searchBar/BusinessLogSearchBar';
|
||||||
|
import DataInitSearchBar from './searchBar/DataInitSearchBar';
|
||||||
|
import LogViewSearchBar from './searchBar/LogViewSearchBar';
|
||||||
|
import AdminViewSearchBar from './searchBar/AdminViewSearchBar';
|
||||||
|
import CaliumRequestSearchBar from './searchBar/CaliumRequestSearchBar';
|
||||||
|
|
||||||
|
//etc
|
||||||
|
import ReportListSummary from './ReportListSummary';
|
||||||
|
import WhiteListSearchBar from './WhiteListRegistBar';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
BoardInfoModal,
|
BoardInfoModal,
|
||||||
BoardRegistModal,
|
BoardRegistModal,
|
||||||
MailDetailModal,
|
MailDetailModal,
|
||||||
|
SearchFilter,
|
||||||
MailListSearchBar,
|
MailListSearchBar,
|
||||||
|
LandInfoSearchBar,
|
||||||
|
BusinessLogSearchBar,
|
||||||
|
DataInitSearchBar,
|
||||||
|
LogViewSearchBar,
|
||||||
|
AdminViewSearchBar,
|
||||||
|
CaliumRequestSearchBar,
|
||||||
ReportListAnswerModal,
|
ReportListAnswerModal,
|
||||||
ReportListDetailModal,
|
ReportListDetailModal,
|
||||||
ReportListSearchBar,
|
ReportListSearchBar,
|
||||||
@@ -31,5 +49,6 @@ export {
|
|||||||
EventListSearchBar,
|
EventListSearchBar,
|
||||||
LandAuctionSearchBar,
|
LandAuctionSearchBar,
|
||||||
LandAuctionModal,
|
LandAuctionModal,
|
||||||
BattleEventModal
|
BattleEventModal,
|
||||||
|
OwnerChangeModal
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
import { modalTypes } from '../../../assets/data';
|
import { modalTypes } from '../../../assets/data';
|
||||||
import { DynamicModal, Modal, SingleDatePicker, SingleTimePicker } from '../../common';
|
import { DynamicModal, Modal, SingleDatePicker, SingleTimePicker } from '../../common';
|
||||||
import { NONE, TYPE_MODIFY, TYPE_REGISTRY } from '../../../assets/data/adminConstants';
|
import { NONE, TYPE_MODIFY, TYPE_REGISTRY } from '../../../assets/data/adminConstants';
|
||||||
import { useModal } from '../../../utils/hook';
|
import { useModal } from '../../../hooks/hook';
|
||||||
import { convertKTCDate } from '../../../utils';
|
import { convertKTCDate } from '../../../utils';
|
||||||
import {
|
import {
|
||||||
battleEventHotTime,
|
battleEventHotTime,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
TYPE_REGISTRY,
|
TYPE_REGISTRY,
|
||||||
} from '../../../assets/data/adminConstants';
|
} from '../../../assets/data/adminConstants';
|
||||||
import { landAuctionStatus, landAuctionStatusType, languageType, CurrencyType } from '../../../assets/data';
|
import { landAuctionStatus, landAuctionStatusType, languageType, CurrencyType } from '../../../assets/data';
|
||||||
import { useModal } from '../../../utils/hook';
|
import { useModal } from '../../../hooks/hook';
|
||||||
import { convertKTCDate } from '../../../utils';
|
import { convertKTCDate } from '../../../utils';
|
||||||
import { msToMinutes } from '../../../utils/date';
|
import { msToMinutes } from '../../../utils/date';
|
||||||
|
|
||||||
|
|||||||
395
src/components/ServiceManage/modal/OwnerChangeModal.js
Normal file
395
src/components/ServiceManage/modal/OwnerChangeModal.js
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
import React, { useState, Fragment, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Button from '../../common/button/Button';
|
||||||
|
import Loading from '../../common/Loading';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
BtnWrapper,
|
||||||
|
SearchBarAlert, SelectInput,
|
||||||
|
} from '../../../styles/Components';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FormInput,
|
||||||
|
FormLabel,
|
||||||
|
MessageWrapper,
|
||||||
|
FormRowGroup,
|
||||||
|
FormStatusBar,
|
||||||
|
FormStatusLabel,
|
||||||
|
FormStatusWarning,
|
||||||
|
FormButtonContainer, FormGroup, FormItemGroup, SubText,
|
||||||
|
} from '../../../styles/ModuleComponents';
|
||||||
|
import { modalTypes } from '../../../assets/data';
|
||||||
|
import { DynamicModal, Modal, SingleDatePicker, SingleTimePicker } from '../../common';
|
||||||
|
import { NONE, TYPE_MODIFY, TYPE_REGISTRY } from '../../../assets/data/adminConstants';
|
||||||
|
import { useModal } from '../../../hooks/hook';
|
||||||
|
import { convertKTCDate } from '../../../utils';
|
||||||
|
import { BattleEventModify, BattleEventSingleRegist } from '../../../apis/Battle';
|
||||||
|
import { battleEventStatusType } from '../../../assets/data/types';
|
||||||
|
import { isValidDayRange } from '../../../utils/date';
|
||||||
|
import CheckBox from '../../common/input/CheckBox';
|
||||||
|
import { LandOwnedChangesRegist, LandOwnerChangesDelete, UserInfoView } from '../../../apis';
|
||||||
|
|
||||||
|
const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, setDetailData }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false); // 로딩 창
|
||||||
|
const {
|
||||||
|
modalState,
|
||||||
|
handleModalView,
|
||||||
|
handleModalClose
|
||||||
|
} = useModal({
|
||||||
|
cancel: 'hidden',
|
||||||
|
registConfirm: 'hidden',
|
||||||
|
registComplete: 'hidden'
|
||||||
|
});
|
||||||
|
|
||||||
|
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
|
||||||
|
const [alertMsg, setAlertMsg] = useState('');
|
||||||
|
|
||||||
|
const [resultData, setResultData] = useState(initData); //데이터 정보
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(content && Object.keys(content).length > 0){
|
||||||
|
const ownerChanges = content.owner_changes;
|
||||||
|
let changes_info;
|
||||||
|
if(ownerChanges && ownerChanges.length > 0){
|
||||||
|
changes_info = ownerChanges.filter(item => item.status === 'WAIT').reduce((maxItem, current) => {
|
||||||
|
return (!maxItem || current.id > maxItem.id ) ? current : maxItem;
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
setResultData({
|
||||||
|
...resultData,
|
||||||
|
land_id: content.land_id,
|
||||||
|
land_name: content.land_name,
|
||||||
|
building_id: content.building_id,
|
||||||
|
building_name: content.building_name,
|
||||||
|
is_reserve: changes_info?.is_reserve || false,
|
||||||
|
reservation_dt: (changes_info && convertKTCDate(changes_info.reservation_dt)) || new Date(),
|
||||||
|
user_guid: changes_info?.user_guid || '',
|
||||||
|
user_name: changes_info?.user_name || '',
|
||||||
|
id: changes_info?.id || null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [modalType, content]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (checkCondition()) {
|
||||||
|
setIsNullValue(false);
|
||||||
|
} else {
|
||||||
|
setIsNullValue(true);
|
||||||
|
}
|
||||||
|
}, [resultData]);
|
||||||
|
|
||||||
|
// 날짜 변경 핸들러
|
||||||
|
const handleStartDateChange = (date) => {
|
||||||
|
if (!date) return;
|
||||||
|
|
||||||
|
const newDate = new Date(date);
|
||||||
|
|
||||||
|
setResultData(prev => ({
|
||||||
|
...prev,
|
||||||
|
reservation_dt: newDate
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 시간 변경 핸들러
|
||||||
|
const handleStartTimeChange = (time) => {
|
||||||
|
if (!time) return;
|
||||||
|
|
||||||
|
const newDateTime = resultData.reservation_dt
|
||||||
|
? new Date(resultData.reservation_dt)
|
||||||
|
: new Date();
|
||||||
|
|
||||||
|
newDateTime.setHours(
|
||||||
|
time.getHours(),
|
||||||
|
time.getMinutes(),
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
setResultData(prev => ({
|
||||||
|
...prev,
|
||||||
|
reservation_dt: newDateTime
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setDetailData({});
|
||||||
|
setResultData(initData);
|
||||||
|
handleDetailView();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
if (!checkCondition()) return;
|
||||||
|
|
||||||
|
handleModalView('registConfirm');
|
||||||
|
break;
|
||||||
|
case "cancel":
|
||||||
|
handleModalView('cancel');
|
||||||
|
break;
|
||||||
|
case "cancelConfirm":
|
||||||
|
handleModalClose('cancel');
|
||||||
|
handleReset();
|
||||||
|
break;
|
||||||
|
case "user":
|
||||||
|
if(isView()) return;
|
||||||
|
const guid = resultData.user_guid;
|
||||||
|
if(!guid || guid.length !== 32){
|
||||||
|
setAlertMsg(t('WARNING_GUID_CHECK'))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
await UserInfoView(token, guid).then(data => {
|
||||||
|
if(Object.keys(data).length === 0){
|
||||||
|
setAlertMsg(t('WARNING_GUID_CHECK'));
|
||||||
|
setResultData({ ...resultData, user_name: '' })
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nickname = data.char_info.character_name;
|
||||||
|
setResultData({ ...resultData, user_name: nickname })
|
||||||
|
}).catch(reason => {
|
||||||
|
setAlertMsg(t('API_FAIL'));
|
||||||
|
}).finally(()=>{
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "registConfirm":
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
if(isView()){
|
||||||
|
setLoading(false);
|
||||||
|
handleModalClose('registConfirm');
|
||||||
|
|
||||||
|
const resvDt = resultData.reservation_dt;
|
||||||
|
const now = new Date();
|
||||||
|
if(resvDt < now){
|
||||||
|
setAlertMsg(t('LAND_OWNED_CHANGES_DELETE_TIME_WARNING'));
|
||||||
|
handleReset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await LandOwnerChangesDelete(token, resultData).then(data => {
|
||||||
|
handleModalClose('registConfirm');
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
handleModalView('registComplete');
|
||||||
|
}else if(data.result === "ERROR_LAND_OWNER_CHANGES_RESERVATION"){
|
||||||
|
setAlertMsg(t('LAND_OWNED_CHANGES_DELETE_STATUS_WARNING'));
|
||||||
|
}else{
|
||||||
|
setAlertMsg(t('DELETE_FAIL'));
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
setAlertMsg(t('API_FAIL'));
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
await LandOwnedChangesRegist(token, resultData).then(data => {
|
||||||
|
handleModalClose('registConfirm');
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
handleModalView('registComplete');
|
||||||
|
}else if(data.result === "GUID_CHECK"){
|
||||||
|
setAlertMsg(t('WARNING_GUID_CHECK'));
|
||||||
|
}else if(data.result === "ERROR_LAND_OWNER_DUPLICATION"){
|
||||||
|
setAlertMsg(t('LAND_OWNER_DUPLICATION_WARNING'));
|
||||||
|
}else if(data.result === "ERROR_LAND_OWNER_CHANGES_DUPLICATION"){
|
||||||
|
setAlertMsg(t('LAND_OWNED_CHANGES_REGIST_DUPLICATION_WARNING'));
|
||||||
|
}else{
|
||||||
|
setAlertMsg(t('REGIST_FAIL'));
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
setAlertMsg(t('API_FAIL'));
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "registComplete":
|
||||||
|
handleModalClose('registComplete');
|
||||||
|
handleReset();
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
setAlertMsg('');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCondition = () => {
|
||||||
|
return (
|
||||||
|
resultData.land_id !== ''
|
||||||
|
&& resultData.land_name !== ''
|
||||||
|
&& resultData.user_guid !== ''
|
||||||
|
&& resultData.user_name !== ''
|
||||||
|
&& (!resultData.is_reserve || (resultData.is_reserve && resultData.reservation_dt !== ''))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isView = (label) => {
|
||||||
|
switch (label) {
|
||||||
|
case "modify":
|
||||||
|
return modalType === TYPE_MODIFY;
|
||||||
|
case "registry":
|
||||||
|
return modalType === TYPE_REGISTRY;
|
||||||
|
case "reservation_dt":
|
||||||
|
case "user":
|
||||||
|
return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY &&resultData?.id === null);
|
||||||
|
default:
|
||||||
|
return modalType === TYPE_MODIFY && resultData?.id !== null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal min="760px" $view={detailView}>
|
||||||
|
<Title $align="center">소유권 변경</Title>
|
||||||
|
<MessageWrapper>
|
||||||
|
<FormRowGroup>
|
||||||
|
<FormItemGroup>
|
||||||
|
<CheckBox
|
||||||
|
label="예약"
|
||||||
|
id="reserve"
|
||||||
|
checked={resultData.is_reserve}
|
||||||
|
setData={e => setResultData({ ...resultData, is_reserve: e.target.checked, reservation_dt: new Date() })}
|
||||||
|
disabled={!isView('user')}
|
||||||
|
/>
|
||||||
|
</FormItemGroup>
|
||||||
|
{resultData.is_reserve && (
|
||||||
|
<>
|
||||||
|
<SingleDatePicker
|
||||||
|
label="발송시간"
|
||||||
|
disabled={!isView('reservation_dt')}
|
||||||
|
dateLabel="발송 일자"
|
||||||
|
onDateChange={handleStartDateChange}
|
||||||
|
selectedDate={resultData?.reservation_dt}
|
||||||
|
/>
|
||||||
|
<SingleTimePicker
|
||||||
|
disabled={!isView('reservation_dt')}
|
||||||
|
selectedTime={resultData?.reservation_dt}
|
||||||
|
onTimeChange={handleStartTimeChange}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</FormRowGroup>
|
||||||
|
<FormRowGroup><SubText>* 예약 발송 미선택 시 등록과 함께 우편이 즉시 발송됩니다.</SubText></FormRowGroup>
|
||||||
|
<FormRowGroup>
|
||||||
|
<FormLabel>변경 랜드</FormLabel>
|
||||||
|
<FormInput
|
||||||
|
type="text"
|
||||||
|
width='150px'
|
||||||
|
value={resultData?.land_id}
|
||||||
|
readOnly={true}
|
||||||
|
handleClick={() => handleSubmit('user')}
|
||||||
|
disabled={!isView('user')}
|
||||||
|
/>
|
||||||
|
<FormInput
|
||||||
|
type="text"
|
||||||
|
width='250px'
|
||||||
|
value={resultData?.land_name}
|
||||||
|
readOnly={true}
|
||||||
|
handleClick={() => handleSubmit('user')}
|
||||||
|
disabled={!isView('user')}
|
||||||
|
/>
|
||||||
|
</FormRowGroup>
|
||||||
|
<FormRowGroup>
|
||||||
|
<FormLabel>수신 대상</FormLabel>
|
||||||
|
<FormInput
|
||||||
|
type="text"
|
||||||
|
placeholder="guid 입력"
|
||||||
|
width='300px'
|
||||||
|
value={resultData?.user_guid}
|
||||||
|
onChange={e => setResultData({ ...resultData, user_guid: e.target.value })}
|
||||||
|
disabled={!isView('user')}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
text="확인"
|
||||||
|
theme={!isView() ? 'primary' : 'disable'}
|
||||||
|
type="submit"
|
||||||
|
size="large"
|
||||||
|
width="100px"
|
||||||
|
handleClick={() => handleSubmit('user')}
|
||||||
|
/>
|
||||||
|
</FormRowGroup>
|
||||||
|
<FormRowGroup>
|
||||||
|
<FormLabel>캐릭터명</FormLabel>
|
||||||
|
<FormInput
|
||||||
|
type="text"
|
||||||
|
width='300px'
|
||||||
|
value={resultData?.user_name}
|
||||||
|
readOnly={true}
|
||||||
|
disabled={!isView('user')}
|
||||||
|
/>
|
||||||
|
</FormRowGroup>
|
||||||
|
{!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>}
|
||||||
|
</MessageWrapper>
|
||||||
|
|
||||||
|
<BtnWrapper $gap="10px" $marginTop="10px" $marginBottom="20px">
|
||||||
|
<FormButtonContainer $gap="10px">
|
||||||
|
<>
|
||||||
|
<Button text="취소" theme="line" handleClick={() => handleSubmit('cancel')} />
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text={isView() ? "예약 삭제": "등록"}
|
||||||
|
name={isView() ? "삭제버튼": "등록버튼"}
|
||||||
|
theme={
|
||||||
|
checkCondition()
|
||||||
|
? 'primary'
|
||||||
|
: 'disable'
|
||||||
|
}
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
</FormButtonContainer>
|
||||||
|
</BtnWrapper>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{/* 확인 모달 */}
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.confirmOkCancel}
|
||||||
|
view={modalState.registConfirmModal}
|
||||||
|
modalText={isView() ? t('LAND_OWNED_CHANGES_SELECT_DELETE') : t('LAND_OWNED_CHANGES_REGIST_CONFIRM')}
|
||||||
|
handleSubmit={() => handleSubmit('registConfirm')}
|
||||||
|
handleCancel={() => handleModalClose('registConfirm')}
|
||||||
|
/>
|
||||||
|
{/* 완료 모달 */}
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.completed}
|
||||||
|
view={modalState.registCompleteModal}
|
||||||
|
modalText={isView() ? t('CANCEL_COMPLETED') : t('REGIST_COMPLTE')}
|
||||||
|
handleSubmit={() => handleSubmit('registComplete')}
|
||||||
|
/>
|
||||||
|
{/* 취소 모달 */}
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.confirmOkCancel}
|
||||||
|
view={modalState.cancelModal}
|
||||||
|
modalText={t('CANCEL_CONFIRM')}
|
||||||
|
handleCancel={() => handleModalClose('cancel')}
|
||||||
|
handleSubmit={() => handleSubmit('cancelConfirm')}
|
||||||
|
/>
|
||||||
|
{/* 경고 모달 */}
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.completed}
|
||||||
|
view={alertMsg ? 'view' : 'hidden'}
|
||||||
|
modalText={alertMsg}
|
||||||
|
handleSubmit={() => handleSubmit('warning')}
|
||||||
|
/>
|
||||||
|
{loading && <Loading/>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initData = {
|
||||||
|
is_reserve: false,
|
||||||
|
land_id: '',
|
||||||
|
land_name: '',
|
||||||
|
user_guid: '',
|
||||||
|
user_name: '',
|
||||||
|
reservation_dt: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OwnerChangeModal;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { styled } from 'styled-components';
|
import { styled } from 'styled-components';
|
||||||
|
|
||||||
import { TextInput, BtnWrapper, InputLabel, SelectInput } from '../../styles/Components';
|
import { TextInput, BtnWrapper, InputLabel, SelectInput } from '../../../styles/Components';
|
||||||
import Button from '../common/button/Button';
|
import Button from '../../common/button/Button';
|
||||||
import CheckBox from '../common/input/CheckBox';
|
import CheckBox from '../../common/input/CheckBox';
|
||||||
import { SearchBarLayout } from '../common/SearchBar';
|
import { SearchBarLayout } from '../../common/SearchBar';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
const AdminViewSearchBar = ({ handleSearch, groupList, setResultData, setCurrentPage }) => {
|
const AdminViewSearchBar = ({ handleSearch, groupList, setResultData, setCurrentPage }) => {
|
||||||
@@ -230,15 +230,8 @@ const BattleEventSearchBar = ({ searchParams, onSearch, onReset, configData, rew
|
|||||||
handleEndDate={date => onSearch({ endDate: date }, false)}
|
handleEndDate={date => onSearch({ endDate: date }, false)}
|
||||||
/>
|
/>
|
||||||
</>,
|
</>,
|
||||||
<></>,<></>,
|
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={onReset} type="button" />
|
|
||||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
];
|
||||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BattleEventSearchBar;
|
export default BattleEventSearchBar;
|
||||||
213
src/components/ServiceManage/searchBar/BusinessLogSearchBar.js
Normal file
213
src/components/ServiceManage/searchBar/BusinessLogSearchBar.js
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import { TextInput, BtnWrapper, InputLabel, SelectInput, InputGroup } from '../../../styles/Components';
|
||||||
|
import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { logAction, logDomain, userSearchType2 } from '../../../assets/data/options';
|
||||||
|
import { BusinessLogList } from '../../../apis/Log';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {SearchFilter} from '../';
|
||||||
|
|
||||||
|
export const useBusinessLogSearch = (token, initialPageSize, setAlertMsg) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [searchParams, setSearchParams] = useState({
|
||||||
|
search_type: 'GUID',
|
||||||
|
search_data: '',
|
||||||
|
log_action: 'None',
|
||||||
|
log_domain: 'BASE',
|
||||||
|
tran_id: '',
|
||||||
|
start_dt: (() => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() - 1);
|
||||||
|
return date;
|
||||||
|
})(),
|
||||||
|
end_dt: (() => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() - 1);
|
||||||
|
return date;
|
||||||
|
})(),
|
||||||
|
filters: [],
|
||||||
|
order_by: 'ASC',
|
||||||
|
page_size: initialPageSize,
|
||||||
|
page_no: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//초기 데이터 로드 안함
|
||||||
|
// const initialLoad = async () => {
|
||||||
|
// await fetchData(searchParams);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// initialLoad();
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const fetchData = useCallback(async (params) => {
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await BusinessLogList(
|
||||||
|
token,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if(result.result === "ERROR_LOG_MEMORY_LIMIT"){
|
||||||
|
setAlertMsg(t('LOG_MEMORY_LIMIT_WARNING'))
|
||||||
|
}else if(result.result === "ERROR_MONGODB_QUERY"){
|
||||||
|
setAlertMsg(t('LOG_MONGGDB_QUERY_WARNING'))
|
||||||
|
}
|
||||||
|
setData(result.data);
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching auction data:', error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const updateSearchParams = useCallback((newParams) => {
|
||||||
|
setSearchParams(prev => ({
|
||||||
|
...prev,
|
||||||
|
...newParams
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearch = useCallback(async (newParams = {}, executeSearch = true) => {
|
||||||
|
const updatedParams = {
|
||||||
|
...searchParams,
|
||||||
|
...newParams,
|
||||||
|
page_no: newParams.page_no || 1 // Reset to first page on new search
|
||||||
|
};
|
||||||
|
updateSearchParams(updatedParams);
|
||||||
|
|
||||||
|
if (executeSearch) {
|
||||||
|
return await fetchData(updatedParams);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [searchParams, fetchData]);
|
||||||
|
|
||||||
|
const handleReset = useCallback(async () => {
|
||||||
|
const now = new Date();
|
||||||
|
now.setDate(now.getDate() - 1);
|
||||||
|
const resetParams = {
|
||||||
|
search_type: 'GUID',
|
||||||
|
search_data: '',
|
||||||
|
log_action: 'None',
|
||||||
|
log_domain: 'BASE',
|
||||||
|
tran_id: '',
|
||||||
|
start_dt: now,
|
||||||
|
end_dt: now,
|
||||||
|
filters: [],
|
||||||
|
order_by: 'ASC',
|
||||||
|
page_size: initialPageSize,
|
||||||
|
page_no: 1
|
||||||
|
};
|
||||||
|
setSearchParams(resetParams);
|
||||||
|
return await fetchData(resetParams);
|
||||||
|
}, [initialPageSize, fetchData]);
|
||||||
|
|
||||||
|
const handlePageChange = useCallback(async (newPage) => {
|
||||||
|
return await handleSearch({ page_no: newPage }, true);
|
||||||
|
}, [handleSearch]);
|
||||||
|
|
||||||
|
const handlePageSizeChange = useCallback(async (newSize) => {
|
||||||
|
return await handleSearch({ page_size: newSize, page_no: 1 }, true);
|
||||||
|
}, [handleSearch]);
|
||||||
|
|
||||||
|
const handleOrderByChange = useCallback(async (newOrder) => {
|
||||||
|
return await handleSearch({ order_by: newOrder }, true);
|
||||||
|
}, [handleSearch]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchParams,
|
||||||
|
loading,
|
||||||
|
data,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
handlePageSizeChange,
|
||||||
|
handleOrderByChange,
|
||||||
|
updateSearchParams
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const BusinessLogSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||||
|
const handleSubmit = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
onSearch(searchParams, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchList = [
|
||||||
|
<>
|
||||||
|
<InputGroup>
|
||||||
|
<SelectInput value={searchParams.search_type} onChange={e => onSearch({search_type: e.target.value }, false)}>
|
||||||
|
{userSearchType2.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
placeholder={searchParams.search_type === 'GUID' ? 'GUID ID 입력' : searchParams.search_type === 'NICKNAME' ? '아바타명 입력' :'Account ID 입력'}
|
||||||
|
value={searchParams.search_data}
|
||||||
|
width="260px"
|
||||||
|
onChange={e => onSearch({ search_data: e.target.value }, false)}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>로그액션</InputLabel>
|
||||||
|
<SelectInput value={searchParams.log_action} onChange={e => onSearch({ log_action: e.target.value }, false)} >
|
||||||
|
{logAction.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>로그도메인</InputLabel>
|
||||||
|
<SelectInput value={searchParams.log_domain} onChange={e => onSearch({ log_domain: e.target.value }, false)} >
|
||||||
|
{logDomain.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
];
|
||||||
|
|
||||||
|
const optionList = [
|
||||||
|
<>
|
||||||
|
<InputLabel>트랜잭션 ID</InputLabel>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
placeholder='트랜잭션 ID 입력'
|
||||||
|
value={searchParams.tran_id}
|
||||||
|
width="300px"
|
||||||
|
onChange={e => onSearch({ tran_id: e.target.value }, false)}
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>일자</InputLabel>
|
||||||
|
<SearchPeriod
|
||||||
|
startDate={searchParams.start_dt}
|
||||||
|
handleStartDate={date => onSearch({ start_dt: date }, false)}
|
||||||
|
endDate={searchParams.end_dt}
|
||||||
|
handleEndDate={date => onSearch({ end_dt: date }, false)}
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
];
|
||||||
|
|
||||||
|
const filterComponent = (
|
||||||
|
<SearchFilter value={searchParams.filters} onChange={e => onSearch({filters: e.target.value }, false)} />
|
||||||
|
);
|
||||||
|
|
||||||
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} filter={filterComponent} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BusinessLogSearchBar;
|
||||||
@@ -72,15 +72,9 @@ const CaliumRequestSearchBar = ({ handleSearch, setResultData }) => {
|
|||||||
))}
|
))}
|
||||||
</SelectInput>
|
</SelectInput>
|
||||||
</>,
|
</>,
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={handleReset} type="button" />
|
|
||||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return <SearchBarLayout firstColumnData={searchList} direction={'column'} />;
|
return <SearchBarLayout firstColumnData={searchList} direction={'column'} onReset={handleReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CaliumRequestSearchBar;
|
export default CaliumRequestSearchBar;
|
||||||
125
src/components/ServiceManage/searchBar/DataInitSearchBar.js
Normal file
125
src/components/ServiceManage/searchBar/DataInitSearchBar.js
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import { BtnWrapper, InputLabel, TextInput } from '../../../styles/Components';
|
||||||
|
import Button from '../../common/button/Button';
|
||||||
|
import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { InitHistoryList } from '../../../apis/Data';
|
||||||
|
|
||||||
|
export const useDataInitSearch = (token, setAlertMsg) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [searchParams, setSearchParams] = useState({
|
||||||
|
tran_id: '',
|
||||||
|
start_dt: (() => {
|
||||||
|
return new Date();
|
||||||
|
})(),
|
||||||
|
end_dt: (() => {
|
||||||
|
return new Date();
|
||||||
|
})(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const initialLoad = async () => {
|
||||||
|
await fetchData(searchParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
initialLoad();
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const fetchData = useCallback(async (params) => {
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await InitHistoryList(
|
||||||
|
token,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if(result.result === "ERROR_LOG_MEMORY_LIMIT"){
|
||||||
|
setAlertMsg(t('LOG_MEMORY_LIMIT_WARNING'))
|
||||||
|
}else if(result.result === "ERROR_MONGODB_QUERY"){
|
||||||
|
setAlertMsg(t('LOG_MONGGDB_QUERY_WARNING'))
|
||||||
|
}
|
||||||
|
setData(result.data);
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching auction data:', error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const updateSearchParams = useCallback((newParams) => {
|
||||||
|
setSearchParams(prev => ({
|
||||||
|
...prev,
|
||||||
|
...newParams
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearch = useCallback(async (newParams = {}) => {
|
||||||
|
const updatedParams = {
|
||||||
|
...searchParams,
|
||||||
|
...newParams,
|
||||||
|
};
|
||||||
|
updateSearchParams(updatedParams);
|
||||||
|
return await fetchData(updatedParams);
|
||||||
|
}, [searchParams, fetchData]);
|
||||||
|
|
||||||
|
const handleReset = useCallback(async () => {
|
||||||
|
const now = new Date();
|
||||||
|
const resetParams = {
|
||||||
|
tran_id: '',
|
||||||
|
start_dt: now,
|
||||||
|
end_dt: now,
|
||||||
|
};
|
||||||
|
setSearchParams(resetParams);
|
||||||
|
return await fetchData(resetParams);
|
||||||
|
}, [fetchData]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchParams,
|
||||||
|
loading,
|
||||||
|
data,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
updateSearchParams
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const DataInitSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||||
|
const handleSubmit = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
onSearch(searchParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchList = [
|
||||||
|
<>
|
||||||
|
<InputLabel>트랜잭션 ID</InputLabel>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
placeholder='트랜잭션 ID 입력'
|
||||||
|
value={searchParams.tran_id}
|
||||||
|
width="300px"
|
||||||
|
onChange={e => onSearch({ tran_id: e.target.value })}
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>일자</InputLabel>
|
||||||
|
<SearchPeriod
|
||||||
|
startDate={searchParams.start_dt}
|
||||||
|
handleStartDate={date => onSearch({ start_dt: date }, false)}
|
||||||
|
endDate={searchParams.end_dt}
|
||||||
|
handleEndDate={date => onSearch({ end_dt: date }, false)}
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
];
|
||||||
|
|
||||||
|
return <SearchBarLayout firstColumnData={searchList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DataInitSearchBar;
|
||||||
@@ -89,15 +89,8 @@ const EventListSearchBar = ({ handleSearch, setResultData }) => {
|
|||||||
))}
|
))}
|
||||||
</SelectInput>
|
</SelectInput>
|
||||||
</>,
|
</>,
|
||||||
<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,
|
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={handleReset} type="button" />
|
|
||||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
];
|
||||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={handleReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EventListSearchBar;
|
export default EventListSearchBar;
|
||||||
@@ -114,14 +114,8 @@ const ItemsSearchBar = ({ handleSearch, setResultData }) => {
|
|||||||
maxDate={new Date()}
|
maxDate={new Date()}
|
||||||
/>
|
/>
|
||||||
</>,
|
</>,
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={handleReset} />
|
|
||||||
<Button theme="search" text="검색" type="submit" handleClick={handleSubmit} />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
];
|
||||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={handleReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ItemsSearchBar;
|
export default ItemsSearchBar;
|
||||||
|
|||||||
@@ -191,15 +191,8 @@ const LandAuctionSearchBar = ({ searchParams, onSearch, onReset }) => {
|
|||||||
handleEndDate={date => onSearch({ auctionEndDate: date }, false)}
|
handleEndDate={date => onSearch({ auctionEndDate: date }, false)}
|
||||||
/>
|
/>
|
||||||
</>,
|
</>,
|
||||||
<></>,<></>,
|
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={onReset} type="button" />
|
|
||||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
];
|
||||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LandAuctionSearchBar;
|
export default LandAuctionSearchBar;
|
||||||
194
src/components/ServiceManage/searchBar/LandInfoSearchBar.js
Normal file
194
src/components/ServiceManage/searchBar/LandInfoSearchBar.js
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import { TextInput, BtnWrapper, InputLabel, SelectInput, InputGroup } from '../../../styles/Components';
|
||||||
|
import Button from '../../common/button/Button';
|
||||||
|
import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LandAuctionView, LandInfoData } from '../../../apis';
|
||||||
|
import { landAuctionStatus, landSearchType, landSize, opLandCategoryType } from '../../../assets/data';
|
||||||
|
import { opLandInfoStatusType } from '../../../assets/data/options';
|
||||||
|
|
||||||
|
export const useLandInfoSearch = (token, initialPageSize) => {
|
||||||
|
const [searchParams, setSearchParams] = useState({
|
||||||
|
landType: 'ID',
|
||||||
|
landData: '',
|
||||||
|
landSize: 'ALL',
|
||||||
|
category: 'ALL',
|
||||||
|
status: 'ALL',
|
||||||
|
startDate: '',
|
||||||
|
endDate: '',
|
||||||
|
orderBy: 'DESC',
|
||||||
|
pageSize: initialPageSize,
|
||||||
|
currentPage: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// fetchData(searchParams); // 컴포넌트 마운트 시 초기 데이터 로드
|
||||||
|
const initialLoad = async () => {
|
||||||
|
await fetchData(searchParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
initialLoad();
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const fetchData = useCallback(async (params) => {
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await LandInfoData(
|
||||||
|
token,
|
||||||
|
params.landType,
|
||||||
|
params.landData,
|
||||||
|
params.landSize,
|
||||||
|
params.category,
|
||||||
|
params.status,
|
||||||
|
params.startDate && new Date(params.startDate).toISOString(),
|
||||||
|
params.endDate && new Date(params.endDate).toISOString(),
|
||||||
|
params.orderBy,
|
||||||
|
params.pageSize,
|
||||||
|
params.currentPage
|
||||||
|
);
|
||||||
|
setData(result);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching auction data:', error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const updateSearchParams = useCallback((newParams) => {
|
||||||
|
setSearchParams(prev => ({
|
||||||
|
...prev,
|
||||||
|
...newParams
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearch = useCallback(async (newParams = {}) => {
|
||||||
|
const updatedParams = {
|
||||||
|
...searchParams,
|
||||||
|
...newParams,
|
||||||
|
currentPage: newParams.currentPage || 1 // Reset to first page on new search
|
||||||
|
};
|
||||||
|
updateSearchParams(updatedParams);
|
||||||
|
return await fetchData(updatedParams);
|
||||||
|
}, [searchParams, fetchData]);
|
||||||
|
|
||||||
|
const handleReset = useCallback(async () => {
|
||||||
|
const resetParams = {
|
||||||
|
landType: 'ID',
|
||||||
|
landData: '',
|
||||||
|
landSize: 'ALL',
|
||||||
|
category: 'ALL',
|
||||||
|
status: 'ALL',
|
||||||
|
startDate: '',
|
||||||
|
endDate: '',
|
||||||
|
orderBy: 'DESC',
|
||||||
|
pageSize: initialPageSize,
|
||||||
|
currentPage: 1
|
||||||
|
};
|
||||||
|
setSearchParams(resetParams);
|
||||||
|
return await fetchData(resetParams);
|
||||||
|
}, [initialPageSize, fetchData]);
|
||||||
|
|
||||||
|
const handlePageChange = useCallback(async (newPage) => {
|
||||||
|
return await handleSearch({ currentPage: newPage });
|
||||||
|
}, [handleSearch]);
|
||||||
|
|
||||||
|
const handlePageSizeChange = useCallback(async (newSize) => {
|
||||||
|
return await handleSearch({ pageSize: newSize, currentPage: 1 });
|
||||||
|
}, [handleSearch]);
|
||||||
|
|
||||||
|
const handleOrderByChange = useCallback(async (newOrder) => {
|
||||||
|
return await handleSearch({ orderBy: newOrder });
|
||||||
|
}, [handleSearch]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchParams,
|
||||||
|
loading,
|
||||||
|
data,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
handlePageSizeChange,
|
||||||
|
handleOrderByChange,
|
||||||
|
updateSearchParams
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const LandInfoSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||||
|
const handleSubmit = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
onSearch(searchParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchList = [
|
||||||
|
<>
|
||||||
|
<InputGroup>
|
||||||
|
<SelectInput value={searchParams.landType} onChange={e => onSearch({landType: e.target.value })}>
|
||||||
|
{landSearchType.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
placeholder={searchParams.landType === 'ID' ? '랜드 ID 입력' : '랜드명 입력'}
|
||||||
|
value={searchParams.landData}
|
||||||
|
width="300px"
|
||||||
|
onChange={e => onSearch({ landData: e.target.value })}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>랜드크기</InputLabel>
|
||||||
|
<SelectInput value={searchParams.landSize} onChange={e => onSearch({ landSize: e.target.value }, false)} >
|
||||||
|
{landSize.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>랜드상태</InputLabel>
|
||||||
|
<SelectInput value={searchParams.status} onChange={e => onSearch({ status: e.target.value }, false)} >
|
||||||
|
{opLandInfoStatusType.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
];
|
||||||
|
|
||||||
|
const optionList = [
|
||||||
|
<>
|
||||||
|
<InputLabel>카테고리</InputLabel>
|
||||||
|
<SelectInput value={searchParams.category} onChange={e => onSearch({ category: e.target.value }, false)}>
|
||||||
|
{opLandCategoryType.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>일자</InputLabel>
|
||||||
|
<SearchPeriod
|
||||||
|
startDate={searchParams.startDate}
|
||||||
|
handleStartDate={date => onSearch({ startDate: date }, false)}
|
||||||
|
endDate={searchParams.endDate}
|
||||||
|
handleEndDate={date => onSearch({ endDate: date }, false)}
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
];
|
||||||
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LandInfoSearchBar;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { styled } from 'styled-components';
|
import { styled } from 'styled-components';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { TextInput, InputLabel, SelectInput, BtnWrapper } from '../../styles/Components';
|
import { TextInput, InputLabel, SelectInput, BtnWrapper } from '../../../styles/Components';
|
||||||
import Button from '../common/button/Button';
|
import Button from '../../common/button/Button';
|
||||||
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar';
|
||||||
|
|
||||||
const LogViewSearchBar = ({ handleSearch, resultData }) => {
|
const LogViewSearchBar = ({ handleSearch, resultData }) => {
|
||||||
const [searchData, setSearchData] = useState({
|
const [searchData, setSearchData] = useState({
|
||||||
@@ -130,14 +130,8 @@ const MailListSearchBar = ({ handleSearch, setResultData }) => {
|
|||||||
))}
|
))}
|
||||||
</SelectInput>
|
</SelectInput>
|
||||||
</>,
|
</>,
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={handleReset} type="button" />
|
|
||||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
];
|
||||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={handleReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MailListSearchBar;
|
export default MailListSearchBar;
|
||||||
@@ -123,14 +123,8 @@ const ReportListSearchBar = ({ handleSearch, setResultData }) => {
|
|||||||
<TextInput placeholder="입력" onChange={e => setSearchData({ ...searchData, searchKey: e.target.value })} />
|
<TextInput placeholder="입력" onChange={e => setSearchData({ ...searchData, searchKey: e.target.value })} />
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</>,
|
</>,
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={handleReset} type="button" />
|
|
||||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
];
|
||||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={handleReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ReportListSearchBar;
|
export default ReportListSearchBar;
|
||||||
|
|||||||
238
src/components/ServiceManage/searchBar/SearchFilter.js
Normal file
238
src/components/ServiceManage/searchBar/SearchFilter.js
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { styled } from 'styled-components';
|
||||||
|
import { TextInput, InputLabel, SelectInput } from '../../../styles/Components';
|
||||||
|
import { logDomain, opInputType } from '../../../assets/data/options';
|
||||||
|
|
||||||
|
const SearchFilter = ({ value = [], onChange }) => {
|
||||||
|
const [filters, setFilters] = useState(value || []);
|
||||||
|
const [filterSections, setFilterSections] = useState([{ field_name: '', field_type: 'String', value: '' }]);
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFilters(value || []);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const handleInputChange = (index, field, inputValue) => {
|
||||||
|
const updatedSections = [...filterSections];
|
||||||
|
updatedSections[index] = { ...updatedSections[index], [field]: inputValue };
|
||||||
|
setFilterSections(updatedSections);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddFilter = (index) => {
|
||||||
|
const filterToAdd = filterSections[index];
|
||||||
|
|
||||||
|
// Only add if both field_name and value are provided
|
||||||
|
if (filterToAdd.field_name && filterToAdd.value && filterToAdd.field_type) {
|
||||||
|
const updatedFilters = [...filters, { field_name: filterToAdd.field_name, field_type: filterToAdd.field_type, value: filterToAdd.value }];
|
||||||
|
setFilters(updatedFilters);
|
||||||
|
onChange({ target: { value: updatedFilters } });
|
||||||
|
|
||||||
|
// Reset this section
|
||||||
|
const updatedSections = [...filterSections];
|
||||||
|
updatedSections[index] = { field_name: '', field_type: 'String', value: '' };
|
||||||
|
setFilterSections(updatedSections);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddFilterSection = () => {
|
||||||
|
setFilterSections([...filterSections, { field_name: '', field_type: 'String', value: '' }]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveFilterSection = (index) => {
|
||||||
|
if (filterSections.length > 1) {
|
||||||
|
const updatedSections = filterSections.filter((_, i) => i !== index);
|
||||||
|
setFilterSections(updatedSections);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveFilter = (index) => {
|
||||||
|
const updatedFilters = filters.filter((_, i) => i !== index);
|
||||||
|
setFilters(updatedFilters);
|
||||||
|
onChange({ target: { value: updatedFilters } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleFilters = () => {
|
||||||
|
setIsOpen(!isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterWrapper>
|
||||||
|
<FilterToggle onClick={toggleFilters}>
|
||||||
|
필터 {isOpen ? '▲' : '▼'}
|
||||||
|
</FilterToggle>
|
||||||
|
|
||||||
|
{isOpen && (
|
||||||
|
<>
|
||||||
|
|
||||||
|
{filters.length > 0 && (
|
||||||
|
<FilterSection>
|
||||||
|
{filters.map((filter, index) => (
|
||||||
|
<FilterItem key={index}>
|
||||||
|
<FilterName>{filter.field_name}:</FilterName>
|
||||||
|
<FilterValue>{filter.value}</FilterValue>
|
||||||
|
<RemoveButton onClick={() => handleRemoveFilter(index)}>×</RemoveButton>
|
||||||
|
</FilterItem>
|
||||||
|
))}
|
||||||
|
</FilterSection>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{filterSections.map((section, index) => (
|
||||||
|
<FilterInputSection key={index}>
|
||||||
|
<InputLabel>속성 이름</InputLabel>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
placeholder="속성 이름 입력"
|
||||||
|
value={section.field_name}
|
||||||
|
onChange={(e) => handleInputChange(index, 'field_name', e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InputLabel>속성 유형</InputLabel>
|
||||||
|
<SelectInput
|
||||||
|
value={section.field_type}
|
||||||
|
onChange={(e) => handleInputChange(index, 'field_type', e.target.value)}
|
||||||
|
>
|
||||||
|
{opInputType.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
|
||||||
|
<InputLabel>속성 값</InputLabel>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
placeholder="속성 값 입력"
|
||||||
|
value={section.value}
|
||||||
|
onChange={(e) => handleInputChange(index, 'value', e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<AddButton onClick={() => handleAddFilter(index)}>추가</AddButton>
|
||||||
|
|
||||||
|
{filterSections.length > 1 && (
|
||||||
|
<RemoveButton onClick={() => handleRemoveFilterSection(index)}>×</RemoveButton>
|
||||||
|
)}
|
||||||
|
</FilterInputSection>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/*<AddFilterButton onClick={handleAddFilterSection}>*/}
|
||||||
|
{/* 필터 추가*/}
|
||||||
|
{/*</AddFilterButton>*/}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</FilterWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SearchFilter;
|
||||||
|
|
||||||
|
const FilterWrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FilterToggle = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e5e5e5;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FilterSection = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 15px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FilterItem = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #f0f0f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FilterName = styled.span`
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FilterValue = styled.span`
|
||||||
|
color: #333;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RemoveButton = styled.button`
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #888;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const AddButton = styled.button.attrs({
|
||||||
|
type: 'button'
|
||||||
|
})`
|
||||||
|
background: #1890ff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #40a9ff;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FilterInputSection = styled.div`
|
||||||
|
display: flex;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 0 15px 15px;
|
||||||
|
border-radius: 6px;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
${TextInput} {
|
||||||
|
width: 160px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const AddFilterButton = styled.button.attrs({
|
||||||
|
type: 'button'
|
||||||
|
})`
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
margin: 0 15px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
width: calc(100% - 30px);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e5e5e5;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -104,14 +104,8 @@ const UserBlockSearchBar = ({ handleSearch, setResultData }) => {
|
|||||||
))}
|
))}
|
||||||
</SelectInput>
|
</SelectInput>
|
||||||
</>,
|
</>,
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={handleReset} />
|
|
||||||
<Button theme="search" text="검색" type="submit" handleClick={handleSubmit} />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
];
|
||||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={handleReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserBlockSearchBar;
|
export default UserBlockSearchBar;
|
||||||
|
|||||||
@@ -9,37 +9,41 @@ export const UserInfoSkeleton = () => {
|
|||||||
<SkeletonImg width="200px" height="150px" />
|
<SkeletonImg width="200px" height="150px" />
|
||||||
</ProfileWrapper>
|
</ProfileWrapper>
|
||||||
<UserInfoTable>
|
<UserInfoTable>
|
||||||
<Skeleton width="530px" height="30px" />
|
<tbody>
|
||||||
<Skeleton width="530px" height="30px" />
|
{Array.from({ length: 10 }).map((_, index) => (
|
||||||
<Skeleton width="530px" height="30px" />
|
<tr key={index}>
|
||||||
<Skeleton width="530px" height="30px" />
|
<td>
|
||||||
<Skeleton width="530px" height="30px" />
|
<Skeleton width="530px" height="30px" />
|
||||||
<Skeleton width="530px" height="30px" />
|
</td>
|
||||||
<Skeleton width="530px" height="30px" />
|
</tr>
|
||||||
<Skeleton width="530px" height="30px" />
|
))}
|
||||||
<Skeleton width="530px" height="30px" />
|
</tbody>
|
||||||
<Skeleton width="530px" height="30px" />
|
|
||||||
</UserInfoTable>
|
</UserInfoTable>
|
||||||
</UserDefault>
|
</UserDefault>
|
||||||
<UserInfoTable>
|
<UserInfoTable>
|
||||||
<Skeleton width="750px" height="30px" />
|
<tbody>
|
||||||
<Skeleton width="750px" height="30px" />
|
{Array.from({ length: 4 }).map((_, index) => (
|
||||||
<Skeleton width="750px" height="30px" />
|
<tr key={index}>
|
||||||
<Skeleton width="750px" height="30px" />
|
<td>
|
||||||
|
<Skeleton width="750px" height="30px" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
</UserInfoTable>
|
</UserInfoTable>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserDefault = styled.div`
|
const UserDefault = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 40px;
|
gap: 40px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
const ProfileWrapper = styled.div`
|
const ProfileWrapper = styled.div`
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
border-radius: 75px;
|
border-radius: 75px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
import { TextInput, BtnWrapper, InputLabel, SelectInput } from '../../styles/Components';
|
|
||||||
import Button from '../common/button/Button';
|
|
||||||
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { caliumStatus } from '../../assets/data/options';
|
|
||||||
|
|
||||||
const CaliumRequestSearchBar = ({ handleSearch, setResultData }) => {
|
|
||||||
const [searchData, setSearchData] = useState({
|
|
||||||
content: '',
|
|
||||||
status: 'ALL',
|
|
||||||
startDate: '',
|
|
||||||
endDate: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleSubmit = event => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
handleSearch(
|
|
||||||
searchData.content,
|
|
||||||
searchData.status ? searchData.status : 'ALL',
|
|
||||||
searchData.startDate ? searchData.startDate : '',
|
|
||||||
searchData.endDate ? searchData.endDate : new Date(),
|
|
||||||
(searchData.startDate && searchData.endDate === '') && setSearchData({ startDate : searchData.startDate ,endDate : new Date()}),
|
|
||||||
);
|
|
||||||
|
|
||||||
setResultData(searchData);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setSearchData({
|
|
||||||
content: '',
|
|
||||||
status: 'ALL',
|
|
||||||
startDate: '',
|
|
||||||
endDate: '',
|
|
||||||
order: 'DESC',
|
|
||||||
});
|
|
||||||
|
|
||||||
handleSearch('', 'ALL', '', '');
|
|
||||||
setResultData('', 'ALL', '', '');
|
|
||||||
|
|
||||||
window.location.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const searchList = [
|
|
||||||
<>
|
|
||||||
<InputLabel>등록 일자</InputLabel>
|
|
||||||
<SearchPeriod
|
|
||||||
startDate={searchData.startDate}
|
|
||||||
handleStartDate={data => {
|
|
||||||
setSearchData({ ...searchData, startDate: data });
|
|
||||||
}}
|
|
||||||
endDate={searchData.endDate}
|
|
||||||
handleEndDate={data => setSearchData({ ...searchData, endDate: data })}
|
|
||||||
/>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<InputLabel>요청 내용</InputLabel>
|
|
||||||
<TextInput
|
|
||||||
type="text"
|
|
||||||
placeholder="요청 내용"
|
|
||||||
value={searchData.content}
|
|
||||||
onChange={e => setSearchData({ ...searchData, content: e.target.value })}
|
|
||||||
/>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<InputLabel>요청 상태</InputLabel>
|
|
||||||
<SelectInput value={searchData.status} onChange={e => setSearchData({ ...searchData, status: e.target.value })}>
|
|
||||||
{caliumStatus.map((data, index) => (
|
|
||||||
<option key={index} value={data.value}>
|
|
||||||
{data.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</SelectInput>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<BtnWrapper $gap="8px">
|
|
||||||
<Button theme="reset" handleClick={handleReset} type="button" />
|
|
||||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
|
||||||
</BtnWrapper>
|
|
||||||
</>,
|
|
||||||
];
|
|
||||||
|
|
||||||
return <SearchBarLayout firstColumnData={searchList} direction={'column'} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CaliumRequestSearchBar;
|
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
import CaliumRequestSearchBar from './CaliumRequestSearchBar';
|
|
||||||
import CaliumRequestRegistModal from './CaliumRequestRegistModal'
|
import CaliumRequestRegistModal from './CaliumRequestRegistModal'
|
||||||
import AdminViewSearchBar from './AdminViewSearchBar'
|
|
||||||
import AuthRegistBar from './AuthRegistBar'
|
import AuthRegistBar from './AuthRegistBar'
|
||||||
import LogViewModal from './LogViewModal'
|
import LogViewModal from './LogViewModal'
|
||||||
import LogViewSearchBar from './LogViewSearchBar'
|
|
||||||
import AuthGroupRows from './AuthGroupRows'
|
import AuthGroupRows from './AuthGroupRows'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CaliumRequestSearchBar,
|
|
||||||
CaliumRequestRegistModal,
|
CaliumRequestRegistModal,
|
||||||
AdminViewSearchBar,
|
|
||||||
AuthRegistBar,
|
AuthRegistBar,
|
||||||
LogViewSearchBar,
|
|
||||||
LogViewModal,
|
LogViewModal,
|
||||||
AuthGroupRows
|
AuthGroupRows
|
||||||
}
|
}
|
||||||
75
src/components/common/CircularProgress.js
Normal file
75
src/components/common/CircularProgress.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
// 원형 프로그레스 컴포넌트
|
||||||
|
const CircularProgress = ({
|
||||||
|
progress,
|
||||||
|
size = 40,
|
||||||
|
strokeWidth,
|
||||||
|
backgroundColor = '#E0E0E0',
|
||||||
|
progressColor = '#4A90E2',
|
||||||
|
textColor = '#4A90E2',
|
||||||
|
showText = true,
|
||||||
|
textSize,
|
||||||
|
className
|
||||||
|
}) => {
|
||||||
|
// 기본값 계산
|
||||||
|
const actualStrokeWidth = strokeWidth || size * 0.1; // 프로그레스 바 두께
|
||||||
|
const radius = (size - actualStrokeWidth) / 2; // 원의 반지름
|
||||||
|
const circumference = 2 * Math.PI * radius; // 원의 둘레
|
||||||
|
const strokeDashoffset = circumference - (progress / 100) * circumference; // 진행률에 따른 offset 계산
|
||||||
|
const actualTextSize = textSize || Math.max(10, size * 0.3); // 텍스트 크기
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProgressContainer size={size} className={className}>
|
||||||
|
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
||||||
|
{/* 배경 원 */}
|
||||||
|
<circle
|
||||||
|
cx={size / 2}
|
||||||
|
cy={size / 2}
|
||||||
|
r={radius}
|
||||||
|
fill="none"
|
||||||
|
stroke={backgroundColor}
|
||||||
|
strokeWidth={actualStrokeWidth}
|
||||||
|
/>
|
||||||
|
{/* 진행률 표시 원 */}
|
||||||
|
<circle
|
||||||
|
cx={size / 2}
|
||||||
|
cy={size / 2}
|
||||||
|
r={radius}
|
||||||
|
fill="none"
|
||||||
|
stroke={progressColor}
|
||||||
|
strokeWidth={actualStrokeWidth}
|
||||||
|
strokeDasharray={circumference}
|
||||||
|
strokeDashoffset={strokeDashoffset}
|
||||||
|
strokeLinecap="round"
|
||||||
|
transform={`rotate(-90 ${size / 2} ${size / 2})`}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{showText && (
|
||||||
|
<ProgressText color={textColor} fontSize={actualTextSize}>
|
||||||
|
{`${Math.round(progress)}%`}
|
||||||
|
</ProgressText>
|
||||||
|
)}
|
||||||
|
</ProgressContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CircularProgress;
|
||||||
|
|
||||||
|
// 스타일 컴포넌트
|
||||||
|
const ProgressContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
|
width: ${props => props.size}px;
|
||||||
|
height: ${props => props.size}px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProgressText = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
font-size: ${props => props.fontSize}px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: ${props => props.color};
|
||||||
|
`;
|
||||||
@@ -52,7 +52,7 @@ const SingleTimePicker = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormLabel>{label}</FormLabel>
|
{label && <FormLabel>{label}</FormLabel>}
|
||||||
<TimeContainer>
|
<TimeContainer>
|
||||||
<StyledSelectInput
|
<StyledSelectInput
|
||||||
onChange={handleTimeChange}
|
onChange={handleTimeChange}
|
||||||
|
|||||||
42
src/components/common/DownloadProgress.js
Normal file
42
src/components/common/DownloadProgress.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const DownloadProgress = ({ progress }) => {
|
||||||
|
return (
|
||||||
|
<ProgressWrapper>
|
||||||
|
<ProgressText>다운로드 중... {progress}%</ProgressText>
|
||||||
|
<ProgressBarContainer>
|
||||||
|
<ProgressBarFill style={{ width: `${progress}%` }} />
|
||||||
|
</ProgressBarContainer>
|
||||||
|
</ProgressWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DownloadProgress;
|
||||||
|
|
||||||
|
const ProgressWrapper = styled.div`
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProgressText = styled.div`
|
||||||
|
margin-bottom: 5px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProgressBarContainer = styled.div`
|
||||||
|
height: 20px;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProgressBarFill = styled.div`
|
||||||
|
height: 100%;
|
||||||
|
background-color: #4caf50;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
`;
|
||||||
@@ -9,9 +9,8 @@ import { useEffect, useState } from 'react';
|
|||||||
import Button from '../button/Button';
|
import Button from '../button/Button';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { AuthInfo } from '../../../apis';
|
import { AuthInfo } from '../../../apis';
|
||||||
import { authType } from '../../../assets/data';
|
|
||||||
import { menuConfig } from '../../../assets/data/menuConfig';
|
|
||||||
import { getMenuConfig } from '../../../utils';
|
import { getMenuConfig } from '../../../utils';
|
||||||
|
import { adminAuthLevel } from '../../../assets/data/types';
|
||||||
|
|
||||||
const Navi = () => {
|
const Navi = () => {
|
||||||
const token = sessionStorage.getItem('token');
|
const token = sessionStorage.getItem('token');
|
||||||
@@ -71,54 +70,16 @@ const Navi = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// const menu = [
|
const isClickable = (submenu) => {
|
||||||
// {
|
switch (userInfo.auth_level_type) {
|
||||||
// title: '운영자 관리',
|
case adminAuthLevel.DEVELOPER:
|
||||||
// link: '/usermanage',
|
case adminAuthLevel.READER:
|
||||||
// access: userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.adminSearchRead || auth.id === authType.adminLogSearchRead || auth.id === authType.authoritySettingRead || auth.id === authType.caliumRequestRead),
|
case adminAuthLevel.MASTER:
|
||||||
// submenu: [
|
return true;
|
||||||
// { title: '운영자 조회', link: '/usermanage/adminview', id: authType.adminSearchRead },
|
default:
|
||||||
// { title: '사용 이력 조회', link: '/usermanage/logview', id: authType.adminLogSearchRead },
|
return submenu.authLevel === adminAuthLevel.NONE && userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === submenu.id);
|
||||||
// { title: '권한 설정', link: '/usermanage/authsetting', id: authType.authoritySettingRead },
|
}
|
||||||
// { title: '칼리움 요청', link: '/usermanage/caliumrequest', id: authType.caliumRequestRead },
|
}
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: '지표 관리',
|
|
||||||
// link: '/indexmanage',
|
|
||||||
// access: userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.userIndicatorsRead || auth.id === authType.economicIndicatorsRead),
|
|
||||||
// submenu: [
|
|
||||||
// { title: '유저 지표', link: '/indexmanage/userindex', id: authType.userIndicatorsRead },
|
|
||||||
// { title: '경제 지표', link: '/indexmanage/economicindex', id: authType.economicIndicatorsRead },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: '운영 정보 관리',
|
|
||||||
// link: '/datamanage',
|
|
||||||
// access: userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.userSearchRead || auth.id === authType.contentSearchRead || auth.id === authType.gameLogRead || auth.id === authType.cryptoRead),
|
|
||||||
// submenu: [
|
|
||||||
// { title: '유저 조회', link: '/datamanage/userview', id: authType.userIndicatorsRead },
|
|
||||||
// { title: '컨텐츠 조회', link: '/datamanage/contentsview', id: authType.contentSearchRead },
|
|
||||||
// { title: '게임 로그 조회', link: '/datamanage/gamelogview', id: authType.gameLogRead },
|
|
||||||
// { title: '크립토 조회', link: '/datamanage/cryptview', id: authType.cryptoRead },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: '운영 서비스 관리',
|
|
||||||
// link: '/servicemanage',
|
|
||||||
// access: userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.inGameRead || auth.id === authType.whiteListRead || auth.id === authType.mailRead
|
|
||||||
// || auth.id === authType.blackListRead || auth.id === authType.reportRead || auth.id === authType.itemRead || auth.id === authType.eventRead ),
|
|
||||||
// submenu: [
|
|
||||||
// { title: '인게임 메시지', link: '/servicemanage/board', id: authType.inGameRead },
|
|
||||||
// // { title: '화이트리스트', link: '/servicemanage/whitelist', id: authType.whiteListRead },
|
|
||||||
// { title: '우편', link: '/servicemanage/mail', id: authType.mailRead },
|
|
||||||
// { title: '이용자 제재', link: '/servicemanage/userblock', id: authType.blackListRead },
|
|
||||||
// { title: '신고내역', link: '/servicemanage/reportlist', id: authType.reportRead },
|
|
||||||
// // { title: '아이템 복구 및 삭제', link: '/servicemanage/items', id: authType.itemRead },
|
|
||||||
// { title: '보상 이벤트 관리', link: '/servicemanage/event', id: authType.eventRead },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// ];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -133,14 +94,14 @@ const Navi = () => {
|
|||||||
</TopMenu>
|
</TopMenu>
|
||||||
)}
|
)}
|
||||||
<SubMenu>
|
<SubMenu>
|
||||||
{item.submenu &&
|
{item.submenu && userInfo &&
|
||||||
item.submenu.map((submenu, idx) => {
|
item.submenu.map((submenu, idx) => {
|
||||||
return (
|
return (
|
||||||
<SubMenuItem key={idx} $isclickable={userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === submenu.id) ? 'true' : 'false'}>
|
<SubMenuItem key={idx} $isclickable={isClickable(submenu) ? 'true' : 'false'}>
|
||||||
<NavLink
|
<NavLink
|
||||||
to={userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === submenu.id) ? submenu.link : location.pathname}
|
to={isClickable(submenu) ? submenu.link : location.pathname}
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === submenu.id) ? handleLink(e) : handleModalClose();
|
isClickable(submenu) ? handleLink(e) : handleModalClose();
|
||||||
}}>
|
}}>
|
||||||
{submenu.title}
|
{submenu.title}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ const Profile = () => {
|
|||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const token = sessionStorage.getItem('token');
|
const token = sessionStorage.getItem('token');
|
||||||
setInfoData(await AuthInfo(token));
|
await AuthInfo(token).then(data => {
|
||||||
|
setInfoData(data);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
56
src/components/common/Pagination/DynamoPagination.js
Normal file
56
src/components/common/Pagination/DynamoPagination.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const DynamoPagination = ({
|
||||||
|
pagination,
|
||||||
|
onNextPage,
|
||||||
|
onPrevPage,
|
||||||
|
className
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<PaginationButtons className={className}>
|
||||||
|
<PaginationButton
|
||||||
|
onClick={onPrevPage}
|
||||||
|
disabled={pagination.currentPage === 1}
|
||||||
|
>
|
||||||
|
이전
|
||||||
|
</PaginationButton>
|
||||||
|
<PageInfo>{pagination.currentPage}</PageInfo>
|
||||||
|
<PaginationButton
|
||||||
|
onClick={onNextPage}
|
||||||
|
disabled={!pagination.hasNextPage}
|
||||||
|
>
|
||||||
|
다음
|
||||||
|
</PaginationButton>
|
||||||
|
</PaginationButtons>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default DynamoPagination;
|
||||||
151
src/components/common/Pagination/FrontPagination.js
Normal file
151
src/components/common/Pagination/FrontPagination.js
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import PaginationIcon from '../../../assets/img/icon/icon-pagination.png';
|
||||||
|
|
||||||
|
const FrontPagination = ({
|
||||||
|
data, // 전체 데이터 배열
|
||||||
|
itemsPerPage, // 페이지당 표시할 항목 수
|
||||||
|
currentPage, // 현재 페이지
|
||||||
|
setCurrentPage, // 현재 페이지 설정 함수
|
||||||
|
pageLimit = 10, // 페이지 네비게이션에 표시할 페이지 수
|
||||||
|
onPageChange // 페이지 변경 시 호출될 콜백 함수 (선택 사항)
|
||||||
|
}) => {
|
||||||
|
const [blockNum, setBlockNum] = useState(0);
|
||||||
|
|
||||||
|
// 전체 페이지 수 계산
|
||||||
|
const totalItems = data?.length || 0;
|
||||||
|
const maxPage = Math.ceil(totalItems / itemsPerPage);
|
||||||
|
|
||||||
|
// 페이지 번호 배열 생성
|
||||||
|
const pageNumbers = [];
|
||||||
|
for (let i = 1; i <= maxPage; i++) {
|
||||||
|
pageNumbers.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 현재 블록에 표시할 페이지 번호
|
||||||
|
const v = blockNum * pageLimit;
|
||||||
|
const pArr = pageNumbers.slice(v, pageLimit + v);
|
||||||
|
|
||||||
|
const processPageData = useCallback(() => {
|
||||||
|
if (!data || !Array.isArray(data) || data.length === 0) return [];
|
||||||
|
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
return data.slice(startIndex, endIndex);
|
||||||
|
}, [data, currentPage, itemsPerPage]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (onPageChange) {
|
||||||
|
const pageData = processPageData();
|
||||||
|
onPageChange(pageData);
|
||||||
|
}
|
||||||
|
}, [processPageData, onPageChange]);
|
||||||
|
|
||||||
|
// itemsPerPage나 데이터가 변경되면 블록 초기화
|
||||||
|
useEffect(() => {
|
||||||
|
setBlockNum(0);
|
||||||
|
}, [itemsPerPage, totalItems]);
|
||||||
|
|
||||||
|
// 첫 페이지로 이동
|
||||||
|
const firstPage = () => {
|
||||||
|
setBlockNum(0);
|
||||||
|
setCurrentPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 마지막 페이지로 이동
|
||||||
|
const lastPage = () => {
|
||||||
|
setBlockNum(Math.ceil(maxPage / pageLimit) - 1);
|
||||||
|
setCurrentPage(maxPage);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이전 페이지로 이동
|
||||||
|
const prePage = () => {
|
||||||
|
if (currentPage <= 1) return;
|
||||||
|
|
||||||
|
if (currentPage - 1 <= pageLimit * blockNum) {
|
||||||
|
setBlockNum(n => n - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentPage(n => n - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 다음 페이지로 이동
|
||||||
|
const nextPage = () => {
|
||||||
|
if (currentPage >= maxPage) return;
|
||||||
|
|
||||||
|
if (pageLimit * (blockNum + 1) <= currentPage) {
|
||||||
|
setBlockNum(n => n + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentPage(n => n + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 특정 페이지로 이동
|
||||||
|
const clickPage = number => {
|
||||||
|
setCurrentPage(number);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 현재 표시 중인 항목 범위 계산 (예: "1-10 / 총 100개")
|
||||||
|
const startItem = totalItems === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1;
|
||||||
|
const endItem = Math.min(currentPage * itemsPerPage, totalItems);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PaginationWrapper>
|
||||||
|
<Button $position="0" onClick={firstPage} disabled={currentPage === 1} />
|
||||||
|
<Button $position="-20px" onClick={prePage} disabled={currentPage === 1} />
|
||||||
|
|
||||||
|
{pArr.map(number => (
|
||||||
|
<PageNum
|
||||||
|
$state={currentPage === number ? 'on' : ''}
|
||||||
|
key={number}
|
||||||
|
onClick={() => clickPage(number)}
|
||||||
|
>
|
||||||
|
{number}
|
||||||
|
</PageNum>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Button $position="-40px" onClick={nextPage} disabled={currentPage === maxPage} />
|
||||||
|
<Button $position="-60px" onClick={lastPage} disabled={currentPage === maxPage} />
|
||||||
|
</PaginationWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FrontPagination;
|
||||||
|
|
||||||
|
const PaginationWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Button = styled.button`
|
||||||
|
background: url('${PaginationIcon}') no-repeat;
|
||||||
|
background-position: ${props => props.$position} 0;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
opacity: ${props => props.disabled ? 0.5 : 1};
|
||||||
|
cursor: ${props => props.disabled ? 'default' : 'pointer'};
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-position: ${props => props.$position} ${props => props.disabled ? '0' : '-20px'};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PageNum = styled.div`
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: ${props => (props.$state === 'on' ? '#2c2c2c' : '#CECECE')};
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #2c2c2c;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { styled } from 'styled-components';
|
import { styled } from 'styled-components';
|
||||||
import { TextInput, SelectInput, SearchBarAlert } from '../../../styles/Components';
|
import { TextInput, SelectInput, SearchBarAlert, BtnWrapper } from '../../../styles/Components';
|
||||||
|
import Button from '../button/Button';
|
||||||
|
|
||||||
const SearchBarLayout = ({ firstColumnData, secondColumnData, direction }) => {
|
const SearchBarLayout = ({ firstColumnData, secondColumnData, filter, direction, onReset, handleSubmit }) => {
|
||||||
return (
|
return (
|
||||||
<SearchbarStyle direction={direction}>
|
<SearchbarStyle direction={direction}>
|
||||||
<SearchRow>
|
<SearchRow>
|
||||||
@@ -16,6 +17,17 @@ const SearchBarLayout = ({ firstColumnData, secondColumnData, direction }) => {
|
|||||||
))}
|
))}
|
||||||
</SearchRow>
|
</SearchRow>
|
||||||
)}
|
)}
|
||||||
|
{filter && (
|
||||||
|
<SearchRow>
|
||||||
|
{filter}
|
||||||
|
</SearchRow>
|
||||||
|
)}
|
||||||
|
<SearchRow>
|
||||||
|
<BtnWrapper $gap="8px">
|
||||||
|
<Button theme="search" text="검색" handleClick={handleSubmit} type="button" />
|
||||||
|
<Button theme="reset" handleClick={onReset} type="button" />
|
||||||
|
</BtnWrapper>
|
||||||
|
</SearchRow>
|
||||||
</SearchbarStyle>
|
</SearchbarStyle>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -23,16 +35,16 @@ const SearchBarLayout = ({ firstColumnData, secondColumnData, direction }) => {
|
|||||||
export default SearchBarLayout;
|
export default SearchBarLayout;
|
||||||
|
|
||||||
const SearchbarStyle = styled.div`
|
const SearchbarStyle = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-top: 1px solid #000;
|
border-radius: 8px;
|
||||||
border-bottom: 1px solid #000;
|
border: 1px solid #ddd;
|
||||||
margin: 0 0 40px;
|
margin: 0 0 40px;
|
||||||
flex-flow: ${props => props.direction};
|
flex-flow: ${props => props.direction};
|
||||||
gap: ${props => (props.direction === 'column' ? '20px' : '20px 0')};
|
gap: ${props => (props.direction === 'column' ? '20px' : '20px 0')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SearchItem = styled.div`
|
const SearchItem = styled.div`
|
||||||
@@ -54,4 +66,10 @@ const SearchRow = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 20px 0;
|
gap: 20px 0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
padding-top: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
import { ViewTitleCountType } from '../../../assets/data';
|
import { ViewTitleCountType } from '../../../assets/data';
|
||||||
import { TitleItem, TitleItemLabel, TitleItemValue } from '../../../styles/ModuleComponents';
|
import { TitleItem, TitleItemLabel, TitleItemValue } from '../../../styles/ModuleComponents';
|
||||||
|
|
||||||
const ViewTableInfo = ({children, total, total_all, handleOrderBy, handlePageSize, countType = ViewTitleCountType.total}) => {
|
const ViewTableInfo = ({children, total, total_all, orderType, handleOrderBy, pageType, handlePageSize, countType = ViewTitleCountType.total}) => {
|
||||||
return (
|
return (
|
||||||
<TableInfo>
|
<TableInfo>
|
||||||
{total !== undefined && total_all !== undefined &&
|
{total !== undefined && total_all !== undefined &&
|
||||||
@@ -27,18 +27,44 @@ const ViewTableInfo = ({children, total, total_all, handleOrderBy, handlePageSiz
|
|||||||
}
|
}
|
||||||
</ListCount>}
|
</ListCount>}
|
||||||
<ListOption>
|
<ListOption>
|
||||||
<SelectInput className="input-select" onChange={e => handleOrderBy(e)}>
|
<OrderBySelect orderType={orderType} handleOrderBy={handleOrderBy} />
|
||||||
<option value="DESC">내림차순</option>
|
<PageSelect pageType={pageType} handlePageSize={handlePageSize} />
|
||||||
<option value="ASC">오름차순</option>
|
|
||||||
</SelectInput>
|
|
||||||
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e)}>
|
|
||||||
<option value="50">50개</option>
|
|
||||||
<option value="100">100개</option>
|
|
||||||
</SelectInput>
|
|
||||||
{children}
|
{children}
|
||||||
</ListOption>
|
</ListOption>
|
||||||
</TableInfo>
|
</TableInfo>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const OrderBySelect = ({orderType, handleOrderBy}) => {
|
||||||
|
return(
|
||||||
|
orderType === "asc" ?
|
||||||
|
<SelectInput className="input-select" onChange={e => handleOrderBy(e.target.value)}>
|
||||||
|
<option value="ASC">오름차순</option>
|
||||||
|
<option value="DESC">내림차순</option>
|
||||||
|
</SelectInput>
|
||||||
|
:
|
||||||
|
<SelectInput className="input-select" onChange={e => handleOrderBy(e.target.value)}>
|
||||||
|
<option value="DESC">내림차순</option>
|
||||||
|
<option value="ASC">오름차순</option>
|
||||||
|
</SelectInput>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PageSelect = ({pageType, handlePageSize}) => {
|
||||||
|
return(
|
||||||
|
pageType === "B" ?
|
||||||
|
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e.target.value)}>
|
||||||
|
<option value="500">500개</option>
|
||||||
|
<option value="1000">1000개</option>
|
||||||
|
<option value="5000">5000개</option>
|
||||||
|
<option value="10000">10000개</option>
|
||||||
|
</SelectInput>
|
||||||
|
:
|
||||||
|
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e.target.value)}>
|
||||||
|
<option value="50">50개</option>
|
||||||
|
<option value="100">100개</option>
|
||||||
|
</SelectInput>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default ViewTableInfo;
|
export default ViewTableInfo;
|
||||||
@@ -1,111 +1,506 @@
|
|||||||
import * as XLSX from 'xlsx-js-style';
|
import * as XLSX from 'xlsx-js-style';
|
||||||
import { ExcelDownButton } from '../../../styles/ModuleComponents';
|
import { ExcelDownButton } from '../../../styles/ModuleComponents';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const ExcelDownloadButton = ({ tableRef, data, fileName = 'download.xlsx', sheetName = 'Sheet1', onLoadingChange }) => {
|
||||||
|
const [isDownloading, setIsDownloading] = useState(false);
|
||||||
|
const [lastProgress, setLastProgress] = useState(0);
|
||||||
|
|
||||||
|
// 타임아웃 감지 및 처리
|
||||||
|
useEffect(() => {
|
||||||
|
let timeoutTimer;
|
||||||
|
|
||||||
|
if (isDownloading && lastProgress >= 95) {
|
||||||
|
// 최종 단계에서 타임아웃 감지 타이머 설정
|
||||||
|
timeoutTimer = setTimeout(() => {
|
||||||
|
// 진행 상태가 여전히 변하지 않았다면 타임아웃으로 간주
|
||||||
|
if (isDownloading && lastProgress >= 95) {
|
||||||
|
console.log("Excel download timeout detected, completing process");
|
||||||
|
setIsDownloading(false);
|
||||||
|
if (onLoadingChange) {
|
||||||
|
onLoadingChange({ loading: false, progress: 100 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 15000); // 15초 타임아웃
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (timeoutTimer) clearTimeout(timeoutTimer);
|
||||||
|
};
|
||||||
|
}, [isDownloading, lastProgress, onLoadingChange]);
|
||||||
|
|
||||||
const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName = 'Sheet1' }) => {
|
|
||||||
const isNumeric = (value) => {
|
const isNumeric = (value) => {
|
||||||
// 숫자 또는 숫자 문자열인지 확인
|
// 숫자 또는 숫자 문자열인지 확인
|
||||||
return !isNaN(value) && !isNaN(parseFloat(value));
|
return !isNaN(value) && !isNaN(parseFloat(value));
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadExcel = () => {
|
// 테두리 스타일 정의
|
||||||
try {
|
const borderStyle = {
|
||||||
if (!tableRef.current) return;
|
style: "thin",
|
||||||
|
color: { rgb: "000000" }
|
||||||
|
};
|
||||||
|
|
||||||
const tableElement = tableRef.current;
|
// 기본 셀 스타일
|
||||||
const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
|
const baseCellStyle = {
|
||||||
const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
font: {
|
||||||
|
name: "맑은 고딕",
|
||||||
// 헤더 데이터 추출
|
sz: 11
|
||||||
const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);
|
},
|
||||||
|
border: {
|
||||||
// 바디 데이터 추출 및 숫자 타입 처리
|
top: borderStyle,
|
||||||
const bodyData = Array.from(bodyRows).map(row =>
|
bottom: borderStyle,
|
||||||
Array.from(row.cells).map(cell => {
|
left: borderStyle,
|
||||||
const value = cell.textContent;
|
right: borderStyle
|
||||||
return isNumeric(value) ? parseFloat(value) : value;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// 워크북 생성
|
|
||||||
const wb = XLSX.utils.book_new();
|
|
||||||
|
|
||||||
// 테두리 스타일 정의
|
|
||||||
const borderStyle = {
|
|
||||||
style: "thin",
|
|
||||||
color: { rgb: "000000" }
|
|
||||||
};
|
|
||||||
|
|
||||||
// 스타일 정의
|
|
||||||
const centerStyle = {
|
|
||||||
font: {
|
|
||||||
name: "맑은 고딕",
|
|
||||||
sz: 11
|
|
||||||
},
|
|
||||||
alignment: {
|
|
||||||
horizontal: 'right',
|
|
||||||
vertical: 'right'
|
|
||||||
},
|
|
||||||
border: {
|
|
||||||
top: borderStyle,
|
|
||||||
bottom: borderStyle,
|
|
||||||
left: borderStyle,
|
|
||||||
right: borderStyle
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const headerStyle = {
|
|
||||||
alignment: {
|
|
||||||
horizontal: 'center',
|
|
||||||
vertical: 'center'
|
|
||||||
},
|
|
||||||
fill: {
|
|
||||||
fgColor: { rgb: "d9e1f2" },
|
|
||||||
patternType: "solid"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 데이터에 스타일 적용
|
|
||||||
const wsData = [
|
|
||||||
// 헤더 행
|
|
||||||
headers.map(h => ({
|
|
||||||
v: h,
|
|
||||||
s: headerStyle
|
|
||||||
})),
|
|
||||||
// 데이터 행들
|
|
||||||
...bodyData.map(row =>
|
|
||||||
row.map(cell => ({
|
|
||||||
v: cell,
|
|
||||||
s: centerStyle
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
];
|
|
||||||
|
|
||||||
// 워크시트 생성
|
|
||||||
const ws = XLSX.utils.aoa_to_sheet(wsData);
|
|
||||||
|
|
||||||
// 열 너비 설정 (최소 8, 최대 50)
|
|
||||||
ws['!cols'] = headers.map((_, index) => {
|
|
||||||
const maxLength = Math.max(
|
|
||||||
headers[index].length * 2,
|
|
||||||
...bodyData.map(row => String(row[index] || '').length * 1.2)
|
|
||||||
);
|
|
||||||
return { wch: Math.max(8, Math.min(50, maxLength)) };
|
|
||||||
});
|
|
||||||
|
|
||||||
// 워크시트를 워크북에 추가
|
|
||||||
XLSX.utils.book_append_sheet(wb, ws, sheetName);
|
|
||||||
|
|
||||||
// 엑셀 파일 다운로드
|
|
||||||
XLSX.writeFile(wb, fileName);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Excel download failed:', error);
|
|
||||||
alert('엑셀 다운로드 중 오류가 발생했습니다.');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 헤더 스타일
|
||||||
|
const headerStyle = {
|
||||||
|
...baseCellStyle,
|
||||||
|
font: {
|
||||||
|
...baseCellStyle.font,
|
||||||
|
bold: true
|
||||||
|
},
|
||||||
|
alignment: {
|
||||||
|
horizontal: 'center',
|
||||||
|
vertical: 'center'
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
fgColor: { rgb: "d9e1f2" },
|
||||||
|
patternType: "solid"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 기본 데이터 셀 스타일
|
||||||
|
const dataStyle = {
|
||||||
|
...baseCellStyle,
|
||||||
|
alignment: {
|
||||||
|
horizontal: 'left',
|
||||||
|
vertical: 'center',
|
||||||
|
wrapText: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const flattenObject = (obj, prefix = '') => {
|
||||||
|
return Object.keys(obj).reduce((acc, key) => {
|
||||||
|
const prefixedKey = prefix ? `${prefix}.${key}` : key;
|
||||||
|
|
||||||
|
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
|
||||||
|
Object.assign(acc, flattenObject(obj[key], prefixedKey));
|
||||||
|
} else if (Array.isArray(obj[key])) {
|
||||||
|
// 배열은 JSON 문자열로 변환
|
||||||
|
acc[prefixedKey] = JSON.stringify(obj[key]);
|
||||||
|
} else {
|
||||||
|
acc[prefixedKey] = obj[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLoadingState = (newProgress) => {
|
||||||
|
setLastProgress(newProgress);
|
||||||
|
if (onLoadingChange && typeof onLoadingChange === 'function') {
|
||||||
|
onLoadingChange({loading: true, progress: newProgress});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadTableExcel = async () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
if (!tableRef || !tableRef.current) {
|
||||||
|
reject(new Error('테이블 참조가 없습니다.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Worker에 전달할 데이터 추출
|
||||||
|
updateLoadingState(10);
|
||||||
|
|
||||||
|
// 메인 스레드에서 데이터 추출
|
||||||
|
const tableElement = tableRef.current;
|
||||||
|
const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
|
||||||
|
const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
||||||
|
|
||||||
|
// 일반 행만 포함 (상세 행 제외)
|
||||||
|
const normalBodyRows = Array.from(bodyRows).filter(row => {
|
||||||
|
const hasTdWithColspan = Array.from(row.cells).some(cell => cell.hasAttribute('colspan'));
|
||||||
|
return !hasTdWithColspan;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 헤더 데이터 추출
|
||||||
|
const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);
|
||||||
|
|
||||||
|
// 바디 데이터 추출 및 숫자 타입 처리
|
||||||
|
const bodyData = normalBodyRows.map(row =>
|
||||||
|
Array.from(row.cells).map(cell => {
|
||||||
|
const value = cell.textContent;
|
||||||
|
return isNumeric(value) ? parseFloat(value) : value;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
updateLoadingState(30);
|
||||||
|
|
||||||
|
// 큰 데이터셋 처리를 위해 setTimeout으로 이벤트 루프 차단 방지
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
// 워크북 생성
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
updateLoadingState(50);
|
||||||
|
|
||||||
|
// 처리는 여러 단계로 나누어 이벤트 루프 차단 최소화
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
// 데이터에 스타일 적용
|
||||||
|
const wsData = [
|
||||||
|
// 헤더 행
|
||||||
|
headers.map(h => ({
|
||||||
|
v: h,
|
||||||
|
s: headerStyle
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
|
||||||
|
// 데이터 행 추가 (메모리 사용량 최소화를 위해 별도 처리)
|
||||||
|
const chunkSize = 1000; // 한 번에 처리할 행 수
|
||||||
|
let currentIndex = 0;
|
||||||
|
|
||||||
|
function processDataChunk() {
|
||||||
|
updateLoadingState(50 + Math.floor((currentIndex / bodyData.length) * 30));
|
||||||
|
|
||||||
|
const end = Math.min(currentIndex + chunkSize, bodyData.length);
|
||||||
|
for (let i = currentIndex; i < end; i++) {
|
||||||
|
wsData.push(
|
||||||
|
bodyData[i].map(cell => ({
|
||||||
|
v: cell,
|
||||||
|
s: dataStyle
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndex = end;
|
||||||
|
|
||||||
|
if (currentIndex < bodyData.length) {
|
||||||
|
// 아직 처리할 데이터가 남아있으면 다음 청크 처리 예약
|
||||||
|
setTimeout(processDataChunk, 0);
|
||||||
|
} else {
|
||||||
|
// 모든 데이터 처리 완료 후 워크시트 생성
|
||||||
|
finishExcelCreation(wsData, headers, wb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 첫 번째 청크 처리 시작
|
||||||
|
processDataChunk();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
function finishExcelCreation(wsData, headers, wb) {
|
||||||
|
try {
|
||||||
|
updateLoadingState(80);
|
||||||
|
|
||||||
|
// 워크시트 생성
|
||||||
|
const ws = XLSX.utils.aoa_to_sheet(wsData);
|
||||||
|
|
||||||
|
// 열 너비 설정 (최소 8, 최대 50)
|
||||||
|
ws['!cols'] = headers.map((_, index) => {
|
||||||
|
// 데이터의 일부만 샘플링하여 열 너비 계산 (전체 계산 시 성능 문제)
|
||||||
|
const samplingSize = Math.min(bodyData.length, 500);
|
||||||
|
const sampledRows = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < samplingSize; i++) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * bodyData.length);
|
||||||
|
sampledRows.push(bodyData[randomIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxLength = Math.max(
|
||||||
|
headers[index].length * 2,
|
||||||
|
...sampledRows.map(row => {
|
||||||
|
if (row[index] === undefined) return 0;
|
||||||
|
return String(row[index] || '').length * 1.2;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return { wch: Math.max(8, Math.min(50, maxLength)) };
|
||||||
|
});
|
||||||
|
|
||||||
|
updateLoadingState(90);
|
||||||
|
|
||||||
|
// 최종 다운로드는 별도 타임아웃에서 수행하여 UI 업데이트 가능하게 함
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
// 워크시트를 워크북에 추가
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, sheetName);
|
||||||
|
|
||||||
|
updateLoadingState(95);
|
||||||
|
|
||||||
|
// 파일 다운로드 전 마지막 UI 업데이트를 위한 지연
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
// 엑셀 파일 다운로드
|
||||||
|
XLSX.writeFile(wb, fileName);
|
||||||
|
updateLoadingState(100);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const chunkArray = (array, chunkSize) => {
|
||||||
|
const chunks = [];
|
||||||
|
for (let i = 0; i < array.length; i += chunkSize) {
|
||||||
|
chunks.push(array.slice(i, i + chunkSize));
|
||||||
|
}
|
||||||
|
return chunks;
|
||||||
|
};
|
||||||
|
|
||||||
|
const processDataChunk = async (chunk, headers, allDataRows, processedCount, totalCount) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// 각 청크의 데이터를 처리
|
||||||
|
const rowsData = chunk.map(item => {
|
||||||
|
return headers.map(header => {
|
||||||
|
const value = item[header] !== undefined ? item[header] : '';
|
||||||
|
return {
|
||||||
|
v: value,
|
||||||
|
s: dataStyle
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 진행률 계산 및 콜백 호출
|
||||||
|
const newProgress = Math.min(95, Math.round(((processedCount + chunk.length) / totalCount) * 80) + 15);
|
||||||
|
updateLoadingState(newProgress);
|
||||||
|
|
||||||
|
// 처리된 데이터 행들을 전체 데이터 배열에 추가
|
||||||
|
allDataRows.push(...rowsData);
|
||||||
|
resolve();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadDataExcel = async () => {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
reject(new Error('다운로드할 데이터가 없습니다.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLoadingState(5);
|
||||||
|
|
||||||
|
// 데이터 플랫 변환 과정을 더 작은 청크로 나누기
|
||||||
|
const dataChunkSize = 2000; // 한 번에 처리할 데이터 아이템 수
|
||||||
|
const dataChunks = chunkArray(data, dataChunkSize);
|
||||||
|
let flattenedData = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < dataChunks.length; i++) {
|
||||||
|
await new Promise(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// 청크 내 아이템들을 플랫하게 변환
|
||||||
|
const chunkData = dataChunks[i].map(item => {
|
||||||
|
// 기본 필드
|
||||||
|
const baseData = {
|
||||||
|
'logTime': item.logTime,
|
||||||
|
'GUID': item.userGuid === 'None' ? '' : item.userGuid,
|
||||||
|
'Nickname': item.userNickname === 'None' ? '' : item.userNickname,
|
||||||
|
'Account ID': item.accountId === 'None' ? '' : item.accountId,
|
||||||
|
'Action': item.action,
|
||||||
|
'Domain': item.domain === 'None' ? '' : item.domain,
|
||||||
|
'Tran ID': item.tranId
|
||||||
|
};
|
||||||
|
|
||||||
|
// Actor 데이터 플랫하게 추가
|
||||||
|
const actorData = item.header && item.header.Actor ?
|
||||||
|
flattenObject(item.header.Actor, 'Actor') : {};
|
||||||
|
|
||||||
|
// Infos 데이터 플랫하게 추가
|
||||||
|
let infosData = {};
|
||||||
|
if (item.body && item.body.Infos && Array.isArray(item.body.Infos)) {
|
||||||
|
item.body.Infos.forEach((info) => {
|
||||||
|
infosData = {
|
||||||
|
...infosData,
|
||||||
|
...flattenObject(info, `Info`)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseData,
|
||||||
|
...actorData,
|
||||||
|
...infosData
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
flattenedData = [...flattenedData, ...chunkData];
|
||||||
|
const progress = 5 + Math.floor((i + 1) / dataChunks.length * 10);
|
||||||
|
updateLoadingState(progress);
|
||||||
|
resolve();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모든 항목의 모든 키 수집하여 헤더 생성
|
||||||
|
const allKeys = new Set();
|
||||||
|
|
||||||
|
// 헤더 수집도 청크로 나누기
|
||||||
|
for (let i = 0; i < flattenedData.length; i += dataChunkSize) {
|
||||||
|
await new Promise(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const end = Math.min(i + dataChunkSize, flattenedData.length);
|
||||||
|
for (let j = i; j < end; j++) {
|
||||||
|
Object.keys(flattenedData[j]).forEach(key => allKeys.add(key));
|
||||||
|
}
|
||||||
|
const progress = 15 + Math.floor((i + dataChunkSize) / flattenedData.length * 5);
|
||||||
|
updateLoadingState(progress);
|
||||||
|
resolve();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = Array.from(allKeys);
|
||||||
|
updateLoadingState(20);
|
||||||
|
|
||||||
|
// 워크북 생성
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
|
||||||
|
// 헤더 행 생성
|
||||||
|
const headerRow = headers.map(h => ({
|
||||||
|
v: h,
|
||||||
|
s: headerStyle
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 청크로 데이터 나누기
|
||||||
|
const chunkSize = 500; // 한 번에 처리할 행의 수 (더 작게 조정)
|
||||||
|
const rowChunks = chunkArray(flattenedData, chunkSize);
|
||||||
|
const allDataRows = [];
|
||||||
|
|
||||||
|
// 각 청크 처리
|
||||||
|
let processedCount = 0;
|
||||||
|
for (const chunk of rowChunks) {
|
||||||
|
await processDataChunk(chunk, headers, allDataRows, processedCount, flattenedData.length);
|
||||||
|
processedCount += chunk.length;
|
||||||
|
|
||||||
|
// 메모리 정리를 위한 가비지 컬렉션 힌트
|
||||||
|
if (processedCount % (chunkSize * 10) === 0) {
|
||||||
|
// 5000행마다 짧은 지연을 두어 가비지 컬렉션 기회 제공
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLoadingState(95);
|
||||||
|
|
||||||
|
// 워크시트 데이터 구성 및 파일 다운로드를 메인 로직과 분리
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
// 워크시트 데이터 구성
|
||||||
|
const wsData = [
|
||||||
|
// 헤더 행
|
||||||
|
headerRow,
|
||||||
|
// 데이터 행들
|
||||||
|
...allDataRows
|
||||||
|
];
|
||||||
|
|
||||||
|
// 워크시트 생성 (메모리 최적화)
|
||||||
|
const ws = XLSX.utils.aoa_to_sheet(wsData);
|
||||||
|
|
||||||
|
// 열 너비 설정 (성능 최적화를 위해 샘플링)
|
||||||
|
ws['!cols'] = headers.map((header) => {
|
||||||
|
// 헤더 길이와 샘플 데이터 길이를 기준으로 열 너비 결정
|
||||||
|
const sampleSize = Math.min(flattenedData.length, 500);
|
||||||
|
const samples = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < sampleSize; i++) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * flattenedData.length);
|
||||||
|
const item = flattenedData[randomIndex];
|
||||||
|
if (item[header] !== undefined) {
|
||||||
|
samples.push(String(item[header]).length * 1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxLength = Math.max(
|
||||||
|
header.length * 1.5,
|
||||||
|
...samples
|
||||||
|
);
|
||||||
|
|
||||||
|
return { wch: Math.max(10, Math.min(50, maxLength)) };
|
||||||
|
});
|
||||||
|
|
||||||
|
// 최종 단계 분리
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
// 워크시트를 워크북에 추가
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, sheetName);
|
||||||
|
|
||||||
|
// 엑셀 파일 다운로드
|
||||||
|
XLSX.writeFile(wb, fileName);
|
||||||
|
updateLoadingState(100);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownload = useCallback(async () => {
|
||||||
|
if (isDownloading) return; // 이미 다운로드 중이면 중복 실행 방지
|
||||||
|
|
||||||
|
setIsDownloading(true);
|
||||||
|
setLastProgress(0);
|
||||||
|
if (onLoadingChange) onLoadingChange({loading: true, progress: 0});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (tableRef) {
|
||||||
|
await downloadTableExcel();
|
||||||
|
} else if (data) {
|
||||||
|
await downloadDataExcel();
|
||||||
|
} else {
|
||||||
|
alert('유효한 데이터 소스가 없습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Excel download failed:', error);
|
||||||
|
alert('엑셀 다운로드 중 오류가 발생했습니다.');
|
||||||
|
} finally {
|
||||||
|
// 다운로드 완료 후 짧은 지연 시간을 두어 100% 상태를 잠시 보여줌
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsDownloading(false);
|
||||||
|
if (onLoadingChange) onLoadingChange({loading: false, progress: 100});
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, [tableRef, data, fileName, sheetName, isDownloading, onLoadingChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExcelDownButton onClick={downloadExcel}>
|
<ExcelDownButton onClick={handleDownload} disabled={isDownloading}>
|
||||||
엑셀 다운로드
|
{isDownloading ? '다운로드 중...' : '엑셀 다운로드'}
|
||||||
</ExcelDownButton>
|
</ExcelDownButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
113
src/components/common/button/ExcelDownButton_bak.js
Normal file
113
src/components/common/button/ExcelDownButton_bak.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import * as XLSX from 'xlsx-js-style';
|
||||||
|
import { ExcelDownButton } from '../../../styles/ModuleComponents';
|
||||||
|
|
||||||
|
const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName = 'Sheet1' }) => {
|
||||||
|
const isNumeric = (value) => {
|
||||||
|
// 숫자 또는 숫자 문자열인지 확인
|
||||||
|
return !isNaN(value) && !isNaN(parseFloat(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadExcel = () => {
|
||||||
|
try {
|
||||||
|
if (!tableRef.current) return;
|
||||||
|
|
||||||
|
const tableElement = tableRef.current;
|
||||||
|
const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
|
||||||
|
const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
||||||
|
|
||||||
|
// 헤더 데이터 추출
|
||||||
|
const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);
|
||||||
|
|
||||||
|
// 바디 데이터 추출 및 숫자 타입 처리
|
||||||
|
const bodyData = Array.from(bodyRows).map(row =>
|
||||||
|
Array.from(row.cells).map(cell => {
|
||||||
|
const value = cell.textContent;
|
||||||
|
return isNumeric(value) ? parseFloat(value) : value;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 워크북 생성
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
|
||||||
|
// 테두리 스타일 정의
|
||||||
|
const borderStyle = {
|
||||||
|
style: "thin",
|
||||||
|
color: { rgb: "000000" }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 스타일 정의
|
||||||
|
const centerStyle = {
|
||||||
|
font: {
|
||||||
|
name: "맑은 고딕",
|
||||||
|
sz: 11
|
||||||
|
},
|
||||||
|
alignment: {
|
||||||
|
horizontal: 'right',
|
||||||
|
vertical: 'right'
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
top: borderStyle,
|
||||||
|
bottom: borderStyle,
|
||||||
|
left: borderStyle,
|
||||||
|
right: borderStyle
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const headerStyle = {
|
||||||
|
alignment: {
|
||||||
|
horizontal: 'center',
|
||||||
|
vertical: 'center'
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
fgColor: { rgb: "d9e1f2" },
|
||||||
|
patternType: "solid"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 데이터에 스타일 적용
|
||||||
|
const wsData = [
|
||||||
|
// 헤더 행
|
||||||
|
headers.map(h => ({
|
||||||
|
v: h,
|
||||||
|
s: headerStyle
|
||||||
|
})),
|
||||||
|
// 데이터 행들
|
||||||
|
...bodyData.map(row =>
|
||||||
|
row.map(cell => ({
|
||||||
|
v: cell,
|
||||||
|
s: centerStyle
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
|
// 워크시트 생성
|
||||||
|
const ws = XLSX.utils.aoa_to_sheet(wsData);
|
||||||
|
|
||||||
|
// 열 너비 설정 (최소 8, 최대 50)
|
||||||
|
ws['!cols'] = headers.map((_, index) => {
|
||||||
|
const maxLength = Math.max(
|
||||||
|
headers[index].length * 2,
|
||||||
|
...bodyData.map(row => String(row[index] || '').length * 1.2)
|
||||||
|
);
|
||||||
|
return { wch: Math.max(8, Math.min(50, maxLength)) };
|
||||||
|
});
|
||||||
|
|
||||||
|
// 워크시트를 워크북에 추가
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, sheetName);
|
||||||
|
|
||||||
|
// 엑셀 파일 다운로드
|
||||||
|
XLSX.writeFile(wb, fileName);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Excel download failed:', error);
|
||||||
|
alert('엑셀 다운로드 중 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ExcelDownButton onClick={downloadExcel}>
|
||||||
|
엑셀 다운로드
|
||||||
|
</ExcelDownButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExcelDownloadButton;
|
||||||
62
src/components/common/button/TopButton.js
Normal file
62
src/components/common/button/TopButton.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Button = styled.button`
|
||||||
|
position: fixed;
|
||||||
|
bottom: 30px;
|
||||||
|
right: 30px;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #666666;
|
||||||
|
color: white;
|
||||||
|
font-size: 16px;
|
||||||
|
display: ${props => (props.$show ? 'flex' : 'none')};
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TopButton = () => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const toggleVisibility = () => {
|
||||||
|
if (window.pageYOffset > 300) {
|
||||||
|
setVisible(true);
|
||||||
|
} else {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', toggleVisibility);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('scroll', toggleVisibility);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const scrollToTop = () => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
$show={visible}
|
||||||
|
onClick={scrollToTop}
|
||||||
|
title="맨 위로 이동"
|
||||||
|
>
|
||||||
|
↑
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TopButton;
|
||||||
@@ -11,9 +11,13 @@ import DynamicModal from './modal/DynamicModal';
|
|||||||
import CustomConfirmModal from './modal/CustomConfirmModal';
|
import CustomConfirmModal from './modal/CustomConfirmModal';
|
||||||
import Modal from './modal/Modal';
|
import Modal from './modal/Modal';
|
||||||
import Pagination from './Pagination/Pagination';
|
import Pagination from './Pagination/Pagination';
|
||||||
|
import DynamoPagination from './Pagination/DynamoPagination';
|
||||||
|
import FrontPagination from './Pagination/FrontPagination';
|
||||||
import ViewTableInfo from './Table/ViewTableInfo';
|
import ViewTableInfo from './Table/ViewTableInfo';
|
||||||
import Loading from './Loading';
|
import Loading from './Loading';
|
||||||
|
import DownloadProgress from './DownloadProgress';
|
||||||
import CDivider from './CDivider';
|
import CDivider from './CDivider';
|
||||||
|
import TopButton from './button/TopButton';
|
||||||
export {
|
export {
|
||||||
DatePickerComponent,
|
DatePickerComponent,
|
||||||
DateTimeRangePicker,
|
DateTimeRangePicker,
|
||||||
@@ -38,5 +42,9 @@ export { DateTimeInput,
|
|||||||
Pagination,
|
Pagination,
|
||||||
ViewTableInfo,
|
ViewTableInfo,
|
||||||
Loading,
|
Loading,
|
||||||
CDivider
|
CDivider,
|
||||||
|
TopButton,
|
||||||
|
DynamoPagination,
|
||||||
|
FrontPagination,
|
||||||
|
DownloadProgress
|
||||||
};
|
};
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export {ivenTabType, modalTypes} from './types'
|
|
||||||
export {mailSendType, mailType, mailSendStatus, mailReceiveType, adminLevelType} from './options'
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
export const mailSendType = [
|
|
||||||
{ value: 'ALL', name: '전체' },
|
|
||||||
{ value: 'RESERVE_SEND', name: '예약 발송' },
|
|
||||||
{ value: 'DIRECT_SEND', name: '즉시 발송' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const mailSendStatus = [
|
|
||||||
{ value: 'ALL', name: '전체' },
|
|
||||||
{ value: 'WAIT', name: '대기' },
|
|
||||||
{ value: 'FINISH', name: '완료' },
|
|
||||||
{ value: 'FAIL', name: '실패' },
|
|
||||||
{ value: 'RUNNING', name: '전송중' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const mailType = [
|
|
||||||
{ value: 'ALL', name: '전체' },
|
|
||||||
{ value: 'SYSTEM_GUID', name: '시스템 안내' },
|
|
||||||
{ value: 'INSPECTION_COMPENSATION', name: '점검 보상' },
|
|
||||||
{ value: 'RECOVER_COMPENSATION', name: '복구 보상' },
|
|
||||||
{ value: 'EVENT_COMPENSATION', name: '이벤트 보상' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const mailReceiveType = [
|
|
||||||
{ value: 'ALL', name: '전체' },
|
|
||||||
{ value: 'SINGLE', name: '단일' },
|
|
||||||
{ value: 'MULTIPLE', name: '복수' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const adminLevelType = [
|
|
||||||
{ value: '0', name: '없음' },
|
|
||||||
{ value: '1', name: 'GM' },
|
|
||||||
{ value: '2', name: 'Super GM' },
|
|
||||||
{ value: '3', name: 'Developer' },
|
|
||||||
]
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
export const ivenTabType = {
|
|
||||||
CLOTH: "cloth",
|
|
||||||
PROP: "prop",
|
|
||||||
BEAUTY: "beauty",
|
|
||||||
TATTOO: "tattoo",
|
|
||||||
CURRENCY: "currency",
|
|
||||||
ETC: "etc"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const modalTypes = {
|
|
||||||
confirmOkCancel: "confirmOkCancel",
|
|
||||||
completed: "completed",
|
|
||||||
childOkCancel: "childOkCancel",
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { AuthModal } from '../components/common';
|
import { AuthModal } from '../components/common';
|
||||||
import { authList } from '../store/authList';
|
import { authList } from '../store/authList';
|
||||||
import { INITIAL_CURRENT_PAGE, INITIAL_PAGE_SIZE } from '../assets/data/adminConstants';
|
import { INITIAL_CURRENT_PAGE, INITIAL_PAGE_SIZE } from '../assets/data/adminConstants';
|
||||||
import { PageSkeleton } from '../components/Skeleton/PageSkeleton';
|
import { PageSkeleton } from '../components/Skeleton/PageSkeleton';
|
||||||
|
import { adminAuthLevel, authType } from '../assets/data/types';
|
||||||
|
|
||||||
export const useDateTimeState = (initialDate = '') => {
|
export const useDateTimeState = (initialDate = '') => {
|
||||||
const [date, setDate] = useState(initialDate);
|
const [date, setDate] = useState(initialDate);
|
||||||
@@ -67,10 +68,22 @@ export const withAuth = (requiredAuth) => (WrappedComponent) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasRequiredAuth = userInfo.auth_list &&
|
const authLevelPermissions = {
|
||||||
userInfo.auth_list.some(auth => auth.id === requiredAuth);
|
[adminAuthLevel.DEVELOPER]: [authType.levelReader, authType.levelMaster, authType.levelDeveloper],
|
||||||
|
[adminAuthLevel.MASTER]: [authType.levelReader, authType.levelMaster],
|
||||||
|
[adminAuthLevel.READER]: [authType.levelReader]
|
||||||
|
};
|
||||||
|
|
||||||
if (!hasRequiredAuth) {
|
const allowedAuthTypes = authLevelPermissions[userInfo.auth_level_type] || [];
|
||||||
|
const adminAuth = allowedAuthTypes.includes(requiredAuth);
|
||||||
|
|
||||||
|
if (adminAuth) {
|
||||||
|
return <WrappedComponent {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasRequiredAuth = userInfo.auth_list.some(auth => auth.id === requiredAuth);
|
||||||
|
|
||||||
|
if (!hasRequiredAuth && !adminAuth) {
|
||||||
return <AuthModal />;
|
return <AuthModal />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,10 +94,20 @@ export const withAuth = (requiredAuth) => (WrappedComponent) => {
|
|||||||
|
|
||||||
export const useTable = (tableData = [], options = {mode: 'multi'}) => {
|
export const useTable = (tableData = [], options = {mode: 'multi'}) => {
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
|
const tableDataRef = useRef(tableData);
|
||||||
|
|
||||||
// tableData가 변경될 때 선택된 행 초기화
|
// tableData가 변경될 때 선택된 행 초기화
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedRows([]);
|
const hasDataChanged =
|
||||||
|
tableData.length !== tableDataRef.current.length ||
|
||||||
|
tableData.some((item, index) =>
|
||||||
|
tableDataRef.current[index]?.id !== item.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasDataChanged) {
|
||||||
|
setSelectedRows([]);
|
||||||
|
tableDataRef.current = tableData;
|
||||||
|
}
|
||||||
}, [tableData]);
|
}, [tableData]);
|
||||||
|
|
||||||
// 단일 행 선택/해제
|
// 단일 행 선택/해제
|
||||||
@@ -166,3 +189,76 @@ export const useDataFetch = (fetchFunction, dependencies = [], initialState = nu
|
|||||||
setData
|
setData
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useDynamoDBPagination = (fetchFunction) => {
|
||||||
|
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [pagination, setPagination] = useState({
|
||||||
|
currentPage: 1,
|
||||||
|
pageKeys: { 1: null },
|
||||||
|
hasNextPage: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const resetPagination = useCallback(() => {
|
||||||
|
setPagination({
|
||||||
|
currentPage: 1,
|
||||||
|
pageKeys: { 1: null },
|
||||||
|
hasNextPage: false
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchPage = useCallback(async (page) => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const startKey = pagination.pageKeys[page];
|
||||||
|
const response = await fetchFunction(page, startKey);
|
||||||
|
|
||||||
|
setData(response);
|
||||||
|
|
||||||
|
const updatedPagination = { ...pagination, currentPage: page };
|
||||||
|
|
||||||
|
if (response.pageKey) {
|
||||||
|
updatedPagination.pageKeys[page + 1] = response.pageKey;
|
||||||
|
updatedPagination.hasNextPage = true;
|
||||||
|
} else {
|
||||||
|
updatedPagination.hasNextPage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPagination(updatedPagination);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('페이지 데이터 가져오기 오류:', error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [fetchFunction, pagination]);
|
||||||
|
|
||||||
|
const goToNextPage = useCallback(() => {
|
||||||
|
if (!pagination.hasNextPage) return;
|
||||||
|
|
||||||
|
const nextPage = pagination.currentPage + 1;
|
||||||
|
return fetchPage(nextPage);
|
||||||
|
}, [pagination, fetchPage]);
|
||||||
|
|
||||||
|
const goToPrevPage = useCallback(() => {
|
||||||
|
if (pagination.currentPage <= 1) return;
|
||||||
|
|
||||||
|
const prevPage = pagination.currentPage - 1;
|
||||||
|
return fetchPage(prevPage);
|
||||||
|
}, [pagination, fetchPage]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
pagination,
|
||||||
|
|
||||||
|
setData,
|
||||||
|
setPagination,
|
||||||
|
fetchPage,
|
||||||
|
goToNextPage,
|
||||||
|
goToPrevPage,
|
||||||
|
resetPagination
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
// 공통으로 사용될 함수를 관리하는 폴더입니다. 이 파일은 삭제하셔도 됩니다.
|
|
||||||
20
src/i18n.js
20
src/i18n.js
@@ -8,6 +8,7 @@ const resources = {
|
|||||||
DATE_KTC: '* UTC+9 한국시간 기준으로 설정 (UTC+0 자동 반영처리)',
|
DATE_KTC: '* UTC+9 한국시간 기준으로 설정 (UTC+0 자동 반영처리)',
|
||||||
NOT_ITEM: '존재하지 않는 아이템코드입니다.',
|
NOT_ITEM: '존재하지 않는 아이템코드입니다.',
|
||||||
REGIST_COMPLTE: "등록이 완료되었습니다.",
|
REGIST_COMPLTE: "등록이 완료되었습니다.",
|
||||||
|
INIT_COMPLTE: "초기화 등록이 완료되었습니다.\n순차적으로 처리됩니다.",
|
||||||
REGIST_FAIL: '등록에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
REGIST_FAIL: '등록에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
||||||
UPDATE_FAIL: '수정에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
UPDATE_FAIL: '수정에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
||||||
STOP_FAIL: '중단에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
STOP_FAIL: '중단에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
||||||
@@ -15,6 +16,9 @@ const resources = {
|
|||||||
API_FAIL: '처리 중 오류가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요. 오류가 지속될 경우, 담당자에게 문의해주세요.',
|
API_FAIL: '처리 중 오류가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요. 오류가 지속될 경우, 담당자에게 문의해주세요.',
|
||||||
USER_MAIL_DEL_CONFIRM: '해당 우편을 삭제하시겠습니까?',
|
USER_MAIL_DEL_CONFIRM: '해당 우편을 삭제하시겠습니까?',
|
||||||
USER_GM_CHANGE: 'GM 권한을 변경하시겠습니까?',
|
USER_GM_CHANGE: 'GM 권한을 변경하시겠습니까?',
|
||||||
|
USER_GM_CHANGE_COMPLETE: '권한변경을 완료하였습니다.',
|
||||||
|
USER_KICK_CONFIRM: '해당 유저를 Kick 하시겠습니까?',
|
||||||
|
USER_KICK_COMPLETE: '서버에 정상적으로 Kick을 요청하였습니다.',
|
||||||
DEL_COUNT_CHECK: '보유 개수 이상 삭제할 수 없습니다.',
|
DEL_COUNT_CHECK: '보유 개수 이상 삭제할 수 없습니다.',
|
||||||
DEL_COUNT_CONFIRM: '삭제할 아이템 개수를 입력하세요.\n(보유 개수: {{count}})',
|
DEL_COUNT_CONFIRM: '삭제할 아이템 개수를 입력하세요.\n(보유 개수: {{count}})',
|
||||||
CANCEL_CONFIRM: '취소하시겠습니까?\n취소 시 변경된 값은 초기화됩니다.',
|
CANCEL_CONFIRM: '취소하시겠습니까?\n취소 시 변경된 값은 초기화됩니다.',
|
||||||
@@ -37,7 +41,18 @@ const resources = {
|
|||||||
WARNING_NICKNAME_CHECK: '닉네임을 확인해주세요.',
|
WARNING_NICKNAME_CHECK: '닉네임을 확인해주세요.',
|
||||||
WARNING_EMAIL_CHECK: '이메일을 확인해주세요.',
|
WARNING_EMAIL_CHECK: '이메일을 확인해주세요.',
|
||||||
WARNING_TYPE_CHECK: '타입을 확인해주세요.',
|
WARNING_TYPE_CHECK: '타입을 확인해주세요.',
|
||||||
|
//db
|
||||||
|
LOG_MEMORY_LIMIT_WARNING: '데이터가 너무 많아 조회할 수 없습니다.\n조회조건 조정 후 다시 조회해주세요.',
|
||||||
|
LOG_MONGGDB_QUERY_WARNING: '조회 중 오류가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요.\n오류가 지속될 경우, 담당자에게 문의해주세요.',
|
||||||
|
DATA_INIT_CONFIRM: '대상 {{data}}\n데이터를 초기화 하시겠습니까?\n초기화시 되돌릴 수 없습니다.',
|
||||||
//랜드
|
//랜드
|
||||||
|
LAND_OWNED_CHANGES_WARNING: "해당 랜드는 소유권 변경이 불가능합니다.",
|
||||||
|
LAND_OWNED_CHANGES_REGIST_CONFIRM: "랜드 소유권 변경을 등록하시겠습니까?",
|
||||||
|
LAND_OWNED_CHANGES_SELECT_DELETE: "랜드 소유권 변경 예약을 취소하시겠습니까?",
|
||||||
|
LAND_OWNED_CHANGES_DELETE_TIME_WARNING: "예약시간이 지나 취소할 수 없습니다.",
|
||||||
|
LAND_OWNED_CHANGES_DELETE_STATUS_WARNING: "소유권 변경 예약을 취소할 수 없는 상태입니다.",
|
||||||
|
LAND_OWNED_CHANGES_REGIST_DUPLICATION_WARNING: "소유권 변경이 진행중인 랜드입니다.",
|
||||||
|
LAND_OWNER_DUPLICATION_WARNING: "소유자가 존재하는 랜드입니다.",
|
||||||
LAND_AUCTION_SELECT_DELETE: "선택된 경매를 삭제하시겠습니까?",
|
LAND_AUCTION_SELECT_DELETE: "선택된 경매를 삭제하시겠습니까?",
|
||||||
LAND_AUCTION_WARNING_DELETE: "대기 상태의 경매만 삭제할 수 있습니다.",
|
LAND_AUCTION_WARNING_DELETE: "대기 상태의 경매만 삭제할 수 있습니다.",
|
||||||
LAND_AUCTION_MODAL_STATUS_WARNING: "경매 시작일시 이후에는 변경이 불가합니다.",
|
LAND_AUCTION_MODAL_STATUS_WARNING: "경매 시작일시 이후에는 변경이 불가합니다.",
|
||||||
@@ -88,8 +103,8 @@ const resources = {
|
|||||||
BATTLE_EVENT_MODAL_START_DT_WARNING: "시작 시간은 현재 시간으로부터 10분 이후부터 가능합니다.",
|
BATTLE_EVENT_MODAL_START_DT_WARNING: "시작 시간은 현재 시간으로부터 10분 이후부터 가능합니다.",
|
||||||
BATTLE_EVENT_MODAL_START_DIFF_END_WARNING :"종료일은 시작일보다 하루 이후여야 합니다.",
|
BATTLE_EVENT_MODAL_START_DIFF_END_WARNING :"종료일은 시작일보다 하루 이후여야 합니다.",
|
||||||
BATTLE_EVENT_MODAL_TIME_CHECK_WARNING :"해당 시간에 속하는 이벤트가 존재합니다.",
|
BATTLE_EVENT_MODAL_TIME_CHECK_WARNING :"해당 시간에 속하는 이벤트가 존재합니다.",
|
||||||
BATTLE_EVENT_REGIST_CONFIRM: "랜드 경매를 등록하시겠습니까?",
|
BATTLE_EVENT_REGIST_CONFIRM: "이벤트를 등록하시겠습니까?",
|
||||||
BATTLE_EVENT_UPDATE_CONFIRM: "랜드 경매를 수정하시겠습니까?",
|
BATTLE_EVENT_UPDATE_CONFIRM: "이벤트를 수정하시겠습니까?",
|
||||||
BATTLE_EVENT_SELECT_DELETE: "선택된 이벤트를 삭제하시겠습니까?",
|
BATTLE_EVENT_SELECT_DELETE: "선택된 이벤트를 삭제하시겠습니까?",
|
||||||
BATTLE_EVENT_SELECT_STOP: "선택된 이벤트를 중단하시겠습니까?",
|
BATTLE_EVENT_SELECT_STOP: "선택된 이벤트를 중단하시겠습니까?",
|
||||||
BATTLE_EVENT_STOP_5MINUTES_DATE_WARNING: "이벤트 시작 5분 전에는 중단할 수 없습니다.",
|
BATTLE_EVENT_STOP_5MINUTES_DATE_WARNING: "이벤트 시작 5분 전에는 중단할 수 없습니다.",
|
||||||
@@ -99,6 +114,7 @@ const resources = {
|
|||||||
FILE_INDEX_USER_CONTENT: 'Caliverse_User_Index.xlsx',
|
FILE_INDEX_USER_CONTENT: 'Caliverse_User_Index.xlsx',
|
||||||
FILE_CALIUM_REQUEST: 'Caliverse_Calium_Request.xlsx',
|
FILE_CALIUM_REQUEST: 'Caliverse_Calium_Request.xlsx',
|
||||||
FILE_LAND_AUCTION: 'Caliverse_Land_Auction.xlsx',
|
FILE_LAND_AUCTION: 'Caliverse_Land_Auction.xlsx',
|
||||||
|
FILE_BUSINESS_LOG: 'Caliverse_Log.xlsx',
|
||||||
FILE_BATTLE_EVENT: 'Caliverse_Battle_Event.xlsx'
|
FILE_BATTLE_EVENT: 'Caliverse_Battle_Event.xlsx'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
292
src/pages/DataManage/BusinessLogView.js
Normal file
292
src/pages/DataManage/BusinessLogView.js
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
TableStyle,
|
||||||
|
FormWrapper,
|
||||||
|
TableWrapper,
|
||||||
|
TableActionButton,
|
||||||
|
TableDetailRow,
|
||||||
|
TableDetailContainer,
|
||||||
|
TableDetailFlex,
|
||||||
|
TableDetailColumn,
|
||||||
|
DetailTableInfo,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import { withAuth } from '../../hooks/hook';
|
||||||
|
import {
|
||||||
|
authType,
|
||||||
|
modalTypes,
|
||||||
|
} from '../../assets/data';
|
||||||
|
import { INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE } from '../../assets/data/adminConstants';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
DownloadProgress,
|
||||||
|
DynamicModal,
|
||||||
|
ExcelDownButton,
|
||||||
|
Pagination,
|
||||||
|
TopButton,
|
||||||
|
ViewTableInfo,
|
||||||
|
} from '../../components/common';
|
||||||
|
import { TableSkeleton } from '../../components/Skeleton/TableSkeleton';
|
||||||
|
import BusinessLogSearchBar, { useBusinessLogSearch } from '../../components/ServiceManage/searchBar/BusinessLogSearchBar';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import FrontPagination from '../../components/common/Pagination/FrontPagination';
|
||||||
|
import Loading from '../../components/common/Loading';
|
||||||
|
import CircularProgress from '../../components/common/CircularProgress';
|
||||||
|
|
||||||
|
const BusinessLogView = () => {
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const tableRef = useRef(null);
|
||||||
|
|
||||||
|
const [alertMsg, setAlertMsg] = useState('');
|
||||||
|
|
||||||
|
const [expandedRows, setExpandedRows] = useState({});
|
||||||
|
const [downloadState, setDownloadState] = useState({
|
||||||
|
loading: false,
|
||||||
|
progress: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [itemsPerPage, setItemsPerPage] = useState(500);
|
||||||
|
const [displayData, setDisplayData] = useState([]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
searchParams,
|
||||||
|
loading: dataLoading,
|
||||||
|
data: dataList,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
handleOrderByChange,
|
||||||
|
updateSearchParams
|
||||||
|
} = useBusinessLogSearch(token, 500, setAlertMsg);
|
||||||
|
|
||||||
|
const handlePageSizeChange = (newSize) => {
|
||||||
|
setItemsPerPage(newSize);
|
||||||
|
setCurrentPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClientPageChange = useCallback((slicedData) => {
|
||||||
|
setDisplayData(slicedData);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentPage(1);
|
||||||
|
if (dataList?.generic_list && dataList.generic_list.length > 0) {
|
||||||
|
const initialData = dataList.generic_list.slice(0, itemsPerPage);
|
||||||
|
setDisplayData(initialData);
|
||||||
|
} else {
|
||||||
|
setDisplayData([]);
|
||||||
|
}
|
||||||
|
}, [dataList, itemsPerPage]);
|
||||||
|
|
||||||
|
const toggleRowExpand = (index) => {
|
||||||
|
setExpandedRows(prev => ({
|
||||||
|
...prev,
|
||||||
|
[index]: !prev[index]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableHeaders = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{ id: 'logTime', label: '일시', width: '120px' },
|
||||||
|
{ id: 'userGuid', label: 'GUID', width: '200px' },
|
||||||
|
{ id: 'accountId', label: 'account ID', width: '100px' },
|
||||||
|
{ id: 'userNickname', label: '아바타명', width: '150px' },
|
||||||
|
{ id: 'tranId', label: '트랜잭션 ID', width: '200px' },
|
||||||
|
{ id: 'action', label: '액션', width: '150px' },
|
||||||
|
{ id: 'domain', label: '도메인', width: '120px' },
|
||||||
|
{ id: 'details', label: '상세 정보', width: '100px' }
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
const renderActorData = (actor) => {
|
||||||
|
if (!actor || typeof actor !== 'object') return <></>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DetailTableInfo>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colSpan="2">Actor 정보</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{Object.entries(actor).map(([key, value]) => (
|
||||||
|
<tr key={`actor-${key}`}>
|
||||||
|
<td>{key}</td>
|
||||||
|
<td>
|
||||||
|
{typeof value === 'object' && value !== null
|
||||||
|
? JSON.stringify(value)
|
||||||
|
: String(value)
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</DetailTableInfo>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderInfosData = (infos) => {
|
||||||
|
if (!infos || !Array.isArray(infos) || infos.length === 0) return <></>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DetailTableInfo>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colSpan="2">Infos 정보</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{infos.map((info, infoIndex) => (
|
||||||
|
<Fragment key={`info-${infoIndex}`}>
|
||||||
|
{Object.entries(info).map(([key, value]) => (
|
||||||
|
<tr key={`info-${infoIndex}-${key}`}>
|
||||||
|
<td>{key}</td>
|
||||||
|
<td>
|
||||||
|
{typeof value === 'object' && value !== null
|
||||||
|
? JSON.stringify(value)
|
||||||
|
: Array.isArray(value)
|
||||||
|
? value.join(', ')
|
||||||
|
: String(value)
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</DetailTableInfo>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "warning":
|
||||||
|
setAlertMsg('')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>비즈니스 로그 조회</Title>
|
||||||
|
<FormWrapper>
|
||||||
|
<BusinessLogSearchBar
|
||||||
|
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} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
|
||||||
|
<DownloadContainer>
|
||||||
|
<ExcelDownButton
|
||||||
|
data={dataList?.generic_list}
|
||||||
|
fileName={t('FILE_BUSINESS_LOG')}
|
||||||
|
onLoadingChange={setDownloadState}
|
||||||
|
/>
|
||||||
|
{downloadState.loading && (
|
||||||
|
<CircularProgressWrapper>
|
||||||
|
<CircularProgress
|
||||||
|
progress={downloadState.progress}
|
||||||
|
size={36}
|
||||||
|
progressColor="#4A90E2"
|
||||||
|
/>
|
||||||
|
</CircularProgressWrapper>
|
||||||
|
)}
|
||||||
|
</DownloadContainer>
|
||||||
|
</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>
|
||||||
|
{displayData?.map((item, index) => (
|
||||||
|
<Fragment key={index}>
|
||||||
|
<tr>
|
||||||
|
<td>{item.logTime}</td>
|
||||||
|
<td>{item.userGuid}</td>
|
||||||
|
<td>{item.accountId}</td>
|
||||||
|
<td>{item.userNickname}</td>
|
||||||
|
<td>{item.tranId}</td>
|
||||||
|
<td>{item.action}</td>
|
||||||
|
<td>{item.domain === 'None' ? '-' : item.domain}</td>
|
||||||
|
<td>
|
||||||
|
<TableActionButton onClick={() => toggleRowExpand(index)}>
|
||||||
|
{expandedRows[index] ? '접기' : '상세보기'}
|
||||||
|
</TableActionButton>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{expandedRows[index] && (
|
||||||
|
<TableDetailRow>
|
||||||
|
<td colSpan={tableHeaders.length}>
|
||||||
|
<TableDetailContainer>
|
||||||
|
<TableDetailFlex>
|
||||||
|
<TableDetailColumn>
|
||||||
|
{renderActorData(item.header?.Actor)}
|
||||||
|
</TableDetailColumn>
|
||||||
|
<TableDetailColumn>
|
||||||
|
{renderInfosData(item.body?.Infos)}
|
||||||
|
</TableDetailColumn>
|
||||||
|
</TableDetailFlex>
|
||||||
|
</TableDetailContainer>
|
||||||
|
</td>
|
||||||
|
</TableDetailRow>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</TableStyle>
|
||||||
|
</TableWrapper>
|
||||||
|
{dataList?.generic_list &&
|
||||||
|
<FrontPagination
|
||||||
|
data={dataList.generic_list}
|
||||||
|
itemsPerPage={itemsPerPage}
|
||||||
|
currentPage={currentPage}
|
||||||
|
setCurrentPage={setCurrentPage}
|
||||||
|
pageLimit={10}
|
||||||
|
onPageChange={handleClientPageChange}
|
||||||
|
/>}
|
||||||
|
<TopButton />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.completed}
|
||||||
|
view={alertMsg ? 'view' : 'hidden'}
|
||||||
|
modalText={alertMsg}
|
||||||
|
handleSubmit={() => handleModalSubmit('warning')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withAuth(authType.businessLogRead)(BusinessLogView);
|
||||||
|
|
||||||
|
const DownloadContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CircularProgressWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
285
src/pages/DataManage/LandInfoView.js
Normal file
285
src/pages/DataManage/LandInfoView.js
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
import { styled } from 'styled-components';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import React, { Fragment, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
TableStyle,
|
||||||
|
FormWrapper,
|
||||||
|
TableWrapper,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import Button from '../../components/common/button/Button';
|
||||||
|
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { authList } from '../../store/authList';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { useDataFetch, useModal, useTable, withAuth } from '../../hooks/hook';
|
||||||
|
import {
|
||||||
|
authType,
|
||||||
|
landSize,
|
||||||
|
modalTypes,
|
||||||
|
opLandCategoryType,
|
||||||
|
opLandOwnedType,
|
||||||
|
} from '../../assets/data';
|
||||||
|
import { INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE, TYPE_MODIFY } from '../../assets/data/adminConstants';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { CheckBox, DynamicModal, ExcelDownButton, Pagination, ViewTableInfo } from '../../components/common';
|
||||||
|
import LandInfoSearchBar, { useLandInfoSearch } from '../../components/ServiceManage/searchBar/LandInfoSearchBar';
|
||||||
|
import { TableSkeleton } from '../../components/Skeleton/TableSkeleton';
|
||||||
|
import OwnerChangeModal from '../../components/ServiceManage/modal/OwnerChangeModal';
|
||||||
|
import { opLandInfoStatusType } from '../../assets/data/options';
|
||||||
|
|
||||||
|
const LandInfoView = () => {
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const userInfo = useRecoilValue(authList);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const tableRef = useRef(null);
|
||||||
|
|
||||||
|
const [detailData, setDetailData] = useState({});
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalState,
|
||||||
|
handleModalView,
|
||||||
|
handleModalClose
|
||||||
|
} = useModal({
|
||||||
|
detail: 'hidden',
|
||||||
|
deleteConfirm: 'hidden',
|
||||||
|
deleteComplete: 'hidden'
|
||||||
|
});
|
||||||
|
const [alertMsg, setAlertMsg] = useState('');
|
||||||
|
const [modalType, setModalType] = useState('regist');
|
||||||
|
|
||||||
|
const {
|
||||||
|
searchParams,
|
||||||
|
loading,
|
||||||
|
data: dataList,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
handlePageSizeChange,
|
||||||
|
handleOrderByChange,
|
||||||
|
updateSearchParams
|
||||||
|
} = useLandInfoSearch(token, INITIAL_PAGE_SIZE);
|
||||||
|
|
||||||
|
const {
|
||||||
|
selectedRows,
|
||||||
|
handleSelectRow,
|
||||||
|
isRowSelected,
|
||||||
|
removeSelectedRows
|
||||||
|
} = useTable(dataList?.land_info_list || [], {mode: 'single'});
|
||||||
|
|
||||||
|
// const {
|
||||||
|
// data: landData
|
||||||
|
// } = useDataFetch(() => LandView(token), [token]);
|
||||||
|
|
||||||
|
const handleModalSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "regist":
|
||||||
|
setModalType('regist');
|
||||||
|
const selectRow = selectedRows[0];
|
||||||
|
if(!selectRow.owned) {
|
||||||
|
setAlertMsg(t('LAND_OWNED_CHANGES_WARNING'))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const owner_changes = selectRow.owner_changes;
|
||||||
|
if(owner_changes.length > 0){
|
||||||
|
setModalType(TYPE_MODIFY);
|
||||||
|
}
|
||||||
|
setDetailData(selectRow);
|
||||||
|
handleModalView('detail');
|
||||||
|
break;
|
||||||
|
// case "detail":
|
||||||
|
// await LandAuctionDetailView(token, param).then(data => {
|
||||||
|
// setDetailData(data.auction_detail);
|
||||||
|
// setModalType('modify');
|
||||||
|
// handleModalView('detail');
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// case "delete":
|
||||||
|
// const date_check = selectedRows.every(row => {
|
||||||
|
// const timeDiff = timeDiffMinute(convertKTC(row.auction_start_dt), (new Date));
|
||||||
|
// return timeDiff < 3;
|
||||||
|
// });
|
||||||
|
// if(date_check){
|
||||||
|
// setAlertMsg(t('LAND_AUCTION_DELETE_DATE_WARNING'));
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if(selectedRows[0].status === landAuctionStatusType.auction_start || selectedRows[0].status === landAuctionStatusType.stl_end){
|
||||||
|
// setAlertMsg(t('LAND_AUCTION_DELETE_STATUS_WARNING'));
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// handleModalView('deleteConfirm');
|
||||||
|
// break;
|
||||||
|
// case "deleteConfirm":
|
||||||
|
// let list = [];
|
||||||
|
// let isChecked = false;
|
||||||
|
//
|
||||||
|
// selectedRows.map(data => {
|
||||||
|
// // const row = dataList.list.find(row => row.id === Number(data.id));
|
||||||
|
// // if(row.status !== commonStatus.wait) isChecked = true;
|
||||||
|
// list.push({
|
||||||
|
// id: data.id,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// if(isChecked) {
|
||||||
|
// setAlertMsg(t('LAND_AUCTION_WARNING_DELETE'))
|
||||||
|
// handleModalClose('deleteConfirm');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// await LandAuctionDelete(token, list).then(data => {
|
||||||
|
// handleModalClose('deleteConfirm');
|
||||||
|
// if(data.result === "SUCCESS") {
|
||||||
|
// handleModalView('deleteComplete');
|
||||||
|
// }else{
|
||||||
|
// setAlertMsg(t('DELETE_FAIL'));
|
||||||
|
// }
|
||||||
|
// }).catch(reason => {
|
||||||
|
// setAlertMsg(t('API_FAIL'));
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// break;
|
||||||
|
// case "deleteComplete":
|
||||||
|
// handleModalClose('deleteComplete');
|
||||||
|
// // fetchData(option);
|
||||||
|
// window.location.reload();
|
||||||
|
// break;
|
||||||
|
case "warning":
|
||||||
|
setAlertMsg('')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDetailView = () => {
|
||||||
|
handleModalClose('detail');
|
||||||
|
handleSearch();
|
||||||
|
removeSelectedRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>랜드 정보 조회</Title>
|
||||||
|
<FormWrapper>
|
||||||
|
<LandInfoSearchBar
|
||||||
|
searchParams={searchParams}
|
||||||
|
onSearch={(newParams, executeSearch = true) => {
|
||||||
|
if (executeSearch) {
|
||||||
|
handleSearch(newParams);
|
||||||
|
} else {
|
||||||
|
updateSearchParams(newParams);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
</FormWrapper>
|
||||||
|
<ViewTableInfo total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
|
||||||
|
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_LAND_AUCTION')} />
|
||||||
|
{userInfo.auth_list?.some(auth => auth.id === authType.landUpdate) && (
|
||||||
|
<Button
|
||||||
|
theme={selectedRows.length === 0 ? 'disable' : 'line'}
|
||||||
|
text="소유권 변경"
|
||||||
|
type="button"
|
||||||
|
handleClick={e => handleModalSubmit('regist')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ViewTableInfo>
|
||||||
|
{loading ? <TableSkeleton width='100%' count={15} /> :
|
||||||
|
<>
|
||||||
|
<TableWrapper>
|
||||||
|
<TableStyle ref={tableRef}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="40"></th>
|
||||||
|
<th width="150">랜드 ID</th>
|
||||||
|
<th>랜드 이름</th>
|
||||||
|
<th>랜드 상태</th>
|
||||||
|
<th>카테고리</th>
|
||||||
|
<th>랜드 크기</th>
|
||||||
|
<th>인스턴스 수</th>
|
||||||
|
<th>유저 소유 여부</th>
|
||||||
|
<th>보유자</th>
|
||||||
|
<th>보유시작일</th>
|
||||||
|
<th>낙찰 가격</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{dataList?.land_info_list?.map((data, index) => (
|
||||||
|
<Fragment key={index}>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<CheckBox name={'select'} id={data.id}
|
||||||
|
setData={(e) => handleSelectRow(e, data)}
|
||||||
|
checked={isRowSelected(data.id)} />
|
||||||
|
</td>
|
||||||
|
<td>{data.land_id}</td>
|
||||||
|
<td>{data.land_name}</td>
|
||||||
|
<td>{opLandInfoStatusType.find(option => option.value === data.status)?.name}</td>
|
||||||
|
<td>{opLandCategoryType.find(option => option.value === data.category)?.name}</td>
|
||||||
|
<td>{landSize.find(option => option.value === data.land_size)?.name}</td>
|
||||||
|
<td>{data.socket}</td>
|
||||||
|
<td>{opLandOwnedType.find(option => option.value === data.owned)?.name}</td>
|
||||||
|
<td>{data.owner_user_nickname}</td>
|
||||||
|
{/*<td>{convertKTCDate(data.owner_user_create_date)}</td>*/}
|
||||||
|
<td>{data.owner_user_create_date}</td>
|
||||||
|
<td>{Number(data.owner_price) > 0 ? data.owner_price : ''}</td>
|
||||||
|
</tr>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</TableStyle>
|
||||||
|
</TableWrapper>
|
||||||
|
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<OwnerChangeModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => handleDetailView()} content={detailData} setDetailData={setDetailData} />
|
||||||
|
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.completed}
|
||||||
|
view={alertMsg ? 'view' : 'hidden'}
|
||||||
|
modalText={alertMsg}
|
||||||
|
handleSubmit={() => handleModalSubmit('warning')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withAuth(authType.landRead)(LandInfoView);
|
||||||
|
|
||||||
|
// const TableWrapper = styled.div`
|
||||||
|
// overflow: auto;
|
||||||
|
// border-top: 1px solid #000;
|
||||||
|
// z-index: 1;
|
||||||
|
// &::-webkit-scrollbar {
|
||||||
|
// height: 4px;
|
||||||
|
// }
|
||||||
|
// &::-webkit-scrollbar-thumb {
|
||||||
|
// background: #666666;
|
||||||
|
// }
|
||||||
|
// &::-webkit-scrollbar-track {
|
||||||
|
// background: #d9d9d9;
|
||||||
|
// }
|
||||||
|
// thead {
|
||||||
|
// th {
|
||||||
|
// position: sticky;
|
||||||
|
// top: 0;
|
||||||
|
// z-index: 10;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ${TableStyle} {
|
||||||
|
// min-width: 1000px;
|
||||||
|
// th {
|
||||||
|
// position: sticky;
|
||||||
|
// top: 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// `;
|
||||||
|
|
||||||
|
const LandLink = styled(Link)`
|
||||||
|
color: #61a2d0;
|
||||||
|
text-decoration: underline;
|
||||||
|
`;
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
import { styled } from 'styled-components';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { Fragment, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import { Title, TableStyle, BtnWrapper, ButtonClose, ModalText, FormWrapper } from '../../styles/Components';
|
|
||||||
|
|
||||||
import LandSearchBar from '../../components/DataManage/LandSearchBar';
|
|
||||||
import Button from '../../components/common/button/Button';
|
|
||||||
import QuestDetailModal from '../../components/DataManage/QuestDetailModal';
|
|
||||||
import LandDetailModal from '../../components/DataManage/LandDetailModal';
|
|
||||||
import Modal from '../../components/common/modal/Modal';
|
|
||||||
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { authList } from '../../store/authList';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
import { useDataFetch, useModal, useTable, withAuth } from '../../utils/hook';
|
|
||||||
import { authType, landAuctionStatusType } from '../../assets/data';
|
|
||||||
import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar';
|
|
||||||
import { INITIAL_PAGE_SIZE } from '../../assets/data/adminConstants';
|
|
||||||
import { LandAuctionDelete, LandAuctionDetailView } from '../../apis';
|
|
||||||
import { convertKTC, timeDiffMinute } from '../../utils';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { ExcelDownButton, ViewTableInfo } from '../../components/common';
|
|
||||||
import { LandAuctionSearchBar } from '../../components/ServiceManage';
|
|
||||||
|
|
||||||
const LandView = () => {
|
|
||||||
const token = sessionStorage.getItem('token');
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const userInfo = useRecoilValue(authList);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const tableRef = useRef(null);
|
|
||||||
|
|
||||||
const [detailData, setDetailData] = useState({});
|
|
||||||
|
|
||||||
const {
|
|
||||||
modalState,
|
|
||||||
handleModalView,
|
|
||||||
handleModalClose
|
|
||||||
} = useModal({
|
|
||||||
detail: 'hidden',
|
|
||||||
deleteConfirm: 'hidden',
|
|
||||||
deleteComplete: 'hidden'
|
|
||||||
});
|
|
||||||
const [alertMsg, setAlertMsg] = useState('');
|
|
||||||
const [modalType, setModalType] = useState('regist');
|
|
||||||
|
|
||||||
const {
|
|
||||||
searchParams,
|
|
||||||
data: dataList,
|
|
||||||
handleSearch,
|
|
||||||
handleReset,
|
|
||||||
handlePageChange,
|
|
||||||
handlePageSizeChange,
|
|
||||||
handleOrderByChange,
|
|
||||||
updateSearchParams
|
|
||||||
} = useLandAuctionSearch(token, INITIAL_PAGE_SIZE);
|
|
||||||
|
|
||||||
const {
|
|
||||||
selectedRows,
|
|
||||||
handleSelectRow,
|
|
||||||
isRowSelected
|
|
||||||
} = useTable(dataList?.auction_list || [], {mode: 'single'});
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: landData
|
|
||||||
} = useDataFetch(() => LandView(token), [token]);
|
|
||||||
|
|
||||||
const handleModalSubmit = async (type, param = null) => {
|
|
||||||
switch (type) {
|
|
||||||
// case "regist":
|
|
||||||
// setModalType('regist');
|
|
||||||
// handleModalView('detail');
|
|
||||||
// break;
|
|
||||||
// case "detail":
|
|
||||||
// await LandAuctionDetailView(token, param).then(data => {
|
|
||||||
// setDetailData(data.auction_detail);
|
|
||||||
// setModalType('modify');
|
|
||||||
// handleModalView('detail');
|
|
||||||
// });
|
|
||||||
// break;
|
|
||||||
// case "delete":
|
|
||||||
// const date_check = selectedRows.every(row => {
|
|
||||||
// const timeDiff = timeDiffMinute(convertKTC(row.auction_start_dt), (new Date));
|
|
||||||
// return timeDiff < 3;
|
|
||||||
// });
|
|
||||||
// if(date_check){
|
|
||||||
// setAlertMsg(t('LAND_AUCTION_DELETE_DATE_WARNING'));
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if(selectedRows[0].status === landAuctionStatusType.auction_start || selectedRows[0].status === landAuctionStatusType.stl_end){
|
|
||||||
// setAlertMsg(t('LAND_AUCTION_DELETE_STATUS_WARNING'));
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// handleModalView('deleteConfirm');
|
|
||||||
// break;
|
|
||||||
// case "deleteConfirm":
|
|
||||||
// let list = [];
|
|
||||||
// let isChecked = false;
|
|
||||||
//
|
|
||||||
// selectedRows.map(data => {
|
|
||||||
// // const row = dataList.list.find(row => row.id === Number(data.id));
|
|
||||||
// // if(row.status !== commonStatus.wait) isChecked = true;
|
|
||||||
// list.push({
|
|
||||||
// id: data.id,
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// if(isChecked) {
|
|
||||||
// setAlertMsg(t('LAND_AUCTION_WARNING_DELETE'))
|
|
||||||
// handleModalClose('deleteConfirm');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// await LandAuctionDelete(token, list).then(data => {
|
|
||||||
// handleModalClose('deleteConfirm');
|
|
||||||
// if(data.result === "SUCCESS") {
|
|
||||||
// handleModalView('deleteComplete');
|
|
||||||
// }else{
|
|
||||||
// setAlertMsg(t('DELETE_FAIL'));
|
|
||||||
// }
|
|
||||||
// }).catch(reason => {
|
|
||||||
// setAlertMsg(t('API_FAIL'));
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// break;
|
|
||||||
// case "deleteComplete":
|
|
||||||
// handleModalClose('deleteComplete');
|
|
||||||
// // fetchData(option);
|
|
||||||
// window.location.reload();
|
|
||||||
// break;
|
|
||||||
// case "warning":
|
|
||||||
// setAlertMsg('')
|
|
||||||
// break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Title>랜드 조회</Title>
|
|
||||||
<FormWrapper>
|
|
||||||
<LandAuctionSearchBar
|
|
||||||
searchParams={searchParams}
|
|
||||||
onSearch={(newParams, executeSearch = true) => {
|
|
||||||
if (executeSearch) {
|
|
||||||
handleSearch(newParams);
|
|
||||||
} else {
|
|
||||||
updateSearchParams(newParams);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onReset={handleReset}
|
|
||||||
/>
|
|
||||||
</FormWrapper>
|
|
||||||
<ViewTableInfo total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
|
|
||||||
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_LAND_AUCTION')} />
|
|
||||||
{userInfo.auth_list?.some(auth => auth.id === authType.landDelete) && (
|
|
||||||
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
|
||||||
)}
|
|
||||||
{userInfo.auth_list?.some(auth => auth.id === authType.landUpdate) && (
|
|
||||||
<Button
|
|
||||||
theme={selectedRows.length === 0 ? 'disable' : 'line'}
|
|
||||||
text="소유권 변경"
|
|
||||||
type="button"
|
|
||||||
handleClick={e => handleModalSubmit('regist')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ViewTableInfo>
|
|
||||||
<TableWrapper>
|
|
||||||
<TableStyle ref={tableRef}>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="150">랜드 ID</th>
|
|
||||||
<th>랜드 이름</th>
|
|
||||||
<th>랜드 상태</th>
|
|
||||||
<th>카테고리</th>
|
|
||||||
<th>랜드 크기</th>
|
|
||||||
<th>보유시작일</th>
|
|
||||||
<th width="150">상세보기</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{dataList.map((data, index) => (
|
|
||||||
<Fragment key={index}>
|
|
||||||
<tr>
|
|
||||||
<td>{data.landId}</td>
|
|
||||||
<td>{data.ownerNick}</td>
|
|
||||||
<td>{data.ownerId}</td>
|
|
||||||
<td>{new Date(data.lockInDate).toLocaleString()}</td>
|
|
||||||
<td>
|
|
||||||
<LandLink to={data.landUrl}>{data.landUrl}</LandLink>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{/*<Button theme="line" text="상세보기" handleClick={handleClick} />*/}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</TableStyle>
|
|
||||||
</TableWrapper>
|
|
||||||
{/*<LandDetailModal detailPop={detailPop} handleClick={handleClick} />*/}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withAuth(authType.landRead)(LandView);
|
|
||||||
|
|
||||||
const TableWrapper = styled.div`
|
|
||||||
overflow: auto;
|
|
||||||
border-top: 1px solid #000;
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
height: 4px;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: #666666;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: #d9d9d9;
|
|
||||||
}
|
|
||||||
thead {
|
|
||||||
th {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${TableStyle} {
|
|
||||||
min-width: 1000px;
|
|
||||||
th {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const LandLink = styled(Link)`
|
|
||||||
color: #61a2d0;
|
|
||||||
text-decoration: underline;
|
|
||||||
`;
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export { default as UserView } from './UserView';
|
export { default as UserView } from './UserView';
|
||||||
export { default as LandView } from './LandView';
|
export { default as LandInfoView } from './LandInfoView';
|
||||||
export { default as GameLogView } from './GameLogView';
|
export { default as GameLogView } from './GameLogView';
|
||||||
export { default as CryptView } from './CryptView';
|
export { default as CryptView } from './CryptView';
|
||||||
|
export { default as BusinessLogView} from './BusinessLogView';
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
import { convertKTC, convertKTCDate, convertUTC, timeDiffMinute } from '../../utils';
|
import { convertKTC, convertKTCDate, convertUTC, timeDiffMinute } from '../../utils';
|
||||||
import { BattleEventModal } from '../../components/ServiceManage';
|
import { BattleEventModal } from '../../components/ServiceManage';
|
||||||
import { INITIAL_PAGE_SIZE, INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
import { INITIAL_PAGE_SIZE, INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||||
import { useDataFetch, useModal, useTable, withAuth } from '../../utils/hook';
|
import { useDataFetch, useModal, useTable, withAuth } from '../../hooks/hook';
|
||||||
import { StatusWapper, StatusLabel } from '../../styles/ModuleComponents';
|
import { StatusWapper, StatusLabel } from '../../styles/ModuleComponents';
|
||||||
import { battleEventStatus, battleRepeatType } from '../../assets/data/options';
|
import { battleEventStatus, battleRepeatType } from '../../assets/data/options';
|
||||||
import BattleEventSearchBar, {
|
import BattleEventSearchBar, {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
import { convertKTC, timeDiffMinute } from '../../utils';
|
import { convertKTC, timeDiffMinute } from '../../utils';
|
||||||
import { LandAuctionModal, LandAuctionSearchBar } from '../../components/ServiceManage';
|
import { LandAuctionModal, LandAuctionSearchBar } from '../../components/ServiceManage';
|
||||||
import { INITIAL_PAGE_SIZE, INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
import { INITIAL_PAGE_SIZE, INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||||
import { useDataFetch, useModal, useTable, withAuth } from '../../utils/hook';
|
import { useDataFetch, useModal, useTable, withAuth } from '../../hooks/hook';
|
||||||
import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar';
|
import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar';
|
||||||
import { StatusWapper, ChargeBtn, StatusLabel } from '../../styles/ModuleComponents';
|
import { StatusWapper, ChargeBtn, StatusLabel } from '../../styles/ModuleComponents';
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { MailReceiver, RegistInputRow } from '../../styles/ModuleComponents';
|
import { MailReceiver, RegistInputRow } from '../../styles/ModuleComponents';
|
||||||
import AuthModal from '../../components/common/modal/AuthModal';
|
import AuthModal from '../../components/common/modal/AuthModal';
|
||||||
import { authType } from '../../assets/data';
|
import { authType } from '../../assets/data';
|
||||||
import { useDataFetch } from '../../utils/hook';
|
import { useDataFetch } from '../../hooks/hook';
|
||||||
import { BattleConfigView } from '../../apis/Battle';
|
import { BattleConfigView } from '../../apis/Battle';
|
||||||
import { currencyCodeTypes } from '../../assets/data/types';
|
import { currencyCodeTypes } from '../../assets/data/types';
|
||||||
import { DynamicModal } from '../../components/common';
|
import { DynamicModal } from '../../components/common';
|
||||||
@@ -291,7 +291,6 @@ const MailRegist = () => {
|
|||||||
await MailSingleRegist(token, resultData).then(data => {
|
await MailSingleRegist(token, resultData).then(data => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
handleSubmitModal();
|
handleSubmitModal();
|
||||||
console.log(data);
|
|
||||||
if(data.result === "ERROR"){
|
if(data.result === "ERROR"){
|
||||||
if(data.data.message === "ERROR_MAIL_ITEM_CALIUM_OVER"){
|
if(data.data.message === "ERROR_MAIL_ITEM_CALIUM_OVER"){
|
||||||
setAlertMsg(t('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING'));
|
setAlertMsg(t('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING'));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import Pagination from '../../components/common/Pagination/Pagination';
|
|||||||
import Modal from '../../components/common/modal/Modal';
|
import Modal from '../../components/common/modal/Modal';
|
||||||
|
|
||||||
import { AdminViewList, AdminViewGroupList, AdminLoginApprove, AdminDeleteUser, AdminChangeGroup, AdminChangePw } from '../../apis/Admin';
|
import { AdminViewList, AdminViewGroupList, AdminLoginApprove, AdminDeleteUser, AdminChangeGroup, AdminChangePw } from '../../apis/Admin';
|
||||||
import AdminViewSearchBar from '../../components/UserManage/AdminViewSearchBar';
|
import {AdminViewSearchBar} from '../../components/ServiceManage';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { authList } from '../../store/authList';
|
import { authList } from '../../store/authList';
|
||||||
|
|||||||
@@ -191,14 +191,17 @@ const useAuthSetting = (initialId) => {
|
|||||||
|
|
||||||
// menuConfig를 기반으로 권한 그룹 구조화
|
// menuConfig를 기반으로 권한 그룹 구조화
|
||||||
const authGroups = useMemo(() => {
|
const authGroups = useMemo(() => {
|
||||||
return Object.entries(menuConfig).map(([key, section]) => ({
|
return Object.entries(menuConfig)
|
||||||
id: key,
|
.map(([key, section]) => ({
|
||||||
title: section.title,
|
id: key,
|
||||||
items: Object.entries(section.items).map(([itemKey, item]) => ({
|
title: section.title,
|
||||||
id: itemKey,
|
items: Object.entries(section.items)
|
||||||
title: item.title,
|
.filter(([_, section]) => section.view === true)
|
||||||
permissions: item.permissions
|
.map(([itemKey, item]) => ({
|
||||||
}))
|
id: itemKey,
|
||||||
|
title: item.title,
|
||||||
|
permissions: item.permissions
|
||||||
|
}))
|
||||||
}));
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ import {
|
|||||||
} from '../../styles/ModuleComponents';
|
} from '../../styles/ModuleComponents';
|
||||||
import {Button, ExcelDownButton, Pagination, DynamicModal, ViewTableInfo, Loading} from '../../components/common';
|
import {Button, ExcelDownButton, Pagination, DynamicModal, ViewTableInfo, Loading} from '../../components/common';
|
||||||
import { convertKTC, truncateText } from '../../utils';
|
import { convertKTC, truncateText } from '../../utils';
|
||||||
import { CaliumRequestRegistModal, CaliumRequestSearchBar } from '../../components/UserManage';
|
import { CaliumRequestRegistModal } from '../../components/UserManage';
|
||||||
import { CaliumCharge, CaliumRequestView } from '../../apis';
|
import { CaliumCharge, CaliumRequestView } from '../../apis';
|
||||||
import { withAuth } from '../../utils/hook';
|
import { withAuth } from '../../hooks/hook';
|
||||||
import { convertEndDateToISO, convertStartDateToISO } from '../../utils/date';
|
import { convertEndDateToISO, convertStartDateToISO } from '../../utils/date';
|
||||||
|
import {CaliumRequestSearchBar} from '../../components/ServiceManage';
|
||||||
|
|
||||||
const CaliumRequest = () => {
|
const CaliumRequest = () => {
|
||||||
const token = sessionStorage.getItem('token');
|
const token = sessionStorage.getItem('token');
|
||||||
|
|||||||
257
src/pages/UserManage/DataInitView.js
Normal file
257
src/pages/UserManage/DataInitView.js
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
import React, { useState, Fragment, useMemo } from 'react';
|
||||||
|
import Button from '../../components/common/button/Button';
|
||||||
|
import Loading from '../../components/common/Loading';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
SelectInput,
|
||||||
|
TableStyle,
|
||||||
|
TableWrapper,
|
||||||
|
TableActionButton,
|
||||||
|
TableDetailRow,
|
||||||
|
TableDetailContainer,
|
||||||
|
TableDetailFlex,
|
||||||
|
TableDetailColumn,
|
||||||
|
DetailTableInfo,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import { modalTypes } from '../../assets/data';
|
||||||
|
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
FormLabel,
|
||||||
|
FormRowGroup, MessageWrapper,
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import { authType } from '../../assets/data';
|
||||||
|
import { useModal, withAuth } from '../../hooks/hook';
|
||||||
|
import { DynamicModal, TopButton } from '../../components/common';
|
||||||
|
import { opInitDataType, opSuccessType } from '../../assets/data/options';
|
||||||
|
import { InitData } from '../../apis/Data';
|
||||||
|
import DataInitSearchBar, { useDataInitSearch } from '../../components/ServiceManage/searchBar/DataInitSearchBar';
|
||||||
|
|
||||||
|
const DataInitView = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false); // 로딩 창
|
||||||
|
const {
|
||||||
|
modalState,
|
||||||
|
handleModalView,
|
||||||
|
handleModalClose
|
||||||
|
} = useModal({
|
||||||
|
cancel: 'hidden',
|
||||||
|
registConfirm: 'hidden',
|
||||||
|
registComplete: 'hidden'
|
||||||
|
});
|
||||||
|
|
||||||
|
const [alertMsg, setAlertMsg] = useState('');
|
||||||
|
|
||||||
|
const [resultData, setResultData] = useState(initData); //데이터 정보
|
||||||
|
const [expandedRows, setExpandedRows] = useState({});
|
||||||
|
|
||||||
|
const {
|
||||||
|
searchParams,
|
||||||
|
loading: dataLoading,
|
||||||
|
data: dataList,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
updateSearchParams
|
||||||
|
} = useDataInitSearch(token, setAlertMsg);
|
||||||
|
|
||||||
|
const toggleRowExpand = (index) => {
|
||||||
|
setExpandedRows(prev => ({
|
||||||
|
...prev,
|
||||||
|
[index]: !prev[index]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableHeaders = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{ id: 'logTime', label: '일시', width: '100px' },
|
||||||
|
{ id: 'key', label: '키', width: '150px' },
|
||||||
|
{ id: 'dataType', label: '초기화대상', width: '80px' },
|
||||||
|
{ id: 'tranId', label: '트랜잭션ID', width: '150px' },
|
||||||
|
{ id: 'success', label: '성공여부', width: '60px' },
|
||||||
|
{ id: 'message', label: '작업메시지', width: '300px' },
|
||||||
|
{ id: 'details', label: '상세정보', width: '100px' },
|
||||||
|
// { id: 'recovery', label: '복구', width: '100px' }
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderDetailData = (data) => {
|
||||||
|
if (!data || typeof data !== 'object') return <></>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DetailTableInfo>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colSpan="2">정보</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{Object.entries(data).map(([key, value]) => (
|
||||||
|
<tr key={`${key}`}>
|
||||||
|
<td>{key}</td>
|
||||||
|
<td>
|
||||||
|
{typeof value === 'object' && value !== null
|
||||||
|
? JSON.stringify(value)
|
||||||
|
: String(value)
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</DetailTableInfo>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
handleModalView('registConfirm');
|
||||||
|
break;
|
||||||
|
case "registConfirm":
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
await InitData(token, resultData).then(data => {
|
||||||
|
handleModalClose('registConfirm');
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
handleModalView('registComplete');
|
||||||
|
}else{
|
||||||
|
setAlertMsg(t('REGIST_FAIL'));
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
handleModalClose('registConfirm');
|
||||||
|
setAlertMsg(t('API_FAIL'));
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "registComplete":
|
||||||
|
dataReset();
|
||||||
|
handleModalClose('registComplete');
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
setAlertMsg('');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataReset = () => {
|
||||||
|
setResultData(initData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>데이터 초기화</Title>
|
||||||
|
<MessageWrapper>
|
||||||
|
<FormRowGroup>
|
||||||
|
<FormLabel>초기화 대상</FormLabel>
|
||||||
|
<SelectInput value={resultData?.init_data_type} onChange={e => setResultData({ ...resultData, init_data_type: e.target.value })} width="150px">
|
||||||
|
{opInitDataType.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
<Button
|
||||||
|
text="초기화"
|
||||||
|
theme={'primary'}
|
||||||
|
type="submit"
|
||||||
|
size="large"
|
||||||
|
width="100px"
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
</FormRowGroup>
|
||||||
|
</MessageWrapper>
|
||||||
|
<MessageWrapper>
|
||||||
|
<DataInitSearchBar
|
||||||
|
searchParams={searchParams}
|
||||||
|
onSearch={(newParams, executeSearch = true) => {
|
||||||
|
if (executeSearch) {
|
||||||
|
handleSearch(newParams);
|
||||||
|
} else {
|
||||||
|
updateSearchParams(newParams);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
</MessageWrapper>
|
||||||
|
<TableWrapper>
|
||||||
|
<TableStyle>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{tableHeaders.map(header => (
|
||||||
|
<th key={header.id} width={header.width}>{header.label}</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{dataList?.init_list?.map((item, index) => (
|
||||||
|
<Fragment key={index}>
|
||||||
|
<tr>
|
||||||
|
<td>{item.timestamp}</td>
|
||||||
|
<td>{item.key}</td>
|
||||||
|
<td>{opInitDataType.find(type => type.value === item.initDataType)?.name}</td>
|
||||||
|
<td>{item.tranId}</td>
|
||||||
|
<td>{opSuccessType.find(type => type.value === item.success)?.name}</td>
|
||||||
|
<td>{item.message}</td>
|
||||||
|
<td>
|
||||||
|
<TableActionButton onClick={() => toggleRowExpand(index)}>
|
||||||
|
{expandedRows[index] ? '접기' : '상세보기'}
|
||||||
|
</TableActionButton>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{expandedRows[index] && (
|
||||||
|
<TableDetailRow>
|
||||||
|
<td colSpan={tableHeaders.length}>
|
||||||
|
<TableDetailContainer>
|
||||||
|
<TableDetailFlex>
|
||||||
|
<TableDetailColumn>
|
||||||
|
{renderDetailData(item.data)}
|
||||||
|
</TableDetailColumn>
|
||||||
|
</TableDetailFlex>
|
||||||
|
</TableDetailContainer>
|
||||||
|
</td>
|
||||||
|
</TableDetailRow>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</TableStyle>
|
||||||
|
</TableWrapper>
|
||||||
|
|
||||||
|
{/* 확인 모달 */}
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.confirmOkCancel}
|
||||||
|
view={modalState.registConfirmModal}
|
||||||
|
modalText={t('DATA_INIT_CONFIRM',{data:opInitDataType.find(type => type.value === resultData.init_data_type).name})}
|
||||||
|
handleSubmit={() => handleSubmit('registConfirm')}
|
||||||
|
handleCancel={() => handleModalClose('registConfirm')}
|
||||||
|
/>
|
||||||
|
{/* 완료 모달 */}
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.completed}
|
||||||
|
view={modalState.registCompleteModal}
|
||||||
|
modalText={t('INIT_COMPLTE')}
|
||||||
|
handleSubmit={() => handleSubmit('registComplete')}
|
||||||
|
/>
|
||||||
|
{/* 경고 모달 */}
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.completed}
|
||||||
|
view={alertMsg ? 'view' : 'hidden'}
|
||||||
|
modalText={alertMsg}
|
||||||
|
handleSubmit={() => setAlertMsg('')}
|
||||||
|
/>
|
||||||
|
{(loading || dataLoading) && <Loading/>}
|
||||||
|
<TopButton />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withAuth(authType.levelMaster)(DataInitView);
|
||||||
|
|
||||||
|
const initData = {
|
||||||
|
init_data_type: 'None'
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Fragment, useState, useEffect } from 'react';
|
import { Fragment, useState, useEffect } from 'react';
|
||||||
|
|
||||||
import LogViewSearchBar from '../../components/UserManage/LogViewSearchBar';
|
import {LogViewSearchBar} from '../../components/ServiceManage';
|
||||||
import { Title, FormWrapper, SelectInput, TableInfo, ListCount, ListOption, TableStyle, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
|
import { Title, FormWrapper, SelectInput, TableInfo, ListCount, ListOption, TableStyle, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
|
||||||
import Button from '../../components/common/button/Button';
|
import Button from '../../components/common/button/Button';
|
||||||
import Pagination from '../../components/common/Pagination/Pagination';
|
import Pagination from '../../components/common/Pagination/Pagination';
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ export { default as LogView } from './LogView';
|
|||||||
export { default as AuthSetting } from './AuthSetting';
|
export { default as AuthSetting } from './AuthSetting';
|
||||||
export { default as AuthSettingUpdate } from './AuthSettingUpdate';
|
export { default as AuthSettingUpdate } from './AuthSettingUpdate';
|
||||||
export { default as CaliumRequest} from './CaliumRequest';
|
export { default as CaliumRequest} from './CaliumRequest';
|
||||||
export { default as CaliumRequestRegist} from '../../components/UserManage/CaliumRequestRegistModal';
|
export { default as DataInitView} from './DataInitView';
|
||||||
@@ -78,6 +78,17 @@ export const SelectInput = styled.select`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const FormCenteredContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: calc(100vw - 400px);
|
||||||
|
height: calc(100vh - 500px); /* 상단 영역 높이에 따라 조정 (헤더+필터 영역) */
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
min-height: 100px;
|
||||||
|
`;
|
||||||
|
|
||||||
export const Textarea = styled.textarea`
|
export const Textarea = styled.textarea`
|
||||||
&:focus {
|
&:focus {
|
||||||
border: 1px solid #2c2c2c;
|
border: 1px solid #2c2c2c;
|
||||||
@@ -150,6 +161,7 @@ export const BtnWrapper = styled.div`
|
|||||||
justify-content: ${props => props.$justify};
|
justify-content: ${props => props.$justify};
|
||||||
gap: ${props => props.$gap};
|
gap: ${props => props.$gap};
|
||||||
margin-top: ${props => props.$marginTop};
|
margin-top: ${props => props.$marginTop};
|
||||||
|
margin-bottom: ${props => props.$marginBottom};
|
||||||
padding-top: ${props => props.$paddingTop};
|
padding-top: ${props => props.$paddingTop};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -548,3 +560,62 @@ export const PopupMessage = styled(Link)`
|
|||||||
color: #61a2d0;
|
color: #61a2d0;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const TableDetailRow = styled.tr`
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TableDetailContainer = styled.div`
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TableDetailFlex = styled.div`
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TableDetailColumn = styled.div`
|
||||||
|
flex: 1 1 48%;
|
||||||
|
min-width: 300px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DetailTableInfo = styled.table`
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TableActionButton = styled.button`
|
||||||
|
cursor: pointer;
|
||||||
|
background: #4a89dc;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
font-size: 13px;
|
||||||
|
min-width: max-content;
|
||||||
|
width: 80px;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #3a70bc;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -569,6 +569,13 @@ export const MessageBox = styled.div`
|
|||||||
|
|
||||||
// FORM
|
// FORM
|
||||||
|
|
||||||
|
export const FormItemGroup = styled.div`
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
`;
|
||||||
|
|
||||||
export const FormGroup = styled.div`
|
export const FormGroup = styled.div`
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
`;
|
`;
|
||||||
@@ -576,17 +583,20 @@ export const FormGroup = styled.div`
|
|||||||
export const FormRowGroup = styled.div`
|
export const FormRowGroup = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 20px 40px;
|
gap: 10px 40px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const FormLabel = styled.label`
|
export const FormLabel = styled.label`
|
||||||
display: block;
|
//display: block;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
min-width: fit-content;
|
min-width: fit-content;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const FormInput = styled.input`
|
export const FormInput = styled.input`
|
||||||
|
|||||||
@@ -1,16 +1,28 @@
|
|||||||
import { menuConfig } from '../assets/data/menuConfig';
|
import { menuConfig } from '../assets/data/menuConfig';
|
||||||
|
|
||||||
export const getMenuConfig = (userInfo) => {
|
export const getMenuConfig = (userInfo) => {
|
||||||
return Object.entries(menuConfig).map(([key, group]) => ({
|
const isLiveEnv = process.env.REACT_APP_ENV === 'live';
|
||||||
title: group.title,
|
return Object.entries(menuConfig)
|
||||||
link: `/${key}`,
|
.map(([key, group]) => {
|
||||||
access: hasGroupAccess(userInfo, group),
|
return {
|
||||||
submenu: Object.entries(group.items).map(([itemKey, item]) => ({
|
title: group.title,
|
||||||
title: item.title,
|
link: `/${key}`,
|
||||||
link: `/${key}/${itemKey}`,
|
access: hasGroupAccess(userInfo, group),
|
||||||
id: item.permissions.read
|
submenu: Object.entries(group.items)
|
||||||
}))
|
.filter(([itemKey, item]) => {
|
||||||
}));
|
if(isLiveEnv) {
|
||||||
|
if(item.test && item.test === true) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map(([itemKey, item]) => ({
|
||||||
|
title: item.title,
|
||||||
|
link: `/${key}/${itemKey}`,
|
||||||
|
id: item.permissions.read,
|
||||||
|
authLevel: item.authLevel
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 권한 체크 유틸리티 함수들
|
// 권한 체크 유틸리티 함수들
|
||||||
|
|||||||
Reference in New Issue
Block a user