This commit is contained in:
2025-02-13 16:23:04 +09:00
parent f1d3a67eed
commit 30ec5eabe5
18 changed files with 2 additions and 5648 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,620 +0,0 @@
import { styled } from 'styled-components';
import { useState, useRef, Fragment, useEffect } from 'react';
import Button from '../common/button/Button';
import CheckBox from '../common/input/CheckBox';
import Modal from '../common/modal/Modal';
import { Title, BtnWrapper, SelectInput, TextInput, DatePickerWrapper, InputLabel, Textarea, ModalText, SearchBarAlert } from '../../styles/Components';
import CloseIcon from '../../assets/img/icon/icon-close.png';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { HourList, MinuteList, modalTypes } from '../../assets/data';
import { NoticeRegist } from '../../apis';
import { convertKTC, convertKTCDate } from '../../utils';
import DynamicModal from '../common/modal/DynamicModal';
import { useTranslation } from 'react-i18next';
import {
BoxWrapper, InputGroup2,
MessageBox, MessageWrapper,
NoticeInputGroup, NoticeInputItem, NoticeInputItem2,
NoticeInputRow, NoticeInputRow2,
NoticeRegistGroup,
RegistInputItem, RepeatTime, SubText, SubTextRow, TitleLang,
} from '../../styles/ModuleComponents';
import { languageType } from '../../assets/data/options';
const BoardRegistModal = ({ registView, setRegistView, copyData, setIsCopyData }) => {
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
const { t } = useTranslation();
const [sendHour, setSendHour] = useState('00');
const [sendMin, setSendMin] = useState('00');
const [endHour, setEndHour] = useState('00');
const [endMin, setEndMin] = useState('00');
const [repeatHour, setRepeatHour] = useState('00');
const [repeatMin, setRepeatMin] = useState('00');
const [confirmModal, setConfirmModal] = useState('hidden');
const [completeModal, setCompleteModal] = useState('hidden');
const [errorModal, setErrorModal] = useState('hidden');
const [confirmText, setConfirmText] = useState('');
const [completeText, setCompleteText] = useState('');
const [isNullValue, setIsNullValue] = useState(false);
const [btnValidation, setBtnValidation] = useState(false);
const [message_lang, setMessage_lang] = useState('KO');
const itemId = useRef(1);
const resetData = {
send_dt: '',
message_type: 'CHATTING',
is_repeat: false,
repeat_type: 'COUNT',
repeat_dt: '00:00:00',
repeat_cnt: '1',
game_message: [
{ language: 'KO', content: '' },
{ language: 'EN', content: '' },
{ language: 'JA', content: '' },
],
};
const [resultData, setResultData] = useState({
send_dt: '',
end_dt: '',
message_type: 'CHATTING',
is_repeat: false,
repeat_type: 'COUNT',
repeat_dt: '00:00:00',
repeat_cnt: '1',
game_message: [
{ language: 'KO', content: '' },
{ language: 'EN', content: '' },
{ language: 'JA', content: '' },
],
});
const KOREAN_TIME = copyData && convertKTCDate(copyData.detail.send_dt);
// console.log(resultData);
useEffect(() => {
setResultData({
send_dt: copyData && '',
end_dt: copyData && '',
message_type: copyData ? copyData.detail.message_type : 'CHATTING',
is_repeat: copyData ? copyData.detail.is_repeat : false,
repeat_type: copyData ? copyData.detail.repeat_type : 'COUNT',
repeat_dt: copyData ? copyData.detail.repeat_dt : '00:00:00',
repeat_cnt: copyData ? copyData.detail.repeat_cnt : '1',
game_message: copyData
? copyData.game_message
: [
{ language: 'KO', content: '' },
{ language: 'EN', content: '' },
{ language: 'JA', content: '' },
],
});
if (copyData && copyData.game_message.length <= 1) {
setBtnValidation(true);
}
}, [copyData]);
const handleRepeatTime = e => {
if (e.target.id === 'hour') setRepeatHour(e.target.value);
else if (e.target.id === 'min') setRepeatMin(e.target.value);
setResultData({ ...resultData, repeat_dt: (e.target.id === 'hour' ? e.target.value : repeatHour) + ':' + (e.target.id === 'min' ? e.target.value : repeatMin) + ':00' });
};
// 날짜 설정
const handleSelectedDate = data => {
const sendDate = new Date(data);
if (resultData.send_dt.length === 0 || typeof resultData.send_dt.length != 'undefined') {
setSendHour('00');
setSendMin('00');
}
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate(), sendHour, sendMin);
setResultData({ ...resultData, send_dt: resultSendData });
};
// 시간 설정
const handleSendTime = e => {
let sendDate = '';
if (resultData.send_dt.length === 0 || typeof resultData.send_dt.length != 'undefined') {
sendDate = new Date();
} else {
sendDate = new Date(resultData.send_dt);
}
if (e.target.id === 'hour') setSendHour(e.target.value);
else if (e.target.id === 'min') setSendMin(e.target.value);
// console.log(sendDate);
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);
setResultData({ ...resultData, send_dt: result });
};
// 종료 날짜 설정
const handleSelectedEndDate = data => {
const endDate = new Date(data);
if (resultData.end_dt.length === 0 || typeof resultData.end_dt.length != 'undefined') {
setEndHour('00');
setEndMin('00');
}
let resultEndData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate(), endHour, endMin);
if (resultData.send_dt > resultEndData){
alert("송출 일자보다 작을 수 없습니다.");
resultEndData = new Date(resultData.send_dt);
}
setResultData({ ...resultData, end_dt: resultEndData });
};
//종료 시간 설정
const handleEndTime = e => {
let endDate = '';
if (resultData.end_dt.length === 0 || typeof resultData.end_dt.length != 'undefined') {
endDate = new Date();
} else {
endDate = new Date(resultData.end_dt);
}
if (e.target.id === 'hour') setEndHour(e.target.value);
else if (e.target.id === 'min') setEndMin(e.target.value);
// console.log(sendDate);
let result = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate(), e.target.id === 'hour' ? e.target.value : endHour, e.target.id === 'min' ? e.target.value : endMin);
if (resultData.send_dt > result){
alert("송출 일자보다 작을 수 없습니다.");
result = new Date(resultData.send_dt);
}
setResultData({ ...resultData, end_dt: result });
};
const handleDelete = language => {
let filterList = resultData.game_message.filter(el => el.language !== language);
if (filterList.length === 1) {
setBtnValidation(true);
} else if (filterList.length > 1) {
setBtnValidation(false);
}
setResultData({ ...resultData, game_message: filterList });
};
// 반복횟수 입력 데이터 폼 처리
const handleRepeatValue = e => {
// e.target.value.length === 0 ? setIsNullValue(true) : setIsNullValue(false);
if (e.target.value === '0' || e.target.value === '-0' || e.target.value.length === 0) {
setResultData({ ...resultData, repeat_cnt: '1' });
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setResultData({ ...resultData, repeat_cnt: plusNum });
} else {
setResultData({ ...resultData, repeat_cnt: e.target.value });
}
};
// 등록 버튼
const handleRegistBtn = e => {
e.preventDefault();
if (
!resultData.game_message.some(data => data.content !== '') ||
resultData.game_message.length === 0 ||
resultData.send_dt.length === 0 ||
typeof resultData.send_dt.length != 'undefined' ||
(resultData.is_repeat === true && resultData.repeat_dt === '00:00:00') ||
(resultData.is_repeat === true && resultData.message_type === "COUNT" && resultData.repeat_cnt.length === 0) ||
(resultData.is_repeat === true && resultData.message_type === "COUNT" && resultData.end_dt.length === 0) ||
(resultData.is_repeat === true && resultData.message_type === "COUNT" && typeof resultData.end_dt.length !== 'undefined')
) {
setIsNullValue(true);
} else {
setIsNullValue(false);
setConfirmText('인게임 메시지를 등록하시겠습니까?');
handleConfirmModal('regist');
}
};
// 취소 버튼
const handleCancelBtn = e => {
e.preventDefault();
setConfirmText('인게임 메시지 등록을 취소하시겠습니까?');
handleConfirmModal('cancel');
};
// 입력창 글자 제한
const handleInputData = e => {
if (e.target.value.length > 250) {
return;
}
let list = [...resultData.game_message];
let findIndex = resultData.game_message.findIndex(item => item.language === e.target.id);
list[findIndex].content = e.target.value.trimStart();
setResultData({ ...resultData, game_message: list });
};
// 알림창 텍스트
const handleSubmitNotice = async () => {
const token = sessionStorage.getItem('token');
if (confirmText === '인게임 메시지를 등록하시겠습니까?') {
const message = await NoticeRegist(token, resultData);
message.data.data.message !== '등록 하였습니다.' ? setCompleteText(message.data.data.message) : setCompleteText('등록이 완료되었습니다.');
handleConfirmModal();
handleCompleteModal();
setIsNullValue(false);
setRegistView('hidden');
} else if (confirmText === '인게임 메시지 등록을 취소하시겠습니까?') {
handleConfirmModal();
handleCompleteModal();
setCompleteText('등록이 취소되었습니다.');
setRegistView('hidden');
}
itemId.current = 1;
};
// 언어 선택
const handleLanguage = e => {
setMessage_lang(e.target.value);
if(!resultData.game_message.some(({language}) => language === e.target.value))
setResultData({ ...resultData, game_message: [...resultData.game_message, {language: e.target.value, content: ''}] })
}
// 확인 모달
const handleConfirmModal = step => {
if (confirmModal === 'hidden') {
setConfirmModal('view');
} else {
setConfirmModal('hidden');
}
};
// 완료 모달창
const handleCompleteModal = () => {
setIsCopyData(false);
if (completeModal === 'hidden') {
setCompleteModal('view');
} else {
setCompleteModal('hidden');
setResultData(resetData);
window.location.reload();
}
};
// 필수값 입력 모달창
const handleErrorModal = () => {
if (errorModal === 'hidden') {
setErrorModal('view');
} else {
setErrorModal('hidden');
}
};
// 내용 복사일 때 이동 페이지
return (
<>
<Modal min="960px" $view={registView}>
{/* 등록 후 정보 확인시 타이틀 텍스트 변경 인게임 메시지 등록 -> 인게임 메시지 정보 */}
<Title $align="center"> 인게임 메시지 등록</Title>
<MessageWrapper>
<InputLabel>[메세지 등록 설정]</InputLabel>
<NoticeRegistGroup>
<NoticeInputRow>
<RegistInputItem>
<InputLabel>송출 일자(예약)</InputLabel>
<InputGroup2>
<NoticeInputGroup>
<DatePickerWrapper>
<DatePickerComponent name="시작 일자" handleSelectedDate={data => handleSelectedDate(data)} selectedDate={resultData.send_dt} pastDate={new Date()} />
</DatePickerWrapper>
<SelectInput
onChange={e => handleSendTime(e)}
id="hour"
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'
// : ''
// }
>
{hour}
</option>
))}
</SelectInput>
<SelectInput
onChange={e => handleSendTime(e)}
id="min"
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'
// : ''
// }
>
{min}
</option>
))}
</SelectInput>
</NoticeInputGroup>
<SubText>* UTC+9 한국시간 기준으로 설정 (UTC+0 자동 반영처리)</SubText>
</InputGroup2>
</RegistInputItem>
<RegistInputItem>
<InputLabel>메시지 타입</InputLabel>
<SelectInput onChange={e => setResultData({ ...resultData, message_type: e.target.value })} value={resultData.message_type || ''}>
<option value="CHATTING">채팅</option>
<option value="CHATTING_TOAST">채팅 + 토스트</option>
</SelectInput>
</RegistInputItem>
</NoticeInputRow>
<NoticeInputRow>
<CheckBox label="반복 발송" id="input-regist" inline={false} checked={resultData.is_repeat} setData={e => setResultData({ ...resultData, is_repeat: e.target.checked })} style={{display: 'hidden'}}/>
{resultData.is_repeat === true && (
<>
<RegistInputItem>
<InputLabel>반복 타입</InputLabel>
<SelectInput onChange={e => setResultData({ ...resultData, repeat_type: e.target.value })} value={resultData.repeat_type || ''}>
<option value="COUNT">횟수</option>
<option value="DATE">일자</option>
<option value="TIME">특정시간</option>
</SelectInput>
</RegistInputItem>
</>
)}
</NoticeInputRow>
<NoticeInputRow>
{resultData.is_repeat === true && (
<>
{resultData.repeat_type !== "COUNT" && (
<>
<NoticeInputItem>
<InputLabel>종료 일자</InputLabel>
<InputGroup2>
<NoticeInputGroup>
<DatePickerWrapper>
<DatePickerComponent name="종료 일자" handleSelectedDate={data => handleSelectedEndDate(data)} selectedDate={resultData.end_dt} pastDate={new Date()} />
</DatePickerWrapper>
<SelectInput
onChange={e => handleEndTime(e)}
id="hour"
value={
resultData && String(new Date(resultData.end_dt).getHours()) < 10
? '0' + String(new Date(resultData.end_dt).getHours())
: (resultData && String(new Date(resultData.end_dt).getHours())) || ''
}>
{HourList.map(hour => (
<option
value={hour}
key={hour}
>
{hour}
</option>
))}
</SelectInput>
<SelectInput
onChange={e => handleEndTime(e)}
id="min"
value={
resultData && String(new Date(resultData.end_dt).getMinutes()) < 10
? '0' + String(new Date(resultData.end_dt).getMinutes())
: (resultData && String(new Date(resultData.end_dt).getMinutes())) || ''
}>
{MinuteList.map(min => (
<option
value={min}
key={min}
>
{min}
</option>
))}
</SelectInput>
</NoticeInputGroup>
</InputGroup2>
</NoticeInputItem>
</>
)}
<RegistInputItem>
<RepeatTime>
{resultData.repeat_type === "TIME" && (<span>발송시간</span>)}
<SelectInput onChange={e => handleRepeatTime(e)} id="hour" value={resultData.repeat_dt.split(':')[0] || ''}>
{HourList.map(hour => (
<option
value={hour}
key={hour}
>
{hour}
</option>
))}
</SelectInput>
<SelectInput onChange={e => handleRepeatTime(e)} id="min" value={resultData.repeat_dt.split(':')[1] || ''}>
{MinuteList.map(min => (
<option
value={min}
key={min}
>
{min}
</option>
))}
</SelectInput>
{resultData.repeat_type !== "TIME" ? (
<>
<span>마다</span>
<SubText>* 최초 송출 이후 설정된 시간 단위마다 송출</SubText>
</>
) : (<SubText>* 설정된 시간에만 송출</SubText>)}
</RepeatTime>
</RegistInputItem>
{resultData.repeat_type === "COUNT" && (
<>
<NoticeInputItem>
<InputLabel>반복 횟수</InputLabel>
<NoticeInputGroup>
<TextInput
type="number"
value={resultData.repeat_cnt || ''}
onChange={e => {
handleRepeatValue(e);
}}
width="80px"
/>
<span></span>
</NoticeInputGroup>
</NoticeInputItem>
</>
)}
</>
)}
</NoticeInputRow>
</NoticeRegistGroup>
<NoticeInputRow2>
<InputLabel>
[메세지 작성]
<SubTextRow>* 작성하지 않은 언어는 발송되지 않습니다.</SubTextRow>
</InputLabel>
<NoticeInputItem2>
<InputLabel>언어</InputLabel>
<SelectInput onChange={e => handleLanguage(e) } value={message_lang}>
{languageType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</NoticeInputItem2>
</NoticeInputRow2>
<BoxWrapper>
{resultData.game_message.map(content => {
return (
<Fragment key={content.language}>
{message_lang === content.language && (
<MessageBox>
{/* <BtnWrapper $justify="flex-end">
{btnValidation === false ? (
<ButtonClose
onClick={e => {
e.preventDefault();
handleDelete(content.language);
}}
/>
) : (
<ButtonClose opacity="10%" />
)}
</BtnWrapper> */}
<TitleLang>언어 : {content.language}</TitleLang>
<div>
<Textarea id={content.language} onChange={e => handleInputData(e)} defaultValue={content.content || ''} maxLength={250} />
</div>
</MessageBox>
)}
</Fragment>
);
})}
</BoxWrapper>
</MessageWrapper>
{isNullValue && <SearchBarAlert $marginTop="25px">필수값을 입력해주세요.</SearchBarAlert>}
<BtnWrapper $gap="10px" $justify="center" $marginTop="20px">
<Button text="취소" theme="line" handleClick={handleCancelBtn} />
<Button
type="submit"
text="등록"
name="등록버튼"
theme={
// resultData.game_message.map(data => data.content === '').includes(true) ||
!resultData.game_message.some(data => data.content !== '') ||
resultData.game_message.length === 0 ||
resultData.send_dt.length === 0 ||
typeof resultData.send_dt.length != 'undefined' ||
(resultData.is_repeat === true && resultData.repeat_dt === '00:00:00') ||
(resultData.is_repeat === true && resultData.message_type === "COUNT" && resultData.repeat_cnt.length === 0) ||
(resultData.is_repeat === true && resultData.message_type === "COUNT" && resultData.end_dt.length === 0) ||
(resultData.is_repeat === true && resultData.message_type === "COUNT" && typeof resultData.end_dt.length !== 'undefined')
? 'disable'
: 'primary'
}
handleClick={handleRegistBtn}
/>
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={confirmModal}
modalText={confirmText}
handleSubmit={() => {
doubleSubmitFlag || handleSubmitNotice();
setDoubleSubmitFlag(true);
}}
handleCancel={handleConfirmModal}
/>
{/* 완료 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={completeModal}
modalText={completeText}
handleSubmit={handleCompleteModal}
/>
{/* 필수값 미입력 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={errorModal}
modalText={t('NULL_MSG')}
handleSubmit={handleErrorModal}
/>
</>
);
};
export default BoardRegistModal;

View File

@@ -1,86 +0,0 @@
import { TextInput, BtnWrapper, InputLabel, SelectInput } from '../../styles/Components';
import Button from '../common/button/Button';
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
import { useState } from 'react';
import { caliumStatus } from '../../assets/data/options';
const CaliumRequestSearchBar = ({ handleSearch, setResultData }) => {
const [searchData, setSearchData] = useState({
content: '',
status: 'ALL',
startDate: '',
endDate: '',
});
const handleSubmit = event => {
event.preventDefault();
handleSearch(
searchData.content,
searchData.status ? searchData.status : 'ALL',
searchData.startDate ? searchData.startDate : '',
searchData.endDate ? searchData.endDate : new Date(),
(searchData.startDate && searchData.endDate === '') && setSearchData({ startDate : searchData.startDate ,endDate : new Date()}),
);
setResultData(searchData);
};
const handleReset = () => {
setSearchData({
content: '',
status: 'ALL',
startDate: '',
endDate: '',
order: 'DESC',
});
handleSearch('', 'ALL', '', '');
setResultData('', 'ALL', '', '');
window.location.reload();
};
const searchList = [
<>
<InputLabel>등록 일자</InputLabel>
<SearchPeriod
startDate={searchData.startDate}
handleStartDate={data => {
setSearchData({ ...searchData, startDate: data });
}}
endDate={searchData.endDate}
handleEndDate={data => setSearchData({ ...searchData, endDate: data })}
/>
</>,
<>
<InputLabel>요청 내용</InputLabel>
<TextInput
type="text"
placeholder="요청 내용"
value={searchData.content}
onChange={e => setSearchData({ ...searchData, content: e.target.value })}
/>
</>,
<>
<InputLabel>요청 상태</InputLabel>
<SelectInput value={searchData.status} onChange={e => setSearchData({ ...searchData, status: e.target.value })}>
{caliumStatus.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} type="button" />
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
</BtnWrapper>
</>,
];
return <SearchBarLayout firstColumnData={searchList} direction={'column'} />;
};
export default CaliumRequestSearchBar;

View File

@@ -1,564 +0,0 @@
import { useState, useEffect, Fragment } from 'react';
import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, Textarea, SearchBarAlert } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import Modal from '../../components/common/modal/Modal';
import { EventIsItem, EventModify, MailModify } from '../../apis';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import { authType, benItems, commonStatus, modalTypes, wellType } from '../../assets/data';
import {
AppendRegistBox, AppendRegistTable, AreaBtnClose,
BtnDelete, DetailInputItem, DetailInputRow,
DetailModalWrapper, RegistGroup, DetailRegistInfo, DetailState,
Item, ItemList, LangArea
} from '../../styles/ModuleComponents';
import DynamicModal from '../common/modal/DynamicModal';
import { convertKTC, combineDateTime, timeDiffMinute, convertKTCDate } from '../../utils';
import DateTimeInput from '../common/input/DateTimeInput';
const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData }) => {
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const id = content && content.id;
const updateAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate);
const [time, setTime] = useState({
start_hour: '00',
start_min: '00',
end_hour: '00',
end_min: '00',
}); //시간 정보
const [item, setItem] = useState('');
const [itemCount, setItemCount] = useState('');
const [resource, setResource] = useState('19010001');
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 [isNullValue, setIsNullValue] = useState(false);
// 과거 판단
const [isPast, setIsPast] = useState(false);
const [isChanged, setIsChanged] = useState(false);
const [btnValidation, setBtnValidation] = useState(false);
const [isReadOnly, setIsReadOnly] = useState(false);
const [itemCheckMsg, setItemCheckMsg] = useState('');
const [alertMsg, setAlertMsg] = useState('');
useEffect(() => {
if(content){
const start_dt_KTC = convertKTCDate(content.start_dt)
const end_dt_KTC = convertKTCDate(content.end_dt)
setResultData({
start_dt: start_dt_KTC,
end_dt: end_dt_KTC,
event_type: content.event_type,
mail_list: content.mail_list,
item_list: content.item_list,
});
setTime({ ...time,
start_hour: String(start_dt_KTC.getHours()).padStart(2, '0'),
start_min: String(start_dt_KTC.getMinutes()).padStart(2, '0'),
end_hour: String(end_dt_KTC.getHours()).padStart(2, '0'),
end_min: String(end_dt_KTC.getMinutes()).padStart(2, '0')
});
start_dt_KTC < (new Date) ? setIsPast(true) : setIsPast(false);
content.mail_list.length === 1 && setBtnValidation(true);
}
setItem('');
}, [content]);
useEffect(() => {
if(!updateAuth || isPast){
setIsReadOnly(true);
}else{
setIsReadOnly(false);
}
}, [updateAuth, isPast]);
useEffect(() => {
if (conditionCheck()) {
setIsNullValue(false);
} else {
setIsNullValue(true);
}
}, [resultData]);
useEffect(() => {
setItemCheckMsg('');
}, [item]);
// 아이템 수량 숫자 체크
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)){
setAlertMsg(t('MAIL_ITEM_ADD_BEN'))
return;
}
if(item.length === 0 || itemCount.length === 0) return;
const token = sessionStorage.getItem('token');
const result = await EventIsItem(token, {item: item});
if(result.data.result === "ERROR"){
setItemCheckMsg(t('NOT_ITEM'));
return;
}
const itemIndex = resultData.item_list.findIndex((data) => data.item === item);
if (itemIndex !== -1) {
setItemCheckMsg(t('MAIL_ITEM_ADD_DUPL'));
return;
}
const newItem = { item: item, item_cnt: itemCount, item_name: result.data.data.item_info.item_name };
resultData.item_list.push(newItem);
setIsChanged(true);
setItem('');
setItemCount('');
};
// 아이템 삭제
const onItemRemove = id => {
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
setIsChanged(true);
setResultData({ ...resultData, item_list: filterList });
};
// 자원 수량 숫자 체크
const handleResourceCount = e => {
if (e.target.value === '0' || e.target.value === '-0') {
setResourceCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setResourceCount(plusNum);
} else {
setResourceCount(e.target.value);
}
};
// 자원 추가
const handleResourceList = (e) => {
if(resource.length === 0 || resourceCount.length === 0) return;
const itemIndex = resultData.item_list.findIndex(
(item) => item.item === resource
);
if (itemIndex !== -1) {
const item_cnt = resultData.item_list[itemIndex].item_cnt;
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
} else {
const name = wellType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem);
}
setIsChanged(true);
setResource('')
setResourceCount('');
};
// 입력창 삭제
const 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 handleDateChange = (data, type) => {
const date = new Date(data);
setResultData({
...resultData,
[`${type}_dt`]: combineDateTime(date, time[`${type}_hour`], time[`${type}_min`]),
});
setIsChanged(true);
};
// 시간 처리
const handleTimeChange = (e, type) => {
const { id, value } = e.target;
const newTime = { ...time, [`${type}_${id}`]: value };
setTime(newTime);
const date = resultData[`${type}_dt`] ? new Date(resultData[`${type}_dt`]) : new Date();
setResultData({
...resultData,
[`${type}_dt`]: combineDateTime(date, newTime[`${type}_hour`], newTime[`${type}_min`]),
});
setIsChanged(true);
};
// 확인 버튼 후 다 초기화
const handleReset = () => {
setBtnValidation(false);
setIsNullValue(false);
setIsChanged(false);
};
const conditionCheck = () => {
return (
content && content.mail_list.every(data => data.content !== '' && data.title !== '') &&
isChanged
);
};
const handleModalView = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'view',
}));
}
const handleModalClose = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'hidden',
}));
}
const handleSubmit = async (type, param = null) => {
switch (type) {
case "submit":
if (!conditionCheck()) return;
handleModalView('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;
}
await EventModify(token, id, resultData);
handleModalClose('updateConfirm');
handleModalView('updateComplete');
break;
case "updateComplete":
handleModalClose('updateComplete');
window.location.reload();
break;
case "warning":
setAlertMsg('');
break;
}
}
const detailState = (status) => {
switch (status) {
case commonStatus.wait:
return <DetailState>대기</DetailState>;
case commonStatus.running:
return <DetailState>진행중</DetailState>;
case commonStatus.finish:
return <DetailState result={commonStatus.finish}>완료</DetailState>;
case commonStatus.fail:
return <DetailState result={commonStatus.fail}>실패</DetailState>;
case commonStatus.delete:
return <DetailState result={commonStatus.delete}>삭제</DetailState>;
default:
return null;
}
};
return (
<>
<Modal min="960px" $view={detailView}>
<Title $align="center">이벤트 상세 정보</Title>
{content &&
<DetailRegistInfo>
<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>
</>
)}
</DetailRegistInfo>
}
<DetailModalWrapper>
{content &&
<RegistGroup>
<DetailInputRow>
<DateTimeInput
title="이벤트 기간"
dateName="시작 일자"
selectedDate={convertKTCDate(content.start_dt)}
handleSelectedDate={data => handleDateChange(data, 'start')}
onChange={e => handleTimeChange(e, 'start')}
/>
<DateTimeInput
dateName="종료 일자"
selectedDate={convertKTCDate(content.end_dt)}
handleSelectedDate={data => handleDateChange(data, 'end')}
onChange={e => handleTimeChange(e, 'end')}
/>
</DetailInputRow>
<DetailInputRow>
<DetailInputItem>
<InputLabel>이벤트 상태</InputLabel>
<div>{detailState(content.status)}</div>
</DetailInputItem>
{content.status === commonStatus.delete &&
<DetailInputItem>
<InputLabel>삭제 사유</InputLabel>
<div>{content.delete_desc}</div>
</DetailInputItem>
}
</DetailInputRow>
</RegistGroup>
}
{resultData.mail_list &&
resultData.mail_list.map(data => {
return (
<Fragment key={data.language}>
<AppendRegistBox>
<LangArea>
언어 : {data.language}
{btnValidation === false && !isReadOnly ? (
<AreaBtnClose
onClick={e => {
e.preventDefault();
onLangDelete(data.language);
}}
/>
) : (
<AreaBtnClose opacity="10%" />
)}
</LangArea>
<AppendRegistTable>
<tbody>
<tr>
<th width="120">
<Label>제목</Label>
</th>
<td>
<DetailInputItem>
<TextInput
placeholder="우편 제목 입력"
maxLength="30"
id={data.language}
value={data.title}
readOnly={isReadOnly}
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);
}}
/>
</DetailInputItem>
</td>
</tr>
<tr>
<th>
<Label>내용</Label>
</th>
<td>
<Textarea
value={data.content}
readOnly={isReadOnly}
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>
</AppendRegistTable>
</AppendRegistBox>
</Fragment>
);
})}
<AppendRegistBox>
<AppendRegistTable>
<tbody>
<tr>
<th width="120">
<Label>아이템 첨부</Label>
</th>
<td>
<DetailInputItem>
<TextInput
placeholder="Item Meta id 입력"
value={item}
onChange={e => {
let list = [];
list = e.target.value.trimStart();
setItem(list);
}}
disabled={isReadOnly}
/>
<TextInput
placeholder="수량"
value={itemCount}
type="number"
onChange={e => handleItemCount(e)}
width="90px"
disabled={isReadOnly}
/>
<Button
text="추가"
theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'}
handleClick={handleItemList}
/>
{itemCheckMsg && <SearchBarAlert>{itemCheckMsg}</SearchBarAlert>}
</DetailInputItem>
</td>
</tr>
<tr>
<th width="120">
<Label>자원 첨부</Label>
</th>
<td>
<DetailInputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={isReadOnly}>
{wellType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput
placeholder="수량"
type="number"
value={resourceCount}
disabled={isReadOnly}
onChange={e => handleResourceCount(e)}
width="200px"
/>
<Button
text="추가"
theme={resourceCount.length === 0 || resource.length === 0 ? 'disable' : 'search'}
handleClick={handleResourceList}
width="100px"
height="35px"
errorMessage={isReadOnly} />
</DetailInputItem>
<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>
{!isReadOnly && <BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>}
</Item>
);
})}
</ItemList>
)}
</div>
</td>
</tr>
</tbody>
</AppendRegistTable>
</AppendRegistBox>
</DetailModalWrapper>
<BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px">
<Button
text="확인"
theme="line"
name="확인버튼"
handleClick={() => {
handleDetailView();
handleReset();
setDetailData('');
}}
/>
{!isReadOnly && (
<Button
type="submit"
text="수정"
id="수정버튼"
theme={conditionCheck() ? 'primary' : 'disable'}
handleClick={() => handleSubmit('submit')}
/>
)}
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.updateConfirmModal}
modalText={t('MAIL_UPDATE_SAVE')}
handleCancel={() => handleModalClose('updateConfirm')}
handleSubmit={() => handleSubmit('updateConfirm')}
/>
{/* 완료 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={modalState.updateCompleteModal}
modalText={t('UPDATE_COMPLETED')}
handleSubmit={() => handleSubmit('updateComplete')}
/>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => handleSubmit('warning')}
/>
</>
);
};
export default EventDetailModal;

View File

@@ -1,103 +0,0 @@
import { TextInput, BtnWrapper, InputLabel, SelectInput } from '../../styles/Components';
import Button from '../common/button/Button';
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
import { useState } from 'react';
import { eventStatus } from '../../assets/data';
const EventListSearchBar = ({ handleSearch, setResultData }) => {
const [searchData, setSearchData] = useState({
title: '',
content: '',
status: 'ALL',
startDate: '',
endDate: '',
});
const handleSubmit = event => {
event.preventDefault();
handleSearch(
searchData.title,
searchData.content,
searchData.status ? searchData.status : 'ALL',
searchData.startDate ? searchData.startDate : '',
searchData.endDate ? searchData.endDate : new Date(),
(searchData.startDate && searchData.endDate === '') && setSearchData({ startDate : searchData.startDate ,endDate : new Date()}),
);
setResultData(searchData);
};
const handleReset = () => {
setSearchData({
title: '',
content: '',
status: 'ALL',
startDate: '',
endDate: '',
order: 'DESC',
});
handleSearch('', '', 'ALL', '', '');
setResultData('', '', 'ALL', '', '');
window.location.reload();
};
// console.log("searchData.endDate", searchData.endDate)
const searchList = [
<>
<InputLabel>우편 제목</InputLabel>
<TextInput
type="text"
placeholder="우편 제목"
value={searchData.title}
onChange={e => setSearchData({ ...searchData, title: e.target.value })}
/>
</>,
<>
<InputLabel>조회 일자</InputLabel>
<SearchPeriod
startDate={searchData.startDate}
handleStartDate={data => {
setSearchData({ ...searchData, startDate: data });
}}
endDate={searchData.endDate}
handleEndDate={data => setSearchData({ ...searchData, endDate: data })}
/>
</>,
<>
<InputLabel>우편 내용</InputLabel>
<TextInput
type="text"
placeholder="우편 내용(공백으로 구분)"
value={searchData.content}
onChange={e => setSearchData({ ...searchData, content: e.target.value })}
/>
</>
];
const optionList = [
<>
<InputLabel>이벤트 상태</InputLabel>
<SelectInput value={searchData.status} onChange={e => setSearchData({ ...searchData, status: e.target.value })}>
{eventStatus.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,<></>,
<>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} type="button" />
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
</BtnWrapper>
</>,
];
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
};
export default EventListSearchBar;

View File

@@ -1,127 +0,0 @@
import { useState } from 'react';
import { TextInput, BtnWrapper, InputLabel, SelectInput, InputGroup } from '../../styles/Components';
import Button from '../common/button/Button';
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
const ItemsSearchBar = ({ handleSearch, setResultData }) => {
const [searchData, setSearchData] = useState({
searchType: 'GUID',
data: '',
status: 'ALL',
restore: 'ALL',
sendDate: '',
endDate: '',
});
const searchType = [
{ value: 'GUID', name: 'GUID' },
{ value: 'NAME', name: '닉네임' }
];
const status = [
{ value: 'ALL', name: '상태' },
{ value: 'ACTIVE', name: '활성' },
{ value: 'DEACTIVE', name: '비활성' },
];
const restore = [
{ value: 'ALL', name: '복구' },
{ value: 'POSSIBLE', name: '가능' },
{ value: 'IMPOSSIBLE', name: '불가능' },
];
const handleSubmit = event => {
event.preventDefault();
handleSearch(
searchData.searchType ? searchData.searchType : 'GUID',
searchData.data,
searchData.status ? searchData.status : 'ALL',
searchData.restore ? searchData.restore : 'ALL',
searchData.sendDate ? searchData.sendDate : '',
searchData.endDate ? searchData.endDate : new Date(),
);
setResultData(searchData);
};
// 초기화 버튼
const handleReset = () => {
setSearchData({
searchType: 'GUID',
data: '',
status: 'ALL',
restore: 'ALL',
sendDate: '',
endDate: '',
});
handleSearch('GUID', '', 'ALL', 'ALL', '', '');
setResultData('GUID', '', 'ALL', 'ALL', '', '');
window.location.reload();
};
// console.log(searchData);
const searchList = [
<>
<InputGroup>
<SelectInput value={searchData.searchType} onChange={e => setSearchData({ ...searchData, searchType: e.target.value })}>
{searchType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput
type="text"
placeholder={searchData.searchType === 'GUID' ? 'GUID 입력' : '닉네임 입력'}
value={searchData.data}
width="600px"
onChange={e => setSearchData({ ...searchData, data: e.target.value })}
/>
</InputGroup>
</>
];
const optionList = [
<>
<SelectInput value={searchData.status} onChange={e => setSearchData({ ...searchData, status: e.target.value })}>
{status.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<SelectInput value={searchData.restore} onChange={e => setSearchData({ ...searchData, restore: e.target.value })}>
{restore.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>생성 날짜</InputLabel>
<SearchPeriod
startDate={searchData.sendDate}
handleStartDate={data => {
setSearchData({ ...searchData, sendDate: data });
}}
endDate={searchData.endDate}
handleEndDate={data => setSearchData({ ...searchData, endDate: data })}
maxDate={new Date()}
/>
</>,
<>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} />
<Button theme="search" text="검색" type="submit" handleClick={handleSubmit} />
</BtnWrapper>
</>,
];
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
};
export default ItemsSearchBar;

View File

@@ -1,517 +0,0 @@
import { useState, Fragment, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import Button from '../common/button/Button';
import Loading from '../common/Loading';
import {
Title,
BtnWrapper,
SearchBarAlert, SelectInput, InputLabel,
} from '../../styles/Components';
import {
FormHelperText,
FormInput,
FormLabel,
FormTextArea,
FormTextAreaWrapper,
MessageWrapper,
FormRowGroup,
NoticeInputRow2,
NoticeInputItem2, BoxWrapper, FormStatusBar, FormStatusLabel, FormStatusWarning, FormButtonContainer,
} from '../../styles/ModuleComponents';
import { modalTypes } from '../../assets/data';
import {DynamicModal, Modal, DateTimeRangePicker} from '../common';
import { LandAuctionModify, LandAuctionSingleRegist } from '../../apis';
import { TYPE_MODIFY, TYPE_REGISTRY } from '../../assets/data/adminConstants';
import { landAuctionStatus, landAuctionStatusType, languageType, CurrencyType } from '../../assets/data';
import { useModal } from '../../utils/hook';
import { convertKTCDate } from '../../utils';
const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, setDetailData, landData, buildingData }) => {
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const [loading, setLoading] = useState(false); // 로딩 창
const {
modalState,
handleModalView,
handleModalClose
} = useModal({
cancel: 'hidden',
registConfirm: 'hidden',
registComplete: 'hidden'
});
const [message_lang, setMessage_lang] = useState('KO');
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
const [alertMsg, setAlertMsg] = useState('');
const [selectLand, setSelectLand] = useState(initLandData);
const [resultData, setResultData] = useState(initData); //데이터 정보
const [resetDateTime, setResetDateTime] = useState(false);
useEffect(() => {
if(modalType === TYPE_MODIFY && content && Object.keys(content).length > 0){
setResultData({
land_id: content.land_id,
land_name: content.land_name,
land_socket: content.land_socket,
land_size: content.land_size,
auction_seq: content.auction_seq,
currency_type: content.currency_type,
start_price: content.start_price,
resv_start_dt: convertKTCDate(content.resv_start_dt),
resv_end_dt: convertKTCDate(content.resv_end_dt),
auction_start_dt: convertKTCDate(content.auction_start_dt),
auction_end_dt: convertKTCDate(content.auction_end_dt),
message_list: content.message_list,
});
const land = landData.find(land => land.id === parseInt(content.land_id));
setSelectLand(land);
}
}, [modalType, content]);
useEffect(() => {
if (checkCondition()) {
setIsNullValue(false);
} else {
setIsNullValue(true);
}
}, [resultData]);
useEffect(() => {
if (resetDateTime) {
setResetDateTime(false);
}
}, [resetDateTime]);
// 입력 수량 처리
const handleCount = e => {
const regex = /^\d*\.?\d{0,2}$/;
if (!regex.test(e.target.value) && e.target.value !== '-') {
return;
}
let count = 0;
if (e.target.value === '-0') {
count = 1;
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
count = plusNum;
} else{
count = e.target.value;
}
setResultData((prevState) => ({
...prevState,
start_price: count,
}));
};
const handleReservationChange = {
start: (date) => {
setResultData(prev => ({ ...prev, resv_start_dt: date }));
},
end: (date) => {
setResultData(prev => ({ ...prev, resv_end_dt: date }));
}
};
const handleAuctionChange = {
start: (date) => {
setResultData(prev => ({ ...prev, auction_start_dt: date }));
},
end: (date) => {
setResultData(prev => ({ ...prev, auction_end_dt: date }));
}
};
// 입력 글자 제한
const handleInputData = e => {
if (e.target.value.length > 250) {
return;
}
const updatedMessages = resultData.message_list.map(msg =>
msg.language === message_lang
? { ...msg, content: e.target.value.trimStart() }
: msg
);
setResultData(prev => ({
...prev,
message_list: updatedMessages
}));
};
// 언어 선택
const handleLanguage = e => {
setMessage_lang(e.target.value);
if(!resultData.message_list.some(({language}) => language === e.target.value))
setResultData({ ...resultData, message_list: [...resultData.message_list, {language: e.target.value, content: ''}] })
}
const handleLand = e => {
const land_id = e.target.value;
const land = landData.find(land => land.id === parseInt(land_id));
const instance = buildingData.find(building => building.id === parseInt(land.buildingId))?.socket;
setSelectLand(land);
setResultData({ ...resultData, land_id: land_id, land_name: land.name, land_size: land.size, land_socket: instance });
}
const handleReset = () => {
setMessage_lang('KO')
setDetailData({});
setSelectLand(initLandData);
setResultData(initData);
setResetDateTime(true);
handleDetailView();
}
const handleSubmit = async (type, param = null) => {
switch (type) {
case "submit":
if (!checkCondition()) return;
const minAllowedTime = new Date(new Date().getTime() + 5 * 60000);
if (isView('recv') && resultData.resv_start_dt < minAllowedTime) {
setAlertMsg(t('LAND_AUCTION_MADEL_RESV_START_WARNING'));
return;
}
if (resultData.auction_start_dt < minAllowedTime) {
setAlertMsg(t('LAND_AUCTION_MADEL_AUCTION_START_WARNING'));
return;
}
if(resultData.resv_start_dt >= resultData.auction_start_dt || resultData.resv_start_dt >= resultData.auction_end_dt) {
setAlertMsg(t('LAND_AUCTION_MADEL_AUCTION_DIFF_RESERVATION'))
return;
}
if(resultData.auction_start_dt >= resultData.auction_end_dt) {
setAlertMsg(t('LAND_AUCTION_MADEL_AUCTION_DIFF_AUCTION'))
return;
}
//화면에 머물면서 상태는 안바꼈을 경우가 있기에 경매시작시간 지났을경우 차단
if (modalType === TYPE_MODIFY && resultData.auction_start_dt < new Date()) {
setAlertMsg(t('LAND_AUCTION_MADEL_MODIFY_START'));
return;
}
handleModalView('registConfirm');
break;
case "cancel":
handleModalView('cancel');
break;
case "cancelConfirm":
handleModalClose('cancel');
handleReset();
break;
case "registConfirm":
setLoading(true);
if(isView('modify')){
await LandAuctionModify(token, content?.id, resultData).then(data => {
setLoading(false);
handleModalClose('registConfirm');
if(data.result === "SUCCESS") {
handleModalView('registComplete');
}else if(data.result === "ERROR_AUCTION_STATUS_IMPOSSIBLE"){
setAlertMsg(t('LAND_AUCTION_ERROR_MODIFY_STATUS'));
}else{
setAlertMsg(t('UPDATE_FAIL'));
}
}).catch(reason => {
setAlertMsg(t('API_FAIL'));
});
}
else{
await LandAuctionSingleRegist(token, resultData).then(data => {
setLoading(false);
handleModalClose('registConfirm');
if(data.result === "SUCCESS") {
handleModalView('registComplete');
}else if(data.result === "ERROR_LAND_AUCTION_IMPOSSIBLE"){
setAlertMsg(t('LAND_AUCTION_ERROR_PROGRESS'));
}else if(data.result === "ERROR_AUCTION_LAND_OWNER"){
setAlertMsg(t('LAND_AUCTION_ERROR_OWNER'));
}else{
setAlertMsg(t('REGIST_FAIL'));
}
}).catch(reason => {
setAlertMsg(t('API_FAIL'));
});
}
break;
case "registComplete":
handleModalClose('registComplete');
handleReset();
window.location.reload();
break;
case "warning":
setAlertMsg('');
break;
}
}
const checkCondition = () => {
return (
resultData.start_price > 0
&& resultData.auction_start_dt !== ''
&& resultData.auction_end_dt !== ''
&& resultData.resv_start_dt !== ''
&& resultData.resv_end_dt !== ''
&& resultData.land_id !== ''
// && resultData.message_list?.every(data => data.content !== '')
);
};
const isView = (label) => {
switch (label) {
case "recv":
return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY && content?.status === landAuctionStatusType.wait);
case "auction":
case "price":
case "message":
return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY &&(content?.status === landAuctionStatusType.resv_start || content?.status === landAuctionStatusType.wait));
case "modify":
return modalType === TYPE_MODIFY && (content?.status === landAuctionStatusType.resv_start || content?.status === landAuctionStatusType.wait);
case "registry":
return modalType === TYPE_REGISTRY
default:
return modalType === TYPE_MODIFY && (content?.status === landAuctionStatusType.stl_end
|| content?.status === landAuctionStatusType.auction_start
|| content?.status === landAuctionStatusType.auction_end
|| content?.status === landAuctionStatusType.fail
|| content?.status === landAuctionStatusType.cancel
);
}
}
return (
<>
<Modal min="760px" $view={detailView}>
<Title $align="center">{isView('registry') ? "랜드 경매 등록" : isView('modify') ? "랜드 경매 수정" : "랜드 경매 상세"}</Title>
<MessageWrapper>
<FormRowGroup>
<FormLabel>랜드선택</FormLabel>
<SelectInput value={resultData.land_id} onChange={e => handleLand(e)} disabled={!isView('registry')} width="400px">
{landData && landData.map((data, index) => (
<option key={index} value={data.id}>
{data.name}({data.id})
</option>
))}
</SelectInput>
</FormRowGroup>
<FormRowGroup>
<FormLabel>랜드 이름</FormLabel>
<FormInput
type="text"
disabled={true}
width='400px'
value={resultData?.land_name}
/>
</FormRowGroup>
<FormRowGroup>
<FormLabel>랜드 크기</FormLabel>
<FormInput
type="text"
disabled={true}
width='200px'
value={resultData?.land_size}
/>
<FormLabel>인스턴스 </FormLabel>
<FormInput
type="text"
disabled={true}
width='200px'
value={resultData?.land_socket}
/>
</FormRowGroup>
<FormRowGroup>
<FormLabel>입찰 재화</FormLabel>
<SelectInput value={resultData.currency_type} width='200px' disabled={true} >
{CurrencyType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<FormLabel>입찰시작가</FormLabel>
<FormInput
type="number"
name="price"
value={resultData.start_price}
step={"0.01"}
min={0}
width='200px'
disabled={!isView('price')}
onChange={e => handleCount(e)}
/>
</FormRowGroup>
<DateTimeRangePicker
label="예약기간"
startDate={resultData.resv_start_dt}
endDate={resultData.resv_end_dt}
onStartDateChange={handleReservationChange.start}
onEndDateChange={handleReservationChange.end}
pastDate={new Date()}
disabled={!isView('recv')}
startLabel="시작 일자"
endLabel="종료 일자"
reset={resetDateTime}
setAlert={setAlertMsg}
/>
<DateTimeRangePicker
label="경매기간"
startDate={resultData.auction_start_dt}
endDate={resultData.auction_end_dt}
onStartDateChange={handleAuctionChange.start}
onEndDateChange={handleAuctionChange.end}
pastDate={new Date()}
disabled={!isView('auction')}
startLabel="시작 일자"
endLabel="종료 일자"
reset={resetDateTime}
setAlert={setAlertMsg}
/>
{/*<NoticeInputRow2>*/}
{/* <InputLabel>*/}
{/* 메세지 작성[경매 시작 5분전 공지 - 미구현]*/}
{/* </InputLabel>*/}
{/* <NoticeInputItem2>*/}
{/* <InputLabel>언어</InputLabel>*/}
{/* <SelectInput onChange={e => handleLanguage(e) } value={message_lang}>*/}
{/* {languageType.map((data, index) => (*/}
{/* <option key={index} value={data.value}>*/}
{/* {data.name}*/}
{/* </option>*/}
{/* ))}*/}
{/* </SelectInput>*/}
{/* </NoticeInputItem2>*/}
{/*</NoticeInputRow2>*/}
{/*<BoxWrapper>*/}
{/* {resultData.message_list.map(content => {*/}
{/* return (*/}
{/* <Fragment key={content.language}>*/}
{/* {message_lang === content.language && (*/}
{/* <FormTextAreaWrapper>*/}
{/* <FormTextArea*/}
{/* name="content"*/}
{/* id={content.language}*/}
{/* value={content.content}*/}
{/* onChange={e => handleInputData(e)}*/}
{/* maxLength={250}*/}
{/* disabled={!isView('message')}*/}
{/* />*/}
{/* </FormTextAreaWrapper>*/}
{/* )}*/}
{/* </Fragment>*/}
{/* );*/}
{/* })}*/}
{/*</BoxWrapper>*/}
{!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>}
</MessageWrapper>
<BtnWrapper $gap="10px" $marginTop="10px">
<FormStatusBar>
<FormStatusLabel>
현재상태: {landAuctionStatus.find(data => data.value === content?.status)?.name || "등록"}
</FormStatusLabel>
<FormStatusWarning>
{isView('registry') ? '' : t('LAND_AUCTION_MODAL_STATUS_WARNING')}
</FormStatusWarning>
</FormStatusBar>
<FormButtonContainer $gap="5px">
{isView() ?
<Button
text="확인"
name="확인버튼"
theme="line"
handleClick={() => handleReset()}
/>
:
<>
<Button text="취소" theme="line" handleClick={() => handleSubmit('cancel')} />
<Button
type="submit"
text={isView('modify') ? "수정" : "등록"}
name="등록버튼"
theme={
checkCondition()
? 'primary'
: 'disable'
}
handleClick={() => handleSubmit('submit')}
/>
</>
}
</FormButtonContainer>
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.registConfirmModal}
modalText={isView('modify') ? t('LAND_UPDATE_CONFIRM') : t('LAND_REGIST_CONFIRM')}
handleSubmit={() => handleSubmit('registConfirm')}
handleCancel={() => handleModalClose('registConfirm')}
/>
{/* 완료 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={modalState.registCompleteModal}
modalText={isView('modify') ? t('UPDATE_COMPLETED') : t('REGIST_COMPLTE')}
handleSubmit={() => handleSubmit('registComplete')}
/>
{/* 취소 모달 */}
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.cancelModal}
modalText={t('CANCEL_CONFIRM')}
handleCancel={() => handleModalClose('cancel')}
handleSubmit={() => handleSubmit('cancelConfirm')}
/>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => handleSubmit('warning')}
/>
{loading && <Loading/>}
</>
);
};
export const initData = {
land_id: '',
land_name: '',
land_size: '',
land_socket: '',
currency_type: 'Calium',
start_price: 0,
resv_start_dt: '',
resv_end_dt: '',
auction_start_dt: '',
auction_end_dt: '',
message_list: [
{ language: 'KO', content: '' },
{ language: 'EN', content: '' },
{ language: 'JA', content: '' },
],
}
export const initLandData = {
land_id: 0,
name: '',
size: '',
socket: '',
desc: '',
open: false,
owner: ''
}
export default LandAuctionModal;

View File

@@ -1,205 +0,0 @@
import { TextInput, BtnWrapper, InputLabel, SelectInput, InputGroup } from '../../styles/Components';
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';
export const useLandAuctionSearch = (token, initialPageSize) => {
const [searchParams, setSearchParams] = useState({
landType: 'ID',
landData: '',
userType: 'GUID',
userData: '',
landSize: 'ALL',
status: 'ALL',
auctionStartDate: '',
auctionEndDate: '',
orderBy: 'DESC',
pageSize: initialPageSize,
currentPage: 1
});
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
useEffect(() => {
fetchData(searchParams); // 컴포넌트 마운트 시 초기 데이터 로드
}, [token]);
const fetchData = useCallback(async (params) => {
try {
setLoading(true);
const result = await LandAuctionView(
token,
params.landType,
params.landData,
params.userType,
params.userData,
params.landSize,
params.status,
params.auctionStartDate && new Date(params.auctionStartDate).toISOString(),
params.auctionEndDate && new Date(params.auctionEndDate).toISOString(),
params.orderBy,
params.pageSize,
params.currentPage
);
setData(result);
return result;
} catch (error) {
console.error('Error fetching auction data:', error);
throw error;
} finally {
setLoading(false);
}
}, [token]);
const updateSearchParams = useCallback((newParams) => {
setSearchParams(prev => ({
...prev,
...newParams
}));
}, []);
const handleSearch = useCallback(async (newParams = {}) => {
const updatedParams = {
...searchParams,
...newParams,
currentPage: newParams.currentPage || 1 // Reset to first page on new search
};
updateSearchParams(updatedParams);
return await fetchData(updatedParams);
}, [searchParams, fetchData]);
const handleReset = useCallback(async () => {
const resetParams = {
landType: 'ID',
landData: '',
userType: 'GUID',
userData: '',
landSize: 'ALL',
status: 'ALL',
auctionStartDate: '',
auctionEndDate: '',
orderBy: 'DESC',
pageSize: initialPageSize,
currentPage: 1
};
setSearchParams(resetParams);
return await fetchData(resetParams);
}, [initialPageSize, fetchData]);
const handlePageChange = useCallback(async (newPage) => {
return await handleSearch({ currentPage: newPage });
}, [handleSearch]);
const handlePageSizeChange = useCallback(async (newSize) => {
return await handleSearch({ pageSize: newSize, currentPage: 1 });
}, [handleSearch]);
const handleOrderByChange = useCallback(async (newOrder) => {
return await handleSearch({ orderBy: newOrder });
}, [handleSearch]);
return {
searchParams,
loading,
data,
handleSearch,
handleReset,
handlePageChange,
handlePageSizeChange,
handleOrderByChange,
updateSearchParams
};
};
const LandAuctionSearchBar = ({ searchParams, onSearch, onReset }) => {
const handleSubmit = event => {
event.preventDefault();
onSearch(searchParams);
};
const searchList = [
<>
<InputGroup>
<SelectInput value={searchParams.landType} onChange={e => onSearch({landType: e.target.value })}>
{landSearchType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput
type="text"
placeholder={searchParams.landType === 'ID' ? '랜드 ID 입력' : '랜드명 입력'}
value={searchParams.landData}
width="300px"
onChange={e => onSearch({ landData: e.target.value })}
/>
</InputGroup>
</>,
<>
<InputLabel>낙찰자</InputLabel>
<InputGroup>
<SelectInput value={searchParams.userType} onChange={e => onSearch({userType: e.target.value })}>
{userSearchType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput
type="text"
placeholder={searchParams.userType === 'GUID' ? 'GUID 입력' : '닉네임 입력'}
value={searchParams.userData}
width="300px"
onChange={e => onSearch({ userData: e.target.value })}
/>
</InputGroup>
</>
];
const optionList = [
<>
<InputLabel>랜드크기</InputLabel>
<SelectInput value={searchParams.landSize} onChange={e => onSearch({ landSize: e.target.value }, false)} >
{landSize.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>경매상태</InputLabel>
<SelectInput value={searchParams.status} onChange={e => onSearch({ status: e.target.value }, false)}>
{landAuctionStatus.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>경매 일자</InputLabel>
<SearchPeriod
startDate={searchParams.auctionStartDate}
handleStartDate={date => onSearch({ auctionStartDate: date }, false)}
endDate={searchParams.auctionEndDate}
handleEndDate={date => onSearch({ auctionEndDate: date }, false)}
/>
</>,
<></>,<></>,
<>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={onReset} type="button" />
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
</BtnWrapper>
</>,
];
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
};
export default LandAuctionSearchBar;

View File

@@ -1,979 +0,0 @@
import { styled } from 'styled-components';
import RadioInput from '../../components/common/input/Radio';
import { useState, useEffect, Fragment } from 'react';
import CheckBox from '../../components/common/input/CheckBox';
import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, DatePickerWrapper, Textarea, ModalText, ButtonClose, SearchBarAlert } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import Modal from '../../components/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 '../../components/ServiceManage/MailRegistUploadBtn';
import { benItems, HourList, MinuteList, modalTypes, wellType } from '../../assets/data';
import { EventModify, 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';
const MailDetailModal = ({ detailView, handleDetailView, content }) => {
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
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 [sendHour, setSendHour] = useState('00');
const [sendMin, setSendMin] = useState('00');
const [item, setItem] = useState('');
const [itemCount, setItemCount] = useState('');
const [resource, setResource] = useState('19010001');
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 [isNullValue, setIsNullValue] = useState(false);
// 과거 판단
const [isPast, setIsPast] = useState(false);
const [isChanged, setIsChanged] = useState(false);
const [isItemNullValue, setIsItemNullValue] = useState(false);
const [excelFile, setExcelFile] = useState(content.target ? content.target : '');
const [excelName, setExcelName] = useState(null);
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 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(() => {
document.querySelector('#fileinput').value = '';
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]);
// console.log('downloadData', downloadData);
// console.log('isPast', isPast);
// console.log("guid", resultData.guid)
// console.log("메일 형식", content && content.receive_type," 엑셀네임", excelName)
// 아이템 수량 숫자 체크
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 = () => {
if(benItems.includes(item)){
setAlertMsg(t('MAIL_ITEM_ADD_BEN'))
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('');
}
};
// 아이템 삭제
const onItemRemove = id => {
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 });
};
// 자원 수량 숫자 체크
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 = () => {
resourceCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
if (resourceCount.length === 0 || resourceCount <= 0) {
setIsItemNullValue(true);
} else {
setIsItemNullValue(false);
const name = wellType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem);
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 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 = () => {
setBtnValidation(false);
setIsNullValue(false);
setIsChanged(false);
setUndefinedFile(false);
setIsItemNullValue(false);
setAlertMessage('');
setExcelFile(null);
setExcelName(null);
};
// 상세 페이지에서 파일 삭제
const handleDetailDelete = e => {
e.preventDefault();
if (content && content.is_reserve === true) {
setDownLoadData(undefined);
setUndefinedFile(true);
setResultData({ ...resultData, guid: '' });
delete resultData.file_name;
document.querySelector('#fileinput').value = '';
setExcelFile(null);
}
setIsChanged(true);
};
const handleModalView = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'view',
}));
}
const handleModalClose = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'hidden',
}));
}
const handleSubmit = async (type, param = null) => {
switch (type) {
case "submit":
// if (!conditionCheck()) return;
handleModalView('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('');
break;
}
}
return (
<>
<Modal min="960px" $view={detailView}>
<Title $align="center">우편 상세 정보</Title>
<RegistInfo>
<span>등록자 : {content && content.create_by}</span>
<span>등록일 : {content && convertKTC(content.create_dt, false)}</span>
{content && typeof content.update_by !== 'undefined' && (
<>
<span>수정자 : {content && content.update_by}</span>
<span>수정일 : {content && 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 });
setDisabledBtn(e.target.checked);
setIsChanged(true);
}}
disabled={(content && content.is_reserve === false) || onlyView}
/>
{content && 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}
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 && content.is_reserve === false) || onlyView || isPast}
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'
// : ''
// }
>
{hour}
</option>
))}
</SelectInput>
<SelectInput
onChange={e => {
handleSendTime(e);
setIsChanged(true);
}}
id="min"
disabled={(content && content.is_reserve === false) || onlyView || isPast}
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'
// : ''
// }
>
{min}
</option>
))}
</SelectInput>
</InputGroup>
</InputItem>
)
)}
<InputItem>
<InputLabel>우편 타입</InputLabel>
<SelectInput
onChange={e => {
setResultData({ ...resultData, mail_type: e.target.value });
setIsChanged(true);
}}
value={content && resultData.mail_type}
disabled={(content && content.is_reserve === false) || onlyView || isPast}>
<option value="SELECT">타입 선택</option>
<option value="SYSTEM_GUID">시스템 안내</option>
<option value="INSPECTION_COMPENSATION">점검 보상</option>
<option value="RECOVER_COMPENSATION">복구 보상</option>
<option value="EVENT_COMPENSATION">이벤트 보상</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>}
</div>
</InputItem>
</InputRow>
<MailReceiver>
<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>
</InputItem>
<div>
<InputGroup>
<RadioInput
label="단일"
id="SINGLE"
name="receiver"
value="SINGLE"
disabled={(content && content.is_reserve === false) || onlyView || isPast}
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 : ''}
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>
)}
</div>
</InputItem>
</MailReceiver>
</RegistGroup>
{resultData.mail_list &&
resultData.mail_list.map(data => {
return (
<Fragment key={data.language}>
<MailRegistBox>
<LangArea>
언어 : {data.language}
{btnValidation === false ? (
<BtnClose
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 && content.is_reserve === false) || onlyView || isPast}
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={(content && content.is_reserve === false) || onlyView || isPast}
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={(content && content.is_reserve === false) || onlyView || isPast}
/>
<TextInput
placeholder="수량"
value={itemCount}
type="number"
onChange={e => handleItemCount(e)}
width="90px"
disabled={(content && content.is_reserve === false) || onlyView || isPast}
/>
<Button
text="추가"
theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'}
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>
<th width="120">
<Label>자원 첨부</Label>
</th>
<td>
<InputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={(content && content.is_reserve === false) || onlyView || isPast}>
{wellType.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} />
</InputItem>
{isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
<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>
{(content && content.is_reserve === true) || onlyView || isPast && <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();
}}
/>
{(updateAuth && content && content.is_reserve === true) && !isPast && (
<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'
}
handleClick={() => {
handleModifyModal();
}}
/>
)}
</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')}
/>
</>
);
};
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;
`;

View File

@@ -1,143 +0,0 @@
import styled from 'styled-components';
import { TextInput, BtnWrapper, InputLabel, SelectInput } from '../../styles/Components';
import Button from '../common/button/Button';
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
import { useState } from 'react';
import { mailReceiveType, mailSendStatus, mailSendType, mailType } from '../../assets/data';
const MailListSearchBar = ({ handleSearch, setResultData }) => {
const [searchData, setSearchData] = useState({
mailTitle: '',
content: '',
sendType: 'ALL',
sendStatus: 'ALL',
mailType: 'ALL',
receiveType: 'ALL',
sendDate: '',
endDate: '',
});
const handleSubmit = event => {
event.preventDefault();
handleSearch(
searchData.mailTitle,
searchData.content,
searchData.sendType ? searchData.sendType : 'ALL',
searchData.sendStatus ? searchData.sendStatus : 'ALL',
searchData.mailType ? searchData.mailType : 'ALL',
searchData.receiveType ? searchData.receiveType : 'ALL',
searchData.sendDate ? searchData.sendDate : '',
searchData.endDate ? searchData.endDate : new Date(),
(searchData.sendDate && searchData.endDate === '') && setSearchData({ sendDate : searchData.sendDate ,endDate : new Date()}),
);
setResultData(searchData);
};
const handleReset = () => {
setSearchData({
mailTitle: '',
content: '',
sendType: 'ALL',
sendStatus: 'ALL',
mailType: 'ALL',
receiveType: 'ALL',
sendDate: '',
endDate: '',
order: 'DESC',
});
handleSearch('', '', 'ALL', 'ALL', 'ALL', 'ALL', '', '');
setResultData('', '', 'ALL', 'ALL', 'ALL', 'ALL', '', '');
window.location.reload();
};
// console.log("searchData.endDate", searchData.endDate)
const searchList = [
<>
<InputLabel>우편 제목</InputLabel>
<TextInput
type="text"
placeholder="우편 제목"
value={searchData.mailTitle}
onChange={e => setSearchData({ ...searchData, mailTitle: e.target.value })}
/>
</>,
<>
<InputLabel>조회 일자</InputLabel>
<SearchPeriod
startDate={searchData.sendDate}
handleStartDate={data => {
setSearchData({ ...searchData, sendDate: data });
}}
endDate={searchData.endDate}
handleEndDate={data => setSearchData({ ...searchData, endDate: data })}
maxDate={new Date()}
/>
</>,
<>
<InputLabel>우편 내용</InputLabel>
<TextInput
type="text"
placeholder="우편 내용(공백으로 구분)"
value={searchData.content}
onChange={e => setSearchData({ ...searchData, content: e.target.value })}
/>
</>
];
const optionList = [
<>
<InputLabel>발송 방식</InputLabel>
<SelectInput value={searchData.sendType} onChange={e => setSearchData({ ...searchData, sendType: e.target.value })}>
{mailSendType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>발송 상태</InputLabel>
<SelectInput value={searchData.sendStatus} onChange={e => setSearchData({ ...searchData, sendStatus: e.target.value })}>
{mailSendStatus.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>우편 타입</InputLabel>
<SelectInput value={searchData.mailType} onChange={e => setSearchData({ ...searchData, mailType: e.target.value })}>
{mailType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>수신 대상</InputLabel>
<SelectInput value={searchData.receiveType} onChange={e => setSearchData({ ...searchData, receiveType: e.target.value })}>
{mailReceiveType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} type="button" />
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
</BtnWrapper>
</>,
];
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
};
export default MailListSearchBar;

View File

@@ -1,349 +0,0 @@
import { useState, useEffect } from 'react';
import { styled } from 'styled-components';
import { Title, BtnWrapper, TextInput, Label, Textarea, InputItem, ModalText, SearchBarAlert } from '../../styles/Components';
import { RepostReplyMessage } from '../../apis/Report';
import Modal from '../common/modal/Modal';
import Button from '../common/button/Button';
import CloseIcon from '../../assets/img/icon/icon-close.png';
const ReportListAnswerModal = ({ answerView, setAnswerView, detailData, replyData, pkId, skId }) => {
const token = sessionStorage.getItem('token');
const [replied, setReplied] = useState(false);
// 버튼 validation -> true 일 때 disable 처리
const [isNullValue, setIsNullValue] = useState(false);
// 발송 API 전달용 resultData 세팅
const [resultData, setResultData] = useState({
pk: '',
sk: '',
title: '',
detail: '',
reporter_nickname: '',
});
// 모달 , 모달 메세지용 state
const [modalStep, setModalStep] = useState(false);
const [modalText, setModalText] = useState('');
const [confirmModal, setConfirmModal] = useState('hidden');
const [completeModal, setCompleteModal] = useState('hidden');
// 답변 조회 DATA 세팅용 resultData
useEffect(() => {
// 답변 조회 api에 호출할 pk, sk 세팅용
const pk = encodeURI(pkId);
const sk = encodeURI(skId);
setResultData({
pk: pkId && pk,
sk: skId && sk,
title: replyData ? replyData.title : '',
detail: replyData ? replyData.detail : '',
reporter_nickname: detailData && detailData.reporter_nickname,
});
// 답변 유무 확인용
replyData ? setReplied(true) : setReplied(false);
}, [detailData, replyData]);
// console.log('신고답변작성 : ', resultData);
// Button Validation용 if 문
const btnVali = resultData.title && resultData.title.length > 0 && resultData.detail && resultData.detail.length > 0;
const titleLeng = resultData.title && resultData.title.length;
const detailLeng = resultData.detail && resultData.detail.length;
// Button Validation (2) - 에러 메세지
const handleNullVali = e => {
if (btnVali) {
setIsNullValue(false);
} else {
setIsNullValue(true);
}
};
// 텍스트 변경 함수
const handleNoticeText = e => {
let btnName = e.target.name;
if (btnName === '취소버튼') {
setModalText('신고 답변을 취소하시겠습니까? \n 취소 시 작성된 모든 정보가 사라집니다.');
setModalStep(false);
} else {
setModalText('신고 답변 우편을 발송하시겠습니까?');
setModalStep(true);
}
};
// 확인 모달
const handleConfirmModal = e => {
if (confirmModal === 'hidden') {
setConfirmModal('view');
} else {
setConfirmModal('hidden');
}
};
// 완료 모달창
const handleCompleteModal = () => {
if (completeModal === 'hidden') {
setCompleteModal('view');
} else {
setCompleteModal('hidden');
window.location.reload();
}
};
// 확인 모달창에서 취소, 확인 버튼 처리
const handleModalStep = async e => {
e.preventDefault();
handleConfirmModal();
if (modalStep) {
await RepostReplyMessage(token, resultData);
setModalText('답변 발송이 완료되었습니다.');
handleCompleteModal();
} else {
setModalText('답변이 취소되었습니다.');
handleCompleteModal();
}
handleReset();
};
// 확인 버튼에서 불러올 초기화 처리
const handleReset = () => {
setAnswerView('hidden');
};
return (
<>
<Modal min="960px" $view={answerView}>
<Title $align="center">{replied ? '신고 답변' : '신고 답변 작성'}</Title>
<Subtitle>[답변 수신자 정보]</Subtitle>
<ReportDetailState>
<table>
<caption></caption>
<tbody>
<tr>
<th width="100px">수신자 GUID</th>
<td>
<TextInput value={detailData.reporter_guid || ''} readOnly />
</td>
<th width="100px">닉네임</th>
<td>
<TextInput value={detailData.reporter_nickname || ''} readOnly />
</td>
</tr>
<tr>
<th>제목</th>
<td colSpan="3">{detailData.title || ''}</td>
</tr>
<tr>
<th width="120">발송 경로</th>
<td>개인 우편함</td>
<th width="120">발송자</th>
<td>GM</td>
</tr>
</tbody>
</table>
</ReportDetailState>
<Subtitle>[답변 내용]</Subtitle>
<AnswerRegistTable>
<tbody>
<tr>
<th width="120">
<Label>제목</Label>
</th>
<td>
<InputItem>
<TextInput
maxLength="30"
name="title"
value={resultData.title || ''}
onInput={e => {
setResultData({ ...resultData, title: e.target.value.trimStart() });
}}
readOnly={replyData}
/>
</InputItem>
{/* 답변 작성시에 글자 수 확인 */}
<ReportNotice $color={titleLeng > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({Number(titleLeng)}/30)</ReportNotice>
</td>
</tr>
<tr>
<th>
<Label>내용</Label>
</th>
<td>
<Textarea
value={resultData.detail || ''}
name="detail"
maxLength="2000"
onInput={e => {
setResultData({ ...resultData, detail: e.target.value.trimStart() });
}}
readOnly={replyData}
/>
{/* 답변 작성시에 글자 수 확인 */}
<ReportNotice $color={detailLeng > 1999 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({Number(detailLeng)}/2000)</ReportNotice>
</td>
</tr>
</tbody>
</AnswerRegistTable>
{isNullValue && (
<SearchBarAlert $align="right" $padding="15px">
필수값을 입력해주세요.
</SearchBarAlert>
)}
<BtnWrapper $justify="flex-end" $gap="10px">
{replyData ? (
<Button text="확인" theme="line" handleClick={e => setAnswerView('hidden')} />
) : (
<>
<Button
text="취소"
theme="line"
name="취소버튼"
handleClick={e => {
handleConfirmModal(e);
handleNoticeText(e);
}}
/>
<Button
text="발송"
name="발송버튼"
// errorMessage={btnValidation}
theme={btnVali ? 'primary' : 'disable'}
handleClick={e => {
{
!btnVali ? handleNullVali(e) : handleConfirmModal(e);
handleNoticeText(e);
handleNullVali(e);
}
}}
/>
</>
)}
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmModal} />
</BtnWrapper>
<ModalText $align="center">{modalText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleConfirmModal} />
<Button
text="확인"
theme="primary"
type="submit"
size="large"
width="100%"
handleClick={e => {
handleModalStep(e);
}}
/>
</BtnWrapper>
</Modal>
{/* 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModal} />
</BtnWrapper>
<ModalText $align="center">{modalText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
</BtnWrapper>
</Modal>
</>
);
};
export default ReportListAnswerModal;
const ReportDetailState = styled.div`
border-top: 1px solid #000;
border-bottom: 1px solid #000;
padding: 15px 10px;
font-size: 14px;
margin-bottom: 20px;
input {
height: 35px;
max-width: 330px;
font-size: 14px;
padding: 5px 10px;
}
table {
tr {
th,
td {
padding: 5px;
height: 45px;
vertical-align: middle;
}
}
}
`;
const Subtitle = styled.div`
font-size: 16px;
font-weight: 500;
line-height: 1.6;
`;
const AnswerRegistTable = styled.table`
border-top: 1px solid #999;
margin-bottom: 20px;
th,
td {
padding: 15px 0;
border-bottom: 1px solid #f6f6f6;
}
tr:last-child {
th,
td {
padding: 15px 0;
border-bottom: 1px solid #999;
}
}
th {
vertical-align: top;
line-height: 30px;
}
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 ReportNotice = styled.span`
font-size: 12px;
font-weight: 300;
color: ${props => props.$color || '#999'};
margin-top: 10px;
display: block;
`;
const ButtonClose = styled.button`
width: 16px;
height: 16px;
background: url(${CloseIcon}) 50% 50% no-repeat;
opacity: ${props => props.opacity};
`;

View File

@@ -1,221 +0,0 @@
import { styled } from 'styled-components';
import { Title, BtnWrapper, TextInput, Label, Textarea, InputItem } from '../../styles/Components';
import Modal from '../common/modal/Modal';
import Button from '../common/button/Button';
import { useEffect, useState } from 'react';
import { convertKTC } from '../../utils';
const ReportListDetailModal = ({ detailView, handleDetailView, handleReply, detailData, replyData, replyAuth }) => {
const [dataList, setDataList] = useState([]);
// UTC + 9 처리 해줄 변수
const RESOLVE_TIME = detailData && new Date(detailData.resolution_time);
const REPORT_TIME = detailData && new Date(detailData.create_time);
// 신고 유형 데이터 매핑
const report_type = [
{ value: 'ALL', name: '전체' },
{ value: 'UNMANNERED_ACT', name: '비매너 행위' },
{ value: 'USE_UNHEALTHY_NAMES', name: '불건전 이름 사용' },
{ value: 'CASH_TRADING', name: '현금거래 행위' },
{ value: 'INTERFERENCE_GAME', name: '게임 진행 방해' },
{ value: 'INTERFERENCE_SERVICE', name: '운영서비스 방해' },
{ value: 'ACCOUNT_EXPLOITATION', name: '계정도용' },
{ value: 'BUG_ABUSING', name: '버그/어뷰징' },
{ value: 'USE_HACK', name: '불법프로그램 사용' },
{ value: 'LEAK_PERSONAL_INFO', name: '개인정보 유출' },
{ value: 'PRETENDING_GM', name: '운영자 사칭' },
];
// console.log(detailData);
useEffect(() => {
setDataList({
create_time: detailData && convertKTC(REPORT_TIME, false),
detail: detailData && detailData.detail,
manager_email: replyData && replyData.manager_email,
report_type: detailData && detailData.report_type,
reporter_guid: detailData && detailData.reporter_guid,
reporter_nickname: detailData && detailData.reporter_nickname,
resolution_time: detailData && detailData.resolution_time,
state: detailData && detailData.state,
target_guid: detailData && detailData.target_guid,
target_nickname: detailData && detailData.target_nickname,
title: detailData && detailData.title,
});
}, [detailData]);
// console.log('모달창에서 리포트 상세 정보 : ', dataList);
return (
<>
<Modal min="960px" $view={detailView}>
<Title $align="center">신고내역 상세 정보</Title>
{/* RegistInfo는 답변 완료시에만 보여집니다 */}
{dataList && dataList.resolution_time && (
<RegistInfo>
<span>등록자(이메일주소) : {dataList && dataList.manager_email}</span>
<span>등록일 : {dataList && convertKTC(RESOLVE_TIME)}</span>
</RegistInfo>
)}
<Subtitle>[신고 대상 정보]</Subtitle>
<ReportDetailState>
<table>
<caption></caption>
<tbody>
<tr>
<th width="120">신고 일자</th>
<td>{dataList.create_time}</td>
<th width="120">신고 유형</th>
<td>{report_type.map(data => data.value === (dataList && dataList.report_type) && data.name)}</td>
</tr>
<tr>
<th>처리상태</th>
<td colSpan="3">{dataList && dataList.state === 'RESOLVED' ? <ReportState results="solved">해결</ReportState> : <ReportState results="remain"></ReportState>}</td>
</tr>
<tr>
<th width="100px">신고자 GUID</th>
<td>
<TextInput value={(dataList && dataList.reporter_guid) || ''} readOnly />
</td>
<th width="100px">닉네임</th>
<td>
<TextInput value={(dataList && dataList.reporter_nickname) || ''} readOnly />
</td>
</tr>
<tr>
<th width="100px">신고대상 GUID</th>
<td>
<TextInput value={(dataList && dataList.target_guid) || ''} readOnly />
</td>
<th width="100px">닉네임</th>
<td>
<TextInput value={(dataList && dataList.target_nickname) || ''} readOnly />
</td>
</tr>
</tbody>
</table>
</ReportDetailState>
<Subtitle>[신고내용]</Subtitle>
<AnswerRegistTable>
<tbody>
<tr>
<th width="120">
<Label>제목</Label>
</th>
<td>
<InputItem>
<TextInput maxLength="30" value={(dataList && dataList.title) || ''} readOnly />
</InputItem>
</td>
</tr>
<tr>
<th>
<Label>내용</Label>
</th>
<td>
<Textarea value={(dataList && dataList.detail) || ''} readOnly></Textarea>
</td>
</tr>
</tbody>
</AnswerRegistTable>
<BtnWrapper $justify="flex-end" $gap="10px">
<Button text="확인" theme="line" handleClick={() => handleDetailView()} />
{
// 답변이 있을 때
dataList && dataList.resolution_time ? (
<Button text="답변보기" theme="line" handleClick={() => handleReply()} />
) : // 답변이 없고, 답변 권한이 있을 때
dataList && !dataList.resolution_time && replyAuth ? (
<Button text="답변하기" theme="primary" handleClick={() => handleReply()} />
) : (
// 답변이 없고, 답변 권한도 없을 때
<></>
)
}
</BtnWrapper>
</Modal>
</>
);
};
export default ReportListDetailModal;
const ReportDetailState = styled.div`
border-top: 1px solid #000;
border-bottom: 1px solid #000;
padding: 15px 10px;
font-size: 14px;
margin-bottom: 20px;
input {
height: 35px;
max-width: 330px;
font-size: 14px;
padding: 5px 10px;
}
table {
tr {
th,
td {
padding: 5px;
height: 45px;
vertical-align: middle;
}
}
}
`;
const ReportState = styled.span`
font-weight: 600;
color: ${props => (props.results === 'solved' ? '#08994B' : props.results === 'remain' ? '#ff0000' : '#2c2c2c')};
`;
const RegistInfo = styled.div`
display: flex;
justify-content: flex-end;
gap: 50px;
font-size: 14px;
margin-bottom: 10px;
`;
const Subtitle = styled.div`
font-size: 16px;
font-weight: 500;
line-height: 1.6;
`;
const AnswerRegistTable = styled.table`
border-top: 1px solid #999;
margin-bottom: 20px;
th,
td {
padding: 15px 0;
border-bottom: 1px solid #f6f6f6;
}
tr:last-child {
th,
td {
padding: 15px 0;
border-bottom: 1px solid #999;
}
}
th {
vertical-align: top;
line-height: 30px;
}
td {
${TextInput} {
max-width: 600px;
}
${Textarea} {
width: 100%;
border: 1px solid #d9d9d9;
border-radius: 5px;
height: 150px;
padding: 15px;
&:focus {
border: 1px solid #2c2c2c;
}
}
}
`;

View File

@@ -1,142 +0,0 @@
import { styled } from 'styled-components';
import { TextInput, BtnWrapper, InputLabel, SelectInput } from '../../styles/Components';
import Button from '../common/button/Button';
import { SearchBarLayout, SearchPeriod } from '../common/SearchBar';
import { useState } from 'react';
const ReportListSearchBar = ({ handleSearch, setResultData }) => {
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const reportType = [
{ value: 'ALL', name: '전체' },
{ value: 'UNMANNERED_ACT', name: '비매너 행위' },
{ value: 'USE_UNHEALTHY_NAMES', name: '불건전 이름 사용' },
{ value: 'CASH_TRADING', name: '현금거래 행위' },
{ value: 'INTERFERENCE_GAME', name: '게임 진행 방해' },
{ value: 'INTERFERENCE_SERVICE', name: '운영서비스 방해' },
{ value: 'ACCOUNT_EXPLOITATION', name: '계정도용' },
{ value: 'BUG_ABUSING', name: '버그/어뷰징' },
{ value: 'USE_HACK', name: '불법프로그램 사용' },
{ value: 'LEAK_PERSONAL_INFO', name: '개인정보 유출' },
{ value: 'PRETENDING_GM', name: '운영자 사칭' },
];
const reportState = [
{ value: 'ALL', name: '전체' },
{ value: 'RESOLVED', name: '해결' },
{ value: 'UNRESOLVED', name: '미해결' },
];
const searchType = [
{ value: 'ALL', name: '전체' },
{ value: 'GUID', name: '신고자' },
{ value: 'EMAIL', name: '담당자' },
];
const [searchData, setSearchData] = useState({
startDate: START_DATE,
endDate: END_DATE,
reportType: 'ALL',
status: 'ALL',
searchType: 'ALL',
searchKey: '',
});
// console.log(searchData);
const handleSubmit = event => {
event.preventDefault();
handleSearch(
searchData.startDate ? new Date(searchData.startDate).toISOString().split('.')[0] : new Date(START_DATE).toISOString().split('.')[0],
searchData.endDate ? new Date(searchData.endDate).toISOString().split('.')[0] : new Date(END_DATE).toISOString().split('.')[0],
searchData.reportType ? searchData.reportType : 'ALL',
searchData.status ? searchData.status : 'ALL',
searchData.searchType ? searchData.searchType : 'ALL',
searchData.searchKey ? searchData.searchKey : '',
);
setResultData(searchData);
};
const handleReset = () => {
setSearchData({
startDate: START_DATE,
endDate: END_DATE,
reportType: 'ALL',
status: 'ALL',
searchKey: '',
order: 'DESC',
});
handleSearch(START_DATE, END_DATE, 'ALL', 'ALL', 'ALL', '');
setResultData(START_DATE, END_DATE, 'ALL', 'ALL', 'ALL', '');
window.location.reload();
};
const searchList = [
<>
<InputLabel>신고 일자</InputLabel>
<SearchPeriod
startDate={searchData.startDate}
handleStartDate={data => {
setSearchData({ ...searchData, startDate: data });
}}
endDate={searchData.endDate}
handleEndDate={data => setSearchData({ ...searchData, endDate: data })}
maxDate={new Date()}
/>
</>,
];
const optionList = [
<>
<InputLabel>신고 유형</InputLabel>
<SelectInput value={searchData.reportType || ''} onChange={e => setSearchData({ ...searchData, reportType: e.target.value })}>
{reportType.map((data, index) => (
<option key={index} value={data.value || ''}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>상태</InputLabel>
<SelectInput value={searchData.status || ''} onChange={e => setSearchData({ ...searchData, status: e.target.value })}>
{reportState.map((data, index) => (
<option key={index} value={data.value || ''}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>신고자 / 담당자</InputLabel>
<InputGroup>
<SelectInput value={searchData.searchType || ''} onChange={e => setSearchData({ ...searchData, searchType: e.target.value })}>
{searchType.map((data, index) => (
<option key={index} value={data.value || ''}>
{data.name}
</option>
))}
</SelectInput>
<TextInput placeholder="입력" onChange={e => setSearchData({ ...searchData, searchKey: e.target.value })} />
</InputGroup>
</>,
<>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} type="button" />
<Button theme="search" text="검색" handleClick={handleSubmit} type="submit" />
</BtnWrapper>
</>,
];
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
};
export default ReportListSearchBar;
const InputGroup = styled.div`
display: flex;
align-items: center;
gap: 5px;
`;

View File

@@ -1,206 +0,0 @@
import { Fragment, useState } from 'react';
import { styled } from 'styled-components';
import { Title, TableStyle, BtnWrapper, TextInput } from '../../styles/Components';
import Modal from '../../components/common/modal/Modal';
import Button from '../common/button/Button';
import { convertKTC } from '../../utils';
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: '영구정지' },
];
return (
<>
<Modal min="960px" $view={stateModal}>
<Title $align="center">이용자 제재 상세 정보</Title>
<RegistInfo>
<span>등록자 : {data.create_by}</span>
<span>등록일 : {convertKTC(data.create_dt, false)}</span>
</RegistInfo>
<BlockDetailState>
<table>
<caption></caption>
<tbody>
<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>
<TextInput value={type.map(item => item.value === data.type && item.name).filter(data => data !== false) || ''} disabled />
</td>
<th></th>
<td></td>
</tr>
<tr>
<th>제재 기간</th>
<td colSpan="3">
<InputWrapper>
<TextInput value={period.map(item => item.value === data.period && item.name).filter(data => data !== false) || ''} disabled />
<span>시작일 : {convertKTC(data.start_dt, false)}</span>
<span>종료일 : {convertKTC(data.end_dt, false)}</span>
</InputWrapper>
</td>
</tr>
<tr>
<th>제재 사유</th>
<td>
<TextInput value={sanctions.map(item => item.value === data.sanctions && item.name).filter(data => data !== false) || ''} disabled />
</td>
</tr>
</tbody>
</table>
</BlockDetailState>
<BlockCount>전체 제재 이력({history && history.length})</BlockCount>
<BlockHistory>
<TableStyle>
<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) => (
<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>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</BlockHistory>
<BlockNotice> 삭제된 제재 이력은 표시되지 않습니다.</BlockNotice>
<BtnWrapper $justify="flex-end">
<Button text="확인" theme="line" handleClick={handleModal} />
</BtnWrapper>
</Modal>
</>
);
};
export default UserBlockDetailModal;
const BlockDetailState = styled.div`
border-top: 1px solid #000;
border-bottom: 1px solid #000;
padding: 15px 10px;
font-size: 14px;
margin-bottom: 20px;
input {
height: 35px;
max-width: 330px;
font-size: 14px;
padding: 5px 10px;
}
table {
tr {
th,
td {
padding: 5px;
}
}
}
`;
const RegistInfo = styled.div`
display: flex;
justify-content: flex-end;
gap: 50px;
font-size: 14px;
margin-bottom: 10px;
`;
const BlockCount = styled.div`
font-size: 16px;
font-weight: 500;
line-height: 1.6;
padding-bottom: 5px;
`;
const BlockHistory = styled.div`
max-height: 324px;
position: relative;
overflow: auto;
border-top: 1px solid #000;
${TableStyle} {
border-collapse: separate;
&:before {
display: none;
}
th {
position: sticky;
top: 0;
}
tbody {
tr:first-child {
color: #d60000;
}
tr:last-child td {
border-bottom: 1px solid #e8eaec;
}
}
}
`;
const BlockNotice = styled.span`
font-size: 12px;
color: #686868;
padding-left: 10px;
font-weight: 300;
line-height: 2;
`;
const InputWrapper = styled.div`
display: flex;
gap: 20px;
align-items: center;
`;

View File

@@ -1,117 +0,0 @@
import { useState } from 'react';
import { TextInput, BtnWrapper, InputLabel, SelectInput, InputGroup } from '../../styles/Components';
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';
const UserBlockSearchBar = ({ handleSearch, setResultData }) => {
const [searchData, setSearchData] = useState({
searchType: 'GUID',
data: '',
email: '',
status: 'ALL',
sanctions: 'ALL',
period: 'ALL',
});
const handleSubmit = event => {
event.preventDefault();
handleSearch(
searchData.searchType ? searchData.searchType : 'GUID',
searchData.data,
searchData.email,
searchData.status ? searchData.status : 'ALL',
searchData.sanctions ? searchData.sanctions : 'ALL',
searchData.period ? searchData.period : 'ALL',
);
setResultData(searchData);
};
// 초기화 버튼
const handleReset = () => {
setSearchData({
searchType: 'GUID',
data: '',
email: '',
status: 'ALL',
sanctions: 'ALL',
period: 'ALL',
});
handleSearch('GUID', '', '', 'ALL', 'ALL', 'ALL');
setResultData('GUID', '', '', 'ALL', 'ALL', 'ALL');
window.location.reload();
};
// console.log(searchData);
const searchList = [
<>
<InputLabel>대상</InputLabel>
<InputGroup>
<SelectInput value={searchData.searchType} onChange={e => setSearchData({ ...searchData, searchType: e.target.value })}>
{userSearchType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput
type="text"
placeholder={searchData.searchType === 'GUID' ? 'GUID 입력' : '닉네임 입력'}
value={searchData.data}
width="600px"
onChange={e => setSearchData({ ...searchData, data: e.target.value })}
/>
</InputGroup>
</>,
<>
<InputLabel>등록자</InputLabel>
<TextInput type="text" placeholder="이메일 입력" width="600px" value={searchData.email} onChange={e => setSearchData({ ...searchData, email: e.target.value })} />
</>,
];
const optionList = [
<>
<InputLabel>상태</InputLabel>
<SelectInput value={searchData.status} onChange={e => setSearchData({ ...searchData, status: e.target.value })}>
{blockStatus.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>제재 사유</InputLabel>
<SelectInput value={searchData.sanctions} onChange={e => setSearchData({ ...searchData, sanctions: e.target.value })}>
{blockSanctions.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<InputLabel>제재 기간</InputLabel>
<SelectInput value={searchData.period} onChange={e => setSearchData({ ...searchData, period: e.target.value })}>
{blockPeriod.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
</>,
<>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} />
<Button theme="search" text="검색" type="submit" handleClick={handleSubmit} />
</BtnWrapper>
</>,
];
return <SearchBarLayout firstColumnData={searchList} secondColumnData={optionList} direction={'column'} />;
};
export default UserBlockSearchBar;

View File

@@ -32,7 +32,8 @@ import {
} from '../../../assets/data/adminConstants';
import { landAuctionStatus, landAuctionStatusType, languageType, CurrencyType } from '../../../assets/data';
import { useModal } from '../../../utils/hook';
import { convertKTCDate, msToMinutes } from '../../../utils';
import { convertKTCDate } from '../../../utils';
import { msToMinutes } from '../../../utils/date';
const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, setDetailData, landData, buildingData }) => {
const { t } = useTranslation();

View File

@@ -1,97 +0,0 @@
import DatePicker, { registerLocale } from 'react-datepicker';
import { ko } from 'date-fns/esm/locale';
import { getMonth, getYear } from 'date-fns';
import range from 'lodash/range';
import { months } from '../../assets/data';
registerLocale('ko', ko);
const DatePickerComponent = ({ selectedDate, handleSelectedDate, pastDate, disabled, name, readOnly, maxDate, type }) => {
const years = range(1990, getYear(new Date()) + 100, 1);
const parseDate = (date) => {
if (!date) return null;
try {
// 이미 Date 객체인 경우
if (date instanceof Date) {
return date;
}
// 문자열인 경우
if (typeof date === 'string') {
// ISO 형식의 날짜 문자열인 경우 (예: "2025-01-03T15:00:00")
if (date.includes('T')) {
return new Date(date);
}
// 일반 날짜 문자열인 경우 (예: "2025-01-03")
const [year, month, day] = date.split('-').map(num => num.trim());
return new Date(year, month - 1, day);
}
return null;
} catch (error) {
console.error('Date parsing error:', error);
return null;
}
};
const parsedDate = parseDate(selectedDate);
return (
<DatePicker
onKeyDown={event => {
if (event.keyCode === 8) {
event.preventDefault();
}
}}
disabled={disabled}
readOnly={readOnly}
// selected={type !== 'retention' ? '' : parsedDate}
selected={parsedDate}
onChange={data => handleSelectedDate(data)}
className="datepicker"
placeholderText={name ? name : '검색기간 선택'}
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
locale="ko"
minDate={pastDate}
maxDate={maxDate}
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
);
};
export default DatePickerComponent;

View File

@@ -1,171 +0,0 @@
import React, { useState, useEffect } from 'react';
import DatePickerComponent from './DatePickerComponent';
import { DatePickerWrapper } from '../../styles/Components';
import {
FormRowGroup,
FormLabel,
DateTimeGroup, DateTimeWrapper, DateContainer, StyledSelectInput, TimeSeparator, TimeContainer,
} from '../../styles/ModuleComponents';
import { HourList, MinuteList } from '../../assets/data';
import { useTranslation } from 'react-i18next';
const DateTimeRangePicker = ({
label,
startDate,
endDate,
onStartDateChange,
onEndDateChange,
pastDate = new Date(),
disabled,
startLabel = '시작 일자',
endLabel = '종료 일자',
reset = false,
setAlert
}) => {
const { t } = useTranslation();
const [startHour, setStartHour] = useState('00');
const [startMin, setStartMin] = useState('00');
const [endHour, setEndHour] = useState('00');
const [endMin, setEndMin] = useState('00');
useEffect(() => {
if (startDate) {
const date = new Date(startDate);
setStartHour(String(date.getHours()).padStart(2, '0'));
setStartMin(String(date.getMinutes()).padStart(2, '0'));
}
}, [startDate]);
useEffect(() => {
if (endDate) {
const date = new Date(endDate);
setEndHour(String(date.getHours()).padStart(2, '0'));
setEndMin(String(date.getMinutes()).padStart(2, '0'));
}
}, [endDate]);
useEffect(() => {
if (reset) {
setStartHour('00');
setStartMin('00');
setEndHour('00');
setEndMin('00');
}
}, [reset]);
const handleStartDate = (date) => {
const newDate = new Date(date);
newDate.setHours(parseInt(startHour), parseInt(startMin));
onStartDateChange(newDate);
};
const handleEndDate = (date) => {
let newDate = new Date(date);
newDate.setHours(parseInt(endHour), parseInt(endMin));
if (startDate && newDate < startDate) {
setAlert(t('TIME_START_DIFF_END'));
newDate = new Date(startDate);
}
onEndDateChange(newDate);
};
const handleStartTime = (e) => {
const { id, value } = e.target;
const newDate = startDate ? new Date(startDate) : new Date();
if (id === 'hour') {
setStartHour(value);
newDate.setHours(parseInt(value), parseInt(startMin));
} else {
setStartMin(value);
newDate.setHours(parseInt(startHour), parseInt(value));
}
onStartDateChange(newDate);
};
const handleEndTime = (e) => {
const { id, value } = e.target;
let newDate = endDate ? new Date(endDate) : new Date();
if (id === 'hour') {
setEndHour(value);
newDate.setHours(parseInt(value), parseInt(endMin));
} else {
setEndMin(value);
newDate.setHours(parseInt(endHour), parseInt(value));
}
if (startDate && newDate < startDate) {
setAlert(t('TIME_START_DIFF_END'));
newDate = new Date(startDate)
}
onEndDateChange(newDate);
};
return (
<FormRowGroup>
<FormLabel>{label}</FormLabel>
<DateTimeWrapper>
<DateTimeGroup>
<DateContainer>
<DatePickerWrapper>
<DatePickerComponent
name={startLabel}
handleSelectedDate={handleStartDate}
selectedDate={startDate}
pastDate={pastDate}
disabled={disabled}
/>
</DatePickerWrapper>
</DateContainer>
<TimeContainer>
<StyledSelectInput onChange={handleStartTime} id="hour" value={startHour} disabled={disabled}>
{HourList.map(hour => (
<option value={hour} key={hour}>{hour}</option>
))}
</StyledSelectInput>
<TimeSeparator>:</TimeSeparator>
<StyledSelectInput onChange={handleStartTime} id="min" value={startMin} disabled={disabled}>
{MinuteList.map(min => (
<option value={min} key={min}>{min}</option>
))}
</StyledSelectInput>
</TimeContainer>
</DateTimeGroup>
<DateTimeGroup>
<DateContainer>
<DatePickerWrapper>
<DatePickerComponent
name={endLabel}
handleSelectedDate={handleEndDate}
selectedDate={endDate}
pastDate={pastDate}
disabled={disabled}
/>
</DatePickerWrapper>
</DateContainer>
<TimeContainer>
<StyledSelectInput onChange={handleEndTime} id="hour" value={endHour} disabled={disabled}>
{HourList.map(hour => (
<option value={hour} key={hour}>{hour}</option>
))}
</StyledSelectInput>
<TimeSeparator>:</TimeSeparator>
<StyledSelectInput onChange={handleEndTime} id="min" value={endMin} disabled={disabled}>
{MinuteList.map(min => (
<option value={min} key={min}>{min}</option>
))}
</StyledSelectInput>
</TimeContainer>
</DateTimeGroup>
</DateTimeWrapper>
</FormRowGroup>
);
};
export default DateTimeRangePicker;