Compare commits
7 Commits
5143b45610
...
3169055646
| Author | SHA1 | Date | |
|---|---|---|---|
| 3169055646 | |||
| 5d2e1918d1 | |||
| 4407fdc6b6 | |||
| b01c5cd410 | |||
| 63b3704e89 | |||
| f78a4912a6 | |||
| e25bcdc86e |
@@ -13,11 +13,11 @@ import {
|
|||||||
LogView,
|
LogView,
|
||||||
} from './pages/UserManage';
|
} from './pages/UserManage';
|
||||||
import { EconomicIndex, UserIndex } from './pages/IndexManage';
|
import { EconomicIndex, UserIndex } from './pages/IndexManage';
|
||||||
import { LandInfoView, CryptView, GameLogView, UserView, BusinessLogView, } from './pages/DataManage';
|
import { LandInfoView, GameLogView, UserView, BusinessLogView, MetaItemView, RankManage } from './pages/DataManage';
|
||||||
import {
|
import {
|
||||||
Board,
|
Board,
|
||||||
Event,
|
RewardEvent,
|
||||||
EventRegist,
|
RewardEventRegist,
|
||||||
Items,
|
Items,
|
||||||
Mail,
|
Mail,
|
||||||
MailRegist,
|
MailRegist,
|
||||||
@@ -26,7 +26,8 @@ import {
|
|||||||
UserBlockRegist,
|
UserBlockRegist,
|
||||||
LandAuction,
|
LandAuction,
|
||||||
BattleEvent,
|
BattleEvent,
|
||||||
MenuBanner, MenuBannerRegist,
|
MenuBanner, MenuBannerRegist, Ranking,
|
||||||
|
Event
|
||||||
} from './pages/ServiceManage';
|
} from './pages/ServiceManage';
|
||||||
|
|
||||||
const RouteInfo = () => {
|
const RouteInfo = () => {
|
||||||
@@ -59,8 +60,9 @@ const RouteInfo = () => {
|
|||||||
<Route path="userview" element={<UserView />} />
|
<Route path="userview" element={<UserView />} />
|
||||||
<Route path="landview" element={<LandInfoView />} />
|
<Route path="landview" element={<LandInfoView />} />
|
||||||
<Route path="gamelogview" element={<GameLogView />} />
|
<Route path="gamelogview" element={<GameLogView />} />
|
||||||
<Route path="cryptview" element={<CryptView />} />
|
|
||||||
<Route path="businesslogview" element={<BusinessLogView />} />
|
<Route path="businesslogview" element={<BusinessLogView />} />
|
||||||
|
<Route path="itemdictionary" element={<MetaItemView />} />
|
||||||
|
<Route path="rankmanage" element={<RankManage />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/servicemanage">
|
<Route path="/servicemanage">
|
||||||
<Route path="board" element={<Board />} />
|
<Route path="board" element={<Board />} />
|
||||||
@@ -70,12 +72,14 @@ const RouteInfo = () => {
|
|||||||
<Route path="userblock/userblockregist" element={<UserBlockRegist />} />
|
<Route path="userblock/userblockregist" element={<UserBlockRegist />} />
|
||||||
<Route path="reportlist" element={<ReportList />} />
|
<Route path="reportlist" element={<ReportList />} />
|
||||||
<Route path="items" element={<Items />} />
|
<Route path="items" element={<Items />} />
|
||||||
<Route path="event" element={<Event />} />
|
<Route path="rewardevent" element={<RewardEvent />} />
|
||||||
<Route path="event/eventregist" element={<EventRegist />} />
|
<Route path="rewardevent/eventregist" element={<RewardEventRegist />} />
|
||||||
<Route path="landauction" element={<LandAuction />} />
|
<Route path="landauction" element={<LandAuction />} />
|
||||||
<Route path="battleevent" element={<BattleEvent />} />
|
<Route path="battleevent" element={<BattleEvent />} />
|
||||||
<Route path="menubanner" element={<MenuBanner />} />
|
<Route path="menubanner" element={<MenuBanner />} />
|
||||||
<Route path="menubanner/menubannerregist" element={<MenuBannerRegist />} />
|
<Route path="menubanner/menubannerregist" element={<MenuBannerRegist />} />
|
||||||
|
<Route path="ranking" element={<Ranking />} />
|
||||||
|
<Route path="event" element={<Event />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
58
src/apis/Dictionary.js
Normal file
58
src/apis/Dictionary.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
//운영 정보 관리 - 백과사전 api 연결
|
||||||
|
|
||||||
|
import { Axios, responseFileDownload } from '../utils';
|
||||||
|
|
||||||
|
// 아이템 백과사전 조회
|
||||||
|
export const getItemDictionaryList = async (token, searchType, searchData, largeType, smallType, brand, gender, order, size, currentPage) => {
|
||||||
|
try {
|
||||||
|
const response = await Axios.get(`/api/v1/dictionary/item/list?search_type=${searchType}&search_data=${searchData}
|
||||||
|
&large_type=${largeType}&small_type=${smallType}&brand=${brand}&gender=${gender}
|
||||||
|
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('getItemDictionaryList API error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ItemDictionaryExport = async (token, params) => {
|
||||||
|
try {
|
||||||
|
await Axios.get(`/api/v1/dictionary/item/excel-export?search_type=${params.search_type}&search_data=${params.search_data}
|
||||||
|
&large_type=${params.large_type}&small_type=${params.small_type}&brand=${params.brand}&gender=${params.gender}
|
||||||
|
&lang=${params.lang}&task_id=${params.taskId}`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(response => {
|
||||||
|
responseFileDownload(response, {
|
||||||
|
defaultFileName: 'itemDictionary'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('ItemDictionaryExport Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BrandView = async (token) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(
|
||||||
|
`/api/v1/dictionary/brand/list`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.data.data.brand_list;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('BrandView Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
//운영서비스 관리 - 이벤트 api 연결
|
//운영서비스 관리 - 통합 이벤트 api 연결
|
||||||
|
|
||||||
import { Axios } from '../utils';
|
import { Axios } from '../utils';
|
||||||
|
|
||||||
// 이벤트 리스트 조회
|
// 이벤트 리스트 조회
|
||||||
export const EventView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
|
export const EventView = async (token, searchData, status, startDate, endDate, order, size, currentPage) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.get(
|
const res = await Axios.get(
|
||||||
`/api/v1/event/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
|
`/api/v1/world-event/list?search_data=${searchData}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
|
||||||
&page_size=${size}`,
|
&orderby=${order}&page_no=${currentPage}&page_size=${size}`,
|
||||||
{
|
{
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
},
|
},
|
||||||
@@ -24,11 +24,11 @@ export const EventView = async (token, title, content, status, startDate, endDat
|
|||||||
// 이벤트 상세보기
|
// 이벤트 상세보기
|
||||||
export const EventDetailView = async (token, id) => {
|
export const EventDetailView = async (token, id) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.get(`/api/v1/event/detail/${id}`, {
|
const res = await Axios.get(`/api/v1/world-event/detail/${id}`, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.data.data.detail;
|
return res.data.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('EventDetailView Error', e);
|
throw new Error('EventDetailView Error', e);
|
||||||
@@ -39,11 +39,11 @@ export const EventDetailView = async (token, id) => {
|
|||||||
// 이벤트 등록
|
// 이벤트 등록
|
||||||
export const EventSingleRegist = async (token, params) => {
|
export const EventSingleRegist = async (token, params) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.post(`/api/v1/event`, params, {
|
const res = await Axios.post(`/api/v1/world-event`, params, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('EventSingleRegist Error', e);
|
throw new Error('EventSingleRegist Error', e);
|
||||||
@@ -51,10 +51,10 @@ export const EventSingleRegist = async (token, params) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 우편 수정
|
// 이벤트 수정
|
||||||
export const EventModify = async (token, id, params) => {
|
export const EventModify = async (token, id, params) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.put(`/api/v1/event/${id}`, params, {
|
const res = await Axios.put(`/api/v1/world-event/${id}`, params, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,15 +66,14 @@ export const EventModify = async (token, id, params) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 우편 삭제
|
// 이벤트 삭제
|
||||||
export const EventDelete = async (token, params, id) => {
|
export const EventDelete = async (token, id) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.delete(`/api/v1/event/delete`, {
|
const res = await Axios.delete(`/api/v1/world-event/delete?id=${id}`, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
data: { list: params },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.data.data.list;
|
return res.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('EventDelete Error', e);
|
throw new Error('EventDelete Error', e);
|
||||||
@@ -82,17 +81,20 @@ export const EventDelete = async (token, params, id) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 이벤트 우편 아이템 확인
|
// 이벤트 메타데이터 조회
|
||||||
export const EventIsItem = async (token, params) => {
|
export const EventActionView = async (token) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.post(`/api/v1/event/item`, params, {
|
const res = await Axios.get(
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
`/api/v1/dictionary/event-action/list`,
|
||||||
});
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return res;
|
return res.data.data.event_action_list;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('EventIsItem Error', e);
|
throw new Error('EventActionView Error', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -12,7 +12,7 @@ export const LogViewList = async (token, searchType, searchKey, historyType, sta
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.data.data;
|
return res.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('LogViewList Error', e);
|
throw new Error('LogViewList Error', e);
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ export const getCurrencyDetailList = async (token, searchType, searchData, tranI
|
|||||||
|
|
||||||
export const GameCurrencyDetailLogExport = async (token, params, fileName) => {
|
export const GameCurrencyDetailLogExport = async (token, params, fileName) => {
|
||||||
try {
|
try {
|
||||||
console.log(params);
|
|
||||||
await Axios.post(`/api/v1/log/currency/detail/excel-export`, params, {
|
await Axios.post(`/api/v1/log/currency/detail/excel-export`, params, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
@@ -129,9 +128,9 @@ export const GameCurrencyDetailLogExport = async (token, params, fileName) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getItemDetailList = async (token, searchType, searchData, tranId, logAction, itemLargeType, itemSmallType, countDeltaType, startDate, endDate, order, size, currentPage) => {
|
export const getItemDetailList = async (token, searchType, searchData, itemId, tranId, logAction, itemLargeType, itemSmallType, countDeltaType, startDate, endDate, order, size, currentPage) => {
|
||||||
try {
|
try {
|
||||||
const response = await Axios.get(`/api/v1/log/item/detail/list?search_type=${searchType}&search_data=${searchData}&tran_id=${tranId}
|
const response = await Axios.get(`/api/v1/log/item/detail/list?search_type=${searchType}&search_data=${searchData}&tran_id=${tranId}&item_id=${itemId}
|
||||||
&log_action=${logAction}&item_large_type=${itemLargeType}&item_small_type=${itemSmallType}&count_delta_type=${countDeltaType}&start_dt=${startDate}&end_dt=${endDate}
|
&log_action=${logAction}&item_large_type=${itemLargeType}&item_small_type=${itemSmallType}&count_delta_type=${countDeltaType}&start_dt=${startDate}&end_dt=${endDate}
|
||||||
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
|
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -149,7 +148,6 @@ export const getItemDetailList = async (token, searchType, searchData, tranId, l
|
|||||||
|
|
||||||
export const GameItemDetailLogExport = async (token, params, fileName) => {
|
export const GameItemDetailLogExport = async (token, params, fileName) => {
|
||||||
try {
|
try {
|
||||||
console.log(params);
|
|
||||||
await Axios.post(`/api/v1/log/item/detail/excel-export`, params, {
|
await Axios.post(`/api/v1/log/item/detail/excel-export`, params, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
@@ -187,7 +185,7 @@ export const getCurrencyItemList = async (token, searchType, searchData, tranId,
|
|||||||
|
|
||||||
export const GameCurrencyItemLogExport = async (token, params, fileName) => {
|
export const GameCurrencyItemLogExport = async (token, params, fileName) => {
|
||||||
try {
|
try {
|
||||||
console.log(params);
|
|
||||||
await Axios.post(`/api/v1/log/currency-item/excel-export`, params, {
|
await Axios.post(`/api/v1/log/currency-item/excel-export`, params, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
|
|||||||
99
src/apis/Rank.js
Normal file
99
src/apis/Rank.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
//운영서비스 관리 - 랭킹 스케줄 api 연결
|
||||||
|
|
||||||
|
import { Axios } from '../utils';
|
||||||
|
|
||||||
|
// 리스트 조회
|
||||||
|
export const RankingScheduleView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(
|
||||||
|
`/api/v1/rank/schedule/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
|
||||||
|
&orderby=${order}&page_no=${currentPage}&page_size=${size}`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.data.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RankingScheduleView Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 전투시스템 상세보기
|
||||||
|
export const RankingScheduleDetailView = async (token, id) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(`/api/v1/rank/schedule/detail/${id}`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RankingScheduleDetailView Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 랭킹스케줄 등록
|
||||||
|
export const RankingScheduleSingleRegist = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post(`/api/v1/rank/schedule`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RankingScheduleSingleRegist Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 랭킹스케줄 수정
|
||||||
|
export const RankingScheduleModify = async (token, id, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.put(`/api/v1/rank/schedule/${id}`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RankingScheduleModify Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 랭킹스케줄 삭제
|
||||||
|
export const RankingScheduleDelete = async (token, id) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.delete(`/api/v1/rank/schedule/delete?id=${id}`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RankingScheduleDelete Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RankingDataView = async (token) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(
|
||||||
|
`/api/v1/dictionary/ranking/list`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.data.data.ranking_list;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RankingDataView Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
98
src/apis/RewardEvent.js
Normal file
98
src/apis/RewardEvent.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
//운영서비스 관리 - 이벤트 api 연결
|
||||||
|
|
||||||
|
import { Axios } from '../utils';
|
||||||
|
|
||||||
|
// 이벤트 리스트 조회
|
||||||
|
export const RewardEventView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(
|
||||||
|
`/api/v1/event/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
|
||||||
|
&orderby=${order}&page_no=${currentPage}&page_size=${size}`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.data.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventView Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이벤트 상세보기
|
||||||
|
export const RewardEventDetailView = async (token, id) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(`/api/v1/event/detail/${id}`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data.data.detail;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventDetailView Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이벤트 등록
|
||||||
|
export const RewardEventSingleRegist = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post(`/api/v1/event`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventSingleRegist Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 우편 수정
|
||||||
|
export const RewardEventModify = async (token, id, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.put(`/api/v1/event/${id}`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventModify Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 우편 삭제
|
||||||
|
export const RewardEventDelete = async (token, params, id) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.delete(`/api/v1/event/delete`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
data: { list: params },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data.data.list;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventDelete Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이벤트 우편 아이템 확인
|
||||||
|
export const EventIsItem = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post(`/api/v1/event/item`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('EventIsItem Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -12,7 +12,7 @@ export const UserView = async (token, searchType, searchKey) => {
|
|||||||
{ headers: { Authorization: `Bearer ${token}` } },
|
{ headers: { Authorization: `Bearer ${token}` } },
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.data.data.result;
|
return res.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('UserView Error', e);
|
throw new Error('UserView Error', e);
|
||||||
|
|||||||
@@ -12,12 +12,16 @@ export * from './BlackList';
|
|||||||
export * from './Users';
|
export * from './Users';
|
||||||
export * from './Indicators';
|
export * from './Indicators';
|
||||||
export * from './Item';
|
export * from './Item';
|
||||||
export * from './Event';
|
export * from './RewardEvent';
|
||||||
export * from './Calium';
|
export * from './Calium';
|
||||||
export * from './Land';
|
export * from './Land';
|
||||||
export * from './Menu';
|
export * from './Menu';
|
||||||
// export * from './OpenAI';
|
// export * from './OpenAI';
|
||||||
export * from './Log';
|
export * from './Log';
|
||||||
|
export * from './Data';
|
||||||
|
export * from './Dictionary';
|
||||||
|
export * from './Rank';
|
||||||
|
export * from './Event';
|
||||||
|
|
||||||
const apiModules = {};
|
const apiModules = {};
|
||||||
const allApis = {};
|
const allApis = {};
|
||||||
|
|||||||
18
src/assets/data/apis/eventAPI.json
Normal file
18
src/assets/data/apis/eventAPI.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"baseUrl": "/api/v1/world-event",
|
||||||
|
"endpoints": {
|
||||||
|
"EventView": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "/list",
|
||||||
|
"dataPath": "data",
|
||||||
|
"paramFormat": "query"
|
||||||
|
},
|
||||||
|
"EventDetailView": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "/detail/:id",
|
||||||
|
"dataPath": "data.data",
|
||||||
|
"paramFormat": "query",
|
||||||
|
"paramMapping": ["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"LogViewList": {
|
"LogViewList": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "/list",
|
"url": "/list",
|
||||||
"dataPath": "data.data",
|
"dataPath": "data",
|
||||||
"paramFormat": "query"
|
"paramFormat": "query"
|
||||||
},
|
},
|
||||||
"LogviewDetail": {
|
"LogviewDetail": {
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import itemAPI from './itemAPI.json';
|
import itemAPI from './itemAPI.json';
|
||||||
import menuBannerAPI from './menuBannerAPI.json';
|
import menuBannerAPI from './menuBannerAPI.json';
|
||||||
import historyAPI from './historyAPI.json';
|
import historyAPI from './historyAPI.json';
|
||||||
|
import eventAPI from './eventAPI.json';
|
||||||
|
import rankingAPI from './rankingAPI.json';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
itemAPI,
|
itemAPI,
|
||||||
menuBannerAPI,
|
menuBannerAPI,
|
||||||
historyAPI
|
historyAPI,
|
||||||
|
eventAPI,
|
||||||
|
rankingAPI,
|
||||||
};
|
};
|
||||||
18
src/assets/data/apis/rankingAPI.json
Normal file
18
src/assets/data/apis/rankingAPI.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"baseUrl": "/api/v1/rank/schedule",
|
||||||
|
"endpoints": {
|
||||||
|
"RankingScheduleView": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "/list",
|
||||||
|
"dataPath": "data",
|
||||||
|
"paramFormat": "query"
|
||||||
|
},
|
||||||
|
"RankingScheduleDetailView": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "/detail/:id",
|
||||||
|
"dataPath": "data.data",
|
||||||
|
"paramFormat": "query",
|
||||||
|
"paramMapping": ["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -123,7 +123,7 @@ export const STATUS_STYLES = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const logFieldLabels = {
|
export const FieldLabels = {
|
||||||
// DynamoDB 필드
|
// DynamoDB 필드
|
||||||
'attribFieldName': '속성 명',
|
'attribFieldName': '속성 명',
|
||||||
'pk': '파티션 키',
|
'pk': '파티션 키',
|
||||||
@@ -181,16 +181,40 @@ export const logFieldLabels = {
|
|||||||
'ffa_hot_time': 'FFA 핫타임',
|
'ffa_hot_time': 'FFA 핫타임',
|
||||||
'round_count': '라운드 수',
|
'round_count': '라운드 수',
|
||||||
|
|
||||||
|
//dictionary
|
||||||
|
'max_count': '최대 보유 가능 수량',
|
||||||
|
'stack_max_count': '최대 스택 가능 수량',
|
||||||
|
'expire_type': '아이템 만료 타입',
|
||||||
|
'expire_start_dt': '만료 시작 시간',
|
||||||
|
'expire_end_dt': '만료 종료 시간',
|
||||||
|
'expire_time_sec': '만료 시간 연장 여부',
|
||||||
|
'user_tradable': '유저 간 거래 가능 여부',
|
||||||
|
'system_tradable': '상점에서 판매 가능 여부',
|
||||||
|
'throwable': '버리기 가능 여부',
|
||||||
|
'cart_buy': '상점에서 구매 가능 여부',
|
||||||
|
'rarity': '희귀도',
|
||||||
|
'default_attrib': '기본 속성',
|
||||||
|
'attrib_random_group': '랜덤 그룹',
|
||||||
|
'item_set': '아이템 세트',
|
||||||
|
'buff': '아이템 사용 시 획득 버프',
|
||||||
|
'dress_slot_type': '착용 부위',
|
||||||
|
'product_link': '제품 URL',
|
||||||
|
'prop_small_type': '제작 아이템 그룹',
|
||||||
|
'gacha_group_id': '랜덤박스 그룹 ID',
|
||||||
|
'ugq_action': 'UGQ 사용 가능 여부',
|
||||||
|
'linked_land': '연결된 랜드 ID',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const historyTables = {
|
export const historyTables = {
|
||||||
userBlock: 'black_list',
|
userBlock: 'black_list',
|
||||||
landAuction: 'land_auction',
|
landAuction: 'land_auction',
|
||||||
landOwnerChange: 'land_ownership_changes',
|
landOwnerChange: 'land_ownership_changes',
|
||||||
event: 'event',
|
rewardEvent: 'event',
|
||||||
mail: 'mail',
|
mail: 'mail',
|
||||||
notice: 'notice',
|
notice: 'notice',
|
||||||
battleEvent: 'battle_event',
|
battleEvent: 'battle_event',
|
||||||
caliumRequest: 'calium_request',
|
caliumRequest: 'calium_request',
|
||||||
menuBanner: 'menu_banner',
|
menuBanner: 'menu_banner',
|
||||||
|
event: 'world_event',
|
||||||
|
rankingSchedule: 'ranking_schedule',
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export {authType, ivenTabType, modalTypes, TabUserList, tattooSlot, commonStatus, ViewTitleCountType, landAuctionStatusType} from './types'
|
export {authType, ivenTabType, modalTypes, tattooSlot, commonStatus, ViewTitleCountType, landAuctionStatusType} from './types'
|
||||||
export {
|
export {
|
||||||
mailSendType,
|
mailSendType,
|
||||||
mailType,
|
mailType,
|
||||||
@@ -27,6 +27,7 @@ export {
|
|||||||
opYNType,
|
opYNType,
|
||||||
opUserSessionType,
|
opUserSessionType,
|
||||||
opMailType,
|
opMailType,
|
||||||
amountDeltaType
|
amountDeltaType,
|
||||||
|
TabUserList
|
||||||
} from './options'
|
} from './options'
|
||||||
export {benItems, MinuteList, HourList, caliumRequestInitData, STATUS_STYLES, months, PAGE_SIZE_OPTIONS, ORDER_OPTIONS} from './data'
|
export {benItems, MinuteList, HourList, caliumRequestInitData, STATUS_STYLES, months, PAGE_SIZE_OPTIONS, ORDER_OPTIONS} from './data'
|
||||||
@@ -103,14 +103,6 @@ export const menuConfig = {
|
|||||||
view: false,
|
view: false,
|
||||||
authLevel: adminAuthLevel.NONE
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
// cryptview: {
|
|
||||||
// title: '크립토 조회',
|
|
||||||
// permissions: {
|
|
||||||
// read: authType.cryptoRead
|
|
||||||
// },
|
|
||||||
// view: false,
|
|
||||||
// authLevel: adminAuthLevel.NONE
|
|
||||||
// },
|
|
||||||
businesslogview: {
|
businesslogview: {
|
||||||
title: '비즈니스 로그 조회',
|
title: '비즈니스 로그 조회',
|
||||||
permissions: {
|
permissions: {
|
||||||
@@ -118,6 +110,22 @@ export const menuConfig = {
|
|||||||
},
|
},
|
||||||
view: true,
|
view: true,
|
||||||
authLevel: adminAuthLevel.NONE
|
authLevel: adminAuthLevel.NONE
|
||||||
|
},
|
||||||
|
itemdictionary: {
|
||||||
|
title: '아이템 백과사전 조회',
|
||||||
|
permissions: {
|
||||||
|
read: authType.itemDictionaryRead
|
||||||
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
|
},
|
||||||
|
rankmanage: {
|
||||||
|
title: '랭킹 점수 관리',
|
||||||
|
permissions: {
|
||||||
|
read: authType.rankManagerRead
|
||||||
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -154,17 +162,17 @@ export const menuConfig = {
|
|||||||
view: true,
|
view: true,
|
||||||
authLevel: adminAuthLevel.NONE
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
reportlist: {
|
// reportlist: {
|
||||||
title: '신고내역',
|
// title: '신고내역',
|
||||||
permissions: {
|
// permissions: {
|
||||||
read: authType.reportRead,
|
// read: authType.reportRead,
|
||||||
update: authType.reportUpdate,
|
// update: authType.reportUpdate,
|
||||||
delete: authType.reportDelete
|
// delete: authType.reportDelete
|
||||||
},
|
// },
|
||||||
view: true,
|
// view: true,
|
||||||
authLevel: adminAuthLevel.NONE
|
// authLevel: adminAuthLevel.NONE
|
||||||
},
|
// },
|
||||||
event: {
|
rewardevent: {
|
||||||
title: '보상 이벤트 관리',
|
title: '보상 이벤트 관리',
|
||||||
permissions: {
|
permissions: {
|
||||||
read: authType.eventRead,
|
read: authType.eventRead,
|
||||||
@@ -214,6 +222,26 @@ export const menuConfig = {
|
|||||||
view: true,
|
view: true,
|
||||||
authLevel: adminAuthLevel.NONE
|
authLevel: adminAuthLevel.NONE
|
||||||
},
|
},
|
||||||
|
ranking: {
|
||||||
|
title: '랭킹 스케줄러',
|
||||||
|
permissions: {
|
||||||
|
read: authType.rankingRead,
|
||||||
|
update: authType.rankingUpdate,
|
||||||
|
delete: authType.rankingDelete
|
||||||
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
title: '통합 이벤트 관리',
|
||||||
|
permissions: {
|
||||||
|
read: authType.worldEventRead,
|
||||||
|
update: authType.worldEventUpdate,
|
||||||
|
delete: authType.worldEventDelete
|
||||||
|
},
|
||||||
|
view: true,
|
||||||
|
authLevel: adminAuthLevel.NONE
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -4,6 +4,20 @@ export const languageType = [
|
|||||||
{ value: 'JA', name: '일본어' },
|
{ value: 'JA', name: '일본어' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const TabUserList = [
|
||||||
|
{ value: 'BASIC', name: '기본정보' },
|
||||||
|
{ value: 'AVATAR', name: '아바타' },
|
||||||
|
{ value: 'CLOTH', name: '의상' },
|
||||||
|
{ value: 'TOOL', name: '도구' },
|
||||||
|
{ value: 'INVENTORY', name: '인벤토리' },
|
||||||
|
{ value: 'MAIL', name: '우편' },
|
||||||
|
{ value: 'MYHOME', name: '마이홈' },
|
||||||
|
{ value: 'FRIEND', name: '친구목록' },
|
||||||
|
{ value: 'TATTOO', name: '타투' },
|
||||||
|
{ value: 'QUEST', name: '퀘스트' }
|
||||||
|
// { value: 'CLAIM', name: '클레임' }
|
||||||
|
];
|
||||||
|
|
||||||
export const TabGameLogList = [
|
export const TabGameLogList = [
|
||||||
{ value: 'CURRENCY', name: '재화 로그' },
|
{ value: 'CURRENCY', name: '재화 로그' },
|
||||||
{ value: 'ITEM', name: '아이템 로그' },
|
{ value: 'ITEM', name: '아이템 로그' },
|
||||||
@@ -30,6 +44,12 @@ export const TabUserIndexList = [
|
|||||||
// { value: 'PLAYTIME', name: '플레이타임' },
|
// { value: 'PLAYTIME', name: '플레이타임' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const TabRankManageList = [
|
||||||
|
{ value: 'PIONEER', name: '개척자 랭킹 보드' },
|
||||||
|
{ value: 'RUN_RACE', name: '점프 러너 랭킹 보드' },
|
||||||
|
{ value: 'BATTLE_OBJECT', name: '컴뱃 존 랭킹 보드' }
|
||||||
|
];
|
||||||
|
|
||||||
export const mailSendType = [
|
export const mailSendType = [
|
||||||
{ value: 'ALL', name: '전체' },
|
{ value: 'ALL', name: '전체' },
|
||||||
{ value: 'RESERVE_SEND', name: '예약 발송' },
|
{ value: 'RESERVE_SEND', name: '예약 발송' },
|
||||||
@@ -220,6 +240,11 @@ export const landSearchType = [
|
|||||||
{ value: 'NAME', name: '랜드명' },
|
{ value: 'NAME', name: '랜드명' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const itemSearchType = [
|
||||||
|
{ value: 'ID', name: '아이템ID' },
|
||||||
|
{ value: 'NAME', name: '아이템명' },
|
||||||
|
];
|
||||||
|
|
||||||
export const blockType = [
|
export const blockType = [
|
||||||
{ value: '', name: '선택' },
|
{ value: '', name: '선택' },
|
||||||
{ value: 'Access_Restrictions', name: '접근 제한' },
|
{ value: 'Access_Restrictions', name: '접근 제한' },
|
||||||
@@ -388,7 +413,7 @@ export const opEquipType = [
|
|||||||
{ value: 3, name: '타투장착' },
|
{ value: 3, name: '타투장착' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export const opItemType = [
|
export const opItemLargeType = [
|
||||||
{ value: 'TOOL', name: '도구' },
|
{ value: 'TOOL', name: '도구' },
|
||||||
{ value: 'EXPENDABLE', name: '소모품' },
|
{ value: 'EXPENDABLE', name: '소모품' },
|
||||||
{ value: 'TICKET', name: '티켓' },
|
{ value: 'TICKET', name: '티켓' },
|
||||||
@@ -400,6 +425,95 @@ export const opItemType = [
|
|||||||
{ value: 'CURRENCY', name: '재화' },
|
{ value: 'CURRENCY', name: '재화' },
|
||||||
{ value: 'PRODUCT', name: '제품' },
|
{ value: 'PRODUCT', name: '제품' },
|
||||||
{ value: 'BEAUTY', name: '뷰티' },
|
{ value: 'BEAUTY', name: '뷰티' },
|
||||||
|
{ value: 'SET_BOX', name: '세트 박스' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const opItemSmallType = [
|
||||||
|
{ value: 'HANDMIRROR', name: '손거울' },
|
||||||
|
{ value: 'LIGHTSTICK', name: '응원봉' },
|
||||||
|
{ value: 'FIRECRACKER', name: '폭죽총' },
|
||||||
|
{ value: 'LIGHTSABER', name: '광선검' },
|
||||||
|
{ value: 'REGISTER_ITEM_SOCIAL_ACTION', name: '소셜액션 등록형 아이템' },
|
||||||
|
{ value: 'REGISTER_ITEM_INTERIOR', name: '인테리어 등록형 아이템' },
|
||||||
|
{ value: 'SAIYAN_AURA', name: '초사이어인 오라' },
|
||||||
|
{ value: 'TICKET', name: '소모형 티켓' },
|
||||||
|
{ value: 'RANDOMBOX', name: '골드(재화) 가챠 랜덤 박스' },
|
||||||
|
{ value: 'SHIRT', name: '상의' },
|
||||||
|
{ value: 'DRESS', name: '드레스' },
|
||||||
|
{ value: 'OUTER', name: '겉옷' },
|
||||||
|
{ value: 'PANTS', name: '하의' },
|
||||||
|
{ value: 'GLOVES', name: '장갑' },
|
||||||
|
{ value: 'RING', name: '반지' },
|
||||||
|
{ value: 'BRACELET', name: '팔찌' },
|
||||||
|
{ value: 'BAG', name: '가방(크로스백)' },
|
||||||
|
{ value: 'BACKPACK', name: '가방(백팩)' },
|
||||||
|
{ value: 'CAP', name: '모자' },
|
||||||
|
{ value: 'MASK', name: '가면' },
|
||||||
|
{ value: 'GLASSES', name: '안경' },
|
||||||
|
{ value: 'EARRING', name: '귀걸이' },
|
||||||
|
{ value: 'NECKLACE', name: '목걸이' },
|
||||||
|
{ value: 'SHOES', name: '신발' },
|
||||||
|
{ value: 'SOCKS', name: '양말' },
|
||||||
|
{ value: 'ANKLET', name: '발찌' },
|
||||||
|
{ value: 'OFFICECHAIR', name: '의자' },
|
||||||
|
{ value: 'WALLMOUNTTV', name: '벽걸이TV' },
|
||||||
|
{ value: 'OUTDOORCHAIR', name: '야외용의자' },
|
||||||
|
{ value: 'TV', name: 'TV' },
|
||||||
|
{ value: 'VIGNETTE', name: '소품' },
|
||||||
|
{ value: 'COOKWARE', name: '조리도구' },
|
||||||
|
{ value: 'KITCHEN_TOOL', name: '부엌도구' },
|
||||||
|
{ value: 'LAPTOP', name: '노트북' },
|
||||||
|
{ value: 'OUTDOOR_GOODS', name: '캠핑도구' },
|
||||||
|
{ value: 'BED', name: '침대' },
|
||||||
|
{ value: 'DECO', name: '소형장식품' },
|
||||||
|
{ value: 'FURNITURE', name: '(러그)가구' },
|
||||||
|
{ value: 'MUSIC', name: '악기/음향' },
|
||||||
|
{ value: 'SHELF_S', name: '소형 선반(TV다이)' },
|
||||||
|
{ value: 'SHELF_L', name: '대형 선반(금고,책장)' },
|
||||||
|
{ value: 'SOFA_SINGLE', name: '1인용 소파' },
|
||||||
|
{ value: 'SOFA_COUCH', name: '카우치 소파' },
|
||||||
|
{ value: 'LIGHT_CEILING', name: '천정 조명' },
|
||||||
|
{ value: 'LIGHT_FLOOR', name: '스탠드 조명' },
|
||||||
|
{ value: 'LIGHT_TABLE', name: '탁상 조명' },
|
||||||
|
{ value: 'LIGHT_PENDENT', name: '팬던트 조명' },
|
||||||
|
{ value: 'TABLE_S', name: '소형 테이블' },
|
||||||
|
{ value: 'TABLE_L', name: '대형 테이블' },
|
||||||
|
{ value: 'TABLE_LIVINGROOM', name: '거실 테이블' },
|
||||||
|
{ value: 'TABLE_OFFICE', name: '사무용 테이블' },
|
||||||
|
{ value: 'LEISURE_APPLIANCE', name: '스포츠/여가' },
|
||||||
|
{ value: 'INDUCTION', name: '인덕션' },
|
||||||
|
{ value: 'MICROWAVE', name: '전자레인지' },
|
||||||
|
{ value: 'LARGE_APPLIANCE', name: '대형가전' },
|
||||||
|
{ value: 'COSMETIC', name: '화장품' },
|
||||||
|
{ value: 'CHEST', name: '앞면' },
|
||||||
|
{ value: 'LEFT_ARM', name: '왼팔' },
|
||||||
|
{ value: 'RIGHT_ARM', name: '오른팔' },
|
||||||
|
{ value: 'BACK', name: '후면' },
|
||||||
|
{ value: 'LEFT_LEG', name: '왼다리' },
|
||||||
|
{ value: 'RIGHT_LEG', name: '오른다리' },
|
||||||
|
{ value: 'CARTRIDGE', name: '속성 카트리지' },
|
||||||
|
{ value: 'BUFF_DRINK', name: '드링크(물약) 아이템' },
|
||||||
|
{ value: 'INTERPHONE', name: '인터폰' },
|
||||||
|
{ value: 'MEGAPHONE', name: '확성기' },
|
||||||
|
{ value: 'CURRENCY', name: 'CURRENCY' },
|
||||||
|
{ value: 'NFTLAND', name: 'NFTLAND' },
|
||||||
|
{ value: 'SUMMONSTONE', name: '소환석' },
|
||||||
|
{ value: 'GOLD', name: '골드' },
|
||||||
|
{ value: 'SAPPHIRE', name: '사파이어' },
|
||||||
|
{ value: 'CALIUM', name: '칼리움' },
|
||||||
|
{ value: 'BEAM', name: '빔' },
|
||||||
|
{ value: 'RUBY', name: '루비' },
|
||||||
|
{ value: 'LIGHT_LIMITED', name: '언리얼 라이트 사용 조명' },
|
||||||
|
{ value: 'SPEAKER', name: '재생 기능성 스피커' },
|
||||||
|
{ value: 'SETBOX', name: '세트박스' },
|
||||||
|
{ value: 'DRESS_SHOES', name: '드레스+신발' },
|
||||||
|
{ value: 'SHOULDERBAG', name: '숄더백' },
|
||||||
|
{ value: 'RECIPE', name: '레시피' }
|
||||||
|
]
|
||||||
|
|
||||||
|
export const opGender = [
|
||||||
|
{ value: 'MALE', name: '남성' },
|
||||||
|
{ value: 'FEMALE', name: '여성' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export const opHistoryType = [
|
export const opHistoryType = [
|
||||||
@@ -521,7 +635,10 @@ export const opLogAction = [
|
|||||||
|
|
||||||
export const opCommonStatus = [
|
export const opCommonStatus = [
|
||||||
{ value: 'SUCCESS', name: '성공' },
|
{ value: 'SUCCESS', name: '성공' },
|
||||||
{ value: 'FAIL', name: '실패' }
|
{ value: 'FAIL', name: '실패' },
|
||||||
|
{ value: 'WAIT', name: '대기' },
|
||||||
|
{ value: 'END', name: '종료' },
|
||||||
|
{ value: 'RUNNING', name: '진행중' },
|
||||||
]
|
]
|
||||||
|
|
||||||
// export const logAction = [
|
// export const logAction = [
|
||||||
@@ -820,6 +937,7 @@ export const opCommonStatus = [
|
|||||||
|
|
||||||
export const logAction = [
|
export const logAction = [
|
||||||
{ value: "None", name: "전체" },
|
{ value: "None", name: "전체" },
|
||||||
|
{ value: "AdminToolQuestTaskForceComplete", name: "AdminToolQuestTaskForceComplete" },
|
||||||
{ value: "AIChatDeleteCharacter", name: "AIChatDeleteCharacter" },
|
{ value: "AIChatDeleteCharacter", name: "AIChatDeleteCharacter" },
|
||||||
{ value: "AIChatDeleteUser", name: "AIChatDeleteUser" },
|
{ value: "AIChatDeleteUser", name: "AIChatDeleteUser" },
|
||||||
{ value: "AIChatGetCharacter", name: "AIChatGetCharacter" },
|
{ value: "AIChatGetCharacter", name: "AIChatGetCharacter" },
|
||||||
@@ -854,12 +972,12 @@ export const logAction = [
|
|||||||
{ value: "BeaconShopUpdateDailyCount", name: "BeaconShopUpdateDailyCount" },
|
{ value: "BeaconShopUpdateDailyCount", name: "BeaconShopUpdateDailyCount" },
|
||||||
{ value: "BeaconShopDeleteRecord", name: "BeaconShopDeleteRecord" },
|
{ value: "BeaconShopDeleteRecord", name: "BeaconShopDeleteRecord" },
|
||||||
{ value: "BeaconShopDeactiveItems", name: "BeaconShopDeactiveItems" },
|
{ value: "BeaconShopDeactiveItems", name: "BeaconShopDeactiveItems" },
|
||||||
{ value: "BrokerApiAdmin", name: "BrokerApiAdmin" },
|
// { value: "BrokerApiAdmin", name: "BrokerApiAdmin" },
|
||||||
{ value: "BrokerApiPlanetAuth", name: "BrokerApiPlanetAuth" },
|
{ value: "BrokerApiPlanetAuth", name: "BrokerApiPlanetAuth" },
|
||||||
{ value: "BrokerApiUserExchangeOrderCompleted", name: "BrokerApiUserExchangeOrderCompleted" },
|
{ value: "BrokerApiUserExchangeOrderCompleted", name: "BrokerApiUserExchangeOrderCompleted" },
|
||||||
{ value: "BrokerApiUserExchangeOrderCreated", name: "BrokerApiUserExchangeOrderCreated" },
|
{ value: "BrokerApiUserExchangeOrderCreated", name: "BrokerApiUserExchangeOrderCreated" },
|
||||||
{ value: "BrokerApiUserSystemMailSend", name: "BrokerApiUserSystemMailSend" },
|
// { value: "BrokerApiUserSystemMailSend", name: "BrokerApiUserSystemMailSend" },
|
||||||
{ value: "BrokerApiUserEchoSystemRequest", name: "BrokerApiUserEchoSystemRequest" },
|
// { value: "BrokerApiUserEchoSystemRequest", name: "BrokerApiUserEchoSystemRequest" },
|
||||||
{ value: "BrokerApiUserLogin", name: "BrokerApiUserLogin" },
|
{ value: "BrokerApiUserLogin", name: "BrokerApiUserLogin" },
|
||||||
{ value: "BuffAdd", name: "BuffAdd" },
|
{ value: "BuffAdd", name: "BuffAdd" },
|
||||||
{ value: "BuffDelete", name: "BuffDelete" },
|
{ value: "BuffDelete", name: "BuffDelete" },
|
||||||
@@ -909,6 +1027,7 @@ export const logAction = [
|
|||||||
{ value: "ClaimReward", name: "ClaimReward" },
|
{ value: "ClaimReward", name: "ClaimReward" },
|
||||||
{ value: "ConvertCalium", name: "ConvertCalium" },
|
{ value: "ConvertCalium", name: "ConvertCalium" },
|
||||||
{ value: "ConvertExchangeCalium", name: "ConvertExchangeCalium" },
|
{ value: "ConvertExchangeCalium", name: "ConvertExchangeCalium" },
|
||||||
|
{ value: "ContentsMove", name: "ContentsMove" },
|
||||||
{ value: "CraftFinish", name: "CraftFinish" },
|
{ value: "CraftFinish", name: "CraftFinish" },
|
||||||
{ value: "CraftHelp", name: "CraftHelp" },
|
{ value: "CraftHelp", name: "CraftHelp" },
|
||||||
{ value: "CraftRecipeRegister", name: "CraftRecipeRegister" },
|
{ value: "CraftRecipeRegister", name: "CraftRecipeRegister" },
|
||||||
@@ -935,6 +1054,12 @@ export const logAction = [
|
|||||||
{ value: "FriendAdd", name: "FriendAdd" },
|
{ value: "FriendAdd", name: "FriendAdd" },
|
||||||
{ value: "FriendDelete", name: "FriendDelete" },
|
{ value: "FriendDelete", name: "FriendDelete" },
|
||||||
{ value: "GainLandProfit", name: "GainLandProfit" },
|
{ value: "GainLandProfit", name: "GainLandProfit" },
|
||||||
|
{ value: "GameModeObjectInteraction", name: "GameModeObjectInteraction" },
|
||||||
|
{ value: "GameModeObjectStateUpdate", name: "GameModeObjectStateUpdate" },
|
||||||
|
{ value: "GameModeUserDead", name: "GameModeUserDead" },
|
||||||
|
{ value: "GameModeUserRespawn", name: "GameModeUserRespawn" },
|
||||||
|
{ value: "GameModePenalty", name: "GameModePenalty" },
|
||||||
|
{ value: "GameModeAddMatchCount", name: "GameModeAddMatchCount" },
|
||||||
{ value: "InviteParty", name: "InviteParty" },
|
{ value: "InviteParty", name: "InviteParty" },
|
||||||
{ value: "ItemBuy", name: "ItemBuy" },
|
{ value: "ItemBuy", name: "ItemBuy" },
|
||||||
{ value: "ItemDestroy", name: "ItemDestroy" },
|
{ value: "ItemDestroy", name: "ItemDestroy" },
|
||||||
@@ -965,8 +1090,17 @@ export const logAction = [
|
|||||||
{ value: "MailRead", name: "MailRead" },
|
{ value: "MailRead", name: "MailRead" },
|
||||||
{ value: "MailSend", name: "MailSend" },
|
{ value: "MailSend", name: "MailSend" },
|
||||||
{ value: "MailTaken", name: "MailTaken" },
|
{ value: "MailTaken", name: "MailTaken" },
|
||||||
|
{ value: "MatchReserve", name: "MatchReserve" },
|
||||||
|
{ value: "MatchCancel", name: "MatchCancel" },
|
||||||
|
{ value: "MatchResult", name: "MatchResult" },
|
||||||
|
{ value: "MatchRoomUserJoin", name: "MatchRoomUserJoin" },
|
||||||
|
{ value: "MatchRoomUserQuit", name: "MatchRoomUserQuit" },
|
||||||
|
{ value: "MatchRoomCreate", name: "MatchRoomCreate" },
|
||||||
|
{ value: "MatchRoomUpdate", name: "MatchRoomUpdate" },
|
||||||
|
{ value: "MatchRoomDestroy", name: "MatchRoomDestroy" },
|
||||||
{ value: "ModifyLandInfo", name: "ModifyLandInfo" },
|
{ value: "ModifyLandInfo", name: "ModifyLandInfo" },
|
||||||
{ value: "MoneyChange", name: "MoneyChange" },
|
{ value: "MoneyChange", name: "MoneyChange" },
|
||||||
|
{ value: "MoveToBeacon", name: "MoveToBeacon" },
|
||||||
{ value: "ProductGive", name: "ProductGive" },
|
{ value: "ProductGive", name: "ProductGive" },
|
||||||
{ value: "ProductOpenFailed", name: "ProductOpenFailed" },
|
{ value: "ProductOpenFailed", name: "ProductOpenFailed" },
|
||||||
{ value: "ProductOpenSuccess", name: "ProductOpenSuccess" },
|
{ value: "ProductOpenSuccess", name: "ProductOpenSuccess" },
|
||||||
@@ -990,6 +1124,11 @@ export const logAction = [
|
|||||||
{ value: "ReplySummonParty", name: "ReplySummonParty" },
|
{ value: "ReplySummonParty", name: "ReplySummonParty" },
|
||||||
{ value: "ReservationEnterToServer", name: "ReservationEnterToServer" },
|
{ value: "ReservationEnterToServer", name: "ReservationEnterToServer" },
|
||||||
{ value: "RewardProp", name: "RewardProp" },
|
{ value: "RewardProp", name: "RewardProp" },
|
||||||
|
{ value: "RunRaceFinishReward", name: "RunRaceFinishReward" },
|
||||||
|
{ value: "RunRaceRespawnReward", name: "RunRaceRespawnReward" },
|
||||||
|
{ value: "RunRaceUnFinishReward", name: "RunRaceUnFinishReward" },
|
||||||
|
{ value: "RunRaceCheckPointAbusing", name: "RunRaceCheckPointAbusing" },
|
||||||
|
{ value: "RunRaceResultSummary", name: "RunRaceResultSummary" },
|
||||||
{ value: "SaveMyhome", name: "SaveMyhome" },
|
{ value: "SaveMyhome", name: "SaveMyhome" },
|
||||||
{ value: "SeasonPassBuyCharged", name: "SeasonPassBuyCharged" },
|
{ value: "SeasonPassBuyCharged", name: "SeasonPassBuyCharged" },
|
||||||
{ value: "SeasonPassStartNew", name: "SeasonPassStartNew" },
|
{ value: "SeasonPassStartNew", name: "SeasonPassStartNew" },
|
||||||
@@ -1039,6 +1178,7 @@ export const logAction = [
|
|||||||
{ value: "UpdateGameOption", name: "UpdateGameOption" },
|
{ value: "UpdateGameOption", name: "UpdateGameOption" },
|
||||||
{ value: "UpdateLanguage", name: "UpdateLanguage" },
|
{ value: "UpdateLanguage", name: "UpdateLanguage" },
|
||||||
{ value: "UpdateUgcNpcLike", name: "UpdateUgcNpcLike" },
|
{ value: "UpdateUgcNpcLike", name: "UpdateUgcNpcLike" },
|
||||||
|
{ value: "UpdateGameModePlayerRegulation", name: "UpdateGameModePlayerRegulation" },
|
||||||
{ value: "UserBlock", name: "UserBlock" },
|
{ value: "UserBlock", name: "UserBlock" },
|
||||||
{ value: "UserBlockCancel", name: "UserBlockCancel" },
|
{ value: "UserBlockCancel", name: "UserBlockCancel" },
|
||||||
{ value: "UserCreate", name: "UserCreate" },
|
{ value: "UserCreate", name: "UserCreate" },
|
||||||
@@ -1091,6 +1231,9 @@ export const logDomain = [
|
|||||||
{ value: "Farming", name: "Farming" },
|
{ value: "Farming", name: "Farming" },
|
||||||
{ value: "FarmingReward", name: "FarmingReward" },
|
{ value: "FarmingReward", name: "FarmingReward" },
|
||||||
{ value: "GameLogInOut", name: "GameLogInOut" },
|
{ value: "GameLogInOut", name: "GameLogInOut" },
|
||||||
|
{ value: "GameObjectInteraction", name: "GameObjectInteraction" },
|
||||||
|
{ value: "GameModePenalty", name: "GameModePenalty" },
|
||||||
|
{ value: "GameModePlayRegulation", name: "GameModePlayRegulation" },
|
||||||
{ value: "Item", name: "Item" },
|
{ value: "Item", name: "Item" },
|
||||||
{ value: "IgmApi", name: "IgmApi" },
|
{ value: "IgmApi", name: "IgmApi" },
|
||||||
{ value: "MyHome", name: "MyHome" },
|
{ value: "MyHome", name: "MyHome" },
|
||||||
@@ -1102,6 +1245,9 @@ export const logDomain = [
|
|||||||
{ value: "Mail", name: "Mail" },
|
{ value: "Mail", name: "Mail" },
|
||||||
{ value: "MailStoragePeriodExpired", name: "MailStoragePeriodExpired" },
|
{ value: "MailStoragePeriodExpired", name: "MailStoragePeriodExpired" },
|
||||||
{ value: "MailProfile", name: "MailProfile" },
|
{ value: "MailProfile", name: "MailProfile" },
|
||||||
|
{ value: "MatchUser", name: "MatchUser" },
|
||||||
|
{ value: "MatchServerUser", name: "MatchServerUser" },
|
||||||
|
{ value: "MatchRoom", name: "MatchRoom" },
|
||||||
{ value: "Party", name: "Party" },
|
{ value: "Party", name: "Party" },
|
||||||
{ value: "PartyMember", name: "PartyMember" },
|
{ value: "PartyMember", name: "PartyMember" },
|
||||||
{ value: "PartyVote", name: "PartyVote" },
|
{ value: "PartyVote", name: "PartyVote" },
|
||||||
@@ -1119,6 +1265,11 @@ export const logDomain = [
|
|||||||
{ value: "RenewalShopProducts", name: "RenewalShopProducts" },
|
{ value: "RenewalShopProducts", name: "RenewalShopProducts" },
|
||||||
{ value: "Rental", name: "Rental" },
|
{ value: "Rental", name: "Rental" },
|
||||||
{ value: "RewardProp", name: "RewardProp" },
|
{ value: "RewardProp", name: "RewardProp" },
|
||||||
|
{ value: "RunRaceFinishReward", name: "RunRaceFinishReward" },
|
||||||
|
{ value: "RunRaceRespawnReward", name: "RunRaceRespawnReward" },
|
||||||
|
{ value: "RunRaceUnFinishReward", name: "RunRaceUnFinishReward" },
|
||||||
|
{ value: "RunRaceCheckPointAbusing", name: "RunRaceCheckPointAbusing" },
|
||||||
|
{ value: "RunRaceRewardSummary", name: "RunRaceRewardSummary" },
|
||||||
{ value: "Stage", name: "Stage" },
|
{ value: "Stage", name: "Stage" },
|
||||||
{ value: "SocialAction", name: "SocialAction" },
|
{ value: "SocialAction", name: "SocialAction" },
|
||||||
{ value: "SeasonPass", name: "SeasonPass" },
|
{ value: "SeasonPass", name: "SeasonPass" },
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"initialSearchParams": {
|
"initialSearchParams": {
|
||||||
"searchTitle": "",
|
"searchData": "",
|
||||||
"searchContent": "",
|
|
||||||
"status": "ALL",
|
"status": "ALL",
|
||||||
"startDate": "",
|
"startDate": "",
|
||||||
"endDate": "",
|
"endDate": "",
|
||||||
@@ -13,51 +12,37 @@
|
|||||||
"searchFields": [
|
"searchFields": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"id": "searchTitle",
|
"id": "searchData",
|
||||||
"label": "우편 제목",
|
"label": "제목",
|
||||||
"placeholder": "제목 입력",
|
"placeholder": "제목 입력",
|
||||||
"width": "300px",
|
"width": "300px",
|
||||||
"col": 1
|
"col": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "period",
|
|
||||||
"startDateId": "startDate",
|
|
||||||
"endDateId": "endDate",
|
|
||||||
"label": "조회 일자",
|
|
||||||
"col": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"id": "searchContent",
|
|
||||||
"label": "우편 내용",
|
|
||||||
"placeholder": "우편 내용(공백으로 구분)",
|
|
||||||
"width": "300px",
|
|
||||||
"col": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"id": "status",
|
"id": "status",
|
||||||
"label": "이벤트 상태",
|
"label": "상태",
|
||||||
"optionsRef": "eventStatus",
|
"optionsRef": "opMenuBannerStatus",
|
||||||
"col": 2
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "period",
|
||||||
|
"startDateId": "startDate",
|
||||||
|
"endDateId": "endDate",
|
||||||
|
"label": "기간",
|
||||||
|
"col": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"apiInfo": {
|
"apiInfo": {
|
||||||
"functionName": "EventView",
|
"endpointName": "EventView",
|
||||||
"loadOnMount": true,
|
"loadOnMount": true,
|
||||||
"paramsMapping": [
|
"paramTransforms": [
|
||||||
"searchTitle",
|
|
||||||
"searchContent",
|
|
||||||
"status",
|
|
||||||
{"param": "startDate", "transform": "toISOString"},
|
{"param": "startDate", "transform": "toISOString"},
|
||||||
{"param": "endDate", "transform": "toISOString"},
|
{"param": "endDate", "transform": "toISOString"}
|
||||||
"orderBy",
|
|
||||||
"pageSize",
|
|
||||||
"currentPage"
|
|
||||||
],
|
],
|
||||||
"pageField": "currentPage",
|
"pageField": "page_no",
|
||||||
"pageSizeField": "pageSize",
|
"pageSizeField": "page_size",
|
||||||
"orderField": "orderBy"
|
"orderField": "orderBy"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
119
src/assets/data/pages/eventTable.json
Normal file
119
src/assets/data/pages/eventTable.json
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"id": "eventTable",
|
||||||
|
"selection": {
|
||||||
|
"type": "single",
|
||||||
|
"idField": "id"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"countType": "total",
|
||||||
|
"orderType": "desc",
|
||||||
|
"pageType": "default",
|
||||||
|
"buttons": [
|
||||||
|
{
|
||||||
|
"id": "delete",
|
||||||
|
"text": "선택 삭제",
|
||||||
|
"theme": "line",
|
||||||
|
"disableWhen": "noSelection",
|
||||||
|
"requiredAuth": "worldEventDelete",
|
||||||
|
"action": "delete"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "register",
|
||||||
|
"text": "통합 이벤트 등록",
|
||||||
|
"theme": "primary",
|
||||||
|
"requiredAuth": "worldEventUpdate",
|
||||||
|
"action": "regist"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"id": "checkbox",
|
||||||
|
"type": "checkbox",
|
||||||
|
"width": "40px",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "row_num",
|
||||||
|
"type": "text",
|
||||||
|
"width": "70px",
|
||||||
|
"title": "번호"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "status",
|
||||||
|
"type": "status",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "상태",
|
||||||
|
"option_name": "opCommonStatus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"type": "text",
|
||||||
|
"title": "제목",
|
||||||
|
"width": "150px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "personal_event_action_id",
|
||||||
|
"type": "text",
|
||||||
|
"width": "150px",
|
||||||
|
"title": "개인제작 이벤트 모드"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "global_event_action_id",
|
||||||
|
"type": "text",
|
||||||
|
"width": "150px",
|
||||||
|
"title": "기여도 이벤트 모드"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "max_point",
|
||||||
|
"type": "text",
|
||||||
|
"width": "150px",
|
||||||
|
"title": "기여도 목표점수"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "start_dt",
|
||||||
|
"type": "date",
|
||||||
|
"width": "220px",
|
||||||
|
"title": "시작일(KST)",
|
||||||
|
"format": {
|
||||||
|
"type": "function",
|
||||||
|
"name": "convertKTC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "end_dt",
|
||||||
|
"type": "date",
|
||||||
|
"width": "220px",
|
||||||
|
"title": "종료일(KST)",
|
||||||
|
"format": {
|
||||||
|
"type": "function",
|
||||||
|
"name": "convertKTC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "detail",
|
||||||
|
"type": "button",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "상세보기",
|
||||||
|
"text": "상세보기",
|
||||||
|
"action": {
|
||||||
|
"type": "modal",
|
||||||
|
"target": "detailModal",
|
||||||
|
"dataParam": {
|
||||||
|
"id": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "history",
|
||||||
|
"type": "button",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "히스토리",
|
||||||
|
"text": "히스토리"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort": {
|
||||||
|
"defaultColumn": "row_num",
|
||||||
|
"defaultDirection": "desc"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
"searchType": "ID",
|
"searchType": "ID",
|
||||||
"searchData": "",
|
"searchData": "",
|
||||||
"historyType": "",
|
"historyType": "",
|
||||||
"startDate": "",
|
"startDate": "today",
|
||||||
"endDate": "",
|
"endDate": "today",
|
||||||
"orderBy": "DESC",
|
"orderBy": "DESC",
|
||||||
"pageSize": 50,
|
"pageSize": 50,
|
||||||
"currentPage": 1
|
"currentPage": 1
|
||||||
@@ -37,7 +37,10 @@
|
|||||||
"startDateId": "startDate",
|
"startDateId": "startDate",
|
||||||
"endDateId": "endDate",
|
"endDateId": "endDate",
|
||||||
"label": "기간",
|
"label": "기간",
|
||||||
"col": 2
|
"col": 2,
|
||||||
|
"width": "500px",
|
||||||
|
"format": "YYYY-MM-DD HH:mm:ss",
|
||||||
|
"showTime": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
48
src/assets/data/pages/rankingSearch.json
Normal file
48
src/assets/data/pages/rankingSearch.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"initialSearchParams": {
|
||||||
|
"searchData": "",
|
||||||
|
"status": "ALL",
|
||||||
|
"startDate": "",
|
||||||
|
"endDate": "",
|
||||||
|
"orderBy": "DESC",
|
||||||
|
"pageSize": 50,
|
||||||
|
"currentPage": 1
|
||||||
|
},
|
||||||
|
|
||||||
|
"searchFields": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "searchData",
|
||||||
|
"label": "제목",
|
||||||
|
"placeholder": "제목 입력",
|
||||||
|
"width": "300px",
|
||||||
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"id": "status",
|
||||||
|
"label": "상태",
|
||||||
|
"optionsRef": "opMenuBannerStatus",
|
||||||
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "period",
|
||||||
|
"startDateId": "startDate",
|
||||||
|
"endDateId": "endDate",
|
||||||
|
"label": "기간",
|
||||||
|
"col": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"apiInfo": {
|
||||||
|
"endpointName": "RankingScheduleView",
|
||||||
|
"loadOnMount": true,
|
||||||
|
"paramTransforms": [
|
||||||
|
{"param": "startDate", "transform": "toISOString"},
|
||||||
|
{"param": "endDate", "transform": "toISOString"}
|
||||||
|
],
|
||||||
|
"pageField": "page_no",
|
||||||
|
"pageSizeField": "page_size",
|
||||||
|
"orderField": "orderBy"
|
||||||
|
}
|
||||||
|
}
|
||||||
140
src/assets/data/pages/rankingTable.json
Normal file
140
src/assets/data/pages/rankingTable.json
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
{
|
||||||
|
"id": "rankingTable",
|
||||||
|
"selection": {
|
||||||
|
"type": "single",
|
||||||
|
"idField": "id"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"countType": "total",
|
||||||
|
"orderType": "desc",
|
||||||
|
"pageType": "default",
|
||||||
|
"buttons": [
|
||||||
|
{
|
||||||
|
"id": "delete",
|
||||||
|
"text": "선택 삭제",
|
||||||
|
"theme": "line",
|
||||||
|
"disableWhen": "noSelection",
|
||||||
|
"requiredAuth": "rankingDelete",
|
||||||
|
"action": "delete"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "register",
|
||||||
|
"text": "랭킹 스케줄 등록",
|
||||||
|
"theme": "primary",
|
||||||
|
"requiredAuth": "rankingUpdate",
|
||||||
|
"action": "regist"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"id": "checkbox",
|
||||||
|
"type": "checkbox",
|
||||||
|
"width": "40px",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "row_num",
|
||||||
|
"type": "text",
|
||||||
|
"width": "70px",
|
||||||
|
"title": "번호"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "status",
|
||||||
|
"type": "status",
|
||||||
|
"width": "100px",
|
||||||
|
"title": "상태",
|
||||||
|
"option_name": "opCommonStatus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"type": "text",
|
||||||
|
"title": "제목"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "meta_id",
|
||||||
|
"type": "text",
|
||||||
|
"title": "랭킹모드",
|
||||||
|
"width": "100px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "event_action_id",
|
||||||
|
"type": "text",
|
||||||
|
"title": "이벤트 액션 그룹",
|
||||||
|
"width": "150px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "start_dt",
|
||||||
|
"type": "date",
|
||||||
|
"width": "200px",
|
||||||
|
"title": "시작일(KST)",
|
||||||
|
"format": {
|
||||||
|
"type": "function",
|
||||||
|
"name": "convertKTC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "end_dt",
|
||||||
|
"type": "date",
|
||||||
|
"width": "200px",
|
||||||
|
"title": "종료일(KST)",
|
||||||
|
"format": {
|
||||||
|
"type": "function",
|
||||||
|
"name": "convertKTC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "base_dt",
|
||||||
|
"type": "date",
|
||||||
|
"width": "200px",
|
||||||
|
"title": "기준일(KST)",
|
||||||
|
"format": {
|
||||||
|
"type": "function",
|
||||||
|
"name": "convertKTC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "refresh_interval",
|
||||||
|
"type": "text",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "새로고침 주기"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "initialization_interval",
|
||||||
|
"type": "text",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "초기화 주기"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "snapshot_interval",
|
||||||
|
"type": "text",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "스냅샷 주기"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "detail",
|
||||||
|
"type": "button",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "상세보기",
|
||||||
|
"text": "상세보기",
|
||||||
|
"action": {
|
||||||
|
"type": "modal",
|
||||||
|
"target": "detailModal",
|
||||||
|
"dataParam": {
|
||||||
|
"id": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "history",
|
||||||
|
"type": "button",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "히스토리",
|
||||||
|
"text": "히스토리"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort": {
|
||||||
|
"defaultColumn": "row_num",
|
||||||
|
"defaultDirection": "desc"
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/assets/data/pages/rewardEventSearch.json
Normal file
63
src/assets/data/pages/rewardEventSearch.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"initialSearchParams": {
|
||||||
|
"searchTitle": "",
|
||||||
|
"searchContent": "",
|
||||||
|
"status": "ALL",
|
||||||
|
"startDate": "",
|
||||||
|
"endDate": "",
|
||||||
|
"orderBy": "DESC",
|
||||||
|
"pageSize": 50,
|
||||||
|
"currentPage": 1
|
||||||
|
},
|
||||||
|
|
||||||
|
"searchFields": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "searchTitle",
|
||||||
|
"label": "우편 제목",
|
||||||
|
"placeholder": "제목 입력",
|
||||||
|
"width": "300px",
|
||||||
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "period",
|
||||||
|
"startDateId": "startDate",
|
||||||
|
"endDateId": "endDate",
|
||||||
|
"label": "조회 일자",
|
||||||
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "searchContent",
|
||||||
|
"label": "우편 내용",
|
||||||
|
"placeholder": "우편 내용(공백으로 구분)",
|
||||||
|
"width": "300px",
|
||||||
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"id": "status",
|
||||||
|
"label": "이벤트 상태",
|
||||||
|
"optionsRef": "eventStatus",
|
||||||
|
"col": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"apiInfo": {
|
||||||
|
"functionName": "RewardEventView",
|
||||||
|
"loadOnMount": true,
|
||||||
|
"paramsMapping": [
|
||||||
|
"searchTitle",
|
||||||
|
"searchContent",
|
||||||
|
"status",
|
||||||
|
{"param": "startDate", "transform": "toISOString"},
|
||||||
|
{"param": "endDate", "transform": "toISOString"},
|
||||||
|
"orderBy",
|
||||||
|
"pageSize",
|
||||||
|
"currentPage"
|
||||||
|
],
|
||||||
|
"pageField": "currentPage",
|
||||||
|
"pageSizeField": "pageSize",
|
||||||
|
"orderField": "orderBy"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,15 @@ export const authType = {
|
|||||||
menuBannerRead: 50,
|
menuBannerRead: 50,
|
||||||
menuBannerUpdate: 51,
|
menuBannerUpdate: 51,
|
||||||
menuBannerDelete: 52,
|
menuBannerDelete: 52,
|
||||||
|
itemDictionaryRead: 53,
|
||||||
|
rankManagerRead: 54,
|
||||||
|
rankManagerUpdate: 55,
|
||||||
|
rankingRead: 56,
|
||||||
|
rankingUpdate: 57,
|
||||||
|
rankingDelete: 58,
|
||||||
|
worldEventRead: 59,
|
||||||
|
worldEventUpdate: 60,
|
||||||
|
worldEventDelete: 61,
|
||||||
|
|
||||||
|
|
||||||
levelReader: 999,
|
levelReader: 999,
|
||||||
@@ -75,20 +84,6 @@ export const adminAuthLevel = {
|
|||||||
DEVELOPER: "Developer",
|
DEVELOPER: "Developer",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TabUserList = [
|
|
||||||
{ title: '기본정보' },
|
|
||||||
{ title: '아바타' },
|
|
||||||
{ title: '의상' },
|
|
||||||
{ title: '도구' },
|
|
||||||
{ title: '인벤토리' },
|
|
||||||
{ title: '우편' },
|
|
||||||
{ title: '마이홈' },
|
|
||||||
{ title: '친구목록' },
|
|
||||||
{ title: '타투' },
|
|
||||||
{ title: '퀘스트' },
|
|
||||||
// { title: '클레임' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ivenTabType = {
|
export const ivenTabType = {
|
||||||
CLOTH: "cloth",
|
CLOTH: "cloth",
|
||||||
PROP: "prop",
|
PROP: "prop",
|
||||||
|
|||||||
@@ -93,12 +93,6 @@ const UserInfoTable = styled.table`
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
tr:first-child {
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
border-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
th,
|
th,
|
||||||
td {
|
td {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { UserTattooView } from '../../apis/Users';
|
import { UserTattooView } from '../../apis/Users';
|
||||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||||
|
|
||||||
const UserTatttooInfo = ({ userInfo }) => {
|
const UserTattooInfo = ({ userInfo }) => {
|
||||||
const [dataList, setDataList] = useState();
|
const [dataList, setDataList] = useState();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ const UserTatttooInfo = ({ userInfo }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserTatttooInfo;
|
export default UserTattooInfo;
|
||||||
|
|
||||||
const UserDefaultTable = styled.table`
|
const UserDefaultTable = styled.table`
|
||||||
border: 1px solid #e8eaec;
|
border: 1px solid #e8eaec;
|
||||||
|
|||||||
@@ -32,6 +32,18 @@ const ImageUploadBtn = ({ disabled,
|
|||||||
|
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
|
const koreanRegex = /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/;
|
||||||
|
if (koreanRegex.test(file.name)) {
|
||||||
|
showToast('FILE_KOREAN_NAME_WARNING', {
|
||||||
|
type: alertTypes.warning
|
||||||
|
});
|
||||||
|
if (document.querySelector('#fileinput')) {
|
||||||
|
document.querySelector('#fileinput').value = '';
|
||||||
|
}
|
||||||
|
onFileDelete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 이미지 파일 확장자 체크
|
// 이미지 파일 확장자 체크
|
||||||
const fileExt = file.name.split('.').pop().toLowerCase();
|
const fileExt = file.name.split('.').pop().toLowerCase();
|
||||||
if (fileExt !== 'png' && fileExt !== 'jpg' && fileExt !== 'jpeg') {
|
if (fileExt !== 'png' && fileExt !== 'jpg' && fileExt !== 'jpeg') {
|
||||||
|
|||||||
@@ -1,79 +1,29 @@
|
|||||||
import React from 'react';
|
import { DatePicker } from 'antd';
|
||||||
import DatePickerComponent from './DatePickerComponent';
|
import dayjs from 'dayjs';
|
||||||
import { DatePickerWrapper } from '../../../styles/Components';
|
|
||||||
import {
|
const { RangePicker } = DatePicker;
|
||||||
FormRowGroup,
|
|
||||||
FormLabel,
|
|
||||||
DateContainer,
|
|
||||||
DateTimeWrapper,
|
|
||||||
DateTimeGroup,
|
|
||||||
} from '../../../styles/ModuleComponents';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
const DateRangePicker = ({
|
const DateRangePicker = ({
|
||||||
label,
|
value,
|
||||||
startDate,
|
onChange,
|
||||||
endDate,
|
format,
|
||||||
onStartDateChange,
|
showTime = true,
|
||||||
onEndDateChange,
|
size = 'middle',
|
||||||
pastDate = new Date(),
|
...props
|
||||||
disabled,
|
|
||||||
startLabel = '시작 일자',
|
|
||||||
endLabel = '종료 일자',
|
|
||||||
setAlert,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const handleStartDate = (date) => {
|
|
||||||
const newDate = new Date(date);
|
|
||||||
onStartDateChange(newDate);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEndDate = (date) => {
|
|
||||||
let newDate = new Date(date);
|
|
||||||
|
|
||||||
if (startDate && newDate < startDate) {
|
|
||||||
setAlert(t('DATE_START_DIFF_END'));
|
|
||||||
newDate = new Date(startDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
onEndDateChange(newDate);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormRowGroup>
|
<RangePicker
|
||||||
<FormLabel>{label}</FormLabel>
|
showTime={showTime}
|
||||||
<DateTimeWrapper>
|
value={value ? [dayjs(value[0]), dayjs(value[1])] : [null, null]}
|
||||||
<DateTimeGroup>
|
format={format || 'YYYY-MM-DD HH:mm:ss'}
|
||||||
<DateContainer>
|
onChange={onChange}
|
||||||
<DatePickerWrapper>
|
placeholder={['시작 일시', '종료 일시']}
|
||||||
<DatePickerComponent
|
size={size}
|
||||||
name={startLabel}
|
allowClear={false}
|
||||||
handleSelectedDate={handleStartDate}
|
{...props}
|
||||||
selectedDate={startDate}
|
/>
|
||||||
pastDate={pastDate}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</DatePickerWrapper>
|
|
||||||
</DateContainer>
|
|
||||||
</DateTimeGroup>
|
|
||||||
|
|
||||||
<DateTimeGroup>
|
|
||||||
<DateContainer>
|
|
||||||
<DatePickerWrapper>
|
|
||||||
<DatePickerComponent
|
|
||||||
name={endLabel}
|
|
||||||
handleSelectedDate={handleEndDate}
|
|
||||||
selectedDate={endDate}
|
|
||||||
pastDate={pastDate}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</DatePickerWrapper>
|
|
||||||
</DateContainer>
|
|
||||||
</DateTimeGroup>
|
|
||||||
</DateTimeWrapper>
|
|
||||||
</FormRowGroup>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default DateRangePicker;
|
export default DateRangePicker;
|
||||||
205
src/components/common/Header/Navi_bak.js
Normal file
205
src/components/common/Header/Navi_bak.js
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
import { NavLink, useNavigate } from 'react-router-dom';
|
||||||
|
import arrowIcon from '../../../assets/img/icon/icon-tab.png';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { authList } from '../../../store/authList';
|
||||||
|
import Modal from '../modal/Modal';
|
||||||
|
import { BtnWrapper, ButtonClose, ModalText } from '../../../styles/Components';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import Button from '../button/Button';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { AuthInfo } from '../../../apis';
|
||||||
|
import { getMenuConfig } from '../../../utils';
|
||||||
|
import { adminAuthLevel } from '../../../assets/data/types';
|
||||||
|
|
||||||
|
const Navi = () => {
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const userInfo = useRecoilValue(authList);
|
||||||
|
const menu = getMenuConfig(userInfo);
|
||||||
|
|
||||||
|
const [modalClose, setModalClose] = useState('hidden');
|
||||||
|
const [logoutModalClose, setLogoutModalClose] = useState('hidden');
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleToken = async () => {
|
||||||
|
const tokenStatus = await AuthInfo(token);
|
||||||
|
|
||||||
|
tokenStatus.message === '잘못된 타입의 토큰입니다.' && setLogoutModalClose('view');
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleToken();
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const handleTopMenu = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.target.classList.toggle('active');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLink = e => {
|
||||||
|
let topActive = document.querySelectorAll('nav .active');
|
||||||
|
let currentTopMenu = e.target.closest('ul').previousSibling;
|
||||||
|
for (let i = 0; i < topActive.length; i++) {
|
||||||
|
if (topActive[i] !== currentTopMenu) {
|
||||||
|
topActive[i].classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleToken();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 등록 완료 모달
|
||||||
|
const handleModalClose = () => {
|
||||||
|
if (modalClose === 'hidden') {
|
||||||
|
setModalClose('view');
|
||||||
|
} else {
|
||||||
|
setModalClose('hidden');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 로그아웃 안내 모달
|
||||||
|
const handleConfirmClose = () => {
|
||||||
|
if (logoutModalClose === 'hidden') {
|
||||||
|
setLogoutModalClose('view');
|
||||||
|
} else {
|
||||||
|
setLogoutModalClose('hidden');
|
||||||
|
sessionStorage.removeItem('token');
|
||||||
|
|
||||||
|
navigate('/');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isClickable = (submenu) => {
|
||||||
|
switch (userInfo.auth_level_type) {
|
||||||
|
case adminAuthLevel.DEVELOPER:
|
||||||
|
case adminAuthLevel.READER:
|
||||||
|
case adminAuthLevel.MASTER:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return submenu.authLevel === adminAuthLevel.NONE && userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === submenu.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
{menu.map((item, idx) => {
|
||||||
|
return (
|
||||||
|
<li key={idx}>
|
||||||
|
{item.access && (
|
||||||
|
<TopMenu to={item.link} onClick={handleTopMenu}>
|
||||||
|
{item.title}
|
||||||
|
</TopMenu>
|
||||||
|
)}
|
||||||
|
<SubMenu>
|
||||||
|
{item.submenu && userInfo &&
|
||||||
|
item.submenu.map((submenu, idx) => {
|
||||||
|
return (
|
||||||
|
<SubMenuItem key={idx} $isclickable={isClickable(submenu) ? 'true' : 'false'}>
|
||||||
|
<NavLink
|
||||||
|
to={isClickable(submenu) ? submenu.link : location.pathname}
|
||||||
|
onClick={e => {
|
||||||
|
isClickable(submenu) ? handleLink(e) : handleModalClose();
|
||||||
|
}}>
|
||||||
|
{submenu.title}
|
||||||
|
</NavLink>
|
||||||
|
</SubMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SubMenu>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{/* 접근 불가 모달 */}
|
||||||
|
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={modalClose}>
|
||||||
|
<BtnWrapper $justify="flex-end">
|
||||||
|
<ButtonClose onClick={handleModalClose} />
|
||||||
|
</BtnWrapper>
|
||||||
|
<ModalText $align="center">
|
||||||
|
해당 메뉴에 대한 조회 권한이 없습니다.
|
||||||
|
<br />
|
||||||
|
권한 등급을 변경 후 다시 이용해주세요.
|
||||||
|
</ModalText>
|
||||||
|
<BtnWrapper $gap="10px">
|
||||||
|
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleModalClose} />
|
||||||
|
</BtnWrapper>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{/* 로그아웃 안내 모달 */}
|
||||||
|
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={logoutModalClose}>
|
||||||
|
<BtnWrapper $justify="flex-end">
|
||||||
|
<ButtonClose onClick={handleConfirmClose} />
|
||||||
|
</BtnWrapper>
|
||||||
|
<ModalText $align="center">로그아웃 되었습니다.</ModalText>
|
||||||
|
<BtnWrapper $gap="10px">
|
||||||
|
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmClose} />
|
||||||
|
</BtnWrapper>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navi;
|
||||||
|
|
||||||
|
const TopMenu = styled(NavLink)`
|
||||||
|
padding: 16px 30px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #888;
|
||||||
|
position: relative;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
position: absolute;
|
||||||
|
right: 30px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
background: url('${arrowIcon}') -12px 0 no-repeat;
|
||||||
|
}
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
background: #444;
|
||||||
|
}
|
||||||
|
&.active ~ ul {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
&.active:before {
|
||||||
|
background: url('${arrowIcon}') 0 0 no-repeat;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SubMenu = styled.ul`
|
||||||
|
display: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SubMenuItem = styled.li`
|
||||||
|
background: #eee;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
color: #2c2c2c;
|
||||||
|
a {
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px 30px;
|
||||||
|
color: ${props => (props.$isclickable === 'false' ? '#818181' : '#2c2c2c')};
|
||||||
|
text-align: left;
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
color: ${props => (props.$isclickable === 'false' ? '#818181' : '#2c2c2c')};
|
||||||
|
font-weight: ${props => (props.$isclickable === 'false' ? 400 : 600)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const BackGround = styled.div`
|
||||||
|
background: #eee2;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 100;
|
||||||
|
`;
|
||||||
@@ -125,6 +125,7 @@ const DetailGrid = ({ items, formData, onChange, disabled = false, columns = 4 }
|
|||||||
value={currentValue}
|
value={currentValue}
|
||||||
onChange={(value) => onChange(key, value, handler)}
|
onChange={(value) => onChange(key, value, handler)}
|
||||||
placeholder={placeholder || `${label} 선택`}
|
placeholder={placeholder || `${label} 선택`}
|
||||||
|
popupMatchSelectWidth={false}
|
||||||
>
|
>
|
||||||
{options && options.map((option) => (
|
{options && options.map((option) => (
|
||||||
<Select.Option key={option.value} value={option.value}>
|
<Select.Option key={option.value} value={option.value}>
|
||||||
|
|||||||
55
src/components/common/Layout/DetailInfo.js
Normal file
55
src/components/common/Layout/DetailInfo.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Card, Descriptions } from 'antd';
|
||||||
|
import { getFieldLabel } from '../../../utils';
|
||||||
|
|
||||||
|
const InfoCard = ({
|
||||||
|
title,
|
||||||
|
data,
|
||||||
|
keyPrefix = 'item',
|
||||||
|
size = 'small',
|
||||||
|
column = 1,
|
||||||
|
bordered = true,
|
||||||
|
type = 'inner'
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
if (!data ||
|
||||||
|
typeof data !== 'object' ||
|
||||||
|
Object.keys(data).length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = Object.entries(data).map(([key, value]) => ({
|
||||||
|
key: `${keyPrefix}-${key}`,
|
||||||
|
label: getFieldLabel(key),
|
||||||
|
children: (() => {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.join(', ');
|
||||||
|
}
|
||||||
|
return JSON.stringify(value, null, 2);
|
||||||
|
}
|
||||||
|
return String(value);
|
||||||
|
})()
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
size={size}
|
||||||
|
title={title}
|
||||||
|
type={type}
|
||||||
|
style={{ marginBottom: 16 }}
|
||||||
|
>
|
||||||
|
<Descriptions
|
||||||
|
bordered={bordered}
|
||||||
|
column={column}
|
||||||
|
size={size}
|
||||||
|
items={items}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InfoCard;
|
||||||
@@ -4,5 +4,6 @@ import MainLayout from './MainLayout';
|
|||||||
import AnimatedPageWrapper from './AnimatedPageWrapper';
|
import AnimatedPageWrapper from './AnimatedPageWrapper';
|
||||||
import DetailGrid from './DetailGrid';
|
import DetailGrid from './DetailGrid';
|
||||||
import DetailLayout from './DetailLayout';
|
import DetailLayout from './DetailLayout';
|
||||||
|
import InfoCard from './DetailInfo'
|
||||||
|
|
||||||
export { Layout, LoginLayout, MainLayout, AnimatedPageWrapper, DetailGrid, DetailLayout };
|
export { Layout, LoginLayout, MainLayout, AnimatedPageWrapper, DetailGrid, DetailLayout, InfoCard };
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const SearchBarLayout = ({ firstColumnData, secondColumnData, filter, direction,
|
|||||||
</SearchRow>
|
</SearchRow>
|
||||||
)}
|
)}
|
||||||
{isSearch &&
|
{isSearch &&
|
||||||
<SearchRow>
|
<SearchRow direction={direction}>
|
||||||
<BtnWrapper $gap="8px">
|
<BtnWrapper $gap="8px">
|
||||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="button" />
|
<Button theme="search" text="검색" handleClick={handleSubmit} type="button" />
|
||||||
<Button theme="reset" handleClick={onReset} type="button" />
|
<Button theme="reset" handleClick={onReset} type="button" />
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const DotsButton = styled.button`
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
border: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 점 스타일링 */
|
|
||||||
.dot {
|
|
||||||
width: 3px;
|
|
||||||
height: 3px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #333;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const VerticalDotsButton = ({ text, type = 'button', errorMessage, handleClick, size, width, height, borderColor, disabled, name }) => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DotsButton
|
|
||||||
onSubmit={e => e.preventDefault()}
|
|
||||||
type={type}
|
|
||||||
disabled={disabled}
|
|
||||||
onClick={handleClick}
|
|
||||||
size={size}
|
|
||||||
bordercolor={borderColor}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
name={name}
|
|
||||||
>
|
|
||||||
<div className="dot"></div>
|
|
||||||
<div className="dot"></div>
|
|
||||||
<div className="dot"></div>
|
|
||||||
</DotsButton>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VerticalDotsButton;
|
|
||||||
@@ -1,43 +1,79 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Tabs } from 'antd';
|
import { Tabs } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled, { keyframes } from 'styled-components';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
|
||||||
// 통합된 애니메이션 탭 컴포넌트
|
// 통합된 애니메이션 탭 컴포넌트
|
||||||
const AnimatedTabs = ({ items, activeKey, onChange }) => {
|
const AnimatedTabs = ({ items, activeKey, onChange, tabPosition = 'center' }) => {
|
||||||
// 각 항목의 children을 애니메이션 래퍼로 감싸기
|
// 각 항목의 children을 애니메이션 래퍼로 감싸기
|
||||||
const tabItems = items.map(item => ({
|
const tabItems = items.map(item => ({
|
||||||
key: item.key,
|
key: item.key,
|
||||||
label: item.label,
|
label: item.label,
|
||||||
children: (
|
children: (
|
||||||
<AnimatePresence mode="wait">
|
<AnimatedContent key={`content-${item.key}`}>
|
||||||
<motion.div
|
{item.children}
|
||||||
key={activeKey}
|
</AnimatedContent>
|
||||||
initial={{ opacity: 0, x: 50 }}
|
|
||||||
animate={{ opacity: 1, x: 0 }}
|
|
||||||
exit={{ opacity: 0, x: -50 }}
|
|
||||||
transition={{
|
|
||||||
type: "spring",
|
|
||||||
stiffness: 300,
|
|
||||||
damping: 30
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.children}
|
|
||||||
</motion.div>
|
|
||||||
</AnimatePresence>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// children: (
|
||||||
|
// <AnimatePresence mode="wait">
|
||||||
|
// <motion.div
|
||||||
|
// key={activeKey}
|
||||||
|
// initial={{ opacity: 0, x: 50 }}
|
||||||
|
// animate={{ opacity: 1, x: 0 }}
|
||||||
|
// exit={{ opacity: 0, x: -50 }}
|
||||||
|
// transition={{
|
||||||
|
// type: "spring",
|
||||||
|
// stiffness: 300,
|
||||||
|
// damping: 30
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// {item.children}
|
||||||
|
// </motion.div>
|
||||||
|
// </AnimatePresence>
|
||||||
|
// )
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTabs
|
<StyledTabs
|
||||||
|
type="card"
|
||||||
activeKey={activeKey}
|
activeKey={activeKey}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
centered={true}
|
centered={tabPosition === 'center'}
|
||||||
items={tabItems}
|
items={tabItems}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const slideInRight = keyframes`
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const fadeIn = keyframes`
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const AnimatedContent = styled.div`
|
||||||
|
animation: ${slideInRight} 0.3s ease-out;
|
||||||
|
|
||||||
|
/* 대안으로 더 부드러운 페이드 인 효과 */
|
||||||
|
/* animation: ${fadeIn} 0.4s ease-out; */
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
// const AnimatedTabs = ({ items, activeKey, onChange }) => {
|
// const AnimatedTabs = ({ items, activeKey, onChange }) => {
|
||||||
// return (
|
// return (
|
||||||
// <StyledTabs
|
// <StyledTabs
|
||||||
@@ -76,16 +112,12 @@ const StyledTabs = styled(Tabs)`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
//align-items: center;
|
||||||
|
|
||||||
.ant-tabs-nav {
|
//.ant-tabs-nav {
|
||||||
margin-bottom: 16px;
|
// margin-bottom: 16px;
|
||||||
width: 80%;
|
// width: 80%;
|
||||||
}
|
//}
|
||||||
|
|
||||||
.ant-tabs-nav-wrap {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-tabs-tab {
|
.ant-tabs-tab {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
|
|||||||
@@ -1,231 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const AIMessageInput = ({ onSendMessage }) => {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [message, setMessage] = useState('');
|
|
||||||
const [isSending, setIsSending] = useState(false);
|
|
||||||
const textareaRef = useRef(null);
|
|
||||||
const modalRef = useRef(null);
|
|
||||||
|
|
||||||
// 텍스트 영역 높이 자동 조절
|
|
||||||
useEffect(() => {
|
|
||||||
if (textareaRef.current && isOpen) {
|
|
||||||
textareaRef.current.style.height = 'auto';
|
|
||||||
textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`;
|
|
||||||
}
|
|
||||||
}, [message, isOpen]);
|
|
||||||
|
|
||||||
// 모달 외부 클릭시 닫기
|
|
||||||
useEffect(() => {
|
|
||||||
const handleClickOutside = (event) => {
|
|
||||||
if (modalRef.current && !modalRef.current.contains(event.target)) {
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isOpen) {
|
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('mousedown', handleClickOutside);
|
|
||||||
};
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
// 모달 열기
|
|
||||||
const openModal = () => {
|
|
||||||
setIsOpen(true);
|
|
||||||
// 모달이 열린 후 텍스트 영역에 포커스
|
|
||||||
setTimeout(() => {
|
|
||||||
if (textareaRef.current) {
|
|
||||||
textareaRef.current.focus();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 모달 닫기
|
|
||||||
const closeModal = () => {
|
|
||||||
setIsOpen(false);
|
|
||||||
setMessage('');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 메시지 전송 처리
|
|
||||||
const handleSendMessage = () => {
|
|
||||||
if (message.trim() && !isSending) {
|
|
||||||
setIsSending(true);
|
|
||||||
|
|
||||||
// 메시지 전송 처리
|
|
||||||
if (onSendMessage) {
|
|
||||||
onSendMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 입력 초기화 및 상태 업데이트
|
|
||||||
setMessage('');
|
|
||||||
setIsSending(false);
|
|
||||||
|
|
||||||
// 모달 닫기
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 엔터 키 처리 (Shift+Enter 줄바꿈, Enter 전송)
|
|
||||||
const handleKeyDown = (e) => {
|
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
|
||||||
e.preventDefault();
|
|
||||||
handleSendMessage();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* 메뉴 버튼 */}
|
|
||||||
<MenuButton onClick={openModal}>
|
|
||||||
<div className="dot"></div>
|
|
||||||
<div className="dot"></div>
|
|
||||||
<div className="dot"></div>
|
|
||||||
</MenuButton>
|
|
||||||
|
|
||||||
{/* 모달 오버레이 */}
|
|
||||||
<ModalOverlay isOpen={isOpen}>
|
|
||||||
<InputContainer ref={modalRef} isOpen={isOpen}>
|
|
||||||
<MessageInput
|
|
||||||
ref={textareaRef}
|
|
||||||
value={message}
|
|
||||||
onChange={(e) => setMessage(e.target.value)}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
placeholder="메시지를 입력하세요..."
|
|
||||||
rows={1}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SendButton
|
|
||||||
onClick={handleSendMessage}
|
|
||||||
disabled={!message.trim() || isSending}
|
|
||||||
>
|
|
||||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
|
|
||||||
</svg>
|
|
||||||
</SendButton>
|
|
||||||
</InputContainer>
|
|
||||||
</ModalOverlay>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AIMessageInput;
|
|
||||||
|
|
||||||
const ModalOverlay = styled.div`
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: ${props => props.isOpen ? 'flex' : 'none'};
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 1000;
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 메인 컨테이너
|
|
||||||
const InputContainer = styled.div`
|
|
||||||
width: 90%;
|
|
||||||
max-width: 600px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 12px;
|
|
||||||
background-color: #ffffff;
|
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
animation: ${props => props.isOpen ? 'slideUp 0.3s ease-out' : 'none'};
|
|
||||||
|
|
||||||
@keyframes slideUp {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 메시지 입력 영역
|
|
||||||
const MessageInput = styled.textarea`
|
|
||||||
width: 100%;
|
|
||||||
min-height: 60px;
|
|
||||||
max-height: 200px;
|
|
||||||
padding: 16px 60px 16px 16px;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
resize: none;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.5;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
&::placeholder {
|
|
||||||
color: #9e9ea7;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 전송 버튼
|
|
||||||
const SendButton = styled.button`
|
|
||||||
position: absolute;
|
|
||||||
bottom: 12px;
|
|
||||||
right: 12px;
|
|
||||||
width: 38px;
|
|
||||||
height: 38px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #5436DA;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #4527D0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
background-color: #DADCE0;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 메뉴 버튼 (세로 점 세개)
|
|
||||||
const MenuButton = styled.button`
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
border: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dot {
|
|
||||||
width: 4px;
|
|
||||||
height: 4px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #666;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
311
src/components/modal/EventModal.js
Normal file
311
src/components/modal/EventModal.js
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
import React, { useState, Fragment, useEffect, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Button from '../common/button/Button';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
BtnWrapper,
|
||||||
|
SearchBarAlert,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FormStatusBar,
|
||||||
|
FormStatusLabel,
|
||||||
|
FormStatusWarning,
|
||||||
|
FormButtonContainer,
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import { DetailLayout, Modal} from '../common';
|
||||||
|
import { TYPE_MODIFY, TYPE_REGISTRY } from '../../assets/data/adminConstants';
|
||||||
|
import { convertKTCDate } from '../../utils';
|
||||||
|
import {
|
||||||
|
opCommonStatus,
|
||||||
|
} from '../../assets/data/options';
|
||||||
|
import { alertTypes, commonStatus } from '../../assets/data/types';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { EventModify, EventSingleRegist } from '../../apis';
|
||||||
|
|
||||||
|
const EventModal = ({ modalType, detailView, handleDetailView, content, setDetailData, eventActionData }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const { showToast, showModal } = useAlert();
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
|
||||||
|
const [isNullValue, setIsNullValue] = useState(false);
|
||||||
|
const [resultData, setResultData] = useState(initData);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(modalType === TYPE_MODIFY && content && Object.keys(content).length > 0){
|
||||||
|
setResultData({
|
||||||
|
id: content.id,
|
||||||
|
title: content.title,
|
||||||
|
global_event_action_id: content.global_event_action_id,
|
||||||
|
personal_event_action_id: content.personal_event_action_id,
|
||||||
|
status: content.status,
|
||||||
|
max_point: content.max_point,
|
||||||
|
start_dt: convertKTCDate(content.start_dt),
|
||||||
|
end_dt: convertKTCDate(content.end_dt)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [modalType, content]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (checkCondition()) {
|
||||||
|
setIsNullValue(false);
|
||||||
|
} else {
|
||||||
|
setIsNullValue(true);
|
||||||
|
}
|
||||||
|
}, [resultData]);
|
||||||
|
|
||||||
|
const opEventActionMode = useMemo(() => {
|
||||||
|
return eventActionData?.map(item => ({
|
||||||
|
value: item.id,
|
||||||
|
name: `${item.description}(${item.id})`
|
||||||
|
})) || [];
|
||||||
|
}, [eventActionData]);
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setDetailData({});
|
||||||
|
setResultData(initData);
|
||||||
|
handleDetailView();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
if (!checkCondition()) return;
|
||||||
|
|
||||||
|
const minAllowedTime = new Date(new Date().getTime() + 10 * 60000);
|
||||||
|
const startDt = resultData.start_dt;
|
||||||
|
const endDt = resultData.end_dt;
|
||||||
|
// if (modalType === TYPE_REGISTRY && startDt < minAllowedTime) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if(resultData.repeat_type !== 'NONE' && !isValidDayRange(startDt, endDt)) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //화면에 머물면서 상태는 안바꼈을 경우가 있기에 시작시간 지났을경우 차단
|
||||||
|
// if (modalType === TYPE_REGISTRY && startDt < new Date()) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
showModal(isView('modify') ? 'BATTLE_EVENT_UPDATE_CONFIRM' : 'BATTLE_EVENT_REGIST_CONFIRM', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleSubmit('registConfirm')
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "registConfirm":
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
...resultData
|
||||||
|
};
|
||||||
|
|
||||||
|
if(isView('modify')){
|
||||||
|
await withLoading( async () => {
|
||||||
|
return await EventModify(token, content?.id, params);
|
||||||
|
}).then(data => {
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('UPDATE_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
handleReset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
await withLoading( async () => {
|
||||||
|
return await EventSingleRegist(token, params);
|
||||||
|
}).then(data => {
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('REGIST_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
handleReset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCondition = () => {
|
||||||
|
return (
|
||||||
|
resultData.start_dt !== ''
|
||||||
|
&& resultData.end_dt !== ''
|
||||||
|
&& resultData.title !== ''
|
||||||
|
&& resultData.global_event_action_id > 0
|
||||||
|
&& resultData.personal_event_action_id > 0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isView = (label) => {
|
||||||
|
switch (label) {
|
||||||
|
case "modify":
|
||||||
|
return modalType === TYPE_MODIFY && (content?.status === commonStatus.wait);
|
||||||
|
case "registry":
|
||||||
|
case "mode":
|
||||||
|
return modalType === TYPE_REGISTRY
|
||||||
|
case "start_dt":
|
||||||
|
case "end_dt":
|
||||||
|
case "max_point":
|
||||||
|
case "name":
|
||||||
|
return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY &&(content?.status === commonStatus.wait));
|
||||||
|
default:
|
||||||
|
return modalType === TYPE_MODIFY && (content?.status !== commonStatus.wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemGroups = [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
row: 0,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'text',
|
||||||
|
key: 'title',
|
||||||
|
label: '이벤트명',
|
||||||
|
disabled: !isView('name'),
|
||||||
|
width: '300px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 1,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'date',
|
||||||
|
key: 'start_dt',
|
||||||
|
label: '시작일시',
|
||||||
|
disabled: !isView('start_dt'),
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
width: '200px',
|
||||||
|
showTime: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 1,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'date',
|
||||||
|
key: 'end_dt',
|
||||||
|
label: '종료일시',
|
||||||
|
disabled: !isView('end_dt'),
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
width: '200px',
|
||||||
|
showTime: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 4,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'select',
|
||||||
|
key: 'global_event_action_id',
|
||||||
|
label: '기여도 이벤트 모드',
|
||||||
|
disabled: !isView('mode'),
|
||||||
|
width: '150px',
|
||||||
|
options: opEventActionMode
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 4,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'number',
|
||||||
|
key: 'max_point',
|
||||||
|
label: '기여도 목표점수',
|
||||||
|
disabled: !isView('max_point'),
|
||||||
|
width: '150px'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 5,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'select',
|
||||||
|
key: 'personal_event_action_id',
|
||||||
|
label: '개인제작 이벤트 모드',
|
||||||
|
disabled: !isView('mode'),
|
||||||
|
width: '150px',
|
||||||
|
options: opEventActionMode
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal min="760px" $view={detailView}>
|
||||||
|
<Title $align="center">{isView('registry') ? "통합 이벤트 등록" : isView('modify') ? "통합 이벤트 수정" : "통합 이벤트 상세"}</Title>
|
||||||
|
<DetailLayout
|
||||||
|
itemGroups={itemGroups}
|
||||||
|
formData={resultData}
|
||||||
|
onChange={setResultData}
|
||||||
|
disabled={false}
|
||||||
|
columnCount={4}
|
||||||
|
/>
|
||||||
|
{!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>}
|
||||||
|
<BtnWrapper $gap="10px" $marginTop="10px">
|
||||||
|
<FormStatusBar>
|
||||||
|
<FormStatusLabel>
|
||||||
|
현재상태: {opCommonStatus.find(data => data.value === content?.status)?.name || "등록"}
|
||||||
|
</FormStatusLabel>
|
||||||
|
<FormStatusWarning>
|
||||||
|
{isView('registry') ? '' : t('EVENT_MODAL_STATUS_WARNING')}
|
||||||
|
</FormStatusWarning>
|
||||||
|
</FormStatusBar>
|
||||||
|
<FormButtonContainer $gap="5px">
|
||||||
|
{isView() ?
|
||||||
|
<Button
|
||||||
|
text="확인"
|
||||||
|
name="확인버튼"
|
||||||
|
theme="line"
|
||||||
|
handleClick={() => handleReset()}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
text="취소"
|
||||||
|
theme="line"
|
||||||
|
handleClick={() => showModal('CANCEL_CONFIRM', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleReset()
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text={isView('modify') ? "수정" : "등록"}
|
||||||
|
name="등록버튼"
|
||||||
|
theme={
|
||||||
|
checkCondition()
|
||||||
|
? 'primary'
|
||||||
|
: 'disable'
|
||||||
|
}
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</FormButtonContainer>
|
||||||
|
</BtnWrapper>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initData = {
|
||||||
|
title: '',
|
||||||
|
start_dt: '',
|
||||||
|
end_dt: '',
|
||||||
|
global_event_action_id: '',
|
||||||
|
personal_event_action_id: '',
|
||||||
|
max_point: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EventModal;
|
||||||
|
|
||||||
379
src/components/modal/RankingModal.js
Normal file
379
src/components/modal/RankingModal.js
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
import React, { useState, Fragment, useEffect, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Button from '../common/button/Button';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
BtnWrapper,
|
||||||
|
SearchBarAlert, SelectInput,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FormInput,
|
||||||
|
FormLabel,
|
||||||
|
MessageWrapper,
|
||||||
|
FormRowGroup,
|
||||||
|
FormStatusBar,
|
||||||
|
FormStatusLabel,
|
||||||
|
FormStatusWarning,
|
||||||
|
FormButtonContainer,
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import { DetailLayout, Modal, SingleDatePicker, SingleTimePicker } from '../common';
|
||||||
|
import { NONE, TYPE_MODIFY, TYPE_REGISTRY } from '../../assets/data/adminConstants';
|
||||||
|
import { convertKTCDate } from '../../utils';
|
||||||
|
import {
|
||||||
|
battleEventHotTime,
|
||||||
|
battleEventRoundCount,
|
||||||
|
battleEventStatus,
|
||||||
|
battleRepeatType, opCommonStatus,
|
||||||
|
} from '../../assets/data/options';
|
||||||
|
import { BattleEventModify, BattleEventSingleRegist } from '../../apis/Battle';
|
||||||
|
import { alertTypes, battleEventStatusType, commonStatus } from '../../assets/data/types';
|
||||||
|
import { isValidDayRange } from '../../utils/date';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { RankingScheduleModify, RankingScheduleSingleRegist } from '../../apis';
|
||||||
|
|
||||||
|
const RankingModal = ({ modalType, detailView, handleDetailView, content, setDetailData, rankingData, eventActionData }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const { showToast, showModal } = useAlert();
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
|
||||||
|
const [isNullValue, setIsNullValue] = useState(false);
|
||||||
|
const [resultData, setResultData] = useState(initData);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(modalType === TYPE_MODIFY && content && Object.keys(content).length > 0){
|
||||||
|
setResultData({
|
||||||
|
guid: content.guid,
|
||||||
|
id: content.id,
|
||||||
|
title: content.title,
|
||||||
|
meta_id: content.meta_id,
|
||||||
|
event_action_id: content.event_action_id,
|
||||||
|
refresh_interval: content.refresh_interval,
|
||||||
|
initialization_interval: content.initialization_interval,
|
||||||
|
snapshot_interval: content.snapshot_interval,
|
||||||
|
status: content.status,
|
||||||
|
start_dt: convertKTCDate(content.start_dt),
|
||||||
|
end_dt: convertKTCDate(content.end_dt),
|
||||||
|
base_dt: convertKTCDate(content.base_dt),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [modalType, content]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (checkCondition()) {
|
||||||
|
setIsNullValue(false);
|
||||||
|
} else {
|
||||||
|
setIsNullValue(true);
|
||||||
|
}
|
||||||
|
}, [resultData]);
|
||||||
|
|
||||||
|
const opRankingMode = useMemo(() => {
|
||||||
|
return rankingData?.map(item => ({
|
||||||
|
value: item.id,
|
||||||
|
name: `${item.desc}(${item.id})`
|
||||||
|
})) || [];
|
||||||
|
}, [rankingData]);
|
||||||
|
|
||||||
|
const opEventActionMode = useMemo(() => {
|
||||||
|
return eventActionData?.map(item => ({
|
||||||
|
value: item.id,
|
||||||
|
name: `${item.description}(${item.id})`
|
||||||
|
})) || [];
|
||||||
|
}, [eventActionData]);
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setDetailData({});
|
||||||
|
setResultData(initData);
|
||||||
|
handleDetailView();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
if (!checkCondition()) return;
|
||||||
|
|
||||||
|
// const minAllowedTime = new Date(new Date().getTime() + 10 * 60000);
|
||||||
|
// const startDt = resultData.event_start_dt;
|
||||||
|
// const endDt = resultData.event_end_dt;
|
||||||
|
// if (modalType === TYPE_REGISTRY && startDt < minAllowedTime) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if(resultData.repeat_type !== 'NONE' && !isValidDayRange(startDt, endDt)) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //화면에 머물면서 상태는 안바꼈을 경우가 있기에 시작시간 지났을경우 차단
|
||||||
|
// if (modalType === TYPE_REGISTRY && startDt < new Date()) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
showModal(isView('modify') ? 'SCHEDULE_UPDATE_CONFIRM' : 'SCHEDULE_REGIST_CONFIRM', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleSubmit('registConfirm')
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "registConfirm":
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
...resultData
|
||||||
|
};
|
||||||
|
|
||||||
|
if(isView('modify')){
|
||||||
|
await withLoading( async () => {
|
||||||
|
return await RankingScheduleModify(token, content?.id, params);
|
||||||
|
}).then(data => {
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('UPDATE_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
handleReset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
await withLoading( async () => {
|
||||||
|
return await RankingScheduleSingleRegist(token, params);
|
||||||
|
}).then(data => {
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('REGIST_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
handleReset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCondition = () => {
|
||||||
|
return (
|
||||||
|
resultData.start_dt !== ''
|
||||||
|
&& resultData.end_dt !== ''
|
||||||
|
&& resultData.base_dt !== ''
|
||||||
|
&& resultData.meta_id > 0
|
||||||
|
&& resultData.event_action_id > 0
|
||||||
|
&& resultData.title !== ''
|
||||||
|
&& resultData.refresh_interval > 0
|
||||||
|
&& resultData.initialization_interval > 0
|
||||||
|
&& resultData.snapshot_interval > 0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isView = (label) => {
|
||||||
|
switch (label) {
|
||||||
|
case "modify":
|
||||||
|
return modalType === TYPE_MODIFY && (content?.status === commonStatus.wait);
|
||||||
|
case "registry":
|
||||||
|
return modalType === TYPE_REGISTRY
|
||||||
|
case "start_dt":
|
||||||
|
case "end_dt":
|
||||||
|
case "base_dt":
|
||||||
|
case "name":
|
||||||
|
case "refresh_interval":
|
||||||
|
case "init_interval":
|
||||||
|
case "snapshot_interval":
|
||||||
|
case "mode":
|
||||||
|
case "eventActionMode":
|
||||||
|
return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY &&(content?.status === commonStatus.wait));
|
||||||
|
default:
|
||||||
|
return modalType === TYPE_MODIFY && (content?.status !== commonStatus.wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemGroups = [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
row: 0,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'text',
|
||||||
|
key: 'title',
|
||||||
|
label: '스케줄러명',
|
||||||
|
disabled: !isView('name'),
|
||||||
|
width: '300px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 1,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'date',
|
||||||
|
key: 'start_dt',
|
||||||
|
label: '시작일시',
|
||||||
|
disabled: !isView('start_dt'),
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
width: '200px',
|
||||||
|
showTime: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 1,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'date',
|
||||||
|
key: 'end_dt',
|
||||||
|
label: '종료일시',
|
||||||
|
disabled: !isView('end_dt'),
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
width: '200px',
|
||||||
|
showTime: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 2,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'date',
|
||||||
|
key: 'base_dt',
|
||||||
|
label: '기준일시',
|
||||||
|
disabled: !isView('base_dt'),
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
width: '200px',
|
||||||
|
showTime: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 2,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'number',
|
||||||
|
key: 'refresh_interval',
|
||||||
|
label: '새로고침 주기(분)',
|
||||||
|
disabled: !isView('refresh_interval'),
|
||||||
|
width: '100px',
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 3,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'number',
|
||||||
|
key: 'initialization_interval',
|
||||||
|
label: '초기화 주기(분)',
|
||||||
|
disabled: !isView('init_interval'),
|
||||||
|
width: '100px',
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 3,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'number',
|
||||||
|
key: 'snapshot_interval',
|
||||||
|
label: '스냅샷 주기(분)',
|
||||||
|
disabled: !isView('snapshot_interval'),
|
||||||
|
width: '100px',
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
row: 4,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'select',
|
||||||
|
key: 'meta_id',
|
||||||
|
label: '랭킹 모드',
|
||||||
|
disabled: !isView('mode'),
|
||||||
|
width: '150px',
|
||||||
|
options: opRankingMode
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 4,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'select',
|
||||||
|
key: 'event_action_id',
|
||||||
|
label: '이벤트 액션 그룹',
|
||||||
|
disabled: !isView('eventActionMode'),
|
||||||
|
width: '150px',
|
||||||
|
options: opEventActionMode
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal min="760px" $view={detailView}>
|
||||||
|
<Title $align="center">{isView('registry') ? "랭킹 스케줄러 등록" : isView('modify') ? "랭킹 스케줄러 수정" : "랭킹 스케줄러 상세"}</Title>
|
||||||
|
<DetailLayout
|
||||||
|
itemGroups={itemGroups}
|
||||||
|
formData={resultData}
|
||||||
|
onChange={setResultData}
|
||||||
|
disabled={false}
|
||||||
|
columnCount={4}
|
||||||
|
/>
|
||||||
|
{!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>}
|
||||||
|
<BtnWrapper $gap="10px" $marginTop="10px">
|
||||||
|
<FormStatusBar>
|
||||||
|
<FormStatusLabel>
|
||||||
|
현재상태: {opCommonStatus.find(data => data.value === content?.status)?.name || "등록"}
|
||||||
|
</FormStatusLabel>
|
||||||
|
<FormStatusWarning>
|
||||||
|
{isView('registry') ? '' : t('SCHEDULE_MODAL_STATUS_WARNING')}
|
||||||
|
</FormStatusWarning>
|
||||||
|
</FormStatusBar>
|
||||||
|
<FormButtonContainer $gap="5px">
|
||||||
|
{isView() ?
|
||||||
|
<Button
|
||||||
|
text="확인"
|
||||||
|
name="확인버튼"
|
||||||
|
theme="line"
|
||||||
|
handleClick={() => handleReset()}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
text="취소"
|
||||||
|
theme="line"
|
||||||
|
handleClick={() => showModal('CANCEL_CONFIRM', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleReset()
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text={isView('modify') ? "수정" : "등록"}
|
||||||
|
name="등록버튼"
|
||||||
|
theme={
|
||||||
|
checkCondition()
|
||||||
|
? 'primary'
|
||||||
|
: 'disable'
|
||||||
|
}
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</FormButtonContainer>
|
||||||
|
</BtnWrapper>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initData = {
|
||||||
|
guid: '',
|
||||||
|
title: '',
|
||||||
|
start_dt: '',
|
||||||
|
end_dt: '',
|
||||||
|
base_dt: '',
|
||||||
|
refresh_interval: 60,
|
||||||
|
initialization_interval: 0,
|
||||||
|
snapshot_interval: 1440,
|
||||||
|
meta_id: '',
|
||||||
|
event_action_id: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RankingModal;
|
||||||
|
|
||||||
540
src/components/modal/RewardEventDetailModal.js
Normal file
540
src/components/modal/RewardEventDetailModal.js
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
import { useState, useEffect, Fragment } from 'react';
|
||||||
|
|
||||||
|
import { Input, Button as AntButton, Select, Alert, Space, Card, Row, Col } from 'antd';
|
||||||
|
import { Title, BtnWrapper } from '../../styles/Components';
|
||||||
|
import Button from '../common/button/Button';
|
||||||
|
import Modal from '../common/modal/Modal';
|
||||||
|
import { EventIsItem, RewardEventModify } from '../../apis';
|
||||||
|
|
||||||
|
import { authList } from '../../store/authList';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { authType, benItems, commonStatus, currencyItemCode } from '../../assets/data';
|
||||||
|
import {
|
||||||
|
DetailRegistInfo, DetailState
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import { convertKTC, timeDiffMinute, convertKTCDate } from '../../utils';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import { DetailLayout } from '../common';
|
||||||
|
|
||||||
|
const RewardEventDetailModal = ({ detailView, handleDetailView, content, setDetailData }) => {
|
||||||
|
const userInfo = useRecoilValue(authList);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
const {showModal, showToast} = useAlert();
|
||||||
|
|
||||||
|
const id = content && content.id;
|
||||||
|
const updateAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate);
|
||||||
|
|
||||||
|
const [activeLanguage, setActiveLanguage] = useState('KO');
|
||||||
|
const [item, setItem] = useState('');
|
||||||
|
const [itemCount, setItemCount] = useState(1);
|
||||||
|
const [resource, setResource] = useState('19010001');
|
||||||
|
const [resourceCount, setResourceCount] = useState(1);
|
||||||
|
|
||||||
|
const [resultData, setResultData] = useState({});
|
||||||
|
|
||||||
|
// 과거 판단
|
||||||
|
const [isPast, setIsPast] = useState(false);
|
||||||
|
const [isChanged, setIsChanged] = useState(false);
|
||||||
|
|
||||||
|
const [btnValidation, setBtnValidation] = useState(false);
|
||||||
|
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||||
|
const [itemCheckMsg, setItemCheckMsg] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(content){
|
||||||
|
const start_dt_KTC = convertKTCDate(content.start_dt)
|
||||||
|
const end_dt_KTC = convertKTCDate(content.end_dt)
|
||||||
|
|
||||||
|
setResultData({
|
||||||
|
start_dt: start_dt_KTC,
|
||||||
|
end_dt: end_dt_KTC,
|
||||||
|
event_type: content.event_type,
|
||||||
|
mail_list: content.mail_list,
|
||||||
|
item_list: content.item_list,
|
||||||
|
status: content.status,
|
||||||
|
delete_desc: content.delete_desc
|
||||||
|
});
|
||||||
|
|
||||||
|
start_dt_KTC < (new Date) ? setIsPast(true) : setIsPast(false);
|
||||||
|
content.mail_list.length === 1 && setBtnValidation(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem('');
|
||||||
|
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(!updateAuth || isPast){
|
||||||
|
setIsReadOnly(true);
|
||||||
|
}else{
|
||||||
|
setIsReadOnly(false);
|
||||||
|
}
|
||||||
|
}, [updateAuth, isPast]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setItemCheckMsg('');
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
|
const getLanguageTabItems = () => {
|
||||||
|
return resultData.mail_list?.map(mail => ({
|
||||||
|
key: mail.language,
|
||||||
|
label: mail.language,
|
||||||
|
children: (
|
||||||
|
<div style={{ padding: '10px', minHeight: '400px', height: 'auto' }}>
|
||||||
|
<Row gutter={[16, 24]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<div>
|
||||||
|
<label style={{
|
||||||
|
display: 'block',
|
||||||
|
marginBottom: '8px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'rgba(0, 0, 0, 0.85)',
|
||||||
|
fontSize: '14px'
|
||||||
|
}}>
|
||||||
|
제목 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
value={mail.title || ''}
|
||||||
|
placeholder="우편 제목을 입력하세요"
|
||||||
|
maxLength={30}
|
||||||
|
readOnly={isReadOnly}
|
||||||
|
onChange={(e) => updateMailData(mail.language, 'title', e.target.value.trimStart())}
|
||||||
|
showCount
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<div>
|
||||||
|
<label style={{
|
||||||
|
display: 'block',
|
||||||
|
marginBottom: '8px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'rgba(0, 0, 0, 0.85)',
|
||||||
|
fontSize: '14px'
|
||||||
|
}}>
|
||||||
|
내용 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||||
|
</label>
|
||||||
|
<Input.TextArea
|
||||||
|
value={mail.content || ''}
|
||||||
|
placeholder="우편 내용을 입력하세요"
|
||||||
|
readOnly={isReadOnly}
|
||||||
|
rows={8}
|
||||||
|
maxLength={2000}
|
||||||
|
showCount
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.value.length > 2000) return;
|
||||||
|
updateMailData(mail.language, 'content', e.target.value.trimStart());
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
resize: 'vertical',
|
||||||
|
minHeight: '200px',
|
||||||
|
maxHeight: '400px'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
closable: resultData.mail_list?.length > 1 && !isReadOnly, // 마지막 하나가 아니고 읽기전용이 아닐 때만 삭제 가능
|
||||||
|
})) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateMailData = (language, field, value) => {
|
||||||
|
const updatedMailList = resultData.mail_list.map(mail =>
|
||||||
|
mail.language === language
|
||||||
|
? { ...mail, [field]: value }
|
||||||
|
: mail
|
||||||
|
);
|
||||||
|
setResultData({ ...resultData, mail_list: updatedMailList });
|
||||||
|
setIsChanged(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTabClose = (targetKey) => {
|
||||||
|
if (resultData.mail_list.length <= 1) return;
|
||||||
|
|
||||||
|
const filterList = resultData.mail_list.filter(el => el.language !== targetKey);
|
||||||
|
setResultData({ ...resultData, mail_list: filterList });
|
||||||
|
|
||||||
|
// 삭제된 탭이 현재 활성 탭이었다면 첫 번째 탭으로 변경
|
||||||
|
if (activeLanguage === targetKey) {
|
||||||
|
setActiveLanguage(filterList[0]?.language || 'KO');
|
||||||
|
}
|
||||||
|
setIsChanged(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 추가
|
||||||
|
const handleItemList = async () => {
|
||||||
|
if(benItems.includes(item)){
|
||||||
|
showToast('MAIL_ITEM_ADD_BEN', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(item.length === 0 || itemCount.length === 0) return;
|
||||||
|
|
||||||
|
const result = await EventIsItem(token, {item: item});
|
||||||
|
|
||||||
|
if(result.data.result === "ERROR"){
|
||||||
|
setItemCheckMsg(t('NOT_ITEM'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemIndex = resultData.item_list.findIndex((data) => data.item === item);
|
||||||
|
if (itemIndex !== -1) {
|
||||||
|
setItemCheckMsg(t('MAIL_ITEM_ADD_DUPL'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newItem = { item: item, item_cnt: itemCount, item_name: result.data.data.item_info.item_name };
|
||||||
|
|
||||||
|
resultData.item_list.push(newItem);
|
||||||
|
|
||||||
|
setIsChanged(true);
|
||||||
|
setItem('');
|
||||||
|
setItemCount('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 삭제
|
||||||
|
const onItemRemove = id => {
|
||||||
|
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
|
||||||
|
setIsChanged(true);
|
||||||
|
|
||||||
|
setResultData({ ...resultData, item_list: filterList });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자원 추가
|
||||||
|
const handleResourceList = (e) => {
|
||||||
|
if(resource.length === 0 || resourceCount.length === 0) return;
|
||||||
|
|
||||||
|
const itemIndex = resultData.item_list.findIndex(
|
||||||
|
(item) => item.item === resource
|
||||||
|
);
|
||||||
|
|
||||||
|
if (itemIndex !== -1) {
|
||||||
|
const item_cnt = resultData.item_list[itemIndex].item_cnt;
|
||||||
|
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
|
||||||
|
} else {
|
||||||
|
const name = currencyItemCode.find(well => well.value === resource).name;
|
||||||
|
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
|
||||||
|
resultData.item_list.push(newItem);
|
||||||
|
}
|
||||||
|
setIsChanged(true);
|
||||||
|
setResource('')
|
||||||
|
setResourceCount('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 확인 버튼 후 다 초기화
|
||||||
|
const handleReset = () => {
|
||||||
|
setBtnValidation(false);
|
||||||
|
setIsChanged(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const conditionCheck = () => {
|
||||||
|
return (
|
||||||
|
content && content.mail_list.every(data => data.content !== '' && data.title !== '') &&
|
||||||
|
isChanged
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
if (!conditionCheck()) return;
|
||||||
|
|
||||||
|
showModal('MAIL_UPDATE_SAVE', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleSubmit('updateConfirm')
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "updateConfirm":
|
||||||
|
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date))
|
||||||
|
// 이벤트 시작 30분전이나 이미 SystemMail이 add된 상태에서는 수정할 수 없다.
|
||||||
|
if(content.add_flag || timeDiff <= 30){
|
||||||
|
showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
withLoading( async () => {
|
||||||
|
return await RewardEventModify(token, id, resultData);
|
||||||
|
}).catch(error => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
|
||||||
|
handleDetailView();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const detailState = (status) => {
|
||||||
|
switch (status) {
|
||||||
|
case commonStatus.wait:
|
||||||
|
return <DetailState>대기</DetailState>;
|
||||||
|
case commonStatus.running:
|
||||||
|
return <DetailState>진행중</DetailState>;
|
||||||
|
case commonStatus.finish:
|
||||||
|
return <DetailState result={commonStatus.finish}>완료</DetailState>;
|
||||||
|
case commonStatus.fail:
|
||||||
|
return <DetailState result={commonStatus.fail}>실패</DetailState>;
|
||||||
|
case commonStatus.delete:
|
||||||
|
return <DetailState result={commonStatus.delete}>삭제</DetailState>;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 목록 렌더링 컴포넌트
|
||||||
|
const renderItemList = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{resultData.item_list && resultData.item_list.length > 0 && (
|
||||||
|
<Space wrap>
|
||||||
|
{resultData.item_list.map((data, index) => (
|
||||||
|
<Card
|
||||||
|
key={index}
|
||||||
|
title={data.item_name}
|
||||||
|
size="small"
|
||||||
|
extra={
|
||||||
|
!isReadOnly && (
|
||||||
|
<AntButton
|
||||||
|
type="text"
|
||||||
|
danger
|
||||||
|
size="small"
|
||||||
|
onClick={() => onItemRemove(index)}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</AntButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
style={{ minWidth: '150px' }}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div>{data.item}</div>
|
||||||
|
<div>수량: {data.item_cnt}</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 추가 컴포넌트
|
||||||
|
const renderItemAdd = () => {
|
||||||
|
return (
|
||||||
|
<Space.Compact style={{ width: '100%' }}>
|
||||||
|
<Input
|
||||||
|
placeholder="Item Meta id 입력"
|
||||||
|
value={item}
|
||||||
|
onChange={(e) => setItem(e.target.value.trimStart())}
|
||||||
|
disabled={isReadOnly}
|
||||||
|
style={{ width: '200px' }}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="수량"
|
||||||
|
value={itemCount}
|
||||||
|
onChange={(e) => setItemCount(e.target.value)}
|
||||||
|
disabled={isReadOnly}
|
||||||
|
style={{ width: '120px' }}
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
<AntButton
|
||||||
|
type="primary"
|
||||||
|
onClick={handleItemList}
|
||||||
|
disabled={itemCount.length === 0 || item.length === 0 || isReadOnly}
|
||||||
|
>
|
||||||
|
추가
|
||||||
|
</AntButton>
|
||||||
|
</Space.Compact>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자원 추가 컴포넌트
|
||||||
|
const renderResourceAdd = () => {
|
||||||
|
return (
|
||||||
|
<Space.Compact style={{ width: '100%' }}>
|
||||||
|
<Select
|
||||||
|
value={resource}
|
||||||
|
onChange={setResource}
|
||||||
|
disabled={isReadOnly}
|
||||||
|
style={{ width: '200px' }}
|
||||||
|
placeholder="자원 선택"
|
||||||
|
>
|
||||||
|
{currencyItemCode.map((data, index) => (
|
||||||
|
<Select.Option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="수량"
|
||||||
|
value={resourceCount}
|
||||||
|
disabled={isReadOnly}
|
||||||
|
onChange={(e) => setResourceCount(e.target.value)}
|
||||||
|
style={{ width: '120px' }}
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
<AntButton
|
||||||
|
type="primary"
|
||||||
|
onClick={handleResourceList}
|
||||||
|
disabled={resourceCount.length === 0 || resource.length === 0 || isReadOnly}
|
||||||
|
>
|
||||||
|
추가
|
||||||
|
</AntButton>
|
||||||
|
</Space.Compact>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemGroups = [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
row: 0,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'dateRange',
|
||||||
|
keys: {
|
||||||
|
start: 'start_dt',
|
||||||
|
end: 'end_dt'
|
||||||
|
},
|
||||||
|
label: '이벤트 기간',
|
||||||
|
disabled: isReadOnly,
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
showTime: true,
|
||||||
|
startLabel: '시작 일시',
|
||||||
|
endLabel: '종료 일시'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 0,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 1,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'status',
|
||||||
|
label: '이벤트 상태',
|
||||||
|
render: () => detailState(resultData.status)
|
||||||
|
},
|
||||||
|
...(resultData.status === commonStatus.delete ? [{
|
||||||
|
row: 0,
|
||||||
|
col: 3,
|
||||||
|
colSpan: 1,
|
||||||
|
type: 'display',
|
||||||
|
key: 'delete_desc',
|
||||||
|
label: '삭제 사유',
|
||||||
|
value: resultData.delete_desc || ''
|
||||||
|
}] : [{
|
||||||
|
row: 0,
|
||||||
|
col: 3,
|
||||||
|
colSpan: 1,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'empty_space',
|
||||||
|
label: '',
|
||||||
|
render: () => <div></div>
|
||||||
|
}]),
|
||||||
|
{
|
||||||
|
row: 1,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 4,
|
||||||
|
type: 'tab',
|
||||||
|
key: 'language_tabs',
|
||||||
|
tabItems: getLanguageTabItems(),
|
||||||
|
activeKey: activeLanguage,
|
||||||
|
onTabChange: setActiveLanguage,
|
||||||
|
onTabClose: handleTabClose
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 2,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 4,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'item_add',
|
||||||
|
label: '아이템 추가',
|
||||||
|
render: renderItemAdd
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 3,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 4,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'resource_add',
|
||||||
|
label: '자원 추가',
|
||||||
|
render: renderResourceAdd
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 4,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 4,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'item_list',
|
||||||
|
render: renderItemList
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal min="960px" $view={detailView}>
|
||||||
|
<Title $align="center">이벤트 상세 정보</Title>
|
||||||
|
{content &&
|
||||||
|
<DetailRegistInfo>
|
||||||
|
<span>등록자 : {content.create_by}</span>
|
||||||
|
<span>등록일 : {convertKTC(content.create_dt, false)}</span>
|
||||||
|
{typeof content.update_by !== 'undefined' && (
|
||||||
|
<>
|
||||||
|
<span>수정자 : {content.update_by}</span>
|
||||||
|
<span>수정일 : {convertKTC(content.update_dt, false)}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</DetailRegistInfo>
|
||||||
|
}
|
||||||
|
|
||||||
|
<DetailLayout
|
||||||
|
itemGroups={itemGroups}
|
||||||
|
formData={resultData}
|
||||||
|
onChange={setResultData}
|
||||||
|
disabled={false}
|
||||||
|
columnCount={4}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{itemCheckMsg && (
|
||||||
|
<Alert
|
||||||
|
message={itemCheckMsg}
|
||||||
|
type="error"
|
||||||
|
style={{ marginTop: '8px', width: '300px' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px">
|
||||||
|
<Button
|
||||||
|
text="확인"
|
||||||
|
theme="line"
|
||||||
|
name="확인버튼"
|
||||||
|
handleClick={() => {
|
||||||
|
handleDetailView();
|
||||||
|
handleReset();
|
||||||
|
setDetailData('');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!isReadOnly && (
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text="수정"
|
||||||
|
id="수정버튼"
|
||||||
|
theme={conditionCheck() ? 'primary' : 'disable'}
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</BtnWrapper>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RewardEventDetailModal;
|
||||||
|
|
||||||
|
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
import { TextInput, InputLabel, SelectInput, InputGroup } from '../../styles/Components';
|
import { TextInput, InputLabel, SelectInput, InputGroup } from '../../styles/Components';
|
||||||
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { logAction, logDomain, userSearchType2 } from '../../assets/data/options';
|
import { logAction, logDomain, userSearchType2 } from '../../assets/data/options';
|
||||||
import { BusinessLogList } from '../../apis/Log';
|
import { BusinessLogList } from '../../apis/Log';
|
||||||
import {SearchFilter} from '../ServiceManage';
|
import {SearchFilter} from '../ServiceManage';
|
||||||
import { useAlert } from '../../context/AlertProvider';
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
import { alertTypes } from '../../assets/data/types';
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { DatePicker } from 'antd';
|
||||||
|
import DateRangePicker from '../common/Date/DateRangePicker';
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
export const useBusinessLogSearch = (token, initialPageSize) => {
|
export const useBusinessLogSearch = (token, initialPageSize) => {
|
||||||
const {showToast} = useAlert();
|
const {showToast} = useAlert();
|
||||||
@@ -16,16 +20,8 @@ export const useBusinessLogSearch = (token, initialPageSize) => {
|
|||||||
log_action: 'None',
|
log_action: 'None',
|
||||||
log_domain: 'BASE',
|
log_domain: 'BASE',
|
||||||
tran_id: '',
|
tran_id: '',
|
||||||
start_dt: (() => {
|
start_dt: dayjs().subtract(1, 'day').startOf('day'),
|
||||||
const date = new Date();
|
end_dt: dayjs().subtract(1, 'day').endOf('day'),
|
||||||
date.setDate(date.getDate() - 1);
|
|
||||||
return date;
|
|
||||||
})(),
|
|
||||||
end_dt: (() => {
|
|
||||||
const date = new Date();
|
|
||||||
date.setDate(date.getDate() - 1);
|
|
||||||
return date;
|
|
||||||
})(),
|
|
||||||
filters: [],
|
filters: [],
|
||||||
order_by: 'ASC',
|
order_by: 'ASC',
|
||||||
page_size: initialPageSize,
|
page_size: initialPageSize,
|
||||||
@@ -92,16 +88,14 @@ export const useBusinessLogSearch = (token, initialPageSize) => {
|
|||||||
}, [searchParams, fetchData]);
|
}, [searchParams, fetchData]);
|
||||||
|
|
||||||
const handleReset = useCallback(async () => {
|
const handleReset = useCallback(async () => {
|
||||||
const now = new Date();
|
|
||||||
now.setDate(now.getDate() - 1);
|
|
||||||
const resetParams = {
|
const resetParams = {
|
||||||
search_type: 'GUID',
|
search_type: 'GUID',
|
||||||
search_data: '',
|
search_data: '',
|
||||||
log_action: 'None',
|
log_action: 'None',
|
||||||
log_domain: 'BASE',
|
log_domain: 'BASE',
|
||||||
tran_id: '',
|
tran_id: '',
|
||||||
start_dt: now,
|
start_dt: dayjs().subtract(1, 'day').startOf('day'),
|
||||||
end_dt: now,
|
end_dt: dayjs().subtract(1, 'day').endOf('day'),
|
||||||
filters: [],
|
filters: [],
|
||||||
order_by: 'ASC',
|
order_by: 'ASC',
|
||||||
page_size: initialPageSize,
|
page_size: initialPageSize,
|
||||||
@@ -197,11 +191,11 @@ const BusinessLogSearchBar = ({ searchParams, onSearch, onReset }) => {
|
|||||||
</>,
|
</>,
|
||||||
<>
|
<>
|
||||||
<InputLabel>일자</InputLabel>
|
<InputLabel>일자</InputLabel>
|
||||||
<SearchPeriod
|
<DateRangePicker
|
||||||
startDate={searchParams.start_dt}
|
value={[searchParams.start_dt, searchParams.end_dt]}
|
||||||
handleStartDate={date => onSearch({ start_dt: date }, false)}
|
onChange={(dates) => {
|
||||||
endDate={searchParams.end_dt}
|
onSearch({ start_dt: dates[0], end_dt: dates[1] }, false);
|
||||||
handleEndDate={date => onSearch({ end_dt: date }, false)}
|
}}
|
||||||
/>
|
/>
|
||||||
</>,
|
</>,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
|||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { getOptionsArray } from '../../utils';
|
import { getOptionsArray } from '../../utils';
|
||||||
import { PageSkeleton } from '../Skeleton/SearchSkeleton';
|
import { PageSkeleton } from '../Skeleton/SearchSkeleton';
|
||||||
|
import { DateRangePicker } from '../common';
|
||||||
|
|
||||||
const renderSearchField = (field, searchParams, onSearch) => {
|
const renderSearchField = (field, searchParams, onSearch) => {
|
||||||
const { type, id, label, placeholder, width, optionsRef } = field;
|
const { type, id, label, placeholder, width, optionsRef, format, showTime } = field;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'text':
|
case 'text':
|
||||||
@@ -47,14 +48,45 @@ const renderSearchField = (field, searchParams, onSearch) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
case 'period':
|
case 'period':
|
||||||
|
const startDateValue = searchParams[field.startDateId];
|
||||||
|
const endDateValue = searchParams[field.endDateId];
|
||||||
|
|
||||||
|
// 날짜 값이 있을 때만 배열로 변환
|
||||||
|
const rangeValue = (startDateValue && endDateValue) ?
|
||||||
|
[startDateValue, endDateValue] :
|
||||||
|
null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{label && label !== 'undefined' && <InputLabel $require={field.required}>{label}</InputLabel>}
|
{label && label !== 'undefined' && <InputLabel $require={field.required}>{label}</InputLabel>}
|
||||||
<SearchPeriod
|
<DateRangePicker
|
||||||
startDate={searchParams[field.startDateId]}
|
value={rangeValue}
|
||||||
handleStartDate={date => onSearch({ [field.startDateId]: date }, false)}
|
onChange={(dates) => {
|
||||||
endDate={searchParams[field.endDateId]}
|
if (dates && dates.length === 2) {
|
||||||
handleEndDate={date => onSearch({ [field.endDateId]: date }, false)}
|
let startDate = dates[0];
|
||||||
|
let endDate = dates[1];
|
||||||
|
if(!showTime) {
|
||||||
|
// 시작 날짜는 00:00:00으로 설정
|
||||||
|
startDate = startDate.startOf('day');
|
||||||
|
// 종료 날짜는 23:59:59로 설정
|
||||||
|
endDate = endDate.endOf('day');
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearch({
|
||||||
|
[field.startDateId]: startDate.format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
[field.endDateId]: endDate.format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}, false);
|
||||||
|
} else {
|
||||||
|
onSearch({
|
||||||
|
[field.startDateId]: '',
|
||||||
|
[field.endDateId]: ''
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
showTime={showTime ||false}
|
||||||
|
format={format || "YYYY-MM-DD"}
|
||||||
|
placeholder={['시작일', '종료일']}
|
||||||
|
style={{ width: width || '280px' }}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
217
src/components/searchBar/ItemDictionarySearchBar.js
Normal file
217
src/components/searchBar/ItemDictionarySearchBar.js
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
import { TextInput, InputLabel, SelectInput, InputGroup } from '../../styles/Components';
|
||||||
|
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
itemSearchType,
|
||||||
|
logAction,
|
||||||
|
logDomain, opGender,
|
||||||
|
opItemLargeType,
|
||||||
|
opItemSmallType,
|
||||||
|
userSearchType2,
|
||||||
|
} from '../../assets/data/options';
|
||||||
|
import { BusinessLogList } from '../../apis/Log';
|
||||||
|
import {SearchFilter} from '../ServiceManage';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import { getItemDictionaryList } from '../../apis';
|
||||||
|
|
||||||
|
export const useItemDictionarySearch = (token, initialPageSize) => {
|
||||||
|
const {showToast} = useAlert();
|
||||||
|
|
||||||
|
const [searchParams, setSearchParams] = useState({
|
||||||
|
search_type: 'ID',
|
||||||
|
search_data: '',
|
||||||
|
large_type: 'ALL',
|
||||||
|
small_type: 'ALL',
|
||||||
|
brand: 'ALL',
|
||||||
|
gender: 'ALL',
|
||||||
|
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 getItemDictionaryList(
|
||||||
|
token,
|
||||||
|
params.search_type,
|
||||||
|
params.search_data,
|
||||||
|
params.large_type,
|
||||||
|
params.small_type,
|
||||||
|
params.brand,
|
||||||
|
params.gender,
|
||||||
|
params.order_by,
|
||||||
|
params.page_size,
|
||||||
|
params.page_no
|
||||||
|
);
|
||||||
|
if(result.result === "ERROR"){
|
||||||
|
showToast(result.result, {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
setData(result.data);
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
showToast('error', {type: alertTypes.error});
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const updateSearchParams = useCallback((newParams) => {
|
||||||
|
setSearchParams(prev => ({
|
||||||
|
...prev,
|
||||||
|
...newParams
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearch = useCallback(async (newParams = {}, executeSearch = true) => {
|
||||||
|
const updatedParams = {
|
||||||
|
...searchParams,
|
||||||
|
...newParams,
|
||||||
|
page_no: newParams.page_no || 1 // Reset to first page on new search
|
||||||
|
};
|
||||||
|
updateSearchParams(updatedParams);
|
||||||
|
|
||||||
|
if (executeSearch) {
|
||||||
|
return await fetchData(updatedParams);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [searchParams, fetchData]);
|
||||||
|
|
||||||
|
const handleReset = useCallback(async () => {
|
||||||
|
const now = new Date();
|
||||||
|
now.setDate(now.getDate() - 1);
|
||||||
|
const resetParams = {
|
||||||
|
search_type: 'ID',
|
||||||
|
search_data: '',
|
||||||
|
large_type: 'ALL',
|
||||||
|
small_type: 'ALL',
|
||||||
|
brand: 'ALL',
|
||||||
|
gender: 'ALL',
|
||||||
|
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 ItemDictionarySearchBar = ({ searchParams, onSearch, onReset, brandData }) => {
|
||||||
|
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)}>
|
||||||
|
{itemSearchType.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
placeholder={searchParams.search_type === 'ID' ? '아이템 ID 입력' : '아이템명 입력'}
|
||||||
|
value={searchParams.search_data}
|
||||||
|
width="260px"
|
||||||
|
onChange={e => onSearch({ search_data: e.target.value }, false)}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>대분류</InputLabel>
|
||||||
|
<SelectInput value={searchParams.large_type} onChange={e => onSearch({ large_type: e.target.value }, false)} >
|
||||||
|
<option value='ALL'>전체</option>
|
||||||
|
{opItemLargeType.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>소분류</InputLabel>
|
||||||
|
<SelectInput value={searchParams.small_type} onChange={e => onSearch({ small_type: e.target.value }, false)} >
|
||||||
|
<option value='ALL'>전체</option>
|
||||||
|
{opItemSmallType.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
];
|
||||||
|
|
||||||
|
const optionList = [
|
||||||
|
<>
|
||||||
|
<InputLabel>브랜드</InputLabel>
|
||||||
|
<SelectInput value={searchParams.brand} onChange={e => onSearch({ brand: e.target.value })}>
|
||||||
|
<option value='ALL'>전체</option>
|
||||||
|
{brandData?.map((data, index) => (
|
||||||
|
<option key={index} value={data.id}>
|
||||||
|
{data.brandDesc}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>성별</InputLabel>
|
||||||
|
<SelectInput value={searchParams.gender} onChange={e => onSearch({ gender: e.target.value }, false)} >
|
||||||
|
<option value='ALL'>전체</option>
|
||||||
|
{opGender.map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
</>,
|
||||||
|
];
|
||||||
|
|
||||||
|
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ItemDictionarySearchBar;
|
||||||
@@ -19,6 +19,7 @@ export const useItemLogSearch = (token, initialPageSize) => {
|
|||||||
const [searchParams, setSearchParams] = useState({
|
const [searchParams, setSearchParams] = useState({
|
||||||
search_type: 'GUID',
|
search_type: 'GUID',
|
||||||
search_data: '',
|
search_data: '',
|
||||||
|
item_id: '',
|
||||||
tran_id: '',
|
tran_id: '',
|
||||||
log_action: 'None',
|
log_action: 'None',
|
||||||
item_large_type: 'None',
|
item_large_type: 'None',
|
||||||
@@ -59,6 +60,7 @@ export const useItemLogSearch = (token, initialPageSize) => {
|
|||||||
token,
|
token,
|
||||||
params.search_type,
|
params.search_type,
|
||||||
params.search_data,
|
params.search_data,
|
||||||
|
params.item_id,
|
||||||
params.tran_id,
|
params.tran_id,
|
||||||
params.log_action,
|
params.log_action,
|
||||||
params.item_large_type,
|
params.item_large_type,
|
||||||
@@ -114,6 +116,7 @@ export const useItemLogSearch = (token, initialPageSize) => {
|
|||||||
const resetParams = {
|
const resetParams = {
|
||||||
search_type: 'GUID',
|
search_type: 'GUID',
|
||||||
search_data: '',
|
search_data: '',
|
||||||
|
item_id: '',
|
||||||
tran_id: '',
|
tran_id: '',
|
||||||
log_action: 'None',
|
log_action: 'None',
|
||||||
item_large_type: 'None',
|
item_large_type: 'None',
|
||||||
@@ -190,6 +193,21 @@ const ItemLogSearchBar = ({ searchParams, onSearch, onReset }) => {
|
|||||||
onChange={e => onSearch({ tran_id: e.target.value }, false)}
|
onChange={e => onSearch({ tran_id: e.target.value }, false)}
|
||||||
/>
|
/>
|
||||||
</>,
|
</>,
|
||||||
|
<>
|
||||||
|
<InputLabel>아이템 ID</InputLabel>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
placeholder='아이템 ID 입력'
|
||||||
|
value={searchParams.item_id}
|
||||||
|
width="150px"
|
||||||
|
onChange={e => {
|
||||||
|
// 숫자만 허용하는 정규식
|
||||||
|
const numericValue = e.target.value.replace(/[^0-9]/g, '');
|
||||||
|
onSearch({ item_id: numericValue }, false);
|
||||||
|
}}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
<>
|
<>
|
||||||
<InputLabel>LargeType</InputLabel>
|
<InputLabel>LargeType</InputLabel>
|
||||||
<SelectInput value={searchParams.item_large_type} onChange={e => onSearch({ item_large_type: e.target.value }, false)} >
|
<SelectInput value={searchParams.item_large_type} onChange={e => onSearch({ item_large_type: e.target.value }, false)} >
|
||||||
|
|||||||
124
src/components/searchBar/RankManageSearchBar.js
Normal file
124
src/components/searchBar/RankManageSearchBar.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import { TextInput, SelectInput, InputGroup } from '../../styles/Components';
|
||||||
|
import { SearchBarLayout } from '../common/SearchBar';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
userSearchType2,
|
||||||
|
} from '../../assets/data/options';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import { getItemDictionaryList, UserView } from '../../apis';
|
||||||
|
|
||||||
|
export const useRankManageSearch = (token) => {
|
||||||
|
const {showToast} = useAlert();
|
||||||
|
|
||||||
|
const [searchParams, setSearchParams] = useState({
|
||||||
|
search_type: 'GUID',
|
||||||
|
search_data: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
const fetchData = useCallback(async (params) => {
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await UserView(
|
||||||
|
token,
|
||||||
|
params.search_type,
|
||||||
|
params.search_data
|
||||||
|
);
|
||||||
|
if(result.result === "NOT_USER"){
|
||||||
|
showToast(result.result, {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setData(result.data.result);
|
||||||
|
return result.data.result;
|
||||||
|
} catch (error) {
|
||||||
|
showToast('error', {type: alertTypes.error});
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const updateSearchParams = useCallback((newParams) => {
|
||||||
|
setSearchParams(prev => ({
|
||||||
|
...prev,
|
||||||
|
...newParams
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearch = useCallback(async (newParams = {}, executeSearch = true) => {
|
||||||
|
const updatedParams = {
|
||||||
|
...searchParams,
|
||||||
|
...newParams
|
||||||
|
};
|
||||||
|
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: ''
|
||||||
|
};
|
||||||
|
setSearchParams(resetParams);
|
||||||
|
return await fetchData(resetParams);
|
||||||
|
}, [fetchData]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchParams,
|
||||||
|
loading,
|
||||||
|
data,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
updateSearchParams
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const RankManageSearchBar = ({ 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 입력' : searchParams.search_type === 'ACCOUNT' ? 'ACCOUNT 입력' : '닉네임 입력'}
|
||||||
|
value={searchParams.search_data}
|
||||||
|
width="260px"
|
||||||
|
onChange={e => onSearch({ search_data: e.target.value }, false)}
|
||||||
|
onKeyDown={e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
onSearch({ search_data: e.target.value }, true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</>
|
||||||
|
];
|
||||||
|
|
||||||
|
return <SearchBarLayout firstColumnData={searchList} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RankManageSearchBar;
|
||||||
@@ -1,57 +1,124 @@
|
|||||||
import { styled } from 'styled-components';
|
import { TextInput, SelectInput, InputGroup } from '../../styles/Components';
|
||||||
import Button from '../common/button/Button';
|
import { SearchBarLayout } from '../common/SearchBar';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
userSearchType2,
|
||||||
|
} from '../../assets/data/options';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import { UserView } from '../../apis';
|
||||||
|
|
||||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper } from '../../styles/Components';
|
export const useUserSearch = (token) => {
|
||||||
|
const {showToast} = useAlert();
|
||||||
|
|
||||||
const UserSearchBar = () => {
|
const [searchParams, setSearchParams] = useState({
|
||||||
return (
|
search_type: 'GUID',
|
||||||
|
search_data: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
const fetchData = useCallback(async (params) => {
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await UserView(
|
||||||
|
token,
|
||||||
|
params.search_type,
|
||||||
|
params.search_data
|
||||||
|
);
|
||||||
|
if(result.result === "NOT_USER"){
|
||||||
|
showToast(result.result, {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setData(result.data.result);
|
||||||
|
return result.data.result;
|
||||||
|
} catch (error) {
|
||||||
|
showToast('error', {type: alertTypes.error});
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const updateSearchParams = useCallback((newParams) => {
|
||||||
|
setSearchParams(prev => ({
|
||||||
|
...prev,
|
||||||
|
...newParams
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearch = useCallback(async (newParams = {}, executeSearch = true) => {
|
||||||
|
const updatedParams = {
|
||||||
|
...searchParams,
|
||||||
|
...newParams
|
||||||
|
};
|
||||||
|
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: ''
|
||||||
|
};
|
||||||
|
setSearchParams(resetParams);
|
||||||
|
return await fetchData(resetParams);
|
||||||
|
}, [fetchData]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchParams,
|
||||||
|
loading,
|
||||||
|
data,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
updateSearchParams
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const UserSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||||
|
const handleSubmit = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
onSearch(searchParams, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchList = [
|
||||||
<>
|
<>
|
||||||
<FormWrapper>
|
<InputGroup>
|
||||||
<SearchbarStyle>
|
<SelectInput value={searchParams.search_type} onChange={e => onSearch({search_type: e.target.value }, false)}>
|
||||||
<SearchItem>
|
{userSearchType2.map((data, index) => (
|
||||||
<InputLabel>유저 조회</InputLabel>
|
<option key={index} value={data.value}>
|
||||||
<TextInput type="text" width="300px" placeholder="조회 대상 유저의 GUID를 입력하세요." />
|
{data.name}
|
||||||
</SearchItem>
|
</option>
|
||||||
<BtnWrapper $gap="8px">
|
))}
|
||||||
<Button theme="reset" />
|
</SelectInput>
|
||||||
<Button
|
<TextInput
|
||||||
theme="primary"
|
type="text"
|
||||||
text="검색"
|
placeholder={searchParams.search_type === 'GUID' ? 'GUID 입력' : searchParams.search_type === 'ACCOUNT' ? 'ACCOUNT 입력' : '닉네임 입력'}
|
||||||
handleClick={e => {
|
value={searchParams.search_data}
|
||||||
e.preventDefault();
|
width="260px"
|
||||||
}}
|
onChange={e => onSearch({ search_data: e.target.value }, false)}
|
||||||
/>
|
onKeyDown={e => {
|
||||||
</BtnWrapper>
|
if (e.key === 'Enter') {
|
||||||
</SearchbarStyle>
|
e.preventDefault();
|
||||||
</FormWrapper>
|
onSearch({ search_data: e.target.value }, true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
</>
|
</>
|
||||||
);
|
];
|
||||||
|
|
||||||
|
return <SearchBarLayout firstColumnData={searchList} onReset={onReset} handleSubmit={handleSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserSearchBar;
|
export default UserSearchBar;
|
||||||
|
|
||||||
const SearchbarStyle = styled.div`
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 20px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 20px;
|
|
||||||
border-top: 1px solid #000;
|
|
||||||
border-bottom: 1px solid #000;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
`;
|
|
||||||
const SearchItem = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 20px;
|
|
||||||
margin-right: 50px;
|
|
||||||
|
|
||||||
${TextInput}, ${SelectInput} {
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
${TextInput} {
|
|
||||||
padding: 0 10px;
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import VBPSearchBar from './VBPSearchBar';
|
|||||||
import DecoSearchBar from './DecoSearchBar';
|
import DecoSearchBar from './DecoSearchBar';
|
||||||
import ItemSearchBar from './ItemSearchBar';
|
import ItemSearchBar from './ItemSearchBar';
|
||||||
import LandSearchBar from './LandSearchBar';
|
import LandSearchBar from './LandSearchBar';
|
||||||
import UserSearchBar from './UserSearchBar';
|
|
||||||
import DailySearchBar from './DailySearchBar';
|
import DailySearchBar from './DailySearchBar';
|
||||||
import CommonSearchBar from './CommonSearchBar';
|
import CommonSearchBar from './CommonSearchBar';
|
||||||
import CreditSearchBar from './CreditSearchBar';
|
import CreditSearchBar from './CreditSearchBar';
|
||||||
@@ -36,8 +35,11 @@ import UserCreateLogSearchBar, { useUserCreateLogSearch } from './UserCreateLogS
|
|||||||
import UserLoginLogSearchBar, { useUserLoginLogSearch } from './UserLoginLogSearchBar';
|
import UserLoginLogSearchBar, { useUserLoginLogSearch } from './UserLoginLogSearchBar';
|
||||||
import UserSnapshotLogSearchBar, { useUserSnapshotLogSearch } from './UserSnapshotLogSearchBar';
|
import UserSnapshotLogSearchBar, { useUserSnapshotLogSearch } from './UserSnapshotLogSearchBar';
|
||||||
import AssetsIndexSearchBar, { useAssetsIndexSearch } from './AssetsIndexSearchBar';
|
import AssetsIndexSearchBar, { useAssetsIndexSearch } from './AssetsIndexSearchBar';
|
||||||
|
import ItemDictionarySearchBar, { useItemDictionarySearch } from './ItemDictionarySearchBar';
|
||||||
|
import RankManageSearchBar, { useRankManageSearch } from './RankManageSearchBar';
|
||||||
import LandAuctionSearchBar from './LandAuctionSearchBar';
|
import LandAuctionSearchBar from './LandAuctionSearchBar';
|
||||||
import CaliumRequestSearchBar from './CaliumRequestSearchBar';
|
import CaliumRequestSearchBar from './CaliumRequestSearchBar';
|
||||||
|
import UserSearchBar, {useUserSearch} from './UserSearchBar';
|
||||||
|
|
||||||
// 모든 SearchBar 컴포넌트 export
|
// 모든 SearchBar 컴포넌트 export
|
||||||
export {
|
export {
|
||||||
@@ -46,7 +48,6 @@ export {
|
|||||||
DecoSearchBar,
|
DecoSearchBar,
|
||||||
ItemSearchBar,
|
ItemSearchBar,
|
||||||
LandSearchBar,
|
LandSearchBar,
|
||||||
UserSearchBar,
|
|
||||||
DailySearchBar,
|
DailySearchBar,
|
||||||
CommonSearchBar,
|
CommonSearchBar,
|
||||||
CreditSearchBar,
|
CreditSearchBar,
|
||||||
@@ -93,6 +94,12 @@ export {
|
|||||||
UserSnapshotLogSearchBar,
|
UserSnapshotLogSearchBar,
|
||||||
useUserSnapshotLogSearch,
|
useUserSnapshotLogSearch,
|
||||||
AssetsIndexSearchBar,
|
AssetsIndexSearchBar,
|
||||||
useAssetsIndexSearch
|
useAssetsIndexSearch,
|
||||||
|
ItemDictionarySearchBar,
|
||||||
|
useItemDictionarySearch,
|
||||||
|
RankManageSearchBar,
|
||||||
|
useRankManageSearch,
|
||||||
|
UserSearchBar,
|
||||||
|
useUserSearch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useAlert } from '../context/AlertProvider';
|
|||||||
import { alertTypes } from '../assets/data/types';
|
import { alertTypes } from '../assets/data/types';
|
||||||
import * as APIConfigs from '../assets/data/apis'
|
import * as APIConfigs from '../assets/data/apis'
|
||||||
import { callAPI } from '../utils/apiService';
|
import { callAPI } from '../utils/apiService';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
export const useCommonSearch = (configPath) => {
|
export const useCommonSearch = (configPath) => {
|
||||||
const [config, setConfig] = useState(null);
|
const [config, setConfig] = useState(null);
|
||||||
@@ -24,7 +25,9 @@ export const useCommonSearch = (configPath) => {
|
|||||||
|
|
||||||
// 초기 검색 파라미터 설정
|
// 초기 검색 파라미터 설정
|
||||||
if (configData.initialSearchParams) {
|
if (configData.initialSearchParams) {
|
||||||
setSearchParams(configData.initialSearchParams);
|
const processedParams = processTodayValues(configData.initialSearchParams);
|
||||||
|
setSearchParams(processedParams);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// API 엔드포인트 설정 가져오기
|
// API 엔드포인트 설정 가져오기
|
||||||
@@ -50,6 +53,27 @@ export const useCommonSearch = (configPath) => {
|
|||||||
fetchConfig();
|
fetchConfig();
|
||||||
}, [configPath]);
|
}, [configPath]);
|
||||||
|
|
||||||
|
const processTodayValues = useCallback((params) => {
|
||||||
|
const processedParams = { ...params };
|
||||||
|
|
||||||
|
Object.keys(processedParams).forEach(key => {
|
||||||
|
if (processedParams[key] === 'today') {
|
||||||
|
if (key.toLowerCase().includes('start')) {
|
||||||
|
// start가 포함된 키는 00:00:00으로 설정
|
||||||
|
processedParams[key] = dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
} else if (key.toLowerCase().includes('end')) {
|
||||||
|
// end가 포함된 키는 23:59:59로 설정
|
||||||
|
processedParams[key] = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
} else {
|
||||||
|
// 그 외는 현재 시간으로 설정
|
||||||
|
processedParams[key] = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return processedParams;
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 파라미터 값 변환 (날짜 등)
|
// 파라미터 값 변환 (날짜 등)
|
||||||
const transformParams = useCallback((params) => {
|
const transformParams = useCallback((params) => {
|
||||||
if (!config || !config.apiInfo || !config.apiInfo.paramTransforms) {
|
if (!config || !config.apiInfo || !config.apiInfo.paramTransforms) {
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ export const useRDSPagination = (fetchFunction, initialState = {}) => {
|
|||||||
setPagination(prev => ({
|
setPagination(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
currentPage: page,
|
currentPage: page,
|
||||||
totalItems: response.total_all || response.total || 0,
|
totalItems: response?.total_all || response?.total || 0,
|
||||||
totalPages: response.total_pages || Math.ceil((response.total_all || response.total || 0) / pagination.pageSize)
|
totalPages: response?.total_pages || Math.ceil((response?.total_all || response?.total || 0) / pagination.pageSize)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
11
src/i18n.js
11
src/i18n.js
@@ -141,6 +141,13 @@ const resources = {
|
|||||||
BATTLE_EVENT_STOP_5MINUTES_DATE_WARNING: "이벤트 시작 5분 전에는 중단할 수 없습니다.",
|
BATTLE_EVENT_STOP_5MINUTES_DATE_WARNING: "이벤트 시작 5분 전에는 중단할 수 없습니다.",
|
||||||
BATTLE_EVENT_STATUS_RUNNING_WARNING: "이벤트 진행중에는 중단할 수 없습니다.",
|
BATTLE_EVENT_STATUS_RUNNING_WARNING: "이벤트 진행중에는 중단할 수 없습니다.",
|
||||||
BATTLE_EVENT_MODAL_STATUS_WARNING: "이벤트가 중단일때만 수정이 가능합니다.",
|
BATTLE_EVENT_MODAL_STATUS_WARNING: "이벤트가 중단일때만 수정이 가능합니다.",
|
||||||
|
//이벤트
|
||||||
|
EVENT_MODAL_STATUS_WARNING: "이벤트가 대기일때만 수정이 가능합니다.",
|
||||||
|
//랭킹 스케줄
|
||||||
|
SCHEDULE_MODAL_STATUS_WARNING: "스케줄이 대기일때만 수정이 가능합니다.",
|
||||||
|
SCHEDULE_SELECT_DELETE: "선택된 스케줄을 삭제하시겠습니까?",
|
||||||
|
SCHEDULE_REGIST_CONFIRM: "스케줄을 등록하시겠습니까?",
|
||||||
|
SCHEDULE_UPDATE_CONFIRM: "스케줄을 수정하시겠습니까?",
|
||||||
//메뉴 배너
|
//메뉴 배너
|
||||||
MENU_BANNER_TITLE: "메뉴 배너 관리",
|
MENU_BANNER_TITLE: "메뉴 배너 관리",
|
||||||
MENU_BANNER_CREATE: "메뉴 배너 등록",
|
MENU_BANNER_CREATE: "메뉴 배너 등록",
|
||||||
@@ -163,6 +170,7 @@ const resources = {
|
|||||||
FILE_IMAGE_UPLOAD_ERROR: "이미지 업로드 중 오류가 발생했습니다.",
|
FILE_IMAGE_UPLOAD_ERROR: "이미지 업로드 중 오류가 발생했습니다.",
|
||||||
FILE_NOT_EXIT_ERROR: "유효하지 않은 파일입니다.",
|
FILE_NOT_EXIT_ERROR: "유효하지 않은 파일입니다.",
|
||||||
FILE_SIZE_OVER_ERROR: "파일의 사이즈가 5MB를 초과하였습니다.",
|
FILE_SIZE_OVER_ERROR: "파일의 사이즈가 5MB를 초과하였습니다.",
|
||||||
|
FILE_KOREAN_NAME_WARNING: "한글이름이 들어간 파일은 등록할 수 없습니다.",
|
||||||
//파일명칭
|
//파일명칭
|
||||||
FILE_INDEX_USER_CONTENT: 'Caliverse_User_Index.xlsx',
|
FILE_INDEX_USER_CONTENT: 'Caliverse_User_Index.xlsx',
|
||||||
FILE_INDEX_USER_RETENTION: 'Caliverse_Retention.csv',
|
FILE_INDEX_USER_RETENTION: 'Caliverse_Retention.csv',
|
||||||
@@ -171,6 +179,7 @@ const resources = {
|
|||||||
FILE_INDEX_ITEM_ACQUIRE: 'Caliverse_Item_Acquire.csv',
|
FILE_INDEX_ITEM_ACQUIRE: 'Caliverse_Item_Acquire.csv',
|
||||||
FILE_INDEX_ITEM_CONSUME: 'Caliverse_Item_Consume.csv',
|
FILE_INDEX_ITEM_CONSUME: 'Caliverse_Item_Consume.csv',
|
||||||
FILE_INDEX_ASSETS_CURRENCY: 'Caliverse_Assets_Currency.csv',
|
FILE_INDEX_ASSETS_CURRENCY: 'Caliverse_Assets_Currency.csv',
|
||||||
|
FILE_INDEX_STAT_CURRENCY: 'Caliverse_Stat_Currency.csv',
|
||||||
FILE_INDEX_ASSETS_ITEM: 'Caliverse_Assets_Item.csv',
|
FILE_INDEX_ASSETS_ITEM: 'Caliverse_Assets_Item.csv',
|
||||||
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',
|
||||||
@@ -183,8 +192,10 @@ const resources = {
|
|||||||
FILE_GAME_LOG_ITEM: 'Caliverse_Game_Log_Item',
|
FILE_GAME_LOG_ITEM: 'Caliverse_Game_Log_Item',
|
||||||
FILE_GAME_LOG_CURRENCY_ITEM: 'Caliverse_Game_Log_Currecy_Item',
|
FILE_GAME_LOG_CURRENCY_ITEM: 'Caliverse_Game_Log_Currecy_Item',
|
||||||
FILE_CURRENCY_INDEX: 'Caliverse_Currency_Index',
|
FILE_CURRENCY_INDEX: 'Caliverse_Currency_Index',
|
||||||
|
FILE_DICTIONARY_ITEM: 'Caliverse_Dictionary_Item',
|
||||||
//서버 에러메시지
|
//서버 에러메시지
|
||||||
DYNAMODB_NOT_USER: '유저 정보를 확인해주세요.',
|
DYNAMODB_NOT_USER: '유저 정보를 확인해주세요.',
|
||||||
|
NOT_USER: '유저 정보를 확인해주세요.',
|
||||||
NICKNAME_EXIT_ERROR: '해당 닉네임이 존재합니다.',
|
NICKNAME_EXIT_ERROR: '해당 닉네임이 존재합니다.',
|
||||||
NICKNAME_NUMBER_ERROR: '닉네임은 첫번째 글자에 숫자를 허용하지 않습니다.',
|
NICKNAME_NUMBER_ERROR: '닉네임은 첫번째 글자에 숫자를 허용하지 않습니다.',
|
||||||
NICKNAME_SPECIALCHAR_ERROR: '닉네임은 특수문자를 사용할 수 없습니다.',
|
NICKNAME_SPECIALCHAR_ERROR: '닉네임은 특수문자를 사용할 수 없습니다.',
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
import { styled } from 'styled-components';
|
|
||||||
import { Fragment, useState } from 'react';
|
|
||||||
|
|
||||||
import { Title, TableStyle, ButtonClose, ModalText, BtnWrapper } from '../../styles/Components';
|
|
||||||
|
|
||||||
import UserSearchBar from '../../components/searchBar/UserSearchBar';
|
|
||||||
import Modal from '../../components/common/modal/Modal';
|
|
||||||
import Button from '../../components/common/button/Button';
|
|
||||||
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { authList } from '../../store/authList';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
const CryptView = () => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const userInfo = useRecoilValue(authList);
|
|
||||||
const mokupData = [
|
|
||||||
{
|
|
||||||
getDate: '2023-08-06 18:50:03',
|
|
||||||
getLocation: '유저 거래',
|
|
||||||
itemName: '빛나는 가죽 장갑',
|
|
||||||
serialNo: 'Serial_number',
|
|
||||||
itemCode: 'Item_code',
|
|
||||||
tradeKey: 'User_trade_key',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 15) ? (
|
|
||||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={'view'}>
|
|
||||||
<BtnWrapper $justify="flex-end">
|
|
||||||
<ButtonClose onClick={() => navigate(-1)} />
|
|
||||||
</BtnWrapper>
|
|
||||||
<ModalText $align="center">
|
|
||||||
해당 메뉴에 대한 조회 권한이 없습니다.
|
|
||||||
<br />
|
|
||||||
권한 등급을 변경 후 다시 이용해주세요.
|
|
||||||
</ModalText>
|
|
||||||
<BtnWrapper $gap="10px">
|
|
||||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={() => navigate(-1)} />
|
|
||||||
</BtnWrapper>
|
|
||||||
</Modal>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Title>크립토조회</Title>
|
|
||||||
<UserSearchBar />
|
|
||||||
<TableWrapper>
|
|
||||||
<TableStyle>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="170">획득일자</th>
|
|
||||||
<th width="170">획득처</th>
|
|
||||||
<th width="200">아이템명</th>
|
|
||||||
<th width="250">시리얼 넘버</th>
|
|
||||||
<th width="250">고유 코드</th>
|
|
||||||
<th width="250">거래 key</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{mokupData.map((data, index) => (
|
|
||||||
<Fragment key={index}>
|
|
||||||
<tr>
|
|
||||||
<td>{new Date(data.getDate).toLocaleString()}</td>
|
|
||||||
<td>{data.getLocation}</td>
|
|
||||||
<td>{data.itemName}</td>
|
|
||||||
<td>{data.serialNo}</td>
|
|
||||||
<td>{data.itemCode}</td>
|
|
||||||
<td>{data.tradeKey}</td>
|
|
||||||
</tr>
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</TableStyle>
|
|
||||||
</TableWrapper>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CryptView;
|
|
||||||
|
|
||||||
const TableWrapper = styled.div`
|
|
||||||
max-height: calc(100vh - 287px);
|
|
||||||
overflow: auto;
|
|
||||||
border-top: 1px solid #000;
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 4px;
|
|
||||||
height: 4px;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: #666666;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: #d9d9d9;
|
|
||||||
}
|
|
||||||
thead {
|
|
||||||
th {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${TableStyle} {
|
|
||||||
th {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
289
src/pages/DataManage/MetaItemView.js
Normal file
289
src/pages/DataManage/MetaItemView.js
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { AnimatedPageWrapper, InfoCard } from '../../components/common/Layout';
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
TableStyle,
|
||||||
|
FormWrapper,
|
||||||
|
TableWrapper,
|
||||||
|
TableActionButton,
|
||||||
|
TableDetailRow,
|
||||||
|
TableDetailContainer,
|
||||||
|
TableDetailFlex,
|
||||||
|
TableDetailColumn,
|
||||||
|
DownloadContainer, CircularProgressWrapper,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import { useDataFetch, withAuth } from '../../hooks/hook';
|
||||||
|
import {
|
||||||
|
authType,
|
||||||
|
} from '../../assets/data';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
AnimatedTabs,
|
||||||
|
ViewTableInfo,
|
||||||
|
} from '../../components/common';
|
||||||
|
import { TableSkeleton } from '../../components/Skeleton/TableSkeleton';
|
||||||
|
import CircularProgress from '../../components/common/CircularProgress';
|
||||||
|
|
||||||
|
import {
|
||||||
|
INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE,
|
||||||
|
} from '../../assets/data/adminConstants';
|
||||||
|
import ExcelExportButton from '../../components/common/button/ExcelExportButton';
|
||||||
|
import Pagination from '../../components/common/Pagination/Pagination';
|
||||||
|
import { useItemDictionarySearch, ItemDictionarySearchBar } from '../../components/searchBar';
|
||||||
|
import { numberFormatter } from '../../utils';
|
||||||
|
import { BrandView, ItemDictionaryExport } from '../../apis';
|
||||||
|
import { opGender, opItemLargeType, opItemSmallType } from '../../assets/data/options';
|
||||||
|
import { languageNames } from '../../assets/data/types';
|
||||||
|
|
||||||
|
const MetaItemView = () => {
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const tableRef = useRef(null);
|
||||||
|
|
||||||
|
const [expandedRows, setExpandedRows] = useState({});
|
||||||
|
const [activeLanguage, setActiveLanguage] = useState('ko');
|
||||||
|
|
||||||
|
const [downloadState, setDownloadState] = useState({
|
||||||
|
loading: false,
|
||||||
|
progress: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
searchParams,
|
||||||
|
loading: dataLoading,
|
||||||
|
data: dataList,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
handleOrderByChange,
|
||||||
|
handlePageSizeChange,
|
||||||
|
updateSearchParams
|
||||||
|
} = useItemDictionarySearch(token, INITIAL_PAGE_SIZE);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: brandData
|
||||||
|
} = useDataFetch(() => BrandView(token), [token]);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
setDownloadState({
|
||||||
|
loading: false,
|
||||||
|
progress: 0
|
||||||
|
});
|
||||||
|
},[dataList]);
|
||||||
|
|
||||||
|
const toggleRowExpand = (index) => {
|
||||||
|
setExpandedRows(prev => ({
|
||||||
|
...prev,
|
||||||
|
[index]: !prev[index]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableHeaders = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{ id: 'largeType', label: '대분류', width: '100px' },
|
||||||
|
{ id: 'smallType', label: '소분류', width: '120px' },
|
||||||
|
{ id: 'itemName', label: '아이템명', width: '150px' },
|
||||||
|
{ id: 'itemId', label: '아이템 ID', width: '120px' },
|
||||||
|
{ id: 'brand', label: '브랜드', width: '150px' },
|
||||||
|
{ id: 'gender', label: '성별', width: '100px' },
|
||||||
|
{ id: 'currencySellType', label: '판매시 획득 재화', width: '120px' },
|
||||||
|
{ id: 'currencySellPrice', label: '판매시 획득 재화량', width: '120px' },
|
||||||
|
{ id: 'currencyBuyType', label: '구매시 획득 재화', width: '120px' },
|
||||||
|
{ id: 'currencyBuyPrice', label: '구매시 획득 재화량', width: '120px' },
|
||||||
|
{ id: 'currencyBuyRate', label: '구매시 할인율', width: '120px' },
|
||||||
|
{ id: 'details', label: '상세 정보', width: '100px' }
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderDetailInfo = useCallback((data, title, keyPrefix, index) => {
|
||||||
|
if (!data) return null;
|
||||||
|
|
||||||
|
if (typeof data === 'object' && !Array.isArray(data)) {
|
||||||
|
return (
|
||||||
|
<InfoCard
|
||||||
|
title={title}
|
||||||
|
data={data}
|
||||||
|
keyPrefix={`${keyPrefix}-${index}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(data) && data.length > 0) {
|
||||||
|
const flattenedData = data.reduce((acc, info, dataIndex) => {
|
||||||
|
Object.entries(info).forEach(([key, value]) => {
|
||||||
|
acc[`${dataIndex + 1}_${key}`] = value;
|
||||||
|
});
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InfoCard
|
||||||
|
title={title}
|
||||||
|
data={flattenedData}
|
||||||
|
keyPrefix={`${keyPrefix}-${index}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 언어별 테이블 렌더링
|
||||||
|
const renderTableForLanguage = useCallback((languageKey) => {
|
||||||
|
// 해당 언어의 아이템 리스트 가져오기
|
||||||
|
const languageItemList = dataList?.item_list?.[languageKey] || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{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>
|
||||||
|
{languageItemList.length > 0 ? languageItemList.map((item, index) => (
|
||||||
|
<Fragment key={`${languageKey}-${index}`}>
|
||||||
|
<tr>
|
||||||
|
<td>{opItemLargeType.find(data => data.value === item.type_large)?.name}</td>
|
||||||
|
<td>{opItemSmallType.find(data => data.value === item.type_small)?.name}</td>
|
||||||
|
<td>{item.item_name}</td>
|
||||||
|
<td>{item.item_id}</td>
|
||||||
|
<td>{item.brand === '' ? '-' : item.brand}</td>
|
||||||
|
<td>{opGender.find(data => data.value === item.gender)?.name || '남녀공용'}</td>
|
||||||
|
<td>{item.sell_type}</td>
|
||||||
|
<td>{numberFormatter.formatCurrency(item.sell_price)}</td>
|
||||||
|
<td>{item.buy_type}</td>
|
||||||
|
<td>{numberFormatter.formatCurrency(item.buy_price)}</td>
|
||||||
|
<td>{item.buy_discount_rate}</td>
|
||||||
|
<td>
|
||||||
|
<TableActionButton onClick={() => toggleRowExpand(`${languageKey}-${index}`)}>
|
||||||
|
{expandedRows[`${languageKey}-${index}`] ? '접기' : '상세보기'}
|
||||||
|
</TableActionButton>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{expandedRows[`${languageKey}-${index}`] && (
|
||||||
|
<TableDetailRow>
|
||||||
|
<td colSpan={tableHeaders.length}>
|
||||||
|
<TableDetailContainer>
|
||||||
|
<TableDetailFlex>
|
||||||
|
<TableDetailColumn>
|
||||||
|
{renderDetailInfo(item.country, "Count 정보", "country", `${languageKey}-${index}`)}
|
||||||
|
{renderDetailInfo(item.expire, "아이템 만료 정보", "expire", `${languageKey}-${index}`)}
|
||||||
|
{renderDetailInfo(item.trade, "Trade 정보", "trade", `${languageKey}-${index}`)}
|
||||||
|
</TableDetailColumn>
|
||||||
|
<TableDetailColumn>
|
||||||
|
{renderDetailInfo(item.attrib, "속성 정보", "attrib", `${languageKey}-${index}`)}
|
||||||
|
{renderDetailInfo(item.etc, "기타 정보", "etc", `${languageKey}-${index}`)}
|
||||||
|
</TableDetailColumn>
|
||||||
|
</TableDetailFlex>
|
||||||
|
</TableDetailContainer>
|
||||||
|
</td>
|
||||||
|
</TableDetailRow>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
)) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={tableHeaders.length} style={{ textAlign: 'center', padding: '20px' }}>
|
||||||
|
해당 언어의 데이터가 없습니다.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</TableStyle>
|
||||||
|
</TableWrapper>
|
||||||
|
{languageItemList.length > 0 &&
|
||||||
|
<Pagination
|
||||||
|
postsPerPage={searchParams.page_size}
|
||||||
|
totalPosts={dataList?.total_all}
|
||||||
|
setCurrentPage={handlePageChange}
|
||||||
|
currentPage={searchParams.page_no}
|
||||||
|
pageLimit={INITIAL_PAGE_LIMIT}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}, [dataList, dataLoading, expandedRows, tableHeaders, searchParams, handlePageChange]);
|
||||||
|
|
||||||
|
// 언어별 탭 아이템 생성
|
||||||
|
const tabItems = useMemo(() => {
|
||||||
|
// 실제 데이터에서 사용 가능한 언어만 탭으로 생성
|
||||||
|
const availableLanguages = dataList?.item_list ? Object.keys(dataList.item_list) : ['ko', 'en', 'ja'];
|
||||||
|
|
||||||
|
return availableLanguages.map(langKey => ({
|
||||||
|
key: langKey,
|
||||||
|
label: languageNames[langKey.charAt(0).toUpperCase() + langKey.slice(1)] || langKey.toUpperCase(),
|
||||||
|
children: renderTableForLanguage(langKey)
|
||||||
|
}));
|
||||||
|
}, [dataList, renderTableForLanguage]);
|
||||||
|
|
||||||
|
|
||||||
|
const handleTabChange = (key) => {
|
||||||
|
setActiveLanguage(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
const excelParams = useMemo(() => ({
|
||||||
|
...searchParams,
|
||||||
|
lang: activeLanguage
|
||||||
|
}), [searchParams, activeLanguage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatedPageWrapper>
|
||||||
|
<Title>아이템 백과사전 조회</Title>
|
||||||
|
<FormWrapper>
|
||||||
|
<ItemDictionarySearchBar
|
||||||
|
searchParams={searchParams}
|
||||||
|
onSearch={(newParams, executeSearch = true) => {
|
||||||
|
if (executeSearch) {
|
||||||
|
handleSearch(newParams);
|
||||||
|
} else {
|
||||||
|
updateSearchParams(newParams);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onReset={handleReset}
|
||||||
|
brandData={brandData}
|
||||||
|
/>
|
||||||
|
</FormWrapper>
|
||||||
|
<ViewTableInfo orderType="asc" total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
|
||||||
|
<DownloadContainer>
|
||||||
|
<ExcelExportButton
|
||||||
|
functionName="ItemDictionaryExport"
|
||||||
|
params={excelParams}
|
||||||
|
fileName={t('FILE_DICTIONARY_ITEM')}
|
||||||
|
onLoadingChange={setDownloadState}
|
||||||
|
dataSize={dataList?.total_all}
|
||||||
|
/>
|
||||||
|
{downloadState.loading && (
|
||||||
|
<CircularProgressWrapper>
|
||||||
|
<CircularProgress
|
||||||
|
progress={downloadState.progress}
|
||||||
|
size={36}
|
||||||
|
progressColor="#4A90E2"
|
||||||
|
/>
|
||||||
|
</CircularProgressWrapper>
|
||||||
|
)}
|
||||||
|
</DownloadContainer>
|
||||||
|
</ViewTableInfo>
|
||||||
|
{
|
||||||
|
dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||||
|
<AnimatedTabs
|
||||||
|
items={tabItems}
|
||||||
|
activeKey={activeLanguage}
|
||||||
|
onChange={handleTabChange}
|
||||||
|
tabPosition="left"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</AnimatedPageWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withAuth(authType.itemDictionaryRead)(MetaItemView);
|
||||||
@@ -1,130 +1,149 @@
|
|||||||
import { useState, Fragment } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { TabScroll, Title } from '../../styles/Components';
|
import { TabItem, TabScroll, TabWrapper, Title } from '../../styles/Components';
|
||||||
import { AnimatedPageWrapper } from '../../components/common/Layout'
|
import { AnimatedPageWrapper } from '../../components/common/Layout'
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import UserViewSearchBar from '../../components/searchBar/UserViewSearchBar';
|
import {
|
||||||
import UserDefaultInfo from '../../components/DataManage/UserDefaultInfo';
|
UserDefaultInfo,
|
||||||
import UserAvatarInfo from '../../components/DataManage/UserAvatarInfo';
|
UserAvatarInfo,
|
||||||
import UserDressInfo from '../../components/DataManage/UserDressInfo';
|
UserDressInfo,
|
||||||
import UserToolInfo from '../../components/DataManage/UserToolInfo';
|
UserToolInfo,
|
||||||
import UserInventoryInfo from '../../components/DataManage/UserInventoryInfo';
|
UserInventoryInfo,
|
||||||
import UserMailInfo from '../../components/DataManage/UserMailInfo';
|
UserMailInfo,
|
||||||
import UserMyHomeInfo from '../../components/DataManage/UserMyHomeInfo';
|
UserMyHomeInfo,
|
||||||
import UserFriendInfo from '../../components/DataManage/UserFriendInfo';
|
UserFriendInfo,
|
||||||
import UserTatttooInfo from '../../components/DataManage/UserTattooInfo';
|
UserTattooInfo,
|
||||||
import UserQuestInfo from '../../components/DataManage/UserQuestInfo';
|
UserQuestInfo,
|
||||||
import UserClaimInfo from '../../components/DataManage/UserClaimInfo';
|
UserClaimInfo
|
||||||
|
} from '../../components/DataManage';
|
||||||
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { authList } from '../../store/authList';
|
import { authList } from '../../store/authList';
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
import AuthModal from '../../components/common/modal/AuthModal';
|
|
||||||
import { authType, TabUserList } from '../../assets/data';
|
import { authType, TabUserList } from '../../assets/data';
|
||||||
|
import { UserSearchBar, useUserSearch } from '../../components/searchBar';
|
||||||
|
import { withAuth } from '../../hooks/hook';
|
||||||
|
import { AnimatedTabs } from '../../components/common';
|
||||||
|
|
||||||
const UserView = () => {
|
const UserView = () => {
|
||||||
const userInfo = useRecoilValue(authList);
|
const token = sessionStorage.getItem('token');
|
||||||
|
|
||||||
const [infoView, setInfoView] = useState('none');
|
const [infoView, setInfoView] = useState('none');
|
||||||
const [activeContent, setActiveContent] = useState('기본정보');
|
const [activeTab, setActiveTab] = useState('BASIC');
|
||||||
const [resultData, setResultData] = useState([]);
|
const [resultData, setResultData] = useState();
|
||||||
|
|
||||||
const handleTab = title => {
|
const {
|
||||||
setActiveContent(title);
|
searchParams,
|
||||||
|
loading: dataLoading,
|
||||||
|
data,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
updateSearchParams
|
||||||
|
} = useUserSearch(token);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(data) {
|
||||||
|
setResultData(data);
|
||||||
|
setInfoView('flex');
|
||||||
|
} else {
|
||||||
|
setInfoView('none');
|
||||||
|
}
|
||||||
|
}, [data])
|
||||||
|
|
||||||
|
const tabItems = useMemo(() => {
|
||||||
|
return TabUserList.map(el => ({
|
||||||
|
key: el.value,
|
||||||
|
label: el.name,
|
||||||
|
children: (() => {
|
||||||
|
switch(el.value) {
|
||||||
|
case 'BASIC': return <UserDefaultInfo userInfo={resultData} />;
|
||||||
|
case 'AVATAR': return <UserAvatarInfo userInfo={resultData} />;
|
||||||
|
case 'CLOTH': return <UserDressInfo userInfo={resultData} />;
|
||||||
|
case 'TOOL': return <UserToolInfo userInfo={resultData} />;
|
||||||
|
case 'INVENTORY': return <UserInventoryInfo userInfo={resultData} />;
|
||||||
|
case 'MAIL': return <UserMailInfo userInfo={resultData} />;
|
||||||
|
case 'MYHOME': return <UserMyHomeInfo userInfo={resultData} />;
|
||||||
|
case 'FRIEND': return <UserFriendInfo userInfo={resultData} />;
|
||||||
|
case 'TATTOO': return <UserTattooInfo userInfo={resultData} />;
|
||||||
|
case 'QUEST': return <UserQuestInfo userInfo={resultData} />;
|
||||||
|
case 'CLAIM': return <UserClaimInfo userInfo={resultData} />;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}));
|
||||||
|
}, [resultData]);
|
||||||
|
|
||||||
|
const handleTabChange = (key) => {
|
||||||
|
setActiveTab(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const handleTab = (e, content) => {
|
||||||
|
// e.preventDefault();
|
||||||
|
// setActiveTab(content);
|
||||||
|
// };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AnimatedPageWrapper>
|
||||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.userSearchRead) ? (
|
<Title>유저조회</Title>
|
||||||
<AuthModal />
|
<UserSearchBar
|
||||||
) : (
|
searchParams={searchParams}
|
||||||
<AnimatedPageWrapper>
|
onSearch={(newParams, executeSearch = true) => {
|
||||||
<Title>유저조회</Title>
|
if (executeSearch) {
|
||||||
<UserViewSearchBar setInfoView={setInfoView} resultData={resultData} handleTab={handleTab} setResultData={setResultData} />
|
handleSearch(newParams);
|
||||||
<UserWrapper display={infoView}>
|
} else {
|
||||||
<TabScroll>
|
updateSearchParams(newParams);
|
||||||
<UserTabWrapper>
|
setResultData();
|
||||||
{TabUserList.map((el, idx) => {
|
}
|
||||||
return (
|
}}
|
||||||
<UserTab key={idx} $state={el.title === activeContent ? 'active' : 'unactive'} onClick={() => handleTab(el.title)}>
|
onReset={handleReset}
|
||||||
{el.title}
|
/>
|
||||||
</UserTab>
|
<UserWrapper display={infoView}>
|
||||||
);
|
<AnimatedTabs
|
||||||
})}
|
items={tabItems}
|
||||||
</UserTabWrapper>
|
activeKey={activeTab}
|
||||||
</TabScroll>
|
onChange={handleTabChange}
|
||||||
<UserTabInfo>
|
tabPosition="center"
|
||||||
{activeContent === '기본정보' && <UserDefaultInfo userInfo={resultData && resultData} />}
|
/>
|
||||||
{activeContent === '아바타' && <UserAvatarInfo userInfo={resultData && resultData} />}
|
|
||||||
{activeContent === '의상' && <UserDressInfo userInfo={resultData && resultData} />}
|
{/*<TabScroll>*/}
|
||||||
{activeContent === '도구' && <UserToolInfo userInfo={resultData && resultData} />}
|
{/* <TabWrapper>*/}
|
||||||
{activeContent === '인벤토리' && <UserInventoryInfo userInfo={resultData && resultData} />}
|
{/* {TabUserList.map((el, idx) => {*/}
|
||||||
{activeContent === '우편' && <UserMailInfo userInfo={resultData && resultData} />}
|
{/* return (*/}
|
||||||
{activeContent === '마이홈' && <UserMyHomeInfo userInfo={resultData && resultData} />}
|
{/* <li key={idx}>*/}
|
||||||
{activeContent === '친구목록' && <UserFriendInfo userInfo={resultData && resultData} />}
|
{/* <TabItem $state={activeTab === el.value ? 'active' : 'none'} onClick={e => handleTab(e, el.value)}>*/}
|
||||||
{activeContent === '타투' && <UserTatttooInfo userInfo={resultData && resultData} />}
|
{/* {el.name}*/}
|
||||||
{activeContent === '퀘스트' && <UserQuestInfo userInfo={resultData && resultData} />}
|
{/* </TabItem>*/}
|
||||||
{activeContent === '클레임' && <UserClaimInfo userInfo={resultData && resultData} />}
|
{/* </li>*/}
|
||||||
</UserTabInfo>
|
{/* )*/}
|
||||||
</UserWrapper>
|
{/* })}*/}
|
||||||
</AnimatedPageWrapper>
|
{/* </TabWrapper>*/}
|
||||||
)}
|
{/*</TabScroll>*/}
|
||||||
</>
|
{/*<UserTabInfo>*/}
|
||||||
|
{/* {activeTab === 'BASIC' && <UserDefaultInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'AVATAR' && <UserAvatarInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'CLOTH' && <UserDressInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'TOOL' && <UserToolInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'INVENTORY' && <UserInventoryInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'MAIL' && <UserMailInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'MYHOME' && <UserMyHomeInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'FRIEND' && <UserFriendInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'TATTOO' && <UserTattooInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'QUEST' && <UserQuestInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/* {activeTab === 'CLAIM' && <UserClaimInfo userInfo={resultData && resultData} />}*/}
|
||||||
|
{/*</UserTabInfo>*/}
|
||||||
|
</UserWrapper>
|
||||||
|
</AnimatedPageWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserView;
|
export default withAuth(authType.userSearchRead)(UserView);
|
||||||
|
|
||||||
const UserTab = styled.li`
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 120px;
|
|
||||||
height: 35px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-top-left-radius: 5px;
|
|
||||||
border-top-right-radius: 5px;
|
|
||||||
color: #888;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
font-weight: 700;
|
|
||||||
color: #fff;
|
|
||||||
background: #888;
|
|
||||||
}
|
|
||||||
${props =>
|
|
||||||
props.$state === 'active' &&
|
|
||||||
`font-weight: 700;
|
|
||||||
color: #fff;
|
|
||||||
background: #888;`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const UserTabWrapper = styled.ul`
|
|
||||||
border-bottom: 1px solid #888888;
|
|
||||||
display: flex;
|
|
||||||
background: #fff;
|
|
||||||
`;
|
|
||||||
const UserWrapper = styled.div`
|
const UserWrapper = styled.div`
|
||||||
display: ${props => props.display};
|
display: ${props => props.display};
|
||||||
${props => props.display && `flex-flow:column;`}
|
${props => props.display && `flex-flow:column;`}
|
||||||
min-height: calc(100vh - 345px);
|
min-height: calc(100vh - 345px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #f9f9f9;
|
padding-left: 40px;
|
||||||
`;
|
padding-right: 40px;
|
||||||
|
//background: #f9f9f9;
|
||||||
const UserTabInfo = styled.div`
|
|
||||||
padding: 50px;
|
|
||||||
|
|
||||||
overflow: auto;
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 4px;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: #666666;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: #d9d9d9;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
@@ -1,52 +1,43 @@
|
|||||||
import React, { useState, Fragment } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import {
|
|
||||||
EventDelete,
|
|
||||||
EventDetailView, LogHistory,
|
|
||||||
} from '../../apis';
|
|
||||||
|
|
||||||
import { authList } from '../../store/authList';
|
|
||||||
import { authType, commonStatus, modalTypes, eventStatus } from '../../assets/data';
|
|
||||||
import { Title, FormWrapper, TableStyle, MailTitle, TableWrapper, TextInput, InputItem } from '../../styles/Components';
|
|
||||||
import CheckBox from '../../components/common/input/CheckBox';
|
|
||||||
import Button from '../../components/common/button/Button';
|
|
||||||
import EventDetailModal from '../../components/modal/EventDetailModal';
|
|
||||||
import Pagination from '../../components/common/Pagination/Pagination';
|
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
import DynamicModal from '../../components/common/modal/DynamicModal';
|
import tableInfo from '../../assets/data/pages/eventTable.json'
|
||||||
import AuthModal from '../../components/common/modal/AuthModal';
|
|
||||||
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
|
|
||||||
import { convertKTC, timeDiffMinute } from '../../utils';
|
|
||||||
import {
|
import {
|
||||||
ModalInputItem,
|
authType, commonStatus as CommonStatus,
|
||||||
ModalSubText,
|
} from '../../assets/data';
|
||||||
RegistInputItem,
|
import { Title, FormWrapper} from '../../styles/Components';
|
||||||
StatusLabel, StatusWapper,
|
import {
|
||||||
} from '../../styles/ModuleComponents';
|
Pagination,
|
||||||
import { useLoading } from '../../context/LoadingProvider';
|
CaliTable, TableHeader,
|
||||||
import { useAlert } from '../../context/AlertProvider';
|
} from '../../components/common';
|
||||||
import { CommonSearchBar, useCommonSearch } from '../../components/ServiceManage';
|
|
||||||
import { useModal, useTable } from '../../hooks/hook';
|
|
||||||
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||||
|
import { useDataFetch, useModal, useTable, withAuth } from '../../hooks/hook';
|
||||||
|
import {
|
||||||
|
EventActionView, EventDelete,
|
||||||
|
EventDetailView,
|
||||||
|
LogHistory,
|
||||||
|
} from '../../apis';
|
||||||
|
import { CommonSearchBar } from '../../components/ServiceManage';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
import { alertTypes } from '../../assets/data/types';
|
import { alertTypes } from '../../assets/data/types';
|
||||||
import useCommonSearchOld from '../../hooks/useCommonSearchOld';
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import useEnhancedCommonSearch from '../../hooks/useEnhancedCommonSearch';
|
||||||
import { historyTables } from '../../assets/data/data';
|
import { historyTables } from '../../assets/data/data';
|
||||||
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
||||||
import {AnimatedPageWrapper} from '../../components/common/Layout';
|
import { AnimatedPageWrapper } from '../../components/common/Layout';
|
||||||
|
import EventModal from '../../components/modal/EventModal';
|
||||||
|
|
||||||
const Event = () => {
|
const Event = () => {
|
||||||
const token = sessionStorage.getItem('token');
|
const tableRef = useRef(null);
|
||||||
const userInfo = useRecoilValue(authList);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const {withLoading} = useLoading();
|
|
||||||
const {showToast} = useAlert();
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [detailData, setDetailData] = useState('');
|
const { showToast, showModal } = useAlert();
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
|
||||||
|
const [detailData, setDetailData] = useState({});
|
||||||
const [historyData, setHistoryData] = useState({});
|
const [historyData, setHistoryData] = useState({});
|
||||||
|
const [modalType, setModalType] = useState('regist');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
modalState,
|
modalState,
|
||||||
@@ -54,10 +45,8 @@ const Event = () => {
|
|||||||
handleModalClose
|
handleModalClose
|
||||||
} = useModal({
|
} = useModal({
|
||||||
detail: 'hidden',
|
detail: 'hidden',
|
||||||
delete: 'hidden',
|
history: 'hidden',
|
||||||
history: 'hidden'
|
|
||||||
});
|
});
|
||||||
const [deleteDesc, setDeleteDesc] = useState('');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
config,
|
config,
|
||||||
@@ -65,35 +54,34 @@ const Event = () => {
|
|||||||
data: dataList,
|
data: dataList,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
handleReset,
|
handleReset,
|
||||||
handlePageChange,
|
|
||||||
handlePageSizeChange,
|
|
||||||
handleOrderByChange,
|
handleOrderByChange,
|
||||||
updateSearchParams,
|
updateSearchParams,
|
||||||
configLoaded
|
loading,
|
||||||
} = useCommonSearchOld("eventSearch");
|
configLoaded,
|
||||||
|
handlePageChange,
|
||||||
|
handlePageSizeChange
|
||||||
|
} = useEnhancedCommonSearch("eventSearch");
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: eventActionData
|
||||||
|
} = useDataFetch(() => EventActionView(token), [token]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selectedRows,
|
selectedRows,
|
||||||
handleSelectRow,
|
handleSelectRow,
|
||||||
isRowSelected
|
isRowSelected
|
||||||
} = useTable(dataList?.list || [], {mode: 'single'});
|
} = useTable(dataList?.list || [], {mode: 'single'});
|
||||||
|
|
||||||
// 상세보기 호출
|
const handleAction = async (action, item = null) => {
|
||||||
const handleDetailModal = async (e, id) => {
|
switch (action) {
|
||||||
await EventDetailView(token, id).then(data => {
|
case "regist":
|
||||||
setDetailData(data);
|
setModalType('regist');
|
||||||
});
|
handleModalView('detail');
|
||||||
|
break;
|
||||||
e.preventDefault();
|
|
||||||
handleModalView('detail');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleModalSubmit = async (type, param = null) => {
|
|
||||||
switch (type) {
|
|
||||||
case "history":
|
case "history":
|
||||||
const params = {};
|
const params = {};
|
||||||
params.db_type = "MYSQL"
|
params.db_type = "MYSQL"
|
||||||
params.sql_id = param.id;
|
params.sql_id = item.id;
|
||||||
params.table_name = historyTables.event
|
params.table_name = historyTables.event
|
||||||
|
|
||||||
await LogHistory(token, params).then(data => {
|
await LogHistory(token, params).then(data => {
|
||||||
@@ -101,191 +89,124 @@ const Event = () => {
|
|||||||
handleModalView('history');
|
handleModalView('history');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "delete":
|
case "detail":
|
||||||
const delete_check = selectedRows.every(row => {
|
await EventDetailView(token, item.id).then(data => {
|
||||||
const timeDiff = timeDiffMinute(convertKTC(row.start_dt), (new Date));
|
setDetailData(data.detail);
|
||||||
return row.add_flag || (timeDiff < 30);
|
setModalType('modify');
|
||||||
|
handleModalView('detail');
|
||||||
});
|
});
|
||||||
if(delete_check){
|
break;
|
||||||
showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
|
case "delete":
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleModalView('delete');
|
showModal('EVENT_SELECT_DELETE', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleAction('deleteConfirm')
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case "deleteConfirm":
|
case "deleteConfirm":
|
||||||
let list = [];
|
const low = selectedRows[0];
|
||||||
|
|
||||||
if(deleteDesc.length === 0){
|
if(low.status !== CommonStatus.wait) {
|
||||||
showToast('INPUT_REASON_EMPTY_WARNING', {type: alertTypes.warning});
|
showToast('DELETE_STATUS_ONLY_WAIT', {type: alertTypes.warning});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
delete_desc: deleteDesc
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
handleModalClose('delete');
|
|
||||||
setDeleteDesc('');
|
|
||||||
|
|
||||||
if(isChecked) {
|
|
||||||
showToast('EVENT_WARNING_DELETE', {type: alertTypes.warning});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await withLoading(async () => {
|
await withLoading(async () => {
|
||||||
return await EventDelete(token, list);
|
return await EventDelete(token, low.id);
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
if(data.result === "SUCCESS") {
|
||||||
}).catch(error => {
|
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('DELETE_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
handleSearch(updateSearchParams);
|
handleSearch(updateSearchParams);
|
||||||
})
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AnimatedPageWrapper>
|
||||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventRead) ? (
|
<Title>통합 이벤트 관리</Title>
|
||||||
<AuthModal />
|
|
||||||
) : (
|
|
||||||
<AnimatedPageWrapper>
|
|
||||||
<Title>출석 보상 이벤트 관리</Title>
|
|
||||||
<FormWrapper>
|
|
||||||
<CommonSearchBar
|
|
||||||
config={config}
|
|
||||||
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}>
|
|
||||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventDelete) && (
|
|
||||||
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
|
||||||
)}
|
|
||||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
|
|
||||||
<Button
|
|
||||||
theme="primary"
|
|
||||||
text="이벤트 등록"
|
|
||||||
type="button"
|
|
||||||
handleClick={e => {
|
|
||||||
e.preventDefault();
|
|
||||||
navigate('/servicemanage/event/eventregist');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ViewTableInfo>
|
|
||||||
<TableWrapper>
|
|
||||||
<TableStyle>
|
|
||||||
<caption></caption>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="40">
|
|
||||||
</th>
|
|
||||||
<th width="80">번호</th>
|
|
||||||
<th width="100">이벤트 상태</th>
|
|
||||||
<th width="210">시작 일시</th>
|
|
||||||
<th width="210">종료 일시</th>
|
|
||||||
<th>우편 제목</th>
|
|
||||||
<th width="110">확인 / 수정</th>
|
|
||||||
<th width="200">히스토리</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{dataList?.list?.map(event => (
|
|
||||||
<Fragment key={event.row_num}>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<CheckBox name={'select'} id={event.id}
|
|
||||||
setData={(e) => handleSelectRow(e, event)}
|
|
||||||
checked={isRowSelected(event.id)} />
|
|
||||||
</td>
|
|
||||||
<td>{event.row_num}</td>
|
|
||||||
<StatusWapper>
|
|
||||||
<StatusLabel $status={event.status}>
|
|
||||||
{eventStatus.map(data => data.value === event.status && data.name)}
|
|
||||||
</StatusLabel>
|
|
||||||
</StatusWapper>
|
|
||||||
<td>{convertKTC(event.start_dt)}</td>
|
|
||||||
<td>{convertKTC(event.end_dt)}</td>
|
|
||||||
<MailTitle>{event.title}</MailTitle>
|
|
||||||
<td>
|
|
||||||
<Button theme="line" text="상세보기"
|
|
||||||
handleClick={e => handleDetailModal(e, event.id)} />
|
|
||||||
</td>
|
|
||||||
<td><Button theme="line" text="히스토리"
|
|
||||||
handleClick={e => handleModalSubmit('history', event)} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</TableStyle>
|
|
||||||
</TableWrapper>
|
|
||||||
|
|
||||||
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
{/* 조회조건 */}
|
||||||
|
<FormWrapper>
|
||||||
|
<CommonSearchBar
|
||||||
|
config={config}
|
||||||
|
searchParams={searchParams}
|
||||||
|
onSearch={(newParams, executeSearch = true) => {
|
||||||
|
if (executeSearch) {
|
||||||
|
handleSearch(newParams);
|
||||||
|
} else {
|
||||||
|
updateSearchParams(newParams);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
</FormWrapper>
|
||||||
|
|
||||||
{/*상세*/}
|
{/* 조회헤더 */}
|
||||||
<EventDetailModal
|
<TableHeader
|
||||||
detailView={modalState.detailModal}
|
config={tableInfo.header}
|
||||||
handleDetailView={() =>{
|
total={dataList?.total}
|
||||||
handleModalClose('detail');
|
total_all={dataList?.total_all}
|
||||||
handleSearch(updateSearchParams);
|
handleOrderBy={handleOrderByChange}
|
||||||
}}
|
handlePageSize={handlePageSizeChange}
|
||||||
content={detailData}
|
selectedRows={selectedRows}
|
||||||
setDetailData={setDetailData}
|
onAction={handleAction}
|
||||||
/>
|
navigate={navigate}
|
||||||
|
/>
|
||||||
|
|
||||||
<LogDetailModal
|
{/* 조회테이블 */}
|
||||||
viewMode="changed"
|
<CaliTable
|
||||||
detailView={modalState.historyModal}
|
columns={tableInfo.columns}
|
||||||
handleDetailView={() => handleModalClose('history')}
|
data={dataList?.list}
|
||||||
changedData={historyData}
|
selectedRows={selectedRows}
|
||||||
title="히스토리"
|
onSelectRow={handleSelectRow}
|
||||||
/>
|
onAction={handleAction}
|
||||||
|
refProp={tableRef}
|
||||||
|
loading={loading}
|
||||||
|
isRowSelected={isRowSelected}
|
||||||
|
/>
|
||||||
|
|
||||||
<DynamicModal
|
{/* 페이징 */}
|
||||||
modalType={modalTypes.childOkCancel}
|
<Pagination
|
||||||
view={modalState.deleteModal}
|
postsPerPage={searchParams?.pageSize}
|
||||||
handleCancel={() => handleModalClose('delete')}
|
totalPosts={dataList?.total_all}
|
||||||
handleSubmit={() => handleModalSubmit('deleteConfirm')}
|
setCurrentPage={handlePageChange}
|
||||||
>
|
currentPage={searchParams?.currentPage}
|
||||||
<ModalInputItem>
|
pageLimit={INITIAL_PAGE_LIMIT}
|
||||||
{t('EVENT_SELECT_DELETE')}
|
/>
|
||||||
<RegistInputItem>
|
|
||||||
<TextInput
|
{/* 상세 */}
|
||||||
placeholder="사유 입력"
|
<EventModal
|
||||||
maxLength="30"
|
modalType={modalType}
|
||||||
value={deleteDesc}
|
detailView={modalState.detailModal}
|
||||||
onChange={e => {
|
handleDetailView={() =>{
|
||||||
if (e.target.value.length > 30) return;
|
handleModalClose('detail');
|
||||||
setDeleteDesc(e.target.value.trimStart())
|
handleSearch(updateSearchParams);
|
||||||
}}
|
}}
|
||||||
/>
|
content={detailData}
|
||||||
</RegistInputItem>
|
setDetailData={setDetailData}
|
||||||
<ModalSubText $color={deleteDesc.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30자)</ModalSubText>
|
eventActionData={eventActionData}
|
||||||
</ModalInputItem>
|
/>
|
||||||
</DynamicModal>
|
|
||||||
</AnimatedPageWrapper>
|
<LogDetailModal
|
||||||
)}
|
viewMode="changed"
|
||||||
</>
|
detailView={modalState.historyModal}
|
||||||
);
|
handleDetailView={() => handleModalClose('history')}
|
||||||
|
changedData={historyData}
|
||||||
|
title="히스토리"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</AnimatedPageWrapper>
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Event;
|
export default withAuth(authType.worldEventRead)(Event);
|
||||||
|
|||||||
218
src/pages/ServiceManage/Ranking.js
Normal file
218
src/pages/ServiceManage/Ranking.js
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
|
|
||||||
|
import {
|
||||||
|
authType, commonStatus as CommonStatus,
|
||||||
|
} from '../../assets/data';
|
||||||
|
import { Title, FormWrapper} from '../../styles/Components';
|
||||||
|
import {
|
||||||
|
Pagination,
|
||||||
|
CaliTable, TableHeader,
|
||||||
|
} from '../../components/common';
|
||||||
|
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||||
|
import { useDataFetch, useModal, useTable, withAuth } from '../../hooks/hook';
|
||||||
|
import {
|
||||||
|
EventActionView,
|
||||||
|
LogHistory,
|
||||||
|
RankingDataView, RankingScheduleDelete,
|
||||||
|
RankingScheduleDetailView,
|
||||||
|
} from '../../apis';
|
||||||
|
import { CommonSearchBar } from '../../components/ServiceManage';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import useEnhancedCommonSearch from '../../hooks/useEnhancedCommonSearch';
|
||||||
|
import { historyTables } from '../../assets/data/data';
|
||||||
|
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
||||||
|
import { AnimatedPageWrapper } from '../../components/common/Layout';
|
||||||
|
import tableInfo from '../../assets/data/pages/rankingTable.json'
|
||||||
|
import RankingModal from '../../components/modal/RankingModal';
|
||||||
|
|
||||||
|
const Ranking = () => {
|
||||||
|
const tableRef = useRef(null);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { showToast, showModal } = useAlert();
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
|
||||||
|
const [detailData, setDetailData] = useState({});
|
||||||
|
const [historyData, setHistoryData] = useState({});
|
||||||
|
const [modalType, setModalType] = useState('regist');
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalState,
|
||||||
|
handleModalView,
|
||||||
|
handleModalClose
|
||||||
|
} = useModal({
|
||||||
|
detail: 'hidden',
|
||||||
|
history: 'hidden',
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
config,
|
||||||
|
searchParams,
|
||||||
|
data: dataList,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handleOrderByChange,
|
||||||
|
updateSearchParams,
|
||||||
|
loading,
|
||||||
|
configLoaded,
|
||||||
|
handlePageChange,
|
||||||
|
handlePageSizeChange
|
||||||
|
} = useEnhancedCommonSearch("rankingSearch");
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: rankingData
|
||||||
|
} = useDataFetch(() => RankingDataView(token), [token]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: eventActionData
|
||||||
|
} = useDataFetch(() => EventActionView(token), [token]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
selectedRows,
|
||||||
|
handleSelectRow,
|
||||||
|
isRowSelected
|
||||||
|
} = useTable(dataList?.list || [], {mode: 'single'});
|
||||||
|
|
||||||
|
const handleAction = async (action, item = null) => {
|
||||||
|
switch (action) {
|
||||||
|
case "regist":
|
||||||
|
setModalType('regist');
|
||||||
|
handleModalView('detail');
|
||||||
|
break;
|
||||||
|
case "history":
|
||||||
|
const params = {};
|
||||||
|
params.db_type = "MYSQL"
|
||||||
|
params.sql_id = item.id;
|
||||||
|
params.table_name = historyTables.rankingSchedule
|
||||||
|
|
||||||
|
await LogHistory(token, params).then(data => {
|
||||||
|
setHistoryData(data);
|
||||||
|
handleModalView('history');
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "detail":
|
||||||
|
await RankingScheduleDetailView(token, item.id).then(data => {
|
||||||
|
setDetailData(data.detail);
|
||||||
|
setModalType('modify');
|
||||||
|
handleModalView('detail');
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "delete":
|
||||||
|
|
||||||
|
showModal('SCHEDULE_SELECT_DELETE', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleAction('deleteConfirm')
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "deleteConfirm":
|
||||||
|
const low = selectedRows[0];
|
||||||
|
|
||||||
|
if(low.status !== CommonStatus.wait) {
|
||||||
|
showToast('DELETE_STATUS_ONLY_WAIT', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await withLoading(async () => {
|
||||||
|
return await RankingScheduleDelete(token, low.id);
|
||||||
|
}).then(data => {
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('DELETE_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
handleSearch(updateSearchParams);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatedPageWrapper>
|
||||||
|
<Title>랭킹 스케줄러</Title>
|
||||||
|
|
||||||
|
{/* 조회조건 */}
|
||||||
|
<FormWrapper>
|
||||||
|
<CommonSearchBar
|
||||||
|
config={config}
|
||||||
|
searchParams={searchParams}
|
||||||
|
onSearch={(newParams, executeSearch = true) => {
|
||||||
|
if (executeSearch) {
|
||||||
|
handleSearch(newParams);
|
||||||
|
} else {
|
||||||
|
updateSearchParams(newParams);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
</FormWrapper>
|
||||||
|
|
||||||
|
{/* 조회헤더 */}
|
||||||
|
<TableHeader
|
||||||
|
config={tableInfo.header}
|
||||||
|
total={dataList?.total}
|
||||||
|
total_all={dataList?.total_all}
|
||||||
|
handleOrderBy={handleOrderByChange}
|
||||||
|
handlePageSize={handlePageSizeChange}
|
||||||
|
selectedRows={selectedRows}
|
||||||
|
onAction={handleAction}
|
||||||
|
navigate={navigate}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 조회테이블 */}
|
||||||
|
<CaliTable
|
||||||
|
columns={tableInfo.columns}
|
||||||
|
data={dataList?.list}
|
||||||
|
selectedRows={selectedRows}
|
||||||
|
onSelectRow={handleSelectRow}
|
||||||
|
onAction={handleAction}
|
||||||
|
refProp={tableRef}
|
||||||
|
loading={loading}
|
||||||
|
isRowSelected={isRowSelected}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 페이징 */}
|
||||||
|
<Pagination
|
||||||
|
postsPerPage={searchParams?.pageSize}
|
||||||
|
totalPosts={dataList?.total_all}
|
||||||
|
setCurrentPage={handlePageChange}
|
||||||
|
currentPage={searchParams?.currentPage}
|
||||||
|
pageLimit={INITIAL_PAGE_LIMIT}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 상세 */}
|
||||||
|
<RankingModal
|
||||||
|
modalType={modalType}
|
||||||
|
detailView={modalState.detailModal}
|
||||||
|
handleDetailView={() =>{
|
||||||
|
handleModalClose('detail');
|
||||||
|
handleSearch(updateSearchParams);
|
||||||
|
}}
|
||||||
|
content={detailData}
|
||||||
|
setDetailData={setDetailData}
|
||||||
|
rankingData={rankingData}
|
||||||
|
eventActionData={eventActionData}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LogDetailModal
|
||||||
|
viewMode="changed"
|
||||||
|
detailView={modalState.historyModal}
|
||||||
|
handleDetailView={() => handleModalClose('history')}
|
||||||
|
changedData={historyData}
|
||||||
|
title="히스토리"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</AnimatedPageWrapper>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withAuth(authType.rankingRead)(Ranking);
|
||||||
291
src/pages/ServiceManage/RewardEvent.js
Normal file
291
src/pages/ServiceManage/RewardEvent.js
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
import React, { useState, Fragment } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import {
|
||||||
|
RewardEventDelete,
|
||||||
|
RewardEventDetailView, LogHistory,
|
||||||
|
} from '../../apis';
|
||||||
|
|
||||||
|
import { authList } from '../../store/authList';
|
||||||
|
import { authType, commonStatus, modalTypes, eventStatus } from '../../assets/data';
|
||||||
|
import { Title, FormWrapper, TableStyle, MailTitle, TableWrapper, TextInput, InputItem } from '../../styles/Components';
|
||||||
|
import CheckBox from '../../components/common/input/CheckBox';
|
||||||
|
import Button from '../../components/common/button/Button';
|
||||||
|
import RewardEventDetailModal from '../../components/modal/RewardEventDetailModal';
|
||||||
|
import Pagination from '../../components/common/Pagination/Pagination';
|
||||||
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
|
import DynamicModal from '../../components/common/modal/DynamicModal';
|
||||||
|
import AuthModal from '../../components/common/modal/AuthModal';
|
||||||
|
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
|
||||||
|
import { convertKTC, timeDiffMinute } from '../../utils';
|
||||||
|
import {
|
||||||
|
ModalInputItem,
|
||||||
|
ModalSubText,
|
||||||
|
RegistInputItem,
|
||||||
|
StatusLabel, StatusWapper,
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { CommonSearchBar, useCommonSearch } from '../../components/ServiceManage';
|
||||||
|
import { useModal, useTable } from '../../hooks/hook';
|
||||||
|
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||||
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import useCommonSearchOld from '../../hooks/useCommonSearchOld';
|
||||||
|
import { historyTables } from '../../assets/data/data';
|
||||||
|
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
||||||
|
import {AnimatedPageWrapper} from '../../components/common/Layout';
|
||||||
|
|
||||||
|
const RewardEvent = () => {
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const userInfo = useRecoilValue(authList);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
const {showToast} = useAlert();
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [detailData, setDetailData] = useState('');
|
||||||
|
const [historyData, setHistoryData] = useState({});
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalState,
|
||||||
|
handleModalView,
|
||||||
|
handleModalClose
|
||||||
|
} = useModal({
|
||||||
|
detail: 'hidden',
|
||||||
|
delete: 'hidden',
|
||||||
|
history: 'hidden'
|
||||||
|
});
|
||||||
|
const [deleteDesc, setDeleteDesc] = useState('');
|
||||||
|
|
||||||
|
const {
|
||||||
|
config,
|
||||||
|
searchParams,
|
||||||
|
data: dataList,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
handlePageSizeChange,
|
||||||
|
handleOrderByChange,
|
||||||
|
updateSearchParams,
|
||||||
|
configLoaded
|
||||||
|
} = useCommonSearchOld("rewardEventSearch");
|
||||||
|
const {
|
||||||
|
selectedRows,
|
||||||
|
handleSelectRow,
|
||||||
|
isRowSelected
|
||||||
|
} = useTable(dataList?.list || [], {mode: 'single'});
|
||||||
|
|
||||||
|
// 상세보기 호출
|
||||||
|
const handleDetailModal = async (e, id) => {
|
||||||
|
await RewardEventDetailView(token, id).then(data => {
|
||||||
|
setDetailData(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
handleModalView('detail');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleModalSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "history":
|
||||||
|
const params = {};
|
||||||
|
params.db_type = "MYSQL"
|
||||||
|
params.sql_id = param.id;
|
||||||
|
params.table_name = historyTables.rewardEvent
|
||||||
|
|
||||||
|
await LogHistory(token, params).then(data => {
|
||||||
|
setHistoryData(data);
|
||||||
|
handleModalView('history');
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "delete":
|
||||||
|
const delete_check = selectedRows.every(row => {
|
||||||
|
const timeDiff = timeDiffMinute(convertKTC(row.start_dt), (new Date));
|
||||||
|
return row.add_flag || (timeDiff < 30);
|
||||||
|
});
|
||||||
|
if(delete_check){
|
||||||
|
showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleModalView('delete');
|
||||||
|
break;
|
||||||
|
case "deleteConfirm":
|
||||||
|
let list = [];
|
||||||
|
|
||||||
|
if(deleteDesc.length === 0){
|
||||||
|
showToast('INPUT_REASON_EMPTY_WARNING', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
delete_desc: deleteDesc
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
handleModalClose('delete');
|
||||||
|
setDeleteDesc('');
|
||||||
|
|
||||||
|
if(isChecked) {
|
||||||
|
showToast('EVENT_WARNING_DELETE', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await withLoading(async () => {
|
||||||
|
return await RewardEventDelete(token, list);
|
||||||
|
}).then(data => {
|
||||||
|
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||||
|
}).catch(error => {
|
||||||
|
|
||||||
|
}).finally(() => {
|
||||||
|
handleSearch(updateSearchParams);
|
||||||
|
})
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventRead) ? (
|
||||||
|
<AuthModal />
|
||||||
|
) : (
|
||||||
|
<AnimatedPageWrapper>
|
||||||
|
<Title>출석 보상 이벤트 관리</Title>
|
||||||
|
<FormWrapper>
|
||||||
|
<CommonSearchBar
|
||||||
|
config={config}
|
||||||
|
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}>
|
||||||
|
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventDelete) && (
|
||||||
|
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
||||||
|
)}
|
||||||
|
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
|
||||||
|
<Button
|
||||||
|
theme="primary"
|
||||||
|
text="이벤트 등록"
|
||||||
|
type="button"
|
||||||
|
handleClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
navigate('/servicemanage/rewardevent/eventregist');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ViewTableInfo>
|
||||||
|
<TableWrapper>
|
||||||
|
<TableStyle>
|
||||||
|
<caption></caption>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="40">
|
||||||
|
</th>
|
||||||
|
<th width="80">번호</th>
|
||||||
|
<th width="100">이벤트 상태</th>
|
||||||
|
<th width="210">시작 일시</th>
|
||||||
|
<th width="210">종료 일시</th>
|
||||||
|
<th>우편 제목</th>
|
||||||
|
<th width="110">확인 / 수정</th>
|
||||||
|
<th width="200">히스토리</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{dataList?.list?.map(event => (
|
||||||
|
<Fragment key={event.row_num}>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<CheckBox name={'select'} id={event.id}
|
||||||
|
setData={(e) => handleSelectRow(e, event)}
|
||||||
|
checked={isRowSelected(event.id)} />
|
||||||
|
</td>
|
||||||
|
<td>{event.row_num}</td>
|
||||||
|
<StatusWapper>
|
||||||
|
<StatusLabel $status={event.status}>
|
||||||
|
{eventStatus.map(data => data.value === event.status && data.name)}
|
||||||
|
</StatusLabel>
|
||||||
|
</StatusWapper>
|
||||||
|
<td>{convertKTC(event.start_dt)}</td>
|
||||||
|
<td>{convertKTC(event.end_dt)}</td>
|
||||||
|
<MailTitle>{event.title}</MailTitle>
|
||||||
|
<td>
|
||||||
|
<Button theme="line" text="상세보기"
|
||||||
|
handleClick={e => handleDetailModal(e, event.id)} />
|
||||||
|
</td>
|
||||||
|
<td><Button theme="line" text="히스토리"
|
||||||
|
handleClick={e => handleModalSubmit('history', event)} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</TableStyle>
|
||||||
|
</TableWrapper>
|
||||||
|
|
||||||
|
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
||||||
|
|
||||||
|
{/*상세*/}
|
||||||
|
<RewardEventDetailModal
|
||||||
|
detailView={modalState.detailModal}
|
||||||
|
handleDetailView={() =>{
|
||||||
|
handleModalClose('detail');
|
||||||
|
handleSearch(updateSearchParams);
|
||||||
|
}}
|
||||||
|
content={detailData}
|
||||||
|
setDetailData={setDetailData}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LogDetailModal
|
||||||
|
viewMode="changed"
|
||||||
|
detailView={modalState.historyModal}
|
||||||
|
handleDetailView={() => handleModalClose('history')}
|
||||||
|
changedData={historyData}
|
||||||
|
title="히스토리"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.childOkCancel}
|
||||||
|
view={modalState.deleteModal}
|
||||||
|
handleCancel={() => handleModalClose('delete')}
|
||||||
|
handleSubmit={() => handleModalSubmit('deleteConfirm')}
|
||||||
|
>
|
||||||
|
<ModalInputItem>
|
||||||
|
{t('EVENT_SELECT_DELETE')}
|
||||||
|
<RegistInputItem>
|
||||||
|
<TextInput
|
||||||
|
placeholder="사유 입력"
|
||||||
|
maxLength="30"
|
||||||
|
value={deleteDesc}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.value.length > 30) return;
|
||||||
|
setDeleteDesc(e.target.value.trimStart())
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</RegistInputItem>
|
||||||
|
<ModalSubText $color={deleteDesc.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30자)</ModalSubText>
|
||||||
|
</ModalInputItem>
|
||||||
|
</DynamicModal>
|
||||||
|
</AnimatedPageWrapper>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RewardEvent;
|
||||||
467
src/pages/ServiceManage/RewardEventRegist.js
Normal file
467
src/pages/ServiceManage/RewardEventRegist.js
Normal file
@@ -0,0 +1,467 @@
|
|||||||
|
import React, { useState, Fragment, useEffect } from 'react';
|
||||||
|
import Button from '../../components/common/button/Button';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
BtnWrapper,
|
||||||
|
TextInput,
|
||||||
|
SelectInput,
|
||||||
|
Label,
|
||||||
|
InputLabel,
|
||||||
|
Textarea,
|
||||||
|
SearchBarAlert,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { EventIsItem, RewardEventSingleRegist } from '../../apis';
|
||||||
|
|
||||||
|
import { authList } from '../../store/authList';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
AppendRegistBox, AppendRegistTable, AreaBtnClose,
|
||||||
|
BtnDelete,
|
||||||
|
Item,
|
||||||
|
ItemList, LangArea, ModalItem, ModalItemList, RegistGroup,
|
||||||
|
RegistInputItem,
|
||||||
|
RegistInputRow, RegistNotice, RegistTable,
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import AuthModal from '../../components/common/modal/AuthModal';
|
||||||
|
import { authType, benItems, currencyItemCode } from '../../assets/data';
|
||||||
|
import DateTimeInput from '../../components/common/input/DateTimeInput';
|
||||||
|
import { timeDiffMinute } from '../../utils';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { AnimatedPageWrapper } from '../../components/common/Layout';
|
||||||
|
|
||||||
|
const RewardEventRegist = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const userInfo = useRecoilValue(authList);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const { showToast, showModal } = useAlert();
|
||||||
|
const { withLoading} = useLoading();
|
||||||
|
|
||||||
|
const [item, setItem] = useState(''); // 아이템 값
|
||||||
|
const [itemCount, setItemCount] = useState(''); // 아이템 개수
|
||||||
|
const [resource, setResource] = useState(currencyCodeTypes.gold); // 자원 값
|
||||||
|
const [resourceCount, setResourceCount] = useState(''); // 자원 개수
|
||||||
|
|
||||||
|
const [isNullValue, setIsNullValue] = useState(false);
|
||||||
|
const [btnValidation, setBtnValidation] = useState(false);
|
||||||
|
const [itemCheckMsg, setItemCheckMsg] = useState('');
|
||||||
|
|
||||||
|
const [time, setTime] = useState({
|
||||||
|
start_hour: '00',
|
||||||
|
start_min: '00',
|
||||||
|
end_hour: '00',
|
||||||
|
end_min: '00',
|
||||||
|
}); //시간 정보
|
||||||
|
|
||||||
|
const [resultData, setResultData] = useState({
|
||||||
|
is_reserve: false,
|
||||||
|
start_dt: '',
|
||||||
|
end_dt: '',
|
||||||
|
event_type: 'ATTD',
|
||||||
|
mail_list: [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
language: 'KO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
language: 'EN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
language: 'JA',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
item_list: [],
|
||||||
|
guid: '',
|
||||||
|
}); //데이터 정보
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (checkCondition()) {
|
||||||
|
setIsNullValue(false);
|
||||||
|
} else {
|
||||||
|
setIsNullValue(true);
|
||||||
|
}
|
||||||
|
}, [resultData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setItemCheckMsg('');
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
|
const combineDateTime = (date, hour, min) => {
|
||||||
|
if (!date) return null;
|
||||||
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hour, min);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 날짜 처리
|
||||||
|
const handleDateChange = (data, type) => {
|
||||||
|
const date = new Date(data);
|
||||||
|
setResultData({
|
||||||
|
...resultData,
|
||||||
|
[`${type}_dt`]: combineDateTime(date, time[`${type}_hour`], time[`${type}_min`]),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 시간 처리
|
||||||
|
const handleTimeChange = (e, type) => {
|
||||||
|
const { id, value } = e.target;
|
||||||
|
const newTime = { ...time, [`${type}_${id}`]: value };
|
||||||
|
setTime(newTime);
|
||||||
|
|
||||||
|
const date = resultData[`${type}_dt`] ? resultData[`${type}_dt`] : new Date();
|
||||||
|
|
||||||
|
setResultData({
|
||||||
|
...resultData,
|
||||||
|
[`${type}_dt`]: combineDateTime(date, newTime[`${type}_hour`], newTime[`${type}_min`]),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 수량 숫자 체크
|
||||||
|
const handleItemCount = e => {
|
||||||
|
if (e.target.value === '0' || e.target.value === '-0') {
|
||||||
|
setItemCount('1');
|
||||||
|
e.target.value = '1';
|
||||||
|
} else if (e.target.value < 0) {
|
||||||
|
let plusNum = Math.abs(e.target.value);
|
||||||
|
setItemCount(plusNum);
|
||||||
|
} else {
|
||||||
|
setItemCount(e.target.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 추가
|
||||||
|
const handleItemList = async () => {
|
||||||
|
if(benItems.includes(item)){
|
||||||
|
showToast('MAIL_ITEM_ADD_BEN', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(item.length === 0 || itemCount.length === 0) return;
|
||||||
|
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const result = await EventIsItem(token, {item: item});
|
||||||
|
|
||||||
|
if(result.data.result === "ERROR"){
|
||||||
|
setItemCheckMsg(t('NOT_ITEM'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemIndex = resultData.item_list.findIndex((data) => data.item === item);
|
||||||
|
if (itemIndex !== -1) {
|
||||||
|
showToast('MAIL_ITEM_ADD_DUPL', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newItem = { item: item, item_cnt: itemCount, item_name: result.data.data.item_info.item_name };
|
||||||
|
|
||||||
|
resultData.item_list.push(newItem);
|
||||||
|
setItem('');
|
||||||
|
setItemCount('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 추가된 아이템 삭제
|
||||||
|
const onItemRemove = id => {
|
||||||
|
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
|
||||||
|
setResultData({ ...resultData, item_list: filterList });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 입력창 삭제
|
||||||
|
const onLangDelete = language => {
|
||||||
|
let filterList = resultData.mail_list && resultData.mail_list.filter(el => el.language !== language);
|
||||||
|
|
||||||
|
if (filterList.length === 1) {
|
||||||
|
setBtnValidation(true);
|
||||||
|
} else {
|
||||||
|
setBtnValidation(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setResultData({ ...resultData, mail_list: filterList });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자원 수량 숫자 체크
|
||||||
|
const handleResourceCount = e => {
|
||||||
|
if (e.target.value === '0' || e.target.value === '-0') {
|
||||||
|
setResourceCount('1');
|
||||||
|
e.target.value = '1';
|
||||||
|
} else if (e.target.value < 0) {
|
||||||
|
let plusNum = Math.abs(e.target.value);
|
||||||
|
setResourceCount(plusNum);
|
||||||
|
} else {
|
||||||
|
setResourceCount(e.target.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자원 추가
|
||||||
|
const handleResourceList = (e) => {
|
||||||
|
if(resource.length === 0 || resourceCount.length === 0) return;
|
||||||
|
|
||||||
|
const itemIndex = resultData.item_list.findIndex(
|
||||||
|
(item) => item.item === resource
|
||||||
|
);
|
||||||
|
|
||||||
|
if (itemIndex !== -1) {
|
||||||
|
const item_cnt = resultData.item_list[itemIndex].item_cnt;
|
||||||
|
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
|
||||||
|
} else {
|
||||||
|
const name = currencyItemCode.find(well => well.value === resource).name;
|
||||||
|
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
|
||||||
|
resultData.item_list.push(newItem);
|
||||||
|
}
|
||||||
|
setResourceCount('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
if (!checkCondition()) return;
|
||||||
|
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date))
|
||||||
|
if(timeDiff < 60) {
|
||||||
|
showToast('EVENT_TIME_LIMIT_ADD', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showModal('', {
|
||||||
|
type: alertTypes.confirmChildren,
|
||||||
|
onConfirm: () => handleSubmit('registConfirm'),
|
||||||
|
children: <ModalItem>
|
||||||
|
{t('EVENT_REGIST_CONFIRM')}
|
||||||
|
{resultData.item_list && (
|
||||||
|
<ModalItemList>
|
||||||
|
{resultData.item_list.map((data, index) => {
|
||||||
|
return (
|
||||||
|
<Item key={index}>
|
||||||
|
<span>
|
||||||
|
{data.item_name} {data.item_cnt.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
</Item>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ModalItemList>
|
||||||
|
)}
|
||||||
|
</ModalItem>
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "registConfirm":
|
||||||
|
await withLoading(async () => {
|
||||||
|
return await RewardEventSingleRegist(token, resultData);
|
||||||
|
}).then((result) => {
|
||||||
|
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||||
|
}).catch(() => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
callbackPage();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbackPage = () => {
|
||||||
|
navigate('/servicemanage/rewardevent');
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCondition = () => {
|
||||||
|
return (
|
||||||
|
resultData.mail_list.every(data => data.content !== '' && data.title !== '') &&
|
||||||
|
(resultData.start_dt.length !== 0) &&
|
||||||
|
(resultData.end_dt.length !== 0)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatedPageWrapper>
|
||||||
|
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) ? (
|
||||||
|
<AuthModal/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Title>이벤트 등록</Title>
|
||||||
|
<RegistGroup>
|
||||||
|
<RegistInputRow>
|
||||||
|
<RegistInputItem>
|
||||||
|
<InputLabel>이벤트 타입</InputLabel>
|
||||||
|
<SelectInput onChange={e => setResultData({ ...resultData, event_type: e.target.value })} value={resultData.event_type}>
|
||||||
|
<option value="ATTD">출석 이벤트</option>
|
||||||
|
</SelectInput>
|
||||||
|
</RegistInputItem>
|
||||||
|
<DateTimeInput
|
||||||
|
title="시작 시간"
|
||||||
|
dateName="시작 일자"
|
||||||
|
selectedDate={resultData.start_dt}
|
||||||
|
handleSelectedDate={data => handleDateChange(data, 'start')}
|
||||||
|
onChange={e => handleTimeChange(e, 'start')}
|
||||||
|
/>
|
||||||
|
<DateTimeInput
|
||||||
|
title="종료 시간"
|
||||||
|
dateName="종료 일자"
|
||||||
|
selectedDate={resultData.end_dt}
|
||||||
|
handleSelectedDate={data => handleDateChange(data, 'end')}
|
||||||
|
onChange={e => handleTimeChange(e, 'end')}
|
||||||
|
/>
|
||||||
|
</RegistInputRow>
|
||||||
|
</RegistGroup>
|
||||||
|
{resultData.mail_list.map((data, idx) => {
|
||||||
|
return (
|
||||||
|
<Fragment key={idx}>
|
||||||
|
<AppendRegistBox>
|
||||||
|
<LangArea>
|
||||||
|
언어 : {data.language}
|
||||||
|
{btnValidation === false ? (
|
||||||
|
<AreaBtnClose
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
onLangDelete(data.language);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<AreaBtnClose opacity="10%" />
|
||||||
|
)}
|
||||||
|
</LangArea>
|
||||||
|
<RegistTable>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th width="120">
|
||||||
|
<Label>제목</Label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<RegistInputItem>
|
||||||
|
<TextInput
|
||||||
|
placeholder="우편 제목 입력"
|
||||||
|
maxLength="30"
|
||||||
|
id={data.language}
|
||||||
|
value={data.title}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.value.length > 30) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let list = [...resultData.mail_list];
|
||||||
|
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
|
||||||
|
list[findIndex].title = e.target.value.trimStart();
|
||||||
|
|
||||||
|
setResultData({ ...resultData, mail_list: list });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</RegistInputItem>
|
||||||
|
<RegistNotice $color={data.title.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.title.length}/30자)</RegistNotice>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<Label>내용</Label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<Textarea
|
||||||
|
maxLength="2000"
|
||||||
|
value={data.content}
|
||||||
|
id={data.language}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.value.length > 2000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let list = [...resultData.mail_list];
|
||||||
|
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
|
||||||
|
list[findIndex].content = e.target.value.trimStart();
|
||||||
|
|
||||||
|
setResultData({ ...resultData, mail_list: list });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RegistNotice $color={data.content.length > 1999 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.content.length}/2000자)</RegistNotice>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</RegistTable>
|
||||||
|
</AppendRegistBox>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<AppendRegistBox>
|
||||||
|
<AppendRegistTable>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th width="120">
|
||||||
|
<Label>아이템 첨부</Label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<RegistInputItem>
|
||||||
|
<TextInput placeholder="Item Meta id 입력" value={item} onChange={e => setItem(e.target.value.trimStart())} />
|
||||||
|
<TextInput placeholder="수량" type="number" value={itemCount} onChange={e => handleItemCount(e)} width="100px" />
|
||||||
|
<Button text="추가" theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'} handleClick={handleItemList} width="100px" height="35px" />
|
||||||
|
{itemCheckMsg && <SearchBarAlert>{itemCheckMsg}</SearchBarAlert>}
|
||||||
|
</RegistInputItem>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th width="120">
|
||||||
|
<Label>자원 첨부</Label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<RegistInputItem>
|
||||||
|
<SelectInput onChange={e => setResource(e.target.value)} value={resource}>
|
||||||
|
{currencyItemCode.filter(data => data.value !== currencyCodeTypes.calium).map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
<TextInput placeholder="수량" type="number" value={resourceCount} onChange={e => handleResourceCount(e)} width="200px" />
|
||||||
|
<Button text="추가" theme={resourceCount.length === 0 || resource.length === 0 ? 'disable' : 'search'} handleClick={handleResourceList} width="100px" height="35px" />
|
||||||
|
</RegistInputItem>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{resultData.item_list && (
|
||||||
|
<ItemList>
|
||||||
|
{resultData.item_list.map((data, index) => {
|
||||||
|
return (
|
||||||
|
<Item key={index}>
|
||||||
|
<span>
|
||||||
|
{data.item_name}[{data.item}] ({data.item_cnt})
|
||||||
|
</span>
|
||||||
|
<BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>
|
||||||
|
</Item>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ItemList>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</AppendRegistTable>
|
||||||
|
</AppendRegistBox>
|
||||||
|
{isNullValue && (
|
||||||
|
<SearchBarAlert $align="right" $padding="0 0 15px">
|
||||||
|
{t('NULL_MSG')}
|
||||||
|
</SearchBarAlert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<BtnWrapper $justify="flex-end" $gap="10px">
|
||||||
|
<Button
|
||||||
|
text="취소"
|
||||||
|
theme="line"
|
||||||
|
handleClick={() => showModal('EVENT_REGIST_CANCEL', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => callbackPage()
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text="등록"
|
||||||
|
theme={checkCondition() ? 'primary' : 'disable'}
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
</BtnWrapper>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</AnimatedPageWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RewardEventRegist;
|
||||||
|
|
||||||
@@ -5,9 +5,11 @@ export { default as ReportList } from './ReportList';
|
|||||||
export { default as UserBlock } from './UserBlock';
|
export { default as UserBlock } from './UserBlock';
|
||||||
export { default as UserBlockRegist } from './UserBlockRegist';
|
export { default as UserBlockRegist } from './UserBlockRegist';
|
||||||
export { default as Items } from './Items';
|
export { default as Items } from './Items';
|
||||||
export { default as Event } from './Event';
|
export { default as RewardEvent } from './RewardEvent';
|
||||||
export { default as EventRegist } from './EventRegist';
|
export { default as RewardEventRegist } from './RewardEventRegist';
|
||||||
export { default as LandAuction} from './LandAuction'
|
export { default as LandAuction} from './LandAuction'
|
||||||
export { default as BattleEvent} from './BattleEvent'
|
export { default as BattleEvent} from './BattleEvent'
|
||||||
export { default as MenuBanner} from './MenuBanner'
|
export { default as MenuBanner} from './MenuBanner'
|
||||||
export { default as MenuBannerRegist} from './MenuBannerRegist'
|
export { default as MenuBannerRegist} from './MenuBannerRegist'
|
||||||
|
export { default as Ranking} from './Ranking'
|
||||||
|
export { default as Event} from './Event'
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import useCommonSearchOld from '../../hooks/useCommonSearchOld';
|
|||||||
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
||||||
import { historyTables } from '../../assets/data/data';
|
import { historyTables } from '../../assets/data/data';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const CaliumRequest = () => {
|
const CaliumRequest = () => {
|
||||||
const token = sessionStorage.getItem('token');
|
const token = sessionStorage.getItem('token');
|
||||||
@@ -114,16 +115,8 @@ const CaliumRequest = () => {
|
|||||||
case "logMove":
|
case "logMove":
|
||||||
const searchParams = {
|
const searchParams = {
|
||||||
action: LOG_ACTION_FAIL_CALIUM_ECHO,
|
action: LOG_ACTION_FAIL_CALIUM_ECHO,
|
||||||
start_dt: (() => {
|
start_dt: dayjs().subtract(1, 'day').startOf('day'),
|
||||||
const date = new Date();
|
end_dt: dayjs().subtract(1, 'day').endOf('day'),
|
||||||
date.setDate(date.getDate() - 1);
|
|
||||||
return date;
|
|
||||||
})(),
|
|
||||||
end_dt: (() => {
|
|
||||||
const date = new Date();
|
|
||||||
date.setDate(date.getDate() - 1);
|
|
||||||
return date;
|
|
||||||
})(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 복사한 데이터를 세션 스토리지에 저장
|
// 복사한 데이터를 세션 스토리지에 저장
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import { Title, FormWrapper } from '../../styles/Components';
|
|||||||
import { authType } from '../../assets/data';
|
import { authType } from '../../assets/data';
|
||||||
import { useModal, withAuth } from '../../hooks/hook';
|
import { useModal, withAuth } from '../../hooks/hook';
|
||||||
import { CaliTable } from '../../components/common';
|
import { CaliTable } from '../../components/common';
|
||||||
import tableInfo from '../../assets/data/pages/historyTable.json';
|
|
||||||
import useEnhancedCommonSearch from '../../hooks/useEnhancedCommonSearch';
|
import useEnhancedCommonSearch from '../../hooks/useEnhancedCommonSearch';
|
||||||
import FrontPagination from '../../components/common/Pagination/FrontPagination';
|
import FrontPagination from '../../components/common/Pagination/FrontPagination';
|
||||||
import { INITIAL_CURRENT_PAGE, INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE } from '../../assets/data/adminConstants';
|
import { INITIAL_CURRENT_PAGE, INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE } from '../../assets/data/adminConstants';
|
||||||
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
||||||
|
|
||||||
|
import tableInfo from '../../assets/data/pages/historyTable.json';
|
||||||
|
|
||||||
const LogView = () => {
|
const LogView = () => {
|
||||||
const [detailData, setDetailData] = useState({});
|
const [detailData, setDetailData] = useState({});
|
||||||
const [currentPage, setCurrentPage] = useState(INITIAL_CURRENT_PAGE);
|
const [currentPage, setCurrentPage] = useState(INITIAL_CURRENT_PAGE);
|
||||||
|
|||||||
@@ -727,11 +727,13 @@ export const SearchRow = styled.div`
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 20px 0;
|
gap: 20px 0;
|
||||||
|
|
||||||
&:last-child {
|
${props => props.direction === 'column' && css`
|
||||||
border-top: 1px solid #e0e0e0;
|
&:last-child {
|
||||||
padding-top: 15px;
|
border-top: 1px solid #e0e0e0;
|
||||||
margin-top: 15px;
|
padding-top: 15px;
|
||||||
}
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TabScroll = styled.div`
|
export const TabScroll = styled.div`
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as optionsConfig from '../assets/data/options';
|
import * as optionsConfig from '../assets/data/options';
|
||||||
import { logFieldLabels } from '../assets/data/data';
|
import { FieldLabels } from '../assets/data/data';
|
||||||
|
|
||||||
export const convertKTC = (dt, nation = true) => {
|
export const convertKTC = (dt, nation = true) => {
|
||||||
if (!dt) return "";
|
if (!dt) return "";
|
||||||
@@ -77,12 +77,12 @@ export const loadConfig = async (configPath) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getFieldLabel = (key, value) => {
|
export const getFieldLabel = (key, value) => {
|
||||||
if (logFieldLabels[key]) {
|
if (FieldLabels[key]) {
|
||||||
return logFieldLabels[key];
|
return FieldLabels[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof value === 'string' && logFieldLabels[value]) {
|
if (typeof value === 'string' && FieldLabels[value]) {
|
||||||
return logFieldLabels[value];
|
return FieldLabels[value];
|
||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
|
|||||||
Reference in New Issue
Block a user