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 },
});
return res;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('BlackListDelete', e);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,16 +2,19 @@ import { styled } from 'styled-components';
import { useEffect, useState } from 'react';
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 Modal from '../../components/common/modal/Modal';
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 {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
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({
guid: '',
@@ -20,42 +23,42 @@ const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
});
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]);
// 수정 모달창
const handleModifyModal = () => {
if (modifyModal === 'hidden') {
setModifyModal('view');
} else {
setModifyModal('hidden');
const handleSubmit = async (type, param = null) => {
switch (type) {
case "nicknameChangeSubmit":
showModal('NICKNAME_CHANGES_CONFIRM', {
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 (
<>
@@ -82,29 +85,7 @@ const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
</PwSetTable>
<BtnWrapper $justify="center" $gap="10px">
<Button theme="line" text="취소" handleClick={handleClick} />
<Button theme="primary" text="변경하기" handleClick={handleModifyModal} />
</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} />
<Button theme="primary" text="변경하기" handleClick={() => handleSubmit('nicknameChangeSubmit')} />
</BtnWrapper>
</Modal>
</>

View File

@@ -193,10 +193,10 @@ const UserDefaultInfo = ({ userInfo }) => {
<td colSpan="3">
{dataList.char_info && dataList.char_info.character_name}
<EditButton
hidden={true}
// hidden={true}
onClick={e => {
e.preventDefault();
handleModalClose('pwChange');
handleModalView('pwChange');
}}></EditButton>
</td>
</tr>
@@ -219,7 +219,13 @@ const UserDefaultInfo = ({ userInfo }) => {
</tbody>
</UserInfoTable>
</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="50%">아이템</th>
<th width="100">보유개수</th>
<th width="60">삭제</th>
{/*<th width="60">삭제</th>*/}
</tr>
</thead>
<tbody>
@@ -205,9 +205,9 @@ const UserInventoryInfo = ({ userInfo }) => {
<td>{el.item_id}</td>
<td>{el.item_name}</td>
<td>{el.count}</td>
<td>
<Button theme={authDelete ? "line" : "disable"} id={el.item_guid} name="single" text="삭제" handleClick={e => {handleDeleteConfirmModal(el, type)}} disabled={!authDelete}/>
</td>
{/*<td>*/}
{/* <Button theme={authDelete ? "line" : "disable"} id={el.item_guid} name="single" text="삭제" handleClick={e => {handleDeleteConfirmModal(el, type)}} disabled={!authDelete}/>*/}
{/*</td>*/}
</tr>
);
})}
@@ -246,136 +246,6 @@ const UserInventoryInfo = ({ userInfo }) => {
modalText={t('DEL_COMPLETE')}
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 MailDetailModal from '../../components/DataManage/MailDetailModal';
import { SelectInput, TextInput } from '../../styles/Components';
import { UserMailDelete, UserMailItemDelete, UserMailView } from '../../apis';
import ConfirmModal from '../common/modal/ConfirmModal';
import CompletedModal from '../common/modal/CompletedModal';
import { useTranslation } from 'react-i18next';
import CustomConfirmModal from '../common/modal/CustomConfirmModal';
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 { useDynamoDBPagination, useModal } from '../../hooks/hook';
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 token = sessionStorage.getItem('token');
const { t } = useTranslation();
const authInfo = useRecoilValue(authList);
const {withLoading} = useLoading();
const {showModal, showToast} = useAlert();
// 받은 우편, 보낸 우편
const [option, setOption] = useState('RECEIVE');
const [detail, setDetail] = useState({ title: '', content: '', item_list: [], mail_guid: '' });
const [deleteSelected, setDeleteSelected] = useState({});
const [itemUpdateCount, setItemUpdateCount] = useState('1');
const [authDelete, setAuthDelete] = useState(false);
const {
@@ -36,10 +72,7 @@ const UserMailInfo = ({ userInfo }) => {
handleModalClose
} = useModal({
detail: 'hidden',
deleteItem: 'hidden',
deleteSubmit: 'hidden',
deleteComplete: 'hidden',
deleteItemComplete: 'hidden'
deleteItem: 'hidden'
});
const fetchMailData = async (page, startKey) => {
@@ -74,32 +107,14 @@ const UserMailInfo = ({ userInfo }) => {
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) => {
let params;
let result;
switch (type) {
case "detail":
handleModalView('deleteSubmit');
break;
case "deleteItem":
setDeleteSelected(param);
handleModalView('deleteItem');
showModal('USER_MAIL_DEL_CONFIRM', {
type: alertTypes.confirm,
onConfirm: () => handleModalSubmit('deleteSubmit')
});
break;
case "deleteSubmit":
params = {}
@@ -107,58 +122,64 @@ const UserMailInfo = ({ userInfo }) => {
params.guid = userInfo.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');
handleModalView('deleteComplete');
break;
case "deleteItem":
setDeleteSelected(param);
handleModalView('deleteItem');
break;
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.type = option;
params.guid = userInfo.guid;
params.mail_guid = detail.mail_guid;
params.item_id = deleteSelected.item_id;
params.parrent_count = deleteSelected.count;
params.count = itemUpdateCount;
params.count = count;
result = await UserMailItemDelete(token, params);
if(result.result === "SUCCESS"){
if(deleteSelected.count <= itemUpdateCount){
const item_idx = detail.item_list.findIndex(item => item.item_id === deleteSelected.item_id);
if(item_idx >= 0) {
detail.item_list.splice(item_idx, 1);
await withLoading(async () => {
return await UserMailItemDelete(token, params);
}).then(data => {
if(data.result === "SUCCESS") {
if(deleteSelected.count <= count){
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{
deleteSelected.count = deleteSelected.count - itemUpdateCount;
showToast(data.data.message, {type: alertTypes.error});
}
}
handleModalClose('deleteItem');
handleModalView('deleteItemComplete');
break;
case "deleteComplete":
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');
}).catch(e => {
showToast(e, {type: alertTypes.error});
}).finally(() => {
handleModalClose('deleteItem');
fetchPage(pagination.currentPage);
});
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 (
loading ? <TableSkeleton count={10}/> :
<>
@@ -195,7 +216,6 @@ const UserMailInfo = ({ userInfo }) => {
<th width="100">첨부 아이템</th>
<th width="80">수령</th>
<th width="80">시스템 우편</th>
{/* <th width="170">수령 일자</th> */}
</tr>
</thead>
<tbody>
@@ -218,9 +238,6 @@ const UserMailInfo = ({ userInfo }) => {
<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>{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>
);
})}
@@ -268,7 +285,6 @@ const UserMailInfo = ({ userInfo }) => {
</>
)}
{/*상세*/}
<MailDetailModal
mailModal={modalState.detailModal}
@@ -279,32 +295,19 @@ const UserMailInfo = ({ userInfo }) => {
handleItemDelete={(param) => handleModalSubmit('deleteItem', param)}
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
ChildView={ConfirmChild}
ChildView={() => (
<ConfirmChild
maxCount={deleteSelected.count}
/>
)}
view={modalState.deleteItemModal}
handleSubmit={() => handleModalSubmit('deleteItemSubmit')}
handleCancel={() => 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 {
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 { 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(() => {
excelName && setExcelName(null);
}, [downloadData]);
@@ -12,6 +16,12 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
const handleFile = async e => {
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]);
setExcelName(e.target.files[0].name);
@@ -21,11 +31,9 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
if (message === 'Excel 파일을 선택해주세요.'
|| message === 'Maximum upload size exceeded'
|| message === "guid(32자)를 확인해주세요.") {
setAlertMessage('유효하지 않은 파일입니다.');
showToast('FILE_NOT_EXIT_ERROR', {type: alertTypes.warning});
} else if (message === '중복된 유저 정보가 있습니다.') {
setAlertMessage('중복된 유저 정보가 있습니다.');
} else {
setAlertMessage('');
showToast('DUPLICATE_USER', {type: alertTypes.warning});
}
handlePostGuid(updateData);
@@ -33,7 +41,6 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
// 저장된 엑셀 파일 -> API 로 전송
const handlePostGuid = fileName => {
// console.log("fileName.data.data.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');
};
// console.log('undefinedFile 컴포넌트에서 ', undefinedFile);
// console.log('excelFile ...', excelFile);
// console.log("excelName", excelName);
console.log("excelName", excelName);
console.log("downloadData", downloadData);
// console.log(resultData);
return (
<>
@@ -78,21 +83,19 @@ const MailRegistUploadBtn = ({ disabled, setResultData, resultData, downloadData
)}
{/* 등록 페이지 : 파일이 있을 때 파일 삭제용 버튼 */}
{excelName ? (
<>
<FileButton
onClick={() => {
handleFileDelete();
setExcelName(null);
}}>
파일 삭제
</FileButton>
<AlertText>{alertMessage}</AlertText>
</>
<FileButton
disabled={disabled}
onClick={() => {
handleFileDelete();
setExcelName(null);
}}>
파일 삭제
</FileButton>
) : // 상세 페이지 : 저장된 파일이 복수일 때
resultData.guid && resultData.receive_type === 'MULTIPLE' ? (
<>
<FileButton
disabled={resultData.is_reserve === false && disabledBtn || status === 'FINISH'}
disabled={disabled}
onClick={e => {
handleDetailDelete(e);
setExcelName(null);

View File

@@ -1,5 +1,4 @@
import styled, { css } from 'styled-components';
import { useForm } from 'react-hook-form';
import { useState, useEffect, Fragment } from 'react';
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 { 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 token = sessionStorage.getItem('token');
const {withLoading} = useLoading();
const {showToast} = useAlert();
// onchange states
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'];
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 => {
@@ -75,9 +87,10 @@ const UserBlockUploadBtn = ({ disabled, setGuidList, guidList, typeError, setTyp
</InputGroup>
</FileWrapper>
</div>
<Modal $bgcolor="transparent" min="300px" $view={previewModal}>
<Title $align="center">업로드 미리보기</Title>
<Count> 제재 인원 : 45</Count>
<Count> 제재 인원 : {guidList.length}</Count>
<TableWrapper>
<TableStyle>
<thead>

View File

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

View File

@@ -13,6 +13,7 @@ import { NoticeModify } from '../../../apis';
import { useTranslation } from 'react-i18next';
import { convertKTC, convertKTCDate } from '../../../utils';
import { languageType } from '../../../assets/data/options';
import { CopyBtn } from '../../../styles/ModuleComponents';
const BoardInfoModal = ({ detailView, setDetailView, content, id, setIsCopyData, openRegistModal, userInfo }) => {
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)`
font-size: 16px;
line-height: 24px;

View File

@@ -8,7 +8,7 @@ import { EventIsItem, EventModify } from '../../../apis';
import { authList } from '../../../store/authList';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import { authType, benItems, commonStatus, wellType } from '../../../assets/data';
import { authType, benItems, commonStatus, currencyType } from '../../../assets/data';
import {
AppendRegistBox, AppendRegistTable, AreaBtnClose,
BtnDelete, DetailInputItem, DetailInputRow,
@@ -177,7 +177,7 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
const item_cnt = resultData.item_list[itemIndex].item_cnt;
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
} 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 };
resultData.item_list.push(newItem);
}
@@ -447,7 +447,7 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
<td>
<DetailInputItem>
<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}>
{data.name}
</option>

View File

@@ -1,9 +1,9 @@
import { styled } from 'styled-components';
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 { 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 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 DatePickerComponent from '../../common/Date/DatePickerComponent';
import MailRegistUploadBtn from '../MailRegistUploadBtn';
import { benItems, HourList, MinuteList, modalTypes, wellType } from '../../../assets/data';
import { EventModify, MailModify } from '../../../apis';
import {
authType,
benItems, commonStatus,
HourList,
mailType,
MinuteList,
userType,
currencyType,
} from '../../../assets/data';
import { MailCaliumTotalView, MailIsItem, MailModify } from '../../../apis';
import { authList } from '../../../store/authList';
import { useRecoilValue } from 'recoil';
import { convertKTC, convertKTCDate, timeDiffMinute } from '../../../utils';
import { useTranslation } from 'react-i18next';
import DynamicModal from '../../common/modal/DynamicModal';
import { convertKTC, convertKTCDate } from '../../../utils';
import { useNavigate } from 'react-router-dom';
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 userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const navigate = useNavigate();
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
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 === 23);
const updateAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.mailUpdate);
const [sendHour, setSendHour] = useState('00');
const [sendMin, setSendMin] = useState('00');
const [item, setItem] = useState('');
const [itemCount, setItemCount] = useState('');
const [resource, setResource] = useState('19010001');
const [resource, setResource] = useState(currencyCodeTypes.gold);
const [resourceCount, setResourceCount] = useState('');
const [modifyModal, setModifyModal] = useState('hidden');
const [completeModal, setCompleteModal] = useState('hidden');
const [resultData, setResultData] = useState({});
const [modalState, setModalState] = useState({
updateConfirmModal: 'hidden',
updateCompleteModal: 'hidden',
const [resultData, setResultData] = useState({
is_reserve: false,
send_dt: '',
mail_type: 'SELECT',
receive_type: 'SINGLE',
user_type: 'GUID',
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 [isItemNullValue, setIsItemNullValue] = useState(false);
const [excelFile, setExcelFile] = useState(content.target ? content.target : '');
const [excelName, setExcelName] = useState(null);
const [excelFile, setExcelFile] = useState('');
const [downloadData, setDownLoadData] = useState(null);
const [btnValidation, setBtnValidation] = useState(false);
const [updateMessage, setUpdateMessage] = useState('수정이 완료되었습니다.');
const [alertMessage, setAlertMessage] = useState('');
const [undefinedFile, setUndefinedFile] = useState(false);
const [disabledBtn, setDisabledBtn] = useState(false); // 예약 발송 확인용
const [alertMsg, setAlertMsg] = useState('');
const {
data: caliumTotalData,
} = useDataFetch(() => MailCaliumTotalView(token), [token]);
const KOREAN_TIME = content && convertKTCDate(content.send_dt);
@@ -72,39 +83,43 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
};
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({
is_reserve: content && content.is_reserve,
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('')
content.mail_list.length === 1 && setBtnValidation(true);
content.is_reserve === false && setBtnValidation(true);
setItem('');
if (content.receive_type === 'MULTIPLE') {
setDownLoadData(content.target);
setExcelFile(content.target);
}
}
}, [content]);
// console.log('downloadData', downloadData);
// console.log('isPast', isPast);
// console.log("guid", resultData.guid)
// console.log("메일 형식", content && content.receive_type," 엑셀네임", excelName)
useEffect(() => {
if(content){
if(!updateAuth
|| !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 => {
@@ -120,25 +135,41 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
};
// 아이템 추가
const handleItemList = () => {
const handleItemList = async () => {
if(benItems.includes(item)){
setAlertMsg(t('MAIL_ITEM_ADD_BEN'))
showToast('MAIL_ITEM_ADD_BEN',{type: alertTypes.warning});
return;
}
item.length === 0 || itemCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
if (item.length === '' || itemCount.length === 0 || itemCount <= 0) {
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('');
if(item.length === 0 || itemCount.length === 0 || itemCount <= 0){
showToast('COUNT_EMPTY_WARNING', { type: alertTypes.warning });
return;
}
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]);
setIsChanged(true);
// console.log('filterList', filterList);
setResultData({ ...resultData, item_list: filterList });
};
@@ -166,17 +195,35 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
// 자원 추가
const handleResourceList = () => {
resourceCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
if (resourceCount.length === 0 || resourceCount <= 0) {
setIsItemNullValue(true);
showToast('COUNT_EMPTY_WARNING', { type: alertTypes.warning });
} else {
setIsItemNullValue(false);
const name = wellType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
const itemIndex = resultData.item_list.findIndex(
(item) => item.item === resource,
);
if (itemIndex !== -1) {
const item_cnt = resultData.item_list[itemIndex].item_cnt;
if (resource === currencyCodeTypes.calium) {
if ((Number(resourceCount) + Number(item_cnt)) > caliumTotalData) {
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('');
}
};
@@ -213,149 +260,79 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
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 = () => {
setItem('');
setItemCount('');
setResource(currencyCodeTypes.gold);
setResourceCount('');
setBtnValidation(false);
setIsNullValue(false);
setIsChanged(false);
setUndefinedFile(false);
setIsItemNullValue(false);
setAlertMessage('');
setExcelFile(null);
setExcelName(null);
};
// 상세 페이지에서 파일 삭제
const handleDetailDelete = e => {
e.preventDefault();
//내용복사
const handleCopyContent = () => {
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);
setUndefinedFile(true);
setResultData({ ...resultData, guid: '' });
delete resultData.file_name;
// 복사한 데이터를 세션 스토리지에 저장
sessionStorage.setItem('copyMailData', JSON.stringify(mailData));
document.querySelector('#fileinput').value = '';
setExcelFile(null);
}
setIsChanged(true);
navigate('/servicemanage/mail/mailregist');
};
const handleModalView = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'view',
}));
}
const handleModalClose = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'hidden',
}));
}
const checkCondition = () => {
return (
content &&
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'
&& isChanged
);
};
const handleSubmit = async (type, param = null) => {
switch (type) {
case "submit":
// if (!conditionCheck()) return;
if (!checkCondition()) return;
handleModalView('updateConfirm');
showModal('MAIL_UPDATE_SAVE', {
type: alertTypes.confirm,
onConfirm: () => handleSubmit('updateConfirm')
})
break;
case "updateConfirm":
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date))
// 이벤트 시작 30분전이나 이미 SystemMail이 add된 상태에서는 수정할 수 없다.
if(content.add_flag || timeDiff <= 30){
setAlertMsg(t('EVENT_TIME_LIMIT_UPDATE'));
handleModalClose('updateConfirm');
return;
}
EventModify(token, id, resultData);
handleModalClose('updateConfirm');
handleModalView('updateComplete');
break;
case "updateComplete":
handleModalClose('updateComplete');
window.location.reload();
break;
case "warning":
setAlertMsg('');
await withLoading(async () => {
if(resultData.receive_type === 'MULTIPLE') {
delete resultData.guid
return await MailModify(token, id, resultData);
} else {
return await MailModify(token, id, resultData);
}
}).then(data => {
if(data.result === 'SUCCESS') {
showToast('UPDATE_COMPLETED', {type: alertTypes.success});
handleDetailView();
handleReset();
}else{
showToast(data.data.message, {type: alertTypes.error});
}
}).catch(error => {
showToast('API_FAIL', {type: alertTypes.error});
}).finally(() => {
});
break;
}
}
@@ -364,14 +341,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<>
<Modal min="960px" $view={detailView}>
<Title $align="center">우편 상세 정보</Title>
{content && <>
<RegistInfo>
<span>등록자 : {content && content.create_by}</span>
<span>등록일 : {content && convertKTC(content.create_dt, false)}</span>
{content && typeof content.update_by !== 'undefined' && (
<span>등록자 : {content.create_by}</span>
<span>등록일 : {convertKTC(content.create_dt, false)}</span>
{typeof content.update_by !== 'undefined' && (
<>
<span>수정자 : {content && content.update_by}</span>
<span>수정일 : {content && convertKTC(content.update_dt, false)}</span>
<span>수정자 : {content.update_by}</span>
<span>수정일 : {convertKTC(content.update_dt, false)}</span>
</>
)}
</RegistInfo>
@@ -384,23 +361,20 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
checked={resultData && resultData.is_reserve}
setData={e => {
setResultData({ ...resultData, is_reserve: e.target.checked });
setDisabledBtn(e.target.checked);
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 && (
<InputItem>
<InputLabel>발송 시간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
readOnly={(content && content.is_reserve === false) || onlyView || isPast}
readOnly={(content.is_reserve === false) || isView}
name={initialData.send_dt}
selectedDate={resultData ? resultData.send_dt : initialData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
@@ -410,24 +384,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<SelectInput
onChange={e => handleSendTime(e)}
id="hour"
disabled={(content && content.is_reserve === false) || onlyView || isPast}
disabled={(content.is_reserve === false) || isView}
value={
resultData && String(new Date(resultData.send_dt).getHours()) < 10
? '0' + String(new Date(resultData.send_dt).getHours())
: resultData && String(new Date(resultData.send_dt).getHours())
}>
{HourList.map(hour => (
<option
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'
// : ''
// }
>
<option value={hour} key={hour}>
{hour}
</option>
))}
@@ -438,24 +402,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
setIsChanged(true);
}}
id="min"
disabled={(content && content.is_reserve === false) || onlyView || isPast}
disabled={(content.is_reserve === false) || isView}
value={
resultData && String(new Date(resultData.send_dt).getMinutes()) < 10
? '0' + String(new Date(resultData.send_dt).getMinutes())
: resultData && String(new Date(resultData.send_dt).getMinutes())
}>
{MinuteList.map(min => (
<option
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'
// : ''
// }
>
<option value={min} key={min}>
{min}
</option>
))}
@@ -471,21 +425,22 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
setResultData({ ...resultData, mail_type: e.target.value });
setIsChanged(true);
}}
value={content && resultData.mail_type}
disabled={(content && content.is_reserve === false) || onlyView || isPast}>
value={resultData.mail_type}
disabled={isView}>
<option value="SELECT">타입 선택</option>
<option value="SYSTEM_GUID">시스템 안내</option>
<option value="INSPECTION_COMPENSATION">점검 보상</option>
<option value="RECOVER_COMPENSATION">복구 보상</option>
<option value="EVENT_COMPENSATION">이벤트 보상</option>
{mailType.filter(data => data.value !== 'ALL').map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</InputItem>
<InputItem>
<InputLabel>발송상태</InputLabel>
<div>
{content && initialData.send_status === 'WAIT' && <MailState>대기</MailState>}
{content && initialData.send_status === 'FINISH' && <MailState result="success">완료</MailState>}
{content && initialData.send_status === 'FAIL' && <MailState result="fail">실패</MailState>}
{initialData.send_status === 'WAIT' && <MailState>대기</MailState>}
{initialData.send_status === 'FINISH' && <MailState result="success">완료</MailState>}
{initialData.send_status === 'FAIL' && <MailState result="fail">실패</MailState>}
</div>
</InputItem>
</InputRow>
@@ -493,9 +448,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<InputItem>
<InputLabel>수신대상</InputLabel>
<InputItem>
<SelectInput onChange={e => setResultData({ ...resultData, user_type: e.target.value })} value={resultData.user_type} disabled={(content && content.is_reserve === false) || onlyView || isPast}>
<option value="GUID">GUID</option>
<option value="NICKNAME">아바타명</option>
<SelectInput onChange={e => setResultData({ ...resultData, user_type: e.target.value })}
value={resultData.user_type}
disabled={true}>
{userType2.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</InputItem>
<div>
@@ -505,91 +465,43 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
id="SINGLE"
name="receiver"
value="SINGLE"
disabled={(content && content.is_reserve === false) || onlyView || isPast}
disabled={true}
fontWeight="600"
checked={resultData.receive_type === 'SINGLE'}
handleChange={e => {
setResultData({ ...resultData, receive_type: e.target.id });
setIsChanged(true);
}}
handleClick={handleSingleBtn}
/>
<TextInput
placeholder={resultData.user_type === "GUID" ? "GUID 입력" : resultData.user_type === "NICKNAME" ? "아바타명 입력" : "이메일 입력"}
disabled={resultData.receive_type !== 'SINGLE' || (content && content.is_reserve === false) || onlyView || isPast}
// defaultValue={resultData.receive_type === 'SINGLE' && resultData.guid !== '' ? content.target : ''}
disabled={true}
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>
{content && resultData.receive_type === 'MULTIPLE' && typeof resultData.guid !== 'undefined' ? (
<InputGroup>
<RadioInput
label="복수"
id="MULTIPLE"
name="receiver"
value="MULTIPLE"
fontWeight="600"
checked={content && resultData.receive_type === 'MULTIPLE'}
disabled
/>
<MailRegistUploadBtn
disabled={resultData.receive_type !== 'MULTIPLE'}
downloadData={downloadData}
setResultData={setResultData}
resultData={resultData}
handleDetailDelete={handleDetailDelete}
setExcelFile={setExcelFile}
alertMessage={alertMessage}
setAlertMessage={setAlertMessage}
undefinedFile={undefinedFile}
disabledBtn={disabledBtn}
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>
)}
<InputGroup>
<RadioInput
label="복수"
id="MULTIPLE"
name="receiver"
value="MULTIPLE"
fontWeight="600"
disabled={true}
/>
<MailRegistUploadBtn
disabled={true}
setResultData={setResultData}
resultData={resultData}
setExcelFile={setExcelFile}
handleDetailDelete={() => {}}
disabledBtn={true}
excelName={excelFile}
setExcelName={setExcelFile}
downloadData={downloadData}
status={initialData.send_status}
/>
</InputGroup>
</div>
</InputItem>
</MailReceiver>
</RegistGroup>
{resultData.mail_list &&
resultData.mail_list.map(data => {
resultData?.mail_list?.map(data => {
return (
<Fragment key={data.language}>
<MailRegistBox>
@@ -597,6 +509,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
언어 : {data.language}
{btnValidation === false ? (
<BtnClose
disabled={true}
onClick={e => {
e.preventDefault();
onLangDelete(data.language);
@@ -619,7 +532,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
maxLength="30"
id={data.language}
value={data.title}
readOnly={(content && content.is_reserve === false) || onlyView || isPast}
readOnly={(content.is_reserve === false) || isView}
onChange={e => {
let list = [...resultData.mail_list];
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>
<Textarea
value={data.content}
readOnly={(content && content.is_reserve === false) || onlyView || isPast}
readOnly={isView}
id={data.language}
onChange={e => {
if (e.target.value.length > 2000) {
@@ -678,7 +591,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
list = e.target.value.trimStart();
setItem(list);
}}
disabled={(content && content.is_reserve === false) || onlyView || isPast}
disabled={isView}
/>
<TextInput
placeholder="수량"
@@ -686,36 +599,15 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
type="number"
onChange={e => handleItemCount(e)}
width="90px"
disabled={(content && content.is_reserve === false) || onlyView || isPast}
disabled={isView}
/>
<Button
text="추가"
theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'}
disabled={isView}
handleClick={handleItemList}
errorMessage={(content && content.is_reserve === false) || onlyView || isPast}
/>
</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>
</tr>
<tr>
@@ -724,17 +616,32 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
</th>
<td>
<InputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={(content && content.is_reserve === false) || onlyView || isPast}>
{wellType.map((data, index) => (
<SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={isView}>
{currencyType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput placeholder="수량" type="number" value={resourceCount} disabled={(content && content.is_reserve === false) || onlyView || isPast} onChange={e => handleResourceCount(e)} width="200px" />
<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} />
<TextInput
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>
{isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
<div>
{resultData.item_list && (
@@ -745,7 +652,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
<span>
{data.item_name}[{data.item}] ({data.item_cnt})
</span>
{(content && content.is_reserve === true) || onlyView || isPast && <BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>}
{!isView && <BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>}
</Item>
);
})}
@@ -758,6 +665,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
</MailRegistTable>
</MailRegistBox>
</ModalWrapper>
</>}
<BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px">
<Button
text="확인"
@@ -768,58 +676,21 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
handleReset();
}}
/>
{(updateAuth && content && content.is_reserve === true) && !isPast && (
{!isView && (
<Button
type="submit"
text="수정"
id="수정버튼"
theme={
(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'
}
theme={checkCondition() ? 'primary' : 'disable'}
handleClick={() => {
handleModifyModal();
// handleModifyModal();
handleSubmit('submit')
}}
/>
)}
<Button theme="line" text="내용 복사" handleClick={handleCopyContent} />
</BtnWrapper>
</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 { Title, TableStyle, BtnWrapper, TextInput } from '../../../styles/Components';
import Modal from '../../common/modal/Modal';
import Button from '../../common/button/Button';
import { convertKTC } from '../../../utils';
import { blockPeriod, blockSanctions, blockStatus, blockType, commonStatus } from '../../../assets/data';
const UserBlockDetailModal = ({ stateModal, handleModal, data }) => {
const history = data.history;
const type = [
{ value: 'ACCESS_RESTRICTIONS', name: '접근 제한' },
{ value: 'CHATTING_RESTRICTIONS', name: '채팅 제한' },
];
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: '영구정지' },
];
const [history, setHistory] = useState();
useEffect(() => {
if(data){
setHistory(data.history);
}
},[data])
return (
<>
@@ -49,40 +27,59 @@ const UserBlockDetailModal = ({ stateModal, handleModal, data }) => {
<table>
<caption></caption>
<tbody>
<tr>
<th width="100px">유저 GUID</th>
<tr>
<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>
<TextInput value={data && data.guid || ''} disabled />
<TextInput
value={data && data.reason}
disabled />
</td>
<th width="100px">아바타명</th>
<td>
<TextInput value={data && data.nickname || ''} disabled />
</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>
</>
}
</tr>
</tbody>
</table>
</BlockDetailState>
@@ -90,28 +87,27 @@ const UserBlockDetailModal = ({ stateModal, handleModal, data }) => {
<BlockCount>전체 제재 이력({history && history.length})</BlockCount>
<BlockHistory>
<TableStyle>
<thead>
<tr>
<th>일자</th>
<th width="10%">제재 기간</th>
<thead>
<tr>
<th>일자</th>
<th width="10%">제재 기간</th>
<th width="10%">제재 방식</th>
<th width="20%">제재 사유</th>
<th width="20%">등록자</th>
</tr>
</thead>
<tbody>
{history &&
history.map((content, index) => (
{history?.map((content, index) => (
<Fragment key={index}>
<tr>
<td>
{convertKTC(content.start_dt, false)} ~
{convertKTC(content.end_dt, false)}
</td>
<td>{period.map(item => item.value === data.period && item.name)}</td>
<td>{type.map(item => item.value === data.type && item.name)}</td>
<td>{sanctions.map(item => item.value === data.sanctions && item.name)}</td>
<td></td>
<td>{blockPeriod.find(item => item.value === content.period)?.name}</td>
<td>{blockType.find(item => item.value === content.type)?.name}</td>
<td>{blockSanctions.find(item => item.value === content.sanctions)?.name}</td>
<td>{content.create_by}</td>
</tr>
</Fragment>
))}
@@ -181,9 +177,9 @@ const BlockHistory = styled.div`
top: 0;
}
tbody {
tr:first-child {
color: #d60000;
}
//tr:first-child {
// color: #d60000;
//}
tr:last-child td {
border-bottom: 1px solid #e8eaec;
}

View File

@@ -3,7 +3,7 @@ import Button from '../../common/button/Button';
import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar';
import { useCallback, useEffect, useState } from 'react';
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) => {
const [searchParams, setSearchParams] = useState({
@@ -144,7 +144,7 @@ const LandAuctionSearchBar = ({ searchParams, onSearch, onReset }) => {
<InputLabel>낙찰자</InputLabel>
<InputGroup>
<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}>
{data.name}
</option>

View File

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

View File

@@ -3,7 +3,7 @@ import { TextInput, BtnWrapper, InputLabel, SelectInput, InputGroup } from '../.
import Button from '../../common/button/Button';
import { SearchBarLayout } from '../../common/SearchBar';
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 [searchData, setSearchData] = useState({
@@ -52,7 +52,7 @@ const UserBlockSearchBar = ({ handleSearch, setResultData }) => {
<InputLabel>대상</InputLabel>
<InputGroup>
<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}>
{data.name}
</option>

View File

@@ -5,28 +5,13 @@ import ResetIcon from '../../../assets/img/icon/icon-reset.png';
/**
* @param {string} text 버튼 내부 텍스트
* @param {string} type 버튼 타입 (button, submit)
* @param {string} errorMessage 표시할 에러 메시지
* @param {() => void} handleClick 버튼 클릭시 실행할 이벤트, Route 기능의 경우 history.push와 같은 함수를 이용합니다.
* @param {string} buttonColor 버튼 배경 색상
* @param {string} hoverColor 버튼 호버 배경 색상
* @param {object} props 폰트 관련 속성
*/
const Button = ({ text, type = 'button', errorMessage, 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]);
const Button = ({ text, type = 'button', handleClick, theme, size, width, height, bordercolor, disabled, name }) => {
return (
<Wrapper width={width}>
<ButtonStyle
@@ -42,7 +27,6 @@ const Button = ({ text, type = 'button', errorMessage, handleClick, theme, size,
name={name}>
{text}
</ButtonStyle>
{isShowErrorMessage && <ErrorText>{errorMessage}</ErrorText>}
</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;

View File

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

View File

@@ -44,6 +44,14 @@ const resources = {
WARNING_TYPE_CHECK: '타입을 확인해주세요.',
DATE_START_DIFF_END_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_ITEM_DELETE_TITLE: "선택 삭제",
TABLE_BUTTON_DETAIL_TITLE: "상세보기",
@@ -134,8 +142,9 @@ const resources = {
// 이용자 제재
USER_BLOCK_VALIDATION_WARNING: '유효성 체크가 통과되지 않은 항목이 존재합니다.\r\n수정 후 재등록 해주세요.',
USER_BLOCK_REGIST_DUPLE_WARNING: '이미 제재가 등록된 유저입니다.',
USER_BLOCK_DELETE_CONFIRM: '제재 대상을 삭제하시겠습니까?\r\n삭제 시 제재가 해제됩니다.',
USER_BLOCK_DELETE_CONFIRM: '제재 대상을 해지하시겠습니까?',
USER_BLOCK_REGIST_CONFIRM: '이용자 제재 명단에\r\n등록하시겠습니까?',
USER_BLOCK_DELETE_STATUS_WARNING: '제재를 해지할 수 없는 상태입니다.',
//파일
FILE_IMAGE_EXTENSION_WARNING: "png, jpg 확장자의 이미지만 업로드 가능합니다.",
FILE_IMAGE_UPLOAD_ERROR: "이미지 업로드 중 오류가 발생했습니다.",
@@ -148,7 +157,11 @@ const resources = {
FILE_BUSINESS_LOG: 'Caliverse_Log.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: {

View File

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

View File

@@ -27,11 +27,11 @@ import {
RegistInputRow, RegistNotice, RegistTable,
} from '../../styles/ModuleComponents';
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 { timeDiffMinute } from '../../utils';
import { useAlert } from '../../context/AlertProvider';
import { alertTypes } from '../../assets/data/types';
import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
import { useLoading } from '../../context/LoadingProvider';
const EventRegist = () => {
@@ -44,7 +44,7 @@ const EventRegist = () => {
const [item, setItem] = useState(''); // 아이템 값
const [itemCount, setItemCount] = useState(''); // 아이템 개수
const [resource, setResource] = useState('19010001'); // 자원 값
const [resource, setResource] = useState(currencyCodeTypes.gold); // 자원 값
const [resourceCount, setResourceCount] = useState(''); // 자원 개수
const [isNullValue, setIsNullValue] = useState(false);
@@ -209,7 +209,7 @@ const EventRegist = () => {
const item_cnt = resultData.item_list[itemIndex].item_cnt;
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
} 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 };
resultData.item_list.push(newItem);
}
@@ -403,7 +403,7 @@ const EventRegist = () => {
<td>
<RegistInputItem>
<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}>
{data.name}
</option>

View File

@@ -146,7 +146,6 @@ const Items = () => {
const ConfirmChild = () => {
if(selectedRows === undefined || selectedRows.length === 0) return;
const selectRow = selectedRows[0];
console.log(selectRow)
return(
<InputItem>
<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 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 MailRegistUploadBtn from '../../components/ServiceManage/MailRegistUploadBtn';
@@ -35,6 +35,7 @@ import { useDataFetch } from '../../hooks/hook';
import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
import { useLoading } from '../../context/LoadingProvider';
import { useAlert } from '../../context/AlertProvider';
import { userType2 } from '../../assets/data/options';
const MailRegist = () => {
const navigate = useNavigate();
@@ -44,14 +45,12 @@ const MailRegist = () => {
const { showModal, showToast } = useAlert();
const { withLoading } = useLoading();
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
const [sendHour, setSendHour] = useState('00');
const [sendMin, setSendMin] = useState('00');
const [item, setItem] = useState('');
const [itemCount, setItemCount] = useState('');
const [resource, setResource] = useState('19010001');
const [resource, setResource] = useState(currencyCodeTypes.gold);
const [resourceCount, setResourceCount] = useState(0);
const [isNullValue, setIsNullValue] = useState(false);
@@ -61,8 +60,6 @@ const MailRegist = () => {
const [excelName, setExcelName] = useState(null);
const [isItemNullValue, setIsItemNullValue] = useState(false);
const [isResourceNullValue, setIsResourceNullValue] = useState(false);
const [alertMessage, setAlertMessage] = useState('');
const {
data: caliumTotalData,
@@ -96,6 +93,35 @@ const MailRegist = () => {
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(() => {
if (checkCondition()) {
setIsNullValue(false);
@@ -125,32 +151,35 @@ const MailRegist = () => {
}
item.length === 0 || itemCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
// const token = sessionStorage.getItem('token');
const result = await MailIsItem(token, { item: item });
await withLoading(async () => {
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') {
showToast(result.data.data.message, { type: alertTypes.warning });
return;
}
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 };
if (item.length === '' || itemCount.length === 0 || itemCount <= 0) {
setIsItemNullValue(true);
} else if (item.length !== 0) {
setIsItemNullValue(false);
const itemIndex = resultData.item_list.findIndex(
(item) => item.item === resource,
);
if (itemIndex !== -1) {
showToast('MAIL_ITEM_ADD_DUPL', { type: alertTypes.warning });
return;
resultData.item_list.push(newItem);
setItem('');
setItemCount('');
}
}
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 => {
@@ -213,7 +242,7 @@ const MailRegist = () => {
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 };
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 sendDate = new Date(data);
@@ -317,9 +341,8 @@ const MailRegist = () => {
const checkCondition = () => {
return resultData.mail_list.every(data => data.content !== '' && data.title !== '') &&
(resultData.receive_type === 'MULTIPLE' ? excelFile !== null : resultData.guid !== '') &&
(resultData.is_reserve ? resultData.send_dt.length !== 0 : resultData.send_dt.length === 0) &&
(resultData.is_reserve ? resultData.send_dt.length !== 0 : true) &&
resultData.mail_type !== 'SELECT'
&& !alertMessage
};
return (
@@ -375,10 +398,11 @@ const MailRegist = () => {
<SelectInput onChange={e => setResultData({ ...resultData, mail_type: e.target.value })}
value={resultData.mail_type}>
<option value="SELECT">타입 선택</option>
<option value="SYSTEM_GUID">시스템 안내</option>
<option value="INSPECTION_COMPENSATION">점검 보상</option>
<option value="RECOVER_COMPENSATION">복구 보상</option>
<option value="EVENT_COMPENSATION">이벤트 보상</option>
{mailType.filter(data => data.value !== 'ALL').map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</InputItem>
</RegistInputRow>
@@ -389,9 +413,14 @@ const MailRegist = () => {
<InputItem>
<SelectInput
onChange={e => setResultData({ ...resultData, user_type: e.target.value })}
value={resultData.user_type} disabled={resultData.receive_type !== 'SINGLE'}>
<option value="GUID">GUID</option>
<option value="NICKNAME">아바타명</option>
value={resultData.user_type}
disabled={resultData.receive_type !== 'SINGLE'}
>
{userType2.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
<option value="EMAIL">이메일</option>
</SelectInput>
</InputItem>
@@ -436,8 +465,6 @@ const MailRegist = () => {
setResultData={setResultData}
resultData={resultData}
excelFile={excelFile}
alertMessage={alertMessage}
setAlertMessage={setAlertMessage}
setExcelFile={setExcelFile}
excelName={excelName}
setExcelName={setExcelName}
@@ -541,24 +568,6 @@ const MailRegist = () => {
theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'}
handleClick={handleItemList} width="100px" height="35px" />
</InputItem>
{/* {isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
<div>
{resultData.item_list && (
<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>
</tr>
<tr>
@@ -568,10 +577,11 @@ const MailRegist = () => {
<td>
<InputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource}>
<option value="19010001">골드</option>
<option value="19010002">사파이어</option>
<option value="19010005">루비</option>
<option value="19010003">칼리움</option>
{currencyType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput placeholder="수량" type="number" value={resourceCount}
onChange={e => handleResourceCount(e)} width="200px" />
@@ -634,12 +644,6 @@ const MailRegist = () => {
export default MailRegist;
const InputRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 10px 50px;
`;
const InputItem = styled.div`
display: flex;
align-items: center;

View File

@@ -1,5 +1,5 @@
import { useState, Fragment, useRef } from 'react';
import { Title, FormWrapper } from '../../styles/Components';
import { useState, Fragment, useRef, useTransition } from 'react';
import { Title, FormWrapper, TextInput } from '../../styles/Components';
import { useNavigate } from 'react-router-dom';
import {
CommonSearchBar,
@@ -9,29 +9,35 @@ import {
import { BlackListDetail, BlackListDelete } from '../../apis';
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 { 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 tableInfo from '../../assets/data/pages/userBlockTable.json'
import { useAlert } from '../../context/AlertProvider';
import { useLoading } from '../../context/LoadingProvider';
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 token = sessionStorage.getItem('token');
const navigate = useNavigate();
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
const { t } = useTranslation();
const [detail, setDetail] = useState([]);
const [deleteDesc, setDeleteDesc] = useState('');
const {
modalState,
handleModalView,
handleModalClose
} = useModal({
detail: 'hidden',
delete: 'hidden'
});
const {
@@ -63,27 +69,44 @@ const UserBlock = () => {
});
break;
case "delete":
showModal('USER_BLOCK_DELETE_CONFIRM', {
type: alertTypes.confirm,
onConfirm: () => handleAction('deleteConfirm')
});
const selectRow = selectedRows[0];
if(selectRow.status === commonStatus.cancel || selectRow.status === customStatus.expiration || selectRow.status === commonStatus.fail) {
showToast('USER_BLOCK_DELETE_STATUS_WARNING',{type: alertTypes.warning})
return;
}
handleModalView('delete');
// showModal('USER_BLOCK_DELETE_CONFIRM', {
// type: alertTypes.confirm,
// onConfirm: () => handleAction('deleteConfirm')
// });
break;
case "deleteConfirm":
let list = [];
if(deleteDesc.length === 0){
showToast('INPUT_REASON_EMPTY_WARNING', {type: alertTypes.warning});
return;
}
selectedRows.map(data => {
list.push({
id: data.id,
reason: deleteDesc
});
});
await withLoading(async () => {
return BlackListDelete(token, list);
}).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 => {
showToast('API_FAIL', {type: alertTypes.error});
}).finally(() => {
handleModalClose('delete');
handleSearch(updateSearchParams);
});
@@ -141,6 +164,29 @@ const UserBlock = () => {
data={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});
return;
}
};
}
showModal('USER_BLOCK_REGIST_CONFIRM', {
type: alertTypes.confirm,
onConfirm: () => handleSubmit('registConfirm'),
@@ -237,7 +237,7 @@ const UserBlockRegist = () => {
case 'registConfirm':
await withLoading(async () => {
return await BlackListRegist(token, resultData);
return await BlackListRegist(token, resultData);
}).then(data => {
if(data.result === 'ERROR'){
showToast(data.data.message, {type: alertTypes.error});

View File

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