Compare commits

...

8 Commits

Author SHA1 Message Date
64bf449de7 이용자 제재 해지 사유 입력 2025-05-15 17:50:01 +09:00
9be5bb388a data 정보 수정
우편 내용 복사 기능
우편 코드 정리
유저 인벤토리 아이템 삭제 제거
2025-05-15 17:49:45 +09:00
d219a128bc api result 부분 수정 2025-05-15 17:43:20 +09:00
ce2f3db35c 닉네임 변경 처리 수정 2025-05-12 17:06:11 +09:00
b5efab7755 조회조건 필터 랜더링에 이슈로 컴포넌트 분리작업 2025-05-12 10:46:13 +09:00
1cc20b65e2 이용자제재 수정 2025-05-12 10:44:35 +09:00
0b85232969 전투이벤트 등록 알림 비동기에따른 state 초기화 부분 수정 2025-05-12 10:43:57 +09:00
4291d7976c 이용자제재 엑셀 업로드 처리 수정
이용자제재 상세 표시 부분 수정
2025-05-12 10:43:24 +09:00
34 changed files with 751 additions and 973 deletions

View File

@@ -52,7 +52,7 @@ export const BlackListDelete = async (token, params) => {
data: { list: params }, data: { list: params },
}); });
return res; return res.data;
} catch (e) { } catch (e) {
if (e instanceof Error) { if (e instanceof Error) {
throw new Error('BlackListDelete', e); throw new Error('BlackListDelete', e);

View File

@@ -58,7 +58,7 @@ export const EventModify = async (token, id, params) => {
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
}); });
return res.data.data.list; return res.data;
} catch (e) { } catch (e) {
if (e instanceof Error) { if (e instanceof Error) {
throw new Error('EventModify Error', e); throw new Error('EventModify Error', e);

View File

@@ -72,7 +72,7 @@ export const MailModify = async (token, id, params) => {
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
}); });
return res.data.data.list; return res.data;
} catch (e) { } catch (e) {
if (e instanceof Error) { if (e instanceof Error) {
throw new Error('MailModify Error', e); throw new Error('MailModify Error', e);
@@ -88,7 +88,7 @@ export const MailDelete = async (token, params, id) => {
data: { list: params }, 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('MailDelete Error', e); throw new Error('MailDelete Error', e);
@@ -147,7 +147,7 @@ export const MailIsItem = async (token, 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('MailItemCheck Error', e); throw new Error('MailItemCheck Error', e);

View File

@@ -42,7 +42,7 @@ export const UserChangeNickName = async (token, 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('UserChangeNickName Error', e); throw new Error('UserChangeNickName Error', e);

View File

@@ -7,14 +7,14 @@ export {
adminLevelType, adminLevelType,
logOption, logOption,
eventStatus, eventStatus,
wellType, currencyType,
blockStatus, blockStatus,
blockSanctions, blockSanctions,
blockPeriod, blockPeriod,
blockType, blockType,
caliumStatus, caliumStatus,
landSize, landSize,
userSearchType, userType,
landAuctionStatus, landAuctionStatus,
landSearchType, landSearchType,
CurrencyType, CurrencyType,

View File

@@ -81,7 +81,7 @@ export const landAuctionStatus = [
{ value: 'FAIL', name: '실패' }, { value: 'FAIL', name: '실패' },
]; ];
export const wellType = [ export const currencyType = [
{ value: '19010001', name: '골드' }, { value: '19010001', name: '골드' },
{ value: '19010002', name: '사파이어' }, { value: '19010002', name: '사파이어' },
{ value: '19010005', name: '루비' }, { value: '19010005', name: '루비' },
@@ -129,6 +129,7 @@ export const blockStatus = [
{ value: 'EXPIRATION', name: '기간만료' }, { value: 'EXPIRATION', name: '기간만료' },
{ value: 'WAIT', name: '대기 중' }, { value: 'WAIT', name: '대기 중' },
{ value: 'FAIL', name: '실패' }, { value: 'FAIL', name: '실패' },
{ value: 'CANCEL', name: '해지' },
]; ];
export const blockSanctions = [ export const blockSanctions = [
@@ -156,11 +157,16 @@ export const blockPeriod = [
{ value: 'PERMANENT', name: '영구정지' }, { value: 'PERMANENT', name: '영구정지' },
]; ];
export const userSearchType = [ export const userType = [
{ value: 'GUID', name: 'GUID' }, { value: 'GUID', name: 'GUID' },
{ value: 'NAME', name: '닉네임' }, { value: 'NAME', name: '닉네임' },
]; ];
export const userType2 = [
{ value: 'GUID', name: 'GUID' },
{ value: 'NICKNAME', name: '닉네임' },
];
export const userSearchType2 = [ export const userSearchType2 = [
{ value: 'GUID', name: 'GUID' }, { value: 'GUID', name: 'GUID' },
{ value: 'NICKNAME', name: '닉네임' }, { value: 'NICKNAME', name: '닉네임' },

View File

@@ -12,7 +12,7 @@
"type": "select", "type": "select",
"id": "searchType", "id": "searchType",
"label": "대상", "label": "대상",
"optionsRef": "userSearchType", "optionsRef": "userType",
"col": 1, "col": 1,
"required": true "required": true
}, },

View File

@@ -16,7 +16,7 @@
"type": "select", "type": "select",
"id": "searchType", "id": "searchType",
"label": "대상", "label": "대상",
"optionsRef": "userSearchType", "optionsRef": "userType",
"col": 1 "col": 1
}, },
{ {

View File

@@ -11,7 +11,7 @@
"buttons": [ "buttons": [
{ {
"id": "delete", "id": "delete",
"text": "선택 삭제", "text": "선택 해지",
"theme": "line", "theme": "line",
"disableWhen": "noSelection", "disableWhen": "noSelection",
"requiredAuth": "blackListDelete", "requiredAuth": "blackListDelete",

View File

@@ -119,6 +119,13 @@ export const commonStatus = {
delete: "DELETE", delete: "DELETE",
reject: "REJECT", reject: "REJECT",
complete: "COMPLETE", complete: "COMPLETE",
cancel: "CANCEL"
}
export const customStatus = {
inprogress: "INPROGRESS",
expiration: "EXPIRATION",
} }
export const ViewTitleCountType = { export const ViewTitleCountType = {

View File

@@ -2,16 +2,19 @@ import { styled } from 'styled-components';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Title } from '../../styles/Components'; import { Title } from '../../styles/Components';
import { TextInput, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components'; import { TextInput, BtnWrapper } from '../../styles/Components';
import Button from '../../components/common/button/Button'; import Button from '../../components/common/button/Button';
import Modal from '../../components/common/modal/Modal'; import Modal from '../../components/common/modal/Modal';
import { UserChangeNickName } from '../../apis'; import { UserChangeNickName } from '../../apis';
import { alertTypes } from '../../assets/data/types';
import { useAlert } from '../../context/AlertProvider';
import { useLoading } from '../../context/LoadingProvider';
const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => { const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
let nickName = dataList.char_info && dataList.char_info.character_name; let nickName = dataList.char_info && dataList.char_info.character_name;
const [modifyModal, setModifyModal] = useState('hidden');
const [completeModal, setCompleteModal] = useState('hidden');
const [completeText, setCompleteText] = useState('');
const [resultData, setResultData] = useState({ const [resultData, setResultData] = useState({
guid: '', guid: '',
@@ -20,42 +23,42 @@ const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
}); });
useEffect(() => { useEffect(() => {
setResultData({ ...resultData, guid: dataList.user_info && dataList.user_info.aid, nickname: dataList.char_info && dataList.char_info.character_name }); setResultData({ ...resultData,
guid: dataList.user_info && dataList.user_info.aid,
nickname: dataList.char_info && dataList.char_info.character_name
});
}, [dataList]); }, [dataList]);
// 수정 모달창 const handleSubmit = async (type, param = null) => {
const handleModifyModal = () => { switch (type) {
if (modifyModal === 'hidden') {
setModifyModal('view'); case "nicknameChangeSubmit":
} else { showModal('NICKNAME_CHANGES_CONFIRM', {
setModifyModal('hidden'); type: alertTypes.confirm,
onConfirm: () => handleSubmit('nicknameChange')
});
break;
case "nicknameChange":
const token = sessionStorage.getItem('token');
await withLoading(async () => {
return await UserChangeNickName(token, resultData);
}).then(data =>{
console.log(data);
if(data.result === 'ERROR'){
showToast(data.data.message, {type: alertTypes.error});
}else{
showToast('NICKNAME_CHANGES_COMPLETE', {type: alertTypes.success});
}
}).catch(error => {
showToast(error, {type: alertTypes.error});
}).finally(() => {
handleClick();
});
break;
} }
}; }
// 완료 모달창
const handleCompleteModal = () => {
if (completeModal === 'hidden') {
setCompleteModal('view');
} else {
setCompleteModal('hidden');
handleClick();
completeText === '변경이 완료되었습니다.' && window.location.reload();
}
};
// 수정
const handleModifyNotice = async () => {
const token = sessionStorage.getItem('token');
const message = await UserChangeNickName(token, resultData);
// console.log(message);
message.data.data.message !== '수정 하였습니다.' ? setCompleteText('변경 닉네임이 이미 존재합니다.\n다시 시도해주세요.') : setCompleteText('변경이 완료되었습니다.');
handleCompleteModal();
handleModifyModal();
};
return ( return (
<> <>
@@ -82,29 +85,7 @@ const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
</PwSetTable> </PwSetTable>
<BtnWrapper $justify="center" $gap="10px"> <BtnWrapper $justify="center" $gap="10px">
<Button theme="line" text="취소" handleClick={handleClick} /> <Button theme="line" text="취소" handleClick={handleClick} />
<Button theme="primary" text="변경하기" handleClick={handleModifyModal} /> <Button theme="primary" text="변경하기" handleClick={() => handleSubmit('nicknameChangeSubmit')} />
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={modifyModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleModifyModal} />
</BtnWrapper>
<ModalText $align="center">닉네임을 변경하시겠습니까?</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleModifyModal} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleModifyNotice} />
</BtnWrapper>
</Modal>
{/* 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModal} />
</BtnWrapper>
<ModalText $align="center">{completeText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
</BtnWrapper> </BtnWrapper>
</Modal> </Modal>
</> </>

View File

@@ -193,10 +193,10 @@ const UserDefaultInfo = ({ userInfo }) => {
<td colSpan="3"> <td colSpan="3">
{dataList.char_info && dataList.char_info.character_name} {dataList.char_info && dataList.char_info.character_name}
<EditButton <EditButton
hidden={true} // hidden={true}
onClick={e => { onClick={e => {
e.preventDefault(); e.preventDefault();
handleModalClose('pwChange'); handleModalView('pwChange');
}}></EditButton> }}></EditButton>
</td> </td>
</tr> </tr>
@@ -219,7 +219,13 @@ const UserDefaultInfo = ({ userInfo }) => {
</tbody> </tbody>
</UserInfoTable> </UserInfoTable>
</div> </div>
<NicknameChangeModal pwPop={modalState.pwChangeModal} handleClick={() => handleModalClose('pwChange')} dataList={dataList} /> <NicknameChangeModal
pwPop={modalState.pwChangeModal}
handleClick={() => {
handleModalClose('pwChange');
fetchData();
}}
dataList={dataList} />
</> </>
); );
}; };

View File

@@ -194,7 +194,7 @@ const UserInventoryInfo = ({ userInfo }) => {
<th width="120">ID</th> <th width="120">ID</th>
<th width="50%">아이템</th> <th width="50%">아이템</th>
<th width="100">보유개수</th> <th width="100">보유개수</th>
<th width="60">삭제</th> {/*<th width="60">삭제</th>*/}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -205,9 +205,9 @@ const UserInventoryInfo = ({ userInfo }) => {
<td>{el.item_id}</td> <td>{el.item_id}</td>
<td>{el.item_name}</td> <td>{el.item_name}</td>
<td>{el.count}</td> <td>{el.count}</td>
<td> {/*<td>*/}
<Button theme={authDelete ? "line" : "disable"} id={el.item_guid} name="single" text="삭제" handleClick={e => {handleDeleteConfirmModal(el, type)}} disabled={!authDelete}/> {/* <Button theme={authDelete ? "line" : "disable"} id={el.item_guid} name="single" text="삭제" handleClick={e => {handleDeleteConfirmModal(el, type)}} disabled={!authDelete}/>*/}
</td> {/*</td>*/}
</tr> </tr>
); );
})} })}
@@ -246,136 +246,6 @@ const UserInventoryInfo = ({ userInfo }) => {
modalText={t('DEL_COMPLETE')} modalText={t('DEL_COMPLETE')}
handleComplete={handleDeleteComplete} handleComplete={handleDeleteComplete}
/> />
{/* <InfoSubTitle>의상 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">no.</th>
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.cloth.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper>
<InfoSubTitle>소품 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">no.</th>
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.prop.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper>
<InfoSubTitle>미용 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">no.</th>
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.beauty.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper>
<InfoSubTitle>타투 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">no.</th>
<th width="120">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.tattoo.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper>
<InfoSubTitle>기타 탭</InfoSubTitle>
<UserTableWrapper>
<UserDefaultTable>
<thead>
<tr>
<th width="80">슬롯 no.</th>
<th width="320">ID</th>
<th width="50%">아이템</th>
<th width="100">보유개수</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.inventory_list.etc.map((el, idx) => {
return (
<tr key={idx}>
<td>{idx + 1}</td>
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
</tr>
);
})}
</tbody>
</UserDefaultTable>
</UserTableWrapper> */}
</> </>
); );
}; };

View File

@@ -1,12 +1,10 @@
import { useState, Fragment, useEffect } from 'react'; import { useState, Fragment, useEffect, memo } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import MailDetailModal from '../../components/DataManage/MailDetailModal'; import MailDetailModal from '../../components/DataManage/MailDetailModal';
import { SelectInput, TextInput } from '../../styles/Components'; import { SelectInput, TextInput } from '../../styles/Components';
import { UserMailDelete, UserMailItemDelete, UserMailView } from '../../apis'; import { UserMailDelete, UserMailItemDelete, UserMailView } from '../../apis';
import ConfirmModal from '../common/modal/ConfirmModal';
import CompletedModal from '../common/modal/CompletedModal';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import CustomConfirmModal from '../common/modal/CustomConfirmModal'; import CustomConfirmModal from '../common/modal/CustomConfirmModal';
import { authType, ivenTabType, opMailType } from '../../assets/data'; import { authType, ivenTabType, opMailType } from '../../assets/data';
@@ -17,17 +15,55 @@ import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { opPickupType, opReadType, opYNType } from '../../assets/data/options'; import { opPickupType, opReadType, opYNType } from '../../assets/data/options';
import { useDynamoDBPagination, useModal } from '../../hooks/hook'; import { useDynamoDBPagination, useModal } from '../../hooks/hook';
import { DynamoPagination } from '../common'; import { DynamoPagination } from '../common';
import { useLoading } from '../../context/LoadingProvider';
import { useAlert } from '../../context/AlertProvider';
import { alertTypes } from '../../assets/data/types';
const ConfirmChild = memo(({ maxCount }) => {
const { t } = useTranslation();
const [localCount, setLocalCount] = useState('1');
const {showToast} = useAlert();
const handleChange = (e) => {
let value = e.target.value;
if (value === '0' || value === '-0') {
value = '1';
} else if (value < 0) {
value = Math.abs(value).toString();
} else if(Number(value) > maxCount) {
showToast('DEL_COUNT_CHECK', {type: alertTypes.warning});
value = maxCount.toString();
}
setLocalCount(value);
};
return(
<InputItem>
<p>{t('DEL_COUNT_CONFIRM', {count: maxCount})}</p>
<TextInput
id="user-mail-item-count-input"
placeholder="수량"
type="number"
value={localCount}
onChange={handleChange}
width="200px"
/>
</InputItem>
);
});
const UserMailInfo = ({ userInfo }) => { const UserMailInfo = ({ userInfo }) => {
const token = sessionStorage.getItem('token'); const token = sessionStorage.getItem('token');
const { t } = useTranslation();
const authInfo = useRecoilValue(authList); const authInfo = useRecoilValue(authList);
const {withLoading} = useLoading();
const {showModal, showToast} = useAlert();
// 받은 우편, 보낸 우편 // 받은 우편, 보낸 우편
const [option, setOption] = useState('RECEIVE'); const [option, setOption] = useState('RECEIVE');
const [detail, setDetail] = useState({ title: '', content: '', item_list: [], mail_guid: '' }); const [detail, setDetail] = useState({ title: '', content: '', item_list: [], mail_guid: '' });
const [deleteSelected, setDeleteSelected] = useState({}); const [deleteSelected, setDeleteSelected] = useState({});
const [itemUpdateCount, setItemUpdateCount] = useState('1');
const [authDelete, setAuthDelete] = useState(false); const [authDelete, setAuthDelete] = useState(false);
const { const {
@@ -36,10 +72,7 @@ const UserMailInfo = ({ userInfo }) => {
handleModalClose handleModalClose
} = useModal({ } = useModal({
detail: 'hidden', detail: 'hidden',
deleteItem: 'hidden', deleteItem: 'hidden'
deleteSubmit: 'hidden',
deleteComplete: 'hidden',
deleteItemComplete: 'hidden'
}); });
const fetchMailData = async (page, startKey) => { const fetchMailData = async (page, startKey) => {
@@ -74,32 +107,14 @@ const UserMailInfo = ({ userInfo }) => {
setDetail({ title: title, content: content, item_list: itmeList, mail_guid: mail_guid }); setDetail({ title: title, content: content, item_list: itmeList, mail_guid: mail_guid });
}; };
// 우편 아이템 삭제 개수 처리
const handleItemCount = e => {
if (e.target.value === '0' || e.target.value === '-0') {
setItemUpdateCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setItemUpdateCount(plusNum);
} else if(e.target.value > deleteSelected.count){
alert(t('DEL_COUNT_CHECK'));
setItemUpdateCount(deleteSelected.count);
} else {
setItemUpdateCount(e.target.value);
}
};
const handleModalSubmit = async (type, param = null) => { const handleModalSubmit = async (type, param = null) => {
let params; let params;
let result;
switch (type) { switch (type) {
case "detail": case "detail":
handleModalView('deleteSubmit'); showModal('USER_MAIL_DEL_CONFIRM', {
break; type: alertTypes.confirm,
case "deleteItem": onConfirm: () => handleModalSubmit('deleteSubmit')
setDeleteSelected(param); });
handleModalView('deleteItem');
break; break;
case "deleteSubmit": case "deleteSubmit":
params = {} params = {}
@@ -107,58 +122,64 @@ const UserMailInfo = ({ userInfo }) => {
params.guid = userInfo.guid; params.guid = userInfo.guid;
params.mail_guid = detail.mail_guid; params.mail_guid = detail.mail_guid;
result = await UserMailDelete(token, params); await withLoading(async () => {
return await UserMailDelete(token, params);
}).then(data => {
if(data.result === "SUCCESS") {
showToast('DEL_COMPLETE', {type: alertTypes.success});
}else{
showToast(data.data.message, {type: alertTypes.error});
}
}).catch(e => {
showToast(e, {type: alertTypes.error});
});
handleModalClose('deleteSubmit'); break;
handleModalView('deleteComplete'); case "deleteItem":
setDeleteSelected(param);
handleModalView('deleteItem');
break; break;
case "deleteItemSubmit": case "deleteItemSubmit":
//랜더링 문제로 dom에서 값을 찾아 가져온다.
const inputElement = document.getElementById("user-mail-item-count-input");
if(inputElement === null) showToast('INPUT_VALUE_ERROR',{type: alertTypes.error});
const count = inputElement.value;
params = {} params = {}
params.type = option; params.type = option;
params.guid = userInfo.guid; params.guid = userInfo.guid;
params.mail_guid = detail.mail_guid; params.mail_guid = detail.mail_guid;
params.item_id = deleteSelected.item_id; params.item_id = deleteSelected.item_id;
params.parrent_count = deleteSelected.count; params.parrent_count = deleteSelected.count;
params.count = itemUpdateCount; params.count = count;
result = await UserMailItemDelete(token, params); await withLoading(async () => {
if(result.result === "SUCCESS"){ return await UserMailItemDelete(token, params);
if(deleteSelected.count <= itemUpdateCount){ }).then(data => {
const item_idx = detail.item_list.findIndex(item => item.item_id === deleteSelected.item_id); if(data.result === "SUCCESS") {
if(item_idx >= 0) { if(deleteSelected.count <= count){
detail.item_list.splice(item_idx, 1); const item_idx = detail.item_list.findIndex(item => item.item_id === params.item_id);
if(item_idx >= 0) {
detail.item_list.splice(item_idx, 1);
}
}else{
deleteSelected.count = deleteSelected.count - count;
} }
showToast('DEL_ITEM_COMPLETE', {type: alertTypes.success});
}else{ }else{
deleteSelected.count = deleteSelected.count - itemUpdateCount; showToast(data.data.message, {type: alertTypes.error});
} }
} }).catch(e => {
handleModalClose('deleteItem'); showToast(e, {type: alertTypes.error});
handleModalView('deleteItemComplete'); }).finally(() => {
break; handleModalClose('deleteItem');
case "deleteComplete": fetchPage(pagination.currentPage);
handleModalClose('deleteComplete'); });
handleModalClose('detail');
// const idx = dataList.mail_list.findIndex(mail => mail.mail_guid === detail.mail_guid);
// if(idx >= 0) {
// dataList.mail_list.splice(idx, 1);
// }
fetchPage(pagination.currentPage);
break;
case "deleteItemComplete":
handleModalClose('deleteItemComplete');
break; break;
} }
} }
const ConfirmChild = () => {
return(
<InputItem>
<p>{t('DEL_COUNT_CONFIRM', {count: deleteSelected.count})}</p>
<TextInput placeholder="수량" type="number" value={itemUpdateCount} onChange={e => handleItemCount(e)} width="200px" />
</InputItem>
);
}
return ( return (
loading ? <TableSkeleton count={10}/> : loading ? <TableSkeleton count={10}/> :
<> <>
@@ -195,7 +216,6 @@ const UserMailInfo = ({ userInfo }) => {
<th width="100">첨부 아이템</th> <th width="100">첨부 아이템</th>
<th width="80">수령</th> <th width="80">수령</th>
<th width="80">시스템 우편</th> <th width="80">시스템 우편</th>
{/* <th width="170">수령 일자</th> */}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -218,9 +238,6 @@ const UserMailInfo = ({ userInfo }) => {
<td>{opYNType.find(type => type.value === (mail.item_list.length > 0)).name}</td> <td>{opYNType.find(type => type.value === (mail.item_list.length > 0)).name}</td>
<td>{opPickupType.find(type => type.value === mail.is_get_item).name}</td> <td>{opPickupType.find(type => type.value === mail.is_get_item).name}</td>
<td>{opYNType.find(type => type.value === mail.is_system_mail).name}</td> <td>{opYNType.find(type => type.value === mail.is_system_mail).name}</td>
{/* <td>
{mail.is_get_item_dt && String(new Date(new Date(mail.is_get_item_dt).setHours(new Date(mail.is_get_item_dt).getHours() + 9)).toLocaleString())}
</td> */}
</tr> </tr>
); );
})} })}
@@ -268,7 +285,6 @@ const UserMailInfo = ({ userInfo }) => {
</> </>
)} )}
{/*상세*/} {/*상세*/}
<MailDetailModal <MailDetailModal
mailModal={modalState.detailModal} mailModal={modalState.detailModal}
@@ -279,32 +295,19 @@ const UserMailInfo = ({ userInfo }) => {
handleItemDelete={(param) => handleModalSubmit('deleteItem', param)} handleItemDelete={(param) => handleModalSubmit('deleteItem', param)}
authDelete={authDelete} authDelete={authDelete}
/> />
{/*메일 삭제 모달*/}
<ConfirmModal
modalText={t('USER_MAIL_DEL_CONFIRM')}
view={modalState.deleteSubmitModal}
handleSubmit={() => handleModalSubmit('deleteSubmit')}
handleCancel={() => handleModalClose('deleteSubmit')}
handleClose={() => handleModalClose('deleteSubmit')}
/>
<CompletedModal
view={modalState.deleteCompleteModal}
modalText={t('DEL_COMPLETE')}
handleComplete={() => handleModalSubmit('deleteComplete')}
/>
{/*메일 아이템 삭제 모달*/} {/*메일 아이템 삭제 모달*/}
<CustomConfirmModal <CustomConfirmModal
ChildView={ConfirmChild} ChildView={() => (
<ConfirmChild
maxCount={deleteSelected.count}
/>
)}
view={modalState.deleteItemModal} view={modalState.deleteItemModal}
handleSubmit={() => handleModalSubmit('deleteItemSubmit')} handleSubmit={() => handleModalSubmit('deleteItemSubmit')}
handleCancel={() => handleModalClose('deleteItem')} handleCancel={() => handleModalClose('deleteItem')}
handleClose={() => handleModalClose('deleteItem')} handleClose={() => handleModalClose('deleteItem')}
/> />
<CompletedModal
view={modalState.deleteItemCompleteModal}
modalText={t('DEL_ITEM_COMPLETE')}
handleComplete={() => handleModalSubmit('deleteItemComplete')}
/>
</> </>
); );
}; };
@@ -393,31 +396,4 @@ const SelectContainer = styled.div`
select { select {
height: 30px; height: 30px;
} }
`;
const PaginationButtons = styled.div`
display: flex;
align-items: center;
gap: 10px;
`;
const PaginationButton = styled.button`
background-color: ${props => props.disabled ? '#e0e0e0' : '#6c7eb7'};
color: ${props => props.disabled ? '#a0a0a0' : 'white'};
border: none;
padding: 6px 12px;
border-radius: 4px;
font-size: 12px;
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
transition: background-color 0.2s;
&:hover {
background-color: ${props => props.disabled ? '#e0e0e0' : '#5a6a9b'};
}
`;
const PageInfo = styled.div`
font-size: 12px;
font-weight: 500;
color: #666;
`; `;

View File

@@ -3,8 +3,12 @@ import { useEffect, useState } from 'react';
import { MailExcelDown, MailMultiRegsit } from '../../apis'; import { MailExcelDown, MailMultiRegsit } from '../../apis';
import { AlertText } from '../../styles/Components'; import { AlertText } from '../../styles/Components';
import { useAlert } from '../../context/AlertProvider';
import { alertTypes } from '../../assets/data/types';
const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData, disabledBtn, setExcelFile, handleDetailDelete, excelName, setExcelName, status }) => {
const {showToast} = useAlert();
const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData, disabledBtn, setExcelFile, alertMessage, setAlertMessage, handleDetailDelete, excelName, setExcelName, status }) => {
useEffect(() => { useEffect(() => {
excelName && setExcelName(null); excelName && setExcelName(null);
}, [downloadData]); }, [downloadData]);
@@ -12,6 +16,12 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
const handleFile = async e => { const handleFile = async e => {
const token = sessionStorage.getItem('token'); const token = sessionStorage.getItem('token');
if(e.target.files[0].name.includes('sample')){
document.querySelector('#fileinput').value = '';
showToast('UPLOAD_FILENAME_SAMPLE_WARNING', {type: alertTypes.warning});
return;
}
setExcelFile(e.target.files[0]); setExcelFile(e.target.files[0]);
setExcelName(e.target.files[0].name); setExcelName(e.target.files[0].name);
@@ -21,11 +31,9 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
if (message === 'Excel 파일을 선택해주세요.' if (message === 'Excel 파일을 선택해주세요.'
|| message === 'Maximum upload size exceeded' || message === 'Maximum upload size exceeded'
|| message === "guid(32자)를 확인해주세요.") { || message === "guid(32자)를 확인해주세요.") {
setAlertMessage('유효하지 않은 파일입니다.'); showToast('FILE_NOT_EXIT_ERROR', {type: alertTypes.warning});
} else if (message === '중복된 유저 정보가 있습니다.') { } else if (message === '중복된 유저 정보가 있습니다.') {
setAlertMessage('중복된 유저 정보가 있습니다.'); showToast('DUPLICATE_USER', {type: alertTypes.warning});
} else {
setAlertMessage('');
} }
handlePostGuid(updateData); handlePostGuid(updateData);
@@ -33,7 +41,6 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
// 저장된 엑셀 파일 -> API 로 전송 // 저장된 엑셀 파일 -> API 로 전송
const handlePostGuid = fileName => { const handlePostGuid = fileName => {
// console.log("fileName.data.data.file_name", fileName.data.data.file_name)
setResultData({ ...resultData, file_name: fileName.data.data.file_name }); setResultData({ ...resultData, file_name: fileName.data.data.file_name });
}; };
@@ -51,10 +58,8 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
await MailExcelDown(token, downloadData ? downloadData : 'mail_sample.xlsx'); await MailExcelDown(token, downloadData ? downloadData : 'mail_sample.xlsx');
}; };
// console.log('undefinedFile 컴포넌트에서 ', undefinedFile); console.log("excelName", excelName);
// console.log('excelFile ...', excelFile); console.log("downloadData", downloadData);
// console.log("excelName", excelName);
// console.log(resultData); // console.log(resultData);
return ( return (
<> <>
@@ -78,21 +83,19 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
)} )}
{/* 등록 페이지 : 파일이 있을 때 파일 삭제용 버튼 */} {/* 등록 페이지 : 파일이 있을 때 파일 삭제용 버튼 */}
{excelName ? ( {excelName ? (
<> <FileButton
<FileButton disabled={disabled}
onClick={() => { onClick={() => {
handleFileDelete(); handleFileDelete();
setExcelName(null); setExcelName(null);
}}> }}>
파일 삭제 파일 삭제
</FileButton> </FileButton>
<AlertText>{alertMessage}</AlertText>
</>
) : // 상세 페이지 : 저장된 파일이 복수일 때 ) : // 상세 페이지 : 저장된 파일이 복수일 때
resultData.guid && resultData.receive_type === 'MULTIPLE' ? ( resultData.guid && resultData.receive_type === 'MULTIPLE' ? (
<> <>
<FileButton <FileButton
disabled={resultData.is_reserve === false && disabledBtn || status === 'FINISH'} disabled={disabled}
onClick={e => { onClick={e => {
handleDetailDelete(e); handleDetailDelete(e);
setExcelName(null); setExcelName(null);

View File

@@ -1,5 +1,4 @@
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
import { useForm } from 'react-hook-form';
import { useState, useEffect, Fragment } from 'react'; import { useState, useEffect, Fragment } from 'react';
import Button from '../common/button/Button'; import Button from '../common/button/Button';
@@ -7,9 +6,14 @@ import Modal from '../common/modal/Modal';
import { Title, BtnWrapper, InputGroup, TableStyle, AlertText } from '../../styles/Components'; import { Title, BtnWrapper, InputGroup, TableStyle, AlertText } from '../../styles/Components';
import { BlackListExcelDown, BlackListMultipleUpload } from '../../apis'; import { BlackListExcelDown, BlackListMultipleUpload } from '../../apis';
import { useLoading } from '../../context/LoadingProvider';
import { useAlert } from '../../context/AlertProvider';
import { alertTypes } from '../../assets/data/types';
const UserBlockUploadBtn = ({ disabled, setGuidList, guidList, typeError, setTypeError }) => { const UserBlockUploadBtn = ({ disabled, setGuidList, guidList, typeError, setTypeError }) => {
const token = sessionStorage.getItem('token'); const token = sessionStorage.getItem('token');
const {withLoading} = useLoading();
const {showToast} = useAlert();
// onchange states // onchange states
const [file, setFile] = useState(null); const [file, setFile] = useState(null);
@@ -30,7 +34,15 @@ const UserBlockUploadBtn = ({ disabled, setGuidList, guidList, typeError, setTyp
let fileTypes = ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/csv']; let fileTypes = ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/csv'];
setFile(e.target.files[0]); setFile(e.target.files[0]);
setGuidList(await BlackListMultipleUpload(token, e.target.files[0]));
await withLoading(async () => {
return await BlackListMultipleUpload(token, e.target.files[0]);
}).then(data => {
setGuidList(data);
}).catch(e => {
showToast(e, {type: alertTypes.error});
});
}; };
const handleFileDelete = e => { const handleFileDelete = e => {
@@ -75,9 +87,10 @@ const UserBlockUploadBtn = ({ disabled, setGuidList, guidList, typeError, setTyp
</InputGroup> </InputGroup>
</FileWrapper> </FileWrapper>
</div> </div>
<Modal $bgcolor="transparent" min="300px" $view={previewModal}> <Modal $bgcolor="transparent" min="300px" $view={previewModal}>
<Title $align="center">업로드 미리보기</Title> <Title $align="center">업로드 미리보기</Title>
<Count> 제재 인원 : 45</Count> <Count> 제재 인원 : {guidList.length}</Count>
<TableWrapper> <TableWrapper>
<TableStyle> <TableStyle>
<thead> <thead>

View File

@@ -66,7 +66,8 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
if(modalType === TYPE_REGISTRY && configData?.length > 0){ if(modalType === TYPE_REGISTRY && configData?.length > 0){
setResultData(prev => ({ setResultData(prev => ({
...prev, ...prev,
round_count: configData[0].default_round_count round_count: configData[0].default_round_count,
round_time: configData[0].round_time
})); }));
} }
}, [modalType, configData]); }, [modalType, configData]);
@@ -187,10 +188,10 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
return; return;
} }
if(resultData.round_time === 0){ // if(resultData.round_time === 0){
const config = configData.find(data => data.id === resultData.config_id); // const config = configData.find(data => data.id === resultData.config_id);
setResultData({ ...resultData, round_time: config.round_time }); // setResultData({ ...resultData, round_time: config.round_time });
} // }
showModal(isView('modify') ? 'BATTLE_EVENT_UPDATE_CONFIRM' : 'BATTLE_EVENT_REGIST_CONFIRM', { showModal(isView('modify') ? 'BATTLE_EVENT_UPDATE_CONFIRM' : 'BATTLE_EVENT_REGIST_CONFIRM', {
type: alertTypes.confirm, type: alertTypes.confirm,
@@ -267,6 +268,8 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
} }
} }
console.log(configData)
return ( return (
<> <>
<Modal min="760px" $view={detailView}> <Modal min="760px" $view={detailView}>

View File

@@ -13,6 +13,7 @@ import { NoticeModify } from '../../../apis';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { convertKTC, convertKTCDate } from '../../../utils'; import { convertKTC, convertKTCDate } from '../../../utils';
import { languageType } from '../../../assets/data/options'; import { languageType } from '../../../assets/data/options';
import { CopyBtn } from '../../../styles/ModuleComponents';
const BoardInfoModal = ({ detailView, setDetailView, content, id, setIsCopyData, openRegistModal, userInfo }) => { const BoardInfoModal = ({ detailView, setDetailView, content, id, setIsCopyData, openRegistModal, userInfo }) => {
let viewOnly = userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 17); // 조회만 가능 권한 let viewOnly = userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 17); // 조회만 가능 권한
@@ -980,12 +981,6 @@ const MessageBox = styled.div`
} }
`; `;
const CopyBtn = styled.div`
position: absolute;
right: 0;
top: 0;
`;
const InGameModalText = styled(ModalText)` const InGameModalText = styled(ModalText)`
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;

View File

@@ -8,7 +8,7 @@ import { EventIsItem, EventModify } from '../../../apis';
import { authList } from '../../../store/authList'; import { authList } from '../../../store/authList';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { authType, benItems, commonStatus, wellType } from '../../../assets/data'; import { authType, benItems, commonStatus, currencyType } from '../../../assets/data';
import { import {
AppendRegistBox, AppendRegistTable, AreaBtnClose, AppendRegistBox, AppendRegistTable, AreaBtnClose,
BtnDelete, DetailInputItem, DetailInputRow, BtnDelete, DetailInputItem, DetailInputRow,
@@ -177,7 +177,7 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
const item_cnt = resultData.item_list[itemIndex].item_cnt; const item_cnt = resultData.item_list[itemIndex].item_cnt;
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount); resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
} else { } else {
const name = wellType.find(well => well.value === resource).name; const name = currencyType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name }; const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem); resultData.item_list.push(newItem);
} }
@@ -447,7 +447,7 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
<td> <td>
<DetailInputItem> <DetailInputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={isReadOnly}> <SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={isReadOnly}>
{wellType.map((data, index) => ( {currencyType.map((data, index) => (
<option key={index} value={data.value}> <option key={index} value={data.value}>
{data.name} {data.name}
</option> </option>

View File

@@ -1,9 +1,9 @@
import { styled } from 'styled-components'; import { styled } from 'styled-components';
import RadioInput from '../../common/input/Radio'; import RadioInput from '../../common/input/Radio';
import { useState, useEffect, Fragment } from 'react'; import React, { useState, useEffect, Fragment } from 'react';
import CheckBox from '../../common/input/CheckBox'; import CheckBox from '../../common/input/CheckBox';
import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, DatePickerWrapper, Textarea, ModalText, ButtonClose, SearchBarAlert } from '../../../styles/Components'; import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, DatePickerWrapper, Textarea} from '../../../styles/Components';
import Button from '../../common/button/Button'; import Button from '../../common/button/Button';
import Modal from '../../common/modal/Modal'; import Modal from '../../common/modal/Modal';
@@ -11,57 +11,68 @@ import IconDelete from '../../../assets/img/icon/icon-delete.png';
import CloseIcon from '../../../assets/img/icon/icon-close.png'; import CloseIcon from '../../../assets/img/icon/icon-close.png';
import DatePickerComponent from '../../common/Date/DatePickerComponent'; import DatePickerComponent from '../../common/Date/DatePickerComponent';
import MailRegistUploadBtn from '../MailRegistUploadBtn'; import MailRegistUploadBtn from '../MailRegistUploadBtn';
import { benItems, HourList, MinuteList, modalTypes, wellType } from '../../../assets/data'; import {
import { EventModify, MailModify } from '../../../apis'; authType,
benItems, commonStatus,
HourList,
mailType,
MinuteList,
userType,
currencyType,
} from '../../../assets/data';
import { MailCaliumTotalView, MailIsItem, MailModify } from '../../../apis';
import { authList } from '../../../store/authList'; import { authList } from '../../../store/authList';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { convertKTC, convertKTCDate, timeDiffMinute } from '../../../utils'; import { convertKTC, convertKTCDate } from '../../../utils';
import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom';
import DynamicModal from '../../common/modal/DynamicModal'; import { useDataFetch } from '../../../hooks/hook';
import { useAlert } from '../../../context/AlertProvider';
import { useLoading } from '../../../context/LoadingProvider';
import { alertTypes, currencyCodeTypes } from '../../../assets/data/types';
import { userType2 } from '../../../assets/data/options';
const MailDetailModal = ({ detailView, handleDetailView, content }) => { const MailDetailModal = ({ detailView, handleDetailView, content }) => {
const userInfo = useRecoilValue(authList); const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const token = sessionStorage.getItem('token'); const token = sessionStorage.getItem('token');
const navigate = useNavigate();
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
const id = content && content.id; const id = content && content.id;
const onlyView = userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 23); const updateAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.mailUpdate);
const updateAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 23);
const [sendHour, setSendHour] = useState('00'); const [sendHour, setSendHour] = useState('00');
const [sendMin, setSendMin] = useState('00'); const [sendMin, setSendMin] = useState('00');
const [item, setItem] = useState(''); const [item, setItem] = useState('');
const [itemCount, setItemCount] = useState(''); const [itemCount, setItemCount] = useState('');
const [resource, setResource] = useState('19010001'); const [resource, setResource] = useState(currencyCodeTypes.gold);
const [resourceCount, setResourceCount] = useState(''); const [resourceCount, setResourceCount] = useState('');
const [modifyModal, setModifyModal] = useState('hidden'); const [resultData, setResultData] = useState({
const [completeModal, setCompleteModal] = useState('hidden'); is_reserve: false,
const [resultData, setResultData] = useState({}); send_dt: '',
mail_type: 'SELECT',
const [modalState, setModalState] = useState({ receive_type: 'SINGLE',
updateConfirmModal: 'hidden', user_type: 'GUID',
updateCompleteModal: 'hidden', mail_list: [],
item_list: [],
guid: '',
}); });
const [isNullValue, setIsNullValue] = useState(false);
// 과거 판단 // 과거 판단
const [isPast, setIsPast] = useState(false); const [isView, setIsView] = useState(true);
const [isChanged, setIsChanged] = useState(false); const [isChanged, setIsChanged] = useState(false);
const [isItemNullValue, setIsItemNullValue] = useState(false);
const [excelFile, setExcelFile] = useState(content.target ? content.target : ''); const [excelFile, setExcelFile] = useState('');
const [excelName, setExcelName] = useState(null);
const [downloadData, setDownLoadData] = useState(null); const [downloadData, setDownLoadData] = useState(null);
const [btnValidation, setBtnValidation] = useState(false); const [btnValidation, setBtnValidation] = useState(false);
const [updateMessage, setUpdateMessage] = useState('수정이 완료되었습니다.');
const [alertMessage, setAlertMessage] = useState(''); const {
const [undefinedFile, setUndefinedFile] = useState(false); data: caliumTotalData,
const [disabledBtn, setDisabledBtn] = useState(false); // 예약 발송 확인용 } = useDataFetch(() => MailCaliumTotalView(token), [token]);
const [alertMsg, setAlertMsg] = useState('');
const KOREAN_TIME = content && convertKTCDate(content.send_dt); const KOREAN_TIME = content && convertKTCDate(content.send_dt);
@@ -72,39 +83,43 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
}; };
useEffect(() => { useEffect(() => {
document.querySelector('#fileinput').value = ''; if(content) {
setResultData({
...resultData,
is_reserve: content.is_reserve,
send_dt: KOREAN_TIME,
mail_type: content.mail_type,
receive_type: content.receive_type,
mail_list: content.mail_list,
item_list: content.item_list,
guid: content.target,
file_name: content.receive_type === 'MULTIPLE' ? content.target : null
});
setResultData({ content.mail_list.length === 1 && setBtnValidation(true);
is_reserve: content && content.is_reserve, content.is_reserve === false && setBtnValidation(true);
send_dt: content && KOREAN_TIME,
mail_type: content && content.mail_type,
receive_type: content && content.receive_type,
user_type: content && content.user_type,
mail_list: content && content.mail_list,
item_list: content && content.item_list,
guid: content && content.target,
});
content && content.mail_list.length === 1 && setBtnValidation(true);
content && content.is_reserve === false && setBtnValidation(true);
setItem('');
KOREAN_TIME < new Date ? setIsPast(true) : setIsPast(false);
setExcelFile(content && content.target);
setDownLoadData(content && content.target);
setDisabledBtn(content && content.is_reserve && false);
// 복수 or 단일일 때 content.target을 exel name 으로 지정
(content && content.receive_type === 'MULTIPLE') ?
setExcelName(content && content.target)
: setExcelName('')
setItem('');
if (content.receive_type === 'MULTIPLE') {
setDownLoadData(content.target);
setExcelFile(content.target);
}
}
}, [content]); }, [content]);
// console.log('downloadData', downloadData); useEffect(() => {
// console.log('isPast', isPast); if(content){
// console.log("guid", resultData.guid) if(!updateAuth
// console.log("메일 형식", content && content.receive_type," 엑셀네임", excelName) || !content.is_reserve
|| (content.is_reserve && convertKTCDate(content.send_dt) < new Date)
|| (content.send_status === commonStatus.fail || content.send_status === commonStatus.finish)
){
setIsView(true);
}else{
setIsView(false);
}
}
},[updateAuth, content, resultData])
// 아이템 수량 숫자 체크 // 아이템 수량 숫자 체크
const handleItemCount = e => { const handleItemCount = e => {
@@ -120,25 +135,41 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
}; };
// 아이템 추가 // 아이템 추가
const handleItemList = () => { const handleItemList = async () => {
if(benItems.includes(item)){ if(benItems.includes(item)){
setAlertMsg(t('MAIL_ITEM_ADD_BEN')) showToast('MAIL_ITEM_ADD_BEN',{type: alertTypes.warning});
return; return;
} }
item.length === 0 || itemCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false); if(item.length === 0 || itemCount.length === 0 || itemCount <= 0){
showToast('COUNT_EMPTY_WARNING', { type: alertTypes.warning });
if (item.length === '' || itemCount.length === 0 || itemCount <= 0) { return;
setIsItemNullValue(true);
} else if (item.length !== 0) {
setIsItemNullValue(false);
setIsChanged(true);
const newItem = { item: item, item_cnt: itemCount };
resultData.item_list.push(newItem);
setItem('');
setItemCount('');
} }
await withLoading(async () => {
return await MailIsItem(token, { item: item });
}).then(data => {
if (data.result === 'ERROR') {
showToast(data.data.message, { type: alertTypes.warning });
}else{
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: data.data.item_info.item_name };
resultData.item_list.push(newItem);
setIsChanged(true);
setItem('');
setItemCount('');
}
}).catch(e => {
showToast(e, { type: alertTypes.error });
});
}; };
// 아이템 삭제 // 아이템 삭제
@@ -146,8 +177,6 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]); let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
setIsChanged(true); setIsChanged(true);
// console.log('filterList', filterList);
setResultData({ ...resultData, item_list: filterList }); setResultData({ ...resultData, item_list: filterList });
}; };
@@ -166,17 +195,35 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
// 자원 추가 // 자원 추가
const handleResourceList = () => { const handleResourceList = () => {
resourceCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
if (resourceCount.length === 0 || resourceCount <= 0) { if (resourceCount.length === 0 || resourceCount <= 0) {
setIsItemNullValue(true); showToast('COUNT_EMPTY_WARNING', { type: alertTypes.warning });
} else { } else {
setIsItemNullValue(false); const itemIndex = resultData.item_list.findIndex(
const name = wellType.find(well => well.value === resource).name; (item) => item.item === resource,
const newItem = { item: resource, item_cnt: resourceCount, item_name: name }; );
if (itemIndex !== -1) {
const item_cnt = resultData.item_list[itemIndex].item_cnt;
if (resource === currencyCodeTypes.calium) {
if ((Number(resourceCount) + Number(item_cnt)) > caliumTotalData) {
showToast('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING', { type: alertTypes.warning });
return;
}
}
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
} else {
if (resource === currencyCodeTypes.calium) {
if (Number(resourceCount) > caliumTotalData) {
showToast('MAIL_ITEM_CALIUM_TOTAL_OVER_WARNING', { type: alertTypes.warning });
return;
}
}
const name = currencyType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem);
}
setIsChanged(true);
resultData.item_list.push(newItem);
setResourceCount(''); setResourceCount('');
} }
}; };
@@ -213,149 +260,79 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
setResultData({ ...resultData, send_dt: result }); setResultData({ ...resultData, send_dt: result });
}; };
// 우편 상세 정보 수정
const handleModifyModal = () => {
if (
resultData.mail_list.map(data => data.content === '' || data.title === '').includes(true) ||
(resultData.receive_type === 'MULTIPLE' ? excelFile === null : resultData.guid === '') ||
resultData.send_dt.length === 0 ||
resultData.mail_type === 'SELECT' ||
isChanged === false ||
alertMessage
) {
isChanged === true && setIsNullValue(true);
} else {
// 복수로 수정하거나 복수로 등록할 때 항상 excel name을 넘겨줘야합니다.
content && content.receive_type === 'MULTIPLE' ? setResultData({ ...resultData, file_name: excelName === null ? downloadData : excelName }) : setExcelName('');
// setExcelName(content && content.target)
setIsNullValue(false);
if (modifyModal === 'hidden') {
setModifyModal('view');
} else {
setModifyModal('hidden');
}
}
};
// 상세 정보 단일 체크
const handleSingleBtn = () => {
if (content && content.is_reserve === true && !onlyView && !isPast) {
setResultData({ ...resultData, guid: '' });
delete resultData.file_name;
document.querySelector('#fileinput').value = '';
setExcelFile(null);
setIsChanged(true);
}
};
// 상세 정보 복수 체크
const handleMultiBtn = () => {
if (content && content.is_reserve === true) {
delete resultData.guid;
}
setIsChanged(true);
};
// 완료 모달창
const handleCompleteModal = () => {
if (completeModal === 'hidden') {
setCompleteModal('view');
} else {
setCompleteModal('hidden');
window.location.reload();
}
};
// 메일 수정 버튼
const handleModifyMail = () => {
if(resultData.receive_type === 'MULTIPLE') {
delete resultData.guid
MailModify(token, id, resultData);
} else {
MailModify(token, id, resultData);
}
handleCompleteModal();
handleModifyModal();
};
// 확인 버튼 후 다 초기화 // 확인 버튼 후 다 초기화
const handleReset = () => { const handleReset = () => {
setItem('');
setItemCount('');
setResource(currencyCodeTypes.gold);
setResourceCount('');
setBtnValidation(false); setBtnValidation(false);
setIsNullValue(false);
setIsChanged(false); setIsChanged(false);
setUndefinedFile(false);
setIsItemNullValue(false);
setAlertMessage('');
setExcelFile(null); setExcelFile(null);
setExcelName(null);
}; };
// 상세 페이지에서 파일 삭제 //내용복사
const handleDetailDelete = e => { const handleCopyContent = () => {
e.preventDefault(); const mailData = {
is_reserve: resultData.is_reserve,
mail_type: resultData.mail_type,
mail_list: resultData.mail_list,
item_list: resultData.item_list,
resource_list: resultData.resource_list,
receive_type: resultData.receive_type,
user_type: resultData.user_type,
guid: resultData.guid
};
if (content && content.is_reserve === true) { // 복사한 데이터를 세션 스토리지에 저장
setDownLoadData(undefined); sessionStorage.setItem('copyMailData', JSON.stringify(mailData));
setUndefinedFile(true);
setResultData({ ...resultData, guid: '' });
delete resultData.file_name;
document.querySelector('#fileinput').value = ''; navigate('/servicemanage/mail/mailregist');
setExcelFile(null);
}
setIsChanged(true);
}; };
const handleModalView = (type) => { const checkCondition = () => {
setModalState((prevState) => ({ return (
...prevState, content &&
[`${type}Modal`]: 'view', resultData?.mail_list?.every(data => data.content !== '' && data.title !== '')
})); && ((resultData.is_reserve === true && resultData.send_dt !== '') || resultData.is_reserve === false)
} && (resultData.receive_type === 'MULTIPLE' ? excelFile !== null : resultData.guid !== '')
&& resultData.mail_type !== 'SELECT'
const handleModalClose = (type) => { && isChanged
setModalState((prevState) => ({ );
...prevState, };
[`${type}Modal`]: 'hidden',
}));
}
const handleSubmit = async (type, param = null) => { const handleSubmit = async (type, param = null) => {
switch (type) { switch (type) {
case "submit": case "submit":
// if (!conditionCheck()) return; if (!checkCondition()) return;
handleModalView('updateConfirm'); showModal('MAIL_UPDATE_SAVE', {
type: alertTypes.confirm,
onConfirm: () => handleSubmit('updateConfirm')
})
break; break;
case "updateConfirm": case "updateConfirm":
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date)) await withLoading(async () => {
// 이벤트 시작 30분전이나 이미 SystemMail이 add된 상태에서는 수정할 수 없다. if(resultData.receive_type === 'MULTIPLE') {
if(content.add_flag || timeDiff <= 30){ delete resultData.guid
setAlertMsg(t('EVENT_TIME_LIMIT_UPDATE'));
handleModalClose('updateConfirm'); return await MailModify(token, id, resultData);
return; } else {
} return await MailModify(token, id, resultData);
EventModify(token, id, resultData); }
handleModalClose('updateConfirm'); }).then(data => {
handleModalView('updateComplete'); if(data.result === 'SUCCESS') {
break; showToast('UPDATE_COMPLETED', {type: alertTypes.success});
case "updateComplete": handleDetailView();
handleModalClose('updateComplete'); handleReset();
window.location.reload(); }else{
break; showToast(data.data.message, {type: alertTypes.error});
case "warning": }
setAlertMsg(''); }).catch(error => {
showToast('API_FAIL', {type: alertTypes.error});
}).finally(() => {
});
break; break;
} }
} }
@@ -364,14 +341,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<> <>
<Modal min="960px" $view={detailView}> <Modal min="960px" $view={detailView}>
<Title $align="center">우편 상세 정보</Title> <Title $align="center">우편 상세 정보</Title>
{content && <>
<RegistInfo> <RegistInfo>
<span>등록자 : {content && content.create_by}</span> <span>등록자 : {content.create_by}</span>
<span>등록일 : {content && convertKTC(content.create_dt, false)}</span> <span>등록일 : {convertKTC(content.create_dt, false)}</span>
{content && typeof content.update_by !== 'undefined' && ( {typeof content.update_by !== 'undefined' && (
<> <>
<span>수정자 : {content && content.update_by}</span> <span>수정자 : {content.update_by}</span>
<span>수정일 : {content && convertKTC(content.update_dt, false)}</span> <span>수정일 : {convertKTC(content.update_dt, false)}</span>
</> </>
)} )}
</RegistInfo> </RegistInfo>
@@ -384,23 +361,20 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
checked={resultData && resultData.is_reserve} checked={resultData && resultData.is_reserve}
setData={e => { setData={e => {
setResultData({ ...resultData, is_reserve: e.target.checked }); setResultData({ ...resultData, is_reserve: e.target.checked });
setDisabledBtn(e.target.checked);
setIsChanged(true); setIsChanged(true);
}} }}
disabled={(content && content.is_reserve === false) || onlyView} disabled={(content.is_reserve === false) || isView}
/> />
{content && content.is_reserve === false ? ( {content.is_reserve === false ? (
<></> <></>
) : ( ) : (
content &&
content.is_reserve === true &&
resultData.is_reserve === true && ( resultData.is_reserve === true && (
<InputItem> <InputItem>
<InputLabel>발송 시간</InputLabel> <InputLabel>발송 시간</InputLabel>
<InputGroup> <InputGroup>
<DatePickerWrapper> <DatePickerWrapper>
<DatePickerComponent <DatePickerComponent
readOnly={(content && content.is_reserve === false) || onlyView || isPast} readOnly={(content.is_reserve === false) || isView}
name={initialData.send_dt} name={initialData.send_dt}
selectedDate={resultData ? resultData.send_dt : initialData.send_dt} selectedDate={resultData ? resultData.send_dt : initialData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)} handleSelectedDate={data => handleSelectedDate(data)}
@@ -410,24 +384,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<SelectInput <SelectInput
onChange={e => handleSendTime(e)} onChange={e => handleSendTime(e)}
id="hour" id="hour"
disabled={(content && content.is_reserve === false) || onlyView || isPast} disabled={(content.is_reserve === false) || isView}
value={ value={
resultData && String(new Date(resultData.send_dt).getHours()) < 10 resultData && String(new Date(resultData.send_dt).getHours()) < 10
? '0' + String(new Date(resultData.send_dt).getHours()) ? '0' + String(new Date(resultData.send_dt).getHours())
: resultData && String(new Date(resultData.send_dt).getHours()) : resultData && String(new Date(resultData.send_dt).getHours())
}> }>
{HourList.map(hour => ( {HourList.map(hour => (
<option <option value={hour} key={hour}>
value={hour}
key={hour}
// selected={
// resultData && String(new Date(resultData.send_dt).getHours()) < 10
// ? '0' + String(new Date(resultData.send_dt).getHours()) === hour
// : resultData && String(new Date(resultData.send_dt).getHours()) === hour
// ? 'selected'
// : ''
// }
>
{hour} {hour}
</option> </option>
))} ))}
@@ -438,24 +402,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
setIsChanged(true); setIsChanged(true);
}} }}
id="min" id="min"
disabled={(content && content.is_reserve === false) || onlyView || isPast} disabled={(content.is_reserve === false) || isView}
value={ value={
resultData && String(new Date(resultData.send_dt).getMinutes()) < 10 resultData && String(new Date(resultData.send_dt).getMinutes()) < 10
? '0' + String(new Date(resultData.send_dt).getMinutes()) ? '0' + String(new Date(resultData.send_dt).getMinutes())
: resultData && String(new Date(resultData.send_dt).getMinutes()) : resultData && String(new Date(resultData.send_dt).getMinutes())
}> }>
{MinuteList.map(min => ( {MinuteList.map(min => (
<option <option value={min} key={min}>
value={min}
key={min}
// selected={
// resultData && String(new Date(resultData.send_dt).getMinutes()) < 10
// ? '0' + String(new Date(resultData.send_dt).getMinutes()) === min
// : resultData && String(new Date(resultData.send_dt).getMinutes()) === min
// ? 'selected'
// : ''
// }
>
{min} {min}
</option> </option>
))} ))}
@@ -471,21 +425,22 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
setResultData({ ...resultData, mail_type: e.target.value }); setResultData({ ...resultData, mail_type: e.target.value });
setIsChanged(true); setIsChanged(true);
}} }}
value={content && resultData.mail_type} value={resultData.mail_type}
disabled={(content && content.is_reserve === false) || onlyView || isPast}> disabled={isView}>
<option value="SELECT">타입 선택</option> <option value="SELECT">타입 선택</option>
<option value="SYSTEM_GUID">시스템 안내</option> {mailType.filter(data => data.value !== 'ALL').map((data, index) => (
<option value="INSPECTION_COMPENSATION">점검 보상</option> <option key={index} value={data.value}>
<option value="RECOVER_COMPENSATION">복구 보상</option> {data.name}
<option value="EVENT_COMPENSATION">이벤트 보상</option> </option>
))}
</SelectInput> </SelectInput>
</InputItem> </InputItem>
<InputItem> <InputItem>
<InputLabel>발송상태</InputLabel> <InputLabel>발송상태</InputLabel>
<div> <div>
{content && initialData.send_status === 'WAIT' && <MailState>대기</MailState>} {initialData.send_status === 'WAIT' && <MailState>대기</MailState>}
{content && initialData.send_status === 'FINISH' && <MailState result="success">완료</MailState>} {initialData.send_status === 'FINISH' && <MailState result="success">완료</MailState>}
{content && initialData.send_status === 'FAIL' && <MailState result="fail">실패</MailState>} {initialData.send_status === 'FAIL' && <MailState result="fail">실패</MailState>}
</div> </div>
</InputItem> </InputItem>
</InputRow> </InputRow>
@@ -493,9 +448,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<InputItem> <InputItem>
<InputLabel>수신대상</InputLabel> <InputLabel>수신대상</InputLabel>
<InputItem> <InputItem>
<SelectInput onChange={e => setResultData({ ...resultData, user_type: e.target.value })} value={resultData.user_type} disabled={(content && content.is_reserve === false) || onlyView || isPast}> <SelectInput onChange={e => setResultData({ ...resultData, user_type: e.target.value })}
<option value="GUID">GUID</option> value={resultData.user_type}
<option value="NICKNAME">아바타명</option> disabled={true}>
{userType2.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput> </SelectInput>
</InputItem> </InputItem>
<div> <div>
@@ -505,91 +465,43 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
id="SINGLE" id="SINGLE"
name="receiver" name="receiver"
value="SINGLE" value="SINGLE"
disabled={(content && content.is_reserve === false) || onlyView || isPast} disabled={true}
fontWeight="600" fontWeight="600"
checked={resultData.receive_type === 'SINGLE'} checked={resultData.receive_type === 'SINGLE'}
handleChange={e => {
setResultData({ ...resultData, receive_type: e.target.id });
setIsChanged(true);
}}
handleClick={handleSingleBtn}
/> />
<TextInput <TextInput
placeholder={resultData.user_type === "GUID" ? "GUID 입력" : resultData.user_type === "NICKNAME" ? "아바타명 입력" : "이메일 입력"} disabled={true}
disabled={resultData.receive_type !== 'SINGLE' || (content && content.is_reserve === false) || onlyView || isPast}
// defaultValue={resultData.receive_type === 'SINGLE' && resultData.guid !== '' ? content.target : ''}
value={resultData.receive_type === 'SINGLE' && resultData.guid !== '' ? resultData.guid : ''} value={resultData.receive_type === 'SINGLE' && resultData.guid !== '' ? resultData.guid : ''}
onChange={e => {
let list = [...resultData.guid];
list = e.target.value;
setResultData({ ...resultData, guid: list });
setIsChanged(true);
}}
/> />
</InputGroup> </InputGroup>
{content && resultData.receive_type === 'MULTIPLE' && typeof resultData.guid !== 'undefined' ? ( <InputGroup>
<InputGroup> <RadioInput
<RadioInput label="복수"
label="복수" id="MULTIPLE"
id="MULTIPLE" name="receiver"
name="receiver" value="MULTIPLE"
value="MULTIPLE" fontWeight="600"
fontWeight="600" disabled={true}
checked={content && resultData.receive_type === 'MULTIPLE'} />
disabled <MailRegistUploadBtn
/> disabled={true}
<MailRegistUploadBtn setResultData={setResultData}
disabled={resultData.receive_type !== 'MULTIPLE'} resultData={resultData}
downloadData={downloadData} setExcelFile={setExcelFile}
setResultData={setResultData} handleDetailDelete={() => {}}
resultData={resultData} disabledBtn={true}
handleDetailDelete={handleDetailDelete} excelName={excelFile}
setExcelFile={setExcelFile} setExcelName={setExcelFile}
alertMessage={alertMessage} downloadData={downloadData}
setAlertMessage={setAlertMessage} status={initialData.send_status}
undefinedFile={undefinedFile} />
disabledBtn={disabledBtn} </InputGroup>
excelName={excelName}
setExcelName={setExcelName}
status={initialData.send_status}
/>
</InputGroup>
) : (
<InputGroup>
<RadioInput
label="복수"
id="MULTIPLE"
name="receiver"
value="MULTIPLE"
fontWeight="600"
disabled={(content && content.is_reserve === false) || onlyView || isPast}
handleChange={e => setResultData({ ...resultData, receive_type: e.target.id })}
handleClick={handleMultiBtn}
/>
<MailRegistUploadBtn
setUpdateMessage={setUpdateMessage}
disabled={resultData.receive_type !== 'MULTIPLE'}
setResultData={setResultData}
resultData={resultData}
excelFile={excelFile}
setExcelFile={setExcelFile}
alertMessage={alertMessage}
setAlertMessage={setAlertMessage}
undefinedFile={undefinedFile}
handleDetailDelete={handleDetailDelete}
disabledBtn={disabledBtn}
excelName={excelName}
setExcelName={setExcelName}
status={initialData.send_status}
/>
</InputGroup>
)}
</div> </div>
</InputItem> </InputItem>
</MailReceiver> </MailReceiver>
</RegistGroup> </RegistGroup>
{resultData.mail_list && {resultData.mail_list &&
resultData.mail_list.map(data => { resultData?.mail_list?.map(data => {
return ( return (
<Fragment key={data.language}> <Fragment key={data.language}>
<MailRegistBox> <MailRegistBox>
@@ -597,6 +509,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
언어 : {data.language} 언어 : {data.language}
{btnValidation === false ? ( {btnValidation === false ? (
<BtnClose <BtnClose
disabled={true}
onClick={e => { onClick={e => {
e.preventDefault(); e.preventDefault();
onLangDelete(data.language); onLangDelete(data.language);
@@ -619,7 +532,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
maxLength="30" maxLength="30"
id={data.language} id={data.language}
value={data.title} value={data.title}
readOnly={(content && content.is_reserve === false) || onlyView || isPast} readOnly={(content.is_reserve === false) || isView}
onChange={e => { onChange={e => {
let list = [...resultData.mail_list]; let list = [...resultData.mail_list];
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id); let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
@@ -639,7 +552,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<td> <td>
<Textarea <Textarea
value={data.content} value={data.content}
readOnly={(content && content.is_reserve === false) || onlyView || isPast} readOnly={isView}
id={data.language} id={data.language}
onChange={e => { onChange={e => {
if (e.target.value.length > 2000) { if (e.target.value.length > 2000) {
@@ -678,7 +591,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
list = e.target.value.trimStart(); list = e.target.value.trimStart();
setItem(list); setItem(list);
}} }}
disabled={(content && content.is_reserve === false) || onlyView || isPast} disabled={isView}
/> />
<TextInput <TextInput
placeholder="수량" placeholder="수량"
@@ -686,36 +599,15 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
type="number" type="number"
onChange={e => handleItemCount(e)} onChange={e => handleItemCount(e)}
width="90px" width="90px"
disabled={(content && content.is_reserve === false) || onlyView || isPast} disabled={isView}
/> />
<Button <Button
text="추가" text="추가"
theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'} theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'}
disabled={isView}
handleClick={handleItemList} handleClick={handleItemList}
errorMessage={(content && content.is_reserve === false) || onlyView || isPast}
/> />
</InputItem> </InputItem>
{/* {isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
<div>
{resultData.item_list && (
<ItemList>
{content &&
resultData.item_list.map((data, index) => {
return (
<Item key={index}>
<span>
{data.item}({data.item_cnt})
</span>
{(content && content.is_reserve === false) ||
(updateAuth && <BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>) ||
isPast
}
</Item>
);
})}
</ItemList>
)}
</div> */}
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -724,17 +616,32 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
</th> </th>
<td> <td>
<InputItem> <InputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={(content && content.is_reserve === false) || onlyView || isPast}> <SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={isView}>
{wellType.map((data, index) => ( {currencyType.map((data, index) => (
<option key={index} value={data.value}> <option key={index} value={data.value}>
{data.name} {data.name}
</option> </option>
))} ))}
</SelectInput> </SelectInput>
<TextInput placeholder="수량" type="number" value={resourceCount} disabled={(content && content.is_reserve === false) || onlyView || isPast} onChange={e => handleResourceCount(e)} width="200px" /> <TextInput
<Button text="추가" theme={resourceCount.length === 0 || resource.length === 0 ? 'disable' : 'search'} handleClick={handleResourceList} width="100px" height="35px" errorMessage={(content && content.is_reserve === false) || onlyView || isPast} /> placeholder="수량"
type="number"
value={resourceCount}
disabled={isView}
onChange={e => handleResourceCount(e)}
width="200px"
/>
<Button
text="추가"
theme={resourceCount.length === 0 || resource.length === 0 ? 'disable' : 'search'}
disabled={isView}
handleClick={handleResourceList}
width="100px"
height="35px"
/>
{resource === currencyCodeTypes.calium &&
<Label>(잔여 수량: {caliumTotalData})</Label>}
</InputItem> </InputItem>
{isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
<div> <div>
{resultData.item_list && ( {resultData.item_list && (
@@ -745,7 +652,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<span> <span>
{data.item_name}[{data.item}] ({data.item_cnt}) {data.item_name}[{data.item}] ({data.item_cnt})
</span> </span>
{(content && content.is_reserve === true) || onlyView || isPast && <BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>} {!isView && <BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>}
</Item> </Item>
); );
})} })}
@@ -758,6 +665,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
</MailRegistTable> </MailRegistTable>
</MailRegistBox> </MailRegistBox>
</ModalWrapper> </ModalWrapper>
</>}
<BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px"> <BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px">
<Button <Button
text="확인" text="확인"
@@ -768,58 +676,21 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
handleReset(); handleReset();
}} }}
/> />
{(updateAuth && content && content.is_reserve === true) && !isPast && ( {!isView && (
<Button <Button
type="submit" type="submit"
text="수정" text="수정"
id="수정버튼" id="수정버튼"
theme={ theme={checkCondition() ? 'primary' : 'disable'}
(content && content.mail_list.map(data => data.content === '' || data.title === '').includes(true)) ||
(resultData.receive_type === 'MULTIPLE' ? excelFile === null : resultData.guid === '') ||
resultData.mail_type === 'SELECT' ||
isChanged === false ||
alertMessage.length > 1
? 'disable'
: 'primary'
}
handleClick={() => { handleClick={() => {
handleModifyModal(); // handleModifyModal();
handleSubmit('submit')
}} }}
/> />
)} )}
<Button theme="line" text="내용 복사" handleClick={handleCopyContent} />
</BtnWrapper> </BtnWrapper>
</Modal> </Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={modifyModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleModifyModal} />
</BtnWrapper>
<ModalText $align="center">
우편 정보 수정사항을 <br />
저장하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleModifyModal} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleModifyMail} />
</BtnWrapper>
</Modal>
{/* 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModal} />
</BtnWrapper>
<ModalText $align="center">수정이 완료되었습니다. </ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
</BtnWrapper>
</Modal>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => handleSubmit('warning')}
/>
</> </>
); );
}; };

View File

@@ -1,41 +1,19 @@
import { Fragment, useState } from 'react'; import { Fragment, useEffect, useState } from 'react';
import { styled } from 'styled-components'; import { styled } from 'styled-components';
import { Title, TableStyle, BtnWrapper, TextInput } from '../../../styles/Components'; import { Title, TableStyle, BtnWrapper, TextInput } from '../../../styles/Components';
import Modal from '../../common/modal/Modal'; import Modal from '../../common/modal/Modal';
import Button from '../../common/button/Button'; import Button from '../../common/button/Button';
import { convertKTC } from '../../../utils'; import { convertKTC } from '../../../utils';
import { blockPeriod, blockSanctions, blockStatus, blockType, commonStatus } from '../../../assets/data';
const UserBlockDetailModal = ({ stateModal, handleModal, data }) => { const UserBlockDetailModal = ({ stateModal, handleModal, data }) => {
const history = data.history; const [history, setHistory] = useState();
useEffect(() => {
const type = [ if(data){
{ value: 'ACCESS_RESTRICTIONS', name: '접근 제한' }, setHistory(data.history);
{ value: 'CHATTING_RESTRICTIONS', name: '채팅 제한' }, }
]; },[data])
const sanctions = [
{ value: 'BAD_BEHAVIOR', name: '비매너 행위' },
{ value: 'INAPPROPRIATE_NAME', name: '불건전 이름 사용' },
{ value: 'CASH_TRANSACTION', name: '현금거래 행위' },
{ value: 'GAME_INTERFERENCE', name: '게임 진행 방해' },
{ value: 'SERVICE_INTERFERENCE', name: '운영서비스 방해' },
{ value: 'ACCOUNT_IMPERSONATION', name: '계정도용' },
{ value: 'BUG_ABUSE', name: '버그/어뷰징' },
{ value: 'ILLEGAL_PROGRAM', name: '불법프로그램 사용' },
{ value: 'PERSONAL_INFO_LEAK', name: '개인정보 유출' },
{ value: 'ADMIN_IMPERSONATION', name: '운영자 사칭' },
];
const period = [
{ value: 'WARNING', name: '경고' },
{ value: 'D1', name: '1일' },
{ value: 'D3', name: '3일' },
{ value: 'D7', name: '7일' },
{ value: 'D15', name: '15일' },
{ value: 'D30', name: '30일' },
{ value: 'PERMANENT', name: '영구정지' },
];
return ( return (
<> <>
@@ -49,40 +27,59 @@ const UserBlockDetailModal = ({ stateModal, handleModal, data }) => {
<table> <table>
<caption></caption> <caption></caption>
<tbody> <tbody>
<tr> <tr>
<th width="100px">유저 GUID</th> <th width="100px">유저 GUID</th>
<td>
<TextInput value={data && data.guid || ''} disabled />
</td>
<th width="100px">아바타명</th>
<td>
<TextInput value={data && data.nickname || ''} disabled />
</td>
</tr>
<tr>
<th>제재 기간</th>
<td colSpan="3">
<InputWrapper>
<TextInput
value={data && blockPeriod.find(item => item.value === data.period)?.name}
disabled />
<span>시작일 : {convertKTC(data.start_dt, false)}</span>
<span>종료일 : {convertKTC(data.end_dt, false)}</span>
</InputWrapper>
</td>
</tr>
<tr>
<th>제재 방식</th>
<td>
<TextInput value={data && blockType.find(item => item.value === data.type)?.name}
disabled />
</td>
<th>상태</th>
<td>
<TextInput
value={data && blockStatus.find(item => item.value === data.status)?.name}
disabled />
</td>
</tr>
<tr>
<th>제재 사유</th>
<td>
<TextInput
value={data && blockSanctions.find(item => item.value === data.sanctions)?.name}
disabled />
</td>
{data.status === commonStatus.cancel &&
<>
<th>해지 사유</th>
<td> <td>
<TextInput value={data && data.guid || ''} disabled /> <TextInput
value={data && data.reason}
disabled />
</td> </td>
<th width="100px">아바타명</th> </>
<td> }
<TextInput value={data && data.nickname || ''} disabled /> </tr>
</td>
</tr>
<tr>
<th>제재 방식</th>
<td>
<TextInput value={type.map(item => item.value === data.type && item.name).filter(data => data !== false) || ''} disabled />
</td>
<th></th>
<td></td>
</tr>
<tr>
<th>제재 기간</th>
<td colSpan="3">
<InputWrapper>
<TextInput value={period.map(item => item.value === data.period && item.name).filter(data => data !== false) || ''} disabled />
<span>시작일 : {convertKTC(data.start_dt, false)}</span>
<span>종료일 : {convertKTC(data.end_dt, false)}</span>
</InputWrapper>
</td>
</tr>
<tr>
<th>제재 사유</th>
<td>
<TextInput value={sanctions.map(item => item.value === data.sanctions && item.name).filter(data => data !== false) || ''} disabled />
</td>
</tr>
</tbody> </tbody>
</table> </table>
</BlockDetailState> </BlockDetailState>
@@ -90,28 +87,27 @@ const UserBlockDetailModal = ({ stateModal, handleModal, data }) => {
<BlockCount>전체 제재 이력({history && history.length})</BlockCount> <BlockCount>전체 제재 이력({history && history.length})</BlockCount>
<BlockHistory> <BlockHistory>
<TableStyle> <TableStyle>
<thead> <thead>
<tr> <tr>
<th>일자</th> <th>일자</th>
<th width="10%">제재 기간</th> <th width="10%">제재 기간</th>
<th width="10%">제재 방식</th> <th width="10%">제재 방식</th>
<th width="20%">제재 사유</th> <th width="20%">제재 사유</th>
<th width="20%">등록자</th> <th width="20%">등록자</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{history && {history?.map((content, index) => (
history.map((content, index) => (
<Fragment key={index}> <Fragment key={index}>
<tr> <tr>
<td> <td>
{convertKTC(content.start_dt, false)} ~ {convertKTC(content.start_dt, false)} ~
{convertKTC(content.end_dt, false)} {convertKTC(content.end_dt, false)}
</td> </td>
<td>{period.map(item => item.value === data.period && item.name)}</td> <td>{blockPeriod.find(item => item.value === content.period)?.name}</td>
<td>{type.map(item => item.value === data.type && item.name)}</td> <td>{blockType.find(item => item.value === content.type)?.name}</td>
<td>{sanctions.map(item => item.value === data.sanctions && item.name)}</td> <td>{blockSanctions.find(item => item.value === content.sanctions)?.name}</td>
<td></td> <td>{content.create_by}</td>
</tr> </tr>
</Fragment> </Fragment>
))} ))}
@@ -181,9 +177,9 @@ const BlockHistory = styled.div`
top: 0; top: 0;
} }
tbody { tbody {
tr:first-child { //tr:first-child {
color: #d60000; // color: #d60000;
} //}
tr:last-child td { tr:last-child td {
border-bottom: 1px solid #e8eaec; border-bottom: 1px solid #e8eaec;
} }

View File

@@ -3,7 +3,7 @@ import Button from '../../common/button/Button';
import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar'; import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { LandAuctionView } from '../../../apis'; import { LandAuctionView } from '../../../apis';
import { landAuctionStatus, landSearchType, landSize, userSearchType } from '../../../assets/data'; import { landAuctionStatus, landSearchType, landSize, userType } from '../../../assets/data';
export const useLandAuctionSearch = (token, initialPageSize) => { export const useLandAuctionSearch = (token, initialPageSize) => {
const [searchParams, setSearchParams] = useState({ const [searchParams, setSearchParams] = useState({
@@ -144,7 +144,7 @@ const LandAuctionSearchBar = ({ searchParams, onSearch, onReset }) => {
<InputLabel>낙찰자</InputLabel> <InputLabel>낙찰자</InputLabel>
<InputGroup> <InputGroup>
<SelectInput value={searchParams.userType} onChange={e => onSearch({userType: e.target.value })}> <SelectInput value={searchParams.userType} onChange={e => onSearch({userType: e.target.value })}>
{userSearchType.map((data, index) => ( {userType.map((data, index) => (
<option key={index} value={data.value}> <option key={index} value={data.value}>
{data.name} {data.name}
</option> </option>

View File

@@ -1,8 +1,17 @@
import { useEffect, useState } from 'react'; import { memo, useEffect, useState } from 'react';
import { styled } from 'styled-components'; import { styled } from 'styled-components';
import { TextInput, InputLabel, SelectInput } from '../../../styles/Components'; import { TextInput, InputLabel, SelectInput } from '../../../styles/Components';
import { logDomain, opInputType } from '../../../assets/data/options'; import { logDomain, opInputType } from '../../../assets/data/options';
const TextInputWithHelp = memo(({ helpText, ...props }) => {
return (
<TextInputWrapper>
<TextInput {...props} />
{helpText && <HelpText>{helpText}</HelpText>}
</TextInputWrapper>
);
});
const SearchFilter = ({ value = [], onChange }) => { const SearchFilter = ({ value = [], onChange }) => {
const [filters, setFilters] = useState(value || []); const [filters, setFilters] = useState(value || []);
const [filterSections, setFilterSections] = useState([{ field_name: '', field_type: 'String', value: '' }]); const [filterSections, setFilterSections] = useState([{ field_name: '', field_type: 'String', value: '' }]);
@@ -55,14 +64,7 @@ const SearchFilter = ({ value = [], onChange }) => {
setIsOpen(!isOpen); setIsOpen(!isOpen);
}; };
const TextInputWithHelp = ({ helpText, ...props }) => {
return (
<TextInputWrapper>
<TextInput {...props} />
{helpText && <HelpText>{helpText}</HelpText>}
</TextInputWrapper>
);
};
return ( return (
<FilterWrapper> <FilterWrapper>

View File

@@ -3,7 +3,7 @@ import { TextInput, BtnWrapper, InputLabel, SelectInput, InputGroup } from '../.
import Button from '../../common/button/Button'; import Button from '../../common/button/Button';
import { SearchBarLayout } from '../../common/SearchBar'; import { SearchBarLayout } from '../../common/SearchBar';
import { blockPeriod, blockSanctions, blockSearchType, blockStatus } from '../../../assets/data'; import { blockPeriod, blockSanctions, blockSearchType, blockStatus } from '../../../assets/data';
import { userSearchType } from '../../../assets/data/options'; import { userType } from '../../../assets/data/options';
const UserBlockSearchBar = ({ handleSearch, setResultData }) => { const UserBlockSearchBar = ({ handleSearch, setResultData }) => {
const [searchData, setSearchData] = useState({ const [searchData, setSearchData] = useState({
@@ -52,7 +52,7 @@ const UserBlockSearchBar = ({ handleSearch, setResultData }) => {
<InputLabel>대상</InputLabel> <InputLabel>대상</InputLabel>
<InputGroup> <InputGroup>
<SelectInput value={searchData.searchType} onChange={e => setSearchData({ ...searchData, searchType: e.target.value })}> <SelectInput value={searchData.searchType} onChange={e => setSearchData({ ...searchData, searchType: e.target.value })}>
{userSearchType.map((data, index) => ( {userType.map((data, index) => (
<option key={index} value={data.value}> <option key={index} value={data.value}>
{data.name} {data.name}
</option> </option>

View File

@@ -5,28 +5,13 @@ import ResetIcon from '../../../assets/img/icon/icon-reset.png';
/** /**
* @param {string} text 버튼 내부 텍스트 * @param {string} text 버튼 내부 텍스트
* @param {string} type 버튼 타입 (button, submit) * @param {string} type 버튼 타입 (button, submit)
* @param {string} errorMessage 표시할 에러 메시지
* @param {() => void} handleClick 버튼 클릭시 실행할 이벤트, Route 기능의 경우 history.push와 같은 함수를 이용합니다. * @param {() => void} handleClick 버튼 클릭시 실행할 이벤트, Route 기능의 경우 history.push와 같은 함수를 이용합니다.
* @param {string} buttonColor 버튼 배경 색상 * @param {string} buttonColor 버튼 배경 색상
* @param {string} hoverColor 버튼 호버 배경 색상 * @param {string} hoverColor 버튼 호버 배경 색상
* @param {object} props 폰트 관련 속성 * @param {object} props 폰트 관련 속성
*/ */
const Button = ({ text, type = 'button', errorMessage, handleClick, theme, size, width, height, bordercolor, disabled, name }) => { const Button = ({ text, type = 'button', handleClick, theme, size, width, height, bordercolor, disabled, name }) => {
const [isShowErrorMessage, setIsShowErrorMessage] = useState(false);
const handleButtonClick = () => {
if (errorMessage) {
setIsShowErrorMessage(true);
} else {
handleClick?.();
}
};
useEffect(() => {
errorMessage || setIsShowErrorMessage(false);
}, [errorMessage]);
return ( return (
<Wrapper width={width}> <Wrapper width={width}>
<ButtonStyle <ButtonStyle
@@ -42,7 +27,6 @@ const Button = ({ text, type = 'button', errorMessage, handleClick, theme, size,
name={name}> name={name}>
{text} {text}
</ButtonStyle> </ButtonStyle>
{isShowErrorMessage && <ErrorText>{errorMessage}</ErrorText>}
</Wrapper> </Wrapper>
); );
}; };
@@ -139,13 +123,4 @@ const ButtonStyle = styled.button`
`} `}
`; `;
const ErrorText = styled.p`
color: red;
text-align: center;
font-weight: 400;
font-size: 12px;
margin-top: 10px;
line-height: 15px;
`;
export default Button; export default Button;

View File

@@ -58,6 +58,7 @@ const DynamicModal = ({modalType, view, handleSubmit, handleCancel, modalText, c
<ButtonClose onClick={handleCancel} /> <ButtonClose onClick={handleCancel} />
</BtnWrapper> </BtnWrapper>
<ModalText $align="center"> <ModalText $align="center">
{children && children}
{ChildView && <ChildView />} {ChildView && <ChildView />}
</ModalText> </ModalText>
<BtnWrapper $gap="10px"> <BtnWrapper $gap="10px">

View File

@@ -44,6 +44,14 @@ const resources = {
WARNING_TYPE_CHECK: '타입을 확인해주세요.', WARNING_TYPE_CHECK: '타입을 확인해주세요.',
DATE_START_DIFF_END_WARNING :"종료일은 시작일보다 하루 이후여야 합니다.", DATE_START_DIFF_END_WARNING :"종료일은 시작일보다 하루 이후여야 합니다.",
SEARCH_REQUIRED_WARNING:"필수 입력 항목을 확인해주세요.", SEARCH_REQUIRED_WARNING:"필수 입력 항목을 확인해주세요.",
INPUT_VALUE_ERROR: "입력 값이 확인되지 않습니다.\n새로고침 후 다시 시도해주세요.",
INPUT_REASON_EMPTY_WARNING: "사유를 입력해주세요.",
DUPLICATE_USER: "중복된 유저 정보가 있습니다.",
COUNT_EMPTY_WARNING: "수량을 입력해주세요.",
UPLOAD_FILENAME_SAMPLE_WARNING: "파일명에 sample을 넣을 수 없습니다.\r\n파일명을 변경 후 다시 업로드 해주세요.",
//user
NICKNAME_CHANGES_CONFIRM: '닉네임을 변경하시겠습니까?',
NICKNAME_CHANGES_COMPLETE: '닉네임 변경이 완료되었습니다.',
//table //table
TABLE_ITEM_DELETE_TITLE: "선택 삭제", TABLE_ITEM_DELETE_TITLE: "선택 삭제",
TABLE_BUTTON_DETAIL_TITLE: "상세보기", TABLE_BUTTON_DETAIL_TITLE: "상세보기",
@@ -134,8 +142,9 @@ const resources = {
// 이용자 제재 // 이용자 제재
USER_BLOCK_VALIDATION_WARNING: '유효성 체크가 통과되지 않은 항목이 존재합니다.\r\n수정 후 재등록 해주세요.', USER_BLOCK_VALIDATION_WARNING: '유효성 체크가 통과되지 않은 항목이 존재합니다.\r\n수정 후 재등록 해주세요.',
USER_BLOCK_REGIST_DUPLE_WARNING: '이미 제재가 등록된 유저입니다.', USER_BLOCK_REGIST_DUPLE_WARNING: '이미 제재가 등록된 유저입니다.',
USER_BLOCK_DELETE_CONFIRM: '제재 대상을 삭제하시겠습니까?\r\n삭제 시 제재가 해제됩니다.', USER_BLOCK_DELETE_CONFIRM: '제재 대상을 해지하시겠습니까?',
USER_BLOCK_REGIST_CONFIRM: '이용자 제재 명단에\r\n등록하시겠습니까?', USER_BLOCK_REGIST_CONFIRM: '이용자 제재 명단에\r\n등록하시겠습니까?',
USER_BLOCK_DELETE_STATUS_WARNING: '제재를 해지할 수 없는 상태입니다.',
//파일 //파일
FILE_IMAGE_EXTENSION_WARNING: "png, jpg 확장자의 이미지만 업로드 가능합니다.", FILE_IMAGE_EXTENSION_WARNING: "png, jpg 확장자의 이미지만 업로드 가능합니다.",
FILE_IMAGE_UPLOAD_ERROR: "이미지 업로드 중 오류가 발생했습니다.", FILE_IMAGE_UPLOAD_ERROR: "이미지 업로드 중 오류가 발생했습니다.",
@@ -148,7 +157,11 @@ const resources = {
FILE_BUSINESS_LOG: 'Caliverse_Log.xlsx', FILE_BUSINESS_LOG: 'Caliverse_Log.xlsx',
FILE_BATTLE_EVENT: 'Caliverse_Battle_Event.xlsx', FILE_BATTLE_EVENT: 'Caliverse_Battle_Event.xlsx',
//서버 에러메시지 //서버 에러메시지
DYNAMODB_NOT_USER: '유저 정보를 확인해주세요.' DYNAMODB_NOT_USER: '유저 정보를 확인해주세요.',
NICKNAME_EXIT_ERROR: '해당 닉네임이 존재합니다.',
NICKNAME_NUMBER_ERROR: '닉네임은 첫번째 글자에 숫자를 허용하지 않습니다.',
NICKNAME_SPECIALCHAR_ERROR: '닉네임은 특수문자를 사용할 수 없습니다.',
NICKNAME_LANGTH_ERROR: '닉네임은 최소 2글자에서 최대 12글자까지 허용 합니다.'
} }
}, },
en: { en: {

View File

@@ -100,6 +100,11 @@ const Event = () => {
case "deleteConfirm": case "deleteConfirm":
let list = []; let list = [];
if(deleteDesc.length === 0){
showToast('INPUT_REASON_EMPTY_WARNING', {type: alertTypes.warning});
return;
}
let isChecked = false; let isChecked = false;
selectedRows.map(data => { selectedRows.map(data => {

View File

@@ -27,11 +27,11 @@ import {
RegistInputRow, RegistNotice, RegistTable, RegistInputRow, RegistNotice, RegistTable,
} from '../../styles/ModuleComponents'; } from '../../styles/ModuleComponents';
import AuthModal from '../../components/common/modal/AuthModal'; import AuthModal from '../../components/common/modal/AuthModal';
import { authType, benItems, wellType } from '../../assets/data'; import { authType, benItems, currencyType } from '../../assets/data';
import DateTimeInput from '../../components/common/input/DateTimeInput'; import DateTimeInput from '../../components/common/input/DateTimeInput';
import { timeDiffMinute } from '../../utils'; import { timeDiffMinute } from '../../utils';
import { useAlert } from '../../context/AlertProvider'; import { useAlert } from '../../context/AlertProvider';
import { alertTypes } from '../../assets/data/types'; import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
import { useLoading } from '../../context/LoadingProvider'; import { useLoading } from '../../context/LoadingProvider';
const EventRegist = () => { const EventRegist = () => {
@@ -44,7 +44,7 @@ const EventRegist = () => {
const [item, setItem] = useState(''); // 아이템 값 const [item, setItem] = useState(''); // 아이템 값
const [itemCount, setItemCount] = useState(''); // 아이템 개수 const [itemCount, setItemCount] = useState(''); // 아이템 개수
const [resource, setResource] = useState('19010001'); // 자원 값 const [resource, setResource] = useState(currencyCodeTypes.gold); // 자원 값
const [resourceCount, setResourceCount] = useState(''); // 자원 개수 const [resourceCount, setResourceCount] = useState(''); // 자원 개수
const [isNullValue, setIsNullValue] = useState(false); const [isNullValue, setIsNullValue] = useState(false);
@@ -209,7 +209,7 @@ const EventRegist = () => {
const item_cnt = resultData.item_list[itemIndex].item_cnt; const item_cnt = resultData.item_list[itemIndex].item_cnt;
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount); resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
} else { } else {
const name = wellType.find(well => well.value === resource).name; const name = currencyType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name }; const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem); resultData.item_list.push(newItem);
} }
@@ -403,7 +403,7 @@ const EventRegist = () => {
<td> <td>
<RegistInputItem> <RegistInputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource}> <SelectInput onChange={e => setResource(e.target.value)} value={resource}>
{wellType.map((data, index) => ( {currencyType.filter(data => data.value !== currencyCodeTypes.calium).map((data, index) => (
<option key={index} value={data.value}> <option key={index} value={data.value}>
{data.name} {data.name}
</option> </option>

View File

@@ -146,7 +146,6 @@ const Items = () => {
const ConfirmChild = () => { const ConfirmChild = () => {
if(selectedRows === undefined || selectedRows.length === 0) return; if(selectedRows === undefined || selectedRows.length === 0) return;
const selectRow = selectedRows[0]; const selectRow = selectedRows[0];
console.log(selectRow)
return( return(
<InputItem> <InputItem>
<p>{t('DEL_COUNT_CONFIRM', {count: selectRow?.count})}</p> <p>{t('DEL_COUNT_CONFIRM', {count: selectRow?.count})}</p>

View File

@@ -19,7 +19,7 @@ import {
import IconDelete from '../../assets/img/icon/icon-delete.png'; import IconDelete from '../../assets/img/icon/icon-delete.png';
import CloseIcon from '../../assets/img/icon/icon-close.png'; import CloseIcon from '../../assets/img/icon/icon-close.png';
import { benItems, HourList, MinuteList, wellType } from '../../assets/data'; import { benItems, HourList, mailType, MinuteList, currencyType, userType } from '../../assets/data';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import MailRegistUploadBtn from '../../components/ServiceManage/MailRegistUploadBtn'; import MailRegistUploadBtn from '../../components/ServiceManage/MailRegistUploadBtn';
@@ -35,6 +35,7 @@ import { useDataFetch } from '../../hooks/hook';
import { alertTypes, currencyCodeTypes } from '../../assets/data/types'; import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
import { useLoading } from '../../context/LoadingProvider'; import { useLoading } from '../../context/LoadingProvider';
import { useAlert } from '../../context/AlertProvider'; import { useAlert } from '../../context/AlertProvider';
import { userType2 } from '../../assets/data/options';
const MailRegist = () => { const MailRegist = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -44,14 +45,12 @@ const MailRegist = () => {
const { showModal, showToast } = useAlert(); const { showModal, showToast } = useAlert();
const { withLoading } = useLoading(); const { withLoading } = useLoading();
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
const [sendHour, setSendHour] = useState('00'); const [sendHour, setSendHour] = useState('00');
const [sendMin, setSendMin] = useState('00'); const [sendMin, setSendMin] = useState('00');
const [item, setItem] = useState(''); const [item, setItem] = useState('');
const [itemCount, setItemCount] = useState(''); const [itemCount, setItemCount] = useState('');
const [resource, setResource] = useState('19010001'); const [resource, setResource] = useState(currencyCodeTypes.gold);
const [resourceCount, setResourceCount] = useState(0); const [resourceCount, setResourceCount] = useState(0);
const [isNullValue, setIsNullValue] = useState(false); const [isNullValue, setIsNullValue] = useState(false);
@@ -61,8 +60,6 @@ const MailRegist = () => {
const [excelName, setExcelName] = useState(null); const [excelName, setExcelName] = useState(null);
const [isItemNullValue, setIsItemNullValue] = useState(false); const [isItemNullValue, setIsItemNullValue] = useState(false);
const [isResourceNullValue, setIsResourceNullValue] = useState(false);
const [alertMessage, setAlertMessage] = useState('');
const { const {
data: caliumTotalData, data: caliumTotalData,
@@ -96,6 +93,35 @@ const MailRegist = () => {
guid: '', guid: '',
}); });
useEffect(() => {
// 세션 스토리지에서 복사된 메일 데이터 가져오기
const copiedMailData = sessionStorage.getItem('copyMailData');
if (copiedMailData) {
const mailData = JSON.parse(copiedMailData);
setResultData({
is_reserve: mailData.is_reserve,
send_dt: '',
mail_type: mailData.mail_type,
receive_type: mailData.receive_type,
user_type: mailData.user_type,
mail_list: mailData.mail_list,
item_list: mailData.item_list,
resource_list: mailData.resource_list,
guid: mailData.receive_type === 'SINGLE' ? mailData.guid : ''
});
if (mailData.receive_type === 'MULTIPLE') {
setExcelFile(null);
setExcelName(null);
}
// 사용 후 세션 스토리지 데이터 삭제
sessionStorage.removeItem('copyMailData');
}
}, []);
useEffect(() => { useEffect(() => {
if (checkCondition()) { if (checkCondition()) {
setIsNullValue(false); setIsNullValue(false);
@@ -125,32 +151,35 @@ const MailRegist = () => {
} }
item.length === 0 || itemCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false); item.length === 0 || itemCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
// const token = sessionStorage.getItem('token'); await withLoading(async () => {
const result = await MailIsItem(token, { item: item }); return await MailIsItem(token, { item: item });
}).then(data => {
if (data.result === 'ERROR') {
showToast(data.data.message, { type: alertTypes.warning });
}else{
if (item.length === '' || itemCount.length === 0 || itemCount <= 0) {
setIsItemNullValue(true);
} else if (item.length !== 0) {
setIsItemNullValue(false);
const itemIndex = resultData.item_list.findIndex(
data => data.item === item
);
if (result.data.result === 'ERROR') { if (itemIndex !== -1) {
showToast(result.data.data.message, { type: alertTypes.warning }); showToast('MAIL_ITEM_ADD_DUPL', { type: alertTypes.warning });
return; return;
} }
const newItem = { item: item, item_cnt: itemCount, item_name: data.data.item_info.item_name };
if (item.length === '' || itemCount.length === 0 || itemCount <= 0) { resultData.item_list.push(newItem);
setIsItemNullValue(true); setItem('');
} else if (item.length !== 0) { setItemCount('');
setIsItemNullValue(false); }
const itemIndex = resultData.item_list.findIndex(
(item) => item.item === resource,
);
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 }; }).catch(e => {
showToast(e, { type: alertTypes.error });
});
resultData.item_list.push(newItem);
setItem('');
setItemCount('');
}
}; };
const onItemRemove = id => { const onItemRemove = id => {
@@ -213,7 +242,7 @@ const MailRegist = () => {
return; return;
} }
} }
const name = wellType.find(well => well.value === resource).name; const name = currencyType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name }; const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem); resultData.item_list.push(newItem);
} }
@@ -222,11 +251,6 @@ const MailRegist = () => {
} }
}; };
const onResourceRemove = id => {
let filterList = resultData.resource_list && resultData.resource_list.filter(item => item !== resultData.resource_list[id]);
setResultData({ ...resultData, resource_list: filterList });
};
// 발송 날짜 세팅 로직 // 발송 날짜 세팅 로직
const handleSelectedDate = data => { const handleSelectedDate = data => {
const sendDate = new Date(data); const sendDate = new Date(data);
@@ -317,9 +341,8 @@ const MailRegist = () => {
const checkCondition = () => { const checkCondition = () => {
return resultData.mail_list.every(data => data.content !== '' && data.title !== '') && return resultData.mail_list.every(data => data.content !== '' && data.title !== '') &&
(resultData.receive_type === 'MULTIPLE' ? excelFile !== null : resultData.guid !== '') && (resultData.receive_type === 'MULTIPLE' ? excelFile !== null : resultData.guid !== '') &&
(resultData.is_reserve ? resultData.send_dt.length !== 0 : resultData.send_dt.length === 0) && (resultData.is_reserve ? resultData.send_dt.length !== 0 : true) &&
resultData.mail_type !== 'SELECT' resultData.mail_type !== 'SELECT'
&& !alertMessage
}; };
return ( return (
@@ -375,10 +398,11 @@ const MailRegist = () => {
<SelectInput onChange={e => setResultData({ ...resultData, mail_type: e.target.value })} <SelectInput onChange={e => setResultData({ ...resultData, mail_type: e.target.value })}
value={resultData.mail_type}> value={resultData.mail_type}>
<option value="SELECT">타입 선택</option> <option value="SELECT">타입 선택</option>
<option value="SYSTEM_GUID">시스템 안내</option> {mailType.filter(data => data.value !== 'ALL').map((data, index) => (
<option value="INSPECTION_COMPENSATION">점검 보상</option> <option key={index} value={data.value}>
<option value="RECOVER_COMPENSATION">복구 보상</option> {data.name}
<option value="EVENT_COMPENSATION">이벤트 보상</option> </option>
))}
</SelectInput> </SelectInput>
</InputItem> </InputItem>
</RegistInputRow> </RegistInputRow>
@@ -389,9 +413,14 @@ const MailRegist = () => {
<InputItem> <InputItem>
<SelectInput <SelectInput
onChange={e => setResultData({ ...resultData, user_type: e.target.value })} onChange={e => setResultData({ ...resultData, user_type: e.target.value })}
value={resultData.user_type} disabled={resultData.receive_type !== 'SINGLE'}> value={resultData.user_type}
<option value="GUID">GUID</option> disabled={resultData.receive_type !== 'SINGLE'}
<option value="NICKNAME">아바타명</option> >
{userType2.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
<option value="EMAIL">이메일</option> <option value="EMAIL">이메일</option>
</SelectInput> </SelectInput>
</InputItem> </InputItem>
@@ -436,8 +465,6 @@ const MailRegist = () => {
setResultData={setResultData} setResultData={setResultData}
resultData={resultData} resultData={resultData}
excelFile={excelFile} excelFile={excelFile}
alertMessage={alertMessage}
setAlertMessage={setAlertMessage}
setExcelFile={setExcelFile} setExcelFile={setExcelFile}
excelName={excelName} excelName={excelName}
setExcelName={setExcelName} setExcelName={setExcelName}
@@ -541,24 +568,6 @@ const MailRegist = () => {
theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'} theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'}
handleClick={handleItemList} width="100px" height="35px" /> handleClick={handleItemList} width="100px" height="35px" />
</InputItem> </InputItem>
{/* {isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
<div>
{resultData.item_list && (
<ItemList>
{resultData.item_list.map((data, index) => {
return (
<Item key={index}>
<span>
{data.item}({data.item_cnt})
</span>
<BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>
</Item>
);
})}
</ItemList>
)}
</div> */}
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -568,10 +577,11 @@ const MailRegist = () => {
<td> <td>
<InputItem> <InputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource}> <SelectInput onChange={e => setResource(e.target.value)} value={resource}>
<option value="19010001">골드</option> {currencyType.map((data, index) => (
<option value="19010002">사파이어</option> <option key={index} value={data.value}>
<option value="19010005">루비</option> {data.name}
<option value="19010003">칼리움</option> </option>
))}
</SelectInput> </SelectInput>
<TextInput placeholder="수량" type="number" value={resourceCount} <TextInput placeholder="수량" type="number" value={resourceCount}
onChange={e => handleResourceCount(e)} width="200px" /> onChange={e => handleResourceCount(e)} width="200px" />
@@ -634,12 +644,6 @@ const MailRegist = () => {
export default MailRegist; export default MailRegist;
const InputRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 10px 50px;
`;
const InputItem = styled.div` const InputItem = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -1,5 +1,5 @@
import { useState, Fragment, useRef } from 'react'; import { useState, Fragment, useRef, useTransition } from 'react';
import { Title, FormWrapper } from '../../styles/Components'; import { Title, FormWrapper, TextInput } from '../../styles/Components';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {
CommonSearchBar, CommonSearchBar,
@@ -9,29 +9,35 @@ import {
import { BlackListDetail, BlackListDelete } from '../../apis'; import { BlackListDetail, BlackListDelete } from '../../apis';
import Pagination from '../../components/common/Pagination/Pagination'; import Pagination from '../../components/common/Pagination/Pagination';
import { authType } from '../../assets/data'; import { authType, commonStatus, modalTypes } from '../../assets/data';
import { CaliTable, TableHeader } from '../../components/common'; import { CaliTable, TableHeader } from '../../components/common';
import { useModal, useTable, withAuth } from '../../hooks/hook'; import { useModal, useTable, withAuth } from '../../hooks/hook';
import { alertTypes } from '../../assets/data/types'; import { alertTypes, customStatus } from '../../assets/data/types';
import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants'; import { INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
import tableInfo from '../../assets/data/pages/userBlockTable.json' import tableInfo from '../../assets/data/pages/userBlockTable.json'
import { useAlert } from '../../context/AlertProvider'; import { useAlert } from '../../context/AlertProvider';
import { useLoading } from '../../context/LoadingProvider'; import { useLoading } from '../../context/LoadingProvider';
import useCommonSearchOld from '../../hooks/useCommonSearchOld'; import useCommonSearchOld from '../../hooks/useCommonSearchOld';
import { ModalInputItem, ModalSubText, RegistInputItem } from '../../styles/ModuleComponents';
import DynamicModal from '../../components/common/modal/DynamicModal';
import { useTranslation } from 'react-i18next';
const UserBlock = () => { const UserBlock = () => {
const token = sessionStorage.getItem('token'); const token = sessionStorage.getItem('token');
const navigate = useNavigate(); const navigate = useNavigate();
const {showModal, showToast} = useAlert(); const {showModal, showToast} = useAlert();
const {withLoading} = useLoading(); const {withLoading} = useLoading();
const { t } = useTranslation();
const [detail, setDetail] = useState([]); const [detail, setDetail] = useState([]);
const [deleteDesc, setDeleteDesc] = useState('');
const { const {
modalState, modalState,
handleModalView, handleModalView,
handleModalClose handleModalClose
} = useModal({ } = useModal({
detail: 'hidden', detail: 'hidden',
delete: 'hidden'
}); });
const { const {
@@ -63,27 +69,44 @@ const UserBlock = () => {
}); });
break; break;
case "delete": case "delete":
showModal('USER_BLOCK_DELETE_CONFIRM', { const selectRow = selectedRows[0];
type: alertTypes.confirm, if(selectRow.status === commonStatus.cancel || selectRow.status === customStatus.expiration || selectRow.status === commonStatus.fail) {
onConfirm: () => handleAction('deleteConfirm') showToast('USER_BLOCK_DELETE_STATUS_WARNING',{type: alertTypes.warning})
}); return;
}
handleModalView('delete');
// showModal('USER_BLOCK_DELETE_CONFIRM', {
// type: alertTypes.confirm,
// onConfirm: () => handleAction('deleteConfirm')
// });
break; break;
case "deleteConfirm": case "deleteConfirm":
let list = []; let list = [];
if(deleteDesc.length === 0){
showToast('INPUT_REASON_EMPTY_WARNING', {type: alertTypes.warning});
return;
}
selectedRows.map(data => { selectedRows.map(data => {
list.push({ list.push({
id: data.id, id: data.id,
reason: deleteDesc
}); });
}); });
await withLoading(async () => { await withLoading(async () => {
return BlackListDelete(token, list); return BlackListDelete(token, list);
}).then(data => { }).then(data => {
showToast('DEL_COMPLETE', {type: alertTypes.success}); if(data.result === "SUCCESS") {
showToast('DEL_COMPLETE', {type: alertTypes.success});
}else{
showToast(data.data.message, {type: alertTypes.error});
}
}).catch(reason => { }).catch(reason => {
showToast('API_FAIL', {type: alertTypes.error}); showToast('API_FAIL', {type: alertTypes.error});
}).finally(() => { }).finally(() => {
handleModalClose('delete');
handleSearch(updateSearchParams); handleSearch(updateSearchParams);
}); });
@@ -141,6 +164,29 @@ const UserBlock = () => {
data={detail} data={detail}
handleModal={() => handleModalClose('detail')} handleModal={() => handleModalClose('detail')}
/> />
<DynamicModal
modalType={modalTypes.childOkCancel}
view={modalState.deleteModal}
handleCancel={() => handleModalClose('delete')}
handleSubmit={() => handleAction('deleteConfirm')}
>
<ModalInputItem>
{t('USER_BLOCK_DELETE_CONFIRM')}
<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>
</> </>
); );
}; };

View File

@@ -228,7 +228,7 @@ const UserBlockRegist = () => {
showToast('USER_BLOCK_VALIDATION_WARNING', {type: alertTypes.warning}); showToast('USER_BLOCK_VALIDATION_WARNING', {type: alertTypes.warning});
return; return;
} }
}; }
showModal('USER_BLOCK_REGIST_CONFIRM', { showModal('USER_BLOCK_REGIST_CONFIRM', {
type: alertTypes.confirm, type: alertTypes.confirm,
onConfirm: () => handleSubmit('registConfirm'), onConfirm: () => handleSubmit('registConfirm'),
@@ -237,7 +237,7 @@ const UserBlockRegist = () => {
case 'registConfirm': case 'registConfirm':
await withLoading(async () => { await withLoading(async () => {
return await BlackListRegist(token, resultData); return await BlackListRegist(token, resultData);
}).then(data => { }).then(data => {
if(data.result === 'ERROR'){ if(data.result === 'ERROR'){
showToast(data.data.message, {type: alertTypes.error}); showToast(data.data.message, {type: alertTypes.error});

View File

@@ -825,4 +825,10 @@ export const StyledSelectInput = styled(SelectInput)`
export const TimeSeparator = styled.span` export const TimeSeparator = styled.span`
color: #94a3b8; color: #94a3b8;
margin: 0 2px; margin: 0 2px;
`;
export const CopyBtn = styled.div`
position: absolute;
right: 0;
top: 0;
`; `;