event > rewardEvent 변경
월드이벤트(event) 추가
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
//운영서비스 관리 - 이벤트 api 연결
|
//운영서비스 관리 - 통합 이벤트 api 연결
|
||||||
|
|
||||||
import { Axios } from '../utils';
|
import { Axios } from '../utils';
|
||||||
|
|
||||||
// 이벤트 리스트 조회
|
// 이벤트 리스트 조회
|
||||||
export const EventView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
|
export const EventView = async (token, searchData, status, startDate, endDate, order, size, currentPage) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.get(
|
const res = await Axios.get(
|
||||||
`/api/v1/event/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
|
`/api/v1/world-event/list?search_data=${searchData}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
|
||||||
&page_size=${size}`,
|
&orderby=${order}&page_no=${currentPage}&page_size=${size}`,
|
||||||
{
|
{
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
},
|
},
|
||||||
@@ -24,11 +24,11 @@ export const EventView = async (token, title, content, status, startDate, endDat
|
|||||||
// 이벤트 상세보기
|
// 이벤트 상세보기
|
||||||
export const EventDetailView = async (token, id) => {
|
export const EventDetailView = async (token, id) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.get(`/api/v1/event/detail/${id}`, {
|
const res = await Axios.get(`/api/v1/world-event/detail/${id}`, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.data.data.detail;
|
return res.data.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('EventDetailView Error', e);
|
throw new Error('EventDetailView Error', e);
|
||||||
@@ -39,11 +39,11 @@ export const EventDetailView = async (token, id) => {
|
|||||||
// 이벤트 등록
|
// 이벤트 등록
|
||||||
export const EventSingleRegist = async (token, params) => {
|
export const EventSingleRegist = async (token, params) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.post(`/api/v1/event`, params, {
|
const res = await Axios.post(`/api/v1/world-event`, params, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('EventSingleRegist Error', e);
|
throw new Error('EventSingleRegist Error', e);
|
||||||
@@ -51,10 +51,10 @@ export const EventSingleRegist = async (token, params) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 우편 수정
|
// 이벤트 수정
|
||||||
export const EventModify = async (token, id, params) => {
|
export const EventModify = async (token, id, params) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.put(`/api/v1/event/${id}`, params, {
|
const res = await Axios.put(`/api/v1/world-event/${id}`, params, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,15 +66,14 @@ export const EventModify = async (token, id, params) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 우편 삭제
|
// 이벤트 삭제
|
||||||
export const EventDelete = async (token, params, id) => {
|
export const EventDelete = async (token, id) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.delete(`/api/v1/event/delete`, {
|
const res = await Axios.delete(`/api/v1/world-event/delete?id=${id}`, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
data: { list: params },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.data.data.list;
|
return res.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('EventDelete Error', e);
|
throw new Error('EventDelete Error', e);
|
||||||
@@ -82,17 +81,20 @@ export const EventDelete = async (token, params, id) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 이벤트 우편 아이템 확인
|
// 이벤트 메타데이터 조회
|
||||||
export const EventIsItem = async (token, params) => {
|
export const EventActionView = async (token) => {
|
||||||
try {
|
try {
|
||||||
const res = await Axios.post(`/api/v1/event/item`, params, {
|
const res = await Axios.get(
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
`/api/v1/dictionary/event-action/list`,
|
||||||
});
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return res;
|
return res.data.data.event_action_list;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
throw new Error('EventIsItem Error', e);
|
throw new Error('EventActionView Error', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
98
src/apis/RewardEvent.js
Normal file
98
src/apis/RewardEvent.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
//운영서비스 관리 - 이벤트 api 연결
|
||||||
|
|
||||||
|
import { Axios } from '../utils';
|
||||||
|
|
||||||
|
// 이벤트 리스트 조회
|
||||||
|
export const RewardEventView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(
|
||||||
|
`/api/v1/event/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
|
||||||
|
&orderby=${order}&page_no=${currentPage}&page_size=${size}`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.data.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventView Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이벤트 상세보기
|
||||||
|
export const RewardEventDetailView = async (token, id) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.get(`/api/v1/event/detail/${id}`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data.data.detail;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventDetailView Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이벤트 등록
|
||||||
|
export const RewardEventSingleRegist = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post(`/api/v1/event`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventSingleRegist Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 우편 수정
|
||||||
|
export const RewardEventModify = async (token, id, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.put(`/api/v1/event/${id}`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventModify Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 우편 삭제
|
||||||
|
export const RewardEventDelete = async (token, params, id) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.delete(`/api/v1/event/delete`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
data: { list: params },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data.data.list;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('RewardEventDelete Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이벤트 우편 아이템 확인
|
||||||
|
export const EventIsItem = async (token, params) => {
|
||||||
|
try {
|
||||||
|
const res = await Axios.post(`/api/v1/event/item`, params, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error('EventIsItem Error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
18
src/assets/data/apis/eventAPI.json
Normal file
18
src/assets/data/apis/eventAPI.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"baseUrl": "/api/v1/world-event",
|
||||||
|
"endpoints": {
|
||||||
|
"EventView": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "/list",
|
||||||
|
"dataPath": "data",
|
||||||
|
"paramFormat": "query"
|
||||||
|
},
|
||||||
|
"EventDetailView": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "/detail/:id",
|
||||||
|
"dataPath": "data.data",
|
||||||
|
"paramFormat": "query",
|
||||||
|
"paramMapping": ["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"initialSearchParams": {
|
"initialSearchParams": {
|
||||||
"searchTitle": "",
|
"searchData": "",
|
||||||
"searchContent": "",
|
|
||||||
"status": "ALL",
|
"status": "ALL",
|
||||||
"startDate": "",
|
"startDate": "",
|
||||||
"endDate": "",
|
"endDate": "",
|
||||||
@@ -13,51 +12,37 @@
|
|||||||
"searchFields": [
|
"searchFields": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"id": "searchTitle",
|
"id": "searchData",
|
||||||
"label": "우편 제목",
|
"label": "제목",
|
||||||
"placeholder": "제목 입력",
|
"placeholder": "제목 입력",
|
||||||
"width": "300px",
|
"width": "300px",
|
||||||
"col": 1
|
"col": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "period",
|
|
||||||
"startDateId": "startDate",
|
|
||||||
"endDateId": "endDate",
|
|
||||||
"label": "조회 일자",
|
|
||||||
"col": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"id": "searchContent",
|
|
||||||
"label": "우편 내용",
|
|
||||||
"placeholder": "우편 내용(공백으로 구분)",
|
|
||||||
"width": "300px",
|
|
||||||
"col": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"id": "status",
|
"id": "status",
|
||||||
"label": "이벤트 상태",
|
"label": "상태",
|
||||||
"optionsRef": "eventStatus",
|
"optionsRef": "opMenuBannerStatus",
|
||||||
"col": 2
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "period",
|
||||||
|
"startDateId": "startDate",
|
||||||
|
"endDateId": "endDate",
|
||||||
|
"label": "기간",
|
||||||
|
"col": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"apiInfo": {
|
"apiInfo": {
|
||||||
"functionName": "EventView",
|
"endpointName": "EventView",
|
||||||
"loadOnMount": true,
|
"loadOnMount": true,
|
||||||
"paramsMapping": [
|
"paramTransforms": [
|
||||||
"searchTitle",
|
|
||||||
"searchContent",
|
|
||||||
"status",
|
|
||||||
{"param": "startDate", "transform": "toISOString"},
|
{"param": "startDate", "transform": "toISOString"},
|
||||||
{"param": "endDate", "transform": "toISOString"},
|
{"param": "endDate", "transform": "toISOString"}
|
||||||
"orderBy",
|
|
||||||
"pageSize",
|
|
||||||
"currentPage"
|
|
||||||
],
|
],
|
||||||
"pageField": "currentPage",
|
"pageField": "page_no",
|
||||||
"pageSizeField": "pageSize",
|
"pageSizeField": "page_size",
|
||||||
"orderField": "orderBy"
|
"orderField": "orderBy"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
119
src/assets/data/pages/eventTable.json
Normal file
119
src/assets/data/pages/eventTable.json
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"id": "eventTable",
|
||||||
|
"selection": {
|
||||||
|
"type": "single",
|
||||||
|
"idField": "id"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"countType": "total",
|
||||||
|
"orderType": "desc",
|
||||||
|
"pageType": "default",
|
||||||
|
"buttons": [
|
||||||
|
{
|
||||||
|
"id": "delete",
|
||||||
|
"text": "선택 삭제",
|
||||||
|
"theme": "line",
|
||||||
|
"disableWhen": "noSelection",
|
||||||
|
"requiredAuth": "worldEventDelete",
|
||||||
|
"action": "delete"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "register",
|
||||||
|
"text": "통합 이벤트 등록",
|
||||||
|
"theme": "primary",
|
||||||
|
"requiredAuth": "worldEventUpdate",
|
||||||
|
"action": "regist"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"id": "checkbox",
|
||||||
|
"type": "checkbox",
|
||||||
|
"width": "40px",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "row_num",
|
||||||
|
"type": "text",
|
||||||
|
"width": "70px",
|
||||||
|
"title": "번호"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "status",
|
||||||
|
"type": "status",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "상태",
|
||||||
|
"option_name": "opCommonStatus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"type": "text",
|
||||||
|
"title": "제목",
|
||||||
|
"width": "150px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "personal_event_action_id",
|
||||||
|
"type": "text",
|
||||||
|
"width": "150px",
|
||||||
|
"title": "개인제작 이벤트 모드"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "global_event_action_id",
|
||||||
|
"type": "text",
|
||||||
|
"width": "150px",
|
||||||
|
"title": "기여도 이벤트 모드"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "max_point",
|
||||||
|
"type": "text",
|
||||||
|
"width": "150px",
|
||||||
|
"title": "기여도 목표점수"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "start_dt",
|
||||||
|
"type": "date",
|
||||||
|
"width": "220px",
|
||||||
|
"title": "시작일(KST)",
|
||||||
|
"format": {
|
||||||
|
"type": "function",
|
||||||
|
"name": "convertKTC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "end_dt",
|
||||||
|
"type": "date",
|
||||||
|
"width": "220px",
|
||||||
|
"title": "종료일(KST)",
|
||||||
|
"format": {
|
||||||
|
"type": "function",
|
||||||
|
"name": "convertKTC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "detail",
|
||||||
|
"type": "button",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "상세보기",
|
||||||
|
"text": "상세보기",
|
||||||
|
"action": {
|
||||||
|
"type": "modal",
|
||||||
|
"target": "detailModal",
|
||||||
|
"dataParam": {
|
||||||
|
"id": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "history",
|
||||||
|
"type": "button",
|
||||||
|
"width": "120px",
|
||||||
|
"title": "히스토리",
|
||||||
|
"text": "히스토리"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort": {
|
||||||
|
"defaultColumn": "row_num",
|
||||||
|
"defaultDirection": "desc"
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/assets/data/pages/rewardEventSearch.json
Normal file
63
src/assets/data/pages/rewardEventSearch.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"initialSearchParams": {
|
||||||
|
"searchTitle": "",
|
||||||
|
"searchContent": "",
|
||||||
|
"status": "ALL",
|
||||||
|
"startDate": "",
|
||||||
|
"endDate": "",
|
||||||
|
"orderBy": "DESC",
|
||||||
|
"pageSize": 50,
|
||||||
|
"currentPage": 1
|
||||||
|
},
|
||||||
|
|
||||||
|
"searchFields": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "searchTitle",
|
||||||
|
"label": "우편 제목",
|
||||||
|
"placeholder": "제목 입력",
|
||||||
|
"width": "300px",
|
||||||
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "period",
|
||||||
|
"startDateId": "startDate",
|
||||||
|
"endDateId": "endDate",
|
||||||
|
"label": "조회 일자",
|
||||||
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "searchContent",
|
||||||
|
"label": "우편 내용",
|
||||||
|
"placeholder": "우편 내용(공백으로 구분)",
|
||||||
|
"width": "300px",
|
||||||
|
"col": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"id": "status",
|
||||||
|
"label": "이벤트 상태",
|
||||||
|
"optionsRef": "eventStatus",
|
||||||
|
"col": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"apiInfo": {
|
||||||
|
"functionName": "RewardEventView",
|
||||||
|
"loadOnMount": true,
|
||||||
|
"paramsMapping": [
|
||||||
|
"searchTitle",
|
||||||
|
"searchContent",
|
||||||
|
"status",
|
||||||
|
{"param": "startDate", "transform": "toISOString"},
|
||||||
|
{"param": "endDate", "transform": "toISOString"},
|
||||||
|
"orderBy",
|
||||||
|
"pageSize",
|
||||||
|
"currentPage"
|
||||||
|
],
|
||||||
|
"pageField": "currentPage",
|
||||||
|
"pageSizeField": "pageSize",
|
||||||
|
"orderField": "orderBy"
|
||||||
|
}
|
||||||
|
}
|
||||||
311
src/components/modal/EventModal.js
Normal file
311
src/components/modal/EventModal.js
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
import React, { useState, Fragment, useEffect, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Button from '../common/button/Button';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
BtnWrapper,
|
||||||
|
SearchBarAlert,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FormStatusBar,
|
||||||
|
FormStatusLabel,
|
||||||
|
FormStatusWarning,
|
||||||
|
FormButtonContainer,
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import { DetailLayout, Modal} from '../common';
|
||||||
|
import { TYPE_MODIFY, TYPE_REGISTRY } from '../../assets/data/adminConstants';
|
||||||
|
import { convertKTCDate } from '../../utils';
|
||||||
|
import {
|
||||||
|
opCommonStatus,
|
||||||
|
} from '../../assets/data/options';
|
||||||
|
import { alertTypes, commonStatus } from '../../assets/data/types';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { EventModify, EventSingleRegist } from '../../apis';
|
||||||
|
|
||||||
|
const EventModal = ({ modalType, detailView, handleDetailView, content, setDetailData, eventActionData }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const { showToast, showModal } = useAlert();
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
|
||||||
|
const [isNullValue, setIsNullValue] = useState(false);
|
||||||
|
const [resultData, setResultData] = useState(initData);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(modalType === TYPE_MODIFY && content && Object.keys(content).length > 0){
|
||||||
|
setResultData({
|
||||||
|
id: content.id,
|
||||||
|
title: content.title,
|
||||||
|
global_event_action_id: content.global_event_action_id,
|
||||||
|
personal_event_action_id: content.personal_event_action_id,
|
||||||
|
status: content.status,
|
||||||
|
max_point: content.max_point,
|
||||||
|
start_dt: convertKTCDate(content.start_dt),
|
||||||
|
end_dt: convertKTCDate(content.end_dt)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [modalType, content]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (checkCondition()) {
|
||||||
|
setIsNullValue(false);
|
||||||
|
} else {
|
||||||
|
setIsNullValue(true);
|
||||||
|
}
|
||||||
|
}, [resultData]);
|
||||||
|
|
||||||
|
const opEventActionMode = useMemo(() => {
|
||||||
|
return eventActionData?.map(item => ({
|
||||||
|
value: item.id,
|
||||||
|
name: `${item.description}(${item.id})`
|
||||||
|
})) || [];
|
||||||
|
}, [eventActionData]);
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setDetailData({});
|
||||||
|
setResultData(initData);
|
||||||
|
handleDetailView();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
if (!checkCondition()) return;
|
||||||
|
|
||||||
|
const minAllowedTime = new Date(new Date().getTime() + 10 * 60000);
|
||||||
|
const startDt = resultData.start_dt;
|
||||||
|
const endDt = resultData.end_dt;
|
||||||
|
// if (modalType === TYPE_REGISTRY && startDt < minAllowedTime) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if(resultData.repeat_type !== 'NONE' && !isValidDayRange(startDt, endDt)) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //화면에 머물면서 상태는 안바꼈을 경우가 있기에 시작시간 지났을경우 차단
|
||||||
|
// if (modalType === TYPE_REGISTRY && startDt < new Date()) {
|
||||||
|
// showToast('BATTLE_EVENT_MODAL_START_DT_WARNING', {type: alertTypes.warning});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
showModal(isView('modify') ? 'BATTLE_EVENT_UPDATE_CONFIRM' : 'BATTLE_EVENT_REGIST_CONFIRM', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleSubmit('registConfirm')
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "registConfirm":
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
...resultData
|
||||||
|
};
|
||||||
|
|
||||||
|
if(isView('modify')){
|
||||||
|
await withLoading( async () => {
|
||||||
|
return await EventModify(token, content?.id, params);
|
||||||
|
}).then(data => {
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('UPDATE_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
handleReset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
await withLoading( async () => {
|
||||||
|
return await EventSingleRegist(token, params);
|
||||||
|
}).then(data => {
|
||||||
|
if(data.result === "SUCCESS") {
|
||||||
|
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('REGIST_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
handleReset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCondition = () => {
|
||||||
|
return (
|
||||||
|
resultData.start_dt !== ''
|
||||||
|
&& resultData.end_dt !== ''
|
||||||
|
&& resultData.title !== ''
|
||||||
|
&& resultData.global_event_action_id > 0
|
||||||
|
&& resultData.personal_event_action_id > 0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isView = (label) => {
|
||||||
|
switch (label) {
|
||||||
|
case "modify":
|
||||||
|
return modalType === TYPE_MODIFY && (content?.status === commonStatus.wait);
|
||||||
|
case "registry":
|
||||||
|
case "mode":
|
||||||
|
return modalType === TYPE_REGISTRY
|
||||||
|
case "start_dt":
|
||||||
|
case "end_dt":
|
||||||
|
case "max_point":
|
||||||
|
case "name":
|
||||||
|
return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY &&(content?.status === commonStatus.wait));
|
||||||
|
default:
|
||||||
|
return modalType === TYPE_MODIFY && (content?.status !== commonStatus.wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemGroups = [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
row: 0,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'text',
|
||||||
|
key: 'title',
|
||||||
|
label: '이벤트명',
|
||||||
|
disabled: !isView('name'),
|
||||||
|
width: '300px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 1,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'date',
|
||||||
|
key: 'start_dt',
|
||||||
|
label: '시작일시',
|
||||||
|
disabled: !isView('start_dt'),
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
width: '200px',
|
||||||
|
showTime: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 1,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'date',
|
||||||
|
key: 'end_dt',
|
||||||
|
label: '종료일시',
|
||||||
|
disabled: !isView('end_dt'),
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
width: '200px',
|
||||||
|
showTime: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 4,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'select',
|
||||||
|
key: 'global_event_action_id',
|
||||||
|
label: '기여도 이벤트 모드',
|
||||||
|
disabled: !isView('mode'),
|
||||||
|
width: '150px',
|
||||||
|
options: opEventActionMode
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 4,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'number',
|
||||||
|
key: 'max_point',
|
||||||
|
label: '기여도 목표점수',
|
||||||
|
disabled: !isView('max_point'),
|
||||||
|
width: '150px'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 5,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'select',
|
||||||
|
key: 'personal_event_action_id',
|
||||||
|
label: '개인제작 이벤트 모드',
|
||||||
|
disabled: !isView('mode'),
|
||||||
|
width: '150px',
|
||||||
|
options: opEventActionMode
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal min="760px" $view={detailView}>
|
||||||
|
<Title $align="center">{isView('registry') ? "통합 이벤트 등록" : isView('modify') ? "통합 이벤트 수정" : "통합 이벤트 상세"}</Title>
|
||||||
|
<DetailLayout
|
||||||
|
itemGroups={itemGroups}
|
||||||
|
formData={resultData}
|
||||||
|
onChange={setResultData}
|
||||||
|
disabled={false}
|
||||||
|
columnCount={4}
|
||||||
|
/>
|
||||||
|
{!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>}
|
||||||
|
<BtnWrapper $gap="10px" $marginTop="10px">
|
||||||
|
<FormStatusBar>
|
||||||
|
<FormStatusLabel>
|
||||||
|
현재상태: {opCommonStatus.find(data => data.value === content?.status)?.name || "등록"}
|
||||||
|
</FormStatusLabel>
|
||||||
|
<FormStatusWarning>
|
||||||
|
{isView('registry') ? '' : t('EVENT_MODAL_STATUS_WARNING')}
|
||||||
|
</FormStatusWarning>
|
||||||
|
</FormStatusBar>
|
||||||
|
<FormButtonContainer $gap="5px">
|
||||||
|
{isView() ?
|
||||||
|
<Button
|
||||||
|
text="확인"
|
||||||
|
name="확인버튼"
|
||||||
|
theme="line"
|
||||||
|
handleClick={() => handleReset()}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
text="취소"
|
||||||
|
theme="line"
|
||||||
|
handleClick={() => showModal('CANCEL_CONFIRM', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleReset()
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text={isView('modify') ? "수정" : "등록"}
|
||||||
|
name="등록버튼"
|
||||||
|
theme={
|
||||||
|
checkCondition()
|
||||||
|
? 'primary'
|
||||||
|
: 'disable'
|
||||||
|
}
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</FormButtonContainer>
|
||||||
|
</BtnWrapper>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initData = {
|
||||||
|
title: '',
|
||||||
|
start_dt: '',
|
||||||
|
end_dt: '',
|
||||||
|
global_event_action_id: '',
|
||||||
|
personal_event_action_id: '',
|
||||||
|
max_point: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EventModal;
|
||||||
|
|
||||||
540
src/components/modal/RewardEventDetailModal.js
Normal file
540
src/components/modal/RewardEventDetailModal.js
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
import { useState, useEffect, Fragment } from 'react';
|
||||||
|
|
||||||
|
import { Input, Button as AntButton, Select, Alert, Space, Card, Row, Col } from 'antd';
|
||||||
|
import { Title, BtnWrapper } from '../../styles/Components';
|
||||||
|
import Button from '../common/button/Button';
|
||||||
|
import Modal from '../common/modal/Modal';
|
||||||
|
import { EventIsItem, RewardEventModify } from '../../apis';
|
||||||
|
|
||||||
|
import { authList } from '../../store/authList';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { authType, benItems, commonStatus, currencyItemCode } from '../../assets/data';
|
||||||
|
import {
|
||||||
|
DetailRegistInfo, DetailState
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import { convertKTC, timeDiffMinute, convertKTCDate } from '../../utils';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import { DetailLayout } from '../common';
|
||||||
|
|
||||||
|
const RewardEventDetailModal = ({ detailView, handleDetailView, content, setDetailData }) => {
|
||||||
|
const userInfo = useRecoilValue(authList);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
const {showModal, showToast} = useAlert();
|
||||||
|
|
||||||
|
const id = content && content.id;
|
||||||
|
const updateAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate);
|
||||||
|
|
||||||
|
const [activeLanguage, setActiveLanguage] = useState('KO');
|
||||||
|
const [item, setItem] = useState('');
|
||||||
|
const [itemCount, setItemCount] = useState(1);
|
||||||
|
const [resource, setResource] = useState('19010001');
|
||||||
|
const [resourceCount, setResourceCount] = useState(1);
|
||||||
|
|
||||||
|
const [resultData, setResultData] = useState({});
|
||||||
|
|
||||||
|
// 과거 판단
|
||||||
|
const [isPast, setIsPast] = useState(false);
|
||||||
|
const [isChanged, setIsChanged] = useState(false);
|
||||||
|
|
||||||
|
const [btnValidation, setBtnValidation] = useState(false);
|
||||||
|
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||||
|
const [itemCheckMsg, setItemCheckMsg] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(content){
|
||||||
|
const start_dt_KTC = convertKTCDate(content.start_dt)
|
||||||
|
const end_dt_KTC = convertKTCDate(content.end_dt)
|
||||||
|
|
||||||
|
setResultData({
|
||||||
|
start_dt: start_dt_KTC,
|
||||||
|
end_dt: end_dt_KTC,
|
||||||
|
event_type: content.event_type,
|
||||||
|
mail_list: content.mail_list,
|
||||||
|
item_list: content.item_list,
|
||||||
|
status: content.status,
|
||||||
|
delete_desc: content.delete_desc
|
||||||
|
});
|
||||||
|
|
||||||
|
start_dt_KTC < (new Date) ? setIsPast(true) : setIsPast(false);
|
||||||
|
content.mail_list.length === 1 && setBtnValidation(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem('');
|
||||||
|
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(!updateAuth || isPast){
|
||||||
|
setIsReadOnly(true);
|
||||||
|
}else{
|
||||||
|
setIsReadOnly(false);
|
||||||
|
}
|
||||||
|
}, [updateAuth, isPast]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setItemCheckMsg('');
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
|
const getLanguageTabItems = () => {
|
||||||
|
return resultData.mail_list?.map(mail => ({
|
||||||
|
key: mail.language,
|
||||||
|
label: mail.language,
|
||||||
|
children: (
|
||||||
|
<div style={{ padding: '10px', minHeight: '400px', height: 'auto' }}>
|
||||||
|
<Row gutter={[16, 24]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<div>
|
||||||
|
<label style={{
|
||||||
|
display: 'block',
|
||||||
|
marginBottom: '8px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'rgba(0, 0, 0, 0.85)',
|
||||||
|
fontSize: '14px'
|
||||||
|
}}>
|
||||||
|
제목 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
value={mail.title || ''}
|
||||||
|
placeholder="우편 제목을 입력하세요"
|
||||||
|
maxLength={30}
|
||||||
|
readOnly={isReadOnly}
|
||||||
|
onChange={(e) => updateMailData(mail.language, 'title', e.target.value.trimStart())}
|
||||||
|
showCount
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<div>
|
||||||
|
<label style={{
|
||||||
|
display: 'block',
|
||||||
|
marginBottom: '8px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'rgba(0, 0, 0, 0.85)',
|
||||||
|
fontSize: '14px'
|
||||||
|
}}>
|
||||||
|
내용 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||||
|
</label>
|
||||||
|
<Input.TextArea
|
||||||
|
value={mail.content || ''}
|
||||||
|
placeholder="우편 내용을 입력하세요"
|
||||||
|
readOnly={isReadOnly}
|
||||||
|
rows={8}
|
||||||
|
maxLength={2000}
|
||||||
|
showCount
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.value.length > 2000) return;
|
||||||
|
updateMailData(mail.language, 'content', e.target.value.trimStart());
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
resize: 'vertical',
|
||||||
|
minHeight: '200px',
|
||||||
|
maxHeight: '400px'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
closable: resultData.mail_list?.length > 1 && !isReadOnly, // 마지막 하나가 아니고 읽기전용이 아닐 때만 삭제 가능
|
||||||
|
})) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateMailData = (language, field, value) => {
|
||||||
|
const updatedMailList = resultData.mail_list.map(mail =>
|
||||||
|
mail.language === language
|
||||||
|
? { ...mail, [field]: value }
|
||||||
|
: mail
|
||||||
|
);
|
||||||
|
setResultData({ ...resultData, mail_list: updatedMailList });
|
||||||
|
setIsChanged(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTabClose = (targetKey) => {
|
||||||
|
if (resultData.mail_list.length <= 1) return;
|
||||||
|
|
||||||
|
const filterList = resultData.mail_list.filter(el => el.language !== targetKey);
|
||||||
|
setResultData({ ...resultData, mail_list: filterList });
|
||||||
|
|
||||||
|
// 삭제된 탭이 현재 활성 탭이었다면 첫 번째 탭으로 변경
|
||||||
|
if (activeLanguage === targetKey) {
|
||||||
|
setActiveLanguage(filterList[0]?.language || 'KO');
|
||||||
|
}
|
||||||
|
setIsChanged(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 추가
|
||||||
|
const handleItemList = async () => {
|
||||||
|
if(benItems.includes(item)){
|
||||||
|
showToast('MAIL_ITEM_ADD_BEN', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(item.length === 0 || itemCount.length === 0) return;
|
||||||
|
|
||||||
|
const result = await EventIsItem(token, {item: item});
|
||||||
|
|
||||||
|
if(result.data.result === "ERROR"){
|
||||||
|
setItemCheckMsg(t('NOT_ITEM'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemIndex = resultData.item_list.findIndex((data) => data.item === item);
|
||||||
|
if (itemIndex !== -1) {
|
||||||
|
setItemCheckMsg(t('MAIL_ITEM_ADD_DUPL'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newItem = { item: item, item_cnt: itemCount, item_name: result.data.data.item_info.item_name };
|
||||||
|
|
||||||
|
resultData.item_list.push(newItem);
|
||||||
|
|
||||||
|
setIsChanged(true);
|
||||||
|
setItem('');
|
||||||
|
setItemCount('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 삭제
|
||||||
|
const onItemRemove = id => {
|
||||||
|
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
|
||||||
|
setIsChanged(true);
|
||||||
|
|
||||||
|
setResultData({ ...resultData, item_list: filterList });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자원 추가
|
||||||
|
const handleResourceList = (e) => {
|
||||||
|
if(resource.length === 0 || resourceCount.length === 0) return;
|
||||||
|
|
||||||
|
const itemIndex = resultData.item_list.findIndex(
|
||||||
|
(item) => item.item === resource
|
||||||
|
);
|
||||||
|
|
||||||
|
if (itemIndex !== -1) {
|
||||||
|
const item_cnt = resultData.item_list[itemIndex].item_cnt;
|
||||||
|
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
|
||||||
|
} else {
|
||||||
|
const name = currencyItemCode.find(well => well.value === resource).name;
|
||||||
|
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
|
||||||
|
resultData.item_list.push(newItem);
|
||||||
|
}
|
||||||
|
setIsChanged(true);
|
||||||
|
setResource('')
|
||||||
|
setResourceCount('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 확인 버튼 후 다 초기화
|
||||||
|
const handleReset = () => {
|
||||||
|
setBtnValidation(false);
|
||||||
|
setIsChanged(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const conditionCheck = () => {
|
||||||
|
return (
|
||||||
|
content && content.mail_list.every(data => data.content !== '' && data.title !== '') &&
|
||||||
|
isChanged
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
if (!conditionCheck()) return;
|
||||||
|
|
||||||
|
showModal('MAIL_UPDATE_SAVE', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleSubmit('updateConfirm')
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "updateConfirm":
|
||||||
|
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date))
|
||||||
|
// 이벤트 시작 30분전이나 이미 SystemMail이 add된 상태에서는 수정할 수 없다.
|
||||||
|
if(content.add_flag || timeDiff <= 30){
|
||||||
|
showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
withLoading( async () => {
|
||||||
|
return await RewardEventModify(token, id, resultData);
|
||||||
|
}).catch(error => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
|
||||||
|
handleDetailView();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const detailState = (status) => {
|
||||||
|
switch (status) {
|
||||||
|
case commonStatus.wait:
|
||||||
|
return <DetailState>대기</DetailState>;
|
||||||
|
case commonStatus.running:
|
||||||
|
return <DetailState>진행중</DetailState>;
|
||||||
|
case commonStatus.finish:
|
||||||
|
return <DetailState result={commonStatus.finish}>완료</DetailState>;
|
||||||
|
case commonStatus.fail:
|
||||||
|
return <DetailState result={commonStatus.fail}>실패</DetailState>;
|
||||||
|
case commonStatus.delete:
|
||||||
|
return <DetailState result={commonStatus.delete}>삭제</DetailState>;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 목록 렌더링 컴포넌트
|
||||||
|
const renderItemList = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{resultData.item_list && resultData.item_list.length > 0 && (
|
||||||
|
<Space wrap>
|
||||||
|
{resultData.item_list.map((data, index) => (
|
||||||
|
<Card
|
||||||
|
key={index}
|
||||||
|
title={data.item_name}
|
||||||
|
size="small"
|
||||||
|
extra={
|
||||||
|
!isReadOnly && (
|
||||||
|
<AntButton
|
||||||
|
type="text"
|
||||||
|
danger
|
||||||
|
size="small"
|
||||||
|
onClick={() => onItemRemove(index)}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</AntButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
style={{ minWidth: '150px' }}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div>{data.item}</div>
|
||||||
|
<div>수량: {data.item_cnt}</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 추가 컴포넌트
|
||||||
|
const renderItemAdd = () => {
|
||||||
|
return (
|
||||||
|
<Space.Compact style={{ width: '100%' }}>
|
||||||
|
<Input
|
||||||
|
placeholder="Item Meta id 입력"
|
||||||
|
value={item}
|
||||||
|
onChange={(e) => setItem(e.target.value.trimStart())}
|
||||||
|
disabled={isReadOnly}
|
||||||
|
style={{ width: '200px' }}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="수량"
|
||||||
|
value={itemCount}
|
||||||
|
onChange={(e) => setItemCount(e.target.value)}
|
||||||
|
disabled={isReadOnly}
|
||||||
|
style={{ width: '120px' }}
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
<AntButton
|
||||||
|
type="primary"
|
||||||
|
onClick={handleItemList}
|
||||||
|
disabled={itemCount.length === 0 || item.length === 0 || isReadOnly}
|
||||||
|
>
|
||||||
|
추가
|
||||||
|
</AntButton>
|
||||||
|
</Space.Compact>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자원 추가 컴포넌트
|
||||||
|
const renderResourceAdd = () => {
|
||||||
|
return (
|
||||||
|
<Space.Compact style={{ width: '100%' }}>
|
||||||
|
<Select
|
||||||
|
value={resource}
|
||||||
|
onChange={setResource}
|
||||||
|
disabled={isReadOnly}
|
||||||
|
style={{ width: '200px' }}
|
||||||
|
placeholder="자원 선택"
|
||||||
|
>
|
||||||
|
{currencyItemCode.map((data, index) => (
|
||||||
|
<Select.Option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="수량"
|
||||||
|
value={resourceCount}
|
||||||
|
disabled={isReadOnly}
|
||||||
|
onChange={(e) => setResourceCount(e.target.value)}
|
||||||
|
style={{ width: '120px' }}
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
<AntButton
|
||||||
|
type="primary"
|
||||||
|
onClick={handleResourceList}
|
||||||
|
disabled={resourceCount.length === 0 || resource.length === 0 || isReadOnly}
|
||||||
|
>
|
||||||
|
추가
|
||||||
|
</AntButton>
|
||||||
|
</Space.Compact>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemGroups = [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
row: 0,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 2,
|
||||||
|
type: 'dateRange',
|
||||||
|
keys: {
|
||||||
|
start: 'start_dt',
|
||||||
|
end: 'end_dt'
|
||||||
|
},
|
||||||
|
label: '이벤트 기간',
|
||||||
|
disabled: isReadOnly,
|
||||||
|
format: 'YYYY-MM-DD HH:mm',
|
||||||
|
showTime: true,
|
||||||
|
startLabel: '시작 일시',
|
||||||
|
endLabel: '종료 일시'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 0,
|
||||||
|
col: 2,
|
||||||
|
colSpan: 1,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'status',
|
||||||
|
label: '이벤트 상태',
|
||||||
|
render: () => detailState(resultData.status)
|
||||||
|
},
|
||||||
|
...(resultData.status === commonStatus.delete ? [{
|
||||||
|
row: 0,
|
||||||
|
col: 3,
|
||||||
|
colSpan: 1,
|
||||||
|
type: 'display',
|
||||||
|
key: 'delete_desc',
|
||||||
|
label: '삭제 사유',
|
||||||
|
value: resultData.delete_desc || ''
|
||||||
|
}] : [{
|
||||||
|
row: 0,
|
||||||
|
col: 3,
|
||||||
|
colSpan: 1,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'empty_space',
|
||||||
|
label: '',
|
||||||
|
render: () => <div></div>
|
||||||
|
}]),
|
||||||
|
{
|
||||||
|
row: 1,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 4,
|
||||||
|
type: 'tab',
|
||||||
|
key: 'language_tabs',
|
||||||
|
tabItems: getLanguageTabItems(),
|
||||||
|
activeKey: activeLanguage,
|
||||||
|
onTabChange: setActiveLanguage,
|
||||||
|
onTabClose: handleTabClose
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 2,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 4,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'item_add',
|
||||||
|
label: '아이템 추가',
|
||||||
|
render: renderItemAdd
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 3,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 4,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'resource_add',
|
||||||
|
label: '자원 추가',
|
||||||
|
render: renderResourceAdd
|
||||||
|
},
|
||||||
|
{
|
||||||
|
row: 4,
|
||||||
|
col: 0,
|
||||||
|
colSpan: 4,
|
||||||
|
type: 'custom',
|
||||||
|
key: 'item_list',
|
||||||
|
render: renderItemList
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal min="960px" $view={detailView}>
|
||||||
|
<Title $align="center">이벤트 상세 정보</Title>
|
||||||
|
{content &&
|
||||||
|
<DetailRegistInfo>
|
||||||
|
<span>등록자 : {content.create_by}</span>
|
||||||
|
<span>등록일 : {convertKTC(content.create_dt, false)}</span>
|
||||||
|
{typeof content.update_by !== 'undefined' && (
|
||||||
|
<>
|
||||||
|
<span>수정자 : {content.update_by}</span>
|
||||||
|
<span>수정일 : {convertKTC(content.update_dt, false)}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</DetailRegistInfo>
|
||||||
|
}
|
||||||
|
|
||||||
|
<DetailLayout
|
||||||
|
itemGroups={itemGroups}
|
||||||
|
formData={resultData}
|
||||||
|
onChange={setResultData}
|
||||||
|
disabled={false}
|
||||||
|
columnCount={4}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{itemCheckMsg && (
|
||||||
|
<Alert
|
||||||
|
message={itemCheckMsg}
|
||||||
|
type="error"
|
||||||
|
style={{ marginTop: '8px', width: '300px' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px">
|
||||||
|
<Button
|
||||||
|
text="확인"
|
||||||
|
theme="line"
|
||||||
|
name="확인버튼"
|
||||||
|
handleClick={() => {
|
||||||
|
handleDetailView();
|
||||||
|
handleReset();
|
||||||
|
setDetailData('');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!isReadOnly && (
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text="수정"
|
||||||
|
id="수정버튼"
|
||||||
|
theme={conditionCheck() ? 'primary' : 'disable'}
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</BtnWrapper>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RewardEventDetailModal;
|
||||||
|
|
||||||
|
|
||||||
@@ -1,52 +1,43 @@
|
|||||||
import React, { useState, Fragment } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import {
|
|
||||||
EventDelete,
|
|
||||||
EventDetailView, LogHistory,
|
|
||||||
} from '../../apis';
|
|
||||||
|
|
||||||
import { authList } from '../../store/authList';
|
|
||||||
import { authType, commonStatus, modalTypes, eventStatus } from '../../assets/data';
|
|
||||||
import { Title, FormWrapper, TableStyle, MailTitle, TableWrapper, TextInput, InputItem } from '../../styles/Components';
|
|
||||||
import CheckBox from '../../components/common/input/CheckBox';
|
|
||||||
import Button from '../../components/common/button/Button';
|
|
||||||
import EventDetailModal from '../../components/modal/EventDetailModal';
|
|
||||||
import Pagination from '../../components/common/Pagination/Pagination';
|
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
import DynamicModal from '../../components/common/modal/DynamicModal';
|
import tableInfo from '../../assets/data/pages/eventTable.json'
|
||||||
import AuthModal from '../../components/common/modal/AuthModal';
|
|
||||||
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
|
|
||||||
import { convertKTC, timeDiffMinute } from '../../utils';
|
|
||||||
import {
|
import {
|
||||||
ModalInputItem,
|
authType, commonStatus as CommonStatus,
|
||||||
ModalSubText,
|
} from '../../assets/data';
|
||||||
RegistInputItem,
|
import { Title, FormWrapper} from '../../styles/Components';
|
||||||
StatusLabel, StatusWapper,
|
import {
|
||||||
} from '../../styles/ModuleComponents';
|
Pagination,
|
||||||
import { useLoading } from '../../context/LoadingProvider';
|
CaliTable, TableHeader,
|
||||||
import { useAlert } from '../../context/AlertProvider';
|
} from '../../components/common';
|
||||||
import { CommonSearchBar, useCommonSearch } from '../../components/ServiceManage';
|
|
||||||
import { useModal, useTable } from '../../hooks/hook';
|
|
||||||
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||||
|
import { useDataFetch, useModal, useTable, withAuth } from '../../hooks/hook';
|
||||||
|
import {
|
||||||
|
EventActionView, EventDelete,
|
||||||
|
EventDetailView,
|
||||||
|
LogHistory,
|
||||||
|
} from '../../apis';
|
||||||
|
import { CommonSearchBar } from '../../components/ServiceManage';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
import { alertTypes } from '../../assets/data/types';
|
import { alertTypes } from '../../assets/data/types';
|
||||||
import useCommonSearchOld from '../../hooks/useCommonSearchOld';
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import useEnhancedCommonSearch from '../../hooks/useEnhancedCommonSearch';
|
||||||
import { historyTables } from '../../assets/data/data';
|
import { historyTables } from '../../assets/data/data';
|
||||||
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
||||||
import {AnimatedPageWrapper} from '../../components/common/Layout';
|
import { AnimatedPageWrapper } from '../../components/common/Layout';
|
||||||
|
import EventModal from '../../components/modal/EventModal';
|
||||||
|
|
||||||
const Event = () => {
|
const Event = () => {
|
||||||
const token = sessionStorage.getItem('token');
|
const tableRef = useRef(null);
|
||||||
const userInfo = useRecoilValue(authList);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const {withLoading} = useLoading();
|
|
||||||
const {showToast} = useAlert();
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [detailData, setDetailData] = useState('');
|
const { showToast, showModal } = useAlert();
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
|
||||||
|
const [detailData, setDetailData] = useState({});
|
||||||
const [historyData, setHistoryData] = useState({});
|
const [historyData, setHistoryData] = useState({});
|
||||||
|
const [modalType, setModalType] = useState('regist');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
modalState,
|
modalState,
|
||||||
@@ -54,10 +45,8 @@ const Event = () => {
|
|||||||
handleModalClose
|
handleModalClose
|
||||||
} = useModal({
|
} = useModal({
|
||||||
detail: 'hidden',
|
detail: 'hidden',
|
||||||
delete: 'hidden',
|
history: 'hidden',
|
||||||
history: 'hidden'
|
|
||||||
});
|
});
|
||||||
const [deleteDesc, setDeleteDesc] = useState('');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
config,
|
config,
|
||||||
@@ -65,35 +54,34 @@ const Event = () => {
|
|||||||
data: dataList,
|
data: dataList,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
handleReset,
|
handleReset,
|
||||||
handlePageChange,
|
|
||||||
handlePageSizeChange,
|
|
||||||
handleOrderByChange,
|
handleOrderByChange,
|
||||||
updateSearchParams,
|
updateSearchParams,
|
||||||
configLoaded
|
loading,
|
||||||
} = useCommonSearchOld("eventSearch");
|
configLoaded,
|
||||||
|
handlePageChange,
|
||||||
|
handlePageSizeChange
|
||||||
|
} = useEnhancedCommonSearch("eventSearch");
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: eventActionData
|
||||||
|
} = useDataFetch(() => EventActionView(token), [token]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selectedRows,
|
selectedRows,
|
||||||
handleSelectRow,
|
handleSelectRow,
|
||||||
isRowSelected
|
isRowSelected
|
||||||
} = useTable(dataList?.list || [], {mode: 'single'});
|
} = useTable(dataList?.list || [], {mode: 'single'});
|
||||||
|
|
||||||
// 상세보기 호출
|
const handleAction = async (action, item = null) => {
|
||||||
const handleDetailModal = async (e, id) => {
|
switch (action) {
|
||||||
await EventDetailView(token, id).then(data => {
|
case "regist":
|
||||||
setDetailData(data);
|
setModalType('regist');
|
||||||
});
|
handleModalView('detail');
|
||||||
|
break;
|
||||||
e.preventDefault();
|
|
||||||
handleModalView('detail');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleModalSubmit = async (type, param = null) => {
|
|
||||||
switch (type) {
|
|
||||||
case "history":
|
case "history":
|
||||||
const params = {};
|
const params = {};
|
||||||
params.db_type = "MYSQL"
|
params.db_type = "MYSQL"
|
||||||
params.sql_id = param.id;
|
params.sql_id = item.id;
|
||||||
params.table_name = historyTables.event
|
params.table_name = historyTables.event
|
||||||
|
|
||||||
await LogHistory(token, params).then(data => {
|
await LogHistory(token, params).then(data => {
|
||||||
@@ -101,191 +89,124 @@ const Event = () => {
|
|||||||
handleModalView('history');
|
handleModalView('history');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "delete":
|
case "detail":
|
||||||
const delete_check = selectedRows.every(row => {
|
await EventDetailView(token, item.id).then(data => {
|
||||||
const timeDiff = timeDiffMinute(convertKTC(row.start_dt), (new Date));
|
setDetailData(data.detail);
|
||||||
return row.add_flag || (timeDiff < 30);
|
setModalType('modify');
|
||||||
|
handleModalView('detail');
|
||||||
});
|
});
|
||||||
if(delete_check){
|
break;
|
||||||
showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
|
case "delete":
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleModalView('delete');
|
showModal('EVENT_SELECT_DELETE', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => handleAction('deleteConfirm')
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case "deleteConfirm":
|
case "deleteConfirm":
|
||||||
let list = [];
|
const low = selectedRows[0];
|
||||||
|
|
||||||
if(deleteDesc.length === 0){
|
if(low.status !== CommonStatus.wait) {
|
||||||
showToast('INPUT_REASON_EMPTY_WARNING', {type: alertTypes.warning});
|
showToast('DELETE_STATUS_ONLY_WAIT', {type: alertTypes.warning});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isChecked = false;
|
|
||||||
|
|
||||||
selectedRows.map(data => {
|
|
||||||
const row = dataList.list.find(row => row.id === Number(data.id));
|
|
||||||
if(row.status !== commonStatus.wait) isChecked = true;
|
|
||||||
list.push({
|
|
||||||
id: data.id,
|
|
||||||
delete_desc: deleteDesc
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
handleModalClose('delete');
|
|
||||||
setDeleteDesc('');
|
|
||||||
|
|
||||||
if(isChecked) {
|
|
||||||
showToast('EVENT_WARNING_DELETE', {type: alertTypes.warning});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await withLoading(async () => {
|
await withLoading(async () => {
|
||||||
return await EventDelete(token, list);
|
return await EventDelete(token, low.id);
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
if(data.result === "SUCCESS") {
|
||||||
}).catch(error => {
|
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||||
|
}else{
|
||||||
|
showToast('DELETE_FAIL', {type: alertTypes.error});
|
||||||
|
}
|
||||||
|
}).catch(reason => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
handleSearch(updateSearchParams);
|
handleSearch(updateSearchParams);
|
||||||
})
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AnimatedPageWrapper>
|
||||||
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventRead) ? (
|
<Title>통합 이벤트 관리</Title>
|
||||||
<AuthModal />
|
|
||||||
) : (
|
|
||||||
<AnimatedPageWrapper>
|
|
||||||
<Title>출석 보상 이벤트 관리</Title>
|
|
||||||
<FormWrapper>
|
|
||||||
<CommonSearchBar
|
|
||||||
config={config}
|
|
||||||
searchParams={searchParams}
|
|
||||||
onSearch={(newParams, executeSearch = true) => {
|
|
||||||
if (executeSearch) {
|
|
||||||
handleSearch(newParams);
|
|
||||||
} else {
|
|
||||||
updateSearchParams(newParams);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onReset={handleReset}
|
|
||||||
/>
|
|
||||||
</FormWrapper>
|
|
||||||
<ViewTableInfo total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
|
|
||||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventDelete) && (
|
|
||||||
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
|
||||||
)}
|
|
||||||
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
|
|
||||||
<Button
|
|
||||||
theme="primary"
|
|
||||||
text="이벤트 등록"
|
|
||||||
type="button"
|
|
||||||
handleClick={e => {
|
|
||||||
e.preventDefault();
|
|
||||||
navigate('/servicemanage/event/eventregist');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ViewTableInfo>
|
|
||||||
<TableWrapper>
|
|
||||||
<TableStyle>
|
|
||||||
<caption></caption>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="40">
|
|
||||||
</th>
|
|
||||||
<th width="80">번호</th>
|
|
||||||
<th width="100">이벤트 상태</th>
|
|
||||||
<th width="210">시작 일시</th>
|
|
||||||
<th width="210">종료 일시</th>
|
|
||||||
<th>우편 제목</th>
|
|
||||||
<th width="110">확인 / 수정</th>
|
|
||||||
<th width="200">히스토리</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{dataList?.list?.map(event => (
|
|
||||||
<Fragment key={event.row_num}>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<CheckBox name={'select'} id={event.id}
|
|
||||||
setData={(e) => handleSelectRow(e, event)}
|
|
||||||
checked={isRowSelected(event.id)} />
|
|
||||||
</td>
|
|
||||||
<td>{event.row_num}</td>
|
|
||||||
<StatusWapper>
|
|
||||||
<StatusLabel $status={event.status}>
|
|
||||||
{eventStatus.map(data => data.value === event.status && data.name)}
|
|
||||||
</StatusLabel>
|
|
||||||
</StatusWapper>
|
|
||||||
<td>{convertKTC(event.start_dt)}</td>
|
|
||||||
<td>{convertKTC(event.end_dt)}</td>
|
|
||||||
<MailTitle>{event.title}</MailTitle>
|
|
||||||
<td>
|
|
||||||
<Button theme="line" text="상세보기"
|
|
||||||
handleClick={e => handleDetailModal(e, event.id)} />
|
|
||||||
</td>
|
|
||||||
<td><Button theme="line" text="히스토리"
|
|
||||||
handleClick={e => handleModalSubmit('history', event)} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</TableStyle>
|
|
||||||
</TableWrapper>
|
|
||||||
|
|
||||||
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
{/* 조회조건 */}
|
||||||
|
<FormWrapper>
|
||||||
|
<CommonSearchBar
|
||||||
|
config={config}
|
||||||
|
searchParams={searchParams}
|
||||||
|
onSearch={(newParams, executeSearch = true) => {
|
||||||
|
if (executeSearch) {
|
||||||
|
handleSearch(newParams);
|
||||||
|
} else {
|
||||||
|
updateSearchParams(newParams);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
</FormWrapper>
|
||||||
|
|
||||||
{/*상세*/}
|
{/* 조회헤더 */}
|
||||||
<EventDetailModal
|
<TableHeader
|
||||||
detailView={modalState.detailModal}
|
config={tableInfo.header}
|
||||||
handleDetailView={() =>{
|
total={dataList?.total}
|
||||||
handleModalClose('detail');
|
total_all={dataList?.total_all}
|
||||||
handleSearch(updateSearchParams);
|
handleOrderBy={handleOrderByChange}
|
||||||
}}
|
handlePageSize={handlePageSizeChange}
|
||||||
content={detailData}
|
selectedRows={selectedRows}
|
||||||
setDetailData={setDetailData}
|
onAction={handleAction}
|
||||||
/>
|
navigate={navigate}
|
||||||
|
/>
|
||||||
|
|
||||||
<LogDetailModal
|
{/* 조회테이블 */}
|
||||||
viewMode="changed"
|
<CaliTable
|
||||||
detailView={modalState.historyModal}
|
columns={tableInfo.columns}
|
||||||
handleDetailView={() => handleModalClose('history')}
|
data={dataList?.list}
|
||||||
changedData={historyData}
|
selectedRows={selectedRows}
|
||||||
title="히스토리"
|
onSelectRow={handleSelectRow}
|
||||||
/>
|
onAction={handleAction}
|
||||||
|
refProp={tableRef}
|
||||||
|
loading={loading}
|
||||||
|
isRowSelected={isRowSelected}
|
||||||
|
/>
|
||||||
|
|
||||||
<DynamicModal
|
{/* 페이징 */}
|
||||||
modalType={modalTypes.childOkCancel}
|
<Pagination
|
||||||
view={modalState.deleteModal}
|
postsPerPage={searchParams?.pageSize}
|
||||||
handleCancel={() => handleModalClose('delete')}
|
totalPosts={dataList?.total_all}
|
||||||
handleSubmit={() => handleModalSubmit('deleteConfirm')}
|
setCurrentPage={handlePageChange}
|
||||||
>
|
currentPage={searchParams?.currentPage}
|
||||||
<ModalInputItem>
|
pageLimit={INITIAL_PAGE_LIMIT}
|
||||||
{t('EVENT_SELECT_DELETE')}
|
/>
|
||||||
<RegistInputItem>
|
|
||||||
<TextInput
|
{/* 상세 */}
|
||||||
placeholder="사유 입력"
|
<EventModal
|
||||||
maxLength="30"
|
modalType={modalType}
|
||||||
value={deleteDesc}
|
detailView={modalState.detailModal}
|
||||||
onChange={e => {
|
handleDetailView={() =>{
|
||||||
if (e.target.value.length > 30) return;
|
handleModalClose('detail');
|
||||||
setDeleteDesc(e.target.value.trimStart())
|
handleSearch(updateSearchParams);
|
||||||
}}
|
}}
|
||||||
/>
|
content={detailData}
|
||||||
</RegistInputItem>
|
setDetailData={setDetailData}
|
||||||
<ModalSubText $color={deleteDesc.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30자)</ModalSubText>
|
eventActionData={eventActionData}
|
||||||
</ModalInputItem>
|
/>
|
||||||
</DynamicModal>
|
|
||||||
</AnimatedPageWrapper>
|
<LogDetailModal
|
||||||
)}
|
viewMode="changed"
|
||||||
</>
|
detailView={modalState.historyModal}
|
||||||
);
|
handleDetailView={() => handleModalClose('history')}
|
||||||
|
changedData={historyData}
|
||||||
|
title="히스토리"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</AnimatedPageWrapper>
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Event;
|
export default withAuth(authType.worldEventRead)(Event);
|
||||||
|
|||||||
291
src/pages/ServiceManage/RewardEvent.js
Normal file
291
src/pages/ServiceManage/RewardEvent.js
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
import React, { useState, Fragment } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import {
|
||||||
|
RewardEventDelete,
|
||||||
|
RewardEventDetailView, LogHistory,
|
||||||
|
} from '../../apis';
|
||||||
|
|
||||||
|
import { authList } from '../../store/authList';
|
||||||
|
import { authType, commonStatus, modalTypes, eventStatus } from '../../assets/data';
|
||||||
|
import { Title, FormWrapper, TableStyle, MailTitle, TableWrapper, TextInput, InputItem } from '../../styles/Components';
|
||||||
|
import CheckBox from '../../components/common/input/CheckBox';
|
||||||
|
import Button from '../../components/common/button/Button';
|
||||||
|
import RewardEventDetailModal from '../../components/modal/RewardEventDetailModal';
|
||||||
|
import Pagination from '../../components/common/Pagination/Pagination';
|
||||||
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
|
import DynamicModal from '../../components/common/modal/DynamicModal';
|
||||||
|
import AuthModal from '../../components/common/modal/AuthModal';
|
||||||
|
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
|
||||||
|
import { convertKTC, timeDiffMinute } from '../../utils';
|
||||||
|
import {
|
||||||
|
ModalInputItem,
|
||||||
|
ModalSubText,
|
||||||
|
RegistInputItem,
|
||||||
|
StatusLabel, StatusWapper,
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { CommonSearchBar, useCommonSearch } from '../../components/ServiceManage';
|
||||||
|
import { useModal, useTable } from '../../hooks/hook';
|
||||||
|
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
|
||||||
|
import { alertTypes } from '../../assets/data/types';
|
||||||
|
import useCommonSearchOld from '../../hooks/useCommonSearchOld';
|
||||||
|
import { historyTables } from '../../assets/data/data';
|
||||||
|
import LogDetailModal from '../../components/common/modal/LogDetailModal';
|
||||||
|
import {AnimatedPageWrapper} from '../../components/common/Layout';
|
||||||
|
|
||||||
|
const RewardEvent = () => {
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const userInfo = useRecoilValue(authList);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const {withLoading} = useLoading();
|
||||||
|
const {showToast} = useAlert();
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [detailData, setDetailData] = useState('');
|
||||||
|
const [historyData, setHistoryData] = useState({});
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalState,
|
||||||
|
handleModalView,
|
||||||
|
handleModalClose
|
||||||
|
} = useModal({
|
||||||
|
detail: 'hidden',
|
||||||
|
delete: 'hidden',
|
||||||
|
history: 'hidden'
|
||||||
|
});
|
||||||
|
const [deleteDesc, setDeleteDesc] = useState('');
|
||||||
|
|
||||||
|
const {
|
||||||
|
config,
|
||||||
|
searchParams,
|
||||||
|
data: dataList,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePageChange,
|
||||||
|
handlePageSizeChange,
|
||||||
|
handleOrderByChange,
|
||||||
|
updateSearchParams,
|
||||||
|
configLoaded
|
||||||
|
} = useCommonSearchOld("rewardEventSearch");
|
||||||
|
const {
|
||||||
|
selectedRows,
|
||||||
|
handleSelectRow,
|
||||||
|
isRowSelected
|
||||||
|
} = useTable(dataList?.list || [], {mode: 'single'});
|
||||||
|
|
||||||
|
// 상세보기 호출
|
||||||
|
const handleDetailModal = async (e, id) => {
|
||||||
|
await RewardEventDetailView(token, id).then(data => {
|
||||||
|
setDetailData(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
handleModalView('detail');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleModalSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "history":
|
||||||
|
const params = {};
|
||||||
|
params.db_type = "MYSQL"
|
||||||
|
params.sql_id = param.id;
|
||||||
|
params.table_name = historyTables.rewardEvent
|
||||||
|
|
||||||
|
await LogHistory(token, params).then(data => {
|
||||||
|
setHistoryData(data);
|
||||||
|
handleModalView('history');
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "delete":
|
||||||
|
const delete_check = selectedRows.every(row => {
|
||||||
|
const timeDiff = timeDiffMinute(convertKTC(row.start_dt), (new Date));
|
||||||
|
return row.add_flag || (timeDiff < 30);
|
||||||
|
});
|
||||||
|
if(delete_check){
|
||||||
|
showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleModalView('delete');
|
||||||
|
break;
|
||||||
|
case "deleteConfirm":
|
||||||
|
let list = [];
|
||||||
|
|
||||||
|
if(deleteDesc.length === 0){
|
||||||
|
showToast('INPUT_REASON_EMPTY_WARNING', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isChecked = false;
|
||||||
|
|
||||||
|
selectedRows.map(data => {
|
||||||
|
const row = dataList.list.find(row => row.id === Number(data.id));
|
||||||
|
if(row.status !== commonStatus.wait) isChecked = true;
|
||||||
|
list.push({
|
||||||
|
id: data.id,
|
||||||
|
delete_desc: deleteDesc
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
handleModalClose('delete');
|
||||||
|
setDeleteDesc('');
|
||||||
|
|
||||||
|
if(isChecked) {
|
||||||
|
showToast('EVENT_WARNING_DELETE', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await withLoading(async () => {
|
||||||
|
return await RewardEventDelete(token, list);
|
||||||
|
}).then(data => {
|
||||||
|
showToast('DEL_COMPLETE', {type: alertTypes.success});
|
||||||
|
}).catch(error => {
|
||||||
|
|
||||||
|
}).finally(() => {
|
||||||
|
handleSearch(updateSearchParams);
|
||||||
|
})
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventRead) ? (
|
||||||
|
<AuthModal />
|
||||||
|
) : (
|
||||||
|
<AnimatedPageWrapper>
|
||||||
|
<Title>출석 보상 이벤트 관리</Title>
|
||||||
|
<FormWrapper>
|
||||||
|
<CommonSearchBar
|
||||||
|
config={config}
|
||||||
|
searchParams={searchParams}
|
||||||
|
onSearch={(newParams, executeSearch = true) => {
|
||||||
|
if (executeSearch) {
|
||||||
|
handleSearch(newParams);
|
||||||
|
} else {
|
||||||
|
updateSearchParams(newParams);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
</FormWrapper>
|
||||||
|
<ViewTableInfo total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
|
||||||
|
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventDelete) && (
|
||||||
|
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
|
||||||
|
)}
|
||||||
|
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
|
||||||
|
<Button
|
||||||
|
theme="primary"
|
||||||
|
text="이벤트 등록"
|
||||||
|
type="button"
|
||||||
|
handleClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
navigate('/servicemanage/rewardevent/eventregist');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ViewTableInfo>
|
||||||
|
<TableWrapper>
|
||||||
|
<TableStyle>
|
||||||
|
<caption></caption>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="40">
|
||||||
|
</th>
|
||||||
|
<th width="80">번호</th>
|
||||||
|
<th width="100">이벤트 상태</th>
|
||||||
|
<th width="210">시작 일시</th>
|
||||||
|
<th width="210">종료 일시</th>
|
||||||
|
<th>우편 제목</th>
|
||||||
|
<th width="110">확인 / 수정</th>
|
||||||
|
<th width="200">히스토리</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{dataList?.list?.map(event => (
|
||||||
|
<Fragment key={event.row_num}>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<CheckBox name={'select'} id={event.id}
|
||||||
|
setData={(e) => handleSelectRow(e, event)}
|
||||||
|
checked={isRowSelected(event.id)} />
|
||||||
|
</td>
|
||||||
|
<td>{event.row_num}</td>
|
||||||
|
<StatusWapper>
|
||||||
|
<StatusLabel $status={event.status}>
|
||||||
|
{eventStatus.map(data => data.value === event.status && data.name)}
|
||||||
|
</StatusLabel>
|
||||||
|
</StatusWapper>
|
||||||
|
<td>{convertKTC(event.start_dt)}</td>
|
||||||
|
<td>{convertKTC(event.end_dt)}</td>
|
||||||
|
<MailTitle>{event.title}</MailTitle>
|
||||||
|
<td>
|
||||||
|
<Button theme="line" text="상세보기"
|
||||||
|
handleClick={e => handleDetailModal(e, event.id)} />
|
||||||
|
</td>
|
||||||
|
<td><Button theme="line" text="히스토리"
|
||||||
|
handleClick={e => handleModalSubmit('history', event)} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</TableStyle>
|
||||||
|
</TableWrapper>
|
||||||
|
|
||||||
|
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
|
||||||
|
|
||||||
|
{/*상세*/}
|
||||||
|
<RewardEventDetailModal
|
||||||
|
detailView={modalState.detailModal}
|
||||||
|
handleDetailView={() =>{
|
||||||
|
handleModalClose('detail');
|
||||||
|
handleSearch(updateSearchParams);
|
||||||
|
}}
|
||||||
|
content={detailData}
|
||||||
|
setDetailData={setDetailData}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LogDetailModal
|
||||||
|
viewMode="changed"
|
||||||
|
detailView={modalState.historyModal}
|
||||||
|
handleDetailView={() => handleModalClose('history')}
|
||||||
|
changedData={historyData}
|
||||||
|
title="히스토리"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DynamicModal
|
||||||
|
modalType={modalTypes.childOkCancel}
|
||||||
|
view={modalState.deleteModal}
|
||||||
|
handleCancel={() => handleModalClose('delete')}
|
||||||
|
handleSubmit={() => handleModalSubmit('deleteConfirm')}
|
||||||
|
>
|
||||||
|
<ModalInputItem>
|
||||||
|
{t('EVENT_SELECT_DELETE')}
|
||||||
|
<RegistInputItem>
|
||||||
|
<TextInput
|
||||||
|
placeholder="사유 입력"
|
||||||
|
maxLength="30"
|
||||||
|
value={deleteDesc}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.value.length > 30) return;
|
||||||
|
setDeleteDesc(e.target.value.trimStart())
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</RegistInputItem>
|
||||||
|
<ModalSubText $color={deleteDesc.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30자)</ModalSubText>
|
||||||
|
</ModalInputItem>
|
||||||
|
</DynamicModal>
|
||||||
|
</AnimatedPageWrapper>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RewardEvent;
|
||||||
467
src/pages/ServiceManage/RewardEventRegist.js
Normal file
467
src/pages/ServiceManage/RewardEventRegist.js
Normal file
@@ -0,0 +1,467 @@
|
|||||||
|
import React, { useState, Fragment, useEffect } from 'react';
|
||||||
|
import Button from '../../components/common/button/Button';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Title,
|
||||||
|
BtnWrapper,
|
||||||
|
TextInput,
|
||||||
|
SelectInput,
|
||||||
|
Label,
|
||||||
|
InputLabel,
|
||||||
|
Textarea,
|
||||||
|
SearchBarAlert,
|
||||||
|
} from '../../styles/Components';
|
||||||
|
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { EventIsItem, RewardEventSingleRegist } from '../../apis';
|
||||||
|
|
||||||
|
import { authList } from '../../store/authList';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
AppendRegistBox, AppendRegistTable, AreaBtnClose,
|
||||||
|
BtnDelete,
|
||||||
|
Item,
|
||||||
|
ItemList, LangArea, ModalItem, ModalItemList, RegistGroup,
|
||||||
|
RegistInputItem,
|
||||||
|
RegistInputRow, RegistNotice, RegistTable,
|
||||||
|
} from '../../styles/ModuleComponents';
|
||||||
|
import AuthModal from '../../components/common/modal/AuthModal';
|
||||||
|
import { authType, benItems, currencyItemCode } from '../../assets/data';
|
||||||
|
import DateTimeInput from '../../components/common/input/DateTimeInput';
|
||||||
|
import { timeDiffMinute } from '../../utils';
|
||||||
|
import { useAlert } from '../../context/AlertProvider';
|
||||||
|
import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
|
||||||
|
import { useLoading } from '../../context/LoadingProvider';
|
||||||
|
import { AnimatedPageWrapper } from '../../components/common/Layout';
|
||||||
|
|
||||||
|
const RewardEventRegist = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const userInfo = useRecoilValue(authList);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const { showToast, showModal } = useAlert();
|
||||||
|
const { withLoading} = useLoading();
|
||||||
|
|
||||||
|
const [item, setItem] = useState(''); // 아이템 값
|
||||||
|
const [itemCount, setItemCount] = useState(''); // 아이템 개수
|
||||||
|
const [resource, setResource] = useState(currencyCodeTypes.gold); // 자원 값
|
||||||
|
const [resourceCount, setResourceCount] = useState(''); // 자원 개수
|
||||||
|
|
||||||
|
const [isNullValue, setIsNullValue] = useState(false);
|
||||||
|
const [btnValidation, setBtnValidation] = useState(false);
|
||||||
|
const [itemCheckMsg, setItemCheckMsg] = useState('');
|
||||||
|
|
||||||
|
const [time, setTime] = useState({
|
||||||
|
start_hour: '00',
|
||||||
|
start_min: '00',
|
||||||
|
end_hour: '00',
|
||||||
|
end_min: '00',
|
||||||
|
}); //시간 정보
|
||||||
|
|
||||||
|
const [resultData, setResultData] = useState({
|
||||||
|
is_reserve: false,
|
||||||
|
start_dt: '',
|
||||||
|
end_dt: '',
|
||||||
|
event_type: 'ATTD',
|
||||||
|
mail_list: [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
language: 'KO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
language: 'EN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
language: 'JA',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
item_list: [],
|
||||||
|
guid: '',
|
||||||
|
}); //데이터 정보
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (checkCondition()) {
|
||||||
|
setIsNullValue(false);
|
||||||
|
} else {
|
||||||
|
setIsNullValue(true);
|
||||||
|
}
|
||||||
|
}, [resultData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setItemCheckMsg('');
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
|
const combineDateTime = (date, hour, min) => {
|
||||||
|
if (!date) return null;
|
||||||
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hour, min);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 날짜 처리
|
||||||
|
const handleDateChange = (data, type) => {
|
||||||
|
const date = new Date(data);
|
||||||
|
setResultData({
|
||||||
|
...resultData,
|
||||||
|
[`${type}_dt`]: combineDateTime(date, time[`${type}_hour`], time[`${type}_min`]),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 시간 처리
|
||||||
|
const handleTimeChange = (e, type) => {
|
||||||
|
const { id, value } = e.target;
|
||||||
|
const newTime = { ...time, [`${type}_${id}`]: value };
|
||||||
|
setTime(newTime);
|
||||||
|
|
||||||
|
const date = resultData[`${type}_dt`] ? resultData[`${type}_dt`] : new Date();
|
||||||
|
|
||||||
|
setResultData({
|
||||||
|
...resultData,
|
||||||
|
[`${type}_dt`]: combineDateTime(date, newTime[`${type}_hour`], newTime[`${type}_min`]),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 수량 숫자 체크
|
||||||
|
const handleItemCount = e => {
|
||||||
|
if (e.target.value === '0' || e.target.value === '-0') {
|
||||||
|
setItemCount('1');
|
||||||
|
e.target.value = '1';
|
||||||
|
} else if (e.target.value < 0) {
|
||||||
|
let plusNum = Math.abs(e.target.value);
|
||||||
|
setItemCount(plusNum);
|
||||||
|
} else {
|
||||||
|
setItemCount(e.target.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 아이템 추가
|
||||||
|
const handleItemList = async () => {
|
||||||
|
if(benItems.includes(item)){
|
||||||
|
showToast('MAIL_ITEM_ADD_BEN', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(item.length === 0 || itemCount.length === 0) return;
|
||||||
|
|
||||||
|
const token = sessionStorage.getItem('token');
|
||||||
|
const result = await EventIsItem(token, {item: item});
|
||||||
|
|
||||||
|
if(result.data.result === "ERROR"){
|
||||||
|
setItemCheckMsg(t('NOT_ITEM'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemIndex = resultData.item_list.findIndex((data) => data.item === item);
|
||||||
|
if (itemIndex !== -1) {
|
||||||
|
showToast('MAIL_ITEM_ADD_DUPL', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newItem = { item: item, item_cnt: itemCount, item_name: result.data.data.item_info.item_name };
|
||||||
|
|
||||||
|
resultData.item_list.push(newItem);
|
||||||
|
setItem('');
|
||||||
|
setItemCount('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 추가된 아이템 삭제
|
||||||
|
const onItemRemove = id => {
|
||||||
|
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
|
||||||
|
setResultData({ ...resultData, item_list: filterList });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 입력창 삭제
|
||||||
|
const onLangDelete = language => {
|
||||||
|
let filterList = resultData.mail_list && resultData.mail_list.filter(el => el.language !== language);
|
||||||
|
|
||||||
|
if (filterList.length === 1) {
|
||||||
|
setBtnValidation(true);
|
||||||
|
} else {
|
||||||
|
setBtnValidation(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setResultData({ ...resultData, mail_list: filterList });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자원 수량 숫자 체크
|
||||||
|
const handleResourceCount = e => {
|
||||||
|
if (e.target.value === '0' || e.target.value === '-0') {
|
||||||
|
setResourceCount('1');
|
||||||
|
e.target.value = '1';
|
||||||
|
} else if (e.target.value < 0) {
|
||||||
|
let plusNum = Math.abs(e.target.value);
|
||||||
|
setResourceCount(plusNum);
|
||||||
|
} else {
|
||||||
|
setResourceCount(e.target.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자원 추가
|
||||||
|
const handleResourceList = (e) => {
|
||||||
|
if(resource.length === 0 || resourceCount.length === 0) return;
|
||||||
|
|
||||||
|
const itemIndex = resultData.item_list.findIndex(
|
||||||
|
(item) => item.item === resource
|
||||||
|
);
|
||||||
|
|
||||||
|
if (itemIndex !== -1) {
|
||||||
|
const item_cnt = resultData.item_list[itemIndex].item_cnt;
|
||||||
|
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
|
||||||
|
} else {
|
||||||
|
const name = currencyItemCode.find(well => well.value === resource).name;
|
||||||
|
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
|
||||||
|
resultData.item_list.push(newItem);
|
||||||
|
}
|
||||||
|
setResourceCount('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (type, param = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case "submit":
|
||||||
|
if (!checkCondition()) return;
|
||||||
|
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date))
|
||||||
|
if(timeDiff < 60) {
|
||||||
|
showToast('EVENT_TIME_LIMIT_ADD', {type: alertTypes.warning});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showModal('', {
|
||||||
|
type: alertTypes.confirmChildren,
|
||||||
|
onConfirm: () => handleSubmit('registConfirm'),
|
||||||
|
children: <ModalItem>
|
||||||
|
{t('EVENT_REGIST_CONFIRM')}
|
||||||
|
{resultData.item_list && (
|
||||||
|
<ModalItemList>
|
||||||
|
{resultData.item_list.map((data, index) => {
|
||||||
|
return (
|
||||||
|
<Item key={index}>
|
||||||
|
<span>
|
||||||
|
{data.item_name} {data.item_cnt.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
</Item>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ModalItemList>
|
||||||
|
)}
|
||||||
|
</ModalItem>
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "registConfirm":
|
||||||
|
await withLoading(async () => {
|
||||||
|
return await RewardEventSingleRegist(token, resultData);
|
||||||
|
}).then((result) => {
|
||||||
|
showToast('REGIST_COMPLTE', {type: alertTypes.success});
|
||||||
|
}).catch(() => {
|
||||||
|
showToast('API_FAIL', {type: alertTypes.error});
|
||||||
|
}).finally(() => {
|
||||||
|
callbackPage();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbackPage = () => {
|
||||||
|
navigate('/servicemanage/rewardevent');
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCondition = () => {
|
||||||
|
return (
|
||||||
|
resultData.mail_list.every(data => data.content !== '' && data.title !== '') &&
|
||||||
|
(resultData.start_dt.length !== 0) &&
|
||||||
|
(resultData.end_dt.length !== 0)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatedPageWrapper>
|
||||||
|
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) ? (
|
||||||
|
<AuthModal/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Title>이벤트 등록</Title>
|
||||||
|
<RegistGroup>
|
||||||
|
<RegistInputRow>
|
||||||
|
<RegistInputItem>
|
||||||
|
<InputLabel>이벤트 타입</InputLabel>
|
||||||
|
<SelectInput onChange={e => setResultData({ ...resultData, event_type: e.target.value })} value={resultData.event_type}>
|
||||||
|
<option value="ATTD">출석 이벤트</option>
|
||||||
|
</SelectInput>
|
||||||
|
</RegistInputItem>
|
||||||
|
<DateTimeInput
|
||||||
|
title="시작 시간"
|
||||||
|
dateName="시작 일자"
|
||||||
|
selectedDate={resultData.start_dt}
|
||||||
|
handleSelectedDate={data => handleDateChange(data, 'start')}
|
||||||
|
onChange={e => handleTimeChange(e, 'start')}
|
||||||
|
/>
|
||||||
|
<DateTimeInput
|
||||||
|
title="종료 시간"
|
||||||
|
dateName="종료 일자"
|
||||||
|
selectedDate={resultData.end_dt}
|
||||||
|
handleSelectedDate={data => handleDateChange(data, 'end')}
|
||||||
|
onChange={e => handleTimeChange(e, 'end')}
|
||||||
|
/>
|
||||||
|
</RegistInputRow>
|
||||||
|
</RegistGroup>
|
||||||
|
{resultData.mail_list.map((data, idx) => {
|
||||||
|
return (
|
||||||
|
<Fragment key={idx}>
|
||||||
|
<AppendRegistBox>
|
||||||
|
<LangArea>
|
||||||
|
언어 : {data.language}
|
||||||
|
{btnValidation === false ? (
|
||||||
|
<AreaBtnClose
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
onLangDelete(data.language);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<AreaBtnClose opacity="10%" />
|
||||||
|
)}
|
||||||
|
</LangArea>
|
||||||
|
<RegistTable>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th width="120">
|
||||||
|
<Label>제목</Label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<RegistInputItem>
|
||||||
|
<TextInput
|
||||||
|
placeholder="우편 제목 입력"
|
||||||
|
maxLength="30"
|
||||||
|
id={data.language}
|
||||||
|
value={data.title}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.value.length > 30) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let list = [...resultData.mail_list];
|
||||||
|
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
|
||||||
|
list[findIndex].title = e.target.value.trimStart();
|
||||||
|
|
||||||
|
setResultData({ ...resultData, mail_list: list });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</RegistInputItem>
|
||||||
|
<RegistNotice $color={data.title.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.title.length}/30자)</RegistNotice>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<Label>내용</Label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<Textarea
|
||||||
|
maxLength="2000"
|
||||||
|
value={data.content}
|
||||||
|
id={data.language}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.value.length > 2000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let list = [...resultData.mail_list];
|
||||||
|
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
|
||||||
|
list[findIndex].content = e.target.value.trimStart();
|
||||||
|
|
||||||
|
setResultData({ ...resultData, mail_list: list });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RegistNotice $color={data.content.length > 1999 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.content.length}/2000자)</RegistNotice>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</RegistTable>
|
||||||
|
</AppendRegistBox>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<AppendRegistBox>
|
||||||
|
<AppendRegistTable>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th width="120">
|
||||||
|
<Label>아이템 첨부</Label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<RegistInputItem>
|
||||||
|
<TextInput placeholder="Item Meta id 입력" value={item} onChange={e => setItem(e.target.value.trimStart())} />
|
||||||
|
<TextInput placeholder="수량" type="number" value={itemCount} onChange={e => handleItemCount(e)} width="100px" />
|
||||||
|
<Button text="추가" theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'} handleClick={handleItemList} width="100px" height="35px" />
|
||||||
|
{itemCheckMsg && <SearchBarAlert>{itemCheckMsg}</SearchBarAlert>}
|
||||||
|
</RegistInputItem>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th width="120">
|
||||||
|
<Label>자원 첨부</Label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<RegistInputItem>
|
||||||
|
<SelectInput onChange={e => setResource(e.target.value)} value={resource}>
|
||||||
|
{currencyItemCode.filter(data => data.value !== currencyCodeTypes.calium).map((data, index) => (
|
||||||
|
<option key={index} value={data.value}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</SelectInput>
|
||||||
|
<TextInput placeholder="수량" type="number" value={resourceCount} onChange={e => handleResourceCount(e)} width="200px" />
|
||||||
|
<Button text="추가" theme={resourceCount.length === 0 || resource.length === 0 ? 'disable' : 'search'} handleClick={handleResourceList} width="100px" height="35px" />
|
||||||
|
</RegistInputItem>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{resultData.item_list && (
|
||||||
|
<ItemList>
|
||||||
|
{resultData.item_list.map((data, index) => {
|
||||||
|
return (
|
||||||
|
<Item key={index}>
|
||||||
|
<span>
|
||||||
|
{data.item_name}[{data.item}] ({data.item_cnt})
|
||||||
|
</span>
|
||||||
|
<BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>
|
||||||
|
</Item>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ItemList>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</AppendRegistTable>
|
||||||
|
</AppendRegistBox>
|
||||||
|
{isNullValue && (
|
||||||
|
<SearchBarAlert $align="right" $padding="0 0 15px">
|
||||||
|
{t('NULL_MSG')}
|
||||||
|
</SearchBarAlert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<BtnWrapper $justify="flex-end" $gap="10px">
|
||||||
|
<Button
|
||||||
|
text="취소"
|
||||||
|
theme="line"
|
||||||
|
handleClick={() => showModal('EVENT_REGIST_CANCEL', {
|
||||||
|
type: alertTypes.confirm,
|
||||||
|
onConfirm: () => callbackPage()
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text="등록"
|
||||||
|
theme={checkCondition() ? 'primary' : 'disable'}
|
||||||
|
handleClick={() => handleSubmit('submit')}
|
||||||
|
/>
|
||||||
|
</BtnWrapper>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</AnimatedPageWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RewardEventRegist;
|
||||||
|
|
||||||
Reference in New Issue
Block a user