모달 스크롤 추가
detailGrid 수정 전투이벤트, 랜드경매, 이벤트, 메일 상세 수정 랜드경매 예약종료일 제거
This commit is contained in:
@@ -1,131 +0,0 @@
|
||||
{
|
||||
"baseUrl": "/api/v1/users",
|
||||
"endpoints": {
|
||||
"UserView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/find-users",
|
||||
"dataPath": "data.data.result",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["search_type", "search_key"]
|
||||
},
|
||||
"UserInfoView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/basicinfo",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserChangeNickName": {
|
||||
"method": "PUT",
|
||||
"url": "/api/v1/users/change-nickname",
|
||||
"dataPath": null,
|
||||
"paramFormat": "body",
|
||||
"paramMapping": ["guid", "nickname"]
|
||||
},
|
||||
"UserChangeAdminLevel": {
|
||||
"method": "PUT",
|
||||
"url": "/api/v1/users/change-level",
|
||||
"dataPath": null,
|
||||
"paramFormat": "body",
|
||||
"paramMapping": ["guid", "level"]
|
||||
},
|
||||
"UserKick": {
|
||||
"method": "PUT",
|
||||
"url": "/api/v1/users/user-kick",
|
||||
"dataPath": "data",
|
||||
"paramFormat": "body",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserAvatarView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/avatarinfo",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserClothView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/clothinfo",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserToolView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/toolslot",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserInventoryView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/inventory",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserInventoryItemDelete": {
|
||||
"method": "DELETE",
|
||||
"url": "/api/v1/users/inventory/delete/item",
|
||||
"dataPath": "data",
|
||||
"paramFormat": "body",
|
||||
"paramMapping": ["guid", "inventory_id"]
|
||||
},
|
||||
"UserTattooView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/tattoo",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserQuestView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/quest",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserFriendListView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/friendlist",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
},
|
||||
"UserMailView": {
|
||||
"method": "POST",
|
||||
"url": "/api/v1/users/mail",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "body",
|
||||
"paramMapping": ["guid", "page", "limit"]
|
||||
},
|
||||
"UserMailDelete": {
|
||||
"method": "DELETE",
|
||||
"url": "/api/v1/users/mail/delete",
|
||||
"dataPath": "data",
|
||||
"paramFormat": "body",
|
||||
"paramMapping": ["mail_id"]
|
||||
},
|
||||
"UserMailItemDelete": {
|
||||
"method": "DELETE",
|
||||
"url": "/api/v1/users/mail/delete/item",
|
||||
"dataPath": "data",
|
||||
"paramFormat": "body",
|
||||
"paramMapping": ["mail_id", "item_id"]
|
||||
},
|
||||
"UserMailDetailView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/mail/:id",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "path",
|
||||
"paramMapping": ["id"]
|
||||
},
|
||||
"UserMyhomeView": {
|
||||
"method": "GET",
|
||||
"url": "/api/v1/users/myhome",
|
||||
"dataPath": "data.data",
|
||||
"paramFormat": "query",
|
||||
"paramMapping": ["guid"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import styled from 'styled-components';
|
||||
import dayjs from 'dayjs';
|
||||
import { AnimatedTabs } from '../index';
|
||||
const { RangePicker } = DatePicker;
|
||||
const { TextArea } = Input;
|
||||
|
||||
/**
|
||||
* 위치 지정 가능한 그리드 형태 상세 정보 표시 컴포넌트
|
||||
@@ -51,12 +52,15 @@ const DetailGrid = ({ items, formData, onChange, disabled = false, columns = 4 }
|
||||
handler,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
format,
|
||||
required,
|
||||
showTime,
|
||||
tabItems,
|
||||
activeKey,
|
||||
onTabChange
|
||||
onTabChange,
|
||||
maxLength,
|
||||
rows: textareaRows
|
||||
} = item;
|
||||
|
||||
// 현재 값 가져오기 (formData에서 또는 항목에서)
|
||||
@@ -85,10 +89,35 @@ const DetailGrid = ({ items, formData, onChange, disabled = false, columns = 4 }
|
||||
value={currentValue}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step || 1}
|
||||
onChange={(value) => onChange(key, value, handler)}
|
||||
placeholder={placeholder || `${label} 입력`}
|
||||
/>;
|
||||
|
||||
case 'display':
|
||||
return <Input
|
||||
{...commonProps}
|
||||
value={currentValue || ''}
|
||||
readOnly
|
||||
style={{
|
||||
...commonProps.style,
|
||||
backgroundColor: '#f5f5f5',
|
||||
cursor: 'default'
|
||||
}}
|
||||
placeholder={placeholder || ''}
|
||||
/>;
|
||||
|
||||
case 'textarea':
|
||||
return <TextArea
|
||||
{...commonProps}
|
||||
value={currentValue || ''}
|
||||
onChange={(e) => onChange(key, e.target.value, handler)}
|
||||
placeholder={placeholder}
|
||||
maxLength={maxLength}
|
||||
rows={textareaRows || 4}
|
||||
showCount={!!maxLength}
|
||||
/>;
|
||||
|
||||
case 'select':
|
||||
return (
|
||||
<Select
|
||||
@@ -99,7 +128,7 @@ const DetailGrid = ({ items, formData, onChange, disabled = false, columns = 4 }
|
||||
>
|
||||
{options && options.map((option) => (
|
||||
<Select.Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
{option.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
@@ -131,12 +160,25 @@ const DetailGrid = ({ items, formData, onChange, disabled = false, columns = 4 }
|
||||
currentValue.end ? dayjs(currentValue.end) : null
|
||||
] : null)}
|
||||
format={format || 'YYYY-MM-DD HH:mm:ss'}
|
||||
onChange={(dates, dateStrings) => {
|
||||
if (dates) {
|
||||
onChange={(dates) => {
|
||||
if (dates && dates.length === 2) {
|
||||
// 두 개의 별도 필드에 각각 업데이트
|
||||
if (item.keys) {
|
||||
onChange(item.keys.start, dates[0], handler);
|
||||
onChange(item.keys.end, dates[1], handler);
|
||||
// 두 개의 onChange를 순차적으로 호출하는 대신
|
||||
// 한 번에 두 필드를 모두 업데이트하는 방식으로 변경
|
||||
const updatedData = {
|
||||
...formData,
|
||||
[item.keys.start]: dates[0],
|
||||
[item.keys.end]: dates[1]
|
||||
};
|
||||
|
||||
// handler가 있으면 handler 실행, 없으면 직접 onChange 호출
|
||||
if (handler) {
|
||||
handler(dates, key, updatedData);
|
||||
} else {
|
||||
// onChange를 통해 전체 업데이트된 데이터를 전달
|
||||
onChange('dateRange_update', updatedData, null);
|
||||
}
|
||||
} else {
|
||||
// 기존 방식 지원 (하위 호환성)
|
||||
onChange(key, {
|
||||
@@ -147,8 +189,17 @@ const DetailGrid = ({ items, formData, onChange, disabled = false, columns = 4 }
|
||||
} else {
|
||||
// 두 필드 모두 비우기
|
||||
if (item.keys) {
|
||||
onChange(item.keys.start, null, handler);
|
||||
onChange(item.keys.end, null, handler);
|
||||
const updatedData = {
|
||||
...formData,
|
||||
[item.keys.start]: null,
|
||||
[item.keys.end]: null
|
||||
};
|
||||
|
||||
if (handler) {
|
||||
handler(null, key, updatedData);
|
||||
} else {
|
||||
onChange('dateRange_update', updatedData, null);
|
||||
}
|
||||
} else {
|
||||
onChange(key, { start: null, end: null }, handler);
|
||||
}
|
||||
@@ -204,8 +255,17 @@ const DetailGrid = ({ items, formData, onChange, disabled = false, columns = 4 }
|
||||
case 'custom':
|
||||
return item.render ? item.render(formData, onChange) : null;
|
||||
|
||||
case 'label':
|
||||
default:
|
||||
return <div>{currentValue}</div>;
|
||||
return <div style={{
|
||||
padding: '4px 11px',
|
||||
minHeight: '32px',
|
||||
lineHeight: '24px',
|
||||
fontSize: '15px',
|
||||
color: currentValue ? '#000' : '#bfbfbf'
|
||||
}}>
|
||||
{currentValue || placeholder || ''}
|
||||
</div>;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,24 +22,36 @@ const DetailLayout = ({
|
||||
}) => {
|
||||
// 값 변경 핸들러
|
||||
const handleChange = (key, value, handler) => {
|
||||
// 핸들러가 있으면 핸들러 실행
|
||||
if (handler) {
|
||||
handler(value, key, formData);
|
||||
}
|
||||
let updatedFormData = { ...formData };
|
||||
|
||||
// dateRange 전용 업데이트 처리
|
||||
if (key === 'dateRange_update') {
|
||||
updatedFormData = value; // value가 이미 완전히 업데이트된 객체
|
||||
}
|
||||
// 키가 점 표기법이면 중첩 객체 업데이트
|
||||
if (key.includes('.')) {
|
||||
else if (key.includes('.')) {
|
||||
const [parentKey, childKey] = key.split('.');
|
||||
onChange({
|
||||
updatedFormData = {
|
||||
...formData,
|
||||
[parentKey]: {
|
||||
...formData[parentKey],
|
||||
[childKey]: value
|
||||
}
|
||||
});
|
||||
};
|
||||
} else {
|
||||
// 일반 키는 직접 업데이트
|
||||
onChange({ ...formData, [key]: value });
|
||||
updatedFormData = {
|
||||
...formData,
|
||||
[key]: value
|
||||
};
|
||||
}
|
||||
|
||||
// 핸들러가 있으면 핸들러 실행 (업데이트된 데이터를 전달)
|
||||
if (handler) {
|
||||
handler(value, key, updatedFormData);
|
||||
} else {
|
||||
// 핸들러가 없으면 직접 onChange 호출
|
||||
onChange(updatedFormData);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import * as XLSX from 'xlsx-js-style';
|
||||
import { ExcelDownButton } from '../../../styles/ModuleComponents';
|
||||
|
||||
const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName = 'Sheet1' }) => {
|
||||
const isNumeric = (value) => {
|
||||
// 숫자 또는 숫자 문자열인지 확인
|
||||
return !isNaN(value) && !isNaN(parseFloat(value));
|
||||
};
|
||||
|
||||
const downloadExcel = () => {
|
||||
try {
|
||||
if (!tableRef.current) return;
|
||||
|
||||
const tableElement = tableRef.current;
|
||||
const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
|
||||
const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
||||
|
||||
// 헤더 데이터 추출
|
||||
const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);
|
||||
|
||||
// 바디 데이터 추출 및 숫자 타입 처리
|
||||
const bodyData = Array.from(bodyRows).map(row =>
|
||||
Array.from(row.cells).map(cell => {
|
||||
const value = cell.textContent;
|
||||
return isNumeric(value) ? parseFloat(value) : value;
|
||||
})
|
||||
);
|
||||
|
||||
// 워크북 생성
|
||||
const wb = XLSX.utils.book_new();
|
||||
|
||||
// 테두리 스타일 정의
|
||||
const borderStyle = {
|
||||
style: "thin",
|
||||
color: { rgb: "000000" }
|
||||
};
|
||||
|
||||
// 스타일 정의
|
||||
const centerStyle = {
|
||||
font: {
|
||||
name: "맑은 고딕",
|
||||
sz: 11
|
||||
},
|
||||
alignment: {
|
||||
horizontal: 'right',
|
||||
vertical: 'right'
|
||||
},
|
||||
border: {
|
||||
top: borderStyle,
|
||||
bottom: borderStyle,
|
||||
left: borderStyle,
|
||||
right: borderStyle
|
||||
}
|
||||
};
|
||||
|
||||
const headerStyle = {
|
||||
alignment: {
|
||||
horizontal: 'center',
|
||||
vertical: 'center'
|
||||
},
|
||||
fill: {
|
||||
fgColor: { rgb: "d9e1f2" },
|
||||
patternType: "solid"
|
||||
}
|
||||
};
|
||||
|
||||
// 데이터에 스타일 적용
|
||||
const wsData = [
|
||||
// 헤더 행
|
||||
headers.map(h => ({
|
||||
v: h,
|
||||
s: headerStyle
|
||||
})),
|
||||
// 데이터 행들
|
||||
...bodyData.map(row =>
|
||||
row.map(cell => ({
|
||||
v: cell,
|
||||
s: centerStyle
|
||||
}))
|
||||
)
|
||||
];
|
||||
|
||||
// 워크시트 생성
|
||||
const ws = XLSX.utils.aoa_to_sheet(wsData);
|
||||
|
||||
// 열 너비 설정 (최소 8, 최대 50)
|
||||
ws['!cols'] = headers.map((_, index) => {
|
||||
const maxLength = Math.max(
|
||||
headers[index].length * 2,
|
||||
...bodyData.map(row => String(row[index] || '').length * 1.2)
|
||||
);
|
||||
return { wch: Math.max(8, Math.min(50, maxLength)) };
|
||||
});
|
||||
|
||||
// 워크시트를 워크북에 추가
|
||||
XLSX.utils.book_append_sheet(wb, ws, sheetName);
|
||||
|
||||
// 엑셀 파일 다운로드
|
||||
XLSX.writeFile(wb, fileName);
|
||||
} catch (error) {
|
||||
console.error('Excel download failed:', error);
|
||||
alert('엑셀 다운로드 중 오류가 발생했습니다.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ExcelDownButton onClick={downloadExcel}>
|
||||
엑셀 다운로드
|
||||
</ExcelDownButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExcelDownloadButton;
|
||||
@@ -38,6 +38,8 @@ const ModalBg = styled(motion.div)`
|
||||
min-width: 1080px;
|
||||
display: ${props => (props.$view === 'hidden' ? 'none' : 'block')};
|
||||
z-index: 20;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
`;
|
||||
|
||||
const ModalContainer = styled.div`
|
||||
@@ -52,9 +54,18 @@ const ModalWrapper = styled(motion.div)`
|
||||
min-width: ${props => props.min || 'auto'};
|
||||
padding: ${props => props.$padding || '30px'};
|
||||
border-radius: 30px;
|
||||
max-height: 90%;
|
||||
max-height: calc(100vh - 40px);
|
||||
overflow: auto;
|
||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
|
||||
|
||||
/*모바일*/
|
||||
@media (max-width: 768px) {
|
||||
min-width: unset;
|
||||
max-width: calc(100vw - 20px);
|
||||
max-height: calc(100vh - 20px);
|
||||
padding: ${props => props.$padding || '20px'};
|
||||
border-radius: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Modal = ({ children, $padding, min, $view, $bgcolor }) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, Fragment, useEffect } from 'react';
|
||||
import React, { useState, Fragment, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../common/button/Button';
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
FormStatusWarning,
|
||||
FormButtonContainer,
|
||||
} from '../../styles/ModuleComponents';
|
||||
import { Modal, SingleDatePicker, SingleTimePicker } from '../common';
|
||||
import { DetailLayout, Modal, SingleDatePicker, SingleTimePicker } from '../common';
|
||||
import { NONE, TYPE_MODIFY, TYPE_REGISTRY } from '../../assets/data/adminConstants';
|
||||
import { convertKTCDate } from '../../utils';
|
||||
import {
|
||||
@@ -64,16 +64,6 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
}
|
||||
}, [modalType, content]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if(modalType === TYPE_REGISTRY && configData?.length > 0){
|
||||
// setResultData(prev => ({
|
||||
// ...prev,
|
||||
// round_count: configData[0].default_round_count,
|
||||
// round_time: configData[0].round_time
|
||||
// }));
|
||||
// }
|
||||
// }, [modalType, configData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (checkCondition()) {
|
||||
setIsNullValue(false);
|
||||
@@ -82,104 +72,12 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
}
|
||||
}, [resultData]);
|
||||
|
||||
// 시작 날짜 변경 핸들러
|
||||
const handleStartDateChange = (date) => {
|
||||
if (!date) return;
|
||||
|
||||
const newDate = new Date(date);
|
||||
|
||||
if(resultData.repeat_type !== NONE && resultData.event_end_dt){
|
||||
const endDate = new Date(resultData.event_end_dt);
|
||||
const startDay = new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate());
|
||||
const endDay = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
if (endDay <= startDay) {
|
||||
showToast('DATE_START_DIFF_END_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setResultData(prev => ({
|
||||
...prev,
|
||||
event_start_dt: newDate
|
||||
}));
|
||||
};
|
||||
|
||||
// 시작 시간 변경 핸들러
|
||||
const handleStartTimeChange = (time) => {
|
||||
if (!time) return;
|
||||
|
||||
const newDateTime = resultData.event_start_dt
|
||||
? new Date(resultData.event_start_dt)
|
||||
: new Date();
|
||||
|
||||
newDateTime.setHours(
|
||||
time.getHours(),
|
||||
time.getMinutes(),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
setResultData(prev => ({
|
||||
...prev,
|
||||
event_start_dt: newDateTime
|
||||
}));
|
||||
};
|
||||
|
||||
const handleEndTimeChange = (time) => {
|
||||
if (!time) return;
|
||||
|
||||
const newDateTime = resultData.event_end_time
|
||||
? new Date(resultData.event_end_time)
|
||||
: new Date();
|
||||
|
||||
newDateTime.setHours(
|
||||
time.getHours(),
|
||||
time.getMinutes(),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
setResultData(prev => ({
|
||||
...prev,
|
||||
event_end_time: newDateTime
|
||||
}));
|
||||
};
|
||||
|
||||
// 종료 날짜 변경 핸들러
|
||||
const handleEndDateChange = (date) => {
|
||||
if (!date || !resultData.event_start_dt) return;
|
||||
|
||||
const startDate = new Date(resultData.event_start_dt);
|
||||
const endDate = new Date(date);
|
||||
|
||||
// 일자만 비교하기 위해 년/월/일만 추출
|
||||
const startDay = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
|
||||
const endDay = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
if (endDay <= startDay) {
|
||||
showToast('DATE_START_DIFF_END_WARNING', {type: alertTypes.warning});
|
||||
return;
|
||||
}
|
||||
|
||||
setResultData(prev => ({
|
||||
...prev,
|
||||
event_end_dt: endDate
|
||||
}));
|
||||
};
|
||||
|
||||
const handleConfigChange = (e) => {
|
||||
const config = configData.find(data => String(data.id) === String(e.target.value));
|
||||
if (config) {
|
||||
setResultData({
|
||||
...resultData,
|
||||
config_id: config.id,
|
||||
round_time: config.round_time
|
||||
});
|
||||
} else {
|
||||
showToast('Config not found for value:', e.target.value, {type: alertTypes.warning});
|
||||
}
|
||||
}
|
||||
const opGameMode = useMemo(() => {
|
||||
return gameModeData?.map(item => ({
|
||||
value: item.id,
|
||||
name: `${item.desc}(${item.id})`
|
||||
})) || [];
|
||||
}, [gameModeData]);
|
||||
|
||||
const handleReset = () => {
|
||||
setDetailData({});
|
||||
@@ -282,6 +180,7 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
return (
|
||||
resultData.event_start_dt !== ''
|
||||
&& resultData.group_id !== ''
|
||||
&& resultData.game_mode_id > 0
|
||||
&& resultData.event_name !== ''
|
||||
&& (resultData.repeat_type === 'NONE' || (resultData.repeat_type !== 'NONE' && resultData.event_end_dt !== ''))
|
||||
);
|
||||
@@ -310,122 +209,115 @@ const BattleEventModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
}
|
||||
}
|
||||
|
||||
const itemGroups = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
row: 0,
|
||||
col: 0,
|
||||
colSpan: 2,
|
||||
type: 'text',
|
||||
key: 'group_id',
|
||||
label: '그룹 ID',
|
||||
disabled: !isView('group'),
|
||||
width: '150px',
|
||||
},
|
||||
{
|
||||
row: 0,
|
||||
col: 2,
|
||||
colSpan: 2,
|
||||
type: 'text',
|
||||
key: 'event_name',
|
||||
label: '이벤트명',
|
||||
disabled: !isView('name'),
|
||||
width: '250px',
|
||||
},
|
||||
{
|
||||
row: 1,
|
||||
col: 0,
|
||||
colSpan: 2,
|
||||
type: 'date',
|
||||
key: 'event_start_dt',
|
||||
label: '시작일시',
|
||||
disabled: !isView('start_dt'),
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
width: '200px',
|
||||
showTime: true
|
||||
},
|
||||
{
|
||||
row: 1,
|
||||
col: 2,
|
||||
colSpan: 2,
|
||||
type: 'number',
|
||||
key: 'event_operation_time',
|
||||
label: '진행시간(분)',
|
||||
disabled: !isView('operation_time'),
|
||||
width: '100px',
|
||||
min: 10,
|
||||
},
|
||||
{
|
||||
row: 3,
|
||||
col: 0,
|
||||
colSpan: 2,
|
||||
type: 'select',
|
||||
key: 'repeat_type',
|
||||
label: '반복',
|
||||
disabled: !isView('repeat'),
|
||||
width: '150px',
|
||||
options: battleRepeatType
|
||||
},
|
||||
...(resultData?.repeat_type !== 'NONE' ? [{
|
||||
row: 3,
|
||||
col: 2,
|
||||
colSpan: 2,
|
||||
type: 'date',
|
||||
key: 'event_end_dt',
|
||||
label: '종료일',
|
||||
disabled: !isView('end_dt'),
|
||||
format: 'YYYY-MM-DD',
|
||||
width: '200px'
|
||||
}] : []),
|
||||
{
|
||||
row: 4,
|
||||
col: 0,
|
||||
colSpan: 2,
|
||||
type: 'select',
|
||||
key: 'game_mode_id',
|
||||
label: '게임 모드',
|
||||
disabled: !isView('mode'),
|
||||
width: '150px',
|
||||
options: opGameMode
|
||||
},
|
||||
{
|
||||
row: 4,
|
||||
col: 2,
|
||||
colSpan: 2,
|
||||
type: 'select',
|
||||
key: 'hot_time',
|
||||
label: '핫타임',
|
||||
disabled: !isView('hot'),
|
||||
width: '150px',
|
||||
options: battleEventHotTime.map(value => ({
|
||||
value: value,
|
||||
name: `${value}배`
|
||||
}))
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal min="760px" $view={detailView}>
|
||||
<Title $align="center">{isView('registry') ? "전투시스템 이벤트 등록" : isView('modify') ? "전투시스템 이벤트 수정" : "전투시스템 이벤트 상세"}</Title>
|
||||
<MessageWrapper>
|
||||
<FormRowGroup>
|
||||
<FormLabel>그룹 ID</FormLabel>
|
||||
<FormInput
|
||||
type="text"
|
||||
disabled={!isView('group')}
|
||||
width='150px'
|
||||
value={resultData?.group_id}
|
||||
onChange={e => setResultData({ ...resultData, group_id: e.target.value })}
|
||||
/>
|
||||
<FormLabel>이벤트명</FormLabel>
|
||||
<FormInput
|
||||
type="text"
|
||||
disabled={!isView('name')}
|
||||
width='300px'
|
||||
value={resultData?.event_name}
|
||||
onChange={e => setResultData({ ...resultData, event_name: e.target.value })}
|
||||
/>
|
||||
</FormRowGroup>
|
||||
<FormRowGroup>
|
||||
<SingleDatePicker
|
||||
label="시작일자"
|
||||
disabled={!isView('start_dt')}
|
||||
dateLabel="시작 일자"
|
||||
onDateChange={handleStartDateChange}
|
||||
selectedDate={resultData?.event_start_dt}
|
||||
/>
|
||||
</FormRowGroup>
|
||||
<FormRowGroup>
|
||||
<SingleTimePicker
|
||||
label="시작시간"
|
||||
disabled={!isView('start_dt')}
|
||||
selectedTime={resultData?.event_start_dt}
|
||||
onTimeChange={handleStartTimeChange}
|
||||
/>
|
||||
<FormLabel>진행시간(분)</FormLabel>
|
||||
<FormInput
|
||||
type="number"
|
||||
disabled={!isView('operation_time')}
|
||||
width='100px'
|
||||
min={10}
|
||||
value={resultData?.event_operation_time}
|
||||
onChange={e => setResultData({ ...resultData, event_operation_time: e.target.value })}
|
||||
/>
|
||||
</FormRowGroup>
|
||||
<FormRowGroup>
|
||||
<FormLabel>반복</FormLabel>
|
||||
<SelectInput value={resultData?.repeat_type} onChange={e => setResultData({ ...resultData, repeat_type: e.target.value })} disabled={!isView('repeat')} width="150px">
|
||||
{battleRepeatType.map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
{resultData?.repeat_type !== 'NONE' &&
|
||||
<SingleDatePicker
|
||||
label="종료일자"
|
||||
disabled={!isView('end_dt')}
|
||||
dateLabel="종료 일자"
|
||||
onDateChange={handleEndDateChange}
|
||||
selectedDate={resultData?.event_end_dt}
|
||||
/>
|
||||
}
|
||||
</FormRowGroup>
|
||||
{/*<FormRowGroup>*/}
|
||||
{/* <FormLabel>라운드 시간</FormLabel>*/}
|
||||
{/* <SelectInput value={resultData.config_id} onChange={handleConfigChange} disabled={!isView('config')} width="200px">*/}
|
||||
{/* {configData && configData?.map((data, index) => (*/}
|
||||
{/* <option key={index} value={data.id}>*/}
|
||||
{/* {data.desc}({data.id})*/}
|
||||
{/* </option>*/}
|
||||
{/* ))}*/}
|
||||
{/* </SelectInput>*/}
|
||||
{/* <FormLabel>라운드 수</FormLabel>*/}
|
||||
{/* <SelectInput value={resultData.round_count} onChange={e => setResultData({ ...resultData, round_count: e.target.value })} disabled={!isView('round')} width="100px">*/}
|
||||
{/* {battleEventRoundCount.map((data, index) => (*/}
|
||||
{/* <option key={index} value={data}>*/}
|
||||
{/* {data}*/}
|
||||
{/* </option>*/}
|
||||
{/* ))}*/}
|
||||
{/* </SelectInput>*/}
|
||||
{/*</FormRowGroup>*/}
|
||||
<FormRowGroup>
|
||||
{/*<FormLabel>배정 포드</FormLabel>*/}
|
||||
{/*<SelectInput value={resultData.reward_group_id} onChange={e => setResultData({ ...resultData, reward_group_id: e.target.value })} disabled={!isView('reward')} width="200px">*/}
|
||||
{/* {rewardData && rewardData?.map((data, index) => (*/}
|
||||
{/* <option key={index} value={data.group_id}>*/}
|
||||
{/* {data.desc}({data.group_id})*/}
|
||||
{/* </option>*/}
|
||||
{/* ))}*/}
|
||||
{/*</SelectInput>*/}
|
||||
<FormLabel>게임 모드</FormLabel>
|
||||
<SelectInput value={resultData.game_mode_id} onChange={e => setResultData({ ...resultData, game_mode_id: e.target.value })} disabled={!isView('mode')} width="200px">
|
||||
{gameModeData && gameModeData?.map((data, index) => (
|
||||
<option key={index} value={data.id}>
|
||||
{data.desc}({data.id})
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
<FormLabel>핫타임</FormLabel>
|
||||
<SelectInput value={resultData.hot_time} onChange={e => setResultData({ ...resultData, hot_time: e.target.value })} disabled={!isView('hot')} width="100px">
|
||||
{battleEventHotTime.map((data, index) => (
|
||||
<option key={index} value={data}>
|
||||
{data}배
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</FormRowGroup>
|
||||
|
||||
<DetailLayout
|
||||
itemGroups={itemGroups}
|
||||
formData={resultData}
|
||||
onChange={setResultData}
|
||||
disabled={false}
|
||||
columnCount={4}
|
||||
/>
|
||||
{!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>}
|
||||
</MessageWrapper>
|
||||
|
||||
<BtnWrapper $gap="10px" $marginTop="10px">
|
||||
<FormStatusBar>
|
||||
<FormStatusLabel>
|
||||
@@ -482,7 +374,7 @@ export const initData = {
|
||||
reward_group_id: 1,
|
||||
round_count: 1,
|
||||
hot_time: 1,
|
||||
game_mode_id: 1,
|
||||
game_mode_id: '',
|
||||
event_start_dt: '',
|
||||
event_end_dt: '',
|
||||
event_operation_time: 10
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, Fragment } from 'react';
|
||||
|
||||
import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, Textarea, SearchBarAlert } from '../../styles/Components';
|
||||
import { Input, Button as AntButton, Select, Alert, Space, Card, Row, Col } from 'antd';
|
||||
import { Title, BtnWrapper } from '../../styles/Components';
|
||||
import Button from '../common/button/Button';
|
||||
import Modal from '../common/modal/Modal';
|
||||
import { EventIsItem, EventModify } from '../../apis';
|
||||
@@ -10,16 +11,13 @@ import { useRecoilValue } from 'recoil';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { authType, benItems, commonStatus, currencyItemCode } from '../../assets/data';
|
||||
import {
|
||||
AppendRegistBox, AppendRegistTable, AreaBtnClose,
|
||||
BtnDelete, DetailInputItem, DetailInputRow,
|
||||
DetailModalWrapper, RegistGroup, DetailRegistInfo, DetailState,
|
||||
Item, ItemList, LangArea
|
||||
DetailRegistInfo, DetailState
|
||||
} from '../../styles/ModuleComponents';
|
||||
import { convertKTC, combineDateTime, timeDiffMinute, convertKTCDate } from '../../utils';
|
||||
import DateTimeInput from '../common/input/DateTimeInput';
|
||||
import { convertKTC, timeDiffMinute, convertKTCDate } from '../../utils';
|
||||
import { useLoading } from '../../context/LoadingProvider';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { DetailLayout } from '../common';
|
||||
|
||||
const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData }) => {
|
||||
const userInfo = useRecoilValue(authList);
|
||||
@@ -31,21 +29,14 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
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 [activeLanguage, setActiveLanguage] = useState('KO');
|
||||
const [item, setItem] = useState('');
|
||||
const [itemCount, setItemCount] = useState('');
|
||||
const [itemCount, setItemCount] = useState(1);
|
||||
const [resource, setResource] = useState('19010001');
|
||||
const [resourceCount, setResourceCount] = useState('');
|
||||
const [resourceCount, setResourceCount] = useState(1);
|
||||
|
||||
const [resultData, setResultData] = useState({});
|
||||
|
||||
const [isNullValue, setIsNullValue] = useState(false);
|
||||
// 과거 판단
|
||||
const [isPast, setIsPast] = useState(false);
|
||||
const [isChanged, setIsChanged] = useState(false);
|
||||
@@ -65,13 +56,8 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
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')
|
||||
status: content.status,
|
||||
delete_desc: content.delete_desc
|
||||
});
|
||||
|
||||
start_dt_KTC < (new Date) ? setIsPast(true) : setIsPast(false);
|
||||
@@ -90,29 +76,97 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
}
|
||||
}, [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 getLanguageTabItems = () => {
|
||||
return resultData.mail_list?.map(mail => ({
|
||||
key: mail.language,
|
||||
label: mail.language,
|
||||
children: (
|
||||
<div style={{ padding: '10px', minHeight: '400px', height: 'auto' }}>
|
||||
<Row gutter={[16, 24]}>
|
||||
<Col span={24}>
|
||||
<div>
|
||||
<label style={{
|
||||
display: 'block',
|
||||
marginBottom: '8px',
|
||||
fontWeight: 'bold',
|
||||
color: 'rgba(0, 0, 0, 0.85)',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
제목 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||
</label>
|
||||
<Input
|
||||
value={mail.title || ''}
|
||||
placeholder="우편 제목을 입력하세요"
|
||||
maxLength={30}
|
||||
readOnly={isReadOnly}
|
||||
onChange={(e) => updateMailData(mail.language, 'title', e.target.value.trimStart())}
|
||||
showCount
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<div>
|
||||
<label style={{
|
||||
display: 'block',
|
||||
marginBottom: '8px',
|
||||
fontWeight: 'bold',
|
||||
color: 'rgba(0, 0, 0, 0.85)',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
내용 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||
</label>
|
||||
<Input.TextArea
|
||||
value={mail.content || ''}
|
||||
placeholder="우편 내용을 입력하세요"
|
||||
readOnly={isReadOnly}
|
||||
rows={8}
|
||||
maxLength={2000}
|
||||
showCount
|
||||
onChange={(e) => {
|
||||
if (e.target.value.length > 2000) return;
|
||||
updateMailData(mail.language, 'content', e.target.value.trimStart());
|
||||
}}
|
||||
style={{
|
||||
resize: 'vertical',
|
||||
minHeight: '200px',
|
||||
maxHeight: '400px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
),
|
||||
closable: resultData.mail_list?.length > 1 && !isReadOnly, // 마지막 하나가 아니고 읽기전용이 아닐 때만 삭제 가능
|
||||
})) || [];
|
||||
};
|
||||
|
||||
const updateMailData = (language, field, value) => {
|
||||
const updatedMailList = resultData.mail_list.map(mail =>
|
||||
mail.language === language
|
||||
? { ...mail, [field]: value }
|
||||
: mail
|
||||
);
|
||||
setResultData({ ...resultData, mail_list: updatedMailList });
|
||||
setIsChanged(true);
|
||||
};
|
||||
|
||||
const handleTabClose = (targetKey) => {
|
||||
if (resultData.mail_list.length <= 1) return;
|
||||
|
||||
const filterList = resultData.mail_list.filter(el => el.language !== targetKey);
|
||||
setResultData({ ...resultData, mail_list: filterList });
|
||||
|
||||
// 삭제된 탭이 현재 활성 탭이었다면 첫 번째 탭으로 변경
|
||||
if (activeLanguage === targetKey) {
|
||||
setActiveLanguage(filterList[0]?.language || 'KO');
|
||||
}
|
||||
setIsChanged(true);
|
||||
};
|
||||
|
||||
// 아이템 추가
|
||||
@@ -152,19 +206,6 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
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;
|
||||
@@ -186,45 +227,9 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -281,6 +286,197 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
}
|
||||
};
|
||||
|
||||
// 아이템 목록 렌더링 컴포넌트
|
||||
const renderItemList = () => {
|
||||
return (
|
||||
<div>
|
||||
{resultData.item_list && resultData.item_list.length > 0 && (
|
||||
<Space wrap>
|
||||
{resultData.item_list.map((data, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
title={data.item_name}
|
||||
size="small"
|
||||
extra={
|
||||
!isReadOnly && (
|
||||
<AntButton
|
||||
type="text"
|
||||
danger
|
||||
size="small"
|
||||
onClick={() => onItemRemove(index)}
|
||||
>
|
||||
X
|
||||
</AntButton>
|
||||
)
|
||||
}
|
||||
style={{ minWidth: '150px' }}
|
||||
>
|
||||
<div>
|
||||
<div>{data.item}</div>
|
||||
<div>수량: {data.item_cnt}</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</Space>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 아이템 추가 컴포넌트
|
||||
const renderItemAdd = () => {
|
||||
return (
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<Input
|
||||
placeholder="Item Meta id 입력"
|
||||
value={item}
|
||||
onChange={(e) => setItem(e.target.value.trimStart())}
|
||||
disabled={isReadOnly}
|
||||
style={{ width: '200px' }}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="수량"
|
||||
value={itemCount}
|
||||
onChange={(e) => setItemCount(e.target.value)}
|
||||
disabled={isReadOnly}
|
||||
style={{ width: '120px' }}
|
||||
min={1}
|
||||
/>
|
||||
<AntButton
|
||||
type="primary"
|
||||
onClick={handleItemList}
|
||||
disabled={itemCount.length === 0 || item.length === 0 || isReadOnly}
|
||||
>
|
||||
추가
|
||||
</AntButton>
|
||||
</Space.Compact>
|
||||
);
|
||||
};
|
||||
|
||||
// 자원 추가 컴포넌트
|
||||
const renderResourceAdd = () => {
|
||||
return (
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<Select
|
||||
value={resource}
|
||||
onChange={setResource}
|
||||
disabled={isReadOnly}
|
||||
style={{ width: '200px' }}
|
||||
placeholder="자원 선택"
|
||||
>
|
||||
{currencyItemCode.map((data, index) => (
|
||||
<Select.Option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="수량"
|
||||
value={resourceCount}
|
||||
disabled={isReadOnly}
|
||||
onChange={(e) => setResourceCount(e.target.value)}
|
||||
style={{ width: '120px' }}
|
||||
min={1}
|
||||
/>
|
||||
<AntButton
|
||||
type="primary"
|
||||
onClick={handleResourceList}
|
||||
disabled={resourceCount.length === 0 || resource.length === 0 || isReadOnly}
|
||||
>
|
||||
추가
|
||||
</AntButton>
|
||||
</Space.Compact>
|
||||
);
|
||||
};
|
||||
|
||||
const itemGroups = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
row: 0,
|
||||
col: 0,
|
||||
colSpan: 2,
|
||||
type: 'dateRange',
|
||||
keys: {
|
||||
start: 'start_dt',
|
||||
end: 'end_dt'
|
||||
},
|
||||
label: '이벤트 기간',
|
||||
disabled: isReadOnly,
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
showTime: true,
|
||||
startLabel: '시작 일시',
|
||||
endLabel: '종료 일시'
|
||||
},
|
||||
{
|
||||
row: 0,
|
||||
col: 2,
|
||||
colSpan: 1,
|
||||
type: 'custom',
|
||||
key: 'status',
|
||||
label: '이벤트 상태',
|
||||
render: () => detailState(resultData.status)
|
||||
},
|
||||
...(resultData.status === commonStatus.delete ? [{
|
||||
row: 0,
|
||||
col: 3,
|
||||
colSpan: 1,
|
||||
type: 'display',
|
||||
key: 'delete_desc',
|
||||
label: '삭제 사유',
|
||||
value: resultData.delete_desc || ''
|
||||
}] : [{
|
||||
row: 0,
|
||||
col: 3,
|
||||
colSpan: 1,
|
||||
type: 'custom',
|
||||
key: 'empty_space',
|
||||
label: '',
|
||||
render: () => <div></div>
|
||||
}]),
|
||||
{
|
||||
row: 1,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'tab',
|
||||
key: 'language_tabs',
|
||||
tabItems: getLanguageTabItems(),
|
||||
activeKey: activeLanguage,
|
||||
onTabChange: setActiveLanguage,
|
||||
onTabClose: handleTabClose
|
||||
},
|
||||
{
|
||||
row: 2,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'custom',
|
||||
key: 'item_add',
|
||||
label: '아이템 추가',
|
||||
render: renderItemAdd
|
||||
},
|
||||
{
|
||||
row: 3,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'custom',
|
||||
key: 'resource_add',
|
||||
label: '자원 추가',
|
||||
render: renderResourceAdd
|
||||
},
|
||||
{
|
||||
row: 4,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'custom',
|
||||
key: 'item_list',
|
||||
render: renderItemList
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal min="960px" $view={detailView}>
|
||||
@@ -297,201 +493,22 @@ const EventDetailModal = ({ detailView, handleDetailView, content, setDetailData
|
||||
)}
|
||||
</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>
|
||||
<DetailLayout
|
||||
itemGroups={itemGroups}
|
||||
formData={resultData}
|
||||
onChange={setResultData}
|
||||
disabled={false}
|
||||
columnCount={4}
|
||||
/>
|
||||
|
||||
</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}>
|
||||
{currencyItemCode.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>
|
||||
{itemCheckMsg && (
|
||||
<Alert
|
||||
message={itemCheckMsg}
|
||||
type="error"
|
||||
style={{ marginTop: '8px', width: '300px' }}
|
||||
/>
|
||||
)}
|
||||
<BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px">
|
||||
<Button
|
||||
text="확인"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, Fragment, useEffect } from 'react';
|
||||
import React, { useState, Fragment, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../common/button/Button';
|
||||
import Loading from '../common/Loading';
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
NoticeInputItem2, BoxWrapper, FormStatusBar, FormStatusLabel, FormStatusWarning, FormButtonContainer,
|
||||
} from '../../styles/ModuleComponents';
|
||||
import { modalTypes } from '../../assets/data';
|
||||
import {DynamicModal, Modal, DateTimeRangePicker} from '../common';
|
||||
import { DynamicModal, Modal, DateTimeRangePicker, DetailLayout } from '../common';
|
||||
import { LandAuctionModify, LandAuctionSingleRegist } from '../../apis';
|
||||
import {
|
||||
AUCTION_MIN_MINUTE_TIME,
|
||||
@@ -36,6 +36,7 @@ import { convertKTCDate } from '../../utils';
|
||||
import { msToMinutes } from '../../utils/date';
|
||||
import { useAlert } from '../../context/AlertProvider';
|
||||
import { alertTypes } from '../../assets/data/types';
|
||||
import { battleEventHotTime, battleRepeatType } from '../../assets/data/options';
|
||||
|
||||
const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, setDetailData, landData, buildingData }) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -62,7 +63,6 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
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),
|
||||
});
|
||||
@@ -85,52 +85,26 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
}
|
||||
}, [resetDateTime]);
|
||||
|
||||
// 입력 수량 처리
|
||||
const handleCount = e => {
|
||||
const regex = /^\d*\.?\d{0,2}$/;
|
||||
if (!regex.test(e.target.value) && e.target.value !== '-') {
|
||||
return;
|
||||
}
|
||||
const opLand = useMemo(() => {
|
||||
return landData?.map(item => ({
|
||||
value: item.id,
|
||||
name: `${item.name}(${item.id})`
|
||||
})) || [];
|
||||
}, [landData]);
|
||||
|
||||
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 handleLand = (value, key, currentFormData) => {
|
||||
let land_id = value;
|
||||
|
||||
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 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 updatedData = {
|
||||
...currentFormData,
|
||||
land_name: land.name,
|
||||
land_size: land.size,
|
||||
land_socket: instance
|
||||
};
|
||||
setResultData(updatedData);
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
@@ -236,7 +210,7 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
|
||||
const isView = (label) => {
|
||||
switch (label) {
|
||||
case "recv":
|
||||
case "resv":
|
||||
return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY && content?.status === landAuctionStatusType.wait);
|
||||
case "auction":
|
||||
case "price":
|
||||
@@ -256,95 +230,117 @@ const LandAuctionModal = ({ modalType, detailView, handleDetailView, content, se
|
||||
}
|
||||
}
|
||||
|
||||
const itemGroups = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
row: 0,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'select',
|
||||
key: 'land_id',
|
||||
label: '랜드선택',
|
||||
disabled: !isView('registry'),
|
||||
width: '400px',
|
||||
options: opLand,
|
||||
handler: handleLand
|
||||
},
|
||||
{
|
||||
row: 1,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'display',
|
||||
key: 'land_name',
|
||||
label: '랜드 이름',
|
||||
width: '400px',
|
||||
placeholder: '랜드를 선택하세요'
|
||||
},
|
||||
{
|
||||
row: 2,
|
||||
col: 0,
|
||||
colSpan: 2,
|
||||
type: 'display',
|
||||
key: 'land_size',
|
||||
label: '랜드 크기',
|
||||
placeholder: '랜드를 선택하세요',
|
||||
width: '200px'
|
||||
},
|
||||
{
|
||||
row: 2,
|
||||
col: 2,
|
||||
colSpan: 2,
|
||||
type: 'display',
|
||||
key: 'land_socket',
|
||||
label: '인스턴스 수',
|
||||
placeholder: '랜드를 선택하세요',
|
||||
width: '200px',
|
||||
},
|
||||
{
|
||||
row: 3,
|
||||
col: 0,
|
||||
colSpan: 2,
|
||||
type: 'select',
|
||||
key: 'currency_type',
|
||||
label: '입찰재화',
|
||||
disabled: true,
|
||||
width: '200px',
|
||||
options: CurrencyType
|
||||
},
|
||||
{
|
||||
row: 3,
|
||||
col: 2,
|
||||
colSpan: 2,
|
||||
type: 'number',
|
||||
key: 'start_price',
|
||||
label: '입찰시작가',
|
||||
disabled: !isView('price'),
|
||||
width: '200px',
|
||||
min: 0,
|
||||
step: 0.01
|
||||
},
|
||||
{
|
||||
row: 4,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'date',
|
||||
key: 'resv_start_dt',
|
||||
label: '예약시작일',
|
||||
disabled: !isView('resv'),
|
||||
width: '200px',
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
showTime: true
|
||||
},
|
||||
{
|
||||
row: 5,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'dateRange',
|
||||
keys: {
|
||||
start: 'auction_start_dt',
|
||||
end: 'auction_end_dt'
|
||||
},
|
||||
label: '경매기간',
|
||||
disabled: !isView('auction'),
|
||||
width: '400px',
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
showTime: true
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
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}
|
||||
/>
|
||||
<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}
|
||||
/>
|
||||
<DetailLayout
|
||||
itemGroups={itemGroups}
|
||||
formData={resultData}
|
||||
onChange={setResultData}
|
||||
disabled={false}
|
||||
columnCount={4}
|
||||
/>
|
||||
{!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>}
|
||||
</MessageWrapper>
|
||||
|
||||
<BtnWrapper $gap="10px" $marginTop="10px">
|
||||
<FormStatusBar>
|
||||
<FormStatusLabel>
|
||||
@@ -401,7 +397,6 @@ export const initData = {
|
||||
currency_type: 'Calium',
|
||||
start_price: 0,
|
||||
resv_start_dt: '',
|
||||
resv_end_dt: '',
|
||||
auction_start_dt: '',
|
||||
auction_end_dt: ''
|
||||
}
|
||||
|
||||
@@ -2,6 +2,21 @@ import { styled } from 'styled-components';
|
||||
import RadioInput from '../common/input/Radio';
|
||||
import React, { useState, useEffect, Fragment } from 'react';
|
||||
import CheckBox from '../common/input/CheckBox';
|
||||
import {
|
||||
Input,
|
||||
Button as AntButton,
|
||||
Select,
|
||||
Alert,
|
||||
Space,
|
||||
Card,
|
||||
Row,
|
||||
Col,
|
||||
Checkbox,
|
||||
Radio,
|
||||
DatePicker,
|
||||
TimePicker
|
||||
} from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, DatePickerWrapper, Textarea} from '../../styles/Components';
|
||||
import Button from '../common/button/Button';
|
||||
@@ -32,6 +47,7 @@ import { useLoading } from '../../context/LoadingProvider';
|
||||
import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
|
||||
import { userType2 } from '../../assets/data/options';
|
||||
import { STORAGE_MAIL_COPY } from '../../assets/data/adminConstants';
|
||||
import { DetailLayout } from '../common';
|
||||
|
||||
const MailDetailModal = ({ detailView, handleDetailView, content }) => {
|
||||
const userInfo = useRecoilValue(authList);
|
||||
@@ -46,10 +62,11 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
|
||||
const [sendHour, setSendHour] = useState('00');
|
||||
const [sendMin, setSendMin] = useState('00');
|
||||
|
||||
const [activeLanguage, setActiveLanguage] = useState('KO');
|
||||
const [item, setItem] = useState('');
|
||||
const [itemCount, setItemCount] = useState('');
|
||||
const [itemCount, setItemCount] = useState(1);
|
||||
const [resource, setResource] = useState(currencyCodeTypes.gold);
|
||||
const [resourceCount, setResourceCount] = useState('');
|
||||
const [resourceCount, setResourceCount] = useState(1);
|
||||
|
||||
const [resultData, setResultData] = useState({
|
||||
is_reserve: false,
|
||||
@@ -94,6 +111,7 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
|
||||
mail_list: content.mail_list,
|
||||
item_list: content.item_list,
|
||||
guid: content.target,
|
||||
send_status: content.send_status,
|
||||
file_name: content.receive_type === 'MULTIPLE' ? content.target : null
|
||||
});
|
||||
|
||||
@@ -122,6 +140,136 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
|
||||
}
|
||||
},[updateAuth, content, resultData])
|
||||
|
||||
// 발송 상태 렌더링
|
||||
const renderSendStatus = () => {
|
||||
const status = resultData.send_status;
|
||||
let color = '';
|
||||
let text = '';
|
||||
|
||||
switch (status) {
|
||||
case 'WAIT':
|
||||
color = '#FAAD14';
|
||||
text = '대기';
|
||||
break;
|
||||
case 'FINISH':
|
||||
color = '#52c41a';
|
||||
text = '완료';
|
||||
break;
|
||||
case 'FAIL':
|
||||
color = '#ff4d4f';
|
||||
text = '실패';
|
||||
break;
|
||||
default:
|
||||
color = '#d9d9d9';
|
||||
text = status || '알 수 없음';
|
||||
}
|
||||
|
||||
return (
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
padding: '2px 8px',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: color,
|
||||
color: 'white',
|
||||
fontSize: '14px',
|
||||
fontWeight: '500'
|
||||
}}>
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
// 탭 항목 생성
|
||||
const getLanguageTabItems = () => {
|
||||
return resultData.mail_list?.map(mail => ({
|
||||
key: mail.language,
|
||||
label: mail.language,
|
||||
children: (
|
||||
<div style={{ padding: '10px', minHeight: '400px', height: 'auto' }}>
|
||||
<Row gutter={[16, 24]}>
|
||||
<Col span={24}>
|
||||
<div>
|
||||
<label style={{
|
||||
display: 'block',
|
||||
marginBottom: '8px',
|
||||
fontWeight: 'bold',
|
||||
color: 'rgba(0, 0, 0, 0.85)',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
제목 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||
</label>
|
||||
<Input
|
||||
value={mail.title || ''}
|
||||
placeholder="우편 제목을 입력하세요"
|
||||
maxLength={30}
|
||||
readOnly={isView}
|
||||
onChange={(e) => updateMailData(mail.language, 'title', e.target.value.trimStart())}
|
||||
showCount
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<div>
|
||||
<label style={{
|
||||
display: 'block',
|
||||
marginBottom: '8px',
|
||||
fontWeight: 'bold',
|
||||
color: 'rgba(0, 0, 0, 0.85)',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
내용 <span style={{ color: '#ff4d4f' }}>*</span>
|
||||
</label>
|
||||
<Input.TextArea
|
||||
value={mail.content || ''}
|
||||
placeholder="우편 내용을 입력하세요"
|
||||
readOnly={isView}
|
||||
rows={6}
|
||||
maxLength={2000}
|
||||
showCount
|
||||
onChange={(e) => {
|
||||
if (e.target.value.length > 2000) return;
|
||||
updateMailData(mail.language, 'content', e.target.value.trimStart());
|
||||
}}
|
||||
style={{
|
||||
resize: 'vertical',
|
||||
minHeight: '200px',
|
||||
maxHeight: '400px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
),
|
||||
closable: resultData.mail_list?.length > 1 && !isView,
|
||||
})) || [];
|
||||
};
|
||||
|
||||
// 메일 데이터 업데이트
|
||||
const updateMailData = (language, field, value) => {
|
||||
const updatedMailList = resultData.mail_list.map(mail =>
|
||||
mail.language === language
|
||||
? { ...mail, [field]: value }
|
||||
: mail
|
||||
);
|
||||
setResultData({ ...resultData, mail_list: updatedMailList });
|
||||
setIsChanged(true);
|
||||
};
|
||||
|
||||
// 탭 삭제 핸들러
|
||||
const handleTabClose = (targetKey) => {
|
||||
if (resultData.mail_list.length <= 1) return;
|
||||
|
||||
const filterList = resultData.mail_list.filter(el => el.language !== targetKey);
|
||||
setResultData({ ...resultData, mail_list: filterList });
|
||||
|
||||
if (activeLanguage === targetKey) {
|
||||
setActiveLanguage(filterList[0]?.language || 'KO');
|
||||
}
|
||||
setIsChanged(true);
|
||||
};
|
||||
|
||||
// 아이템 수량 숫자 체크
|
||||
const handleItemCount = e => {
|
||||
if (e.target.value === '0' || e.target.value === '-0') {
|
||||
@@ -338,12 +486,282 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleDateTimeChange = (datetime) => {
|
||||
setResultData({ ...resultData, send_dt: datetime.toDate() });
|
||||
setIsChanged(true);
|
||||
};
|
||||
|
||||
// 아이템 목록 렌더링
|
||||
const renderItemList = () => {
|
||||
return (
|
||||
<div>
|
||||
{resultData.item_list && resultData.item_list.length > 0 ? (
|
||||
<Space wrap>
|
||||
{resultData.item_list.map((data, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
title={data.item_name}
|
||||
size="small"
|
||||
extra={
|
||||
!isView && (
|
||||
<AntButton
|
||||
type="text"
|
||||
danger
|
||||
size="small"
|
||||
onClick={() => onItemRemove(index)}
|
||||
>
|
||||
X
|
||||
</AntButton>
|
||||
)
|
||||
}
|
||||
style={{ minWidth: '150px' }}
|
||||
>
|
||||
<div>
|
||||
<div>{data.item}</div>
|
||||
<div>수량: {data.item_cnt}</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</Space>
|
||||
) : (
|
||||
<Alert message="등록된 아이템이 없습니다." type="info" showIcon />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 아이템 추가 컴포넌트
|
||||
const renderItemAdd = () => {
|
||||
return (
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<Input
|
||||
placeholder="Item Meta id 입력"
|
||||
value={item}
|
||||
onChange={(e) => setItem(e.target.value.trimStart())}
|
||||
disabled={isView}
|
||||
style={{ width: '200px' }}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="수량"
|
||||
value={itemCount}
|
||||
onChange={(e) => setItemCount(e.target.value)}
|
||||
disabled={isView}
|
||||
style={{ width: '120px' }}
|
||||
min={1}
|
||||
/>
|
||||
<AntButton
|
||||
type="primary"
|
||||
onClick={handleItemList}
|
||||
disabled={itemCount.length === 0 || item.length === 0 || isView}
|
||||
>
|
||||
추가
|
||||
</AntButton>
|
||||
</Space.Compact>
|
||||
);
|
||||
};
|
||||
|
||||
// 자원 추가 컴포넌트
|
||||
const renderResourceAdd = () => {
|
||||
return (
|
||||
<div>
|
||||
<Space.Compact style={{ width: '100%', marginBottom: '8px' }}>
|
||||
<Select
|
||||
value={resource}
|
||||
onChange={setResource}
|
||||
disabled={isView}
|
||||
style={{ width: '200px' }}
|
||||
placeholder="자원 선택"
|
||||
>
|
||||
{currencyItemCode.map((data, index) => (
|
||||
<Select.Option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="수량"
|
||||
value={resourceCount}
|
||||
disabled={isView}
|
||||
onChange={(e) => setResourceCount(e.target.value)}
|
||||
style={{ width: '120px' }}
|
||||
min={1}
|
||||
/>
|
||||
<AntButton
|
||||
type="primary"
|
||||
onClick={handleResourceList}
|
||||
disabled={resourceCount.length === 0 || resource.length === 0 || isView}
|
||||
>
|
||||
추가
|
||||
</AntButton>
|
||||
</Space.Compact>
|
||||
{resource === currencyCodeTypes.calium && (
|
||||
<div style={{ fontSize: '12px', color: '#666' }}>
|
||||
잔여 수량: {caliumTotalData}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 수신대상 렌더링
|
||||
const renderReceiver = () => {
|
||||
return (
|
||||
<div>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<div>
|
||||
<Radio.Group value={resultData.receive_type} disabled>
|
||||
<Space direction="vertical">
|
||||
<Radio value="SINGLE">
|
||||
단일: {resultData.receive_type === 'SINGLE' ? resultData.guid : ''}
|
||||
</Radio>
|
||||
<Radio value="MULTIPLE">
|
||||
복수: {resultData.receive_type === 'MULTIPLE' ? excelFile : ''}
|
||||
</Radio>
|
||||
</Space>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const itemGroups = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
row: 0,
|
||||
col: 0,
|
||||
colSpan: 1,
|
||||
type: 'custom',
|
||||
key: 'is_reserve',
|
||||
render: () => (
|
||||
<Checkbox
|
||||
checked={resultData.is_reserve}
|
||||
disabled
|
||||
onChange={(e) => {
|
||||
setResultData({ ...resultData, is_reserve: e.target.checked });
|
||||
setIsChanged(true);
|
||||
}}
|
||||
>
|
||||
예약 발송
|
||||
</Checkbox>
|
||||
)
|
||||
},
|
||||
...(resultData.is_reserve ? [{
|
||||
row: 0,
|
||||
col: 1,
|
||||
colSpan: 2,
|
||||
type: 'custom',
|
||||
key: 'send_dt',
|
||||
label: '발송 시간',
|
||||
render: () => (
|
||||
<DatePicker
|
||||
showTime
|
||||
allowClear={false}
|
||||
value={resultData.send_dt ? dayjs(resultData.send_dt) : null}
|
||||
onChange={handleDateTimeChange}
|
||||
disabled={!content?.is_reserve || isView}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
style={{ width: '200px' }}
|
||||
disabledDate={(current) => current && current < dayjs().startOf('day')}
|
||||
/>
|
||||
)
|
||||
}] : []),
|
||||
{
|
||||
row: 1,
|
||||
col: 0,
|
||||
colSpan: 1,
|
||||
type: 'select',
|
||||
key: 'mail_type',
|
||||
label: '우편 타입',
|
||||
value: resultData.mail_type,
|
||||
disabled: isView,
|
||||
options: [
|
||||
{ value: 'SELECT', name: '타입 선택' },
|
||||
...mailType.filter(data => data.value !== 'ALL')
|
||||
],
|
||||
handler: (value) => {
|
||||
setResultData({ ...resultData, mail_type: value });
|
||||
setIsChanged(true);
|
||||
}
|
||||
},
|
||||
{
|
||||
row: 1,
|
||||
col: 2,
|
||||
colSpan: 1,
|
||||
type: 'custom',
|
||||
key: 'send_status',
|
||||
label: '발송상태',
|
||||
render: renderSendStatus
|
||||
},
|
||||
{
|
||||
row: 2,
|
||||
col: 1,
|
||||
colSpan: 1,
|
||||
type: 'select',
|
||||
key: 'user_type',
|
||||
label: '수신대상 타입',
|
||||
value: resultData.user_type,
|
||||
disabled: true,
|
||||
options: userType2
|
||||
},
|
||||
{
|
||||
row: 2,
|
||||
col: 2,
|
||||
colSpan: 2,
|
||||
type: 'custom',
|
||||
key: 'receiver',
|
||||
label: '수신대상',
|
||||
render: renderReceiver
|
||||
},
|
||||
{
|
||||
row: 3,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'tab',
|
||||
key: 'language_tabs',
|
||||
tabItems: getLanguageTabItems(),
|
||||
activeKey: activeLanguage,
|
||||
onTabChange: setActiveLanguage,
|
||||
onTabClose: handleTabClose
|
||||
},
|
||||
{
|
||||
row: 4,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'custom',
|
||||
key: 'item_add',
|
||||
label: '아이템 추가',
|
||||
render: renderItemAdd
|
||||
},
|
||||
{
|
||||
row: 5,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'custom',
|
||||
key: 'resource_add',
|
||||
label: '자원 추가',
|
||||
render: renderResourceAdd
|
||||
},
|
||||
{
|
||||
row: 6,
|
||||
col: 0,
|
||||
colSpan: 4,
|
||||
type: 'custom',
|
||||
key: 'item_list',
|
||||
render: renderItemList
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal min="960px" $view={detailView}>
|
||||
<Title $align="center">우편 상세 정보</Title>
|
||||
{content && <>
|
||||
<RegistInfo>
|
||||
{content && <RegistInfo>
|
||||
<span>등록자 : {content.create_by}</span>
|
||||
<span>등록일 : {convertKTC(content.create_dt, false)}</span>
|
||||
{typeof content.update_by !== 'undefined' && (
|
||||
@@ -352,321 +770,14 @@ const MailDetailModal = ({ detailView, handleDetailView, content }) => {
|
||||
<span>수정일 : {convertKTC(content.update_dt, false)}</span>
|
||||
</>
|
||||
)}
|
||||
</RegistInfo>
|
||||
<ModalWrapper>
|
||||
<RegistGroup>
|
||||
<InputRow>
|
||||
<CheckBox
|
||||
label="예약 발송"
|
||||
id="reserve"
|
||||
checked={resultData && resultData.is_reserve}
|
||||
setData={e => {
|
||||
setResultData({ ...resultData, is_reserve: e.target.checked });
|
||||
setIsChanged(true);
|
||||
}}
|
||||
disabled={(content.is_reserve === false) || isView}
|
||||
/>
|
||||
{content.is_reserve === false ? (
|
||||
<></>
|
||||
) : (
|
||||
resultData.is_reserve === true && (
|
||||
<InputItem>
|
||||
<InputLabel>발송 시간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
readOnly={(content.is_reserve === false) || isView}
|
||||
name={initialData.send_dt}
|
||||
selectedDate={resultData ? resultData.send_dt : initialData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
pastDate={new Date()}
|
||||
/>
|
||||
</DatePickerWrapper>
|
||||
<SelectInput
|
||||
onChange={e => handleSendTime(e)}
|
||||
id="hour"
|
||||
disabled={(content.is_reserve === false) || isView}
|
||||
value={
|
||||
resultData && String(new Date(resultData.send_dt).getHours()) < 10
|
||||
? '0' + String(new Date(resultData.send_dt).getHours())
|
||||
: resultData && String(new Date(resultData.send_dt).getHours())
|
||||
}>
|
||||
{HourList.map(hour => (
|
||||
<option value={hour} key={hour}>
|
||||
{hour}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
<SelectInput
|
||||
onChange={e => {
|
||||
handleSendTime(e);
|
||||
setIsChanged(true);
|
||||
}}
|
||||
id="min"
|
||||
disabled={(content.is_reserve === false) || isView}
|
||||
value={
|
||||
resultData && String(new Date(resultData.send_dt).getMinutes()) < 10
|
||||
? '0' + String(new Date(resultData.send_dt).getMinutes())
|
||||
: resultData && String(new Date(resultData.send_dt).getMinutes())
|
||||
}>
|
||||
{MinuteList.map(min => (
|
||||
<option value={min} key={min}>
|
||||
{min}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
</InputItem>
|
||||
)
|
||||
)}
|
||||
<InputItem>
|
||||
<InputLabel>우편 타입</InputLabel>
|
||||
<SelectInput
|
||||
onChange={e => {
|
||||
setResultData({ ...resultData, mail_type: e.target.value });
|
||||
setIsChanged(true);
|
||||
}}
|
||||
value={resultData.mail_type}
|
||||
disabled={isView}>
|
||||
<option value="SELECT">타입 선택</option>
|
||||
{mailType.filter(data => data.value !== 'ALL').map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</InputItem>
|
||||
<InputItem>
|
||||
<InputLabel>발송상태</InputLabel>
|
||||
<div>
|
||||
{initialData.send_status === 'WAIT' && <MailState>대기</MailState>}
|
||||
{initialData.send_status === 'FINISH' && <MailState result="success">완료</MailState>}
|
||||
{initialData.send_status === 'FAIL' && <MailState result="fail">실패</MailState>}
|
||||
</div>
|
||||
</InputItem>
|
||||
</InputRow>
|
||||
<MailReceiver>
|
||||
<InputItem>
|
||||
<InputLabel>수신대상</InputLabel>
|
||||
<InputItem>
|
||||
<SelectInput onChange={e => setResultData({ ...resultData, user_type: e.target.value })}
|
||||
value={resultData.user_type}
|
||||
disabled={true}>
|
||||
{userType2.map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</InputItem>
|
||||
<div>
|
||||
<InputGroup>
|
||||
<RadioInput
|
||||
label="단일"
|
||||
id="SINGLE"
|
||||
name="receiver"
|
||||
value="SINGLE"
|
||||
disabled={true}
|
||||
fontWeight="600"
|
||||
checked={resultData.receive_type === 'SINGLE'}
|
||||
/>
|
||||
<TextInput
|
||||
disabled={true}
|
||||
value={resultData.receive_type === 'SINGLE' && resultData.guid !== '' ? resultData.guid : ''}
|
||||
/>
|
||||
</InputGroup>
|
||||
<InputGroup>
|
||||
<RadioInput
|
||||
label="복수"
|
||||
id="MULTIPLE"
|
||||
name="receiver"
|
||||
value="MULTIPLE"
|
||||
fontWeight="600"
|
||||
disabled={true}
|
||||
/>
|
||||
<MailRegistUploadBtn
|
||||
disabled={true}
|
||||
setResultData={setResultData}
|
||||
resultData={resultData}
|
||||
setExcelFile={setExcelFile}
|
||||
handleDetailDelete={() => {}}
|
||||
disabledBtn={true}
|
||||
excelName={excelFile}
|
||||
setExcelName={setExcelFile}
|
||||
downloadData={downloadData}
|
||||
status={initialData.send_status}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</InputItem>
|
||||
</MailReceiver>
|
||||
</RegistGroup>
|
||||
{resultData.mail_list &&
|
||||
resultData?.mail_list?.map(data => {
|
||||
return (
|
||||
<Fragment key={data.language}>
|
||||
<MailRegistBox>
|
||||
<LangArea>
|
||||
언어 : {data.language}
|
||||
{btnValidation === false ? (
|
||||
<BtnClose
|
||||
disabled={true}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
onLangDelete(data.language);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<BtnClose opacity="10%" />
|
||||
)}
|
||||
</LangArea>
|
||||
<MailRegistTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>제목</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<TextInput
|
||||
placeholder="우편 제목 입력"
|
||||
maxLength="30"
|
||||
id={data.language}
|
||||
value={data.title}
|
||||
readOnly={(content.is_reserve === false) || isView}
|
||||
onChange={e => {
|
||||
let list = [...resultData.mail_list];
|
||||
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
|
||||
list[findIndex].title = e.target.value.trimStart();
|
||||
|
||||
setResultData({ ...resultData, mail_list: list });
|
||||
setIsChanged(true);
|
||||
}}
|
||||
/>
|
||||
</InputItem>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<Label>내용</Label>
|
||||
</th>
|
||||
<td>
|
||||
<Textarea
|
||||
value={data.content}
|
||||
readOnly={isView}
|
||||
id={data.language}
|
||||
onChange={e => {
|
||||
if (e.target.value.length > 2000) {
|
||||
return;
|
||||
}
|
||||
let list = [...resultData.mail_list];
|
||||
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
|
||||
list[findIndex].content = e.target.value.trimStart();
|
||||
|
||||
setResultData({ ...resultData, mail_list: list });
|
||||
setIsChanged(true);
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</MailRegistTable>
|
||||
</MailRegistBox>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
<MailRegistBox>
|
||||
<MailRegistTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>아이템 첨부</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<TextInput
|
||||
placeholder="Item Meta id 입력"
|
||||
value={item}
|
||||
onChange={e => {
|
||||
let list = [];
|
||||
list = e.target.value.trimStart();
|
||||
setItem(list);
|
||||
}}
|
||||
disabled={isView}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="수량"
|
||||
value={itemCount}
|
||||
type="number"
|
||||
onChange={e => handleItemCount(e)}
|
||||
width="90px"
|
||||
disabled={isView}
|
||||
/>
|
||||
<Button
|
||||
text="추가"
|
||||
theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'}
|
||||
disabled={isView}
|
||||
handleClick={handleItemList}
|
||||
/>
|
||||
</InputItem>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th width="120">
|
||||
<Label>자원 첨부</Label>
|
||||
</th>
|
||||
<td>
|
||||
<InputItem>
|
||||
<SelectInput onChange={e => setResource(e.target.value)} value={resource} disabled={isView}>
|
||||
{currencyItemCode.map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
<TextInput
|
||||
placeholder="수량"
|
||||
type="number"
|
||||
value={resourceCount}
|
||||
disabled={isView}
|
||||
onChange={e => handleResourceCount(e)}
|
||||
width="200px"
|
||||
/>
|
||||
<Button
|
||||
text="추가"
|
||||
theme={resourceCount.length === 0 || resource.length === 0 ? 'disable' : 'search'}
|
||||
disabled={isView}
|
||||
handleClick={handleResourceList}
|
||||
width="100px"
|
||||
height="35px"
|
||||
/>
|
||||
{resource === currencyCodeTypes.calium &&
|
||||
<Label>(잔여 수량: {caliumTotalData})</Label>}
|
||||
</InputItem>
|
||||
|
||||
<div>
|
||||
{resultData.item_list && (
|
||||
<ItemList>
|
||||
{resultData.item_list.map((data, index) => {
|
||||
return (
|
||||
<Item key={index}>
|
||||
<span>
|
||||
{data.item_name}[{data.item}] ({data.item_cnt})
|
||||
</span>
|
||||
{!isView && <BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>}
|
||||
</Item>
|
||||
);
|
||||
})}
|
||||
</ItemList>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</MailRegistTable>
|
||||
</MailRegistBox>
|
||||
</ModalWrapper>
|
||||
</>}
|
||||
</RegistInfo>}
|
||||
<DetailLayout
|
||||
itemGroups={itemGroups}
|
||||
formData={resultData}
|
||||
onChange={setResultData}
|
||||
disabled={false}
|
||||
columnCount={4}
|
||||
/>
|
||||
<BtnWrapper $justify="flex-end" $gap="10px" $paddingTop="20px">
|
||||
<Button
|
||||
text="확인"
|
||||
|
||||
@@ -205,7 +205,6 @@ const LandAuction = () => {
|
||||
{/*<th width="100">경매 재화</th>*/}
|
||||
<th width="90">경매 시작가</th>
|
||||
<th width="200">예약 시작일</th>
|
||||
<th width="200">예약 종료일</th>
|
||||
<th width="200">경매 시작일</th>
|
||||
<th width="200">경매 종료일</th>
|
||||
<th width="200">낙찰 정산일</th>
|
||||
@@ -236,7 +235,6 @@ const LandAuction = () => {
|
||||
{/*<td>{auction.currency_type}</td>*/}
|
||||
<td>{auction.start_price}</td>
|
||||
<td>{convertKTC(auction.resv_start_dt)}</td>
|
||||
<td>{convertKTC(auction.resv_end_dt)}</td>
|
||||
<td>{convertKTC(auction.auction_start_dt)}</td>
|
||||
<td>{convertKTC(auction.auction_end_dt)}</td>
|
||||
<td>{convertKTC(auction.close_end_dt)}</td>
|
||||
|
||||
Reference in New Issue
Block a user