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() ? +