Compare commits
3 Commits
1598fa93b6
...
3e5c3f0167
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e5c3f0167 | |||
| 826459f304 | |||
| d2ac5b338e |
16
src/App.js
16
src/App.js
@@ -5,16 +5,22 @@ import GlobalStyles from './styles/GlobalStyles';
|
||||
import RouteInfo from './RouteInfo';
|
||||
|
||||
import './i18n';
|
||||
import { AlertProvider } from './context/AlertProvider';
|
||||
import { LoadingProvider } from './context/LoadingProvider';
|
||||
|
||||
function App() {
|
||||
const isToken = sessionStorage.getItem('token') ? true : false;
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<GlobalStyles />
|
||||
<ControllLink>{isToken ? <Link to="/main" /> : <Link to="/fail" />}</ControllLink>
|
||||
<RouteInfo />
|
||||
</BrowserRouter>
|
||||
<AlertProvider>
|
||||
<LoadingProvider>
|
||||
<BrowserRouter>
|
||||
<GlobalStyles />
|
||||
<ControllLink>{isToken ? <Link to="/main" /> : <Link to="/fail" />}</ControllLink>
|
||||
<RouteInfo />
|
||||
</BrowserRouter>
|
||||
</LoadingProvider>
|
||||
</AlertProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
UserBlockRegist,
|
||||
WhiteList,
|
||||
LandAuction,
|
||||
BattleEvent
|
||||
BattleEvent, MenuBanner, MenuBannerRegist,
|
||||
} from './pages/ServiceManage';
|
||||
|
||||
const RouteInfo = () => {
|
||||
@@ -75,6 +75,8 @@ const RouteInfo = () => {
|
||||
<Route path="event/eventregist" element={<EventRegist />} />
|
||||
<Route path="landauction" element={<LandAuction />} />
|
||||
<Route path="battleevent" element={<BattleEvent />} />
|
||||
<Route path="menubanner" element={<MenuBanner />} />
|
||||
<Route path="menubanner/menubannerregist" element={<MenuBannerRegist />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
@@ -67,7 +67,7 @@ export const BlackListRegist = async (token, params) => {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BlacklistRegist', e);
|
||||
|
||||
114
src/apis/Menu.js
Normal file
114
src/apis/Menu.js
Normal file
@@ -0,0 +1,114 @@
|
||||
//운영서비스 관리 - 메뉴배너 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 리스트 조회
|
||||
export const MenuBannerView = async (token, searchData, status, startDate, endDate, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/menu/banner/list?search_data=${searchData}&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('MenuBannerView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 상세보기
|
||||
export const MenuBannerDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/menu/banner/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MenuBannerDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 등록
|
||||
export const MenuBannerSingleRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/menu/banner`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MenuBannerSingleRegist Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 수정
|
||||
export const MenuBannerModify = async (token, id, params) => {
|
||||
try {
|
||||
const res = await Axios.put(`/api/v1/menu/banner/${id}`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MenuBannerModify Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제
|
||||
export const MenuBannerDelete = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/menu/banner/delete`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MenuBannerDelete Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const MenuImageUpload = async (token, file) => {
|
||||
const exelFile = new FormData();
|
||||
exelFile.append('file', file);
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/menu/image-upload`, exelFile, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MenuImageUpload', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const MenuImageDelete = async (token, filename) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/menu/image-delete?file=${filename}`, {
|
||||
headers: {Authorization: `Bearer ${token}`},
|
||||
});
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MenuImageDelete', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
21
src/apis/OpenAI.js
Normal file
21
src/apis/OpenAI.js
Normal file
@@ -0,0 +1,21 @@
|
||||
//AI api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
|
||||
export const AnalyzeAI = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post('/api/v1/ai/analyze', params, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('analyzeAI Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -12,3 +12,5 @@ export * from './Item';
|
||||
export * from './Event';
|
||||
export * from './Calium';
|
||||
export * from './Land';
|
||||
export * from './Menu';
|
||||
export * from './OpenAI';
|
||||
|
||||
@@ -7,5 +7,6 @@ export const NONE = 'NONE';
|
||||
export const ONE_MINUTE_MS = 60000;
|
||||
export const ONE_MINUTE_SECOND = 60;
|
||||
export const AUCTION_MIN_MINUTE_TIME = 15; // 15분
|
||||
export const IMAGE_MAX_SIZE = 5242880;
|
||||
|
||||
export { INITIAL_PAGE_SIZE, INITIAL_CURRENT_PAGE, INITIAL_PAGE_LIMIT };
|
||||
|
||||
@@ -24,11 +24,39 @@ export const caliumRequestInitData = {
|
||||
content: '',
|
||||
}
|
||||
|
||||
export const ORDER_OPTIONS = {
|
||||
asc: [
|
||||
{ value: 'ASC', label: '오름차순' },
|
||||
{ value: 'DESC', label: '내림차순' }
|
||||
],
|
||||
desc: [
|
||||
{ value: 'DESC', label: '내림차순' },
|
||||
{ value: 'ASC', label: '오름차순' }
|
||||
],
|
||||
};
|
||||
|
||||
export const PAGE_SIZE_OPTIONS = {
|
||||
default: [
|
||||
{ value: '50', label: '50개' },
|
||||
{ value: '100', label: '100개' }
|
||||
],
|
||||
B: [
|
||||
{ value: '500', label: '500개' },
|
||||
{ value: '1000', label: '1000개' },
|
||||
{ value: '5000', label: '5000개' },
|
||||
{ value: '10000', label: '10000개' }
|
||||
],
|
||||
};
|
||||
|
||||
export const STATUS_STYLES = {
|
||||
COMPLETE: {
|
||||
background: '#58AB62',
|
||||
color: 'white'
|
||||
},
|
||||
EXPIRATION: {
|
||||
background: '#58AB62',
|
||||
color: 'white'
|
||||
},
|
||||
WAIT: {
|
||||
background: '#DEBB46',
|
||||
color: 'black'
|
||||
@@ -57,6 +85,10 @@ export const STATUS_STYLES = {
|
||||
background: '#4287f5',
|
||||
color: 'white'
|
||||
},
|
||||
INPROGRESS: {
|
||||
background: '#4287f5',
|
||||
color: 'white'
|
||||
},
|
||||
AUCTION_END: {
|
||||
background: '#A37FB8',
|
||||
color: 'white'
|
||||
@@ -73,6 +105,10 @@ export const STATUS_STYLES = {
|
||||
background: '#FFB59B',
|
||||
color: 'white'
|
||||
},
|
||||
DELETE: {
|
||||
background: '#FFB59B',
|
||||
color: 'white'
|
||||
},
|
||||
RUNNING: {
|
||||
background: '#4287f5',
|
||||
color: 'white'
|
||||
|
||||
@@ -28,4 +28,4 @@ export {
|
||||
opUserSessionType,
|
||||
opMailType,
|
||||
} from './options'
|
||||
export {benItems, MinuteList, HourList, caliumRequestInitData, STATUS_STYLES, months} from './data'
|
||||
export {benItems, MinuteList, HourList, caliumRequestInitData, STATUS_STYLES, months, PAGE_SIZE_OPTIONS, ORDER_OPTIONS} from './data'
|
||||
@@ -194,6 +194,16 @@ export const menuConfig = {
|
||||
view: true,
|
||||
authLevel: adminAuthLevel.NONE
|
||||
},
|
||||
menubanner: {
|
||||
title: '메뉴 배너 관리',
|
||||
permissions: {
|
||||
read: authType.menuBannerRead,
|
||||
update: authType.menuBannerUpdate,
|
||||
delete: authType.menuBannerDelete
|
||||
},
|
||||
view: true,
|
||||
authLevel: adminAuthLevel.NONE
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -10,6 +10,18 @@ export const mailSendType = [
|
||||
{ value: 'DIRECT_SEND', name: '즉시 발송' },
|
||||
];
|
||||
|
||||
export const message_type = [
|
||||
{ value: 'CHATTING', name: '채팅 타입' },
|
||||
{ value: 'CHATTING_TOAST', name: '채팅 + 토스트' },
|
||||
];
|
||||
|
||||
export const sendStatus = [
|
||||
{ value: 'WAIT', name: '대기' },
|
||||
{ value: 'RUNNING', name: '송출중' },
|
||||
{ value: 'FINISH', name: '완료' },
|
||||
{ value: 'FAIL', name: '실패' },
|
||||
];
|
||||
|
||||
export const mailSendStatus = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'WAIT', name: '대기' },
|
||||
@@ -278,6 +290,13 @@ export const opInputType = [
|
||||
{ value: 'Boolean', name: '부울' },
|
||||
];
|
||||
|
||||
export const opMenuBannerStatus = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'WAIT', name: '대기' },
|
||||
{ value: 'RUNNING', name: '진행중' },
|
||||
{ value: 'FINISH', name: '만료' },
|
||||
];
|
||||
|
||||
// export const logAction = [
|
||||
// { value: "None", name: "ALL" },
|
||||
// { value: "AIChatDeleteCharacter", name: "NPC 삭제" },
|
||||
|
||||
53
src/assets/data/pages/caliumRequestSearch.json
Normal file
53
src/assets/data/pages/caliumRequestSearch.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"initialSearchParams": {
|
||||
"searchContent": "",
|
||||
"status": "ALL",
|
||||
"startDate": "",
|
||||
"endDate": "",
|
||||
"orderBy": "DESC",
|
||||
"pageSize": 50,
|
||||
"currentPage": 1
|
||||
},
|
||||
|
||||
"searchFields": [
|
||||
{
|
||||
"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": "caliumStatus",
|
||||
"col": 1
|
||||
}
|
||||
],
|
||||
|
||||
"apiInfo": {
|
||||
"functionName": "CaliumRequestView",
|
||||
"loadOnMount": true,
|
||||
"paramsMapping": [
|
||||
"searchContent",
|
||||
"status",
|
||||
{"param": "startDate", "transform": "toISOString"},
|
||||
{"param": "endDate", "transform": "toISOString"},
|
||||
"orderBy",
|
||||
"pageSize",
|
||||
"currentPage"
|
||||
],
|
||||
"pageField": "currentPage",
|
||||
"pageSizeField": "pageSize",
|
||||
"orderField": "orderBy"
|
||||
}
|
||||
}
|
||||
63
src/assets/data/pages/eventSearch.json
Normal file
63
src/assets/data/pages/eventSearch.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": "EventView",
|
||||
"loadOnMount": true,
|
||||
"paramsMapping": [
|
||||
"searchTitle",
|
||||
"searchContent",
|
||||
"status",
|
||||
{"param": "startDate", "transform": "toISOString"},
|
||||
{"param": "endDate", "transform": "toISOString"},
|
||||
"orderBy",
|
||||
"pageSize",
|
||||
"currentPage"
|
||||
],
|
||||
"pageField": "currentPage",
|
||||
"pageSizeField": "pageSize",
|
||||
"orderField": "orderBy"
|
||||
}
|
||||
}
|
||||
90
src/assets/data/pages/mailSearch.json
Normal file
90
src/assets/data/pages/mailSearch.json
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"initialSearchParams": {
|
||||
"searchTitle": "",
|
||||
"searchContent": "",
|
||||
"sendType": "ALL",
|
||||
"status": "ALL",
|
||||
"mailType": "ALL",
|
||||
"receiveType": "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": "sendType",
|
||||
"label": "발송 방식",
|
||||
"optionsRef": "mailSendType",
|
||||
"col": 2
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "status",
|
||||
"label": "발송 상태",
|
||||
"optionsRef": "mailSendStatus",
|
||||
"col": 2
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "mailType",
|
||||
"label": "우편 타입",
|
||||
"optionsRef": "mailType",
|
||||
"col": 2
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "receiveType",
|
||||
"label": "수신 대상",
|
||||
"optionsRef": "mailReceiveType",
|
||||
"col": 2
|
||||
}
|
||||
],
|
||||
|
||||
"apiInfo": {
|
||||
"functionName": "MailView",
|
||||
"loadOnMount": true,
|
||||
"paramsMapping": [
|
||||
"searchTitle",
|
||||
"searchContent",
|
||||
"sendType",
|
||||
"status",
|
||||
"mailType",
|
||||
"receiveType",
|
||||
{"param": "startDate", "transform": "toISOString"},
|
||||
{"param": "endDate", "transform": "toISOString"},
|
||||
"orderBy",
|
||||
"pageSize",
|
||||
"currentPage"
|
||||
],
|
||||
"pageField": "currentPage",
|
||||
"pageSizeField": "pageSize",
|
||||
"orderField": "orderBy"
|
||||
}
|
||||
}
|
||||
126
src/assets/data/pages/menuBanner.json
Normal file
126
src/assets/data/pages/menuBanner.json
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"pageId": "menuBanner",
|
||||
"title": "메뉴 배너 관리",
|
||||
"endpoint": "/api/v1/menu/banner",
|
||||
"permissions": ["battleEventRead", "battleEventUpdate", "battleEventDelete"],
|
||||
"layout": {
|
||||
"type": "standard",
|
||||
"components": ["search", "table", "pagination"]
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"id": "create",
|
||||
"label": "배너 등록",
|
||||
"type": "button",
|
||||
"theme": "primary",
|
||||
"permission": "battleEventUpdate",
|
||||
"action": {
|
||||
"type": "modal",
|
||||
"target": "createModal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "delete",
|
||||
"label": "선택 삭제",
|
||||
"type": "button",
|
||||
"theme": "line",
|
||||
"permission": "battleEventDelete",
|
||||
"requireSelection": true,
|
||||
"action": {
|
||||
"type": "modal",
|
||||
"target": "deleteConfirmModal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"modals": {
|
||||
"createModal": {
|
||||
"id": "createModal",
|
||||
"type": "form",
|
||||
"title": "메뉴 배너 등록",
|
||||
"size": "medium",
|
||||
"schema": "menuBannerForm",
|
||||
"actions": [
|
||||
{
|
||||
"id": "cancel",
|
||||
"label": "취소",
|
||||
"type": "button",
|
||||
"theme": "line",
|
||||
"action": {
|
||||
"type": "close"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "submit",
|
||||
"label": "등록",
|
||||
"type": "button",
|
||||
"theme": "primary",
|
||||
"action": {
|
||||
"type": "api",
|
||||
"method": "POST",
|
||||
"endpoint": "/api/v1/menu/banner",
|
||||
"successAction": {
|
||||
"type": "close",
|
||||
"then": "refresh"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"detailModal": {
|
||||
"id": "detailModal",
|
||||
"type": "form",
|
||||
"title": "메뉴 배너 상세",
|
||||
"size": "medium",
|
||||
"schema": "menuBannerForm",
|
||||
"readOnly": true,
|
||||
"dataSource": {
|
||||
"type": "api",
|
||||
"endpoint": "/api/v1/menu/banner/detail/{id}"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"id": "close",
|
||||
"label": "확인",
|
||||
"type": "button",
|
||||
"theme": "line",
|
||||
"action": {
|
||||
"type": "close"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"deleteConfirmModal": {
|
||||
"id": "deleteConfirmModal",
|
||||
"type": "confirm",
|
||||
"title": "배너 삭제",
|
||||
"message": "선택한 배너를 삭제하시겠습니까?",
|
||||
"actions": [
|
||||
{
|
||||
"id": "cancel",
|
||||
"label": "취소",
|
||||
"type": "button",
|
||||
"theme": "line",
|
||||
"action": {
|
||||
"type": "close"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "confirm",
|
||||
"label": "삭제",
|
||||
"type": "button",
|
||||
"theme": "primary",
|
||||
"action": {
|
||||
"type": "api",
|
||||
"method": "DELETE",
|
||||
"endpoint": "/api/v1/menu/banner/delete",
|
||||
"dataTransform": "selectedToRequestBody",
|
||||
"successAction": {
|
||||
"type": "close",
|
||||
"then": "refresh"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
176
src/assets/data/pages/menuBannerRegist.json
Normal file
176
src/assets/data/pages/menuBannerRegist.json
Normal file
@@ -0,0 +1,176 @@
|
||||
{
|
||||
"modal": {
|
||||
"titles": {
|
||||
"create": "메뉴배너 등록",
|
||||
"update": "메뉴배너 수정",
|
||||
"view": "메뉴배너 상세"
|
||||
},
|
||||
"grid": {
|
||||
"rows": 8,
|
||||
"columns": 12
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"id": "date_range",
|
||||
"type": "dateTimeRange",
|
||||
"label": "등록기간",
|
||||
"position": { "row": 0, "col": 0, "width": 12 },
|
||||
"startDateField": "start_dt",
|
||||
"endDateField": "end_dt",
|
||||
"startDateLabel": "시작일자",
|
||||
"endDateLabel": "종료일자",
|
||||
"validations": ["required"],
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"]
|
||||
},
|
||||
{
|
||||
"id": "title",
|
||||
"type": "text",
|
||||
"label": "배너 제목",
|
||||
"position": { "row": 1, "col": 0, "width": 6 },
|
||||
"validations": ["required"],
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"],
|
||||
"width": "100%"
|
||||
},
|
||||
{
|
||||
"id": "image_ko",
|
||||
"type": "imageUpload",
|
||||
"label": "이미지 첨부 (KO)",
|
||||
"position": { "row": 2, "col": 0, "width": 12 },
|
||||
"language": "KO",
|
||||
"validations": ["required"],
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"]
|
||||
},
|
||||
{
|
||||
"id": "image_en",
|
||||
"type": "imageUpload",
|
||||
"label": "이미지 첨부 (EN)",
|
||||
"position": { "row": 3, "col": 0, "width": 12 },
|
||||
"language": "EN",
|
||||
"validations": ["required"],
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"]
|
||||
},
|
||||
{
|
||||
"id": "image_ja",
|
||||
"type": "imageUpload",
|
||||
"label": "이미지 첨부 (JA)",
|
||||
"position": { "row": 4, "col": 0, "width": 12 },
|
||||
"language": "JA",
|
||||
"validations": ["required"],
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"]
|
||||
},
|
||||
{
|
||||
"id": "is_link",
|
||||
"type": "checkbox",
|
||||
"label": "이미지 링크 여부",
|
||||
"position": { "row": 5, "col": 0, "width": 12 },
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"]
|
||||
},
|
||||
{
|
||||
"id": "link_ko",
|
||||
"type": "textWithSuffix",
|
||||
"label": "웹 링크",
|
||||
"position": { "row": 6, "col": 0, "width": 12 },
|
||||
"suffix": "KO",
|
||||
"conditional": { "field": "is_link", "operator": "==", "value": true },
|
||||
"validations": ["required"],
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"],
|
||||
"width": "100%"
|
||||
},
|
||||
{
|
||||
"id": "link_en",
|
||||
"type": "textWithSuffix",
|
||||
"label": "",
|
||||
"position": { "row": 7, "col": 0, "width": 12 },
|
||||
"suffix": "EN",
|
||||
"conditional": { "field": "is_link", "operator": "==", "value": true },
|
||||
"validations": ["required"],
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"],
|
||||
"width": "100%"
|
||||
},
|
||||
{
|
||||
"id": "link_ja",
|
||||
"type": "textWithSuffix",
|
||||
"label": "",
|
||||
"position": { "row": 8, "col": 0, "width": 12 },
|
||||
"suffix": "JA",
|
||||
"conditional": { "field": "is_link", "operator": "==", "value": true },
|
||||
"validations": ["required"],
|
||||
"visibleOn": ["create", "update", "view"],
|
||||
"editableOn": ["create", "update"],
|
||||
"width": "100%"
|
||||
}
|
||||
],
|
||||
"actions": {
|
||||
"create": [
|
||||
{ "id": "cancel", "label": "취소", "theme": "line", "action": "cancel" },
|
||||
{ "id": "submit", "label": "등록", "theme": "primary", "action": "submit" }
|
||||
],
|
||||
"update": [
|
||||
{ "id": "cancel", "label": "취소", "theme": "line", "action": "cancel" },
|
||||
{ "id": "submit", "label": "수정", "theme": "primary", "action": "submit" }
|
||||
],
|
||||
"view": [
|
||||
{ "id": "cancel", "label": "취소", "theme": "line", "action": "cancel" },
|
||||
{ "id": "edit", "label": "수정", "theme": "primary", "action": "edit" }
|
||||
]
|
||||
},
|
||||
"validations": {
|
||||
"create": [
|
||||
{
|
||||
"condition": "start_dt < (new Date() + 60 * 60000)",
|
||||
"message": "EVENT_TIME_LIMIT_ADD"
|
||||
},
|
||||
{
|
||||
"condition": "end_dt <= start_dt",
|
||||
"message": "DATE_START_DIFF_END_WARNING"
|
||||
}
|
||||
],
|
||||
"update": [
|
||||
{
|
||||
"condition": "end_dt <= start_dt",
|
||||
"message": "DATE_START_DIFF_END_WARNING"
|
||||
}
|
||||
]
|
||||
},
|
||||
"api": {
|
||||
"create": {
|
||||
"endpoint": "MenuBannerSingleRegist",
|
||||
"errorMapping": {
|
||||
"ERROR_API_FAIL": "API_FAIL",
|
||||
"ERROR_REGIST_FAIL": "REGIST_FAIL"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"endpoint": "MenuBannerUpdate",
|
||||
"errorMapping": {
|
||||
"ERROR_API_FAIL": "API_FAIL",
|
||||
"ERROR_UPDATE_FAIL": "UPDATE_FAIL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"initData": {
|
||||
"title": "",
|
||||
"is_link": false,
|
||||
"start_dt": "",
|
||||
"end_dt": "",
|
||||
"image_list": [
|
||||
{ "language": "KO", "content": "" },
|
||||
{ "language": "EN", "content": "" },
|
||||
{ "language": "JA", "content": "" }
|
||||
],
|
||||
"link_list": [
|
||||
{ "language": "KO", "content": "" },
|
||||
{ "language": "EN", "content": "" },
|
||||
{ "language": "JA", "content": "" }
|
||||
]
|
||||
}
|
||||
}
|
||||
53
src/assets/data/pages/menuBannerSearch.json
Normal file
53
src/assets/data/pages/menuBannerSearch.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"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": {
|
||||
"functionName": "MenuBannerView",
|
||||
"loadOnMount": true,
|
||||
"paramsMapping": [
|
||||
"searchData",
|
||||
"status",
|
||||
{"param": "startDate", "transform": "toISOString"},
|
||||
{"param": "endDate", "transform": "toISOString"},
|
||||
"orderBy",
|
||||
"pageSize",
|
||||
"currentPage"
|
||||
],
|
||||
"pageField": "currentPage",
|
||||
"pageSizeField": "pageSize",
|
||||
"orderField": "orderBy"
|
||||
}
|
||||
}
|
||||
107
src/assets/data/pages/menuBannerTable.json
Normal file
107
src/assets/data/pages/menuBannerTable.json
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"id": "menuBannerTable",
|
||||
"selection": {
|
||||
"type": "single",
|
||||
"idField": "id"
|
||||
},
|
||||
"header": {
|
||||
"countType": "total",
|
||||
"orderType": "desc",
|
||||
"pageType": "default",
|
||||
"buttons": [
|
||||
{
|
||||
"id": "delete",
|
||||
"text": "선택 삭제",
|
||||
"theme": "line",
|
||||
"disableWhen": "noSelection",
|
||||
"requiredAuth": "battleEventDelete",
|
||||
"action": "delete"
|
||||
},
|
||||
{
|
||||
"id": "register",
|
||||
"text": "이미지 등록",
|
||||
"theme": "primary",
|
||||
"requiredAuth": "battleEventUpdate",
|
||||
"action": "navigate",
|
||||
"navigateTo": "/servicemanage/menubanner/menubannerregist"
|
||||
}
|
||||
]
|
||||
},
|
||||
"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": "opMenuBannerStatus"
|
||||
},
|
||||
{
|
||||
"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": "title",
|
||||
"type": "text",
|
||||
"title": "설명 제목"
|
||||
},
|
||||
{
|
||||
"id": "is_link",
|
||||
"type": "option",
|
||||
"width": "90px",
|
||||
"title": "링크여부",
|
||||
"option_name": "opYNType"
|
||||
},
|
||||
{
|
||||
"id": "detail",
|
||||
"type": "button",
|
||||
"width": "120px",
|
||||
"title": "상세보기",
|
||||
"text": "상세보기",
|
||||
"action": {
|
||||
"type": "modal",
|
||||
"target": "detailModal",
|
||||
"dataParam": {
|
||||
"id": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "update_by",
|
||||
"type": "text",
|
||||
"width": "150px",
|
||||
"title": "히스토리"
|
||||
}
|
||||
],
|
||||
"sort": {
|
||||
"defaultColumn": "row_num",
|
||||
"defaultDirection": "desc"
|
||||
}
|
||||
}
|
||||
78
src/assets/data/pages/userBlockSearch.json
Normal file
78
src/assets/data/pages/userBlockSearch.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"initialSearchParams": {
|
||||
"searchType": "GUID",
|
||||
"searchData": "",
|
||||
"email": "",
|
||||
"status": "ALL",
|
||||
"sanctions": "ALL",
|
||||
"period": "ALL",
|
||||
"orderBy": "DESC",
|
||||
"pageSize": 50,
|
||||
"currentPage": 1
|
||||
},
|
||||
|
||||
"searchFields": [
|
||||
{
|
||||
"type": "select",
|
||||
"id": "searchType",
|
||||
"label": "대상",
|
||||
"optionsRef": "eventStatus",
|
||||
"col": 1
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "searchData",
|
||||
"placeholder": "대상 입력",
|
||||
"width": "300px",
|
||||
"col": 1
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "email",
|
||||
"label": "등록자",
|
||||
"placeholder": "이메일 입력",
|
||||
"width": "300px",
|
||||
"col": 1
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "status",
|
||||
"label": "상태",
|
||||
"optionsRef": "blockStatus",
|
||||
"col": 2
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "sanctions",
|
||||
"label": "제재 사유",
|
||||
"optionsRef": "blockSanctions",
|
||||
"col": 2
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "period",
|
||||
"label": "제재 기간",
|
||||
"optionsRef": "blockPeriod",
|
||||
"col": 2
|
||||
}
|
||||
],
|
||||
|
||||
"apiInfo": {
|
||||
"functionName": "BlackListView",
|
||||
"loadOnMount": true,
|
||||
"paramsMapping": [
|
||||
"searchType",
|
||||
"searchData",
|
||||
"email",
|
||||
"status",
|
||||
"sanctions",
|
||||
"period",
|
||||
"orderBy",
|
||||
"pageSize",
|
||||
"currentPage"
|
||||
],
|
||||
"pageField": "currentPage",
|
||||
"pageSizeField": "pageSize",
|
||||
"orderField": "orderBy"
|
||||
}
|
||||
}
|
||||
101
src/assets/data/pages/userBlockTable.json
Normal file
101
src/assets/data/pages/userBlockTable.json
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"id": "userBlockTable",
|
||||
"selection": {
|
||||
"type": "single",
|
||||
"idField": "id"
|
||||
},
|
||||
"header": {
|
||||
"countType": "total",
|
||||
"orderType": "desc",
|
||||
"pageType": "default",
|
||||
"buttons": [
|
||||
{
|
||||
"id": "delete",
|
||||
"text": "선택 삭제",
|
||||
"theme": "line",
|
||||
"disableWhen": "noSelection",
|
||||
"requiredAuth": "blackListDelete",
|
||||
"action": "delete"
|
||||
},
|
||||
{
|
||||
"id": "register",
|
||||
"text": "제재 등록",
|
||||
"theme": "primary",
|
||||
"requiredAuth": "blackListUpdate",
|
||||
"action": "navigate",
|
||||
"navigateTo": "/servicemanage/userblock/userblockregist"
|
||||
}
|
||||
]
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"id": "checkbox",
|
||||
"type": "checkbox",
|
||||
"width": "40px",
|
||||
"title": ""
|
||||
},
|
||||
{
|
||||
"id": "row_num",
|
||||
"type": "text",
|
||||
"width": "80px",
|
||||
"title": "번호"
|
||||
},
|
||||
{
|
||||
"id": "guid",
|
||||
"type": "text",
|
||||
"width": "20%",
|
||||
"title": "GUID"
|
||||
},
|
||||
{
|
||||
"id": "nickname",
|
||||
"type": "text",
|
||||
"width": "20%",
|
||||
"title": "아바타명"
|
||||
},
|
||||
{
|
||||
"id": "status",
|
||||
"type": "status",
|
||||
"width": "100px",
|
||||
"title": "상태",
|
||||
"option_name": "blockStatus"
|
||||
},
|
||||
{
|
||||
"id": "period",
|
||||
"type": "option",
|
||||
"width": "100px",
|
||||
"title": "제재 기간",
|
||||
"option_name": "blockPeriod"
|
||||
},
|
||||
{
|
||||
"id": "sanctions",
|
||||
"type": "option",
|
||||
"width": "250px",
|
||||
"title": "제재 사유",
|
||||
"option_name": "blockSanctions"
|
||||
},
|
||||
{
|
||||
"id": "create_by",
|
||||
"type": "text",
|
||||
"width": "150px",
|
||||
"title": "등록자"
|
||||
},
|
||||
{
|
||||
"id": "detail",
|
||||
"type": "button",
|
||||
"width": "120px",
|
||||
"title": "상세보기",
|
||||
"text": "상세보기",
|
||||
"action": {
|
||||
"type": "modal",
|
||||
"target": "detailModal",
|
||||
"dataParam": {
|
||||
"id": "id"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"sort": {
|
||||
"defaultColumn": "row_num",
|
||||
"defaultDirection": "desc"
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,9 @@ export const authType = {
|
||||
battleEventUpdate: 47,
|
||||
battleEventDelete: 48,
|
||||
businessLogRead: 49,
|
||||
|
||||
menuBannerRead: 50,
|
||||
menuBannerUpdate: 51,
|
||||
menuBannerDelete: 52,
|
||||
|
||||
|
||||
levelReader: 999,
|
||||
@@ -57,6 +59,15 @@ export const authType = {
|
||||
levelDeveloper: 99999,
|
||||
};
|
||||
|
||||
export const alertTypes = {
|
||||
info: 0,
|
||||
success: 1,
|
||||
warning: 2,
|
||||
error: 3,
|
||||
confirm: 4,
|
||||
confirmChildren: 5
|
||||
}
|
||||
|
||||
export const adminAuthLevel = {
|
||||
NONE: "None",
|
||||
READER: "Reader",
|
||||
|
||||
@@ -22,6 +22,9 @@ import LogViewSearchBar from './searchBar/LogViewSearchBar';
|
||||
import AdminViewSearchBar from './searchBar/AdminViewSearchBar';
|
||||
import CaliumRequestSearchBar from './searchBar/CaliumRequestSearchBar';
|
||||
|
||||
import CommonSearchBar from './searchBar/CommonSearchBar';
|
||||
import useCommonSearch from './searchBar/useCommonSearch';
|
||||
|
||||
//etc
|
||||
import ReportListSummary from './ReportListSummary';
|
||||
import WhiteListSearchBar from './WhiteListRegistBar';
|
||||
@@ -50,5 +53,7 @@ export {
|
||||
LandAuctionSearchBar,
|
||||
LandAuctionModal,
|
||||
BattleEventModal,
|
||||
OwnerChangeModal
|
||||
OwnerChangeModal,
|
||||
useCommonSearch,
|
||||
CommonSearchBar
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useState, Fragment, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../../common/button/Button';
|
||||
import Loading from '../../common/Loading';
|
||||
|
||||
import {
|
||||
Title,
|
||||
@@ -19,10 +18,8 @@ import {
|
||||
FormStatusWarning,
|
||||
FormButtonContainer,
|
||||
} from '../../../styles/ModuleComponents';
|
||||
import { modalTypes } from '../../../assets/data';
|
||||
import { DynamicModal, Modal, SingleDatePicker, SingleTimePicker } from '../../common';
|
||||
import { Modal, SingleDatePicker, SingleTimePicker } from '../../common';
|
||||
import { NONE, TYPE_MODIFY, TYPE_REGISTRY } from '../../../assets/data/adminConstants';
|
||||
import { useModal } from '../../../hooks/hook';
|
||||
import { convertKTCDate } from '../../../utils';
|
||||
import {
|
||||
battleEventHotTime,
|
||||
@@ -31,28 +28,19 @@ import {
|
||||
battleRepeatType,
|
||||
} from '../../../assets/data/options';
|
||||
import { BattleEventModify, BattleEventSingleRegist } from '../../../apis/Battle';
|
||||
import { battleEventStatusType } from '../../../assets/data/types';
|
||||
import { alertTypes, battleEventStatusType } from '../../../assets/data/types';
|
||||
import { isValidDayRange } from '../../../utils/date';
|
||||
import { useAlert } from '../../../context/AlertProvider';
|
||||
import { useLoading } from '../../../context/LoadingProvider';
|
||||
|
||||
const BattleEventModal = ({ modalType, detailView, handleDetailView, content, setDetailData, configData, rewardData }) => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
const { showToast, showModal } = useAlert();
|
||||
const {withLoading} = useLoading();
|
||||
|
||||
const [loading, setLoading] = useState(false); // 로딩 창
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
cancel: 'hidden',
|
||||
registConfirm: 'hidden',
|
||||
registComplete: 'hidden'
|
||||
});
|
||||
|
||||
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
|
||||
const [resultData, setResultData] = useState(initData); //데이터 정보
|
||||
const [isNullValue, setIsNullValue] = useState(false);
|
||||
const [resultData, setResultData] = useState(initData);
|
||||
|
||||
useEffect(() => {
|
||||
if(modalType === TYPE_MODIFY && content && Object.keys(content).length > 0){
|
||||
@@ -103,7 +91,7 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
const endDay = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
if (endDay <= startDay) {
|
||||
setAlertMsg(t('BATTLE_EVENT_MODAL_START_DIFF_END_WARNING'));
|
||||
showToast('DATE_START_DIFF_END_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +135,7 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
const endDay = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
if (endDay <= startDay) {
|
||||
setAlertMsg(t('BATTLE_EVENT_MODAL_START_DIFF_END_WARNING'));
|
||||
showToast('DATE_START_DIFF_END_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,7 +154,7 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
round_time: config.round_time
|
||||
});
|
||||
} else {
|
||||
console.warn('Config not found for value:', e.target.value);
|
||||
showToast('Config not found for value:', e.target.value, {type: alertTypes.warning});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,17 +173,17 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
const startDt = resultData.event_start_dt;
|
||||
const endDt = resultData.event_end_dt;
|
||||
if (modalType === TYPE_REGISTRY && startDt < minAllowedTime) {
|
||||
setAlertMsg(t('BATTLE_EVENT_MODAL_START_DT_WARNING'));
|
||||
showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
if(resultData.repeat_type !== 'NONE' && !isValidDayRange(startDt, endDt)) {
|
||||
setAlertMsg(t('BATTLE_EVENT_MODAL_START_DIFF_END_WARNING'))
|
||||
showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
//화면에 머물면서 상태는 안바꼈을 경우가 있기에 시작시간 지났을경우 차단
|
||||
if (modalType === TYPE_REGISTRY && startDt < new Date()) {
|
||||
setAlertMsg(t('BATTLE_EVENT_MODAL_START_DT_WARNING'));
|
||||
showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -204,57 +192,48 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
setResultData({ ...resultData, round_time: config.round_time });
|
||||
}
|
||||
|
||||
handleModalView('registConfirm');
|
||||
break;
|
||||
case "cancel":
|
||||
handleModalView('cancel');
|
||||
break;
|
||||
case "cancelConfirm":
|
||||
handleModalClose('cancel');
|
||||
handleReset();
|
||||
showModal(isView('modify') ? 'BATTLE_EVENT_UPDATE_CONFIRM' : 'BATTLE_EVENT_REGIST_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('registConfirm')
|
||||
});
|
||||
|
||||
break;
|
||||
case "registConfirm":
|
||||
setLoading(true);
|
||||
|
||||
if(isView('modify')){
|
||||
await BattleEventModify(token, content?.id, resultData).then(data => {
|
||||
setLoading(false);
|
||||
handleModalClose('registConfirm');
|
||||
await withLoading( async () => {
|
||||
return await BattleEventModify(token, content?.id, resultData);
|
||||
}).then(data => {
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('registComplete');
|
||||
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
|
||||
}else if(data.result === "ERROR_BATTLE_EVENT_TIME_OVER"){
|
||||
setAlertMsg(t('BATTLE_EVENT_MODAL_TIME_CHECK_WARNING'));
|
||||
showToast('BATTLE_EVENT_MODAL_TIME_CHECK_WARNING', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('UPDATE_FAIL'));
|
||||
showToast('UPDATE_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
handleReset();
|
||||
});
|
||||
}
|
||||
else{
|
||||
await BattleEventSingleRegist(token, resultData).then(data => {
|
||||
setLoading(false);
|
||||
handleModalClose('registConfirm');
|
||||
await withLoading( async () => {
|
||||
return await BattleEventSingleRegist(token, resultData);
|
||||
}).then(data => {
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('registComplete');
|
||||
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||
}else if(data.result === "ERROR_BATTLE_EVENT_TIME_OVER"){
|
||||
setAlertMsg(t('BATTLE_EVENT_MODAL_TIME_CHECK_WARNING'));
|
||||
showToast('BATTLE_EVENT_MODAL_TIME_CHECK_WARNING', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('REGIST_FAIL'));
|
||||
showToast('REGIST_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
handleReset();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "registComplete":
|
||||
handleModalClose('registComplete');
|
||||
handleReset();
|
||||
window.location.reload();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +383,14 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
/>
|
||||
:
|
||||
<>
|
||||
<Button text="취소" theme="line" handleClick={() => handleSubmit('cancel')} />
|
||||
<Button
|
||||
text="취소"
|
||||
theme="line"
|
||||
handleClick={() => showModal('CANCEL_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleReset()
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
text={isView('modify') ? "수정" : "등록"}
|
||||
@@ -421,38 +407,6 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
</FormButtonContainer>
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
|
||||
{/* 확인 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.registConfirmModal}
|
||||
modalText={isView('modify') ? t('BATTLE_EVENT_UPDATE_CONFIRM') : t('BATTLE_EVENT_REGIST_CONFIRM')}
|
||||
handleSubmit={() => handleSubmit('registConfirm')}
|
||||
handleCancel={() => handleModalClose('registConfirm')}
|
||||
/>
|
||||
{/* 완료 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.registCompleteModal}
|
||||
modalText={isView('modify') ? t('UPDATE_COMPLETED') : t('REGIST_COMPLTE')}
|
||||
handleSubmit={() => handleSubmit('registComplete')}
|
||||
/>
|
||||
{/* 취소 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.cancelModal}
|
||||
modalText={t('CANCEL_CONFIRM')}
|
||||
handleCancel={() => handleModalClose('cancel')}
|
||||
handleSubmit={() => handleSubmit('cancelConfirm')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('warning')}
|
||||
/>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,26 +3,30 @@ import { useState, useEffect, Fragment } from 'react';
|
||||
import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, Textarea, SearchBarAlert } from '../../../styles/Components';
|
||||
import Button from '../../common/button/Button';
|
||||
import Modal from '../../common/modal/Modal';
|
||||
import { EventIsItem, EventModify, MailModify } from '../../../apis';
|
||||
import { EventIsItem, EventModify } from '../../../apis';
|
||||
|
||||
import { authList } from '../../../store/authList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { authType, benItems, commonStatus, modalTypes, wellType } from '../../../assets/data';
|
||||
import { authType, benItems, commonStatus, wellType } from '../../../assets/data';
|
||||
import {
|
||||
AppendRegistBox, AppendRegistTable, AreaBtnClose,
|
||||
BtnDelete, DetailInputItem, DetailInputRow,
|
||||
DetailModalWrapper, RegistGroup, DetailRegistInfo, DetailState,
|
||||
Item, ItemList, LangArea
|
||||
} from '../../../styles/ModuleComponents';
|
||||
import DynamicModal from '../../common/modal/DynamicModal';
|
||||
import { convertKTC, combineDateTime, timeDiffMinute, convertKTCDate } from '../../../utils';
|
||||
import DateTimeInput from '../../common/input/DateTimeInput';
|
||||
import { useLoading } from '../../../context/LoadingProvider';
|
||||
import { useAlert } from '../../../context/AlertProvider';
|
||||
import { alertTypes } from '../../../assets/data/types';
|
||||
|
||||
const EventDetailModal = ({ 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);
|
||||
@@ -39,15 +43,8 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
const [resource, setResource] = useState('19010001');
|
||||
const [resourceCount, setResourceCount] = useState('');
|
||||
|
||||
const [modifyModal, setModifyModal] = useState('hidden');
|
||||
const [completeModal, setCompleteModal] = useState('hidden');
|
||||
const [resultData, setResultData] = useState({});
|
||||
|
||||
const [modalState, setModalState] = useState({
|
||||
updateConfirmModal: 'hidden',
|
||||
updateCompleteModal: 'hidden',
|
||||
});
|
||||
|
||||
const [isNullValue, setIsNullValue] = useState(false);
|
||||
// 과거 판단
|
||||
const [isPast, setIsPast] = useState(false);
|
||||
@@ -56,7 +53,6 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
const [btnValidation, setBtnValidation] = useState(false);
|
||||
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||
const [itemCheckMsg, setItemCheckMsg] = useState('');
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if(content){
|
||||
@@ -122,12 +118,11 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
// 아이템 추가
|
||||
const handleItemList = async () => {
|
||||
if(benItems.includes(item)){
|
||||
setAlertMsg(t('MAIL_ITEM_ADD_BEN'))
|
||||
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"){
|
||||
@@ -240,45 +235,31 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
);
|
||||
};
|
||||
|
||||
const handleModalView = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'view',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalClose = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'hidden',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "submit":
|
||||
if (!conditionCheck()) return;
|
||||
|
||||
handleModalView('updateConfirm');
|
||||
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){
|
||||
setAlertMsg(t('EVENT_TIME_LIMIT_UPDATE'));
|
||||
handleModalClose('updateConfirm');
|
||||
showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
await EventModify(token, id, resultData);
|
||||
handleModalClose('updateConfirm');
|
||||
handleModalView('updateComplete');
|
||||
break;
|
||||
case "updateComplete":
|
||||
handleModalClose('updateComplete');
|
||||
window.location.reload();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('');
|
||||
withLoading( async () => {
|
||||
return await EventModify(token, id, resultData);
|
||||
}).catch(error => {
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
|
||||
handleDetailView();
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -533,28 +514,6 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
)}
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
{/* 확인 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.updateConfirmModal}
|
||||
modalText={t('MAIL_UPDATE_SAVE')}
|
||||
handleCancel={() => handleModalClose('updateConfirm')}
|
||||
handleSubmit={() => handleSubmit('updateConfirm')}
|
||||
/>
|
||||
{/* 완료 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.updateCompleteModal}
|
||||
modalText={t('UPDATE_COMPLETED')}
|
||||
handleSubmit={() => handleSubmit('updateComplete')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('warning')}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -34,25 +34,18 @@ import { landAuctionStatus, landAuctionStatusType, languageType, CurrencyType }
|
||||
import { useModal } from '../../../hooks/hook';
|
||||
import { convertKTCDate } from '../../../utils';
|
||||
import { msToMinutes } from '../../../utils/date';
|
||||
import { useAlert } from '../../../context/AlertProvider';
|
||||
import { alertTypes } from '../../../assets/data/types';
|
||||
|
||||
const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, setDetailData, landData, buildingData }) => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
const { showToast, showModal } = useAlert();
|
||||
|
||||
const [loading, setLoading] = useState(false); // 로딩 창
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
cancel: 'hidden',
|
||||
registConfirm: 'hidden',
|
||||
registComplete: 'hidden'
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message_lang, setMessage_lang] = useState('KO');
|
||||
|
||||
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
const [selectLand, setSelectLand] = useState(initLandData);
|
||||
|
||||
const [resultData, setResultData] = useState(initData); //데이터 정보
|
||||
@@ -181,41 +174,38 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
|
||||
const minAllowedTime = new Date(new Date().getTime() + 5 * 60000);
|
||||
if (isView('recv') && resultData.resv_start_dt < minAllowedTime) {
|
||||
setAlertMsg(t('LAND_AUCTION_MODEL_RESV_START_WARNING'));
|
||||
showToast('LAND_AUCTION_MODEL_RESV_START_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
if (resultData.auction_start_dt < minAllowedTime) {
|
||||
setAlertMsg(t('LAND_AUCTION_MODEL_AUCTION_START_WARNING'));
|
||||
showToast('LAND_AUCTION_MODEL_AUCTION_START_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
if(resultData.resv_start_dt >= resultData.auction_start_dt || resultData.resv_start_dt >= resultData.auction_end_dt) {
|
||||
setAlertMsg(t('LAND_AUCTION_MODEL_AUCTION_DIFF_RESERVATION'))
|
||||
showToast('LAND_AUCTION_MODEL_AUCTION_DIFF_RESERVATION', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
if(resultData.auction_start_dt >= resultData.auction_end_dt) {
|
||||
setAlertMsg(t('LAND_AUCTION_MODEL_AUCTION_DIFF_AUCTION'))
|
||||
showToast('LAND_AUCTION_MODEL_AUCTION_DIFF_AUCTION', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
const diffAuctionTime = resultData.auction_end_dt - resultData.auction_start_dt;
|
||||
if(msToMinutes(diffAuctionTime) < AUCTION_MIN_MINUTE_TIME){
|
||||
setAlertMsg(t('LAND_AUCTION_MODEL_MIN_TIME_WARNING'))
|
||||
showToast('LAND_AUCTION_MODEL_MIN_TIME_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
//화면에 머물면서 상태는 안바꼈을 경우가 있기에 경매시작시간 지났을경우 차단
|
||||
if (modalType === TYPE_MODIFY && resultData.auction_start_dt < new Date()) {
|
||||
setAlertMsg(t('LAND_AUCTION_MADEL_MODIFY_START'));
|
||||
showToast('LAND_AUCTION_MADEL_MODIFY_START', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
handleModalView('registConfirm');
|
||||
break;
|
||||
case "cancel":
|
||||
handleModalView('cancel');
|
||||
break;
|
||||
case "cancelConfirm":
|
||||
handleModalClose('cancel');
|
||||
handleReset();
|
||||
showModal(isView('modify') ? 'LAND_UPDATE_CONFIRM' : 'LAND_REGIST_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('registConfirm')
|
||||
});
|
||||
|
||||
break;
|
||||
case "registConfirm":
|
||||
setLoading(true);
|
||||
@@ -223,44 +213,38 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
if(isView('modify')){
|
||||
await LandAuctionModify(token, content?.id, resultData).then(data => {
|
||||
setLoading(false);
|
||||
handleModalClose('registConfirm');
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('registComplete');
|
||||
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
|
||||
}else if(data.result === "ERROR_AUCTION_STATUS_IMPOSSIBLE"){
|
||||
setAlertMsg(t('LAND_AUCTION_ERROR_MODIFY_STATUS'));
|
||||
showToast('LAND_AUCTION_ERROR_MODIFY_STATUS', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('UPDATE_FAIL'));
|
||||
showToast('UPDATE_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
handleReset();
|
||||
});
|
||||
}
|
||||
else{
|
||||
await LandAuctionSingleRegist(token, resultData).then(data => {
|
||||
setLoading(false);
|
||||
handleModalClose('registConfirm');
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('registComplete');
|
||||
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||
}else if(data.result === "ERROR_LAND_AUCTION_IMPOSSIBLE"){
|
||||
setAlertMsg(t('LAND_AUCTION_ERROR_PROGRESS'));
|
||||
showToast('LAND_AUCTION_ERROR_PROGRESS', {type: alertTypes.error});
|
||||
}else if(data.result === "ERROR_AUCTION_LAND_OWNER"){
|
||||
setAlertMsg(t('LAND_AUCTION_ERROR_OWNER'));
|
||||
showToast('LAND_AUCTION_ERROR_OWNER', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('REGIST_FAIL'));
|
||||
showToast('REGIST_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
handleReset();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "registComplete":
|
||||
handleModalClose('registComplete');
|
||||
handleReset();
|
||||
window.location.reload();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +355,6 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
startLabel="시작 일자"
|
||||
endLabel="종료 일자"
|
||||
reset={resetDateTime}
|
||||
setAlert={setAlertMsg}
|
||||
/>
|
||||
<DateTimeRangePicker
|
||||
label="경매기간"
|
||||
@@ -384,7 +367,6 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
startLabel="시작 일자"
|
||||
endLabel="종료 일자"
|
||||
reset={resetDateTime}
|
||||
setAlert={setAlertMsg}
|
||||
/>
|
||||
|
||||
{/*<NoticeInputRow2>*/}
|
||||
@@ -444,7 +426,13 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
/>
|
||||
:
|
||||
<>
|
||||
<Button text="취소" theme="line" handleClick={() => handleSubmit('cancel')} />
|
||||
<Button
|
||||
text="취소"
|
||||
theme="line"
|
||||
handleClick={() => showModal('CANCEL_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleReset()
|
||||
})} />
|
||||
<Button
|
||||
type="submit"
|
||||
text={isView('modify') ? "수정" : "등록"}
|
||||
@@ -462,36 +450,6 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
|
||||
{/* 확인 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.registConfirmModal}
|
||||
modalText={isView('modify') ? t('LAND_UPDATE_CONFIRM') : t('LAND_REGIST_CONFIRM')}
|
||||
handleSubmit={() => handleSubmit('registConfirm')}
|
||||
handleCancel={() => handleModalClose('registConfirm')}
|
||||
/>
|
||||
{/* 완료 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.registCompleteModal}
|
||||
modalText={isView('modify') ? t('UPDATE_COMPLETED') : t('REGIST_COMPLTE')}
|
||||
handleSubmit={() => handleSubmit('registComplete')}
|
||||
/>
|
||||
{/* 취소 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.cancelModal}
|
||||
modalText={t('CANCEL_CONFIRM')}
|
||||
handleCancel={() => handleModalClose('cancel')}
|
||||
handleSubmit={() => handleSubmit('cancelConfirm')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('warning')}
|
||||
/>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -25,28 +25,20 @@ import { NONE, TYPE_MODIFY, TYPE_REGISTRY } from '../../../assets/data/adminCons
|
||||
import { useModal } from '../../../hooks/hook';
|
||||
import { convertKTCDate } from '../../../utils';
|
||||
import { BattleEventModify, BattleEventSingleRegist } from '../../../apis/Battle';
|
||||
import { battleEventStatusType } from '../../../assets/data/types';
|
||||
import { alertTypes, battleEventStatusType } from '../../../assets/data/types';
|
||||
import { isValidDayRange } from '../../../utils/date';
|
||||
import CheckBox from '../../common/input/CheckBox';
|
||||
import { LandOwnedChangesRegist, LandOwnerChangesDelete, UserInfoView } from '../../../apis';
|
||||
import { useLoading } from '../../../context/LoadingProvider';
|
||||
import { useAlert } from '../../../context/AlertProvider';
|
||||
|
||||
const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, setDetailData }) => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
const [loading, setLoading] = useState(false); // 로딩 창
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
cancel: 'hidden',
|
||||
registConfirm: 'hidden',
|
||||
registComplete: 'hidden'
|
||||
});
|
||||
const {showModal, showToast} = useAlert();
|
||||
const {withLoading} = useLoading();
|
||||
|
||||
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
|
||||
const [resultData, setResultData] = useState(initData); //데이터 정보
|
||||
|
||||
@@ -126,94 +118,87 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
case "submit":
|
||||
if (!checkCondition()) return;
|
||||
|
||||
handleModalView('registConfirm');
|
||||
showModal(isView() ? 'LAND_OWNED_CHANGES_SELECT_DELETE' : 'LAND_OWNED_CHANGES_REGIST_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('registConfirm')
|
||||
});
|
||||
break;
|
||||
case "cancel":
|
||||
handleModalView('cancel');
|
||||
break;
|
||||
case "cancelConfirm":
|
||||
handleModalClose('cancel');
|
||||
handleReset();
|
||||
showModal('CANCEL_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleReset()
|
||||
});
|
||||
break;
|
||||
case "user":
|
||||
if(isView()) return;
|
||||
const guid = resultData.user_guid;
|
||||
if(!guid || guid.length !== 32){
|
||||
setAlertMsg(t('WARNING_GUID_CHECK'))
|
||||
showToast('WARNING_GUID_CHECK', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
await UserInfoView(token, guid).then(data => {
|
||||
|
||||
await withLoading(async () => {
|
||||
return await UserInfoView(token, guid);
|
||||
}).then(data => {
|
||||
if(Object.keys(data).length === 0){
|
||||
setAlertMsg(t('WARNING_GUID_CHECK'));
|
||||
showToast('WARNING_GUID_CHECK', {type: alertTypes.error});
|
||||
setResultData({ ...resultData, user_name: '' })
|
||||
return;
|
||||
}
|
||||
const nickname = data.char_info.character_name;
|
||||
setResultData({ ...resultData, user_name: nickname })
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
}).finally(()=>{
|
||||
setLoading(false);
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
});
|
||||
break;
|
||||
case "registConfirm":
|
||||
setLoading(true);
|
||||
|
||||
if(isView()){
|
||||
setLoading(false);
|
||||
handleModalClose('registConfirm');
|
||||
|
||||
const resvDt = resultData.reservation_dt;
|
||||
const now = new Date();
|
||||
if(resvDt < now){
|
||||
setAlertMsg(t('LAND_OWNED_CHANGES_DELETE_TIME_WARNING'));
|
||||
showToast('LAND_OWNED_CHANGES_DELETE_TIME_WARNING', {type: alertTypes.warning});
|
||||
handleReset();
|
||||
return;
|
||||
}
|
||||
|
||||
await LandOwnerChangesDelete(token, resultData).then(data => {
|
||||
handleModalClose('registConfirm');
|
||||
await withLoading(async () => {
|
||||
return await LandOwnerChangesDelete(token, resultData);
|
||||
}).then(data => {
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('registComplete');
|
||||
showToast('CANCEL_COMPLETED', {type: alertTypes.success});
|
||||
}else if(data.result === "ERROR_LAND_OWNER_CHANGES_RESERVATION"){
|
||||
setAlertMsg(t('LAND_OWNED_CHANGES_DELETE_STATUS_WARNING'));
|
||||
showToast('LAND_OWNED_CHANGES_DELETE_STATUS_WARNING', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('DELETE_FAIL'));
|
||||
showToast('DELETE_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
handleReset();
|
||||
});
|
||||
}else{
|
||||
await LandOwnedChangesRegist(token, resultData).then(data => {
|
||||
handleModalClose('registConfirm');
|
||||
await withLoading(async () => {
|
||||
return await LandOwnedChangesRegist(token, resultData);
|
||||
}).then(data => {
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('registComplete');
|
||||
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||
}else if(data.result === "GUID_CHECK"){
|
||||
setAlertMsg(t('WARNING_GUID_CHECK'));
|
||||
showToast('WARNING_GUID_CHECK', {type: alertTypes.error});
|
||||
}else if(data.result === "ERROR_LAND_OWNER_DUPLICATION"){
|
||||
setAlertMsg(t('LAND_OWNER_DUPLICATION_WARNING'));
|
||||
showToast('LAND_OWNER_DUPLICATION_WARNING', {type: alertTypes.error});
|
||||
}else if(data.result === "ERROR_LAND_OWNER_CHANGES_DUPLICATION"){
|
||||
setAlertMsg(t('LAND_OWNED_CHANGES_REGIST_DUPLICATION_WARNING'));
|
||||
showToast('LAND_OWNED_CHANGES_REGIST_DUPLICATION_WARNING', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('REGIST_FAIL'));
|
||||
showToast('REGIST_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
handleReset();
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
case "registComplete":
|
||||
handleModalClose('registComplete');
|
||||
handleReset();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -347,37 +332,6 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
|
||||
{/* 확인 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.registConfirmModal}
|
||||
modalText={isView() ? t('LAND_OWNED_CHANGES_SELECT_DELETE') : t('LAND_OWNED_CHANGES_REGIST_CONFIRM')}
|
||||
handleSubmit={() => handleSubmit('registConfirm')}
|
||||
handleCancel={() => handleModalClose('registConfirm')}
|
||||
/>
|
||||
{/* 완료 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.registCompleteModal}
|
||||
modalText={isView() ? t('CANCEL_COMPLETED') : t('REGIST_COMPLTE')}
|
||||
handleSubmit={() => handleSubmit('registComplete')}
|
||||
/>
|
||||
{/* 취소 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.cancelModal}
|
||||
modalText={t('CANCEL_CONFIRM')}
|
||||
handleCancel={() => handleModalClose('cancel')}
|
||||
handleSubmit={() => handleSubmit('cancelConfirm')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('warning')}
|
||||
/>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -76,15 +76,9 @@ const AdminViewSearchBar = ({ handleSearch, groupList, setResultData, setCurrent
|
||||
<>
|
||||
{/*<CheckBox id="input-check" label="가입 신청" checked={searchData.joinCheck} setData={e => setSearchData({ ...searchData, joinCheck: e.target.checked })} />*/}
|
||||
</>,
|
||||
<>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} type="button" />
|
||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
||||
</BtnWrapper>
|
||||
</>,
|
||||
];
|
||||
|
||||
return <SearchBarLayout firstColumnData={searchList} />;
|
||||
return <SearchBarLayout firstColumnData={searchList} direction={'column'} handleSubmit={handleSubmit} onReset={handleReset} />;
|
||||
};
|
||||
|
||||
export default AdminViewSearchBar;
|
||||
|
||||
134
src/components/ServiceManage/searchBar/CommonSearchBar.js
Normal file
134
src/components/ServiceManage/searchBar/CommonSearchBar.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import { TextInput, InputLabel, SelectInput, InputGroup } from '../../../styles/Components';
|
||||
import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar';
|
||||
import { Fragment } from 'react';
|
||||
import { getOptionsArray } from '../../../utils';
|
||||
|
||||
const renderSearchField = (field, searchParams, onSearch) => {
|
||||
const { type, id, label, placeholder, width, optionsRef } = field;
|
||||
|
||||
switch (type) {
|
||||
case 'text':
|
||||
return (
|
||||
<>
|
||||
{label && <InputLabel>{label}</InputLabel>}
|
||||
<TextInput
|
||||
type="text"
|
||||
placeholder={placeholder || ''}
|
||||
value={searchParams[id] || ''}
|
||||
width={width || '100%'}
|
||||
onChange={e => onSearch({ [id]: e.target.value }, false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
case 'select':
|
||||
return (
|
||||
<>
|
||||
{label && <InputLabel>{label}</InputLabel>}
|
||||
<SelectInput
|
||||
value={searchParams[id] || ''}
|
||||
onChange={e => onSearch({ [id]: e.target.value }, false)}
|
||||
>
|
||||
{getOptionsArray(optionsRef).map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</>
|
||||
);
|
||||
|
||||
case 'period':
|
||||
return (
|
||||
<>
|
||||
{label && <InputLabel>{label}</InputLabel>}
|
||||
<SearchPeriod
|
||||
startDate={searchParams[field.startDateId]}
|
||||
handleStartDate={date => onSearch({ [field.startDateId]: date }, false)}
|
||||
endDate={searchParams[field.endDateId]}
|
||||
handleEndDate={date => onSearch({ [field.endDateId]: date }, false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
case 'searchGroup':
|
||||
// 검색 타입과 입력이 결합된 컴포넌트
|
||||
return (
|
||||
<InputGroup>
|
||||
<SelectInput
|
||||
value={searchParams[field.selectId] || ''}
|
||||
onChange={e => onSearch({ [field.selectId]: e.target.value }, false)}
|
||||
>
|
||||
{getOptionsArray(field.optionsRef).map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
<TextInput
|
||||
type="text"
|
||||
placeholder={
|
||||
field.placeholderMapping &&
|
||||
field.placeholderMapping[searchParams[field.selectId]] ?
|
||||
field.placeholderMapping[searchParams[field.selectId]] :
|
||||
field.placeholderMapping?.default || ''
|
||||
}
|
||||
value={searchParams[field.inputId] || ''}
|
||||
width={field.width || '100%'}
|
||||
onChange={e => onSearch({ [field.inputId]: e.target.value }, false)}
|
||||
/>
|
||||
</InputGroup>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const CommonSearchBar = ({ config, searchParams, onSearch, onReset, customProps }) => {
|
||||
if (!config || !config.searchFields) {
|
||||
return <div>Loading search configuration...</div>;
|
||||
}
|
||||
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
onSearch(searchParams, true);
|
||||
};
|
||||
|
||||
// 검색 필드를 컬럼별로 분류
|
||||
const firstColumnFields = config.searchFields.filter(field => field.col === 1);
|
||||
const secondColumnFields = config.searchFields.filter(field => field.col === 2);
|
||||
const filterField = config.searchFields.find(field => field.col === 'filter');
|
||||
|
||||
const firstColumnData = firstColumnFields.map((field, index) => (
|
||||
<Fragment key={`first-${index}`}>
|
||||
{renderSearchField(field, searchParams, onSearch)}
|
||||
</Fragment>
|
||||
));
|
||||
|
||||
const secondColumnData = secondColumnFields.length > 0 ?
|
||||
secondColumnFields.map((field, index) => (
|
||||
<Fragment key={`second-${index}`}>
|
||||
{renderSearchField(field, searchParams, onSearch)}
|
||||
</Fragment>
|
||||
)) :
|
||||
undefined;
|
||||
|
||||
const filter = filterField ?
|
||||
renderSearchField(filterField, searchParams, onSearch) :
|
||||
undefined;
|
||||
|
||||
return (
|
||||
<SearchBarLayout
|
||||
firstColumnData={firstColumnData}
|
||||
secondColumnData={secondColumnData}
|
||||
filter={filter}
|
||||
direction={'column'}
|
||||
onReset={onReset}
|
||||
handleSubmit={handleSubmit}
|
||||
{...customProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommonSearchBar;
|
||||
@@ -129,7 +129,7 @@ const LandInfoSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
const searchList = [
|
||||
<>
|
||||
<InputGroup>
|
||||
<SelectInput value={searchParams.landType} onChange={e => onSearch({landType: e.target.value })}>
|
||||
<SelectInput value={searchParams.landType} onChange={e => onSearch({landType: e.target.value }, false)}>
|
||||
{landSearchType.map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
@@ -141,7 +141,7 @@ const LandInfoSearchBar = ({ searchParams, onSearch, onReset }) => {
|
||||
placeholder={searchParams.landType === 'ID' ? '랜드 ID 입력' : '랜드명 입력'}
|
||||
value={searchParams.landData}
|
||||
width="300px"
|
||||
onChange={e => onSearch({ landData: e.target.value })}
|
||||
onChange={e => onSearch({ landData: e.target.value }, false)}
|
||||
/>
|
||||
</InputGroup>
|
||||
</>,
|
||||
|
||||
@@ -109,15 +109,9 @@ const LogViewSearchBar = ({ handleSearch, resultData }) => {
|
||||
maxDate={new Date()}
|
||||
/>
|
||||
</>,
|
||||
<>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} type="button" />
|
||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
|
||||
</BtnWrapper>
|
||||
</>,
|
||||
];
|
||||
|
||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={periodList} direction={'column'} />;
|
||||
return <SearchBarLayout firstColumnData={searchList} secondColumnData={periodList} direction={'column'} handleSubmit={handleSubmit} onReset={handleReset} />;
|
||||
};
|
||||
|
||||
export default LogViewSearchBar;
|
||||
|
||||
182
src/components/ServiceManage/searchBar/useCommonSearch.js
Normal file
182
src/components/ServiceManage/searchBar/useCommonSearch.js
Normal file
@@ -0,0 +1,182 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { loadConfig } from '../../../utils';
|
||||
import * as APIs from '../../../apis';
|
||||
import { useAlert } from '../../../context/AlertProvider';
|
||||
import { alertTypes } from '../../../assets/data/types';
|
||||
|
||||
export const useCommonSearch = (token, configPath) => {
|
||||
const [config, setConfig] = useState(null);
|
||||
const [searchParams, setSearchParams] = useState({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState(null);
|
||||
const [configLoaded, setConfigLoaded] = useState(false);
|
||||
const { showToast } = useAlert();
|
||||
|
||||
// 설정 파일 로드
|
||||
useEffect(() => {
|
||||
const fetchConfig = async () => {
|
||||
try {
|
||||
const configData = await loadConfig(configPath);
|
||||
setConfig(configData);
|
||||
|
||||
// 초기 검색 파라미터 설정
|
||||
if (configData.initialSearchParams) {
|
||||
setSearchParams(configData.initialSearchParams);
|
||||
}
|
||||
|
||||
setConfigLoaded(true);
|
||||
} catch (error) {
|
||||
console.error('Error loading search configuration:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchConfig();
|
||||
}, [configPath]);
|
||||
|
||||
// 파라미터 값 변환 (날짜 등)
|
||||
const transformParam = (param, value) => {
|
||||
if (typeof param === 'object' && param.transform) {
|
||||
if (param.transform === 'toISOString' && value) {
|
||||
return new Date(value).toISOString();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
// API 호출에 필요한 파라미터 준비
|
||||
const prepareApiParams = useCallback((params) => {
|
||||
if (!config || !config.apiInfo || !config.apiInfo.paramsMapping) {
|
||||
return [token, params];
|
||||
}
|
||||
|
||||
// 파라미터 배열 매핑
|
||||
return config.apiInfo.paramsMapping.map(param => {
|
||||
if (param === 'token') return token;
|
||||
|
||||
if (typeof param === 'object') {
|
||||
return transformParam(param, params[param.param]);
|
||||
}
|
||||
|
||||
return params[param];
|
||||
});
|
||||
}, [token, config]);
|
||||
|
||||
// 데이터 가져오기
|
||||
const fetchData = useCallback(async (params) => {
|
||||
if (!token || !config || !config.apiInfo || !config.apiInfo.functionName) return;
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const apiParams = prepareApiParams(params);
|
||||
|
||||
// API 호출
|
||||
const functionName = config.apiInfo.functionName;
|
||||
if (!APIs[functionName]) {
|
||||
console.error(`API function ${functionName} not found!`);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await APIs[functionName](token, ...apiParams);
|
||||
|
||||
// 에러 처리
|
||||
if (result.result && result.result.startsWith('ERROR_')) {
|
||||
showToast('SEARCH_FAIL',{type: alertTypes.error})
|
||||
}
|
||||
|
||||
setData(result.data || result);
|
||||
return result.data || result;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching data:`, error);
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [token, config, prepareApiParams]);
|
||||
|
||||
// 초기 데이터 로드
|
||||
useEffect(() => {
|
||||
if (configLoaded && config && searchParams && config.apiInfo?.loadOnMount) {
|
||||
fetchData(searchParams);
|
||||
}
|
||||
}, [configLoaded, config, searchParams, fetchData]);
|
||||
|
||||
// 검색 파라미터 업데이트
|
||||
const updateSearchParams = useCallback((newParams) => {
|
||||
setSearchParams(prev => ({
|
||||
...prev,
|
||||
...newParams
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// 검색 처리
|
||||
const handleSearch = useCallback(async (newParams = {}, executeSearch = true) => {
|
||||
if (!config) return null;
|
||||
|
||||
const pageField = config.apiInfo?.pageField || 'currentPage';
|
||||
|
||||
const updatedParams = {
|
||||
...searchParams,
|
||||
...newParams,
|
||||
[pageField]: newParams[pageField] || 1 // 새 검색 시 첫 페이지로 리셋
|
||||
};
|
||||
|
||||
updateSearchParams(updatedParams);
|
||||
|
||||
if (executeSearch) {
|
||||
return await fetchData(updatedParams);
|
||||
}
|
||||
return null;
|
||||
}, [searchParams, fetchData, config, updateSearchParams]);
|
||||
|
||||
// 검색 초기화
|
||||
const handleReset = useCallback(async () => {
|
||||
if (!config || !config.initialSearchParams) return null;
|
||||
|
||||
setSearchParams(config.initialSearchParams);
|
||||
return await fetchData(config.initialSearchParams);
|
||||
}, [config, fetchData]);
|
||||
|
||||
// 페이지 변경
|
||||
const handlePageChange = useCallback(async (newPage) => {
|
||||
if (!config) return null;
|
||||
|
||||
const pageField = config.apiInfo?.pageField || 'currentPage';
|
||||
return await handleSearch({ [pageField]: newPage }, true);
|
||||
}, [handleSearch, config]);
|
||||
|
||||
// 페이지 크기 변경
|
||||
const handlePageSizeChange = useCallback(async (newSize) => {
|
||||
if (!config) return null;
|
||||
|
||||
const pageField = config.apiInfo?.pageField || 'currentPage';
|
||||
const pageSizeField = config.apiInfo?.pageSizeField || 'pageSize';
|
||||
return await handleSearch({
|
||||
[pageSizeField]: newSize,
|
||||
[pageField]: 1
|
||||
}, true);
|
||||
}, [handleSearch, config]);
|
||||
|
||||
// 정렬 방식 변경
|
||||
const handleOrderByChange = useCallback(async (newOrder) => {
|
||||
if (!config) return null;
|
||||
|
||||
const orderField = config.apiInfo?.orderField || 'orderBy';
|
||||
return await handleSearch({ [orderField]: newOrder }, true);
|
||||
}, [handleSearch, config]);
|
||||
|
||||
return {
|
||||
config,
|
||||
searchParams,
|
||||
loading,
|
||||
data,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleOrderByChange,
|
||||
updateSearchParams,
|
||||
configLoaded
|
||||
};
|
||||
};
|
||||
|
||||
export default useCommonSearch;
|
||||
@@ -33,7 +33,7 @@ const AuthRegistBar = ({ handleRegistModalClose, $isNullValue, registData, setRe
|
||||
</>,
|
||||
];
|
||||
|
||||
return <SearchBarLayout firstColumnData={searchList} />;
|
||||
return <SearchBarLayout firstColumnData={searchList} isSearch={false} direction='column' />;
|
||||
};
|
||||
|
||||
export default AuthRegistBar;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useState, Fragment, useEffect } from 'react';
|
||||
import React, { useState, Fragment, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../common/button/Button';
|
||||
import Loading from '../common/Loading';
|
||||
|
||||
import {
|
||||
Title,
|
||||
@@ -13,23 +12,20 @@ import {
|
||||
FormGroup, FormHelperText, FormInput, FormLabel,
|
||||
FormTextArea, FormTextAreaWrapper, MessageWrapper, FormRowGroup, FormRowInput,
|
||||
} from '../../styles/ModuleComponents';
|
||||
import { modalTypes, caliumRequestInitData } from '../../assets/data';
|
||||
import {DynamicModal, Modal} from '../common';
|
||||
import { caliumRequestInitData } from '../../assets/data';
|
||||
import { Modal} from '../common';
|
||||
import { CaliumLimitCount, CaliumRequestRegist } from '../../apis/Calium';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
|
||||
const CaliumRequestRegistModal = ({ registView, setRegistView, userInfo }) => {
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
const [loading, setLoading] = useState(false); // 로딩 창
|
||||
const [modalState, setModalState] = useState({
|
||||
cancelModal: 'hidden',
|
||||
registConfirmModal: 'hidden',
|
||||
registCompleteModal: 'hidden',
|
||||
}); // 모달 관리
|
||||
const {showModal, showToast } = useAlert();
|
||||
const {withLoading} = useLoading();
|
||||
|
||||
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
|
||||
const [resultData, setResultData] = useState(caliumRequestInitData); //데이터 정보
|
||||
const [maxCount, setMaxCount] = useState(0)
|
||||
@@ -75,69 +71,42 @@ const CaliumRequestRegistModal = ({ registView, setRegistView, userInfo }) => {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleModalView = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'view',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalClose = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'hidden',
|
||||
}));
|
||||
}
|
||||
|
||||
const initData = () =>{
|
||||
const handleReset = () =>{
|
||||
setMaxCount(0);
|
||||
setResultData(caliumRequestInitData);
|
||||
setRegistView();
|
||||
}
|
||||
|
||||
const handleSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "maxCount":
|
||||
setLoading(true);
|
||||
await CaliumLimitCount(token, resultData).then(data => {
|
||||
await withLoading(async () => {
|
||||
return await CaliumLimitCount(token, resultData);
|
||||
}).then(data => {
|
||||
setMaxCount(data.reward_total_count);
|
||||
setLoading(false);
|
||||
}).catch(reason => {
|
||||
console.log(reason);
|
||||
setLoading(false);
|
||||
setAlertMsg(t('SEARCH_LIMIT_FAIL'));
|
||||
})
|
||||
showToast('SEARCH_LIMIT_FAIL', {type: alertTypes.error})
|
||||
});
|
||||
break;
|
||||
case "submit":
|
||||
if (!checkCondition()) return;
|
||||
|
||||
handleModalView('registConfirm');
|
||||
break;
|
||||
case "cancel":
|
||||
handleModalView('cancel');
|
||||
break;
|
||||
case "cancelConfirm":
|
||||
initData();
|
||||
handleModalClose('cancel');
|
||||
setRegistView();
|
||||
showModal('CALIUM_REGIST_CONFIRM',{
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('registConfirm')
|
||||
})
|
||||
break;
|
||||
|
||||
case "registConfirm":
|
||||
setLoading(true);
|
||||
|
||||
await CaliumRequestRegist(token, resultData).then(data => {
|
||||
setLoading(false);
|
||||
await withLoading(async () => {
|
||||
return await CaliumRequestRegist(token, resultData);
|
||||
}).then(data => {
|
||||
showToast('CALIUM_REGIST_COMPLTE', {type: alertTypes.success});
|
||||
}).catch(error => {
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
handleReset();
|
||||
});
|
||||
|
||||
handleModalClose('registConfirm');
|
||||
handleModalView('registComplete');
|
||||
break;
|
||||
case "registComplete":
|
||||
initData();
|
||||
handleModalClose('registComplete');
|
||||
setRegistView();
|
||||
window.location.reload();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -224,7 +193,14 @@ const CaliumRequestRegistModal = ({ registView, setRegistView, userInfo }) => {
|
||||
</MessageWrapper>
|
||||
|
||||
<BtnWrapper $gap="10px" $justify="center" $marginTop="20px">
|
||||
<Button text="취소" theme="line" handleClick={() => handleSubmit('cancel')} />
|
||||
<Button
|
||||
text="취소"
|
||||
theme="line"
|
||||
handleClick={() => showModal('CANCEL_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleReset()
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
text="등록"
|
||||
@@ -239,37 +215,6 @@ const CaliumRequestRegistModal = ({ registView, setRegistView, userInfo }) => {
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
|
||||
{/* 확인 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.registConfirmModal}
|
||||
modalText={t('CALIUM_REGIST_CONFIRM')}
|
||||
handleSubmit={() => handleSubmit('registConfirm')}
|
||||
handleCancel={() => handleModalClose('registConfirm')}
|
||||
/>
|
||||
{/* 완료 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.registCompleteModal}
|
||||
modalText={t('CALIUM_REGIST_COMPLTE')}
|
||||
handleSubmit={() => handleSubmit('registComplete')}
|
||||
/>
|
||||
{/* 취소 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.cancelModal}
|
||||
modalText={t('CALIUM_REGIST_CANCEL')}
|
||||
handleCancel={() => handleModalClose('cancel')}
|
||||
handleSubmit={() => handleSubmit('cancelConfirm')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('warning')}
|
||||
/>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
112
src/components/common/Custom/CaliTable.js
Normal file
112
src/components/common/Custom/CaliTable.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { DetailMessage, TableStyle, TableWrapper } from '../../../styles/Components';
|
||||
import { StatusLabel } from '../../../styles/ModuleComponents';
|
||||
import { Button, CheckBox } from '../index';
|
||||
import { convertKTC, getOptionsArray } from '../../../utils';
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
const CaliTable = ({
|
||||
columns,
|
||||
data,
|
||||
selectedRows = [],
|
||||
onSelectRow,
|
||||
onAction,
|
||||
refProp
|
||||
}) => {
|
||||
|
||||
const renderCell = (column, item) => {
|
||||
const { type, id, option_name, format, action } = column;
|
||||
const value = item[id];
|
||||
|
||||
const options = getOptionsArray(option_name);
|
||||
|
||||
switch (type) {
|
||||
case 'text':
|
||||
return value;
|
||||
|
||||
case 'date':
|
||||
return convertKTC(value);
|
||||
|
||||
case 'status':
|
||||
const statusOption = options.find(opt => opt.value === value);
|
||||
return (
|
||||
<StatusWapper>
|
||||
<StatusLabel $status={value}>
|
||||
{statusOption ? statusOption.name : value}
|
||||
</StatusLabel>
|
||||
</StatusWapper>
|
||||
);
|
||||
|
||||
case 'button':
|
||||
return (
|
||||
<Button
|
||||
theme="line"
|
||||
text={column.text || "액션"}
|
||||
handleClick={() => onAction(id, item)}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'checkbox':
|
||||
return (
|
||||
<CheckBox
|
||||
name={column.name || 'select'}
|
||||
id={item.id}
|
||||
setData={(e) => onSelectRow(e, item)}
|
||||
checked={selectedRows.some(row => row.id === item.id)}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'option':
|
||||
const dataOption = options.find(opt => opt.value === value);
|
||||
return (
|
||||
dataOption ? dataOption.name : value
|
||||
);
|
||||
|
||||
case "link":
|
||||
return (
|
||||
<DetailMessage onClick={() => onAction(action)}>
|
||||
{value.content.length > 20 ? value.content.slice(0, 20) + '...' : value.content || ''}
|
||||
</DetailMessage>
|
||||
);
|
||||
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TableWrapper>
|
||||
<TableStyle ref={refProp}>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((column, index) => (
|
||||
<th key={index} width={column.width || 'auto'}>
|
||||
{column.title}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.map((item, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
{columns.map((column, colIndex) => (
|
||||
<td key={colIndex}>
|
||||
{renderCell(column, item)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default CaliTable;
|
||||
|
||||
const StatusWapper = styled.div`
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
} from '../../../styles/ModuleComponents';
|
||||
import { HourList, MinuteList } from '../../../assets/data';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAlert } from '../../../context/AlertProvider';
|
||||
import { alertTypes } from '../../../assets/data/types';
|
||||
|
||||
const DateTimeRangePicker = ({
|
||||
label,
|
||||
@@ -19,10 +21,11 @@ const DateTimeRangePicker = ({
|
||||
disabled,
|
||||
startLabel = '시작 일자',
|
||||
endLabel = '종료 일자',
|
||||
reset = false,
|
||||
setAlert
|
||||
reset = false
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { showToast } = useAlert();
|
||||
|
||||
const [startHour, setStartHour] = useState('00');
|
||||
const [startMin, setStartMin] = useState('00');
|
||||
const [endHour, setEndHour] = useState('00');
|
||||
@@ -64,7 +67,7 @@ const DateTimeRangePicker = ({
|
||||
newDate.setHours(parseInt(endHour), parseInt(endMin));
|
||||
|
||||
if (startDate && newDate < startDate) {
|
||||
setAlert(t('TIME_START_DIFF_END'));
|
||||
showToast('TIME_START_DIFF_END', {type: alertTypes.warning});
|
||||
newDate = new Date(startDate);
|
||||
}
|
||||
|
||||
@@ -99,7 +102,7 @@ const DateTimeRangePicker = ({
|
||||
}
|
||||
|
||||
if (startDate && newDate < startDate) {
|
||||
setAlert(t('TIME_START_DIFF_END'));
|
||||
showToast('TIME_START_DIFF_END', {type: alertTypes.warning});
|
||||
newDate = new Date(startDate)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { styled } from 'styled-components';
|
||||
import { TextInput, SelectInput, SearchBarAlert, BtnWrapper } from '../../../styles/Components';
|
||||
import Button from '../button/Button';
|
||||
|
||||
const SearchBarLayout = ({ firstColumnData, secondColumnData, filter, direction, onReset, handleSubmit }) => {
|
||||
const SearchBarLayout = ({ firstColumnData, secondColumnData, filter, direction, onReset, handleSubmit, isSearch = true }) => {
|
||||
return (
|
||||
<SearchbarStyle direction={direction}>
|
||||
<SearchRow>
|
||||
@@ -22,12 +22,14 @@ const SearchBarLayout = ({ firstColumnData, secondColumnData, filter, direction,
|
||||
{filter}
|
||||
</SearchRow>
|
||||
)}
|
||||
<SearchRow>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="button" />
|
||||
<Button theme="reset" handleClick={onReset} type="button" />
|
||||
</BtnWrapper>
|
||||
</SearchRow>
|
||||
{isSearch &&
|
||||
<SearchRow>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="search" text="검색" handleClick={handleSubmit} type="button" />
|
||||
<Button theme="reset" handleClick={onReset} type="button" />
|
||||
</BtnWrapper>
|
||||
</SearchRow>
|
||||
}
|
||||
</SearchbarStyle>
|
||||
);
|
||||
};
|
||||
|
||||
80
src/components/common/Table/TableHeader.js
Normal file
80
src/components/common/Table/TableHeader.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { authList } from '../../../store/authList';
|
||||
import { authType } from '../../../assets/data';
|
||||
import { Button, ExcelDownButton, ViewTableInfo } from '../index';
|
||||
|
||||
const TableHeader = ({
|
||||
config,
|
||||
tableRef,
|
||||
total,
|
||||
total_all,
|
||||
handleOrderBy,
|
||||
handlePageSize,
|
||||
selectedRows = [],
|
||||
onAction,
|
||||
navigate
|
||||
}) => {
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleButtonClick = (button, e) => {
|
||||
e?.preventDefault();
|
||||
|
||||
if (button.action === 'navigate' && button.navigateTo && navigate) {
|
||||
navigate(button.navigateTo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (onAction) {
|
||||
onAction(button.action, button.id);
|
||||
}
|
||||
};
|
||||
|
||||
const renderButton = (button, index) => {
|
||||
const hasAuth = button.requiredAuth ?
|
||||
userInfo.auth_list?.some(auth => auth.id === authType[button.requiredAuth]) :
|
||||
true;
|
||||
|
||||
if (!hasAuth) return null;
|
||||
|
||||
if (button.component === 'ExcelDownButton') {
|
||||
return (
|
||||
<ExcelDownButton
|
||||
key={index}
|
||||
tableRef={tableRef}
|
||||
fileName={button.props?.fileName ? t(button.props.fileName) : ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const buttonTheme = button.disableWhen === 'noSelection' && selectedRows.length === 0
|
||||
? 'disable'
|
||||
: button.theme;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={index}
|
||||
theme={buttonTheme}
|
||||
text={button.text}
|
||||
handleClick={(e) => handleButtonClick(button, e)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewTableInfo
|
||||
total={total}
|
||||
total_all={total_all}
|
||||
handleOrderBy={handleOrderBy}
|
||||
handlePageSize={handlePageSize}
|
||||
orderType={config.orderType}
|
||||
pageType={config.pageType}
|
||||
countType={config.countType}
|
||||
>
|
||||
{config.buttons.map(renderButton)}
|
||||
</ViewTableInfo>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableHeader;
|
||||
@@ -4,28 +4,28 @@ import {
|
||||
SelectInput,
|
||||
TableInfo,
|
||||
} from '../../../styles/Components';
|
||||
import { ViewTitleCountType } from '../../../assets/data';
|
||||
import { ORDER_OPTIONS, PAGE_SIZE_OPTIONS, ViewTitleCountType } from '../../../assets/data';
|
||||
import { TitleItem, TitleItemLabel, TitleItemValue } from '../../../styles/ModuleComponents';
|
||||
|
||||
const ViewTableInfo = ({children, total, total_all, orderType, handleOrderBy, pageType, handlePageSize, countType = ViewTitleCountType.total}) => {
|
||||
const ViewTableInfo = ({
|
||||
children,
|
||||
total,
|
||||
total_all,
|
||||
orderType = 'desc',
|
||||
handleOrderBy,
|
||||
pageType = 'default',
|
||||
handlePageSize,
|
||||
countType = ViewTitleCountType.total
|
||||
}) => {
|
||||
return (
|
||||
<TableInfo>
|
||||
{total !== undefined && total_all !== undefined &&
|
||||
<ListCount>
|
||||
{ countType === ViewTitleCountType.total && `총 : ${total ?? 0} 건 / ${total_all ?? 0} 건`}
|
||||
{ countType === ViewTitleCountType.calium &&
|
||||
<>
|
||||
<TitleItem>
|
||||
<TitleItemLabel>누적 충전</TitleItemLabel>
|
||||
<TitleItemValue color='#b7e0c3' fontWeight='bold'>{total_all ?? 0}</TitleItemValue>
|
||||
</TitleItem>
|
||||
<TitleItem>
|
||||
<TitleItemLabel>잔여 수량</TitleItemLabel>
|
||||
<TitleItemValue color='#B39063' fontWeight='bold'>{total ?? 0}</TitleItemValue>
|
||||
</TitleItem>
|
||||
</>
|
||||
}
|
||||
</ListCount>}
|
||||
<ListCount>
|
||||
{COUNT_TYPE_RENDERERS[countType] ?
|
||||
COUNT_TYPE_RENDERERS[countType](total, total_all) :
|
||||
COUNT_TYPE_RENDERERS[ViewTitleCountType.total](total, total_all)}
|
||||
</ListCount>
|
||||
}
|
||||
<ListOption>
|
||||
<OrderBySelect orderType={orderType} handleOrderBy={handleOrderBy} />
|
||||
<PageSelect pageType={pageType} handlePageSize={handlePageSize} />
|
||||
@@ -35,36 +35,44 @@ const ViewTableInfo = ({children, total, total_all, orderType, handleOrderBy, pa
|
||||
);
|
||||
};
|
||||
|
||||
const OrderBySelect = ({orderType, handleOrderBy}) => {
|
||||
return(
|
||||
orderType === "asc" ?
|
||||
<SelectInput className="input-select" onChange={e => handleOrderBy(e.target.value)}>
|
||||
<option value="ASC">오름차순</option>
|
||||
<option value="DESC">내림차순</option>
|
||||
</SelectInput>
|
||||
:
|
||||
<SelectInput className="input-select" onChange={e => handleOrderBy(e.target.value)}>
|
||||
<option value="DESC">내림차순</option>
|
||||
<option value="ASC">오름차순</option>
|
||||
</SelectInput>
|
||||
);
|
||||
}
|
||||
const COUNT_TYPE_RENDERERS = {
|
||||
[ViewTitleCountType.total]: (total, total_all) => `총 : ${total ?? 0} 건 / ${total_all ?? 0} 건`,
|
||||
[ViewTitleCountType.calium]: (total, total_all) => (
|
||||
<>
|
||||
<TitleItem>
|
||||
<TitleItemLabel>누적 충전</TitleItemLabel>
|
||||
<TitleItemValue color='#b7e0c3' fontWeight='bold'>{total_all ?? 0}</TitleItemValue>
|
||||
</TitleItem>
|
||||
<TitleItem>
|
||||
<TitleItemLabel>잔여 수량</TitleItemLabel>
|
||||
<TitleItemValue color='#B39063' fontWeight='bold'>{total ?? 0}</TitleItemValue>
|
||||
</TitleItem>
|
||||
</>
|
||||
),
|
||||
};
|
||||
|
||||
const PageSelect = ({pageType, handlePageSize}) => {
|
||||
return(
|
||||
pageType === "B" ?
|
||||
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e.target.value)}>
|
||||
<option value="500">500개</option>
|
||||
<option value="1000">1000개</option>
|
||||
<option value="5000">5000개</option>
|
||||
<option value="10000">10000개</option>
|
||||
</SelectInput>
|
||||
:
|
||||
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e.target.value)}>
|
||||
<option value="50">50개</option>
|
||||
<option value="100">100개</option>
|
||||
</SelectInput>
|
||||
const OrderBySelect = ({ orderType, handleOrderBy }) => {
|
||||
const options = ORDER_OPTIONS[orderType] || ORDER_OPTIONS.desc;
|
||||
|
||||
return (
|
||||
<SelectInput className="input-select" onChange={e => handleOrderBy(e.target.value)}>
|
||||
{options.map(option => (
|
||||
<option key={option.value} value={option.value}>{option.label}</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const PageSelect = ({ pageType, handlePageSize }) => {
|
||||
const options = PAGE_SIZE_OPTIONS[pageType] || PAGE_SIZE_OPTIONS.default;
|
||||
|
||||
return (
|
||||
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e.target.value)}>
|
||||
{options.map(option => (
|
||||
<option key={option.value} value={option.value}>{option.label}</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewTableInfo;
|
||||
156
src/components/common/alert/ToastAlert.js
Normal file
156
src/components/common/alert/ToastAlert.js
Normal file
@@ -0,0 +1,156 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled, { keyframes, css, createGlobalStyle } from 'styled-components';
|
||||
import { alertTypes } from '../../../assets/data/types';
|
||||
|
||||
const ToastAlert = ({ id, message, type = alertTypes.info, position = 'top-center', onClose }) => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const handleClose = () => {
|
||||
setIsVisible(true);
|
||||
setTimeout(() => {
|
||||
onClose();
|
||||
}, 300);
|
||||
};
|
||||
|
||||
return (
|
||||
<ToastContainer $type={type} $position={position} $isVisible={isVisible}>
|
||||
<IconWrapper $type={type}>
|
||||
<ToastIcon type={type} />
|
||||
</IconWrapper>
|
||||
<ToastMessage>{message}</ToastMessage>
|
||||
<CloseButton onClick={handleClose}>×</CloseButton>
|
||||
</ToastContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const ToastIcon = ({ type }) => {
|
||||
switch (type) {
|
||||
case alertTypes.success:
|
||||
return <span>✓</span>;
|
||||
case alertTypes.error:
|
||||
return <span>✗</span>;
|
||||
case alertTypes.warning:
|
||||
return <span>⚠</span>;
|
||||
case alertTypes.info:
|
||||
default:
|
||||
return <span>ℹ</span>;
|
||||
}
|
||||
};
|
||||
|
||||
const fadeIn = keyframes`
|
||||
from { opacity: 0; transform: translateX(-50%) translateY(-20px); }
|
||||
to { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||
`;
|
||||
|
||||
const fadeOut = keyframes`
|
||||
from { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||
to { opacity: 0; transform: translateX(-50%) translateY(-20px); }
|
||||
`;
|
||||
|
||||
// 위치에 따른 스타일 지정 함수
|
||||
const getPositionStyle = (position) => {
|
||||
const positions = {
|
||||
'top-left': css`
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
`,
|
||||
'top-center': css`
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
`,
|
||||
'top-right': css`
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
`,
|
||||
'bottom-left': css`
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
`,
|
||||
'bottom-center': css`
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
`,
|
||||
'bottom-right': css`
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
`
|
||||
};
|
||||
|
||||
return positions[position] || positions['top-center'];
|
||||
};
|
||||
|
||||
// 타입에 따른 스타일 지정 함수
|
||||
const getTypeStyle = (type) => {
|
||||
const types = {
|
||||
[alertTypes.success]: css`
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border-color: #c3e6cb;
|
||||
`,
|
||||
[alertTypes.error]: css`
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border-color: #f5c6cb;
|
||||
`,
|
||||
[alertTypes.warning]: css`
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
border-color: #ffeeba;
|
||||
`,
|
||||
[alertTypes.info]: css`
|
||||
background-color: #d1ecf1;
|
||||
color: #0c5460;
|
||||
border-color: #bee5eb;
|
||||
`
|
||||
};
|
||||
|
||||
return types[type] || types['info'];
|
||||
};
|
||||
|
||||
const ToastContainer = styled.div`
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 250px;
|
||||
max-width: 450px;
|
||||
padding: 12px 15px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 10px;
|
||||
z-index: 9999;
|
||||
|
||||
animation: ${props => props.$isExiting ? fadeOut : fadeIn} 0.3s ease forwards;
|
||||
|
||||
${props => getPositionStyle(props.$position)}
|
||||
${props => getTypeStyle(props.$type)}
|
||||
`;
|
||||
|
||||
const IconWrapper = styled.div`
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
const ToastMessage = styled.div`
|
||||
flex: 1;
|
||||
padding-right: 10px;
|
||||
`;
|
||||
|
||||
const CloseButton = styled.button`
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export default ToastAlert;
|
||||
53
src/components/common/button/VerticalDotsButton.js
Normal file
53
src/components/common/button/VerticalDotsButton.js
Normal file
@@ -0,0 +1,53 @@
|
||||
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;
|
||||
@@ -14,10 +14,14 @@ import Pagination from './Pagination/Pagination';
|
||||
import DynamoPagination from './Pagination/DynamoPagination';
|
||||
import FrontPagination from './Pagination/FrontPagination';
|
||||
import ViewTableInfo from './Table/ViewTableInfo';
|
||||
import TableHeader from './Table/TableHeader';
|
||||
import Loading from './Loading';
|
||||
import DownloadProgress from './DownloadProgress';
|
||||
import CDivider from './CDivider';
|
||||
import TopButton from './button/TopButton';
|
||||
|
||||
import CaliTable from './Custom/CaliTable'
|
||||
|
||||
export {
|
||||
DatePickerComponent,
|
||||
DateTimeRangePicker,
|
||||
@@ -41,10 +45,12 @@ export { DateTimeInput,
|
||||
Modal,
|
||||
Pagination,
|
||||
ViewTableInfo,
|
||||
TableHeader,
|
||||
Loading,
|
||||
CDivider,
|
||||
TopButton,
|
||||
DynamoPagination,
|
||||
FrontPagination,
|
||||
DownloadProgress
|
||||
DownloadProgress,
|
||||
CaliTable
|
||||
};
|
||||
231
src/components/common/input/MessageInput.js
Normal file
231
src/components/common/input/MessageInput.js
Normal file
@@ -0,0 +1,231 @@
|
||||
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;
|
||||
}
|
||||
`;
|
||||
@@ -53,10 +53,6 @@ const DynamicModal = ({modalType, view, handleSubmit, handleCancel, modalText, c
|
||||
);
|
||||
case modalTypes.childOkCancel:
|
||||
return (
|
||||
// <ModalWrapper view={view} modalText={modalText} handleCancel={handleCancel} children={children} >
|
||||
// <CancelButton handleClick={handleCancel} />
|
||||
// <OkButton handleClick={handleSubmit} />
|
||||
// </ModalWrapper>
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={view}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleCancel} />
|
||||
|
||||
155
src/context/AlertProvider.js
Normal file
155
src/context/AlertProvider.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import React, { createContext, useContext, useState, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DynamicModal from '../components/common/modal/DynamicModal';
|
||||
import { modalTypes } from '../assets/data';
|
||||
import ToastAlert from '../components/common/alert/ToastAlert';
|
||||
import { alertTypes } from '../assets/data/types';
|
||||
|
||||
const AlertContext = createContext();
|
||||
|
||||
export const AlertProvider = ({ children }) => {
|
||||
const { t } = useTranslation();
|
||||
// 모달 알림 상태
|
||||
const [modalState, setModalState] = useState({
|
||||
isVisible: false,
|
||||
message: '',
|
||||
type: alertTypes.info,
|
||||
onConfirm: null,
|
||||
onCancel: null,
|
||||
children: null
|
||||
});
|
||||
|
||||
// 토스트 알림 상태 (여러 개를 관리하기 위해 배열 사용)
|
||||
const [toasts, setToasts] = useState([]);
|
||||
|
||||
// 토스트 알림 표시 함수
|
||||
const showToast = useCallback((message, options = {}) => {
|
||||
const {
|
||||
type = alertTypes.info,
|
||||
duration = 5000,
|
||||
translateKey = true,
|
||||
position = 'top-center' // 'top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'
|
||||
} = options;
|
||||
|
||||
const id = Date.now();
|
||||
const newToast = {
|
||||
id,
|
||||
message: translateKey ? t(message) : message,
|
||||
type,
|
||||
duration,
|
||||
position
|
||||
};
|
||||
|
||||
setToasts(prev => [...prev, newToast]);
|
||||
|
||||
// 자동 소멸
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
removeToast(id);
|
||||
}, duration);
|
||||
}
|
||||
}, [t]);
|
||||
|
||||
// 토스트 알림 제거 함수
|
||||
const removeToast = useCallback((id) => {
|
||||
setToasts(prev => prev.filter(toast => toast.id !== id));
|
||||
}, []);
|
||||
|
||||
// 모달 알림 표시 함수
|
||||
const showModal = useCallback((message, options = {}) => {
|
||||
const {
|
||||
type = alertTypes.info,
|
||||
onConfirm = null,
|
||||
onCancel = null,
|
||||
translateKey = true,
|
||||
children = null
|
||||
} = options;
|
||||
|
||||
setModalState({
|
||||
isVisible: true,
|
||||
message: translateKey ? t(message) : message,
|
||||
type,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
children
|
||||
});
|
||||
}, [t]);
|
||||
|
||||
// 모달 알림 숨기기 함수
|
||||
const hideModal = useCallback(() => {
|
||||
setModalState(prev => ({
|
||||
...prev,
|
||||
isVisible: false
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// 모달 확인 핸들러
|
||||
const handleConfirm = useCallback(() => {
|
||||
if (modalState.onConfirm) {
|
||||
modalState.onConfirm();
|
||||
}
|
||||
hideModal();
|
||||
}, [modalState.onConfirm, hideModal]);
|
||||
|
||||
// 모달 취소 핸들러
|
||||
const handleCancel = useCallback(() => {
|
||||
if (modalState.onCancel) {
|
||||
modalState.onCancel();
|
||||
}
|
||||
hideModal();
|
||||
}, [modalState.onCancel, hideModal]);
|
||||
|
||||
// 모달 타입에 따른 모달 타입 결정
|
||||
const getModalType = () => {
|
||||
switch (modalState.type) {
|
||||
case alertTypes.confirm:
|
||||
return modalTypes.confirmOkCancel;
|
||||
case alertTypes.confirmChildren:
|
||||
return modalTypes.childOkCancel;
|
||||
case alertTypes.success:
|
||||
case alertTypes.warning:
|
||||
case alertTypes.error:
|
||||
case alertTypes.info:
|
||||
default:
|
||||
return modalTypes.completed;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertContext.Provider value={{ showToast, removeToast, showModal, hideModal }}>
|
||||
{children}
|
||||
|
||||
{/* 토스트 알림 컨테이너 */}
|
||||
<div className="toast-container">
|
||||
{toasts.map(toast => (
|
||||
<ToastAlert
|
||||
key={toast.id}
|
||||
id={toast.id}
|
||||
message={toast.message}
|
||||
type={toast.type}
|
||||
position={toast.position}
|
||||
onClose={() => removeToast(toast.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 모달 알림 */}
|
||||
<DynamicModal
|
||||
modalType={getModalType()}
|
||||
view={modalState.isVisible ? 'view' : 'hidden'}
|
||||
modalText={modalState.message}
|
||||
handleSubmit={handleConfirm}
|
||||
handleCancel={modalState.type === alertTypes.confirm || modalState.type === alertTypes.confirmChildren ? handleCancel : null}
|
||||
children={modalState.children}
|
||||
/>
|
||||
</AlertContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAlert = () => {
|
||||
const context = useContext(AlertContext);
|
||||
if (!context) {
|
||||
throw new Error('useAlert must be used within an AlertProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
37
src/context/LoadingProvider.js
Normal file
37
src/context/LoadingProvider.js
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import Loading from '../components/common/Loading';
|
||||
|
||||
const LoadingContext = createContext();
|
||||
|
||||
export const LoadingProvider = ({ children }) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const showLoading = () => setIsLoading(true);
|
||||
const hideLoading = () => setIsLoading(false);
|
||||
|
||||
const withLoading = async (asyncFunction) => {
|
||||
const startTime = Date.now();
|
||||
showLoading();
|
||||
try {
|
||||
return await asyncFunction();
|
||||
} finally {
|
||||
const processTime = Date.now() - startTime;
|
||||
|
||||
if(processTime < 1000){
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
|
||||
hideLoading();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<LoadingContext.Provider value={{ isLoading, showLoading, hideLoading, withLoading }}>
|
||||
{children}
|
||||
{isLoading && <Loading />}
|
||||
</LoadingContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useLoading = () => useContext(LoadingContext);
|
||||
30
src/i18n.js
30
src/i18n.js
@@ -13,7 +13,8 @@ const resources = {
|
||||
UPDATE_FAIL: '수정에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
||||
STOP_FAIL: '중단에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
||||
DELETE_FAIL: '삭제에 실패하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
||||
API_FAIL: '처리 중 오류가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요. 오류가 지속될 경우, 담당자에게 문의해주세요.',
|
||||
API_FAIL: '처리 중 오류가 발생하였습니다. 새로고침 후 다시 한번 진행해 주세요. 오류가 지속될 경우, 담당자에게 문의해주세요.',
|
||||
SEARCH_FAIL: '조회 중 에러가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요.',
|
||||
USER_MAIL_DEL_CONFIRM: '해당 우편을 삭제하시겠습니까?',
|
||||
USER_GM_CHANGE: 'GM 권한을 변경하시겠습니까?',
|
||||
USER_GM_CHANGE_COMPLETE: '권한변경을 완료하였습니다.',
|
||||
@@ -41,6 +42,10 @@ const resources = {
|
||||
WARNING_NICKNAME_CHECK: '닉네임을 확인해주세요.',
|
||||
WARNING_EMAIL_CHECK: '이메일을 확인해주세요.',
|
||||
WARNING_TYPE_CHECK: '타입을 확인해주세요.',
|
||||
DATE_START_DIFF_END_WARNING :"종료일은 시작일보다 하루 이후여야 합니다.",
|
||||
//table
|
||||
TABLE_ITEM_DELETE_TITLE: "선택 삭제",
|
||||
TABLE_BUTTON_DETAIL_TITLE: "상세보기",
|
||||
//db
|
||||
LOG_MEMORY_LIMIT_WARNING: '데이터가 너무 많아 조회할 수 없습니다.\n조회조건 조정 후 다시 조회해주세요.',
|
||||
LOG_MONGGDB_QUERY_WARNING: '조회 중 오류가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요.\n오류가 지속될 경우, 담당자에게 문의해주세요.',
|
||||
@@ -88,7 +93,13 @@ const resources = {
|
||||
MAIL_ITEM_ADD_DUPL: '이미 추가된 아이템입니다. 삭제 후 다시 추가해주세요.',
|
||||
MAIL_ITEM_ADD_BEN: '첨부 할 수 없는 아이템입니다.',
|
||||
MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING: '첨부 가능한 칼리움 수량을 초과 하였습니다.',
|
||||
MAIL_SEND_STATUS_WARNING: '발송 처리가 완료된 우편은 삭제할 수 없습니다.',
|
||||
MAIL_REGIST_CONFIRM: '우편을 등록하시겠습니까?',
|
||||
MAIL_REGIST_COMPLETE: '우편이 정상 등록되었습니다.',
|
||||
MAIL_REGIST_CANCEL: "우편 등록을 취소하시겠습니까?\n\r취소 시 설정된 값은 반영되지 않습니다.",
|
||||
MAIL_CANCEL: '우편 등록이 취소되었습니다.',
|
||||
//인게임 메시지
|
||||
BOARD_DELETE_CONFIRM: "선택된 인게임 메세지를 삭제하시겠습니까?\r\n삭제 시 설정 정보가 제거됩니다.",
|
||||
//칼리움 요청
|
||||
CHARGE_COMPLTED: '해당 건에 대한 충전 처리가 완료되었습니다.',
|
||||
CALIUM_CHARGE_CONFIRM: '선택건에 대한 충전을 진행하시겠습니까?',
|
||||
@@ -101,7 +112,6 @@ const resources = {
|
||||
SEARCH_LIMIT_FAIL: '인출 가능 수량 조회에 대한 요청 중 오류가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요. 오류가 지속될 경우, 담당자에게 문의해주세요.',
|
||||
//전투시스템
|
||||
BATTLE_EVENT_MODAL_START_DT_WARNING: "시작 시간은 현재 시간으로부터 10분 이후부터 가능합니다.",
|
||||
BATTLE_EVENT_MODAL_START_DIFF_END_WARNING :"종료일은 시작일보다 하루 이후여야 합니다.",
|
||||
BATTLE_EVENT_MODAL_TIME_CHECK_WARNING :"해당 시간에 속하는 이벤트가 존재합니다.",
|
||||
BATTLE_EVENT_REGIST_CONFIRM: "이벤트를 등록하시겠습니까?",
|
||||
BATTLE_EVENT_UPDATE_CONFIRM: "이벤트를 수정하시겠습니까?",
|
||||
@@ -110,6 +120,22 @@ const resources = {
|
||||
BATTLE_EVENT_STOP_5MINUTES_DATE_WARNING: "이벤트 시작 5분 전에는 중단할 수 없습니다.",
|
||||
BATTLE_EVENT_STATUS_RUNNING_WARNING: "이벤트 진행중에는 중단할 수 없습니다.",
|
||||
BATTLE_EVENT_MODAL_STATUS_WARNING: "이벤트가 중단일때만 수정이 가능합니다.",
|
||||
//메뉴 배너
|
||||
MENU_BANNER_TITLE: "메뉴 배너 관리",
|
||||
MENU_BANNER_CREATE: "메뉴 배너 등록",
|
||||
MENU_BANNER_REGIST_CONFIRM: "배너를 등록하시겠습니까?",
|
||||
MENU_BANNER_SELECT_DELETE: "선택된 배너를 삭제하시겠습니까?",
|
||||
MENU_BANNER_REGIST_CANCEL: "배너 등록을 취소하시겠습니까?\n\r취소 시 설정된 값은 반영되지 않습니다.",
|
||||
// 이용자 제재
|
||||
USER_BLOCK_VALIDATION_WARNING: '유효성 체크가 통과되지 않은 항목이 존재합니다.\r\n수정 후 재등록 해주세요.',
|
||||
USER_BLOCK_REGIST_DUPLE_WARNING: '이미 제재가 등록된 유저입니다.',
|
||||
USER_BLOCK_DELETE_CONFIRM: '제재 대상을 삭제하시겠습니까?\r\n삭제 시 제재가 해제됩니다.',
|
||||
USER_BLOCK_REGIST_CONFIRM: '이용자 제재 명단에\r\n등록하시겠습니까?',
|
||||
//파일
|
||||
FILE_IMAGE_EXTENSION_WARNING: "png, jpg 확장자의 이미지만 업로드 가능합니다.",
|
||||
FILE_IMAGE_UPLOAD_ERROR: "이미지 업로드 중 오류가 발생했습니다.",
|
||||
FILE_NOT_EXIT_ERROR: "유효하지 않은 파일입니다.",
|
||||
FILE_SIZE_OVER_ERROR: "파일의 사이즈가 5MB를 초과하였습니다.",
|
||||
//파일명칭
|
||||
FILE_INDEX_USER_CONTENT: 'Caliverse_User_Index.xlsx',
|
||||
FILE_CALIUM_REQUEST: 'Caliverse_Calium_Request.xlsx',
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
import { INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE } from '../../assets/data/adminConstants';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Button,
|
||||
DownloadProgress,
|
||||
DynamicModal,
|
||||
ExcelDownButton,
|
||||
@@ -34,6 +35,9 @@ import styled from 'styled-components';
|
||||
import FrontPagination from '../../components/common/Pagination/FrontPagination';
|
||||
import Loading from '../../components/common/Loading';
|
||||
import CircularProgress from '../../components/common/CircularProgress';
|
||||
import VerticalDotsButton from '../../components/common/button/VerticalDotsButton';
|
||||
import MessageInput from '../../components/common/input/MessageInput';
|
||||
import { AnalyzeAI } from '../../apis';
|
||||
|
||||
const BusinessLogView = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
@@ -171,6 +175,15 @@ const BusinessLogView = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleMessage = (message) => {
|
||||
const params = {}
|
||||
params.message = message;
|
||||
params.type = 'BUSINESS_LOG'
|
||||
params.conditions = searchParams;
|
||||
AnalyzeAI(token, params);
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>비즈니스 로그 조회</Title>
|
||||
@@ -204,6 +217,7 @@ const BusinessLogView = () => {
|
||||
</CircularProgressWrapper>
|
||||
)}
|
||||
</DownloadContainer>
|
||||
<MessageInput onSendMessage={handleMessage} />
|
||||
</ViewTableInfo>
|
||||
{dataLoading ? <TableSkeleton width='100%' count={15} /> :
|
||||
<>
|
||||
|
||||
@@ -29,13 +29,15 @@ import LandInfoSearchBar, { useLandInfoSearch } from '../../components/ServiceMa
|
||||
import { TableSkeleton } from '../../components/Skeleton/TableSkeleton';
|
||||
import OwnerChangeModal from '../../components/ServiceManage/modal/OwnerChangeModal';
|
||||
import { opLandInfoStatusType } from '../../assets/data/options';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
|
||||
const LandInfoView = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const navigate = useNavigate();
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const tableRef = useRef(null);
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const [detailData, setDetailData] = useState({});
|
||||
|
||||
@@ -48,7 +50,6 @@ const LandInfoView = () => {
|
||||
deleteConfirm: 'hidden',
|
||||
deleteComplete: 'hidden'
|
||||
});
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
const [modalType, setModalType] = useState('regist');
|
||||
|
||||
const {
|
||||
@@ -80,7 +81,7 @@ const LandInfoView = () => {
|
||||
setModalType('regist');
|
||||
const selectRow = selectedRows[0];
|
||||
if(!selectRow.owned) {
|
||||
setAlertMsg(t('LAND_OWNED_CHANGES_WARNING'))
|
||||
showToast('LAND_OWNED_CHANGES_WARNING', {type:alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
const owner_changes = selectRow.owner_changes;
|
||||
@@ -147,15 +148,12 @@ const LandInfoView = () => {
|
||||
// // fetchData(option);
|
||||
// window.location.reload();
|
||||
// break;
|
||||
case "warning":
|
||||
setAlertMsg('')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const handleDetailView = () => {
|
||||
handleModalClose('detail');
|
||||
handleSearch();
|
||||
handleSearch(updateSearchParams);
|
||||
removeSelectedRows();
|
||||
}
|
||||
|
||||
@@ -235,14 +233,12 @@ const LandInfoView = () => {
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
<OwnerChangeModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => handleDetailView()} content={detailData} setDetailData={setDetailData} />
|
||||
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleModalSubmit('warning')}
|
||||
<OwnerChangeModal
|
||||
modalType={modalType}
|
||||
detailView={modalState.detailModal}
|
||||
handleDetailView={() => handleDetailView()}
|
||||
content={detailData}
|
||||
setDetailData={setDetailData}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -38,13 +38,17 @@ import BattleEventSearchBar, {
|
||||
useBattleEventSearch,
|
||||
} from '../../components/ServiceManage/searchBar/BattleEventSearchBar';
|
||||
import { getDateOnly, getTimeOnly, secondToMinutes } from '../../utils/date';
|
||||
import { battleEventStatusType } from '../../assets/data/types';
|
||||
import { alertTypes, battleEventStatusType } from '../../assets/data/types';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
|
||||
const BattleEvent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const tableRef = useRef(null);
|
||||
const { showToast, showModal } = useAlert();
|
||||
const {withLoading} = useLoading();
|
||||
|
||||
const [detailData, setDetailData] = useState({});
|
||||
|
||||
@@ -53,13 +57,8 @@ const BattleEvent = () => {
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
stopConfirm: 'hidden',
|
||||
stopComplete: 'hidden',
|
||||
detail: 'hidden',
|
||||
deleteConfirm: 'hidden',
|
||||
deleteComplete: 'hidden'
|
||||
});
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
const [modalType, setModalType] = useState('regist');
|
||||
|
||||
const {
|
||||
@@ -141,50 +140,61 @@ const BattleEvent = () => {
|
||||
return timeDiff < 3;
|
||||
});
|
||||
if(date_check){
|
||||
setAlertMsg(t('LAND_AUCTION_DELETE_DATE_WARNING'));
|
||||
showToast('LAND_AUCTION_DELETE_DATE_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
if(selectedRows[0].status === landAuctionStatusType.auction_start || selectedRows[0].status === landAuctionStatusType.stl_end){
|
||||
setAlertMsg(t('LAND_AUCTION_DELETE_STATUS_WARNING'));
|
||||
showToast('LAND_AUCTION_DELETE_STATUS_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
handleModalView('deleteConfirm');
|
||||
showModal('BATTLE_EVENT_SELECT_DELETE', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleModalSubmit('deleteConfirm')
|
||||
});
|
||||
break;
|
||||
|
||||
case "stop":
|
||||
const select_item = selectedRows[0];
|
||||
if(select_item.status === battleEventStatusType.running){
|
||||
setAlertMsg(t('BATTLE_EVENT_STATUS_RUNNING_WARNING'));
|
||||
showToast('BATTLE_EVENT_STATUS_RUNNING_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
const isStopTimeCheck = isStopMinutes(select_item.event_start_dt);
|
||||
if(isStopTimeCheck){
|
||||
setAlertMsg(t('BATTLE_EVENT_STOP_5MINUTES_DATE_WARNING'));
|
||||
showToast('BATTLE_EVENT_STOP_5MINUTES_DATE_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
if(isRunningTime(select_item.event_start_dt, select_item.event_operation_time)){
|
||||
setAlertMsg(t('BATTLE_EVENT_STATUS_RUNNING_WARNING'));
|
||||
showToast('BATTLE_EVENT_STATUS_RUNNING_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
handleModalView('stopConfirm');
|
||||
showModal('BATTLE_EVENT_SELECT_STOP', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleModalSubmit('stopConfirm')
|
||||
});
|
||||
break;
|
||||
|
||||
case "stopConfirm":
|
||||
const stop_item = selectedRows[0];
|
||||
|
||||
await BattleEventStop(token, stop_item.id, stop_item).then(data => {
|
||||
handleModalClose('stopConfirm');
|
||||
await withLoading(async () => {
|
||||
return await BattleEventStop(token, stop_item.id, stop_item);
|
||||
}).then(data => {
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('stopComplete');
|
||||
showToast('STOP_COMPLETE', {type: alertTypes.success});
|
||||
handleSearch(updateSearchParams);
|
||||
}else if(data.result === "ERROR_BATTLE_EVENT_STATUS_START_IMPOSSIBLE"){
|
||||
setAlertMsg(t('BATTLE_EVENT_STATUS_RUNNING_WARNING'));
|
||||
showToast('BATTLE_EVENT_STATUS_RUNNING_WARNING', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('STOP_FAIL'));
|
||||
showToast('STOP_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case "deleteConfirm":
|
||||
let list = [];
|
||||
let isChecked = false;
|
||||
@@ -198,35 +208,24 @@ const BattleEvent = () => {
|
||||
});
|
||||
|
||||
if(isChecked) {
|
||||
setAlertMsg(t('LAND_AUCTION_WARNING_DELETE'))
|
||||
handleModalClose('deleteConfirm');
|
||||
showToast('LAND_AUCTION_WARNING_DELETE', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
await BattleEventDelete(token, list).then(data => {
|
||||
handleModalClose('deleteConfirm');
|
||||
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('deleteComplete');
|
||||
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||
handleSearch(updateSearchParams);
|
||||
}else if(data.result === "ERROR_AUCTION_STATUS_IMPOSSIBLE"){
|
||||
setAlertMsg(t('LAND_AUCTION_ERROR_DELETE_STATUS'));
|
||||
showToast('LAND_AUCTION_ERROR_DELETE_STATUS', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('DELETE_FAIL'));
|
||||
showToast('DELETE_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
});
|
||||
|
||||
break;
|
||||
case "deleteComplete":
|
||||
handleModalClose('deleteComplete');
|
||||
window.location.reload();
|
||||
break;
|
||||
case "stopComplete":
|
||||
handleModalClose('stopComplete');
|
||||
window.location.reload();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('')
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -332,45 +331,19 @@ const BattleEvent = () => {
|
||||
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
||||
|
||||
{/*상세*/}
|
||||
<BattleEventModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData} configData={battleConfigData} rewardData={battleRewardData} />
|
||||
<BattleEventModal
|
||||
modalType={modalType}
|
||||
detailView={modalState.detailModal}
|
||||
handleDetailView={() =>{
|
||||
handleModalClose('detail');
|
||||
handleSearch(updateSearchParams);
|
||||
}}
|
||||
content={detailData}
|
||||
setDetailData={setDetailData}
|
||||
configData={battleConfigData}
|
||||
rewardData={battleRewardData}
|
||||
/>
|
||||
|
||||
{/*중단 확인*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.stopConfirmModal}
|
||||
handleCancel={() => handleModalClose('stopConfirm')}
|
||||
handleSubmit={() => handleModalSubmit('stopConfirm')}
|
||||
modalText={t('BATTLE_EVENT_SELECT_STOP')}
|
||||
/>
|
||||
{/*중단 완료*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.stopCompleteModal}
|
||||
handleSubmit={() => handleModalSubmit('stopComplete')}
|
||||
modalText={t('STOP_COMPLETE')}
|
||||
/>
|
||||
{/*삭제 확인*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.deleteConfirmModal}
|
||||
handleCancel={() => handleModalClose('deleteConfirm')}
|
||||
handleSubmit={() => handleModalSubmit('deleteConfirm')}
|
||||
modalText={t('BATTLE_EVENT_SELECT_DELETE')}
|
||||
/>
|
||||
{/*삭제 완료*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.deleteCompleteModal}
|
||||
handleSubmit={() => handleModalSubmit('deleteComplete')}
|
||||
modalText={t('DEL_COMPLETE')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleModalSubmit('warning')}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -24,15 +24,21 @@ import { BoardInfoModal, BoardRegistModal } from '../../components/ServiceManage
|
||||
import { authList } from '../../store/authList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { convertKTC } from '../../utils';
|
||||
import { convertKTC, timeDiffMinute } from '../../utils';
|
||||
import AuthModal from '../../components/common/modal/AuthModal';
|
||||
import { authType } from '../../assets/data';
|
||||
import { authType, landAuctionStatusType } from '../../assets/data';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { message_type, sendStatus } from '../../assets/data/options';
|
||||
import { BattleEventDelete, BattleEventDetailView, BattleEventStop } from '../../apis/Battle';
|
||||
import { alertTypes, battleEventStatusType } from '../../assets/data/types';
|
||||
import { useModal, useTable, withAuth } from '../../hooks/hook';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
|
||||
const Board = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const {showModal, showToast} = useAlert()
|
||||
const {withLoading} = useLoading();
|
||||
|
||||
const [isCopyData, setIsCopyData] = useState(false);
|
||||
|
||||
@@ -41,21 +47,12 @@ const Board = () => {
|
||||
const [registView, setRegistView] = useState('hidden');
|
||||
const [detailData, setDetailData] = useState('');
|
||||
const [detailId, setDetailId] = useState('');
|
||||
const [selectedRow, setSelectedRow] = useState([]);
|
||||
const [deleteModalClose, setDeleteModalClose] = useState('hidden');
|
||||
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
|
||||
|
||||
const message_type = [
|
||||
{ value: 'CHATTING', name: '채팅 타입' },
|
||||
{ value: 'CHATTING_TOAST', name: '채팅 + 토스트' },
|
||||
];
|
||||
|
||||
const sendStatus = [
|
||||
{ value: 'WAIT', name: '대기' },
|
||||
{ value: 'RUNNING', name: '송출중' },
|
||||
{ value: 'FINISH', name: '완료' },
|
||||
{ value: 'FAIL', name: '실패' },
|
||||
];
|
||||
const {
|
||||
selectedRows,
|
||||
handleSelectRow,
|
||||
isRowSelected
|
||||
} = useTable(dataList || [], {mode: 'single'});
|
||||
|
||||
const fetchData = async () => {
|
||||
setDataList(await NoticeListView(token));
|
||||
@@ -65,201 +62,136 @@ const Board = () => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
// 체크박스 선택 리스트
|
||||
const handleSelectCheckBox = e => {
|
||||
let list = [...selectedRow];
|
||||
if (e.target.checked) {
|
||||
list.push(e.target.id);
|
||||
setSelectedRow(list);
|
||||
} else {
|
||||
const filterList = list.filter(data => e.target.id !== data);
|
||||
setSelectedRow(filterList);
|
||||
const handleModalSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "detail":
|
||||
await NoticeDetailView(token, param.id).then(data => {
|
||||
setDetailData(data);
|
||||
setDetailId(param.id);
|
||||
setDetailView('view');
|
||||
});
|
||||
break;
|
||||
case "delete":
|
||||
if(selectedRows.length === 0) return;
|
||||
showModal('BOARD_DELETE_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleModalSubmit('deleteConfirm')
|
||||
});
|
||||
break;
|
||||
|
||||
case "deleteConfirm":
|
||||
let list = [];
|
||||
|
||||
selectedRows.map(data =>
|
||||
list.push({
|
||||
message_id: data.id,
|
||||
}),
|
||||
);
|
||||
|
||||
await withLoading(async () => {
|
||||
return await NoticeDelete(token, list);
|
||||
}).then(data => {
|
||||
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||
}).catch(reason => {
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 전체 선택 구현
|
||||
const handleAllSelect = () => {
|
||||
let list = [];
|
||||
if (document.getElementById('check-all').checked === true) {
|
||||
dataList.map((data, index) => {
|
||||
document.getElementsByName('select')[index].checked = true;
|
||||
list.push(String(data.id));
|
||||
});
|
||||
} else if (document.getElementById('check-all').checked === false) {
|
||||
for (let i = 0; i < dataList.length; i++) {
|
||||
dataList.map((data, index) => (document.getElementsByName('select')[index].checked = false));
|
||||
list = [];
|
||||
}
|
||||
}
|
||||
setSelectedRow(list);
|
||||
};
|
||||
|
||||
// 선택 삭제 함수
|
||||
const handleSelectedDelete = () => {
|
||||
let list = [];
|
||||
|
||||
selectedRow.map(data =>
|
||||
list.push({
|
||||
message_id: data,
|
||||
}),
|
||||
);
|
||||
NoticeDelete(token, list);
|
||||
|
||||
handleDeleteModalClose();
|
||||
handleConfirmeModalClose();
|
||||
};
|
||||
|
||||
// 선택 삭제 모달
|
||||
const handleDeleteModalClose = () => {
|
||||
if (selectedRow.length !== 0 && deleteModalClose === 'hidden') {
|
||||
setDeleteModalClose('view');
|
||||
} else {
|
||||
setDeleteModalClose('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제, 승인, 저장 확인 모달창
|
||||
const handleConfirmeModalClose = () => {
|
||||
if (confirmModalClose === 'hidden') {
|
||||
setConfirmModalClose('view');
|
||||
} else {
|
||||
setConfirmModalClose('hidden');
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
const handleDetailModal = async (e, id) => {
|
||||
setDetailData(await NoticeDetailView(token, id));
|
||||
setDetailId(id);
|
||||
|
||||
e.preventDefault();
|
||||
setDetailView('view');
|
||||
};
|
||||
|
||||
const handleCountSelectedRow = () => {
|
||||
return dataList && document.querySelectorAll('input[name="select"]:checked').length === dataList.length;
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.inGameRead) ? (
|
||||
<AuthModal/>
|
||||
) : (
|
||||
<>
|
||||
<Title>인게임 메시지</Title>
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.inGameDelete) && (
|
||||
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={handleDeleteModalClose} />
|
||||
)}
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.inGameUpdate) && (
|
||||
<Button
|
||||
theme="primary"
|
||||
text="메시지 등록"
|
||||
value="2"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
setRegistView('view');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40">
|
||||
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
|
||||
</th>
|
||||
<th width="80">번호</th>
|
||||
<th width="100">채팅 타입</th>
|
||||
<th width="200">송출 일자</th>
|
||||
<th>메시지</th>
|
||||
<th width="80">송출 횟수</th>
|
||||
<th width="100">송출 상태</th>
|
||||
<th width="120">등록자</th>
|
||||
<th width="200">등록자(이메일주소)</th>
|
||||
<th width="200">등록일자</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.map(notice => (
|
||||
<Fragment key={notice.id}>
|
||||
<tr>
|
||||
<td>
|
||||
<CheckBox name={'select'} id={notice.id} setData={e => handleSelectCheckBox(e)} />
|
||||
</td>
|
||||
<td>{notice.row_num}</td>
|
||||
<td>{message_type.map(item => item.value === notice.message_type && item.name)}</td>
|
||||
<td>{convertKTC(notice.send_dt)}</td>
|
||||
<td>
|
||||
<DetailMessage onClick={e => handleDetailModal(e, notice.id)}>
|
||||
{notice.content.length > 20 ? notice.content.slice(0, 20) + '...' : notice.content || ''}
|
||||
</DetailMessage>
|
||||
</td>
|
||||
<td>
|
||||
{/*{notice.send_cnt} / {notice.repeat_cnt}*/}
|
||||
{notice.send_cnt}
|
||||
</td>
|
||||
<td>
|
||||
{sendStatus.map(data => data.value === notice.send_status && data.name)}
|
||||
</td>
|
||||
<td>{notice.create_name}</td>
|
||||
<td>{notice.create_by}</td>
|
||||
<td>{convertKTC(notice.create_dt)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
{/* 인게임 메세지 정보 */}
|
||||
<BoardInfoModal
|
||||
detailView={detailView}
|
||||
setDetailView={setDetailView}
|
||||
content={detailData}
|
||||
id={detailId}
|
||||
setIsCopyData={setIsCopyData}
|
||||
openRegistModal={setRegistView}
|
||||
userInfo={userInfo}
|
||||
<Title>인게임 메시지</Title>
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.inGameDelete) && (
|
||||
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
||||
)}
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.inGameUpdate) && (
|
||||
<Button
|
||||
theme="primary"
|
||||
text="메시지 등록"
|
||||
value="2"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
setRegistView('view');
|
||||
}}
|
||||
/>
|
||||
{/* 인게임 메세지 등록 */}
|
||||
<BoardRegistModal registView={registView} setRegistView={setRegistView} copyData={isCopyData ? detailData : ''} setIsCopyData={setIsCopyData} userInfo={userInfo} />
|
||||
</TableWrapper>
|
||||
|
||||
{/* 선택 삭제 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteModalClose}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleDeleteModalClose} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">
|
||||
선택된 인게임 메세지를 삭제하시겠습니까?
|
||||
<br />
|
||||
삭제 시 설정 정보가 제거됩니다.
|
||||
</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleDeleteModalClose} />
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedDelete} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
{/* 확인 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleConfirmeModalClose} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
)}
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40">
|
||||
</th>
|
||||
<th width="80">번호</th>
|
||||
<th width="100">채팅 타입</th>
|
||||
<th width="200">송출 일자</th>
|
||||
<th>메시지</th>
|
||||
<th width="80">송출 횟수</th>
|
||||
<th width="100">송출 상태</th>
|
||||
<th width="120">등록자</th>
|
||||
<th width="200">등록자(이메일주소)</th>
|
||||
<th width="200">등록일자</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.map(notice => (
|
||||
<Fragment key={notice.id}>
|
||||
<tr>
|
||||
<td>
|
||||
<CheckBox name={'select'} id={notice.id}
|
||||
setData={(e) => handleSelectRow(e, notice)}
|
||||
checked={isRowSelected(notice.id)} />
|
||||
</td>
|
||||
<td>{notice.row_num}</td>
|
||||
<td>{message_type.find(item => item.value === notice.message_type)?.name}</td>
|
||||
<td>{convertKTC(notice.send_dt)}</td>
|
||||
<td>
|
||||
<DetailMessage
|
||||
onClick={() => handleModalSubmit('detail', { id: notice.id })}>
|
||||
{notice.content.length > 20 ? notice.content.slice(0, 20) + '...' : notice.content || ''}
|
||||
</DetailMessage>
|
||||
</td>
|
||||
<td>
|
||||
{/*{notice.send_cnt} / {notice.repeat_cnt}*/}
|
||||
{notice.send_cnt}
|
||||
</td>
|
||||
<td>
|
||||
{sendStatus.find(data => data.value === notice.send_status)?.name}
|
||||
</td>
|
||||
<td>{notice.create_name}</td>
|
||||
<td>{notice.create_by}</td>
|
||||
<td>{convertKTC(notice.create_dt)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
{/* 인게임 메세지 정보 */}
|
||||
<BoardInfoModal
|
||||
detailView={detailView}
|
||||
setDetailView={setDetailView}
|
||||
content={detailData}
|
||||
id={detailId}
|
||||
setIsCopyData={setIsCopyData}
|
||||
openRegistModal={setRegistView}
|
||||
userInfo={userInfo}
|
||||
/>
|
||||
{/* 인게임 메세지 등록 */}
|
||||
<BoardRegistModal registView={registView} setRegistView={setRegistView} copyData={isCopyData ? detailData : ''} setIsCopyData={setIsCopyData} userInfo={userInfo} />
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Board;
|
||||
export default withAuth(authType.inGameRead)(Board);
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useState, useEffect, Fragment, memo } from 'react';
|
||||
import { useState, Fragment } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
EventDelete,
|
||||
EventDetailView,
|
||||
EventView,
|
||||
EventDetailView
|
||||
} from '../../apis';
|
||||
|
||||
import { authList } from '../../store/authList';
|
||||
@@ -21,99 +20,56 @@ 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 EventListSearchBar from '../../components/ServiceManage/searchBar/EventListSearchBar';
|
||||
import CustomConfirmModal from '../../components/common/modal/CustomConfirmModal';
|
||||
import { ModalInputItem, ModalSubText, RegistInputItem, RegistNotice, SubText } from '../../styles/ModuleComponents';
|
||||
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';
|
||||
|
||||
const Event = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const {withLoading} = useLoading();
|
||||
const {showToast} = useAlert();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(50);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [detailData, setDetailData] = useState('');
|
||||
|
||||
const [selectedRow, setSelectedRow] = useState([]);
|
||||
const [searchData, setSearchData] = useState({
|
||||
title: '',
|
||||
content: '',
|
||||
status: 'ALL',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
detail: 'hidden',
|
||||
delete: 'hidden',
|
||||
});
|
||||
const [orderBy, setOrderBy] = useState('DESC');
|
||||
const [modalState, setModalState] = useState({
|
||||
detailModal: 'hidden',
|
||||
deleteConfirmModal: 'hidden',
|
||||
deleteCompleteModal: 'hidden',
|
||||
});
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
const [deleteDesc, setDeleteDesc] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
fetchData('', '', 'ALL', '', '');
|
||||
setSelectedRow([]);
|
||||
}, [currentPage]);
|
||||
|
||||
// 리스트 조회
|
||||
const fetchData = async (title, content, status, startDate, endDate, order, size) => {
|
||||
setDataList(
|
||||
await EventView(
|
||||
token,
|
||||
title,
|
||||
content,
|
||||
status,
|
||||
startDate && new Date(startDate).toISOString(),
|
||||
endDate && new Date(endDate).toISOString(),
|
||||
order ? order : orderBy,
|
||||
size ? size : pageSize,
|
||||
currentPage,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (title, content, status, startDate, endDate) => {
|
||||
fetchData(title, content, status, startDate && new Date(startDate).toISOString(), endDate && new Date(endDate).toISOString());
|
||||
};
|
||||
|
||||
// 오름차순 내림차순
|
||||
const handleOrderBy = e => {
|
||||
const order = e.target.value;
|
||||
|
||||
setOrderBy(order);
|
||||
fetchData(
|
||||
searchData.title,
|
||||
searchData.content,
|
||||
searchData.status,
|
||||
searchData.startDate && new Date(searchData.startDate).toISOString(),
|
||||
searchData.endDate && new Date(searchData.endDate).toISOString(),
|
||||
order,
|
||||
pageSize,
|
||||
);
|
||||
};
|
||||
|
||||
const handlePageSize = e => {
|
||||
const size = e.target.value;
|
||||
setPageSize(size);
|
||||
setCurrentPage(1);
|
||||
|
||||
fetchData(
|
||||
searchData.title,
|
||||
searchData.content,
|
||||
searchData.status,
|
||||
searchData.startDate && new Date(searchData.startDate).toISOString(),
|
||||
searchData.endDate && new Date(searchData.endDate).toISOString(),
|
||||
orderBy,
|
||||
size,
|
||||
1,
|
||||
);
|
||||
};
|
||||
const {
|
||||
config,
|
||||
searchParams,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleOrderByChange,
|
||||
updateSearchParams,
|
||||
configLoaded
|
||||
} = useCommonSearch(token, "eventSearch");
|
||||
const {
|
||||
selectedRows,
|
||||
handleSelectRow,
|
||||
isRowSelected
|
||||
} = useTable(dataList?.list || [], {mode: 'single'});
|
||||
|
||||
// 상세보기 호출
|
||||
const handleDetailModal = async (e, id) => {
|
||||
@@ -125,68 +81,27 @@ const Event = () => {
|
||||
handleModalView('detail');
|
||||
};
|
||||
|
||||
// 체크박스 선택 리스트
|
||||
const handleSelectCheckBox = (e, event) => {
|
||||
let list = [...selectedRow];
|
||||
if (e.target.checked) {
|
||||
list.push(event);
|
||||
setSelectedRow(list);
|
||||
} else {
|
||||
const filterList = list.filter(data => e.target.id !== data);
|
||||
setSelectedRow(filterList);
|
||||
}
|
||||
};
|
||||
|
||||
// 전체 선택 구현
|
||||
const handleAllSelect = () => {
|
||||
let list = [];
|
||||
if (document.getElementById('check-all').checked === true) {
|
||||
dataList.list.map((data, index) => {
|
||||
document.getElementsByName('select')[index].checked = true;
|
||||
list.push(String(data.id));
|
||||
});
|
||||
} else if (document.getElementById('check-all').checked === false) {
|
||||
for (let i = 0; i < dataList.list.length; i++) {
|
||||
dataList.list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
|
||||
list = [];
|
||||
}
|
||||
}
|
||||
setSelectedRow(list);
|
||||
};
|
||||
|
||||
const handleModalView = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'view',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalClose = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'hidden',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "delete":
|
||||
const delete_check = selectedRow.every(row => {
|
||||
const delete_check = selectedRows.every(row => {
|
||||
const timeDiff = timeDiffMinute(convertKTC(row.start_dt), (new Date));
|
||||
return row.add_flag || (timeDiff < 30);
|
||||
});
|
||||
if(delete_check){
|
||||
setAlertMsg(t('EVENT_TIME_LIMIT_UPDATE'));
|
||||
showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
handleModalView('deleteConfirm');
|
||||
|
||||
handleModalView('delete');
|
||||
break;
|
||||
case "deleteConfirm":
|
||||
let list = [];
|
||||
|
||||
let isChecked = false;
|
||||
|
||||
selectedRow.map(data => {
|
||||
selectedRows.map(data => {
|
||||
const row = dataList.list.find(row => row.id === Number(data.id));
|
||||
if(row.status !== commonStatus.wait) isChecked = true;
|
||||
list.push({
|
||||
@@ -195,32 +110,28 @@ const Event = () => {
|
||||
});
|
||||
});
|
||||
|
||||
handleModalClose('delete');
|
||||
setDeleteDesc('');
|
||||
|
||||
if(isChecked) {
|
||||
setAlertMsg(t('EVENT_WARNING_DELETE'))
|
||||
handleModalClose('deleteConfirm');
|
||||
showToast('EVENT_WARNING_DELETE', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
EventDelete(token, list);
|
||||
await withLoading(async () => {
|
||||
return await EventDelete(token, list);
|
||||
}).then(data => {
|
||||
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||
}).catch(error => {
|
||||
|
||||
}).finally(() => {
|
||||
handleSearch(updateSearchParams);
|
||||
})
|
||||
|
||||
handleModalClose('deleteConfirm');
|
||||
handleModalView('deleteComplete');
|
||||
break;
|
||||
case "deleteComplete":
|
||||
handleModalClose('deleteComplete');
|
||||
// fetchData(option);
|
||||
window.location.reload();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const handleCountSelectedRow = () => {
|
||||
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventRead) ? (
|
||||
@@ -229,11 +140,22 @@ const Event = () => {
|
||||
<>
|
||||
<Title>출석 보상 이벤트 관리</Title>
|
||||
<FormWrapper>
|
||||
<EventListSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
|
||||
<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={handleOrderBy} handlePageSize={handlePageSize}>
|
||||
<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={selectedRow.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
||||
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
||||
)}
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
|
||||
<Button
|
||||
@@ -253,7 +175,6 @@ const Event = () => {
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40">
|
||||
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
|
||||
</th>
|
||||
<th width="80">번호</th>
|
||||
<th width="100">이벤트 상태</th>
|
||||
@@ -265,39 +186,52 @@ const Event = () => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.list &&
|
||||
dataList.list.map(event => (
|
||||
{dataList?.list?.map(event => (
|
||||
<Fragment key={event.row_num}>
|
||||
<tr>
|
||||
<td>
|
||||
<CheckBox name={'select'} id={event.id} setData={e => handleSelectCheckBox(e, event)} />
|
||||
<CheckBox name={'select'} id={event.id}
|
||||
setData={(e) => handleSelectRow(e, event)}
|
||||
checked={isRowSelected(event.id)} />
|
||||
</td>
|
||||
<td>{event.row_num}</td>
|
||||
<td>{eventStatus.map(data => data.value === event.status && data.name)}</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)} />
|
||||
<Button theme="line" text="상세보기"
|
||||
handleClick={e => handleDetailModal(e, event.id)} />
|
||||
</td>
|
||||
<td>{event.create_by}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
|
||||
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
|
||||
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
||||
|
||||
{/*상세*/}
|
||||
<EventDetailModal detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData}/>
|
||||
<EventDetailModal
|
||||
detailView={modalState.detailModal}
|
||||
handleDetailView={() =>{
|
||||
handleModalClose('detail');
|
||||
handleSearch(updateSearchParams);
|
||||
}}
|
||||
content={detailData}
|
||||
setDetailData={setDetailData}
|
||||
/>
|
||||
|
||||
{/*삭제 확인*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.childOkCancel}
|
||||
view={modalState.deleteConfirmModal}
|
||||
handleCancel={() => handleModalClose('deleteConfirm')}
|
||||
view={modalState.deleteModal}
|
||||
handleCancel={() => handleModalClose('delete')}
|
||||
handleSubmit={() => handleModalSubmit('deleteConfirm')}
|
||||
>
|
||||
<ModalInputItem>
|
||||
@@ -316,20 +250,6 @@ const Event = () => {
|
||||
<ModalSubText $color={deleteDesc.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30자)</ModalSubText>
|
||||
</ModalInputItem>
|
||||
</DynamicModal>
|
||||
{/*삭제 완료*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.deleteCompleteModal}
|
||||
handleSubmit={() => handleModalSubmit('deleteComplete')}
|
||||
modalText={t('DEL_COMPLETE')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleModalSubmit('warning')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState, Fragment, useEffect } from 'react';
|
||||
import React, { useState, Fragment, useEffect } from 'react';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import Loading from '../../components/common/Loading';
|
||||
|
||||
import {
|
||||
Title,
|
||||
@@ -23,38 +22,34 @@ import {
|
||||
AppendRegistBox, AppendRegistTable, AreaBtnClose,
|
||||
BtnDelete,
|
||||
Item,
|
||||
ItemList, LangArea, ModalInputItem, ModalItem, ModalItemList, ModalSubText, RegistGroup,
|
||||
ItemList, LangArea, ModalItem, ModalItemList, RegistGroup,
|
||||
RegistInputItem,
|
||||
RegistInputRow, RegistNotice, RegistTable,
|
||||
} from '../../styles/ModuleComponents';
|
||||
import AuthModal from '../../components/common/modal/AuthModal';
|
||||
import { authType, benItems, modalTypes, wellType } from '../../assets/data';
|
||||
import DynamicModal from '../../components/common/modal/DynamicModal';
|
||||
import { authType, benItems, wellType } from '../../assets/data';
|
||||
import DateTimeInput from '../../components/common/input/DateTimeInput';
|
||||
import { timeDiffMinute } from '../../utils';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
|
||||
const EventRegist = () => {
|
||||
const navigate = useNavigate();
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
const [loading, setLoading] = useState(false); // 로딩 창
|
||||
const [modalState, setModalState] = useState({
|
||||
cancelModal: 'hidden',
|
||||
registConfirmModal: 'hidden',
|
||||
registCompleteModal: 'hidden',
|
||||
}); // 모달 관리
|
||||
const { showToast, showModal } = useAlert();
|
||||
const { withLoading} = useLoading();
|
||||
|
||||
const [item, setItem] = useState(''); // 아이템 값
|
||||
const [itemCount, setItemCount] = useState(''); // 아이템 개수
|
||||
const [resource, setResource] = useState('19010001'); // 자원 값
|
||||
const [resourceCount, setResourceCount] = useState(''); // 자원 개수
|
||||
|
||||
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
|
||||
const [btnValidation, setBtnValidation] = useState(false); // 입력창 버튼
|
||||
const [isNullValue, setIsNullValue] = useState(false);
|
||||
const [btnValidation, setBtnValidation] = useState(false);
|
||||
const [itemCheckMsg, setItemCheckMsg] = useState('');
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
|
||||
const [time, setTime] = useState({
|
||||
start_hour: '00',
|
||||
@@ -145,7 +140,7 @@ const EventRegist = () => {
|
||||
// 아이템 추가
|
||||
const handleItemList = async () => {
|
||||
if(benItems.includes(item)){
|
||||
setAlertMsg(t('MAIL_ITEM_ADD_BEN'))
|
||||
showToast('MAIL_ITEM_ADD_BEN', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
if(item.length === 0 || itemCount.length === 0) return;
|
||||
@@ -160,7 +155,7 @@ const EventRegist = () => {
|
||||
|
||||
const itemIndex = resultData.item_list.findIndex((data) => data.item === item);
|
||||
if (itemIndex !== -1) {
|
||||
setItemCheckMsg(t('MAIL_ITEM_ADD_DUPL'));
|
||||
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 };
|
||||
@@ -221,56 +216,56 @@ const EventRegist = () => {
|
||||
setResourceCount('');
|
||||
};
|
||||
|
||||
const handleModalView = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'view',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalClose = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'hidden',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "submit":
|
||||
if (!checkCondition()) return;
|
||||
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date))
|
||||
if(timeDiff < 60) {
|
||||
setAlertMsg(t('EVENT_TIME_LIMIT_ADD'));
|
||||
showToast('EVENT_TIME_LIMIT_ADD', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
handleModalView('registConfirm');
|
||||
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 "cancel":
|
||||
|
||||
handleModalView('deleteSubmit');
|
||||
break;
|
||||
case "registConfirm":
|
||||
setLoading(true);
|
||||
|
||||
const result = await EventSingleRegist(token, resultData);
|
||||
|
||||
setLoading(false);
|
||||
handleModalClose('registConfirm');
|
||||
handleModalView('registComplete');
|
||||
break;
|
||||
case "registComplete":
|
||||
handleModalClose('registComplete');
|
||||
|
||||
navigate('/servicemanage/event');
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('');
|
||||
await withLoading(async () => {
|
||||
return await EventSingleRegist(token, resultData);
|
||||
}).then((result) => {
|
||||
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||
}).catch(() => {
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
callbackPage();
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const callbackPage = () => {
|
||||
navigate('/servicemanage/event');
|
||||
}
|
||||
|
||||
const checkCondition = () => {
|
||||
return (
|
||||
resultData.mail_list.every(data => data.content !== '' && data.title !== '') &&
|
||||
@@ -446,7 +441,14 @@ const EventRegist = () => {
|
||||
)}
|
||||
|
||||
<BtnWrapper $justify="flex-end" $gap="10px">
|
||||
<Button text="취소" theme="line" handleClick={() => handleModalView('cancel')} />
|
||||
<Button
|
||||
text="취소"
|
||||
theme="line"
|
||||
handleClick={() => showModal('EVENT_REGIST_CANCEL', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => callbackPage()
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
text="등록"
|
||||
@@ -454,61 +456,6 @@ const EventRegist = () => {
|
||||
handleClick={() => handleSubmit('submit')}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
|
||||
{/* 등록 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.registConfirmModal}
|
||||
modalText={t('EVENT_REGIST_CONFIRM')}
|
||||
handleSubmit={() => handleSubmit('registConfirm')}
|
||||
handleCancel={() => handleModalClose('registConfirm')}
|
||||
/>
|
||||
<DynamicModal
|
||||
modalType={modalTypes.childOkCancel}
|
||||
view={modalState.registConfirmModal}
|
||||
handleCancel={() => handleModalClose('registConfirm')}
|
||||
handleSubmit={() => handleSubmit('registConfirm')}
|
||||
>
|
||||
<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>
|
||||
</DynamicModal>
|
||||
{/* 완료 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.registCompleteModal}
|
||||
modalText={t('REGIST_COMPLTE')}
|
||||
handleSubmit={() => handleSubmit('registComplete')}
|
||||
/>
|
||||
{/* 취소 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.cancelModal}
|
||||
modalText={t('EVENT_REGIST_CANCEL')}
|
||||
handleCancel={() => handleModalClose('cancel')}
|
||||
handleSubmit={() => handleSubmit('cancel')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('warning')}
|
||||
/>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -32,12 +32,15 @@ import { INITIAL_PAGE_SIZE, INITIAL_PAGE_LIMIT } from '../../assets/data/adminCo
|
||||
import { useDataFetch, useModal, useTable, withAuth } from '../../hooks/hook';
|
||||
import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar';
|
||||
import { StatusWapper, ChargeBtn, StatusLabel } from '../../styles/ModuleComponents';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
|
||||
const LandAuction = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const tableRef = useRef(null);
|
||||
const { showToast, showModal } = useAlert();
|
||||
|
||||
const [detailData, setDetailData] = useState({});
|
||||
|
||||
@@ -47,8 +50,6 @@ const LandAuction = () => {
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
detail: 'hidden',
|
||||
deleteConfirm: 'hidden',
|
||||
deleteComplete: 'hidden'
|
||||
});
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
const [modalType, setModalType] = useState('regist');
|
||||
@@ -97,14 +98,17 @@ const LandAuction = () => {
|
||||
return timeDiff < 3;
|
||||
});
|
||||
if(date_check){
|
||||
setAlertMsg(t('LAND_AUCTION_DELETE_DATE_WARNING'));
|
||||
showToast('LAND_AUCTION_DELETE_DATE_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
if(selectedRows[0].status === landAuctionStatusType.auction_start || selectedRows[0].status === landAuctionStatusType.stl_end){
|
||||
setAlertMsg(t('LAND_AUCTION_DELETE_STATUS_WARNING'));
|
||||
showToast('LAND_AUCTION_DELETE_STATUS_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
handleModalView('deleteConfirm');
|
||||
showModal('LAND_AUCTION_SELECT_DELETE', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleModalSubmit('deleteConfirm')
|
||||
});
|
||||
break;
|
||||
case "deleteConfirm":
|
||||
let list = [];
|
||||
@@ -119,31 +123,24 @@ const LandAuction = () => {
|
||||
});
|
||||
|
||||
if(isChecked) {
|
||||
setAlertMsg(t('LAND_AUCTION_WARNING_DELETE'))
|
||||
handleModalClose('deleteConfirm');
|
||||
showToast('LAND_AUCTION_WARNING_DELETE', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
await LandAuctionDelete(token, list).then(data => {
|
||||
handleModalClose('deleteConfirm');
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('deleteComplete');
|
||||
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||
}else if(data.result === "ERROR_AUCTION_STATUS_IMPOSSIBLE"){
|
||||
setAlertMsg(t('LAND_AUCTION_ERROR_DELETE_STATUS'));
|
||||
showToast('LAND_AUCTION_ERROR_DELETE_STATUS', {type: alertTypes.error});
|
||||
}else{
|
||||
setAlertMsg(t('DELETE_FAIL'));
|
||||
showToast('DELETE_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
handleSearch(updateSearchParams);
|
||||
});
|
||||
|
||||
break;
|
||||
case "deleteComplete":
|
||||
handleModalClose('deleteComplete');
|
||||
window.location.reload();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('')
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -243,30 +240,19 @@ const LandAuction = () => {
|
||||
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
||||
|
||||
{/*상세*/}
|
||||
<LandAuctionModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData} landData={landData} buildingData={buildingData} />
|
||||
<LandAuctionModal
|
||||
modalType={modalType}
|
||||
detailView={modalState.detailModal}
|
||||
handleDetailView={() => {
|
||||
handleModalClose('detail');
|
||||
handleSearch(updateSearchParams);
|
||||
}}
|
||||
content={detailData}
|
||||
setDetailData={setDetailData}
|
||||
landData={landData}
|
||||
buildingData={buildingData}
|
||||
/>
|
||||
|
||||
{/*삭제 확인*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.deleteConfirmModal}
|
||||
handleCancel={() => handleModalClose('deleteConfirm')}
|
||||
handleSubmit={() => handleModalSubmit('deleteConfirm')}
|
||||
modalText={t('LAND_AUCTION_SELECT_DELETE')}
|
||||
/>
|
||||
{/*삭제 완료*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.deleteCompleteModal}
|
||||
handleSubmit={() => handleModalSubmit('deleteComplete')}
|
||||
modalText={t('DEL_COMPLETE')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleModalSubmit('warning')}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -1,330 +1,221 @@
|
||||
import styled from 'styled-components';
|
||||
import { useState, useEffect, Fragment } from 'react';
|
||||
import { useState, Fragment } from 'react';
|
||||
|
||||
import CheckBox from '../../components/common/input/CheckBox';
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
|
||||
import { MailDelete, MailDetailView, MailView } from '../../apis';
|
||||
import { MailDelete, MailDetailView } from '../../apis';
|
||||
|
||||
import { Title, FormWrapper, TableInfo, ListCount, ListOption, TableStyle, SelectInput, MailTitle, TableWrapper, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
|
||||
import { Title, FormWrapper, TableStyle, MailTitle, TableWrapper } from '../../styles/Components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import MailDetailModal from '../../components/ServiceManage/modal/MailDetailModal';
|
||||
import MailListSearchBar from '../../components/ServiceManage/searchBar/MailListSearchBar';
|
||||
import Pagination from '../../components/common/Pagination/Pagination';
|
||||
|
||||
import { authList } from '../../store/authList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { authType, mailReceiveType, mailSendStatus, mailSendType, mailType } from '../../assets/data';
|
||||
import AuthModal from '../../components/common/modal/AuthModal';
|
||||
import {
|
||||
authType,
|
||||
mailReceiveType,
|
||||
mailSendStatus,
|
||||
mailSendType,
|
||||
mailType,
|
||||
} from '../../assets/data';
|
||||
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
|
||||
import { convertKTC} from '../../utils';
|
||||
import { convertKTC } from '../../utils';
|
||||
import { StatusLabel, StatusWapper } from '../../styles/ModuleComponents';
|
||||
import { CommonSearchBar, useCommonSearch } from '../../components/ServiceManage';
|
||||
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { useModal, useTable, withAuth } from '../../hooks/hook';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
|
||||
const Mail = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userInfo = useRecoilValue(authList);
|
||||
|
||||
const {showModal, showToast} = useAlert();
|
||||
const {withLoading} = useLoading();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(50);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [detailView, setDetailView] = useState('hidden');
|
||||
const [detailData, setDetailData] = useState('');
|
||||
|
||||
const [selectedRow, setSelectedRow] = useState([]);
|
||||
const [searchData, setSearchData] = useState({
|
||||
mailTitle: '',
|
||||
content: '',
|
||||
sendType: 'ALL',
|
||||
sendStatus: 'ALL',
|
||||
mailType: 'ALL',
|
||||
receiveType: 'ALL',
|
||||
sendDate: '',
|
||||
endDate: '',
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
detail: 'hidden',
|
||||
});
|
||||
const [orderBy, setOrderBy] = useState('DESC');
|
||||
|
||||
const [deleteModalClose, setDeleteModalClose] = useState('hidden');
|
||||
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
|
||||
const {
|
||||
config,
|
||||
searchParams,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleOrderByChange,
|
||||
updateSearchParams,
|
||||
configLoaded
|
||||
} = useCommonSearch(token, "mailSearch");
|
||||
|
||||
// console.log(dataList);
|
||||
const {
|
||||
selectedRows,
|
||||
handleSelectRow,
|
||||
isRowSelected
|
||||
} = useTable(dataList?.list || [], {mode: 'single'});
|
||||
|
||||
useEffect(() => {
|
||||
fetchData('', '', 'ALL', 'ALL', 'ALL', 'ALL', '', '');
|
||||
setSelectedRow([]);
|
||||
}, [currentPage]);
|
||||
const handleModalSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "detail":
|
||||
await MailDetailView(token, param).then(data => {
|
||||
setDetailData(data);
|
||||
handleModalView('detail');
|
||||
});
|
||||
break;
|
||||
case "delete":
|
||||
if(selectedRows.length === 0) return;
|
||||
|
||||
// 리스트 조회
|
||||
const fetchData = async (mailTitle, content, sendType, sendStatus, mailType, receiveType, sendDate, endDate, order, size) => {
|
||||
setDataList(
|
||||
await MailView(
|
||||
token,
|
||||
mailTitle,
|
||||
content,
|
||||
sendType,
|
||||
sendStatus,
|
||||
mailType,
|
||||
receiveType,
|
||||
sendDate && new Date(sendDate).toISOString(),
|
||||
endDate && new Date(endDate).toISOString(),
|
||||
order ? order : orderBy,
|
||||
size ? size : pageSize,
|
||||
currentPage,
|
||||
),
|
||||
);
|
||||
};
|
||||
showModal('MAIL_SELECT_DELETE', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleModalSubmit('deleteConfirm')
|
||||
});
|
||||
break;
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (mailTitle, content, sendType, sendStatus, mailType, receiveType, sendDate, endDate) => {
|
||||
fetchData(mailTitle, content, sendType, sendStatus, mailType, receiveType, sendDate && new Date(sendDate).toISOString(), endDate && new Date(endDate).toISOString());
|
||||
};
|
||||
case "deleteConfirm":
|
||||
let list = [];
|
||||
|
||||
// 오름차순 내림차순
|
||||
const handleOrderBy = e => {
|
||||
const order = e.target.value;
|
||||
let isChecked = false;
|
||||
|
||||
setOrderBy(order);
|
||||
fetchData(
|
||||
searchData.mailTitle,
|
||||
searchData.content,
|
||||
searchData.sendType,
|
||||
searchData.sendStatus,
|
||||
searchData.mailType,
|
||||
searchData.receiveType,
|
||||
searchData.sendDate && new Date(searchData.sendDate).toISOString(),
|
||||
searchData.endDate && new Date(searchData.endDate).toISOString(),
|
||||
order,
|
||||
pageSize,
|
||||
);
|
||||
};
|
||||
selectedRows.map(data => {
|
||||
const row = dataList.list.find(row => row.id === Number(data.id));
|
||||
if(row.send_status === "FINISH" || row.send_status === "RUNNING" || row.send_status === "FAIL") isChecked = true;
|
||||
list.push({
|
||||
id: data.id,
|
||||
});
|
||||
});
|
||||
|
||||
const handlePageSize = e => {
|
||||
const size = e.target.value;
|
||||
setPageSize(size);
|
||||
setCurrentPage(1);
|
||||
if(isChecked) {
|
||||
showToast('MAIL_SEND_STATUS_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
fetchData(
|
||||
searchData.mailTitle,
|
||||
searchData.content,
|
||||
searchData.sendType,
|
||||
searchData.sendStatus,
|
||||
searchData.mailType,
|
||||
searchData.receiveType,
|
||||
searchData.sendDate && new Date(searchData.sendDate).toISOString(),
|
||||
searchData.endDate && new Date(searchData.endDate).toISOString(),
|
||||
orderBy,
|
||||
size,
|
||||
1,
|
||||
);
|
||||
};
|
||||
withLoading( async () => {
|
||||
return await MailDelete(token, list);
|
||||
}).then(data => {
|
||||
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||
}).catch(reason => {
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
handleSearch(updateSearchParams);
|
||||
});
|
||||
|
||||
const handleDetailView = () => {
|
||||
if (detailView === 'hidden') setDetailView('view');
|
||||
else setDetailView('hidden');
|
||||
};
|
||||
|
||||
const handleDetailModal = async (e, id) => {
|
||||
setDetailData(await MailDetailView(token, id));
|
||||
|
||||
e.preventDefault();
|
||||
handleDetailView();
|
||||
};
|
||||
|
||||
// 체크박스 선택 리스트
|
||||
const handleSelectCheckBox = e => {
|
||||
let list = [...selectedRow];
|
||||
if (e.target.checked) {
|
||||
list.push(e.target.id);
|
||||
setSelectedRow(list);
|
||||
} else {
|
||||
const filterList = list.filter(data => e.target.id !== data);
|
||||
setSelectedRow(filterList);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 전체 선택 구현
|
||||
const handleAllSelect = () => {
|
||||
let list = [];
|
||||
if (document.getElementById('check-all').checked === true) {
|
||||
dataList.list.map((data, index) => {
|
||||
document.getElementsByName('select')[index].checked = true;
|
||||
list.push(String(data.id));
|
||||
});
|
||||
} else if (document.getElementById('check-all').checked === false) {
|
||||
for (let i = 0; i < dataList.list.length; i++) {
|
||||
dataList.list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
|
||||
list = [];
|
||||
}
|
||||
}
|
||||
setSelectedRow(list);
|
||||
};
|
||||
|
||||
// 선택 삭제 함수
|
||||
const handleSelectedDelete = () => {
|
||||
let list = [];
|
||||
|
||||
let isChecked = false;
|
||||
|
||||
selectedRow.map(data => {
|
||||
const row = dataList.list.find(row => row.id === Number(data));
|
||||
if(row.send_status === "FINISH" || row.send_status === "RUNNING") isChecked = true;
|
||||
list.push({
|
||||
id: data,
|
||||
});
|
||||
});
|
||||
|
||||
if(isChecked) {
|
||||
alert("발송 완료한 우편은 삭제할 수 없습니다.")
|
||||
handleDeleteModalClose();
|
||||
return;
|
||||
}
|
||||
|
||||
MailDelete(token, list);
|
||||
|
||||
handleDeleteModalClose();
|
||||
handleConfirmeModalClose();
|
||||
};
|
||||
|
||||
// 선택 삭제 모달
|
||||
const handleDeleteModalClose = () => {
|
||||
if (selectedRow.length !== 0 && deleteModalClose === 'hidden') {
|
||||
setDeleteModalClose('view');
|
||||
} else {
|
||||
setDeleteModalClose('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제, 승인, 저장 확인 모달창
|
||||
const handleConfirmeModalClose = () => {
|
||||
if (confirmModalClose === 'hidden') {
|
||||
setConfirmModalClose('view');
|
||||
} else {
|
||||
setConfirmModalClose('hidden');
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCountSelectedRow = () => {
|
||||
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.mailRead) ? (
|
||||
<AuthModal/>
|
||||
) : (
|
||||
<>
|
||||
<Title>우편 조회 및 발송 관리</Title>
|
||||
<FormWrapper>
|
||||
<MailListSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
|
||||
</FormWrapper>
|
||||
<ViewTableInfo total={dataList.total} total_all={dataList.total_all} handleOrderBy={handleOrderBy} handlePageSize={handlePageSize}>
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.mailDelete) && (
|
||||
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={handleDeleteModalClose} />
|
||||
)}
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.mailUpdate) && (
|
||||
<Button
|
||||
theme="primary"
|
||||
text="우편 등록"
|
||||
type="button"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
navigate('/servicemanage/mail/mailregist');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ViewTableInfo>
|
||||
<TableWrapper>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40">
|
||||
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
|
||||
</th>
|
||||
<th width="80">번호</th>
|
||||
<th width="210">등록 일시</th>
|
||||
<th width="210">발송 일시</th>
|
||||
<th width="100">발송 방식</th>
|
||||
<th width="100">발송 상태</th>
|
||||
<th width="100">우편 타입</th>
|
||||
<th width="100">수신 대상</th>
|
||||
<th>우편 제목</th>
|
||||
<th width="110">확인 / 수정</th>
|
||||
<th width="200">등록자(처리자)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.list &&
|
||||
dataList.list.map(mail => (
|
||||
<Fragment key={mail.row_num}>
|
||||
<tr>
|
||||
<td>
|
||||
<CheckBox name={'select'} id={mail.id} setData={e => handleSelectCheckBox(e)} />
|
||||
</td>
|
||||
<td>{mail.row_num}</td>
|
||||
<td>{convertKTC(mail.create_dt)}</td>
|
||||
<td>{convertKTC(mail.send_dt)}</td>
|
||||
<td>{mailSendType.map(data => data.value === mail.send_type && data.name)}</td>
|
||||
<td>
|
||||
{mail.send_status === 'FAIL' ? (
|
||||
<ListState>{mailSendStatus.map(data => data.value === mail.send_status && data.name)}</ListState>
|
||||
) : (
|
||||
mailSendStatus.map(data => data.value === mail.send_status && data.name)
|
||||
)}
|
||||
</td>
|
||||
<td>{mailType.map(data => data.value === mail.mail_type && data.name)}</td>
|
||||
<td>{mailReceiveType.map(data => data.value === mail.receive_type && data.name)}</td>
|
||||
<MailTitle>{mail.title}</MailTitle>
|
||||
<td>
|
||||
<Button theme="line" text="상세보기" handleClick={e => handleDetailModal(e, mail.id)} />
|
||||
</td>
|
||||
<td>{mail.create_by}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
|
||||
<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.mailDelete) && (
|
||||
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
||||
)}
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.mailUpdate) && (
|
||||
<Button
|
||||
theme="primary"
|
||||
text="우편 등록"
|
||||
type="button"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
navigate('/servicemanage/mail/mailregist');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ViewTableInfo>
|
||||
<TableWrapper>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40">
|
||||
</th>
|
||||
<th width="80">번호</th>
|
||||
<th width="210">등록 일시</th>
|
||||
<th width="210">발송 일시</th>
|
||||
<th width="100">발송 방식</th>
|
||||
<th width="100">발송 상태</th>
|
||||
<th width="100">우편 타입</th>
|
||||
<th width="100">수신 대상</th>
|
||||
<th>우편 제목</th>
|
||||
<th width="110">확인 / 수정</th>
|
||||
<th width="200">등록자(처리자)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList?.list?.map(mail => (
|
||||
<Fragment key={mail.row_num}>
|
||||
<tr>
|
||||
<td>
|
||||
<CheckBox name={'select'} id={mail.id}
|
||||
setData={(e) => handleSelectRow(e, mail)}
|
||||
checked={isRowSelected(mail.id)} />
|
||||
</td>
|
||||
<td>{mail.row_num}</td>
|
||||
<td>{convertKTC(mail.create_dt)}</td>
|
||||
<td>{convertKTC(mail.send_dt)}</td>
|
||||
<td>{mailSendType.map(data => data.value === mail.send_type && data.name)}</td>
|
||||
<StatusWapper>
|
||||
<StatusLabel $status={mail.send_status}>
|
||||
{mailSendStatus.map(data => data.value === mail.send_status && data.name)}
|
||||
</StatusLabel>
|
||||
</StatusWapper>
|
||||
<td>{mailType.map(data => data.value === mail.mail_type && data.name)}</td>
|
||||
<td>{mailReceiveType.map(data => data.value === mail.receive_type && data.name)}</td>
|
||||
<MailTitle>{mail.title}</MailTitle>
|
||||
<td>
|
||||
<Button theme="line" text="상세보기" handleClick={e => handleModalSubmit('detail', mail.id)} />
|
||||
</td>
|
||||
<td>{mail.create_by}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
||||
|
||||
<MailDetailModal
|
||||
detailView={modalState.detailModal}
|
||||
handleDetailView={() =>{
|
||||
handleModalClose('detail');
|
||||
handleSearch(updateSearchParams);
|
||||
}}
|
||||
content={detailData}
|
||||
/>
|
||||
|
||||
<MailDetailModal detailView={detailView} handleDetailView={handleDetailView} content={detailData} />
|
||||
{/* 선택 삭제 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteModalClose}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleDeleteModalClose} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">
|
||||
선택된 우편을 삭제하시겠습니까?
|
||||
<br />
|
||||
삭제 시 설정 정보가 제거됩니다.
|
||||
</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleDeleteModalClose} />
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedDelete} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
{/* 확인 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleConfirmeModalClose} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Mail;
|
||||
export default withAuth(authType.mailRead)(Mail);
|
||||
|
||||
const ListState = styled.span`
|
||||
color: #d60000;
|
||||
|
||||
@@ -1,40 +1,48 @@
|
||||
import { useState, Fragment } from 'react';
|
||||
import React, { useState, Fragment, useEffect } from 'react';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import RadioInput from '../../components/common/input/Radio';
|
||||
import CheckBox from '../../components/common/input/CheckBox';
|
||||
import Loading from '../../components/common/Loading';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { Title, BtnWrapper, TextInput, SelectInput, Label, InputLabel, DatePickerWrapper, Textarea, ModalText, ButtonClose, SearchBarAlert, AlertText } from '../../styles/Components';
|
||||
import {
|
||||
Title,
|
||||
BtnWrapper,
|
||||
TextInput,
|
||||
SelectInput,
|
||||
Label,
|
||||
InputLabel,
|
||||
DatePickerWrapper,
|
||||
Textarea,
|
||||
SearchBarAlert,
|
||||
} from '../../styles/Components';
|
||||
|
||||
import IconDelete from '../../assets/img/icon/icon-delete.png';
|
||||
import CloseIcon from '../../assets/img/icon/icon-close.png';
|
||||
|
||||
import { HourList, MinuteList, modalTypes, wellType } from '../../assets/data';
|
||||
import { benItems, HourList, MinuteList, wellType } from '../../assets/data';
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import MailRegistUploadBtn from '../../components/ServiceManage/MailRegistUploadBtn';
|
||||
import DatePickerComponent from '../../components/common/Date/DatePickerComponent';
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
import { MailCaliumTotalView, MailIsItem, MailMultiRegsit, MailSingleRegist } from '../../apis';
|
||||
import { MailCaliumTotalView, MailIsItem, MailSingleRegist } from '../../apis';
|
||||
|
||||
import { authList } from '../../store/authList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MailReceiver, RegistInputRow } from '../../styles/ModuleComponents';
|
||||
import AuthModal from '../../components/common/modal/AuthModal';
|
||||
import { authType } from '../../assets/data';
|
||||
import { useDataFetch } from '../../hooks/hook';
|
||||
import { BattleConfigView } from '../../apis/Battle';
|
||||
import { currencyCodeTypes } from '../../assets/data/types';
|
||||
import { DynamicModal } from '../../components/common';
|
||||
import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
|
||||
const MailRegist = () => {
|
||||
const navigate = useNavigate();
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const environment = process.env.REACT_APP_ENV;
|
||||
const token = sessionStorage.getItem('token');
|
||||
const { showModal, showToast } = useAlert();
|
||||
const { withLoading } = useLoading();
|
||||
|
||||
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
|
||||
|
||||
@@ -46,9 +54,6 @@ const MailRegist = () => {
|
||||
const [resource, setResource] = useState('19010001');
|
||||
const [resourceCount, setResourceCount] = useState(0);
|
||||
|
||||
const [cancelModalClose, setCancelModalClose] = useState('hidden');
|
||||
const [submitModal, setSubmitModal] = useState('hidden');
|
||||
const [completeModal, setCompleteModal] = useState('hidden');
|
||||
const [isNullValue, setIsNullValue] = useState(false);
|
||||
const [btnValidation, setBtnValidation] = useState(false);
|
||||
|
||||
@@ -57,13 +62,10 @@ const MailRegist = () => {
|
||||
|
||||
const [isItemNullValue, setIsItemNullValue] = useState(false);
|
||||
const [isResourceNullValue, setIsResourceNullValue] = useState(false);
|
||||
const [confirmText, setConfirmText] = useState('');
|
||||
const [alertMessage, setAlertMessage] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
|
||||
const {
|
||||
data: caliumTotalData
|
||||
data: caliumTotalData,
|
||||
} = useDataFetch(() => MailCaliumTotalView(token), [token]);
|
||||
|
||||
const [resultData, setResultData] = useState({
|
||||
@@ -87,16 +89,20 @@ const MailRegist = () => {
|
||||
title: '',
|
||||
content: '',
|
||||
language: 'JA',
|
||||
}
|
||||
},
|
||||
],
|
||||
item_list: [],
|
||||
resource_list: [],
|
||||
guid: '',
|
||||
});
|
||||
|
||||
const benItems = [
|
||||
"19010003"
|
||||
];
|
||||
useEffect(() => {
|
||||
if (checkCondition()) {
|
||||
setIsNullValue(false);
|
||||
} else {
|
||||
setIsNullValue(true);
|
||||
}
|
||||
}, [resultData]);
|
||||
|
||||
// 아이템 수량 숫자 체크
|
||||
const handleItemCount = e => {
|
||||
@@ -113,17 +119,17 @@ const MailRegist = () => {
|
||||
|
||||
// 아이템 추가
|
||||
const handleItemList = async () => {
|
||||
if(benItems.includes(item) && environment === "live"){
|
||||
alert(t('MAIL_ITEM_ADD_BEN'))
|
||||
if (benItems.includes(item) && environment === 'live') {
|
||||
showToast('MAIL_ITEM_ADD_BEN', { type: alertTypes.warning });
|
||||
return;
|
||||
}
|
||||
item.length === 0 || itemCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
|
||||
|
||||
// const token = sessionStorage.getItem('token');
|
||||
const result = await MailIsItem(token, {item: item});
|
||||
const result = await MailIsItem(token, { item: item });
|
||||
|
||||
if(result.data.result === "ERROR"){
|
||||
alert(result.data.data.message);
|
||||
if (result.data.result === 'ERROR') {
|
||||
showToast(result.data.data.message, { type: alertTypes.warning });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,11 +138,11 @@ const MailRegist = () => {
|
||||
} else if (item.length !== 0) {
|
||||
setIsItemNullValue(false);
|
||||
const itemIndex = resultData.item_list.findIndex(
|
||||
(item) => item.item === resource
|
||||
(item) => item.item === resource,
|
||||
);
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
alert(t('MAIL_ITEM_ADD_DUPL'));
|
||||
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 };
|
||||
@@ -188,22 +194,22 @@ const MailRegist = () => {
|
||||
setIsItemNullValue(false);
|
||||
|
||||
const itemIndex = resultData.item_list.findIndex(
|
||||
(item) => item.item === resource
|
||||
(item) => item.item === resource,
|
||||
);
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
const item_cnt = resultData.item_list[itemIndex].item_cnt;
|
||||
if(resource === currencyCodeTypes.calium){
|
||||
if((Number(resourceCount) + Number(item_cnt)) > caliumTotalData){
|
||||
setAlertMsg(t('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING'))
|
||||
if (resource === currencyCodeTypes.calium) {
|
||||
if ((Number(resourceCount) + Number(item_cnt)) > caliumTotalData) {
|
||||
showToast('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING', { type: alertTypes.warning });
|
||||
return;
|
||||
}
|
||||
}
|
||||
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
|
||||
} else {
|
||||
if(resource === currencyCodeTypes.calium){
|
||||
if(Number(resourceCount) > caliumTotalData){
|
||||
setAlertMsg(t('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING'))
|
||||
if (resource === currencyCodeTypes.calium) {
|
||||
if (Number(resourceCount) > caliumTotalData) {
|
||||
showToast('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING', { type: alertTypes.warning });
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -211,7 +217,7 @@ const MailRegist = () => {
|
||||
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
|
||||
resultData.item_list.push(newItem);
|
||||
}
|
||||
|
||||
|
||||
setResourceCount('');
|
||||
}
|
||||
};
|
||||
@@ -248,72 +254,51 @@ const MailRegist = () => {
|
||||
setResultData({ ...resultData, send_dt: result });
|
||||
};
|
||||
|
||||
const handleSubmitModal = () => {
|
||||
if (
|
||||
resultData.mail_list.map(data => data.content === '' || data.title === '').includes(true) ||
|
||||
(resultData.receive_type === 'MULTIPLE' ? excelFile === null : resultData.guid === '') ||
|
||||
(resultData.is_reserve && resultData.send_dt.length === 0) ||
|
||||
resultData.mail_type === 'SELECT' ||
|
||||
alertMessage
|
||||
) {
|
||||
setIsNullValue(true);
|
||||
} else if (submitModal === 'hidden') {
|
||||
setIsNullValue(false);
|
||||
setSubmitModal('view');
|
||||
} else {
|
||||
setSubmitModal('hidden');
|
||||
const handleSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case 'submit':
|
||||
if (!checkCondition()) return;
|
||||
|
||||
showModal('MAIL_REGIST_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('registConfirm'),
|
||||
});
|
||||
break;
|
||||
|
||||
case 'registConfirm':
|
||||
await withLoading(async () => {
|
||||
return await MailSingleRegist(token, resultData);
|
||||
}).then(data => {
|
||||
if (data.result === 'ERROR') {
|
||||
if (data.data.message === 'ERROR_MAIL_ITEM_CALIUM_OVER') {
|
||||
showToast('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING', { type: alertTypes.error });
|
||||
} else if (data.data.message === 'NOT_EXIT_EXCEL') {
|
||||
showToast('EXCEL_SELECT', { type: alertTypes.error });
|
||||
} else if (data.data.message === 'GUID_CHECK') {
|
||||
showToast('WARNING_GUID_CHECK', { type: alertTypes.error });
|
||||
} else if (data.data.message === 'NICKNAME_CHECK') {
|
||||
showToast('WARNING_NICKNAME_CHECK', { type: alertTypes.error });
|
||||
} else if (data.data.message === 'EMAIL_CHECK') {
|
||||
showToast('WARNING_EMAIL_CHECK', { type: alertTypes.error });
|
||||
} else if (data.data.message === 'USERTYPE_CHECK_EXCEL') {
|
||||
showToast('WARNING_TYPE_CHECK', { type: alertTypes.error });
|
||||
} else {
|
||||
showToast(data.data.message, { type: alertTypes.error });
|
||||
}
|
||||
} else {
|
||||
showToast('MAIL_REGIST_COMPLETE', { type: alertTypes.success });
|
||||
}
|
||||
}).catch(error => {
|
||||
showToast('API_FAIL', { type: alertTypes.error });
|
||||
}).finally(() => {
|
||||
callbackPage();
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 취소 모달
|
||||
const handleCancelModalClose = () => {
|
||||
if (cancelModalClose === 'hidden') {
|
||||
setCancelModalClose('view');
|
||||
} else {
|
||||
setCancelModalClose('hidden');
|
||||
setConfirmText(t('MAIL_CANCEL'));
|
||||
}
|
||||
};
|
||||
|
||||
// 완료 모달창
|
||||
const handleCompleteModal = () => {
|
||||
if (completeModal === 'hidden') {
|
||||
setCompleteModal('view');
|
||||
} else {
|
||||
setCompleteModal('hidden');
|
||||
|
||||
navigate('/servicemanage/mail');
|
||||
}
|
||||
};
|
||||
|
||||
const handleRegistMail = async () => {
|
||||
setLoading(true);
|
||||
await MailSingleRegist(token, resultData).then(data => {
|
||||
setLoading(false);
|
||||
handleSubmitModal();
|
||||
if(data.result === "ERROR"){
|
||||
if(data.data.message === "ERROR_MAIL_ITEM_CALIUM_OVER"){
|
||||
setAlertMsg(t('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING'));
|
||||
}else if(data.data.message === "NOT_EXIT_EXCEL"){
|
||||
setAlertMsg(t('EXCEL_SELECT'));
|
||||
}else if(data.data.message === "GUID_CHECK"){
|
||||
setAlertMsg(t('WARNING_GUID_CHECK'));
|
||||
}else if(data.data.message === "NICKNAME_CHECK"){
|
||||
setAlertMsg(t('WARNING_NICKNAME_CHECK'));
|
||||
}else if(data.data.message === "EMAIL_CHECK"){
|
||||
setAlertMsg(t('WARNING_EMAIL_CHECK'));
|
||||
}else if(data.data.message === "USERTYPE_CHECK_EXCEL") {
|
||||
setAlertMsg(t('WARNING_TYPE_CHECK'));
|
||||
}else{
|
||||
setAlertMsg(data.data.message);
|
||||
}
|
||||
}else{
|
||||
setConfirmText('우편이 정상 등록되었습니다.');
|
||||
handleCompleteModal();
|
||||
}
|
||||
}).catch(error => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
});
|
||||
const callbackPage = () => {
|
||||
navigate('/servicemanage/mail');
|
||||
};
|
||||
|
||||
const handleSingleBtn = () => {
|
||||
@@ -328,13 +313,19 @@ const MailRegist = () => {
|
||||
const handleMultiBtn = () => {
|
||||
delete resultData.guid;
|
||||
};
|
||||
// console.log('alertMessage', alertMessage);
|
||||
// console.log('resultData: ', resultData);
|
||||
|
||||
const checkCondition = () => {
|
||||
return resultData.mail_list.every(data => data.content !== '' && data.title !== '') &&
|
||||
(resultData.receive_type === 'MULTIPLE' ? excelFile !== null : resultData.guid !== '') &&
|
||||
(resultData.is_reserve ? resultData.send_dt.length !== 0 : resultData.send_dt.length === 0) &&
|
||||
resultData.mail_type !== 'SELECT'
|
||||
&& !alertMessage
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.mailUpdate) ? (
|
||||
<AuthModal/>
|
||||
<AuthModal />
|
||||
) : (
|
||||
<>
|
||||
<Title>우편 등록</Title>
|
||||
@@ -345,7 +336,11 @@ const MailRegist = () => {
|
||||
label="예약 발송"
|
||||
id="reserve"
|
||||
checked={resultData.is_reserve}
|
||||
setData={e => setResultData({ ...resultData, is_reserve: e.target.checked, send_dt: new Date() })}
|
||||
setData={e => setResultData({
|
||||
...resultData,
|
||||
is_reserve: e.target.checked,
|
||||
send_dt: new Date(),
|
||||
})}
|
||||
/>
|
||||
<InputItem>
|
||||
{resultData.is_reserve && (
|
||||
@@ -353,7 +348,9 @@ const MailRegist = () => {
|
||||
<InputLabel>발송 시간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent name="시작 일자" selectedDate={resultData.send_dt} handleSelectedDate={data => handleSelectedDate(data)} pastDate={new Date()} />
|
||||
<DatePickerComponent name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
pastDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
<SelectInput onChange={e => handleSendTime(e)} id="hour">
|
||||
{HourList.map(hour => (
|
||||
@@ -375,7 +372,8 @@ const MailRegist = () => {
|
||||
</InputItem>
|
||||
<InputItem>
|
||||
<InputLabel>우편 타입</InputLabel>
|
||||
<SelectInput onChange={e => setResultData({ ...resultData, mail_type: e.target.value })} value={resultData.mail_type}>
|
||||
<SelectInput onChange={e => setResultData({ ...resultData, mail_type: e.target.value })}
|
||||
value={resultData.mail_type}>
|
||||
<option value="SELECT">타입 선택</option>
|
||||
<option value="SYSTEM_GUID">시스템 안내</option>
|
||||
<option value="INSPECTION_COMPENSATION">점검 보상</option>
|
||||
@@ -389,7 +387,9 @@ const MailRegist = () => {
|
||||
<InputItem>
|
||||
<InputLabel>수신대상</InputLabel>
|
||||
<InputItem>
|
||||
<SelectInput onChange={e => setResultData({ ...resultData, user_type: e.target.value })} value={resultData.user_type} disabled={resultData.receive_type !== 'SINGLE'}>
|
||||
<SelectInput
|
||||
onChange={e => setResultData({ ...resultData, user_type: e.target.value })}
|
||||
value={resultData.user_type} disabled={resultData.receive_type !== 'SINGLE'}>
|
||||
<option value="GUID">GUID</option>
|
||||
<option value="NICKNAME">아바타명</option>
|
||||
<option value="EMAIL">이메일</option>
|
||||
@@ -404,11 +404,14 @@ const MailRegist = () => {
|
||||
value="SINGLE"
|
||||
fontWeight="600"
|
||||
checked={resultData.receive_type === 'SINGLE'}
|
||||
handleChange={e => setResultData({ ...resultData, receive_type: e.target.id })}
|
||||
handleChange={e => setResultData({
|
||||
...resultData,
|
||||
receive_type: e.target.id,
|
||||
})}
|
||||
handleClick={handleSingleBtn}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={resultData.user_type === "GUID" ? "GUID 입력" : resultData.user_type === "NICKNAME" ? "아바타명 입력" : "이메일 입력"}
|
||||
placeholder={resultData.user_type === 'GUID' ? 'GUID 입력' : resultData.user_type === 'NICKNAME' ? '아바타명 입력' : '이메일 입력'}
|
||||
disabled={resultData.receive_type !== 'SINGLE'}
|
||||
onChange={e => setResultData({ ...resultData, guid: e.target.value })}
|
||||
value={resultData.receive_type === 'SINGLE' && resultData.guid ? resultData.guid : ''}
|
||||
@@ -422,7 +425,10 @@ const MailRegist = () => {
|
||||
value="MULTIPLE"
|
||||
fontWeight="600"
|
||||
checked={resultData.receive_type === 'MULTIPLE'}
|
||||
handleChange={e => setResultData({ ...resultData, receive_type: e.target.id })}
|
||||
handleChange={e => setResultData({
|
||||
...resultData,
|
||||
receive_type: e.target.id,
|
||||
})}
|
||||
handleClick={handleMultiBtn}
|
||||
/>
|
||||
<MailRegistUploadBtn
|
||||
@@ -460,55 +466,57 @@ const MailRegist = () => {
|
||||
</LangArea>
|
||||
<MailRegistTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>제목</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<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 });
|
||||
}}
|
||||
/>
|
||||
</InputItem>
|
||||
<MailNotice $color={data.title.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.title.length}/30자)</MailNotice>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<Label>내용</Label>
|
||||
</th>
|
||||
<td>
|
||||
<Textarea
|
||||
maxLength="2000"
|
||||
value={data.content}
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>제목</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<TextInput
|
||||
placeholder="우편 제목 입력"
|
||||
maxLength="30"
|
||||
id={data.language}
|
||||
value={data.title}
|
||||
onChange={e => {
|
||||
if (e.target.value.length > 2000) {
|
||||
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].content = e.target.value.trimStart();
|
||||
list[findIndex].title = e.target.value.trimStart();
|
||||
|
||||
setResultData({ ...resultData, mail_list: list });
|
||||
}}
|
||||
/>
|
||||
<MailNotice $color={data.content.length > 1999 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.content.length}/2000자)</MailNotice>
|
||||
</td>
|
||||
</tr>
|
||||
</InputItem>
|
||||
<MailNotice $color={data.title.length > 29 ? 'red' : '#666'}>* 최대 등록 가능
|
||||
글자수 ({data.title.length}/30자)</MailNotice>
|
||||
</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 });
|
||||
}}
|
||||
/>
|
||||
<MailNotice $color={data.content.length > 1999 ? 'red' : '#666'}>* 최대 등록
|
||||
가능 글자수 ({data.content.length}/2000자)</MailNotice>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</MailRegistTable>
|
||||
</MailRegistBox>
|
||||
@@ -519,17 +527,21 @@ const MailRegist = () => {
|
||||
<MailRegistBox>
|
||||
<MailRegistTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>아이템 첨부</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<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" />
|
||||
</InputItem>
|
||||
{/* {isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>아이템 첨부</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<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" />
|
||||
</InputItem>
|
||||
{/* {isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
|
||||
|
||||
<div>
|
||||
{resultData.item_list && (
|
||||
@@ -547,44 +559,48 @@ const MailRegist = () => {
|
||||
</ItemList>
|
||||
)}
|
||||
</div> */}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>자원 첨부</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<SelectInput onChange={e => setResource(e.target.value)} value={resource}>
|
||||
<option value="19010001">골드</option>
|
||||
<option value="19010002">사파이어</option>
|
||||
<option value="19010005">루비</option>
|
||||
<option value="19010003">칼리움</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" />
|
||||
{resource === currencyCodeTypes.calium && <Label>(잔여 수량: {caliumTotalData})</Label>}
|
||||
</InputItem>
|
||||
{isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>자원 첨부</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<SelectInput onChange={e => setResource(e.target.value)} value={resource}>
|
||||
<option value="19010001">골드</option>
|
||||
<option value="19010002">사파이어</option>
|
||||
<option value="19010005">루비</option>
|
||||
<option value="19010003">칼리움</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" />
|
||||
{resource === currencyCodeTypes.calium &&
|
||||
<Label>(잔여 수량: {caliumTotalData})</Label>}
|
||||
</InputItem>
|
||||
{isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
|
||||
|
||||
<div>
|
||||
{resultData.item_list && (
|
||||
<ItemList>
|
||||
{resultData.item_list.map((data, index) => {
|
||||
return (
|
||||
<Item key={index}>
|
||||
<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>
|
||||
<BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>
|
||||
</Item>
|
||||
);
|
||||
})}
|
||||
</ItemList>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</MailRegistTable>
|
||||
</MailRegistBox>
|
||||
@@ -595,83 +611,21 @@ const MailRegist = () => {
|
||||
)}
|
||||
|
||||
<BtnWrapper $justify="flex-end" $gap="10px">
|
||||
<Button text="취소" theme="line" handleClick={handleCancelModalClose} />
|
||||
<Button
|
||||
text="취소"
|
||||
theme="line"
|
||||
handleClick={() => showModal('MAIL_REGIST_CANCEL', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => callbackPage(),
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
text="등록"
|
||||
theme={
|
||||
resultData.mail_list.map(data => data.content === '' || data.title === '').includes(true) ||
|
||||
(resultData.receive_type === 'MULTIPLE' ? excelFile === null : resultData.guid === '') ||
|
||||
(resultData.is_reserve && resultData.send_dt.length === 0) ||
|
||||
resultData.mail_type === 'SELECT' ||
|
||||
alertMessage
|
||||
? 'disable'
|
||||
: 'primary'
|
||||
}
|
||||
handleClick={handleSubmitModal}
|
||||
theme={checkCondition() ? 'primary' : 'disable'}
|
||||
handleClick={() => handleSubmit('submit')}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
{/* 등록 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={submitModal}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleSubmitModal} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">우편을 등록하시겠습니까?</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleSubmitModal} />
|
||||
<Button
|
||||
text="확인"
|
||||
theme="primary"
|
||||
type="submit"
|
||||
size="large"
|
||||
width="100%"
|
||||
handleClick={() => {handleRegistMail();}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
{/* 완료 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleCompleteModal} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">{confirmText}</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
{/* 취소 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={cancelModalClose}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleCancelModalClose} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">
|
||||
우편 등록을 취소하시겠습니까?
|
||||
<br />
|
||||
취소 시 설정된 값은 반영되지 않습니다.
|
||||
</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleCancelModalClose} />
|
||||
<Button
|
||||
text="확인"
|
||||
theme="primary"
|
||||
type="submit"
|
||||
size="large"
|
||||
width="100%"
|
||||
handleClick={() => {
|
||||
handleCancelModalClose();
|
||||
handleCompleteModal();
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => setAlertMsg('')}
|
||||
/>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
@@ -681,126 +635,134 @@ const MailRegist = () => {
|
||||
export default MailRegist;
|
||||
|
||||
const InputRow = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 50px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 50px;
|
||||
`;
|
||||
|
||||
const InputItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
${TextInput},${SelectInput} {
|
||||
height: 35px;
|
||||
font-size: 14px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 20px;
|
||||
}
|
||||
${SelectInput} {
|
||||
width: max-content;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
${TextInput} {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
${SelectInput} {
|
||||
width: max-content;
|
||||
}
|
||||
`;
|
||||
|
||||
const InputGroup = styled.div`
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const MailNotice = styled.span`
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
color: ${props => props.$color || '#999'};
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
color: ${props => props.$color || '#999'};
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
`;
|
||||
const RegistGroup = styled.div`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-flow: column;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
font-size: 14px;
|
||||
margin-bottom: 40px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-flow: column;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
font-size: 14px;
|
||||
margin-bottom: 40px;
|
||||
|
||||
${MailNotice} {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
${MailNotice} {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
const BtnClose = styled.button`
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url(${CloseIcon}) 50% 50% no-repeat;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
right: 20px;
|
||||
opacity: ${props => props.opacity};
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url(${CloseIcon}) 50% 50% no-repeat;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
right: 20px;
|
||||
opacity: ${props => props.opacity};
|
||||
`;
|
||||
|
||||
const LangArea = styled.div`
|
||||
background: #f9f9f9;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
background: #f9f9f9;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const MailRegistBox = styled.div`
|
||||
margin-bottom: 20px;
|
||||
border-top: 1px solid #999;
|
||||
border-bottom: 1px solid #999;
|
||||
margin-bottom: 20px;
|
||||
border-top: 1px solid #999;
|
||||
border-bottom: 1px solid #999;
|
||||
`;
|
||||
|
||||
const MailRegistTable = styled.table`
|
||||
th {
|
||||
vertical-align: top;
|
||||
line-height: 35px;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
}
|
||||
td {
|
||||
${InputItem} {
|
||||
gap: 5px;
|
||||
}
|
||||
${TextInput} {
|
||||
max-width: 600px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
${Textarea} {
|
||||
width: 100%;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 5px;
|
||||
height: 255px;
|
||||
padding: 15px;
|
||||
&:focus {
|
||||
border: 1px solid #2c2c2c;
|
||||
}
|
||||
}
|
||||
}
|
||||
th {
|
||||
vertical-align: top;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
}
|
||||
|
||||
td {
|
||||
${InputItem} {
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
${TextInput} {
|
||||
max-width: 600px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
${Textarea} {
|
||||
width: 100%;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 5px;
|
||||
height: 255px;
|
||||
padding: 15px;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid #2c2c2c;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ItemList = styled.ul`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
padding: 10px 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
padding: 10px 20px;
|
||||
`;
|
||||
|
||||
const Item = styled.li`
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const BtnDelete = styled.button`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: url(${IconDelete}) 50% 50% no-repeat;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: url(${IconDelete}) 50% 50% no-repeat;
|
||||
`;
|
||||
|
||||
247
src/pages/ServiceManage/MenuBanner.js
Normal file
247
src/pages/ServiceManage/MenuBanner.js
Normal file
@@ -0,0 +1,247 @@
|
||||
import { useState, Fragment, useRef } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
|
||||
import { authList } from '../../store/authList';
|
||||
import {
|
||||
authType,
|
||||
modalTypes,
|
||||
landAuctionStatusType, opYNType,
|
||||
} from '../../assets/data';
|
||||
import { Title, FormWrapper, TableStyle, TableWrapper} from '../../styles/Components';
|
||||
import {
|
||||
CheckBox,
|
||||
Button,
|
||||
DynamicModal,
|
||||
Pagination,
|
||||
ViewTableInfo,
|
||||
} from '../../components/common';
|
||||
import { convertKTC, timeDiffMinute } from '../../utils';
|
||||
import { INITIAL_PAGE_SIZE, INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||
import { useModal, useTable, withAuth } from '../../hooks/hook';
|
||||
import { StatusWapper, StatusLabel } from '../../styles/ModuleComponents';
|
||||
import { opMenuBannerStatus } from '../../assets/data/options';
|
||||
import MenuBannerSearchBar, { useMenuBannerSearch } from '../../components/ServiceManage/searchBar/MenuBannerSearchBar';
|
||||
import { MenuBannerDelete, MenuBannerDetailView } from '../../apis';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import MenuBannerModal from '../../components/ServiceManage/modal/MenuBannerModal';
|
||||
|
||||
const MenuBanner = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const tableRef = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [detailData, setDetailData] = useState({});
|
||||
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
detail: 'hidden',
|
||||
deleteConfirm: 'hidden',
|
||||
deleteComplete: 'hidden'
|
||||
});
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
const [modalType, setModalType] = useState('regist');
|
||||
|
||||
const {
|
||||
searchParams,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleOrderByChange,
|
||||
updateSearchParams
|
||||
} = useMenuBannerSearch(token, INITIAL_PAGE_SIZE);
|
||||
|
||||
const {
|
||||
selectedRows,
|
||||
handleSelectRow,
|
||||
isRowSelected
|
||||
} = useTable(dataList?.event_list || [], {mode: 'single'});
|
||||
|
||||
|
||||
const handleModalSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "regist":
|
||||
setModalType('regist');
|
||||
handleModalView('detail');
|
||||
break;
|
||||
case "detail":
|
||||
await MenuBannerDetailView(token, param).then(data => {
|
||||
setDetailData(data.event_detail);
|
||||
setModalType('modify');
|
||||
handleModalView('detail');
|
||||
});
|
||||
break;
|
||||
case "delete":
|
||||
const date_check = selectedRows.every(row => {
|
||||
const timeDiff = timeDiffMinute(convertKTC(row.auction_start_dt), (new Date));
|
||||
return timeDiff < 3;
|
||||
});
|
||||
if(date_check){
|
||||
setAlertMsg(t('LAND_AUCTION_DELETE_DATE_WARNING'));
|
||||
return;
|
||||
}
|
||||
if(selectedRows[0].status === landAuctionStatusType.auction_start || selectedRows[0].status === landAuctionStatusType.stl_end){
|
||||
setAlertMsg(t('LAND_AUCTION_DELETE_STATUS_WARNING'));
|
||||
return;
|
||||
}
|
||||
handleModalView('deleteConfirm');
|
||||
break;
|
||||
case "deleteConfirm":
|
||||
let list = [];
|
||||
let isChecked = false;
|
||||
|
||||
selectedRows.map(data => {
|
||||
// const row = dataList.list.find(row => row.id === Number(data.id));
|
||||
// if(row.status !== commonStatus.wait) isChecked = true;
|
||||
list.push({
|
||||
id: data.id,
|
||||
});
|
||||
});
|
||||
|
||||
if(isChecked) {
|
||||
setAlertMsg(t('LAND_AUCTION_WARNING_DELETE'))
|
||||
handleModalClose('deleteConfirm');
|
||||
return;
|
||||
}
|
||||
|
||||
await MenuBannerDelete(token, list).then(data => {
|
||||
handleModalClose('deleteConfirm');
|
||||
if(data.result === "SUCCESS") {
|
||||
handleModalView('deleteComplete');
|
||||
}else if(data.result === "ERROR_AUCTION_STATUS_IMPOSSIBLE"){
|
||||
setAlertMsg(t('LAND_AUCTION_ERROR_DELETE_STATUS'));
|
||||
}else{
|
||||
setAlertMsg(t('DELETE_FAIL'));
|
||||
}
|
||||
}).catch(reason => {
|
||||
setAlertMsg(t('API_FAIL'));
|
||||
});
|
||||
|
||||
break;
|
||||
case "deleteComplete":
|
||||
handleModalClose('deleteComplete');
|
||||
window.location.reload();
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>메뉴 배너 관리</Title>
|
||||
<FormWrapper>
|
||||
<MenuBannerSearchBar
|
||||
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?.some(auth => auth.id === authType.battleEventDelete) && (
|
||||
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
||||
)}
|
||||
{userInfo.auth_list?.some(auth => auth.id === authType.battleEventUpdate) && (
|
||||
<Button
|
||||
theme="primary"
|
||||
text="이미지 등록"
|
||||
type="button"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
navigate('/servicemanage/menubanner/menubannerregist');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ViewTableInfo>
|
||||
<TableWrapper>
|
||||
<TableStyle ref={tableRef}>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40"></th>
|
||||
<th width="70">번호</th>
|
||||
<th width="80">등록 상태</th>
|
||||
<th width="150">시작일(KST)</th>
|
||||
<th width="150">종료일(KST)</th>
|
||||
<th width="300">설명 제목</th>
|
||||
<th width="90">링크여부</th>
|
||||
<th width="100">상세보기</th>
|
||||
<th width="150">히스토리</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList?.list?.map(banner => (
|
||||
<tr key={banner.row_num}>
|
||||
<td>
|
||||
<CheckBox name={'select'} id={banner.id}
|
||||
setData={(e) => handleSelectRow(e, banner)}
|
||||
checked={isRowSelected(banner.id)} />
|
||||
</td>
|
||||
<td>{banner.row_num}</td>
|
||||
<StatusWapper>
|
||||
<StatusLabel $status={banner.status}>
|
||||
{opMenuBannerStatus.find(data => data.value === banner.status)?.name}
|
||||
</StatusLabel>
|
||||
</StatusWapper>
|
||||
<td>{convertKTC(banner.start_dt)}</td>
|
||||
<td>{convertKTC(banner.end_dt)}</td>
|
||||
<td>{banner.title}</td>
|
||||
<td>{opYNType.find(data => data.value === banner.is_link)?.name}</td>
|
||||
<td>
|
||||
<Button theme="line" text="상세보기"
|
||||
handleClick={e => handleModalSubmit('detail', banner.id)} />
|
||||
</td>
|
||||
<td>{banner.update_by}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
|
||||
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
||||
|
||||
{/*상세*/}
|
||||
<MenuBannerModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData} />
|
||||
|
||||
{/*삭제 확인*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.deleteConfirmModal}
|
||||
handleCancel={() => handleModalClose('deleteConfirm')}
|
||||
handleSubmit={() => handleModalSubmit('deleteConfirm')}
|
||||
modalText={t('MENU_BANNER_SELECT_DELETE')}
|
||||
/>
|
||||
{/*삭제 완료*/}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.deleteCompleteModal}
|
||||
handleSubmit={() => handleModalSubmit('deleteComplete')}
|
||||
modalText={t('DEL_COMPLETE')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleModalSubmit('warning')}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
export default withAuth(authType.battleEventRead)(MenuBanner);
|
||||
415
src/pages/ServiceManage/MenuBannerRegist.js
Normal file
415
src/pages/ServiceManage/MenuBannerRegist.js
Normal file
@@ -0,0 +1,415 @@
|
||||
import React, { useState, Fragment, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
import Loading from '../../components/common/Loading';
|
||||
import {
|
||||
Title,
|
||||
BtnWrapper,
|
||||
SearchBarAlert,
|
||||
} from '../../styles/Components';
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { MenuBannerSingleRegist } from '../../apis';
|
||||
|
||||
import { authList } from '../../store/authList';
|
||||
import {
|
||||
FormInput, FormInputSuffix, FormInputSuffixWrapper, FormLabel, FormRowGroup,RegistGroup,
|
||||
} from '../../styles/ModuleComponents';
|
||||
import AuthModal from '../../components/common/modal/AuthModal';
|
||||
import { authType, modalTypes } from '../../assets/data';
|
||||
import DynamicModal from '../../components/common/modal/DynamicModal';
|
||||
import { timeDiffMinute } from '../../utils';
|
||||
import { SingleDatePicker, SingleTimePicker } from '../../components/common';
|
||||
import CheckBox from '../../components/common/input/CheckBox';
|
||||
import ImageUploadBtn from '../../components/ServiceManage/ImageUploadBtn';
|
||||
import { useModal } from '../../hooks/hook';
|
||||
|
||||
const MenuBannerRegist = () => {
|
||||
const navigate = useNavigate();
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
const [loading, setLoading] = useState(false); // 로딩 창
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
cancel: 'hidden',
|
||||
registConfirm: 'hidden',
|
||||
registComplete: 'hidden'
|
||||
});
|
||||
|
||||
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
|
||||
const [resultData, setResultData] = useState(initData); //데이터 정보
|
||||
const [resetDateTime, setResetDateTime] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (checkCondition()) {
|
||||
setIsNullValue(false);
|
||||
} else {
|
||||
setIsNullValue(true);
|
||||
}
|
||||
}, [resultData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (resetDateTime) {
|
||||
setResetDateTime(false);
|
||||
}
|
||||
}, [resetDateTime]);
|
||||
|
||||
// 시작 날짜 변경 핸들러
|
||||
const handleStartDateChange = (date) => {
|
||||
if (!date) return;
|
||||
|
||||
const newDate = new Date(date);
|
||||
|
||||
if(resultData.end_dt){
|
||||
const endDate = new Date(resultData.end_dt);
|
||||
const startDay = new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate());
|
||||
const endDay = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
if (endDay <= startDay) {
|
||||
setAlertMsg(t('DATE_START_DIFF_END_WARNING'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setResultData(prev => ({
|
||||
...prev,
|
||||
start_dt: newDate
|
||||
}));
|
||||
};
|
||||
|
||||
// 시작 시간 변경 핸들러
|
||||
const handleStartTimeChange = (time) => {
|
||||
if (!time) return;
|
||||
|
||||
const newDateTime = resultData.start_dt
|
||||
? new Date(resultData.start_dt)
|
||||
: new Date();
|
||||
|
||||
newDateTime.setHours(
|
||||
time.getHours(),
|
||||
time.getMinutes(),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
setResultData(prev => ({
|
||||
...prev,
|
||||
start_dt: newDateTime
|
||||
}));
|
||||
};
|
||||
|
||||
// 종료 날짜 변경 핸들러
|
||||
const handleEndDateChange = (date) => {
|
||||
if (!date || !resultData.start_dt) return;
|
||||
|
||||
const startDate = new Date(resultData.start_dt);
|
||||
const endDate = new Date(date);
|
||||
|
||||
// 일자만 비교하기 위해 년/월/일만 추출
|
||||
const startDay = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
|
||||
const endDay = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
if (endDay <= startDay) {
|
||||
setAlertMsg(t('DATE_START_DIFF_END_WARNING'));
|
||||
return;
|
||||
}
|
||||
|
||||
setResultData(prev => ({
|
||||
...prev,
|
||||
end_dt: endDate
|
||||
}));
|
||||
};
|
||||
|
||||
// 종료 시간 변경 핸들러
|
||||
const handleEndTimeChange = (time) => {
|
||||
if (!time) return;
|
||||
|
||||
const newDateTime = resultData.end_dt
|
||||
? new Date(resultData.end_dt)
|
||||
: new Date();
|
||||
|
||||
newDateTime.setHours(
|
||||
time.getHours(),
|
||||
time.getMinutes(),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
setResultData(prev => ({
|
||||
...prev,
|
||||
end_dt: newDateTime
|
||||
}));
|
||||
};
|
||||
|
||||
// 이미지 업로드
|
||||
const handleImageUpload = (language, file, fileName) => {
|
||||
const imageIndex = resultData.image_list.findIndex(img => img.language === language);
|
||||
|
||||
if (imageIndex !== -1) {
|
||||
const updatedImageList = [...resultData.image_list];
|
||||
updatedImageList[imageIndex] = {
|
||||
...updatedImageList[imageIndex],
|
||||
content: fileName,
|
||||
};
|
||||
|
||||
setResultData({
|
||||
...resultData,
|
||||
image_list: updatedImageList
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 이미지 삭제
|
||||
const handleImageDelete = (language) => {
|
||||
const imageIndex = resultData.image_list.findIndex(img => img.language === language);
|
||||
|
||||
if (imageIndex !== -1) {
|
||||
const updatedImageList = [...resultData.image_list];
|
||||
updatedImageList[imageIndex] = {
|
||||
...updatedImageList[imageIndex],
|
||||
content: '',
|
||||
};
|
||||
|
||||
setResultData({
|
||||
...resultData,
|
||||
image_list: updatedImageList
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "submit":
|
||||
if (!checkCondition()) return;
|
||||
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date))
|
||||
if(timeDiff < 60) {
|
||||
setAlertMsg(t('EVENT_TIME_LIMIT_ADD'));
|
||||
return;
|
||||
}
|
||||
|
||||
handleModalView('registConfirm');
|
||||
break;
|
||||
case "cancel":
|
||||
|
||||
handleModalClose('cancel');
|
||||
navigate('/servicemanage/menubanner');
|
||||
break;
|
||||
case "registConfirm":
|
||||
setLoading(true);
|
||||
|
||||
const result = await MenuBannerSingleRegist(token, resultData);
|
||||
|
||||
setLoading(false);
|
||||
handleModalClose('registConfirm');
|
||||
handleModalView('registComplete');
|
||||
break;
|
||||
case "registComplete":
|
||||
handleModalClose('registComplete');
|
||||
|
||||
navigate('/servicemanage/menubanner');
|
||||
break;
|
||||
case "warning":
|
||||
setAlertMsg('');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const checkCondition = () => {
|
||||
return (
|
||||
(resultData.start_dt.length !== 0) &&
|
||||
(resultData.end_dt.length !== 0) &&
|
||||
resultData.title !== '' &&
|
||||
resultData.image_list.every(data => data.content !== '') &&
|
||||
(resultData.is_link === false || (resultData.is_link === true && resultData.link_list.every(data => data.content !== '')))
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) ? (
|
||||
<AuthModal/>
|
||||
) : (
|
||||
<>
|
||||
<Title>메뉴배너 등록</Title>
|
||||
<RegistGroup>
|
||||
<FormRowGroup>
|
||||
<FormLabel>등록기간</FormLabel>
|
||||
<SingleDatePicker
|
||||
label="시작일자"
|
||||
dateLabel="시작 일자"
|
||||
onDateChange={handleStartDateChange}
|
||||
selectedDate={resultData?.start_dt}
|
||||
/>
|
||||
<SingleTimePicker
|
||||
selectedTime={resultData?.start_dt}
|
||||
onTimeChange={handleStartTimeChange}
|
||||
/>
|
||||
<SingleDatePicker
|
||||
label="종료일자"
|
||||
dateLabel="종료 일자"
|
||||
onDateChange={handleEndDateChange}
|
||||
selectedDate={resultData?.end_dt}
|
||||
/>
|
||||
<SingleTimePicker
|
||||
selectedTime={resultData?.end_dt}
|
||||
onTimeChange={handleEndTimeChange}
|
||||
/>
|
||||
</FormRowGroup>
|
||||
|
||||
<FormRowGroup>
|
||||
<FormLabel>배너 제목</FormLabel>
|
||||
<FormInput
|
||||
type="text"
|
||||
width='50%'
|
||||
value={resultData?.title}
|
||||
onChange={e => setResultData({ ...resultData, title: e.target.value })}
|
||||
/>
|
||||
</FormRowGroup>
|
||||
<FormLabel>이미지 첨부</FormLabel>
|
||||
{resultData.image_list.map((data, idx) => (
|
||||
<LanguageWrapper key={idx}>
|
||||
<LanguageLabel>{data.language}</LanguageLabel>
|
||||
<ImageUploadBtn
|
||||
onImageUpload={(file, fileName) => handleImageUpload(data.language, file, fileName)}
|
||||
onFileDelete={() => handleImageDelete(data.language)}
|
||||
fileName={data.content}
|
||||
setAlertMessage={setAlertMsg}
|
||||
/>
|
||||
</LanguageWrapper>
|
||||
))}
|
||||
<FormRowGroup>
|
||||
<CheckBox
|
||||
label="이미지 링크 여부"
|
||||
id="reserve"
|
||||
checked={resultData.is_link}
|
||||
setData={e => setResultData({ ...resultData, is_link: e.target.checked })}
|
||||
/>
|
||||
</FormRowGroup>
|
||||
{resultData?.is_link &&
|
||||
<>
|
||||
<FormRowGroup>
|
||||
<FormLabel>웹 링크</FormLabel>
|
||||
<LanguageWrapper width="50%" >
|
||||
{resultData.link_list.map((data, idx) => (
|
||||
<FormInputSuffixWrapper>
|
||||
<FormInput
|
||||
type="text"
|
||||
value={resultData?.link_list[idx].content}
|
||||
onChange={e => {
|
||||
const updatedLinkList = [...resultData.link_list];
|
||||
updatedLinkList[idx] = { ...updatedLinkList[idx], content: e.target.value };
|
||||
setResultData({ ...resultData, link_list: updatedLinkList });
|
||||
}}
|
||||
suffix="true"
|
||||
/>
|
||||
<FormInputSuffix>{data.language}</FormInputSuffix>
|
||||
</FormInputSuffixWrapper>
|
||||
))}
|
||||
</LanguageWrapper>
|
||||
</FormRowGroup>
|
||||
</>
|
||||
}
|
||||
</RegistGroup>
|
||||
|
||||
{isNullValue && (
|
||||
<SearchBarAlert $align="right" $padding="0 0 15px">
|
||||
{t('NULL_MSG')}
|
||||
</SearchBarAlert>
|
||||
)}
|
||||
|
||||
<BtnWrapper $justify="flex-end" $gap="10px">
|
||||
<Button text="취소" theme="line" handleClick={() => handleModalView('cancel')} />
|
||||
<Button
|
||||
type="submit"
|
||||
text="등록"
|
||||
theme={checkCondition() ? 'primary' : 'disable'}
|
||||
handleClick={() => handleSubmit('submit')}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
|
||||
{/* 등록 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.registConfirmModal}
|
||||
modalText={t('MENU_BANNER_REGIST_CONFIRM')}
|
||||
handleSubmit={() => handleSubmit('registConfirm')}
|
||||
handleCancel={() => handleModalClose('registConfirm')}
|
||||
/>
|
||||
{/* 완료 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.registCompleteModal}
|
||||
modalText={t('REGIST_COMPLTE')}
|
||||
handleSubmit={() => handleSubmit('registComplete')}
|
||||
/>
|
||||
{/* 취소 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.cancelModal}
|
||||
modalText={t('MENU_BANNER_REGIST_CANCEL')}
|
||||
handleCancel={() => handleModalClose('cancel')}
|
||||
handleSubmit={() => handleSubmit('cancel')}
|
||||
/>
|
||||
{/* 경고 모달 */}
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={alertMsg ? 'view' : 'hidden'}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('warning')}
|
||||
/>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const initData = {
|
||||
title: '',
|
||||
is_link: false,
|
||||
start_dt: '',
|
||||
end_dt: '',
|
||||
image_list: [
|
||||
{ language: 'KO', content: '' },
|
||||
{ language: 'EN', content: '' },
|
||||
{ language: 'JA', content: '' },
|
||||
],
|
||||
link_list: [
|
||||
{ language: 'KO', content: '' },
|
||||
{ language: 'EN', content: '' },
|
||||
{ language: 'JA', content: '' },
|
||||
],
|
||||
}
|
||||
|
||||
export default MenuBannerRegist;
|
||||
|
||||
const LanguageWrapper = styled.div`
|
||||
width: ${props => props.width || '100%'};
|
||||
//margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
padding-left: 90px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const LanguageLabel = styled.h4`
|
||||
color: #444;
|
||||
margin: 0 0 10px 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
@@ -1,339 +1,145 @@
|
||||
import { useState, Fragment, useEffect } from 'react';
|
||||
import CheckBox from '../../components/common/input/CheckBox';
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import styled from 'styled-components';
|
||||
import { Title, FormWrapper, TableInfo, ListCount, ListOption, TableStyle, SelectInput, TableWrapper, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
|
||||
import { useState, Fragment, useRef } from 'react';
|
||||
import { Title, FormWrapper } from '../../styles/Components';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { UserBlockDetailModal, UserBlockSearchBar } from '../../components/ServiceManage/';
|
||||
import { BlackListView, BlackListDetail, BlackListDelete } from '../../apis';
|
||||
import {
|
||||
CommonSearchBar,
|
||||
useCommonSearch,
|
||||
UserBlockDetailModal,
|
||||
} from '../../components/ServiceManage/';
|
||||
import { BlackListDetail, BlackListDelete } from '../../apis';
|
||||
import Pagination from '../../components/common/Pagination/Pagination';
|
||||
|
||||
import { authList } from '../../store/authList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { authType, blockPeriod, blockSanctions, blockStatus } from '../../assets/data';
|
||||
import AuthModal from '../../components/common/modal/AuthModal';
|
||||
import { authType } from '../../assets/data';
|
||||
import { CaliTable, TableHeader } from '../../components/common';
|
||||
import { useModal, useTable, withAuth } from '../../hooks/hook';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||
import tableInfo from '../../assets/data/pages/userBlockTable.json'
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
|
||||
const UserBlock = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userInfo = useRecoilValue(authList);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const {showModal, showToast} = useAlert();
|
||||
const {withLoading} = useLoading();
|
||||
|
||||
// 페이지 네이션 관련
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(50);
|
||||
|
||||
// 데이터 조회 관련
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [detailView, setDetailView] = useState('hidden');
|
||||
const [selectedRow, setSelectedRow] = useState([]);
|
||||
const [selectedData, setSelectedData] = useState([]);
|
||||
const [detail, setDetail] = useState([]);
|
||||
const [requestValue, setRequestValue] = useState([]);
|
||||
|
||||
// 모달 관련 변수
|
||||
const [confirmModal, setConfirmModal] = useState('hidden');
|
||||
const [completeModal, setCompleteModal] = useState('hidden');
|
||||
|
||||
// 검색 관련 변수
|
||||
const [orderBy, setOrderBy] = useState('DESC');
|
||||
const [searchData, setSearchData] = useState({
|
||||
searchType: 'GUID',
|
||||
searchKey: '',
|
||||
status: 'ALL',
|
||||
sanctions: 'ALL',
|
||||
period: 'ALL',
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
detail: 'hidden',
|
||||
});
|
||||
|
||||
// 등록하기 모달 연결
|
||||
const handleDetail = async dataValue => {
|
||||
if (detailView === 'hidden') {
|
||||
setDetailView('block');
|
||||
} else {
|
||||
setDetailView('hidden');
|
||||
setDetail([]);
|
||||
|
||||
const {
|
||||
config,
|
||||
searchParams,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleOrderByChange,
|
||||
updateSearchParams,
|
||||
configLoaded
|
||||
} = useCommonSearch(token, "userBlockSearch");
|
||||
|
||||
const {
|
||||
selectedRows,
|
||||
handleSelectRow,
|
||||
isRowSelected
|
||||
} = useTable(dataList?.list || [], {mode: 'single'});
|
||||
|
||||
const handleAction = async (action, item = null) => {
|
||||
switch (action) {
|
||||
case "detail":
|
||||
await BlackListDetail(token, item.id).then(data => {
|
||||
setDetail(data);
|
||||
handleModalView('detail');
|
||||
});
|
||||
break;
|
||||
case "delete":
|
||||
showModal('USER_BLOCK_DELETE_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleAction('deleteConfirm')
|
||||
});
|
||||
break;
|
||||
case "deleteConfirm":
|
||||
let list = [];
|
||||
|
||||
selectedRows.map(data => {
|
||||
list.push({
|
||||
id: data.id,
|
||||
});
|
||||
});
|
||||
|
||||
await withLoading(async () => {
|
||||
return BlackListDelete(token, list);
|
||||
}).then(data => {
|
||||
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||
}).catch(reason => {
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() => {
|
||||
handleSearch(updateSearchParams);
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
setSelectedData(dataValue);
|
||||
|
||||
const id = dataValue.id;
|
||||
|
||||
if (id) {
|
||||
setDetail(await BlackListDetail(token, id));
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = async (searchType, data, email, status, sanctions, period, order, size) => {
|
||||
setDataList(await BlackListView(token, searchType, data, email, status, sanctions, period, order ? order : orderBy, size ? size : pageSize, currentPage));
|
||||
};
|
||||
|
||||
// 검색 기능
|
||||
const handleSearch = (searchType, data, email, status, sanctions, period) => {
|
||||
fetchData(searchType, data, email, status, sanctions, period);
|
||||
};
|
||||
|
||||
// 배열 정렬
|
||||
const handleOrderBy = e => {
|
||||
const order = e.target.value;
|
||||
|
||||
setOrderBy(order);
|
||||
fetchData(searchData.searchType, searchData.searchKey, searchData.email, searchData.status, searchData.sanctions, searchData.period, order, pageSize);
|
||||
};
|
||||
|
||||
const handlePageSize = e => {
|
||||
const size = e.target.value;
|
||||
setPageSize(size);
|
||||
setCurrentPage(1);
|
||||
|
||||
fetchData(searchData.searchType, searchData.searchKey, searchData.email, searchData.status, searchData.sanctions, searchData.period, orderBy, size, 1);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(searchData.searchType, searchData.searchKey, searchData.email, searchData.status, searchData.sanctions, searchData.period, orderBy, pageSize);
|
||||
setSelectedRow([]);
|
||||
}, [currentPage]);
|
||||
|
||||
// 전체 선택
|
||||
const handleAllSelect = () => {
|
||||
const checkAll = document.getElementById('check-all');
|
||||
let list = [];
|
||||
|
||||
if (checkAll.checked === true) {
|
||||
dataList.list.map((data, index) => {
|
||||
document.getElementsByName('select')[index].checked = true;
|
||||
list.push(String(data.id));
|
||||
});
|
||||
} else if (checkAll.checked === false) {
|
||||
for (let i = 0; i < dataList.list.length; i++) {
|
||||
dataList.list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
|
||||
list = [];
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedRow(list);
|
||||
};
|
||||
|
||||
// 일부 선택
|
||||
const handleSelectCheckBox = e => {
|
||||
let list = [...selectedRow];
|
||||
|
||||
if (e.target.checked) {
|
||||
list.push(e.target.id);
|
||||
|
||||
setSelectedRow(list);
|
||||
} else {
|
||||
const filterList = list.filter(data => e.target.id !== data);
|
||||
setSelectedRow(filterList);
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제 여부 모달
|
||||
const handleConfirmeModalClose = () => {
|
||||
if (confirmModal === 'hidden') {
|
||||
setConfirmModal('view');
|
||||
} else {
|
||||
setConfirmModal('hidden');
|
||||
setRequestValue([]);
|
||||
}
|
||||
};
|
||||
|
||||
// 선택, 복수 삭제 모달창
|
||||
const handleConfirmModal = data => {
|
||||
if (data.id) {
|
||||
setSelectedData(data.id);
|
||||
if (selectedData) {
|
||||
setRequestValue(data.id);
|
||||
}
|
||||
} else if (selectedRow) {
|
||||
setRequestValue(selectedRow);
|
||||
}
|
||||
|
||||
handleConfirmeModalClose();
|
||||
};
|
||||
|
||||
// 완료 확인 모달
|
||||
const handleCompleteModal = () => {
|
||||
if (completeModal === 'hidden') {
|
||||
setCompleteModal('view');
|
||||
} else {
|
||||
setCompleteModal('hidden');
|
||||
setRequestValue([]);
|
||||
handleConfirmeModalClose();
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제 기능 구현
|
||||
const handleDelete = () => {
|
||||
let list = [];
|
||||
|
||||
if (requestValue === selectedRow) {
|
||||
selectedRow.map(data =>
|
||||
list.push({
|
||||
id: data,
|
||||
}),
|
||||
);
|
||||
BlackListDelete(token, list);
|
||||
// console.log(list);
|
||||
// console.log(selectedRow);
|
||||
handleCompleteModal();
|
||||
} else if (requestValue !== selectedRow) {
|
||||
list.push({ id: selectedData });
|
||||
// console.log(list);
|
||||
BlackListDelete(token, list);
|
||||
handleCompleteModal();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCountSelectedRow = () => {
|
||||
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.blackListRead) ? (
|
||||
<AuthModal/>
|
||||
) : (
|
||||
<>
|
||||
<Title>이용자 제재 조회 및 등록</Title>
|
||||
<FormWrapper>
|
||||
<UserBlockSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
|
||||
</FormWrapper>
|
||||
<TableInfo>
|
||||
<ListCount>
|
||||
총 : {dataList && dataList.total} 건 / {dataList && dataList.total_all} 건
|
||||
</ListCount>
|
||||
<ListOption>
|
||||
<SelectInput
|
||||
name=""
|
||||
id=""
|
||||
className="input-select"
|
||||
onChange={e => {
|
||||
handleOrderBy(e);
|
||||
}}>
|
||||
<option value="DESC">내림차순</option>
|
||||
<option value="ASC">오름차순</option>
|
||||
</SelectInput>
|
||||
<SelectInput
|
||||
name=""
|
||||
id=""
|
||||
onChange={e => {
|
||||
handlePageSize(e);
|
||||
}}
|
||||
className="input-select">
|
||||
<option value="50">50개</option>
|
||||
<option value="100">100개</option>
|
||||
</SelectInput>
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 30) && (
|
||||
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} type="submit" text="선택 삭제" id={selectedRow} handleClick={() => handleConfirmModal(selectedRow)} />
|
||||
)}
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 25) && (
|
||||
<Button
|
||||
theme="primary"
|
||||
type="submit"
|
||||
text="제재 등록"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
navigate('/servicemanage/userblock/userblockregist');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40">
|
||||
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
|
||||
</th>
|
||||
<th width="80">번호</th>
|
||||
<th width="20%">GUID</th>
|
||||
<th width="20%">아바타명</th>
|
||||
<th width="100">상태</th>
|
||||
<th width="100">제재 기간</th>
|
||||
<th width="250">제재 사유</th>
|
||||
<th width="150">등록자</th>
|
||||
<th width="110">상세정보</th>
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 30) && <th width="80">삭제</th>}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.list &&
|
||||
dataList.list.map(data => (
|
||||
<Fragment key={data.id}>
|
||||
<tr>
|
||||
<td>
|
||||
<CheckBox name={'select'} id={data.id} setData={e => handleSelectCheckBox(e)} />
|
||||
</td>
|
||||
<td>{data.row_num}</td>
|
||||
<td>{data.guid}</td>
|
||||
<td>{data.nickname}</td>
|
||||
{data.status === 'INPROGRESS' ? (
|
||||
<td>
|
||||
<ListState>{blockStatus.map(item => item.value === data.status && item.name)}</ListState>
|
||||
</td>
|
||||
) : (
|
||||
<td>{blockStatus.map(item => item.value === data.status && item.name)}</td>
|
||||
)}
|
||||
<td>{blockPeriod.map(item => item.value === data.period && item.name)}</td>
|
||||
<td>{blockSanctions.map(item => item.value === data.sanctions && item.name)}</td>
|
||||
<td>{data.create_by}</td>
|
||||
<td>
|
||||
<Button theme="line" text="상세보기" handleClick={() => handleDetail(data)} />
|
||||
</td>
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 30) && (
|
||||
<td>
|
||||
<Button theme="line" id={data.id} name="single" text="삭제" handleClick={() => handleConfirmModal(data)} />
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
|
||||
<UserBlockDetailModal stateModal={detailView} data={detail} handleModal={handleDetail} />
|
||||
<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}
|
||||
/>
|
||||
<Pagination
|
||||
postsPerPage={searchParams.pageSize}
|
||||
totalPosts={dataList?.total_all}
|
||||
setCurrentPage={handlePageChange}
|
||||
currentPage={searchParams.currentPage}
|
||||
pageLimit={INITIAL_PAGE_LIMIT}
|
||||
/>
|
||||
|
||||
{/* 선택 삭제 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModal}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleConfirmeModalClose} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">
|
||||
제재 대상을 삭제하시겠습니까?
|
||||
<br />
|
||||
삭제 시 제재가 해제됩니다.
|
||||
</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleConfirmeModalClose} />
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleDelete} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
|
||||
{/* 완료창 모달 */}
|
||||
|
||||
{/* 삭제 완료 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleCompleteModal} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
<UserBlockDetailModal
|
||||
stateModal={modalState.detailModal}
|
||||
data={detail}
|
||||
handleModal={() => handleModalClose('detail')}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserBlock;
|
||||
|
||||
const ListState = styled.span`
|
||||
color: #d60000;
|
||||
`;
|
||||
export default withAuth(authType.blackListRead)(UserBlock);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
import RadioInput from '../../components/common/input/Radio';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { Title, BtnWrapper, TextInput, SelectInput, DatePickerWrapper, ButtonClose, ModalText } from '../../styles/Components';
|
||||
import { Title, BtnWrapper, TextInput, SelectInput, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
import DatePickerComponent from '../../components/common/Date/DatePickerComponent';
|
||||
|
||||
import { authType, blockPeriod, blockSanctions, blockType, HourList, MinuteList } from '../../assets/data';
|
||||
@@ -17,19 +16,23 @@ import { getMonth, getYear, setHours } from 'date-fns';
|
||||
import range from 'lodash/range';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import UserBlockUploadBtn from '../../components/ServiceManage/UserBlockUploadBtn';
|
||||
import { BlackListRegist, BlackListMultipleUpload } from '../../apis';
|
||||
import { BlackListRegist} from '../../apis';
|
||||
|
||||
import { authList } from '../../store/authList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import AuthModal from '../../components/common/modal/AuthModal';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
|
||||
registerLocale('ko', ko);
|
||||
|
||||
const UserBlockRegist = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
const navigate = useNavigate();
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const {showModal, showToast} = useAlert();
|
||||
const {withLoading} = useLoading();
|
||||
|
||||
//시간 관련 변수
|
||||
const [sendHour, setSendHour] = useState('00');
|
||||
@@ -44,8 +47,6 @@ const UserBlockRegist = () => {
|
||||
const years = range(1990, getYear(new Date()) + 1, 1);
|
||||
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
|
||||
|
||||
const [registModalClose, setRegistModalClose] = useState('hidden');
|
||||
const [completeModalClose, setCompleteModalClose] = useState('hidden');
|
||||
const [isNullValue, setIsNullValue] = useState(false);
|
||||
|
||||
const [guidList, setGuidList] = useState([]);
|
||||
@@ -58,7 +59,6 @@ const UserBlockRegist = () => {
|
||||
start_dt: '',
|
||||
end_dt: '',
|
||||
});
|
||||
const [confirmText, setConfirmText] = useState('');
|
||||
|
||||
const handleChange = e => {
|
||||
setSelectData(e.target.value);
|
||||
@@ -76,12 +76,6 @@ const UserBlockRegist = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const [previewModal, setPreviewModal] = useState('hidden');
|
||||
const handlePreview = e => {
|
||||
e.preventDefault();
|
||||
previewModal === 'hidden' ? setPreviewModal('view') : setPreviewModal('hidden');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectData === 'single') {
|
||||
setSelectSingle(false);
|
||||
@@ -113,6 +107,14 @@ const UserBlockRegist = () => {
|
||||
}
|
||||
}, [selectData, guidList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (checkCondition()) {
|
||||
setIsNullValue(false);
|
||||
} else {
|
||||
setIsNullValue(true);
|
||||
}
|
||||
}, [resultData]);
|
||||
|
||||
// 제재 시간 세팅 로직
|
||||
const handleSendTime = e => {
|
||||
let sendDate = new Date(resultData.start_dt);
|
||||
@@ -212,57 +214,54 @@ const UserBlockRegist = () => {
|
||||
if (resultData.period === 'WARNING') setEndHour('01');
|
||||
};
|
||||
|
||||
// 등록 확인 모달
|
||||
let notNull = resultData.list.length > 0 && resultData.type && resultData.period && resultData.sanctions && resultData.start_dt && resultData.end_dt;
|
||||
const callbackPage = () => {
|
||||
navigate('/servicemanage/userblock');
|
||||
}
|
||||
|
||||
const handleRegistModalClose = () => {
|
||||
if(selectData === 'multi'){
|
||||
const guidChk = guidList.some(guid => guid.validate === false);
|
||||
if(guidChk) {
|
||||
alert("유효성 체크가 통과되지 않은 항목이 존재합니다. \r\n수정 후 재등록 해주세요.")
|
||||
return;
|
||||
}
|
||||
}
|
||||
const handleSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case 'submit':
|
||||
if (!checkCondition()) return;
|
||||
if(selectData === 'multi'){
|
||||
const guidChk = guidList.some(guid => guid.validate === false);
|
||||
if(guidChk) {
|
||||
showToast('USER_BLOCK_VALIDATION_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
};
|
||||
showModal('USER_BLOCK_REGIST_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('registConfirm'),
|
||||
});
|
||||
break;
|
||||
|
||||
if (notNull) {
|
||||
registModalClose === 'hidden' ? setRegistModalClose('view') : setRegistModalClose('hidden');
|
||||
setIsNullValue(false);
|
||||
} else {
|
||||
setIsNullValue(true);
|
||||
case 'registConfirm':
|
||||
await withLoading(async () => {
|
||||
return await BlackListRegist(token, resultData);
|
||||
}).then(data => {
|
||||
if(data.result === 'ERROR'){
|
||||
showToast(data.data.message, {type: alertTypes.error});
|
||||
}else{
|
||||
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||
}
|
||||
}).catch(error => {
|
||||
showToast('API_FAIL', { type: alertTypes.error });
|
||||
}).finally(() => {
|
||||
callbackPage();
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 등록 완료 모달
|
||||
const handleCompleteModalClose = () => {
|
||||
if (completeModalClose === 'hidden') {
|
||||
setCompleteModalClose('view');
|
||||
} else {
|
||||
setCompleteModalClose('hidden');
|
||||
handleReset();
|
||||
handleRegistModalClose();
|
||||
|
||||
navigate('/servicemanage/userblock');
|
||||
window.location.reload();
|
||||
}
|
||||
const checkCondition = () => {
|
||||
return resultData.list.length > 0 &&
|
||||
resultData.type &&
|
||||
resultData.period &&
|
||||
resultData.sanctions &&
|
||||
resultData.start_dt &&
|
||||
resultData.end_dt;
|
||||
};
|
||||
|
||||
// 등록 api 연동
|
||||
const handleRegist = async () => {
|
||||
const message = await BlackListRegist(token, resultData);
|
||||
|
||||
message.data.data.message === '등록 하였습니다.' ?
|
||||
setConfirmText('등록이 완료되었습니다.') :
|
||||
message.data.data.message === 'admindb_exit_error' ?
|
||||
setConfirmText("이미 등록된 유저입니다.") : setConfirmText(message.data.data.message);
|
||||
|
||||
handleRegistModalClose();
|
||||
handleCompleteModalClose();
|
||||
};
|
||||
|
||||
// console.log('DataList: ', resultData);
|
||||
// console.log('resultData.period :', resultData.period);
|
||||
// console.log(resultData.period.length);
|
||||
|
||||
return (
|
||||
<>
|
||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.blackListUpdate) ? (
|
||||
@@ -547,43 +546,23 @@ const UserBlockRegist = () => {
|
||||
|
||||
{isNullValue && <SearchBarAlert>설정값을 모두 입력해주세요.</SearchBarAlert>}
|
||||
<BtnWrapper $justify="flex-end" $gap="10px">
|
||||
<Button
|
||||
text="취소"
|
||||
theme="line"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
navigate('/servicemanage/userblock');
|
||||
}}
|
||||
/>
|
||||
<Button type="submit" text="등록" name="등록버튼" handleClick={handleRegistModalClose} theme={notNull ? 'primary' : 'disable'} />
|
||||
<BtnWrapper $justify="flex-end" $gap="10px">
|
||||
<Button
|
||||
text="취소"
|
||||
theme="line"
|
||||
handleClick={() => showModal('MAIL_REGIST_CANCEL', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => callbackPage(),
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
text="등록"
|
||||
theme={checkCondition() ? 'primary' : 'disable'}
|
||||
handleClick={() => handleSubmit('submit')}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</BtnWrapper>
|
||||
|
||||
{/* 일괄등록 모달창 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={registModalClose}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleRegistModalClose} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">
|
||||
이용자 제재 명단에
|
||||
<br />
|
||||
등록하시겠습니까?
|
||||
</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleRegistModalClose} />
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleRegist} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
|
||||
{/* 등록 완료 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModalClose}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleCompleteModalClose} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">{confirmText}</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModalClose} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -10,3 +10,5 @@ export { default as Event } from './Event';
|
||||
export { default as EventRegist } from './EventRegist';
|
||||
export { default as LandAuction} from './LandAuction'
|
||||
export { default as BattleEvent} from './BattleEvent'
|
||||
export { default as MenuBanner} from './MenuBanner'
|
||||
export { default as MenuBannerRegist} from './MenuBannerRegist'
|
||||
|
||||
@@ -10,14 +10,17 @@ import { Title, FormWrapper, BtnWrapper, TableStyle } from '../../styles/Compone
|
||||
import { GroupDetailViewList, GroupModify } from '../../apis';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import AuthModal from '../../components/common/modal/AuthModal';
|
||||
import DynamicModal from '../../components/common/modal/DynamicModal';
|
||||
import { AuthGroupRows } from '../../components/UserManage';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
|
||||
const AuthSettingUpdate = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const { showModal, showToast } = useAlert();
|
||||
const { withLoading } = useLoading();
|
||||
|
||||
const id = location.pathname.split('/')[3];
|
||||
const [msg, setMsg] = useState('');
|
||||
@@ -62,27 +65,15 @@ const AuthSettingUpdate = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleModalView = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'view',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalClose = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'hidden',
|
||||
}));
|
||||
const handleNavigate = () => {
|
||||
navigate('/usermanage/authsetting');
|
||||
}
|
||||
|
||||
const handleSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "cancelConfirm":
|
||||
setMsg(t('CANCEL_COMPLETED'));
|
||||
|
||||
handleModalClose('cancelConfirm');
|
||||
handleModalView('complete');
|
||||
showToast('CANCEL_COMPLETED', {type: alertTypes.success});
|
||||
handleNavigate();
|
||||
break;
|
||||
case "registConfirm":
|
||||
const token = sessionStorage.getItem('token');
|
||||
@@ -90,18 +81,16 @@ const AuthSettingUpdate = () => {
|
||||
.flat()
|
||||
.map(permissionId => ({ auth_id: permissionId }));
|
||||
|
||||
await GroupModify(token, id, resultList);
|
||||
await withLoading(async () => {
|
||||
return await GroupModify(token, id, resultList);
|
||||
}).then(data => {
|
||||
showToast('SAVE_COMPLETED', {type: alertTypes.success});
|
||||
}).catch(error => {
|
||||
|
||||
setMsg(t('SAVE_COMPLETED'));
|
||||
}).finally(() => {
|
||||
handleNavigate();
|
||||
});
|
||||
|
||||
handleModalClose('registConfirm');
|
||||
handleModalView('complete');
|
||||
break;
|
||||
case "complete":
|
||||
handleModalClose('complete');
|
||||
setMsg('');
|
||||
navigate('/usermanage/authsetting');
|
||||
// window.location.reload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -140,40 +129,25 @@ const AuthSettingUpdate = () => {
|
||||
<BtnWrapper $justify="flex-end" $gap="5px">
|
||||
<Button
|
||||
theme="line"
|
||||
handleClick={() => handleModalView('cancelConfirm')}
|
||||
handleClick={() =>
|
||||
showModal('CANCEL_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('cancelConfirm')
|
||||
})
|
||||
}
|
||||
text="취소"
|
||||
/>
|
||||
<Button
|
||||
theme="primary"
|
||||
handleClick={() => handleModalView('registConfirm')}
|
||||
handleClick={() => showModal('SAVE_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('registConfirm')
|
||||
})}
|
||||
text="저장"
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</FormWrapper>
|
||||
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.cancelConfirmModal}
|
||||
modalText={t('CANCEL_CONFIRM')}
|
||||
handleSubmit={() => handleSubmit('cancelConfirm')}
|
||||
handleCancel={() => handleModalClose('cancelConfirm')}
|
||||
/>
|
||||
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.registConfirmModal}
|
||||
modalText={t('SAVE_CONFIRM')}
|
||||
handleSubmit={() => handleSubmit('registConfirm')}
|
||||
handleCancel={() => handleModalClose('registConfirm')}
|
||||
/>
|
||||
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.completeModal}
|
||||
modalText={msg}
|
||||
handleSubmit={() => handleSubmit('complete')}
|
||||
/>
|
||||
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, Fragment, useRef } from 'react';
|
||||
import { useState, Fragment, useRef } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
@@ -7,7 +7,6 @@ import { authList } from '../../store/authList';
|
||||
import {
|
||||
authType,
|
||||
commonStatus,
|
||||
modalTypes,
|
||||
ViewTitleCountType,
|
||||
caliumStatus
|
||||
} from '../../assets/data';
|
||||
@@ -17,159 +16,79 @@ import {
|
||||
ChargeBtn,
|
||||
StatusLabel,
|
||||
} from '../../styles/ModuleComponents';
|
||||
import {Button, ExcelDownButton, Pagination, DynamicModal, ViewTableInfo, Loading} from '../../components/common';
|
||||
import {Button, ExcelDownButton, Pagination, ViewTableInfo} from '../../components/common';
|
||||
import { convertKTC, truncateText } from '../../utils';
|
||||
import { CaliumRequestRegistModal } from '../../components/UserManage';
|
||||
import { CaliumCharge, CaliumRequestView } from '../../apis';
|
||||
import { withAuth } from '../../hooks/hook';
|
||||
import { convertEndDateToISO, convertStartDateToISO } from '../../utils/date';
|
||||
import {CaliumRequestSearchBar} from '../../components/ServiceManage';
|
||||
import { CaliumCharge } from '../../apis';
|
||||
import { useModal, withAuth } from '../../hooks/hook';
|
||||
import { CommonSearchBar, useCommonSearch } from '../../components/ServiceManage';
|
||||
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
|
||||
const CaliumRequest = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userInfo = useRecoilValue(authList);
|
||||
const { t } = useTranslation();
|
||||
const { showModal, showToast } = useAlert();
|
||||
const {withLoading} = useLoading();
|
||||
|
||||
const tableRef = useRef(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(50);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [detailData, setDetailData] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
const [selectCharge, setSelectCharge] = useState({});
|
||||
|
||||
const [searchData, setSearchData] = useState({
|
||||
content: '',
|
||||
status: 'ALL',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
});
|
||||
const [orderBy, setOrderBy] = useState('DESC');
|
||||
const [modalState, setModalState] = useState({
|
||||
detailModal: 'hidden',
|
||||
registerModal: 'hidden',
|
||||
chargedCompleteModal: 'hidden',
|
||||
chargedConfirmModal: 'hidden'
|
||||
const {
|
||||
modalState,
|
||||
handleModalView,
|
||||
handleModalClose
|
||||
} = useModal({
|
||||
register: 'hidden',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetchData('', 'ALL', '', '');
|
||||
}, [currentPage]);
|
||||
|
||||
// 리스트 조회
|
||||
const fetchData = async (content, status, startDate, endDate, order, size) => {
|
||||
setDataList(
|
||||
await CaliumRequestView(
|
||||
token,
|
||||
content,
|
||||
status,
|
||||
startDate && convertStartDateToISO(startDate),
|
||||
endDate && convertEndDateToISO(endDate),
|
||||
order ? order : orderBy,
|
||||
size ? size : pageSize,
|
||||
currentPage,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (content, status, startDate, endDate) => {
|
||||
fetchData(content, status, startDate, endDate);
|
||||
};
|
||||
|
||||
// 오름차순 내림차순
|
||||
const handleOrderBy = e => {
|
||||
const order = e.target.value;
|
||||
|
||||
setOrderBy(order);
|
||||
fetchData(
|
||||
searchData.content,
|
||||
searchData.status,
|
||||
searchData.startDate,
|
||||
searchData.endDate,
|
||||
order,
|
||||
pageSize,
|
||||
);
|
||||
};
|
||||
|
||||
const handlePageSize = e => {
|
||||
const size = e.target.value;
|
||||
setPageSize(size);
|
||||
setCurrentPage(1);
|
||||
|
||||
fetchData(
|
||||
searchData.content,
|
||||
searchData.status,
|
||||
searchData.startDate,
|
||||
searchData.endDate,
|
||||
orderBy,
|
||||
size,
|
||||
1,
|
||||
);
|
||||
};
|
||||
|
||||
// 상세보기 호출
|
||||
// const handleDetailModal = async (e, id) => {
|
||||
// await EventDetailView(token, id).then(data => {
|
||||
// setDetailData(data);
|
||||
// });
|
||||
//
|
||||
// e.preventDefault();
|
||||
// handleModalView('detail');
|
||||
// };
|
||||
|
||||
const handleModalView = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'view',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalClose = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'hidden',
|
||||
}));
|
||||
}
|
||||
const {
|
||||
config,
|
||||
searchParams,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleOrderByChange,
|
||||
updateSearchParams,
|
||||
configLoaded
|
||||
} = useCommonSearch(token, "caliumRequestSearch");
|
||||
|
||||
const handleSubmit = async (type, param = null) => {
|
||||
switch (type) {
|
||||
case "detail":
|
||||
setDetailData(param);
|
||||
handleModalView('detail');
|
||||
showModal(param, {
|
||||
type: alertTypes.info
|
||||
});
|
||||
break;
|
||||
case "chargedConfirm":
|
||||
setSelectCharge({id: param.id, count: param.count});
|
||||
handleModalView('chargedConfirm');
|
||||
break;
|
||||
case "charged":
|
||||
handleModalClose('chargedConfirm');
|
||||
setLoading(true);
|
||||
await CaliumCharge(token, selectCharge).then(data => {
|
||||
setLoading(false);
|
||||
if(data.result === "SUCCESS") {
|
||||
setAlertMsg(t('CHARGE_COMPLTED'));
|
||||
handleModalView('chargedComplete');
|
||||
}else if(data.result === "ERROR_CALIUM_FINISH") {
|
||||
setAlertMsg(t('CHARGE_FINISH_FAIL'));
|
||||
handleModalView('chargedComplete');
|
||||
}else{
|
||||
setAlertMsg(t('CHARGE_FAIL'));
|
||||
handleModalView('chargedComplete');
|
||||
}
|
||||
setSelectCharge({});
|
||||
showModal('CALIUM_CHARGE_CONFIRM', {
|
||||
type: alertTypes.confirm,
|
||||
onConfirm: () => handleSubmit('charged')
|
||||
});
|
||||
break;
|
||||
case "chargedComplete":
|
||||
handleModalClose('chargedComplete');
|
||||
await fetchData(
|
||||
searchData.content,
|
||||
searchData.status,
|
||||
searchData.startDate,
|
||||
searchData.endDate
|
||||
);
|
||||
case "charged":
|
||||
await withLoading( async () => {
|
||||
return await CaliumCharge(token, selectCharge);
|
||||
}).then(data => {
|
||||
if(data.result === "SUCCESS") {
|
||||
showToast('CHARGE_COMPLTED', {type: alertTypes.success});
|
||||
}else if(data.result === "ERROR_CALIUM_FINISH") {
|
||||
showToast('CHARGE_FINISH_FAIL', {type: alertTypes.error});
|
||||
}else{
|
||||
showToast('CHARGE_FAIL', {type: alertTypes.error});
|
||||
}
|
||||
}).catch(error => {
|
||||
showToast('API_FAIL', {type: alertTypes.error});
|
||||
}).finally(() =>{
|
||||
setSelectCharge({});
|
||||
handleSearch(updateSearchParams);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -178,9 +97,20 @@ const CaliumRequest = () => {
|
||||
<>
|
||||
<Title>칼리움 사용 수량 요청</Title>
|
||||
<FormWrapper>
|
||||
<CaliumRequestSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
|
||||
<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={handleOrderBy} handlePageSize={handlePageSize} countType={ViewTitleCountType.calium}>
|
||||
<ViewTableInfo total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange} countType={ViewTitleCountType.calium}>
|
||||
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_CALIUM_REQUEST')} />
|
||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
|
||||
<Button
|
||||
@@ -210,8 +140,7 @@ const CaliumRequest = () => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.list &&
|
||||
dataList.list.map(calium => (
|
||||
{dataList?.list?.map(calium => (
|
||||
<Fragment key={calium.row_num}>
|
||||
<tr>
|
||||
{/*<td>{calium.row_num}</td>*/}
|
||||
@@ -238,39 +167,23 @@ const CaliumRequest = () => {
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
|
||||
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
|
||||
<Pagination
|
||||
postsPerPage={searchParams.pageSize}
|
||||
totalPosts={dataList?.total_all}
|
||||
setCurrentPage={handlePageChange}
|
||||
currentPage={searchParams.currentPage}
|
||||
pageLimit={INITIAL_PAGE_LIMIT}
|
||||
/>
|
||||
|
||||
{/*상세*/}
|
||||
{/*<EventDetailModal detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData}/>*/}
|
||||
<CaliumRequestRegistModal
|
||||
registView={modalState.registerModal}
|
||||
setRegistView={() => {
|
||||
handleModalClose('register');
|
||||
handleSearch(updateSearchParams);
|
||||
}}
|
||||
userInfo={userInfo}
|
||||
/>
|
||||
|
||||
<CaliumRequestRegistModal registView={modalState.registerModal} setRegistView={() => handleModalClose('register')} userInfo={userInfo} />
|
||||
|
||||
<DynamicModal
|
||||
modalType={modalTypes.confirmOkCancel}
|
||||
view={modalState.chargedConfirmModal}
|
||||
handleCancel={() => handleModalClose('chargedConfirm')}
|
||||
handleSubmit={() => handleSubmit('charged')}
|
||||
modalText={t('CALIUM_CHARGE_CONFIRM')}
|
||||
/>
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.chargedCompleteModal}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('chargedComplete')}
|
||||
/>
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.chargedCompleteModal}
|
||||
modalText={alertMsg}
|
||||
handleSubmit={() => handleSubmit('chargedComplete')}
|
||||
/>
|
||||
<DynamicModal
|
||||
modalType={modalTypes.completed}
|
||||
view={modalState.detailModal}
|
||||
modalText={detailData}
|
||||
handleSubmit={() => handleModalClose('detail')}
|
||||
/>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -618,4 +618,5 @@ export const TableActionButton = styled.button`
|
||||
&:hover {
|
||||
background: #3a70bc;
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
|
||||
@@ -614,6 +614,10 @@ export const FormInput = styled.input`
|
||||
color: ${props => props.color || '#cccccc'};
|
||||
background: ${props => props.background_color || '#f6f6f6'};
|
||||
}
|
||||
|
||||
${props => props.suffix && `
|
||||
padding-right: 60px; // 라벨 영역을 위한 여백 확보
|
||||
`}
|
||||
`;
|
||||
|
||||
export const FormRowInput = styled.input`
|
||||
@@ -691,6 +695,30 @@ export const FormTextAreaWrapper = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
export const FormInputSuffixWrapper = styled.div`
|
||||
position: relative;
|
||||
width: ${props => props.width || '100%'};
|
||||
display: inline-block;
|
||||
`;
|
||||
|
||||
export const FormInputSuffix = styled.div`
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f1f5f9;
|
||||
color: #475569;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 0 12px;
|
||||
border-radius: 0 8px 8px 0;
|
||||
border-left: 1px solid #e2e8f0;
|
||||
`;
|
||||
|
||||
export const TitleItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -740,6 +768,7 @@ export const StatusWapper = styled.td`
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const ExcelDownButton = styled.button`
|
||||
|
||||
Reference in New Issue
Block a user