diff --git a/src/apis/Event.js b/src/apis/Event.js
index bbf9e33..d532145 100644
--- a/src/apis/Event.js
+++ b/src/apis/Event.js
@@ -1,13 +1,13 @@
-//운영서비스 관리 - 이벤트 api 연결
+//운영서비스 관리 - 통합 이벤트 api 연결
import { Axios } from '../utils';
// 이벤트 리스트 조회
-export const EventView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
+export const EventView = async (token, searchData, status, startDate, endDate, order, size, currentPage) => {
try {
const res = await Axios.get(
- `/api/v1/event/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
- &page_size=${size}`,
+ `/api/v1/world-event/list?search_data=${searchData}&status=${status}&start_dt=${startDate}&end_dt=${endDate}
+ &orderby=${order}&page_no=${currentPage}&page_size=${size}`,
{
headers: { Authorization: `Bearer ${token}` },
},
@@ -24,11 +24,11 @@ export const EventView = async (token, title, content, status, startDate, endDat
// 이벤트 상세보기
export const EventDetailView = async (token, id) => {
try {
- const res = await Axios.get(`/api/v1/event/detail/${id}`, {
+ const res = await Axios.get(`/api/v1/world-event/detail/${id}`, {
headers: { Authorization: `Bearer ${token}` },
});
- return res.data.data.detail;
+ return res.data.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventDetailView Error', e);
@@ -39,11 +39,11 @@ export const EventDetailView = async (token, id) => {
// 이벤트 등록
export const EventSingleRegist = async (token, params) => {
try {
- const res = await Axios.post(`/api/v1/event`, params, {
+ const res = await Axios.post(`/api/v1/world-event`, params, {
headers: { Authorization: `Bearer ${token}` },
});
- return res;
+ return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventSingleRegist Error', e);
@@ -51,10 +51,10 @@ export const EventSingleRegist = async (token, params) => {
}
};
-// 우편 수정
+// 이벤트 수정
export const EventModify = async (token, id, params) => {
try {
- const res = await Axios.put(`/api/v1/event/${id}`, params, {
+ const res = await Axios.put(`/api/v1/world-event/${id}`, params, {
headers: { Authorization: `Bearer ${token}` },
});
@@ -66,15 +66,14 @@ export const EventModify = async (token, id, params) => {
}
};
-// 우편 삭제
-export const EventDelete = async (token, params, id) => {
+// 이벤트 삭제
+export const EventDelete = async (token, id) => {
try {
- const res = await Axios.delete(`/api/v1/event/delete`, {
- headers: { Authorization: `Bearer ${token}` },
- data: { list: params },
+ const res = await Axios.delete(`/api/v1/world-event/delete?id=${id}`, {
+ headers: { Authorization: `Bearer ${token}` }
});
- return res.data.data.list;
+ return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('EventDelete Error', e);
@@ -82,17 +81,20 @@ export const EventDelete = async (token, params, id) => {
}
};
-// 이벤트 우편 아이템 확인
-export const EventIsItem = async (token, params) => {
+// 이벤트 메타데이터 조회
+export const EventActionView = async (token) => {
try {
- const res = await Axios.post(`/api/v1/event/item`, params, {
- headers: { Authorization: `Bearer ${token}` },
- });
+ const res = await Axios.get(
+ `/api/v1/dictionary/event-action/list`,
+ {
+ headers: { Authorization: `Bearer ${token}` },
+ },
+ );
- return res;
+ return res.data.data.event_action_list;
} catch (e) {
if (e instanceof Error) {
- throw new Error('EventIsItem Error', e);
+ throw new Error('EventActionView Error', e);
}
}
};
\ No newline at end of file
diff --git a/src/apis/RewardEvent.js b/src/apis/RewardEvent.js
new file mode 100644
index 0000000..65d7af2
--- /dev/null
+++ b/src/apis/RewardEvent.js
@@ -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);
+ }
+ }
+};
\ No newline at end of file
diff --git a/src/assets/data/apis/eventAPI.json b/src/assets/data/apis/eventAPI.json
new file mode 100644
index 0000000..d7dd13c
--- /dev/null
+++ b/src/assets/data/apis/eventAPI.json
@@ -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"]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/assets/data/pages/eventSearch.json b/src/assets/data/pages/eventSearch.json
index fcd5d3e..9731c1c 100644
--- a/src/assets/data/pages/eventSearch.json
+++ b/src/assets/data/pages/eventSearch.json
@@ -1,7 +1,6 @@
{
"initialSearchParams": {
- "searchTitle": "",
- "searchContent": "",
+ "searchData": "",
"status": "ALL",
"startDate": "",
"endDate": "",
@@ -13,51 +12,37 @@
"searchFields": [
{
"type": "text",
- "id": "searchTitle",
- "label": "우편 제목",
+ "id": "searchData",
+ "label": "제목",
"placeholder": "제목 입력",
"width": "300px",
"col": 1
},
- {
- "type": "period",
- "startDateId": "startDate",
- "endDateId": "endDate",
- "label": "조회 일자",
- "col": 1
- },
- {
- "type": "text",
- "id": "searchContent",
- "label": "우편 내용",
- "placeholder": "우편 내용(공백으로 구분)",
- "width": "300px",
- "col": 1
- },
{
"type": "select",
"id": "status",
- "label": "이벤트 상태",
- "optionsRef": "eventStatus",
- "col": 2
+ "label": "상태",
+ "optionsRef": "opMenuBannerStatus",
+ "col": 1
+ },
+ {
+ "type": "period",
+ "startDateId": "startDate",
+ "endDateId": "endDate",
+ "label": "기간",
+ "col": 1
}
],
"apiInfo": {
- "functionName": "EventView",
+ "endpointName": "EventView",
"loadOnMount": true,
- "paramsMapping": [
- "searchTitle",
- "searchContent",
- "status",
+ "paramTransforms": [
{"param": "startDate", "transform": "toISOString"},
- {"param": "endDate", "transform": "toISOString"},
- "orderBy",
- "pageSize",
- "currentPage"
+ {"param": "endDate", "transform": "toISOString"}
],
- "pageField": "currentPage",
- "pageSizeField": "pageSize",
+ "pageField": "page_no",
+ "pageSizeField": "page_size",
"orderField": "orderBy"
}
}
\ No newline at end of file
diff --git a/src/assets/data/pages/eventTable.json b/src/assets/data/pages/eventTable.json
new file mode 100644
index 0000000..368dda0
--- /dev/null
+++ b/src/assets/data/pages/eventTable.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/src/assets/data/pages/rewardEventSearch.json b/src/assets/data/pages/rewardEventSearch.json
new file mode 100644
index 0000000..d4d297c
--- /dev/null
+++ b/src/assets/data/pages/rewardEventSearch.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/src/components/modal/EventModal.js b/src/components/modal/EventModal.js
new file mode 100644
index 0000000..cf77ca2
--- /dev/null
+++ b/src/components/modal/EventModal.js
@@ -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 (
+ <>
+
+ {isView('registry') ? "통합 이벤트 등록" : isView('modify') ? "통합 이벤트 수정" : "통합 이벤트 상세"}
+
+ {!isView() && isNullValue && {t('REQUIRED_VALUE_CHECK')}}
+
+
+
+ 현재상태: {opCommonStatus.find(data => data.value === content?.status)?.name || "등록"}
+
+
+ {isView('registry') ? '' : t('EVENT_MODAL_STATUS_WARNING')}
+
+
+
+ {isView() ?
+
+
+
+ >
+ );
+};
+
+export const initData = {
+ title: '',
+ start_dt: '',
+ end_dt: '',
+ global_event_action_id: '',
+ personal_event_action_id: '',
+ max_point: 0
+}
+
+export default EventModal;
+
diff --git a/src/components/modal/RewardEventDetailModal.js b/src/components/modal/RewardEventDetailModal.js
new file mode 100644
index 0000000..7300195
--- /dev/null
+++ b/src/components/modal/RewardEventDetailModal.js
@@ -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: (
+
+
+
+
+
+ updateMailData(mail.language, 'title', e.target.value.trimStart())}
+ showCount
+ size="large"
+ />
+
+
+
+
+
+ {
+ if (e.target.value.length > 2000) return;
+ updateMailData(mail.language, 'content', e.target.value.trimStart());
+ }}
+ style={{
+ resize: 'vertical',
+ minHeight: '200px',
+ maxHeight: '400px'
+ }}
+ />
+
+
+
+
+ ),
+ 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 대기;
+ case commonStatus.running:
+ return 진행중;
+ case commonStatus.finish:
+ return 완료;
+ case commonStatus.fail:
+ return 실패;
+ case commonStatus.delete:
+ return 삭제;
+ default:
+ return null;
+ }
+ };
+
+ // 아이템 목록 렌더링 컴포넌트
+ const renderItemList = () => {
+ return (
+
+ {resultData.item_list && resultData.item_list.length > 0 && (
+
+ {resultData.item_list.map((data, index) => (
+ onItemRemove(index)}
+ >
+ X
+
+ )
+ }
+ style={{ minWidth: '150px' }}
+ >
+
+
{data.item}
+
수량: {data.item_cnt}
+
+
+ ))}
+
+ )}
+
+ );
+ };
+
+ // 아이템 추가 컴포넌트
+ const renderItemAdd = () => {
+ return (
+
+ setItem(e.target.value.trimStart())}
+ disabled={isReadOnly}
+ style={{ width: '200px' }}
+ />
+ setItemCount(e.target.value)}
+ disabled={isReadOnly}
+ style={{ width: '120px' }}
+ min={1}
+ />
+
+ 추가
+
+
+ );
+ };
+
+ // 자원 추가 컴포넌트
+ const renderResourceAdd = () => {
+ return (
+
+
+ setResourceCount(e.target.value)}
+ style={{ width: '120px' }}
+ min={1}
+ />
+
+ 추가
+
+
+ );
+ };
+
+ 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: () =>
+ }]),
+ {
+ 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 (
+ <>
+
+ 이벤트 상세 정보
+ {content &&
+
+ 등록자 : {content.create_by}
+ 등록일 : {convertKTC(content.create_dt, false)}
+ {typeof content.update_by !== 'undefined' && (
+ <>
+ 수정자 : {content.update_by}
+ 수정일 : {convertKTC(content.update_dt, false)}
+ >
+ )}
+
+ }
+
+
+
+ {itemCheckMsg && (
+
+ )}
+
+ {
+ handleDetailView();
+ handleReset();
+ setDetailData('');
+ }}
+ />
+ {!isReadOnly && (
+ handleSubmit('submit')}
+ />
+ )}
+
+
+ >
+ );
+};
+
+export default RewardEventDetailModal;
+
+
diff --git a/src/pages/ServiceManage/Event.js b/src/pages/ServiceManage/Event.js
index 144e48c..a513cf6 100644
--- a/src/pages/ServiceManage/Event.js
+++ b/src/pages/ServiceManage/Event.js
@@ -1,52 +1,43 @@
-import React, { useState, Fragment } from 'react';
+import React, { useState, useRef } from 'react';
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 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 tableInfo from '../../assets/data/pages/eventTable.json'
+
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';
+ authType, commonStatus as CommonStatus,
+} from '../../assets/data';
+import { Title, FormWrapper} from '../../styles/Components';
+import {
+ Pagination,
+ CaliTable, TableHeader,
+} from '../../components/common';
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
+import { useDataFetch, useModal, useTable, withAuth } from '../../hooks/hook';
+import {
+ EventActionView, EventDelete,
+ EventDetailView,
+ LogHistory,
+} from '../../apis';
+import { CommonSearchBar } from '../../components/ServiceManage';
+import { useAlert } from '../../context/AlertProvider';
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 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 token = sessionStorage.getItem('token');
- const userInfo = useRecoilValue(authList);
- const { t } = useTranslation();
- const {withLoading} = useLoading();
- const {showToast} = useAlert();
-
+ const tableRef = useRef(null);
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 [modalType, setModalType] = useState('regist');
const {
modalState,
@@ -54,10 +45,8 @@ const Event = () => {
handleModalClose
} = useModal({
detail: 'hidden',
- delete: 'hidden',
- history: 'hidden'
+ history: 'hidden',
});
- const [deleteDesc, setDeleteDesc] = useState('');
const {
config,
@@ -65,35 +54,34 @@ const Event = () => {
data: dataList,
handleSearch,
handleReset,
- handlePageChange,
- handlePageSizeChange,
handleOrderByChange,
updateSearchParams,
- configLoaded
- } = useCommonSearchOld("eventSearch");
+ loading,
+ configLoaded,
+ handlePageChange,
+ handlePageSizeChange
+ } = useEnhancedCommonSearch("eventSearch");
+
+ const {
+ data: eventActionData
+ } = useDataFetch(() => EventActionView(token), [token]);
+
const {
selectedRows,
handleSelectRow,
isRowSelected
} = useTable(dataList?.list || [], {mode: 'single'});
- // 상세보기 호출
- const handleDetailModal = async (e, id) => {
- await EventDetailView(token, id).then(data => {
- setDetailData(data);
- });
-
- e.preventDefault();
- handleModalView('detail');
- };
-
-
- const handleModalSubmit = async (type, param = null) => {
- switch (type) {
+ const handleAction = async (action, item = null) => {
+ switch (action) {
+ case "regist":
+ setModalType('regist');
+ handleModalView('detail');
+ break;
case "history":
const params = {};
params.db_type = "MYSQL"
- params.sql_id = param.id;
+ params.sql_id = item.id;
params.table_name = historyTables.event
await LogHistory(token, params).then(data => {
@@ -101,191 +89,124 @@ const Event = () => {
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);
+ case "detail":
+ await EventDetailView(token, item.id).then(data => {
+ setDetailData(data.detail);
+ setModalType('modify');
+ handleModalView('detail');
});
- if(delete_check){
- showToast('EVENT_TIME_LIMIT_UPDATE', {type: alertTypes.warning});
- return;
- }
+ break;
+ case "delete":
- handleModalView('delete');
+ showModal('EVENT_SELECT_DELETE', {
+ type: alertTypes.confirm,
+ onConfirm: () => handleAction('deleteConfirm')
+ });
break;
case "deleteConfirm":
- let list = [];
+ const low = selectedRows[0];
- 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});
+ if(low.status !== CommonStatus.wait) {
+ showToast('DELETE_STATUS_ONLY_WAIT', {type: alertTypes.warning});
return;
}
await withLoading(async () => {
- return await EventDelete(token, list);
+ return await EventDelete(token, low.id);
}).then(data => {
- showToast('DEL_COMPLETE', {type: alertTypes.success});
- }).catch(error => {
-
+ if(data.result === "SUCCESS") {
+ showToast('DEL_COMPLETE', {type: alertTypes.success});
+ }else{
+ showToast('DELETE_FAIL', {type: alertTypes.error});
+ }
+ }).catch(reason => {
+ showToast('API_FAIL', {type: alertTypes.error});
}).finally(() => {
handleSearch(updateSearchParams);
- })
+ });
+ break;
+ default:
break;
}
- }
+ };
return (
- <>
- {userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventRead) ? (
-
- ) : (
-
- 출석 보상 이벤트 관리
-
- {
- if (executeSearch) {
- handleSearch(newParams);
- } else {
- updateSearchParams(newParams);
- }
- }}
- onReset={handleReset}
- />
-
-
- {userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventDelete) && (
- handleModalSubmit('delete')} />
- )}
- {userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
- {
- e.preventDefault();
- navigate('/servicemanage/event/eventregist');
- }}
- />
- )}
-
-
-
-
-
-
- |
- |
- 번호 |
- 이벤트 상태 |
- 시작 일시 |
- 종료 일시 |
- 우편 제목 |
- 확인 / 수정 |
- 히스토리 |
-
-
-
- {dataList?.list?.map(event => (
-
-
- |
- handleSelectRow(e, event)}
- checked={isRowSelected(event.id)} />
- |
- {event.row_num} |
-
-
- {eventStatus.map(data => data.value === event.status && data.name)}
-
-
- {convertKTC(event.start_dt)} |
- {convertKTC(event.end_dt)} |
- {event.title}
-
- handleDetailModal(e, event.id)} />
- |
- handleModalSubmit('history', event)} />
- |
-
-
- ))}
-
-
-
+
+ 통합 이벤트 관리
-
+ {/* 조회조건 */}
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
- {/*상세*/}
- {
- handleModalClose('detail');
- handleSearch(updateSearchParams);
- }}
- content={detailData}
- setDetailData={setDetailData}
- />
+ {/* 조회헤더 */}
+
- handleModalClose('history')}
- changedData={historyData}
- title="히스토리"
- />
+ {/* 조회테이블 */}
+
- handleModalClose('delete')}
- handleSubmit={() => handleModalSubmit('deleteConfirm')}
- >
-
- {t('EVENT_SELECT_DELETE')}
-
- {
- if (e.target.value.length > 30) return;
- setDeleteDesc(e.target.value.trimStart())
- }}
- />
-
- 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30자)
-
-
-
- )}
- >
- );
+ {/* 페이징 */}
+
+
+ {/* 상세 */}
+ {
+ handleModalClose('detail');
+ handleSearch(updateSearchParams);
+ }}
+ content={detailData}
+ setDetailData={setDetailData}
+ eventActionData={eventActionData}
+ />
+
+ handleModalClose('history')}
+ changedData={historyData}
+ title="히스토리"
+ />
+
+
+ )
};
-export default Event;
+export default withAuth(authType.worldEventRead)(Event);
diff --git a/src/pages/ServiceManage/RewardEvent.js b/src/pages/ServiceManage/RewardEvent.js
new file mode 100644
index 0000000..793966d
--- /dev/null
+++ b/src/pages/ServiceManage/RewardEvent.js
@@ -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) ? (
+
+ ) : (
+
+ 출석 보상 이벤트 관리
+
+ {
+ if (executeSearch) {
+ handleSearch(newParams);
+ } else {
+ updateSearchParams(newParams);
+ }
+ }}
+ onReset={handleReset}
+ />
+
+
+ {userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventDelete) && (
+ handleModalSubmit('delete')} />
+ )}
+ {userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
+ {
+ e.preventDefault();
+ navigate('/servicemanage/rewardevent/eventregist');
+ }}
+ />
+ )}
+
+
+
+
+
+
+ |
+ |
+ 번호 |
+ 이벤트 상태 |
+ 시작 일시 |
+ 종료 일시 |
+ 우편 제목 |
+ 확인 / 수정 |
+ 히스토리 |
+
+
+
+ {dataList?.list?.map(event => (
+
+
+ |
+ handleSelectRow(e, event)}
+ checked={isRowSelected(event.id)} />
+ |
+ {event.row_num} |
+
+
+ {eventStatus.map(data => data.value === event.status && data.name)}
+
+
+ {convertKTC(event.start_dt)} |
+ {convertKTC(event.end_dt)} |
+ {event.title}
+
+ handleDetailModal(e, event.id)} />
+ |
+ handleModalSubmit('history', event)} />
+ |
+
+
+ ))}
+
+
+
+
+
+
+ {/*상세*/}
+ {
+ handleModalClose('detail');
+ handleSearch(updateSearchParams);
+ }}
+ content={detailData}
+ setDetailData={setDetailData}
+ />
+
+ handleModalClose('history')}
+ changedData={historyData}
+ title="히스토리"
+ />
+
+ handleModalClose('delete')}
+ handleSubmit={() => handleModalSubmit('deleteConfirm')}
+ >
+
+ {t('EVENT_SELECT_DELETE')}
+
+ {
+ if (e.target.value.length > 30) return;
+ setDeleteDesc(e.target.value.trimStart())
+ }}
+ />
+
+ 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30자)
+
+
+
+ )}
+ >
+ );
+};
+
+export default RewardEvent;
diff --git a/src/pages/ServiceManage/RewardEventRegist.js b/src/pages/ServiceManage/RewardEventRegist.js
new file mode 100644
index 0000000..8e74e30
--- /dev/null
+++ b/src/pages/ServiceManage/RewardEventRegist.js
@@ -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:
+ {t('EVENT_REGIST_CONFIRM')}
+ {resultData.item_list && (
+
+ {resultData.item_list.map((data, index) => {
+ return (
+ -
+
+ {data.item_name} {data.item_cnt.toLocaleString()}
+
+
+ );
+ })}
+
+ )}
+
+ });
+ 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 (
+
+ {userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) ? (
+
+ ) : (
+ <>
+ 이벤트 등록
+
+
+
+ 이벤트 타입
+ setResultData({ ...resultData, event_type: e.target.value })} value={resultData.event_type}>
+
+
+
+ handleDateChange(data, 'start')}
+ onChange={e => handleTimeChange(e, 'start')}
+ />
+ handleDateChange(data, 'end')}
+ onChange={e => handleTimeChange(e, 'end')}
+ />
+
+
+ {resultData.mail_list.map((data, idx) => {
+ return (
+
+
+
+ 언어 : {data.language}
+ {btnValidation === false ? (
+ {
+ e.preventDefault();
+ onLangDelete(data.language);
+ }}
+ />
+ ) : (
+
+ )}
+
+
+
+
+ |
+
+ |
+
+
+ {
+ 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 });
+ }}
+ />
+
+ 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.title.length}/30자)
+ |
+
+
+ |
+
+ |
+
+ |
+
+
+
+
+
+ );
+ })}
+
+
+
+
+
+ |
+
+ |
+
+
+ setItem(e.target.value.trimStart())} />
+ handleItemCount(e)} width="100px" />
+
+ {itemCheckMsg && {itemCheckMsg}}
+
+ |
+
+
+ |
+
+ |
+
+
+ setResource(e.target.value)} value={resource}>
+ {currencyItemCode.filter(data => data.value !== currencyCodeTypes.calium).map((data, index) => (
+
+ ))}
+
+ handleResourceCount(e)} width="200px" />
+
+
+
+
+ {resultData.item_list && (
+
+ {resultData.item_list.map((data, index) => {
+ return (
+ -
+
+ {data.item_name}[{data.item}] ({data.item_cnt})
+
+ onItemRemove(index)}>
+
+ );
+ })}
+
+ )}
+
+ |
+
+
+
+
+ {isNullValue && (
+
+ {t('NULL_MSG')}
+
+ )}
+
+
+ showModal('EVENT_REGIST_CANCEL', {
+ type: alertTypes.confirm,
+ onConfirm: () => callbackPage()
+ })}
+ />
+ handleSubmit('submit')}
+ />
+
+ >
+ )}
+
+ );
+};
+
+export default RewardEventRegist;
+