diff --git a/src/components/flight/OldOperationApprovalsTable.js b/src/components/flight/OldOperationApprovalsTable.js new file mode 100644 index 00000000..df145b36 --- /dev/null +++ b/src/components/flight/OldOperationApprovalsTable.js @@ -0,0 +1,1523 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Button, Card } from '@component/ui'; +import { openModal } from '@src/redux/features/comn/message/messageSlice'; +import { FaAngleDown, FaAngleUp } from 'react-icons/fa'; +import { Form, Input, Table, Typography } from 'antd'; +import { + updateLaancAprv, + getLaancAprvList, + updateLaancAprvReview +} from '@src/redux/features/laanc/laancThunk'; +import { ERROR_MESSAGE, ERROR_TITLE } from '@src/configs/msgConst'; +import OperationModal from './OperationModal'; +import ScrollContainer from 'react-indiana-drag-scroll'; +import OperationCheckBoxModal from './OperationCheckBoxModal'; + +export default function OldOperationApprovalsTable(props) { + const dispatch = useDispatch(); + + // 비행승인 목록 + const { laancAprvList, laancElev, laancAprvLoading } = useSelector( + state => state.laancState + ); + + // 승인, 미승인, 비대상 건수 + const [approvalCdValue, setApprovalCdValue] = useState({ + S: 0, + F: 0, + C: 0, + U: 0 + }); + + // 체크박스 팝업 테이블 데이터 + const [checkData, setCheckData] = useState([]); + + const scrollContainerRef = useRef(null); + + // 확장된 행 키 + const [expandedRowKeys, setExpandedRowKeys] = useState([]); + + // 확장된 행 데이터 + const [expandedRowDatas, setExpandedRowDatas] = useState([]); + + // 수정 키 + const [editingKey, setEditingKey] = useState(''); + + // select row key + const [selectedRowKey, setSelectedRowKey] = useState(''); + + const [ismodal, setIsModal] = useState(false); + + const [form] = Form.useForm(); + + // 체크박스 선택 + const [checkList, setCheckList] = useState([]); + + // 체큽작스 모달 + const [isCheckBoxModal, setIsCheckBoxModal] = useState(false); + + // 수정 키 확인 + const isEditing = record => record.key === editingKey; + + // 유효성 기체 데이터 + const [validData, setValidData] = useState(); + + useEffect(() => { + let approvalCdValue = { S: 0, F: 0, C: 0, U: 0 }; + + laancAprvList.map(item => { + item.areaList.map(area => { + if (area.approvalCd === 'S') { + approvalCdValue.S += 1; + } else if (area.approvalCd === 'F') { + approvalCdValue.F += 1; + } else if (area.approvalCd === 'C') { + approvalCdValue.C += 1; + } else { + approvalCdValue.U += 1; + } + }); + }); + + setApprovalCdValue({ + F: approvalCdValue.F, + S: approvalCdValue.S, + C: approvalCdValue.C, + U: approvalCdValue.U + }); + + if (editingKey) { + setEditingKey(''); + } + setCheckList([]); + }, [laancAprvList]); + + const handlerReviewed = type => { + let text = '검토대기'; + if (type === 'U') { + text = '검토불필요'; + } else if (!type) { + text = '검토대기'; + } else { + if (type === 'R') { + text = '검토완료'; + } else if (type === 'W') { + text = '검토대기'; + } else if (type === 'Q') { + text = '검토요청'; + } else if (type === 'A') { + text = '검토재요청'; + } else if (type === 'C') { + text = '검토취소'; + } + } + + return text; + }; + + // 데이터 + const columns = [ + { + title: ( + <> + 신청 +
+ 번호 + + ), + dataIndex: 'applyNo', + align: 'center', + width: '60px' + }, + { + title: ( + <> + 신청
월 + + ), + dataIndex: 'applyDtMonth', + width: '70px', + align: 'center', + render: text => `${text}월` + }, + { + title: ( + <> + 신청
일 + + ), + dataIndex: 'applyDtDay', + width: '70px', + align: 'center', + + render: text => `${text}일` + }, + { + title: ( + <> + 신청
+ 구역 수 + + ), + dataIndex: 'areaList', + align: 'center', + width: '85px', + render: areaList => <>총{areaList.length}건 + }, + { + title: <>신청자, + dataIndex: 'applyNm', + width: '90px', + align: 'center', + + render: text => (text ? text : '-') + }, + { + title: ( + <> + 행정
+ 구역 1 + + ), + dataIndex: 'areaList', + width: '90px', + align: 'center', + render: areaList => { + return areaList.length <= 1 && areaList[0].addr1 + ? areaList[0].addr1 + : '-'; + } + }, + { + title: ( + <> + 행정
+ 구역 2 + + ), + dataIndex: 'areaList', + width: '90px', + align: 'center', + render: areaList => { + return areaList.length <= 1 && areaList[0].addr2 + ? areaList[0].addr2 + : '-'; + } + }, + { + title: ( + <> + 상세 +
+ 주소 + + ), + dataIndex: 'areaList', + width: '100px', + align: 'center', + render: areaList => { + return areaList.length <= 1 && areaList[0].addr3 + ? areaList[0].addr3 + : '-'; + } + }, + { + title: ( + <> + 장애물 +
+ 제한 표면 + + ), + dataIndex: 'areaList', + width: '100px', + align: 'center', + render: areaList => { + return areaList.length <= 1 + ? areaList[0]?.limitZoneNm + ? areaList[0]?.limitZoneNm + : '-' + : '-'; + // return areaList.length <= 1 ? areaList[0].limitZoneNm : '-'; + } + }, + { + title: ( + <> + 중심 좌표 +
+ (위도, 경도) + + ), + dataIndex: 'areaList', + align: 'center', + width: '90px', + render: areaList => { + return areaList.length <= 1 ? ( + <> + {areaList[0].lat.toFixed(5)}, +
+ {areaList[0].lon.toFixed(5)} + + ) : ( + '-' + ); + } + }, + { + title: ( + <> + 비행 반경 +
+ (m이내) + + ), + dataIndex: 'bufferZone', + align: 'center', + width: '80px', + render: (text, record) => { + return text ? text : '-'; + } + }, + { + title: ( + <> + 요청 반경 +
+ (m이내) + + ), + dataIndex: 'reqRadius', + align: 'center', + width: '80px', + editable: true, + render: (text, record) => { + return text ? text : '-'; + } + }, + { + title: ( + <> + 허용 반경 +
+ (m이내) + + ), + dataIndex: 'allowRadius', + align: 'center', + width: '80px', + render: (text, record) => { + return text ? text : '-'; + } + }, + { + title: ( + <> + 신청 고도 +
+ (m이하) + + ), + dataIndex: 'fltElev', + align: 'center', + width: '80px', + editable: true, + render: (text, record) => { + return text ? text : '-'; + } + }, + { + title: ( + <> + 요청 고도 +
+ (m이하) + + ), + dataIndex: 'reqElev', + align: 'center', + width: '80px', + editable: true, + render: (text, record) => { + return text ? text : '-'; + } + }, + { + title: ( + <> + 허용 고도 +
+ (m이하) + + ), + dataIndex: 'fltElevMax', + align: 'center', + width: '90px', + render: (text, record) => { + return text || text === 0 ? text : '-'; + } + }, + + { + title: <>비행 목적, + dataIndex: 'areaList', + align: 'center', + width: '110px', + render: areaList => { + return areaList.length <= 1 && areaList[0].purpose + ? areaList[0].purpose + : '-'; + } + }, + { + title: ( + <> + LAANC +
+ 검토 결과 + + ), + dataIndex: 'areaList', + align: 'center', + width: '140px', + render: areaList => { + const approvalCounts = areaList.reduce( + (counts, item) => { + if (item.approvalCd === 'U') { + counts.unapproved += 1; + } else if (item.approvalCd === 'S') { + counts.approved += 1; + } else if (item.approvalCd === 'C') { + counts.conditionalapproval += 1; + } else { + counts.pending += 1; + } + return counts; + }, + { unapproved: 0, approved: 0, pending: 0, conditionalapproval: 0 } + ); + + return ( + <> + {areaList.length > 1 ? ( + <> + 승인: {approvalCounts.approved}건
+ 미승인: + {approvalCounts.pending}건 +
+ 조건부승인:{approvalCounts.conditionalapproval}건
+ 비대상: + {approvalCounts.unapproved}건 + + ) : ( + <> + {areaList[0].approvalCd === 'U' + ? '비대상' + : areaList[0].approvalCd === 'S' + ? '승인' + : areaList[0].approvalCd === 'C' + ? '조건부승인' + : '미승인'} + + )} + + ); + } + }, + { + title: <>더보기, + dataIndex: 'areaList', + align: 'center', + width: '130px', + render: (areaList, record) => + areaList.length > 1 ? ( + + ) : ( + <>- + ) + }, + { + title: '편집', + dataIndex: 'planAreaSno', + align: 'center', + width: '110px', + render: (_, record) => { + const editable = isEditing(record); + return record.areaList.length <= 1 ? ( + editable ? ( + + save(record.key, 'outer')} + style={{ + marginRight: 8 + }} + > + 저장 + + cancel()}>취소 + + ) : ( + + ) + ) : ( + <>- + ); + } + }, + { + title: ( + <> + 관제과 +
+ 검토결과 + + ), + dataIndex: 'areaList', + align: 'center', + width: '130px', + render: areaList => { + const approvalCounts = areaList.reduce( + (counts, item) => { + if (item.reviewedType === 'U') { + counts.unapproved += 1; + } else if (item.reviewedType === 'R') { + counts.reviewed += 1; + } else if (item.reviewedType === 'Q') { + counts.request += 1; + } else if (item.reviewedType === 'W') { + counts.wait += 1; + } else if (item.reviewedType === 'A') { + counts.rerequest += 1; + } else if (item.reviewedType === 'C') { + counts.cancel += 1; + } + return counts; + }, + { + reviewed: 0, + wait: 0, + cancel: 0, + unapproved: 0, + request: 0, + rerequest: 0 + } + ); + + return ( + <> + {areaList.length > 1 ? ( + <> + 검토완료: {approvalCounts.reviewed}건
+ 검토대기:{approvalCounts.wait}건
+ 검토요청: + {approvalCounts.request}건 +
+ 검토재요청: + {approvalCounts.rerequest}건 +
+ 검토요청취소: + {approvalCounts.cancel}건 +
+ 검토불필요: + {approvalCounts.unapproved}건 + + ) : ( + <>{handlerReviewed(areaList[0].reviewedType)} + )} + + ); + } + }, + { + title: ( + <> + 재검토
+ 사유 + + ), + dataIndex: 'reviewedReason', + width: '70px', + align: 'center', + + render: text => (text ? `${text}` : '-') + }, + { + title: '비고', + dataIndex: 'areaList', + align: 'center', + width: '130px', + render: (areaList, record) => + areaList.length <= 1 ? ( + + ) : ( + <>- + ) + } + ]; + + // 확장된 테이블 데이터 + const expandedRowRender = record => { + const childColumns = [ + { + dataIndex: 'applyNo', + width: '60px', + align: 'center' + }, + { + dataIndex: 'applyDtMonth', + width: '70px', + align: 'center', + + render: text => `${text}월` + }, + { + dataIndex: 'applyDtDay', + width: '70px', + align: 'center', + render: text => `${text}일` + }, + { + dataIndex: 'zoneNo', + align: 'center', + width: '85px', + render: text => { + return <>구역{text}; + } + }, + { + dataIndex: 'applyNm', + align: 'center', + width: '90px', + render: text => { + return text ? text : '-'; + } + }, + { + dataIndex: 'addr1', + align: 'center', + width: '90px', + render: text => { + return text ? text : <>-; + } + }, + { + dataIndex: 'addr2', + align: 'center', + width: '90px', + render: text => { + return text ? text : <>-; + } + }, + { + dataIndex: 'addr3', + align: 'center', + width: '100px', + render: text => { + return text ? text : <>-; + } + }, + + { + dataIndex: 'limitZoneNm', + align: 'center', + width: '100px', + render: text => { + return text ? text : <>-; + } + }, + + { + align: 'center', + width: '90px', + dataIndex: ['lat', 'lon'], + render: (text, record) => { + const lat = record.lat; + const lon = record.lon; + return ( + <> + {lat.toFixed(5)},
+ {lon.toFixed(5)} + + ); + } + }, + { + dataIndex: 'bufferZone', + align: 'center', + width: '80px', + render: text => { + return text ? text : <>-; + } + }, + { + dataIndex: 'reqRadius', + align: 'center', + editable: true, + width: '80px', + render: text => { + return text ? text : <>-; + } + }, + { + dataIndex: 'allowRadius', + align: 'center', + width: '80px', + render: text => { + return text ? text : <>-; + } + }, + { + dataIndex: 'fltElev', + align: 'center', + editable: true, + width: '80px', + render: text => { + return text ? text : <>-; + } + }, + { + dataIndex: 'reqElev', + align: 'center', + width: '80px', + editable: true, + + render: text => { + return text ? text : <>-; + } + }, + { + dataIndex: 'fltElevMax', + align: 'center', + width: '90px', + render: text => { + return text ? text : <>-; + } + }, + + { + dataIndex: 'purpose', + align: 'center', + width: '110px', + render: text => { + return text ? text : <>-; + } + }, + { + dataIndex: 'approvalCd', + align: 'center', + width: '140px', + render: text => ( + <> + {text === 'U' + ? '비대상' + : text === 'S' + ? '승인' + : text === 'C' + ? '조건부승인' + : '미승인'} + + ) + }, + { + dataIndex: '더보기', + align: 'center', + width: '130px', + render: text => { + return <>-; + } + }, + { + dataIndex: 'planAreaSno', + align: 'center', + width: '110px', + render: (_, record) => { + const editable = isEditing(record); + return editable ? ( + + save(record.key, 'inner')} + style={{ + marginRight: 8 + }} + > + 저장 + + cancel()}>취소 + + ) : ( + + ); + } + }, + { + dataIndex: 'reviewedType', + align: 'center', + width: '130px', + render: text => { + return text === 'U' + ? '검토불필요' + : text === 'R' + ? '검토완료' + : text === 'C' + ? '검토요청취소' + : text === 'Q' + ? '검토요청' + : text === 'A' + ? '검토재요청' + : '검토대기'; + } + }, + { + dataIndex: 'reviewedReason', + width: '70px', + align: 'center', + render: text => (text ? `${text}` : '-') + }, + { + title: '비고', + dataIndex: 'planAreaSno', + align: 'center', + width: '130px', + render: (_, record) => ( + + ) + } + ]; + + const data = []; + record.areaList.map((item, index) => { + data.push({ + key: `${item.planSno}`, + applyNo: item.applyNo, + applyDt: item.applyDt, + zoneNo: item.zoneNo, + lat: item.lat, + lon: item.lon, + applyDtDay: item.applyDtDay, + applyDtMonth: item.applyDtMonth, + bufferZone: item.bufferZone, + fltElev: item.fltElev, + rm: item.rm, + era: item.era, + reqRadius: item.reqRadius, + allowRadius: item.allowRadius, + fltElevMax: item.fltElevMax, + purpose: item.purpose, + reviewedType: item.reviewedType, + applyNm: item.applyNm, + dtl: item.dtl, + approvalCd: item.approvalCd, + planAreaSno: item.planAreaSno, + planSno: item.planSno, + reqElev: item.reqElev, + addr1: item.addr1, + addr2: item.addr2, + addr3: item.addr3, + reviewedReason: item.reviewedReason, + limitZoneNm: item?.limitZoneNm ? item?.limitZoneNm : '-' + }); + }); + + return ( +
+ { + let className = ''; + if (record.approvalCd === 'S') { + className += 'flight-approval-row'; + } else if (record.approvalCd === 'F') { + className += 'flight-not-approval-row'; + } else if (record.approvalCd === 'C') { + className += 'flight-condition-approval-row editable-row'; + } else className; + + if (record.planAreaSno === props.selected[0]) { + if (props.selected.length <= 1) { + className += ' flight-approval-row-click'; + } + } + return className; + }} + size='small' + columns={childColumns.map(col => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: record => ({ + record, + inputType: + col.dataIndex === 'bufferZone' || col.dataIndex === 'fltElev' + ? 'number' + : 'text', + dataIndex: col.dataIndex, + title: col.title, + editing: isEditing(record) + }) + }; + })} + rowSelection={{ + ...childRowSelection, + selectedRowKeys: checkList.filter(key => + key.startsWith(`child_${record.planSno}_`) + ) + }} + dataSource={record.areaList.map((item, index) => ({ + ...item, + key: `child_${record.planSno}_${item.planAreaSno}` + }))} + pagination={false} + components={{ + body: { + cell: EditableCell + } + }} + onRow={record => ({ + onClick: event => { + if (editingKey !== '') { + return; // edit 상태면 이벤트 실행 안 함 + } + setSelectedRowKey(record.planAreaSno); + + // 이벤트 버블링을 막기 위해 클릭된 요소가 'Edit' 버튼인지 확인 + if ( + event.target.tagName !== 'BUTTON' && + event.target.tagName !== 'A' && + !event.target.closest('.ant-input') && // input 요소를 감지 + !event.target.closest('.ant-input-number') // inputNumber 요소를 감지 + ) { + handleInRowClick(record); + } + } + })} + showHeader={false} + rowHoverable={false} + /> + + ); + }; + + // 테이블 데이터 + const mergedColumns = columns.map(col => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: record => ({ + record, + inputType: + col.dataIndex === 'bufferZone' || col.dataIndex === 'fltElev' + ? 'number' + : 'text', + dataIndex: col.dataIndex, + title: col.title, + editing: isEditing(record) + }) + }; + }); + + // 단순 메시지 표출 모달 + const handlerErrorModal = (header, body, isRefresh) => { + dispatch( + openModal({ + header: header, + body: body, + isHistoryBack: false, + isRefresh: isRefresh + }) + ); + }; + + // 수정 이벤트 + const edit = record => { + form.setFieldsValue({ + planAreaSno: record.planAreaSno, + reqRadius: record.reqRadius, + fltElev: record.fltElev, + dtl: record.dtl, + era: record.era, + rm: record.rm, + reqRadius: record.reqRadius, + reqElev: record.reqElev, + ...record + }); + setEditingKey(record.key); + }; + + // 취소 이벤트 + const cancel = () => { + setEditingKey(''); + }; + + // 저장 이벤트 + const save = async (key, type) => { + try { + const row = await form.validateFields(); + const index = { ...row, planAreaSno: selectedRowKey }; + + const updateRes = await dispatch( + updateLaancAprv([ + { + planAreaSno: selectedRowKey, + reqRadius: row.reqRadius || '', + fltElev: row.fltElev || '', + dtl: row.dtl || '', + era: row.era || '', + rm: row.rm || '', + reqRadius: row.reqRadius || '', + reqElev: row.reqElev || '' + } + ]) + ); + + if (updateRes.meta.requestStatus === 'fulfilled') { + setEditingKey(''); + await dispatch( + getLaancAprvList({ + searchStDt: props.startDate, + searchEndDt: props.endDate + }) + ); + } else { + handlerErrorModal(ERROR_TITLE, ERROR_MESSAGE, true); + } + } catch (errInfo) { + handlerErrorModal(ERROR_TITLE, ERROR_MESSAGE, true); + } + }; + + // 모달 오픈 핸들러 + const handlerOpenModal = (approval, fltElev, fltElevMax) => { + if (approval === 'F') { + dispatch( + openModal({ + header: '미승인 사유', + body: `관제권 내 제한고도(신청고도${fltElev}m/허용고도${fltElevMax}m) 입니다.`, + type: 'error' + }) + ); + } else if (approval === 'S') { + dispatch( + openModal({ + header: '승인 사유', + body: `관제권 내 허용고도(신청고도${fltElev}m/허용고도${fltElevMax}m) 입니다.`, + type: 'success' + }) + ); + } else if (approval === 'C') { + dispatch( + openModal({ + header: '조건부 승인 사유', + body: `관제권 내 조건부 승인(신청고도${fltElev}m/허용고도${ + fltElevMax === undefined ? 150 : fltElevMax + }m) 입니다.`, + type: 'warning' + }) + ); + } else { + dispatch( + openModal({ + header: '비대상 사유', + body: `해당 구역은 비 대상(신청고도${fltElev}m/허용고도${ + fltElevMax === undefined ? 150 : fltElevMax + }m) 지역 입니다.`, + type: 'error' + }) + ); + } + }; + + // 날짜 포맷 변경 + const formatDate = dateString => { + const date = new Date(dateString); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}년 ${month}월 ${day}일 `; + }; + + // 테이블 행 확장 이벤트 + const handleExpand = record => { + const key = record.key; + const areaList = record.areaList; + const expanded = expandedRowKeys.includes(key); + + const keys = expanded + ? expandedRowKeys.filter(k => k !== key) + : [...expandedRowKeys, key]; + setExpandedRowKeys(keys); + + const datas = expanded + ? expandedRowDatas.filter(k => k.planSno !== record.planSno) + : [ + ...expandedRowDatas, + { + applyNo: record.applyNo, + planSno: record.planSno, + areaList + } + ]; + setExpandedRowDatas(datas); + + if (!expanded) { + props.handlerDetail(record.areaList); + } else { + if (props.selectedPlanSno === record.planSno) { + props.handlerDetail([]); + } + } + }; + + // 테이블 행 클릭 이벤트 + const handleRowClick = row => { + if (row.areaList.length > 1) return; + handlerOpenModal( + row.areaList[0].approvalCd, + row.areaList[0].fltElev, + row.areaList[0].fltElevMax + ); + props.handlerDetail([row.areaList[0]]); + }; + + // 확장된 테이블 행 클릭 이벤트 + const handleInRowClick = async row => { + handlerOpenModal(row.approvalCd, row.fltElev, row.fltElevMax); + if (props.selectedPlanSno !== row.planSno) { + // 지도에 표출되지 않은 확장 테이블 행 클릭 + const areaList = expandedRowDatas.filter( + data => data.planSno === row.planSno + ); + await props.handlerDetail(areaList[0].areaList); + props.handlerDetail([row]); + } else { + // 지도에 표출된 확장 테이블 행 클릭 + await props.handlerDetail([row]); + } + }; + + // 유효성 검사 모달창 + const handleIsModal = record => { + setValidData({ + droneList: record.droneList, + pilotList: record.pilotList + }); + + // if (record.droneList || record.pilotList) { + setIsModal(!ismodal); + // } + }; + + // 부모 체크박스 + const rowSelection = { + selectedRowKeys: checkList, + getCheckboxProps: record => { + const allChildrenReviewed = record.areaList.every( + child => child.reviewedType === 'R' + ); + return { + disabled: allChildrenReviewed, + name: record.key + }; + }, + onSelect: (record, selected, selectedRows, nativeEvent) => { + let newCheckList = [...checkList]; // const를 let으로 변경 + const key = record.key; + + if (key.startsWith('parent_')) { + const childKeys = record.areaList + .filter(child => child.reviewedType !== 'R') + .map(child => `child_${record.planSno}_${child.planAreaSno}`); + + if (selected) { + newCheckList.push(key, ...childKeys); + } else { + newCheckList = newCheckList.filter( + k => k !== key && !childKeys.includes(k) + ); + } + } + + updateCheckList(newCheckList); + }, + onSelectAll: (selected, selectedRows, changeRows) => { + const newCheckList = selected + ? laancAprvList.flatMap(parent => { + const parentKey = `parent_${parent.planSno}`; + + const childKeys = parent.areaList.map( + child => `child_${parent.planSno}_${child.planAreaSno}` + ); + + return parent.areaList.length > 0 ? [parentKey, ...childKeys] : []; + }) + : []; + + updateCheckList(newCheckList); + } + }; + + // updateCheckList 함수 수정 + const updateCheckList = list => { + const newList = [...list]; + + // 부모 키 업데이트 + laancAprvList.forEach(parent => { + const parentKey = `parent_${parent.planSno}`; + const selectableChildren = parent.areaList.filter( + child => child.reviewedType !== 'R' + ); + + const childKeys = selectableChildren.map( + child => `child_${parent.planSno}_${child.planAreaSno}` + ); + const allSelectableChildrenChecked = childKeys.every(key => + newList.includes(key) + ); + const someSelectableChildrenChecked = childKeys.some(key => + newList.includes(key) + ); + + if (allSelectableChildrenChecked && selectableChildren.length > 0) { + if (!newList.includes(parentKey)) { + newList.push(parentKey); + } + } else if (!someSelectableChildrenChecked) { + const index = newList.indexOf(parentKey); + if (index > -1) { + newList.splice(index, 1); + } + } + }); + + setCheckList(newList); + }; + + // 자식 테이블 체크박스 + const childRowSelection = { + selectedRowKeys: checkList.filter(key => key.startsWith('child_')), + getCheckboxProps: record => ({ + disabled: record.reviewedType === 'R', + name: record.key + }), + + onSelect: (record, selected, selectedRows) => { + const key = record.key; + let newCheckList = [...checkList]; + + if (selected) { + newCheckList.push(key); + } else { + newCheckList = newCheckList.filter(k => k !== key); + } + updateCheckList(newCheckList); + } + }; + + // 체크박스 업데이트 함수 + const handleCheckbox = async type => { + if (checkList.length === 0) { + return handlerErrorModal('검토 실패', '검토 항목을 선택해주세요.', false); + } + + const planAreaSnoList = [ + ...new Set( + checkList + .filter(item => item.startsWith('child_')) + .map(item => item.split('_').pop()) + ) + ]; + + const res = laancAprvList.flatMap(item => + item.areaList.filter(area => + planAreaSnoList.includes(area.planAreaSno.toString()) + ) + ); + + // 검토 요청, 재 요청 조건 + const checkCondition = { + A: item => item.reviewedType !== 'R' && item.reviewedType !== 'C', + Q: item => item.reviewedType !== 'W' + }; + + // 요청 실패 시 에러 메시지 + const errorMessages = { + A: [ + '재 요청 요청 실패', + '검토결과 완료, 검토취소인 신청건만 재 요청이 가능합니다.' + ], + Q: ['검토 요청 실패', '검토결과 대기인 신청건만 요청이 가능합니다.'] + }; + + // 요청 성공 시 액션 핸들러 + const actionHandlers = { + A: () => { + setCheckData(res); + setIsCheckBoxModal(true); + }, + Q: async () => { + try { + await dispatch( + updateLaancAprvReview({ planAreaSnoList, reviewedType: 'Q' }) + ); + props.handlerSearch( + props.filterId, + { startDate: props.startDate, endDate: props.endDate }, + props.filterArea + ); + setCheckList([]); + } catch (error) { + return handlerErrorModal( + '검토 실패', + '검토를 요청을 실패하였습니다. 다시 시도해주세요.', + false + ); + } + } + }; + + // 요청 실패 시 에러 메시지 핸들러 + if (res.some(checkCondition[type])) { + return handlerErrorModal(...errorMessages[type]); + } + + // 요청 성공 시 액션 핸들러 실행 + await actionHandlers[type](); + }; + + return ( +
+
+
+

비행승인 신청 검토결과 목록

+
+
+ {ismodal && ( + + )} + {isCheckBoxModal && ( + + )} +
+
+ + {formatDate(props.startDate)} + {props.endDate && props.startDate !== props.endDate + ? '~' + formatDate(props.endDate) + ' ' + : null} + 총 + {approvalCdValue.S + + approvalCdValue.F + + approvalCdValue.U + + approvalCdValue.C} + 건 + +
+
+ + + +
+
+
+
+
    +
  • 승인 {approvalCdValue.S}건
  • +
  • 미승인 {approvalCdValue.F}건
  • +
  • + 조건부 승인 {approvalCdValue.C}건 +
  • +
  • 비대상 {approvalCdValue.U}건
  • +
  • 전송 0건
  • +
  • 미 전송 0건
  • +
+
+
+
+ +
+ {laancAprvList?.length > 0 ? ( +
+ +
+ +
({ + ...item, + key: `parent_${item.planSno}` + }))} + columns={mergedColumns} + rowClassName={record => { + let className = ''; + if (record.areaList.length <= 1) { + if (record?.areaList[0]?.approvalCd === 'S') { + className += 'flight-approval-row editable-row'; + } else if (record.areaList[0].approvalCd === 'F') { + className += + 'flight-not-approval-row editable-row'; + } else if (record.areaList[0].approvalCd === 'C') { + className += + 'flight-condition-approval-row editable-row'; + } else className += 'editable-row'; + } + + if (props.selected?.length > 0) { + if ( + record.areaList.length <= 1 && + record.areaList[0].planAreaSno === + props.selected[0] + ) { + className += ' flight-approval-row-click'; + } + } + + if (expandedRowKeys.includes(record.key)) { + className += ' expanded-row'; + } + if (record.areaList.length > 1) { + className += 'expanded-tr'; + } + return className; + }} + onRow={record => ({ + onClick: event => { + if (editingKey !== '') { + return; // edit 상태면 이벤트 실행 안 함 + } + setSelectedRowKey(record.planAreaSno); + // 이벤트 버블링을 막기 위해 클릭된 요소가 'Edit' 버튼인지 확인 + if ( + event.target.tagName !== 'BUTTON' && + event.target.tagName !== 'A' && + !event.target.closest('.ant-input') && // input 요소를 감지 + !event.target.closest('.ant-input-number') // inputNumber 요소를 감지 + ) { + handleRowClick(record); + } + } + })} + rowSelection={{ + ...rowSelection + }} + loading={laancAprvLoading} + expandable={{ + expandedRowRender, + expandedRowKeys: expandedRowKeys, + onExpand: (expanded, record) => handleExpand(record), + rowExpandable: record => record?.areaList?.length > 1 + }} + scroll={{ + x: 1700 + }} + rowHoverable={false} + expandIconColumnIndex={-1} + /> + + + + + ) : ( +
+

비행승인 신청 건수가 없습니다.

+
+ )} + + + + + ); +} + +const EditableCell = ({ + editing, + dataIndex, + title, + inputType, + record, + index, + children, + ...restProps +}) => { + const inputNode = inputType === 'number' ? : ; + return ( + + ); +}; diff --git a/src/components/flight/OperationApprovalsTable.js b/src/components/flight/OperationApprovalsTable.js index e861bdb5..1810dc38 100644 --- a/src/components/flight/OperationApprovalsTable.js +++ b/src/components/flight/OperationApprovalsTable.js @@ -35,12 +35,6 @@ export default function OperationApprovalsTable(props) { const scrollContainerRef = useRef(null); - // 확장된 행 키 - const [expandedRowKeys, setExpandedRowKeys] = useState([]); - - // 확장된 행 데이터 - const [expandedRowDatas, setExpandedRowDatas] = useState([]); - // 수정 키 const [editingKey, setEditingKey] = useState(''); @@ -57,9 +51,6 @@ export default function OperationApprovalsTable(props) { // 체큽작스 모달 const [isCheckBoxModal, setIsCheckBoxModal] = useState(false); - // 수정 키 확인 - const isEditing = record => record.key === editingKey; - // 유효성 기체 데이터 const [validData, setValidData] = useState(); @@ -87,35 +78,57 @@ export default function OperationApprovalsTable(props) { U: approvalCdValue.U }); - if (editingKey) { - setEditingKey(''); - } setCheckList([]); }, [laancAprvList]); - const handlerReviewed = type => { - let text = '검토대기'; - if (type === 'U') { - text = '검토불필요'; - } else if (!type) { - text = '검토대기'; - } else { - if (type === 'R') { - text = '검토완료'; - } else if (type === 'W') { - text = '검토대기'; - } else if (type === 'Q') { - text = '검토요청'; - } else if (type === 'A') { - text = '검토재요청'; - } else if (type === 'C') { - text = '검토취소'; + const handlerReviewed = (value, type) => { + let text = ''; + if (type === 'operation') { + switch (value) { + case 'W': + text = '검토대기'; + break; + case 'Q': + text = '검토요청'; + break; + case 'A': + text = '검토재요청'; + break; + case 'R': + text = '검토완료'; + break; + case 'C': + text = '검토취소'; + break; + case 'U': + text = '검토불필요'; + break; + } + return text; + } else if (type === 'control') { + switch (value) { + case 'R': + text = '검토완료'; + break; + case 'Q': + text = '검토요청'; + break; + case 'W': + text = '-'; + break; + case 'A': + text = '검토재요청'; + break; + case 'C': + text = '요청반려'; + break; + case 'U': + text = '-'; + break; } + return text; } - - return text; }; - // 데이터 const columns = [ { @@ -139,7 +152,7 @@ export default function OperationApprovalsTable(props) { dataIndex: 'applyDtMonth', width: '70px', align: 'center', - render: text => `${text}월` + render: applyDtMonth => `${applyDtMonth}월` }, { title: ( @@ -151,7 +164,7 @@ export default function OperationApprovalsTable(props) { width: '70px', align: 'center', - render: text => `${text}일` + render: applyDtDay => `${applyDtDay}일` }, { title: ( @@ -160,18 +173,17 @@ export default function OperationApprovalsTable(props) { 구역 수 ), - dataIndex: 'areaList', + dataIndex: 'zoneNo', align: 'center', width: '85px', - render: areaList => <>총{areaList.length}건 + render: zoneNo => <>총{zoneNo}건 }, { title: <>신청자, dataIndex: 'applyNm', width: '90px', align: 'center', - - render: text => (text ? text : '-') + render: applyNm => applyNm ?? '-' }, { title: ( @@ -180,13 +192,11 @@ export default function OperationApprovalsTable(props) { 구역 1 ), - dataIndex: 'areaList', + dataIndex: 'addr1', width: '90px', align: 'center', - render: areaList => { - return areaList.length <= 1 && areaList[0].addr1 - ? areaList[0].addr1 - : '-'; + render: addr1 => { + return addr1 ?? '-'; } }, { @@ -196,13 +206,11 @@ export default function OperationApprovalsTable(props) { 구역 2 ), - dataIndex: 'areaList', + dataIndex: 'addr2', width: '90px', align: 'center', - render: areaList => { - return areaList.length <= 1 && areaList[0].addr2 - ? areaList[0].addr2 - : '-'; + render: addr2 => { + return addr2 ?? '-'; } }, { @@ -213,13 +221,11 @@ export default function OperationApprovalsTable(props) { 주소 ), - dataIndex: 'areaList', + dataIndex: 'addr3', width: '100px', align: 'center', - render: areaList => { - return areaList.length <= 1 && areaList[0].addr3 - ? areaList[0].addr3 - : '-'; + render: addr3 => { + return addr3 ?? '-'; } }, { @@ -230,15 +236,11 @@ export default function OperationApprovalsTable(props) { 제한 표면 ), - dataIndex: 'areaList', + dataIndex: 'limitZoneNm', width: '100px', align: 'center', - render: areaList => { - return areaList.length <= 1 - ? areaList[0]?.limitZoneNm - ? areaList[0]?.limitZoneNm - : '-' - : '-'; + render: limitZoneNm => { + return limitZoneNm ?? '-'; // return areaList.length <= 1 ? areaList[0].limitZoneNm : '-'; } }, @@ -250,18 +252,16 @@ export default function OperationApprovalsTable(props) { (위도, 경도) ), - dataIndex: 'areaList', + dataIndex: 'planAreaSno', align: 'center', width: '90px', - render: areaList => { - return areaList.length <= 1 ? ( + render: (text, record) => { + return ( <> - {areaList[0].lat.toFixed(5)}, + {record.lat.toFixed(5)},
- {areaList[0].lon.toFixed(5)} + {record.lon.toFixed(5)} - ) : ( - '-' ); } }, @@ -276,8 +276,8 @@ export default function OperationApprovalsTable(props) { dataIndex: 'bufferZone', align: 'center', width: '80px', - render: (text, record) => { - return text ? text : '-'; + render: (bufferZone, record) => { + return bufferZone ?? '-'; } }, { @@ -292,8 +292,8 @@ export default function OperationApprovalsTable(props) { align: 'center', width: '80px', editable: true, - render: (text, record) => { - return text ? text : '-'; + render: (reqRadius, record) => { + return reqRadius ?? '-'; } }, { @@ -307,8 +307,8 @@ export default function OperationApprovalsTable(props) { dataIndex: 'allowRadius', align: 'center', width: '80px', - render: (text, record) => { - return text ? text : '-'; + render: (allowRadius, record) => { + return allowRadius ?? '-'; } }, { @@ -323,8 +323,8 @@ export default function OperationApprovalsTable(props) { align: 'center', width: '80px', editable: true, - render: (text, record) => { - return text ? text : '-'; + render: (fltElev, record) => { + return fltElev ?? '-'; } }, { @@ -339,8 +339,8 @@ export default function OperationApprovalsTable(props) { align: 'center', width: '80px', editable: true, - render: (text, record) => { - return text ? text : '-'; + render: (reqElev, record) => { + return reqElev ?? '-'; } }, { @@ -361,13 +361,11 @@ export default function OperationApprovalsTable(props) { { title: <>비행 목적, - dataIndex: 'areaList', + dataIndex: 'purpose', align: 'center', width: '110px', - render: areaList => { - return areaList.length <= 1 && areaList[0].purpose - ? areaList[0].purpose - : '-'; + render: purpose => { + return purpose ?? '-'; } }, { @@ -378,109 +376,39 @@ export default function OperationApprovalsTable(props) { 검토 결과 ), - dataIndex: 'areaList', + dataIndex: 'approvalCd', align: 'center', width: '140px', - render: areaList => { - const approvalCounts = areaList.reduce( - (counts, item) => { - if (item.approvalCd === 'U') { - counts.unapproved += 1; - } else if (item.approvalCd === 'S') { - counts.approved += 1; - } else if (item.approvalCd === 'C') { - counts.conditionalapproval += 1; - } else { - counts.pending += 1; - } - return counts; - }, - { unapproved: 0, approved: 0, pending: 0, conditionalapproval: 0 } - ); - + render: approvalCd => { return ( <> - {areaList.length > 1 ? ( - <> - 승인: {approvalCounts.approved}건
- 미승인: - {approvalCounts.pending}건 -
- 조건부승인:{approvalCounts.conditionalapproval}건
- 비대상: - {approvalCounts.unapproved}건 - - ) : ( - <> - {areaList[0].approvalCd === 'U' - ? '비대상' - : areaList[0].approvalCd === 'S' - ? '승인' - : areaList[0].approvalCd === 'C' - ? '조건부승인' - : '미승인'} - - )} + {approvalCd === 'U' + ? '비대상' + : approvalCd === 'S' + ? '승인' + : approvalCd === 'C' + ? '조건부승인' + : '미승인'} ); } }, { - title: <>더보기, - dataIndex: 'areaList', + title: ( + <> + 운항과 +
+ 확인결과 + + ), + dataIndex: 'reviewedType', align: 'center', width: '130px', - render: (areaList, record) => - areaList.length > 1 ? ( - - ) : ( - <>- - ) - }, - { - title: '편집', - dataIndex: 'planAreaSno', - align: 'center', - width: '110px', - render: (_, record) => { - const editable = isEditing(record); - return record.areaList.length <= 1 ? ( - editable ? ( - - save(record.key, 'outer')} - style={{ - marginRight: 8 - }} - > - 저장 - - cancel()}>취소 - - ) : ( - - ) - ) : ( - <>- + render: reviewedType => { + return ( + <> + <>{handlerReviewed(reviewedType, 'operation')} + ); } }, @@ -489,65 +417,35 @@ export default function OperationApprovalsTable(props) { <> 관제과
- 검토결과 + 확인결과 ), - dataIndex: 'areaList', + dataIndex: 'cReviewedType', align: 'center', width: '130px', - render: areaList => { - const approvalCounts = areaList.reduce( - (counts, item) => { - if (item.reviewedType === 'U') { - counts.unapproved += 1; - } else if (item.reviewedType === 'R') { - counts.reviewed += 1; - } else if (item.reviewedType === 'Q') { - counts.request += 1; - } else if (item.reviewedType === 'W') { - counts.wait += 1; - } else if (item.reviewedType === 'A') { - counts.rerequest += 1; - } else if (item.reviewedType === 'C') { - counts.cancel += 1; - } - return counts; - }, - { - reviewed: 0, - wait: 0, - cancel: 0, - unapproved: 0, - request: 0, - rerequest: 0 - } - ); - + render: cReviewedType => { return ( <> - {areaList.length > 1 ? ( - <> - 검토완료: {approvalCounts.reviewed}건
- 검토대기:{approvalCounts.wait}건
- 검토요청: - {approvalCounts.request}건 -
- 검토재요청: - {approvalCounts.rerequest}건 -
- 검토요청취소: - {approvalCounts.cancel}건 -
- 검토불필요: - {approvalCounts.unapproved}건 - - ) : ( - <>{handlerReviewed(areaList[0].reviewedType)} - )} + <>{handlerReviewed(cReviewedType, 'control')} ); } }, + { + title: ( + <> + 전송 +
+ 상태 + + ), + dataIndex: 'planAreaSno', + width: '100px', + align: 'center', + render: planAreaSno => { + return '미전송'; + } + }, { title: ( <> @@ -558,378 +456,21 @@ export default function OperationApprovalsTable(props) { dataIndex: 'reviewedReason', width: '70px', align: 'center', - - render: text => (text ? `${text}` : '-') + render: reviewedReason => reviewedReason ?? '-' }, { title: '비고', - dataIndex: 'areaList', + dataIndex: 'planAreaSno', align: 'center', width: '130px', - render: (areaList, record) => - areaList.length <= 1 ? ( - - ) : ( - <>- - ) + render: (areaList, record) => ( + + ) } ]; - // 확장된 테이블 데이터 - const expandedRowRender = record => { - const childColumns = [ - { - dataIndex: 'applyNo', - width: '60px', - align: 'center' - }, - { - dataIndex: 'applyDtMonth', - width: '70px', - align: 'center', - - render: text => `${text}월` - }, - { - dataIndex: 'applyDtDay', - width: '70px', - align: 'center', - render: text => `${text}일` - }, - { - dataIndex: 'zoneNo', - align: 'center', - width: '85px', - render: text => { - return <>구역{text}; - } - }, - { - dataIndex: 'applyNm', - align: 'center', - width: '90px', - render: text => { - return text ? text : '-'; - } - }, - { - dataIndex: 'addr1', - align: 'center', - width: '90px', - render: text => { - return text ? text : <>-; - } - }, - { - dataIndex: 'addr2', - align: 'center', - width: '90px', - render: text => { - return text ? text : <>-; - } - }, - { - dataIndex: 'addr3', - align: 'center', - width: '100px', - render: text => { - return text ? text : <>-; - } - }, - - { - dataIndex: 'limitZoneNm', - align: 'center', - width: '100px', - render: text => { - return text ? text : <>-; - } - }, - - { - align: 'center', - width: '90px', - dataIndex: ['lat', 'lon'], - render: (text, record) => { - const lat = record.lat; - const lon = record.lon; - return ( - <> - {lat.toFixed(5)},
- {lon.toFixed(5)} - - ); - } - }, - { - dataIndex: 'bufferZone', - align: 'center', - width: '80px', - render: text => { - return text ? text : <>-; - } - }, - { - dataIndex: 'reqRadius', - align: 'center', - editable: true, - width: '80px', - render: text => { - return text ? text : <>-; - } - }, - { - dataIndex: 'allowRadius', - align: 'center', - width: '80px', - render: text => { - return text ? text : <>-; - } - }, - { - dataIndex: 'fltElev', - align: 'center', - editable: true, - width: '80px', - render: text => { - return text ? text : <>-; - } - }, - { - dataIndex: 'reqElev', - align: 'center', - width: '80px', - editable: true, - - render: text => { - return text ? text : <>-; - } - }, - { - dataIndex: 'fltElevMax', - align: 'center', - width: '90px', - render: text => { - return text ? text : <>-; - } - }, - - { - dataIndex: 'purpose', - align: 'center', - width: '110px', - render: text => { - return text ? text : <>-; - } - }, - { - dataIndex: 'approvalCd', - align: 'center', - width: '140px', - render: text => ( - <> - {text === 'U' - ? '비대상' - : text === 'S' - ? '승인' - : text === 'C' - ? '조건부승인' - : '미승인'} - - ) - }, - { - dataIndex: '더보기', - align: 'center', - width: '130px', - render: text => { - return <>-; - } - }, - { - dataIndex: 'planAreaSno', - align: 'center', - width: '110px', - render: (_, record) => { - const editable = isEditing(record); - return editable ? ( - - save(record.key, 'inner')} - style={{ - marginRight: 8 - }} - > - 저장 - - cancel()}>취소 - - ) : ( - - ); - } - }, - { - dataIndex: 'reviewedType', - align: 'center', - width: '130px', - render: text => { - return text === 'U' - ? '검토불필요' - : text === 'R' - ? '검토완료' - : text === 'C' - ? '검토요청취소' - : text === 'Q' - ? '검토요청' - : text === 'A' - ? '검토재요청' - : '검토대기'; - } - }, - { - dataIndex: 'reviewedReason', - width: '70px', - align: 'center', - render: text => (text ? `${text}` : '-') - }, - { - title: '비고', - dataIndex: 'planAreaSno', - align: 'center', - width: '130px', - render: (_, record) => ( - - ) - } - ]; - - const data = []; - record.areaList.map((item, index) => { - data.push({ - key: `${item.planSno}`, - applyNo: item.applyNo, - applyDt: item.applyDt, - zoneNo: item.zoneNo, - lat: item.lat, - lon: item.lon, - applyDtDay: item.applyDtDay, - applyDtMonth: item.applyDtMonth, - bufferZone: item.bufferZone, - fltElev: item.fltElev, - rm: item.rm, - era: item.era, - reqRadius: item.reqRadius, - allowRadius: item.allowRadius, - fltElevMax: item.fltElevMax, - purpose: item.purpose, - reviewedType: item.reviewedType, - applyNm: item.applyNm, - dtl: item.dtl, - approvalCd: item.approvalCd, - planAreaSno: item.planAreaSno, - planSno: item.planSno, - reqElev: item.reqElev, - addr1: item.addr1, - addr2: item.addr2, - addr3: item.addr3, - reviewedReason: item.reviewedReason, - limitZoneNm: item?.limitZoneNm ? item?.limitZoneNm : '-' - }); - }); - - return ( - -
+ {editing ? ( + + {inputNode} + + ) : ( + children + )} +
{ - let className = ''; - if (record.approvalCd === 'S') { - className += 'flight-approval-row'; - } else if (record.approvalCd === 'F') { - className += 'flight-not-approval-row'; - } else if (record.approvalCd === 'C') { - className += 'flight-condition-approval-row editable-row'; - } else className; - - if (record.planAreaSno === props.selected[0]) { - if (props.selected.length <= 1) { - className += ' flight-approval-row-click'; - } - } - return className; - }} - size='small' - columns={childColumns.map(col => { - if (!col.editable) { - return col; - } - return { - ...col, - onCell: record => ({ - record, - inputType: - col.dataIndex === 'bufferZone' || col.dataIndex === 'fltElev' - ? 'number' - : 'text', - dataIndex: col.dataIndex, - title: col.title, - editing: isEditing(record) - }) - }; - })} - rowSelection={{ - ...childRowSelection, - selectedRowKeys: checkList.filter(key => - key.startsWith(`child_${record.planSno}_`) - ) - }} - dataSource={record.areaList.map((item, index) => ({ - ...item, - key: `child_${record.planSno}_${item.planAreaSno}` - }))} - pagination={false} - components={{ - body: { - cell: EditableCell - } - }} - onRow={record => ({ - onClick: event => { - if (editingKey !== '') { - return; // edit 상태면 이벤트 실행 안 함 - } - setSelectedRowKey(record.planAreaSno); - - // 이벤트 버블링을 막기 위해 클릭된 요소가 'Edit' 버튼인지 확인 - if ( - event.target.tagName !== 'BUTTON' && - event.target.tagName !== 'A' && - !event.target.closest('.ant-input') && // input 요소를 감지 - !event.target.closest('.ant-input-number') // inputNumber 요소를 감지 - ) { - handleInRowClick(record); - } - } - })} - showHeader={false} - rowHoverable={false} - /> - - ); - }; - // 테이블 데이터 const mergedColumns = columns.map(col => { if (!col.editable) { @@ -939,13 +480,10 @@ export default function OperationApprovalsTable(props) { ...col, onCell: record => ({ record, - inputType: - col.dataIndex === 'bufferZone' || col.dataIndex === 'fltElev' - ? 'number' - : 'text', + editable: col.editable, dataIndex: col.dataIndex, - title: col.title, - editing: isEditing(record) + title: col.title + // handleSave }) }; }); @@ -978,11 +516,6 @@ export default function OperationApprovalsTable(props) { setEditingKey(record.key); }; - // 취소 이벤트 - const cancel = () => { - setEditingKey(''); - }; - // 저장 이벤트 const save = async (key, type) => { try { @@ -1070,63 +603,10 @@ export default function OperationApprovalsTable(props) { return `${year}년 ${month}월 ${day}일 `; }; - // 테이블 행 확장 이벤트 - const handleExpand = record => { - const key = record.key; - const areaList = record.areaList; - const expanded = expandedRowKeys.includes(key); - - const keys = expanded - ? expandedRowKeys.filter(k => k !== key) - : [...expandedRowKeys, key]; - setExpandedRowKeys(keys); - - const datas = expanded - ? expandedRowDatas.filter(k => k.planSno !== record.planSno) - : [ - ...expandedRowDatas, - { - applyNo: record.applyNo, - planSno: record.planSno, - areaList - } - ]; - setExpandedRowDatas(datas); - - if (!expanded) { - props.handlerDetail(record.areaList); - } else { - if (props.selectedPlanSno === record.planSno) { - props.handlerDetail([]); - } - } - }; - // 테이블 행 클릭 이벤트 const handleRowClick = row => { - if (row.areaList.length > 1) return; - handlerOpenModal( - row.areaList[0].approvalCd, - row.areaList[0].fltElev, - row.areaList[0].fltElevMax - ); - props.handlerDetail([row.areaList[0]]); - }; - - // 확장된 테이블 행 클릭 이벤트 - const handleInRowClick = async row => { handlerOpenModal(row.approvalCd, row.fltElev, row.fltElevMax); - if (props.selectedPlanSno !== row.planSno) { - // 지도에 표출되지 않은 확장 테이블 행 클릭 - const areaList = expandedRowDatas.filter( - data => data.planSno === row.planSno - ); - await props.handlerDetail(areaList[0].areaList); - props.handlerDetail([row]); - } else { - // 지도에 표출된 확장 테이블 행 클릭 - await props.handlerDetail([row]); - } + props.handlerDetail([row]); }; // 유효성 검사 모달창 @@ -1141,131 +621,26 @@ export default function OperationApprovalsTable(props) { // } }; - // 부모 체크박스 + // 체크박스 헨들러 const rowSelection = { selectedRowKeys: checkList, - getCheckboxProps: record => { - const allChildrenReviewed = record.areaList.every( - child => child.reviewedType === 'R' - ); - return { - disabled: allChildrenReviewed, - name: record.key - }; - }, - onSelect: (record, selected, selectedRows, nativeEvent) => { - let newCheckList = [...checkList]; // const를 let으로 변경 - const key = record.key; - - if (key.startsWith('parent_')) { - const childKeys = record.areaList - .filter(child => child.reviewedType !== 'R') - .map(child => `child_${record.planSno}_${child.planAreaSno}`); - - if (selected) { - newCheckList.push(key, ...childKeys); - } else { - newCheckList = newCheckList.filter( - k => k !== key && !childKeys.includes(k) - ); - } - } - - updateCheckList(newCheckList); - }, - onSelectAll: (selected, selectedRows, changeRows) => { - const newCheckList = selected - ? laancAprvList.flatMap(parent => { - const parentKey = `parent_${parent.planSno}`; - - const childKeys = parent.areaList.map( - child => `child_${parent.planSno}_${child.planAreaSno}` - ); - - return parent.areaList.length > 0 ? [parentKey, ...childKeys] : []; - }) - : []; - - updateCheckList(newCheckList); + onChange: selectedRowKeys => { + setCheckList(selectedRowKeys); } }; - // updateCheckList 함수 수정 - const updateCheckList = list => { - const newList = [...list]; - - // 부모 키 업데이트 - laancAprvList.forEach(parent => { - const parentKey = `parent_${parent.planSno}`; - const selectableChildren = parent.areaList.filter( - child => child.reviewedType !== 'R' - ); - - const childKeys = selectableChildren.map( - child => `child_${parent.planSno}_${child.planAreaSno}` - ); - const allSelectableChildrenChecked = childKeys.every(key => - newList.includes(key) - ); - const someSelectableChildrenChecked = childKeys.some(key => - newList.includes(key) - ); - - if (allSelectableChildrenChecked && selectableChildren.length > 0) { - if (!newList.includes(parentKey)) { - newList.push(parentKey); - } - } else if (!someSelectableChildrenChecked) { - const index = newList.indexOf(parentKey); - if (index > -1) { - newList.splice(index, 1); - } - } - }); - - setCheckList(newList); - }; - - // 자식 테이블 체크박스 - const childRowSelection = { - selectedRowKeys: checkList.filter(key => key.startsWith('child_')), - getCheckboxProps: record => ({ - disabled: record.reviewedType === 'R', - name: record.key - }), - - onSelect: (record, selected, selectedRows) => { - const key = record.key; - let newCheckList = [...checkList]; - - if (selected) { - newCheckList.push(key); - } else { - newCheckList = newCheckList.filter(k => k !== key); - } - updateCheckList(newCheckList); - } - }; - - // 체크박스 업데이트 함수 + // 체크박스 분기 처리 const handleCheckbox = async type => { if (checkList.length === 0) { return handlerErrorModal('검토 실패', '검토 항목을 선택해주세요.', false); } - const planAreaSnoList = [ - ...new Set( - checkList - .filter(item => item.startsWith('child_')) - .map(item => item.split('_').pop()) - ) - ]; - const res = laancAprvList.flatMap(item => item.areaList.filter(area => - planAreaSnoList.includes(area.planAreaSno.toString()) + checkList.includes(area.planAreaSno.toString()) ) ); + console.log('>>', checkList); // 검토 요청, 재 요청 조건 const checkCondition = { @@ -1291,7 +666,7 @@ export default function OperationApprovalsTable(props) { Q: async () => { try { await dispatch( - updateLaancAprvReview({ planAreaSnoList, reviewedType: 'Q' }) + updateLaancAprvReview({ checkList, reviewedType: 'Q' }) ); props.handlerSearch( props.filterId, @@ -1402,77 +777,53 @@ export default function OperationApprovalsTable(props) { ignoreElements='.editable-input' // 이 클래스의 요소는 드래그 이벤트에서 제외 >
-
+
({ - ...item, - key: `parent_${item.planSno}` - }))} + dataSource={laancAprvList + .map((item, index) => + item.areaList.map((area, index) => ({ + ...area, + key: `${area.planAreaSno}` + })) + ) + .flat()} columns={mergedColumns} rowClassName={record => { let className = ''; - if (record.areaList.length <= 1) { - if (record?.areaList[0]?.approvalCd === 'S') { - className += 'flight-approval-row editable-row'; - } else if (record.areaList[0].approvalCd === 'F') { - className += - 'flight-not-approval-row editable-row'; - } else if (record.areaList[0].approvalCd === 'C') { - className += - 'flight-condition-approval-row editable-row'; - } else className += 'editable-row'; - } + if (record?.approvalCd === 'S') { + className += 'flight-approval-row editable-row'; + } else if (record.approvalCd === 'F') { + className += 'flight-not-approval-row editable-row'; + } else if (record.approvalCd === 'C') { + className += + 'flight-condition-approval-row editable-row'; + } else className += 'editable-row'; if (props.selected?.length > 0) { - if ( - record.areaList.length <= 1 && - record.areaList[0].planAreaSno === - props.selected[0] - ) { + if (record.planAreaSno === props.selected[0]) { className += ' flight-approval-row-click'; } } - if (expandedRowKeys.includes(record.key)) { - className += ' expanded-row'; - } - if (record.areaList.length > 1) { + if (record.length > 1) { className += 'expanded-tr'; } return className; }} onRow={record => ({ onClick: event => { - if (editingKey !== '') { - return; // edit 상태면 이벤트 실행 안 함 - } - setSelectedRowKey(record.planAreaSno); - // 이벤트 버블링을 막기 위해 클릭된 요소가 'Edit' 버튼인지 확인 - if ( - event.target.tagName !== 'BUTTON' && - event.target.tagName !== 'A' && - !event.target.closest('.ant-input') && // input 요소를 감지 - !event.target.closest('.ant-input-number') // inputNumber 요소를 감지 - ) { - handleRowClick(record); - } + handleRowClick(record); } })} rowSelection={{ ...rowSelection }} loading={laancAprvLoading} - expandable={{ - expandedRowRender, - expandedRowKeys: expandedRowKeys, - onExpand: (expanded, record) => handleExpand(record), - rowExpandable: record => record?.areaList?.length > 1 - }} scroll={{ x: 1700 }} @@ -1499,25 +850,40 @@ export default function OperationApprovalsTable(props) { } const EditableCell = ({ - editing, + editable, + children, dataIndex, - title, - inputType, record, - index, - children, + handleSave, ...restProps }) => { - const inputNode = inputType === 'number' ? : ; - return ( - - ); + const toggleEdit = () => { + form.setFieldsValue({ + [dataIndex]: record[dataIndex] + }); + }; + + const save = async () => { + try { + const values = await form.validateFields(); + handleSave({ ...record, ...values }); + } catch (errInfo) {} + }; + + let childNode = children; + + if (editable) { + childNode = ( + e.stopPropagation()} // 클릭 이벤트 전파 중지 + > + + + ); + } + + return ; }; diff --git a/src/containers/flight/OperationApprovalsContainer.js b/src/containers/flight/OperationApprovalsContainer.js index d7e7a88b..706c8c0b 100644 --- a/src/containers/flight/OperationApprovalsContainer.js +++ b/src/containers/flight/OperationApprovalsContainer.js @@ -23,6 +23,7 @@ import { MapControl } from '../../components/map/MapControl'; import { clientSetIsMapLoading } from '@src/redux/features/laanc/laancSlice'; import { clientMapInit } from '@src/redux/features/control/map/mapSlice'; import OperationApprovalsTable from '@src/components/flight/OperationApprovalsTable'; +import OldOperationApprovalsTable from '@src/components/flight/OldOperationApprovalsTable'; import { getLaancAprvList } from '@src/redux/features/laanc/laancThunk'; import dayjs from 'dayjs'; import { setLogout } from '@src/redux/features/account/auth/authThunk'; @@ -974,7 +975,7 @@ export default function OperationApprovalsContainer({ mode }) { handlerApprovalCd={handlerApprovalCd} pageName='operation' /> -
- {editing ? ( - - {inputNode} - - ) : ( - children - )} - {childNode}