Files
operationSystem-front/src/components/modal/MailDetailModal.js
bcjang 6f9f0307ac component 정리
currencyLogSearchBar 생성
currencyLogCOntent 생성
excelExportButton api호출 방식 수정
2025-06-12 14:08:11 +09:00

852 lines
24 KiB
JavaScript

import { styled } from 'styled-components';
import RadioInput from '../common/input/Radio';
import React, { useState, useEffect, Fragment } from 'react';
import CheckBox from '../common/input/CheckBox';
import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, DatePickerWrapper, Textarea} from '../../styles/Components';
import Button from '../common/button/Button';
import Modal from '../common/modal/Modal';
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 '../ServiceManage/MailRegistUploadBtn';
import {
authType,
benItems, commonStatus,
HourList,
mailType,
MinuteList,
userType,
currencyItemCode,
} from '../../assets/data';
import { MailCaliumTotalView, MailIsItem, MailModify } from '../../apis';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
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';
import { STORAGE_MAIL_COPY } from '../../assets/data/adminConstants';
const MailDetailModal = ({ detailView, handleDetailView, content }) => {
const userInfo = useRecoilValue(authList);
const token = sessionStorage.getItem('token');
const navigate = useNavigate();
const {showModal, showToast} = useAlert();
const {withLoading} = useLoading();
const id = content && content.id;
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(currencyCodeTypes.gold);
const [resourceCount, setResourceCount] = useState('');
const [resultData, setResultData] = useState({
is_reserve: false,
send_dt: '',
mail_type: 'SELECT',
receive_type: 'SINGLE',
user_type: 'GUID',
mail_list: [],
item_list: [],
guid: '',
});
// 과거 판단
const [isView, setIsView] = useState(true);
const [isChanged, setIsChanged] = useState(false);
const [excelFile, setExcelFile] = useState('');
const [downloadData, setDownLoadData] = useState(null);
const [btnValidation, setBtnValidation] = useState(false);
const {
data: caliumTotalData,
} = useDataFetch(() => MailCaliumTotalView(token), [token]);
const KOREAN_TIME = content && convertKTCDate(content.send_dt);
const initialData = {
send_hour: content && KOREAN_TIME.getHours() < 10 ? '0' + content && KOREAN_TIME.getHours() : content && KOREAN_TIME.getHours(),
send_min: content && KOREAN_TIME.getMinutes() < 10 ? '0' + content && KOREAN_TIME.getMinutes() : content && KOREAN_TIME.getMinutes(),
send_status: content && content.send_status,
};
useEffect(() => {
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
});
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]);
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 => {
if (e.target.value === '0' || e.target.value === '-0') {
setItemCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setItemCount(plusNum);
} else {
setItemCount(e.target.value);
}
};
// 아이템 추가
const handleItemList = async () => {
if(benItems.includes(item)){
showToast('MAIL_ITEM_ADD_BEN',{type: alertTypes.warning});
return;
}
if(item.length === 0 || itemCount.length === 0 || 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 });
});
};
// 아이템 삭제
const onItemRemove = id => {
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
setIsChanged(true);
setResultData({ ...resultData, item_list: filterList });
};
// 자원 수량 숫자 체크
const handleResourceCount = e => {
if (e.target.value === '0' || e.target.value === '-0') {
setResourceCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setResourceCount(plusNum);
} else {
setResourceCount(e.target.value);
}
};
// 자원 추가
const handleResourceList = () => {
if (resourceCount.length === 0 || resourceCount <= 0) {
showToast('COUNT_EMPTY_WARNING', { type: alertTypes.warning });
} else {
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 = currencyItemCode.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem);
}
setIsChanged(true);
setResourceCount('');
}
};
// 입력창 삭제
const onLangDelete = language => {
let filterList = resultData.mail_list && resultData.mail_list.filter(el => el.language !== language);
if (filterList.length === 1) setBtnValidation(true);
setIsChanged(true);
setResultData({ ...resultData, mail_list: filterList });
};
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate(), sendHour, sendMin);
setIsChanged(true);
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 시간 세팅 로직
const handleSendTime = e => {
if (e.target.id === 'hour') setSendHour(e.target.value);
else if (e.target.id === 'min') setSendMin(e.target.value);
const sendDate = new Date(resultData.send_dt);
const result = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate(), e.target.id === 'hour' ? e.target.value : sendHour, e.target.id === 'min' ? e.target.value : sendMin);
setIsChanged(true);
setResultData({ ...resultData, send_dt: result });
};
// 확인 버튼 후 다 초기화
const handleReset = () => {
setItem('');
setItemCount('');
setResource(currencyCodeTypes.gold);
setResourceCount('');
setBtnValidation(false);
setIsChanged(false);
setExcelFile(null);
};
//내용복사
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
};
// 복사한 데이터를 세션 스토리지에 저장
sessionStorage.setItem(STORAGE_MAIL_COPY, JSON.stringify(mailData));
navigate('/servicemanage/mail/mailregist');
};
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 (!checkCondition()) return;
showModal('MAIL_UPDATE_SAVE', {
type: alertTypes.confirm,
onConfirm: () => handleSubmit('updateConfirm')
})
break;
case "updateConfirm":
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;
}
}
return (
<>
<Modal min="960px" $view={detailView}>
<Title $align="center">우편 상세 정보</Title>
{content && <>
<RegistInfo>
<span>등록자 : {content.create_by}</span>
<span>등록일 : {convertKTC(content.create_dt, false)}</span>
{typeof content.update_by !== 'undefined' && (
<>
<span>수정자 : {content.update_by}</span>
<span>수정일 : {convertKTC(content.update_dt, false)}</span>
</>
)}
</RegistInfo>
<ModalWrapper>
<RegistGroup>
<InputRow>
<CheckBox
label="예약 발송"
id="reserve"
checked={resultData && resultData.is_reserve}
setData={e => {
setResultData({ ...resultData, is_reserve: e.target.checked });
setIsChanged(true);
}}
disabled={(content.is_reserve === false) || isView}
/>
{content.is_reserve === false ? (
<></>
) : (
resultData.is_reserve === true && (
<InputItem>
<InputLabel>발송 시간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
readOnly={(content.is_reserve === false) || isView}
name={initialData.send_dt}
selectedDate={resultData ? resultData.send_dt : initialData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
pastDate={new Date()}
/>
</DatePickerWrapper>
<SelectInput
onChange={e => handleSendTime(e)}
id="hour"
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}>
{hour}
</option>
))}
</SelectInput>
<SelectInput
onChange={e => {
handleSendTime(e);
setIsChanged(true);
}}
id="min"
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}>
{min}
</option>
))}
</SelectInput>
</InputGroup>
</InputItem>
)
)}
<InputItem>
<InputLabel>우편 타입</InputLabel>
<SelectInput
onChange={e => {
setResultData({ ...resultData, mail_type: e.target.value });
setIsChanged(true);
}}
value={resultData.mail_type}
disabled={isView}>
<option value="SELECT">타입 선택</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>
{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>
<MailReceiver>
<InputItem>
<InputLabel>수신대상</InputLabel>
<InputItem>
<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>
<InputGroup>
<RadioInput
label="단일"
id="SINGLE"
name="receiver"
value="SINGLE"
disabled={true}
fontWeight="600"
checked={resultData.receive_type === 'SINGLE'}
/>
<TextInput
disabled={true}
value={resultData.receive_type === 'SINGLE' && resultData.guid !== '' ? resultData.guid : ''}
/>
</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 => {
return (
<Fragment key={data.language}>
<MailRegistBox>
<LangArea>
언어 : {data.language}
{btnValidation === false ? (
<BtnClose
disabled={true}
onClick={e => {
e.preventDefault();
onLangDelete(data.language);
}}
/>
) : (
<BtnClose opacity="10%" />
)}
</LangArea>
<MailRegistTable>
<tbody>
<tr>
<th width="120">
<Label>제목</Label>
</th>
<td>
<InputItem>
<TextInput
placeholder="우편 제목 입력"
maxLength="30"
id={data.language}
value={data.title}
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);
list[findIndex].title = e.target.value.trimStart();
setResultData({ ...resultData, mail_list: list });
setIsChanged(true);
}}
/>
</InputItem>
</td>
</tr>
<tr>
<th>
<Label>내용</Label>
</th>
<td>
<Textarea
value={data.content}
readOnly={isView}
id={data.language}
onChange={e => {
if (e.target.value.length > 2000) {
return;
}
let list = [...resultData.mail_list];
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
list[findIndex].content = e.target.value.trimStart();
setResultData({ ...resultData, mail_list: list });
setIsChanged(true);
}}
/>
</td>
</tr>
</tbody>
</MailRegistTable>
</MailRegistBox>
</Fragment>
);
})}
<MailRegistBox>
<MailRegistTable>
<tbody>
<tr>
<th width="120">
<Label>아이템 첨부</Label>
</th>
<td>
<InputItem>
<TextInput
placeholder="Item Meta id 입력"
value={item}
onChange={e => {
let list = [];
list = e.target.value.trimStart();
setItem(list);
}}
disabled={isView}
/>
<TextInput
placeholder="수량"
value={itemCount}
type="number"
onChange={e => handleItemCount(e)}
width="90px"
disabled={isView}
/>
<Button
text="추가"
theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'}
disabled={isView}
handleClick={handleItemList}
/>
</InputItem>
</td>
</tr>
<tr>
<th width="120">
<Label>자원 첨부</Label>
</th>
<td>
<InputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={isView}>
{currencyItemCode.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<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>
<div>
{resultData.item_list && (
<ItemList>
{resultData.item_list.map((data, index) => {
return (
<Item key={index}>
<span>
{data.item_name}[{data.item}] ({data.item_cnt})
</span>
{!isView && <BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>}
</Item>
);
})}
</ItemList>
)}
</div>
</td>
</tr>
</tbody>
</MailRegistTable>
</MailRegistBox>
</ModalWrapper>
</>}
<BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px">
<Button
text="확인"
theme="line"
name="확인버튼"
handleClick={() => {
handleDetailView();
handleReset();
}}
/>
{!isView && (
<Button
type="submit"
text="수정"
id="수정버튼"
theme={checkCondition() ? 'primary' : 'disable'}
handleClick={() => {
// handleModifyModal();
handleSubmit('submit')
}}
/>
)}
<Button theme="line" text="내용 복사" handleClick={handleCopyContent} />
</BtnWrapper>
</Modal>
</>
);
};
export default MailDetailModal;
const InputItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
${TextInput},${SelectInput} {
height: 35px;
font-size: 14px;
}
${TextInput} {
padding: 0 15px;
}
${SelectInput} {
width: max-content;
}
`;
const ModalWrapper = styled.div`
max-height: 70vh;
padding-bottom: 5px;
overflow: auto;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
`;
const RegistInfo = styled.div`
display: flex;
justify-content: flex-end;
gap: 50px;
font-size: 14px;
margin-bottom: 10px;
`;
const BtnClose = styled.button`
width: 16px;
height: 16px;
background: url(${CloseIcon}) 50% 50% no-repeat;
position: absolute;
top: 50%;
transform: translate(0, -50%);
right: 20px;
opacity: ${props => props.opacity};
`;
const LangArea = styled.div`
background: #f9f9f9;
padding: 10px 20px;
font-size: 14px;
font-weight: 600;
position: relative;
`;
const MailRegistBox = styled.div`
margin-bottom: 20px;
border-top: 1px solid #999;
border-bottom: 1px solid #999;
`;
const MailRegistTable = styled.table`
th,
td {
padding: 15px 0;
}
td {
${TextInput} {
max-width: 600px;
}
${Textarea} {
width: 100%;
border: 1px solid #d9d9d9;
border-radius: 5px;
height: 150px;
padding: 15px;
&:focus {
border: 1px solid #2c2c2c;
}
}
}
`;
const RegistGroup = styled.div`
display: flex;
width: 100%;
flex-flow: column;
gap: 20px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
font-size: 14px;
margin-bottom: 40px;
`;
const InputRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px 50px;
`;
const InputGroup = styled.div`
display: flex;
gap: 5px;
align-items: center;
`;
const MailReceiver = styled.div`
display: flex;
align-items: flex-start;
${InputItem} {
align-items: flex-start;
}
${InputLabel} {
line-height: 35px;
}
${TextInput} {
margin-left: 20px;
width: 400px;
}
${InputGroup} {
margin-bottom: 5px;
}
`;
const MailState = styled.span`
font-weight: 600;
color: ${props => (props.result === 'success' ? '#08994B' : props.result === 'fail' ? '#ff0000' : '#2c2c2c')};
`;
const ItemList = styled.ul`
display: flex;
gap: 20px;
padding: 10px 20px;
flex-wrap: wrap;
`;
const Item = styled.li`
display: flex;
gap: 5px;
align-items: center;
`;
const BtnDelete = styled.button`
width: 12px;
height: 12px;
background: url(${IconDelete}) 50% 50% no-repeat;
`;