sanguu516
2 months ago
15 changed files with 2682 additions and 18 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,154 @@
|
||||
import { useEffect, useState } from 'react'; |
||||
import dayjs from 'dayjs'; |
||||
import { useDispatch } from '@src/redux/store'; |
||||
import { getLaancAprvList } from '@src/redux/features/laanc/laancThunk'; |
||||
import ControlApprovalsTable from '@src/components/flight/ControlApprovalsTable'; |
||||
import NewFlightApprovalsReport from '@src/components/flight/NewFlightApprovalsReport'; |
||||
|
||||
function ControlRightMenuContainer() { |
||||
const [filter, setFilter] = useState(''); |
||||
const [startDate, setStartDate] = useState(dayjs().format('YYYY-MM-DD')); |
||||
const [endDate, setEndDate] = useState(); |
||||
const [selected, setSelected] = useState(null); |
||||
// 식별번호
|
||||
const [filterId, setFilterId] = useState(''); |
||||
|
||||
// 지역
|
||||
const [filterArea, setFilterArea] = useState(''); |
||||
|
||||
const dispatch = useDispatch(); |
||||
|
||||
useEffect(() => { |
||||
handlerOpnerPostMessage('initalState', null); |
||||
window.addEventListener('message', opnerMessage); |
||||
|
||||
return () => { |
||||
window.removeEventListener('message', opnerMessage); |
||||
}; |
||||
}, []); |
||||
|
||||
const opnerMessage = e => { |
||||
const { type } = e.data; |
||||
const { payload } = e.data; |
||||
|
||||
switch (type) { |
||||
case 'initalState': |
||||
setFilter(payload.filter); |
||||
setSelected(payload.selected); |
||||
setStartDate(payload.startDate); |
||||
setEndDate(payload.endDate); |
||||
|
||||
return; |
||||
// case 'handlerSearchRs':
|
||||
// console.log(payload.filter);
|
||||
// setFilter(payload.filter);
|
||||
|
||||
// return;
|
||||
default: |
||||
break; |
||||
} |
||||
}; |
||||
|
||||
const handlerOpnerPostMessage = (type, payload) => { |
||||
switch (type) { |
||||
case 'initalState': |
||||
window.opener.postMessage({ type, payload }); |
||||
return; |
||||
case 'search': |
||||
window.opener.postMessage({ type, payload }); |
||||
return; |
||||
case 'detail': |
||||
window.opener.postMessage({ type, payload }); |
||||
return; |
||||
case 'closedSync': |
||||
window.opener.postMessage({ type, payload }); |
||||
default: |
||||
break; |
||||
} |
||||
}; |
||||
|
||||
const handlerSearch = (search, searchDate, filterArea) => { |
||||
setStartDate(searchDate.startDate); |
||||
setEndDate(searchDate.endDate); |
||||
setFilter(search); |
||||
if ( |
||||
search != '' && |
||||
(search === '승인' || search === '미승인' || search === '비대상') |
||||
) { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea, |
||||
approvalCd: search === '승인' ? 'S' : search === '미승인' ? 'F' : 'U' |
||||
}) |
||||
); |
||||
} else if (search != '') { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea, |
||||
applyNo: search |
||||
}) |
||||
); |
||||
} else { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea |
||||
}) |
||||
); |
||||
} |
||||
localStorage.setItem( |
||||
'popupState', |
||||
JSON.stringify({ |
||||
filter: search, |
||||
selected: filterArea, |
||||
startDate: searchDate.startDate, |
||||
endDate: searchDate.endDate |
||||
}) |
||||
); |
||||
handlerOpnerPostMessage('search', { search, searchDate, filterArea }); |
||||
}; |
||||
|
||||
const handlerDetail = area => { |
||||
setSelected(area.planAreaSno); |
||||
handlerOpnerPostMessage('detail', { area }); |
||||
}; |
||||
|
||||
const handleBeforeUnload = () => { |
||||
handlerOpnerPostMessage('closedSync', ''); |
||||
}; |
||||
|
||||
return ( |
||||
<div className='right-menu active'> |
||||
<div |
||||
className='right-layer active flight-approval-layer' |
||||
style={{ width: '100vw' }} |
||||
> |
||||
<div className='layer-content'> |
||||
<NewFlightApprovalsReport |
||||
handlerSearch={handlerSearch} |
||||
filterId={filterId} |
||||
setFilterId={setFilterId} |
||||
filterArea={filterArea} |
||||
setFilterArea={setFilterArea} |
||||
/> |
||||
<ControlApprovalsTable |
||||
filter={filter} |
||||
filterArea={filterArea} |
||||
filterId={filterId} |
||||
startDate={startDate} |
||||
endDate={endDate} |
||||
selected={selected} |
||||
handlerDetail={handlerDetail} |
||||
/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
export default ControlRightMenuContainer; |
@ -0,0 +1,655 @@
|
||||
import { useEffect, useRef, useState, lazy, Suspense } from 'react'; |
||||
import { useDispatch, useSelector } from '@src/redux/store'; |
||||
import NewFlightApprovalsReport from '../../components/flight/NewFlightApprovalsReport'; |
||||
import { |
||||
InitFeature, |
||||
handlerFitBounds, |
||||
handlerGetCircleCoord, |
||||
flightlayerWayPoint, |
||||
flightlayerPolyline, |
||||
flightlayerPolygon, |
||||
flightlayerBuffer |
||||
} from '../../utility/MapUtils'; |
||||
import { useHistory } from 'react-router-dom'; |
||||
import useMapType from '@hooks/useMapType'; |
||||
import { clientSaveAreaCoordinateList } from '@src/redux/features/laanc/laancSlice'; |
||||
import { MapControl } from '../../components/map/MapControl'; |
||||
import { clientSetIsMapLoading } from '@src/redux/features/laanc/laancSlice'; |
||||
import { clientMapInit } from '@src/redux/features/control/map/mapSlice'; |
||||
import ControlApprovalsTable from '@src/components/flight/ControlApprovalsTable'; |
||||
import { getLaancAprvList } from '@src/redux/features/laanc/laancThunk'; |
||||
import dayjs from 'dayjs'; |
||||
import { setLogout } from '@src/redux/features/account/auth/authThunk'; |
||||
import logo from '../../assets/images/logo/kac_logo_ icon.svg'; |
||||
import { AiOutlinePoweroff } from 'react-icons/ai'; |
||||
import WebsocketClient from '../../components/websocket/WebsocketClient'; |
||||
import { |
||||
Card, |
||||
ButtonGroup, |
||||
Button, |
||||
Modal, |
||||
ModalHeader, |
||||
ModalBody, |
||||
ModalFooter, |
||||
Table |
||||
} from '@component/ui'; |
||||
import { FiUsers, FiFileText } from 'react-icons/fi'; |
||||
|
||||
export default function ControlApprovalsContainer({ mode }) { |
||||
const dispatch = useDispatch(); |
||||
const history = useHistory(); |
||||
|
||||
const [selected, setSelected] = useState(null); |
||||
const [isMapLoading, setIsMapLoading] = useState(false); |
||||
// 비행구역 그리기
|
||||
|
||||
const [filter, setFilter] = useState(''); |
||||
// 지도
|
||||
const [mapObject, setMapObject] = useState(); |
||||
const [mapType, setMapType] = useMapType(); |
||||
const { areaCoordList, isOpenModal } = useSelector(state => state.laancState); |
||||
|
||||
const [startDate, setStartDate] = useState(dayjs().format('YYYY-MM-DD')); |
||||
const [endDate, setEndDate] = useState(dayjs().format('YYYY-MM-DD')); |
||||
|
||||
// 식별번호
|
||||
const [filterId, setFilterId] = useState(''); |
||||
|
||||
// 지역
|
||||
const [filterArea, setFilterArea] = useState('전체'); |
||||
|
||||
// 시군구
|
||||
const [district, setDistrict] = useState('전체'); |
||||
// 미니맵 레이어
|
||||
const [previewLayer, setPreviewLayer] = useState(); |
||||
|
||||
const map = useSelector(state => state.mapState.map); |
||||
|
||||
// popup
|
||||
const [isPopup, setIsPopup] = useState(false); |
||||
const [popup, setPopup] = useState(null); |
||||
const popupRef = useRef(null); |
||||
const rightMenuRef = useRef(null); |
||||
const savedRightMenuRef = useRef(null); |
||||
const [clientX, setClientX] = useState(0); |
||||
|
||||
const previewGeo2 = { |
||||
type: 'FeatureCollection', |
||||
features: [] |
||||
}; |
||||
|
||||
useEffect(() => { |
||||
const searchStDt = dayjs().format('YYYY-MM-DD'); |
||||
const searchEndDt = dayjs().format('YYYY-MM-DD'); |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt, |
||||
searchEndDt |
||||
}) |
||||
); |
||||
}, []); |
||||
|
||||
useEffect(() => { |
||||
if (areaCoordList.length !== 0) { |
||||
handlerPreviewDraw(); |
||||
} |
||||
}, [areaCoordList]); |
||||
|
||||
useEffect(() => { |
||||
if (map) { |
||||
window._mapbox = map; |
||||
let mapInstance = mode === 'container' ? map : window.opener._mapbox; |
||||
setMapObject(mapInstance); |
||||
} |
||||
}, [map]); |
||||
|
||||
useEffect(() => { |
||||
const childMessage = e => { |
||||
if (e.data.type) { |
||||
const { type } = e.data; |
||||
const { payload } = e.data; |
||||
console.log(payload); |
||||
switch (type) { |
||||
case 'initalState': |
||||
popupRef.current.postMessage({ |
||||
type: 'initalState', |
||||
payload: { |
||||
filter, |
||||
selected, |
||||
startDate, |
||||
endDate |
||||
} |
||||
}); |
||||
|
||||
return; |
||||
case 'search': |
||||
const { search, searchDate, filterArea } = payload; |
||||
|
||||
handlerSearch(search, searchDate, filterArea); |
||||
return; |
||||
case 'detail': |
||||
const { area } = payload; |
||||
handlerDetail(area); |
||||
return; |
||||
case 'closedSync': |
||||
popupRef.current.close(); |
||||
// localStorage.removeItem('popupState');
|
||||
return; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
let timer; |
||||
if (rightMenuRef.current) { |
||||
savedRightMenuRef.current = rightMenuRef.current.getBoundingClientRect(); |
||||
} |
||||
if (popup) { |
||||
timer = setInterval(() => { |
||||
if (popup.closed) { |
||||
setIsPopup(false); |
||||
clearInterval(timer); |
||||
} |
||||
|
||||
if (savedRightMenuRef.current) { |
||||
const popupX = popup.screenX; |
||||
|
||||
const parentX = window.screenX + savedRightMenuRef.current.left - 70; |
||||
|
||||
const parentWidth = savedRightMenuRef.current.width; |
||||
|
||||
if (popupX >= parentX && popupX <= parentX + parentWidth) { |
||||
popup.close(); |
||||
setIsPopup(false); |
||||
clearInterval(timer); |
||||
} |
||||
} |
||||
}, 500); // 1초마다 체크
|
||||
} |
||||
window.addEventListener('message', childMessage); |
||||
|
||||
return () => { |
||||
clearInterval(timer); |
||||
window.removeEventListener('message', childMessage); |
||||
}; |
||||
}, [popup]); |
||||
|
||||
useEffect(() => { |
||||
const handleBeforeUnload = e => { |
||||
localStorage.removeItem('persist:root'); |
||||
|
||||
if (popupRef.current) { |
||||
popupRef.current.close(); |
||||
} |
||||
}; |
||||
|
||||
window.addEventListener('beforeunload', handleBeforeUnload); |
||||
|
||||
return () => { |
||||
window.removeEventListener('beforeunload', handleBeforeUnload); |
||||
}; |
||||
}, []); |
||||
|
||||
const handleDragEnd = e => { |
||||
setIsPopup(true); |
||||
const el = document.querySelector('.flight-approval-layer'); |
||||
|
||||
const popupWidth = el.offsetWidth; // 팝업의 너비
|
||||
const popupHeight = el.offsetHeight; // 팝업의 너비
|
||||
|
||||
// const popupX = window.screenX + window.outerWidth - e.clientX; // 오른쪽 끝에 띄우기
|
||||
// const popupY = Math.round(
|
||||
// window.screenY + window.outerHeight / 2 - popupHeight / 2
|
||||
// );
|
||||
// setClientX(window.screenX + window.outerWidth - e.clientX);
|
||||
const popupX = |
||||
window.screenX + |
||||
(window.outerWidth - popupWidth) / 2 + |
||||
e.clientX - |
||||
window.innerWidth / 2; // 드래그 끝나는 지점
|
||||
const popupY = Math.round( |
||||
window.screenY + window.outerHeight / 2 - popupHeight / 2 |
||||
); |
||||
const option = `width=${popupWidth},height=${popupHeight},left=${popupX},top=${popupY}`; |
||||
popupRef.current = window.open('/rightMenu', 'NewWindow', option); |
||||
// setPopupOption(option);
|
||||
setPopup(popupRef.current); |
||||
}; |
||||
|
||||
const handlerSearch = (search, searchDate, filterArea) => { |
||||
setStartDate(searchDate.startDate); |
||||
setEndDate(searchDate.endDate); |
||||
|
||||
if ( |
||||
search != '' && |
||||
(search === '승인' || |
||||
search === '미승인' || |
||||
search === '비대상' || |
||||
search === '조건부승인') |
||||
) { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea, |
||||
approvalCd: |
||||
search === '승인' |
||||
? 'S' |
||||
: search === '미승인' |
||||
? 'F' |
||||
: search === '조건부승인' |
||||
? 'C' |
||||
: 'U' |
||||
}) |
||||
); |
||||
} else if (search != '') { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea, |
||||
applyNo: search |
||||
}) |
||||
); |
||||
} else { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea |
||||
}) |
||||
); |
||||
} |
||||
// );
|
||||
setFilter(search); |
||||
if (popup) { |
||||
popupRef.current.postMessage({ |
||||
type: 'handlerSearchRs', |
||||
payload: { search } |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
const handlerDetail = area => { |
||||
setSelected(area.planAreaSno); |
||||
|
||||
dispatch(clientSaveAreaCoordinateList([area])); |
||||
|
||||
handlerMapInit(); |
||||
}; |
||||
|
||||
const handlerMapInit = () => { |
||||
let mapInstance = mode === 'container' ? map : window.opener._mapbox; |
||||
|
||||
if (mapInstance.getSource('preview')) { |
||||
} else { |
||||
mapInstance.addSource('preview', { |
||||
type: 'geojson', |
||||
data: previewGeo2 |
||||
}); |
||||
mapInstance.addLayer(flightlayerWayPoint('preview')); |
||||
mapInstance.addLayer(flightlayerBuffer('preview')); |
||||
mapInstance.addLayer(flightlayerPolygon('preview')); |
||||
mapInstance.addLayer(flightlayerPolyline('preview')); |
||||
} |
||||
|
||||
dispatch(clientSetIsMapLoading(true)); |
||||
const preview = mapInstance.getSource('preview'); |
||||
|
||||
if (preview) setPreviewLayer(preview); |
||||
|
||||
setIsMapLoading(true); |
||||
|
||||
setMapObject(mapInstance); |
||||
|
||||
dispatch(clientMapInit(mapInstance)); |
||||
}; |
||||
|
||||
const handlerPreviewDraw = () => { |
||||
if (areaCoordList.length > 0) { |
||||
const areas = areaCoordList[0]; |
||||
|
||||
previewGeo2.features = []; |
||||
|
||||
let fitZoomPaths = []; |
||||
const radius = areas.bufferZone; |
||||
const circleCoords = handlerGetCircleCoord( |
||||
[areas.lon, areas.lat], |
||||
radius |
||||
); |
||||
|
||||
const circle = InitFeature('Polygon', 'CIRCLE'); |
||||
circle.properties.center = [areas.lon, areas.lat]; |
||||
circle.geometry.coordinates = circleCoords; |
||||
|
||||
previewGeo2.features.push(circle); |
||||
|
||||
mapObject?.setCenter(circle.properties.center); |
||||
|
||||
fitZoomPaths = circleCoords[0]; |
||||
|
||||
handlerFitBounds(mapObject, fitZoomPaths, 400, 'CIRCLE', 'flight'); |
||||
|
||||
// mapObject.setPaintProperty('waypoint', 'circle-radius', 10);
|
||||
mapObject?.getSource('preview').setData(previewGeo2); |
||||
} |
||||
}; |
||||
|
||||
const handlerLogout = async () => { |
||||
const { payload } = await dispatch(setLogout()); |
||||
|
||||
if (payload === 'SUCCESS') { |
||||
history.replace('/account/login'); |
||||
} |
||||
}; |
||||
|
||||
const [testModal, setTestModal] = useState(false); |
||||
|
||||
return ( |
||||
<> |
||||
<div className='map' style={{ width: '100%' }}> |
||||
<div className='test_modal'> |
||||
<Button |
||||
color='primary' |
||||
size='lg' |
||||
onClick={() => setTestModal(!testModal)} |
||||
> |
||||
test |
||||
</Button> |
||||
</div> |
||||
<div> |
||||
<Modal |
||||
isOpen={testModal} |
||||
toggle={() => setTestModal(!testModal)} |
||||
className='modal-dialog-centered modal-lg' |
||||
> |
||||
<ModalHeader toggle={() => setTestModal(!testModal)}> |
||||
유효성 검사 상세보기 |
||||
</ModalHeader> |
||||
<ModalBody className='onestop-validation'> |
||||
<div className='pal-table-warp'> |
||||
<h5 className='table-ti'> |
||||
<FiFileText /> |
||||
기체 정보 |
||||
</h5> |
||||
<div className='pal-table'> |
||||
<Table responsive> |
||||
<thead> |
||||
<tr> |
||||
<th width='80'>No</th> |
||||
<th width='120'>유효성 검사</th> |
||||
<th>제작 번호</th> |
||||
<th>기체 용도</th> |
||||
<th>기체 중량</th> |
||||
<th>보험 가입</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td width='80'>1</td> |
||||
<td width='120' className='success'> |
||||
성공 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='success'>가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
</tbody> |
||||
</Table> |
||||
</div> |
||||
</div> |
||||
<div className='pal-table-warp'> |
||||
<h5 className='table-ti'> |
||||
<FiUsers /> |
||||
조종자 정보 |
||||
</h5> |
||||
<div className='pal-table'> |
||||
<Table> |
||||
<thead> |
||||
<tr> |
||||
<th width='80'>No</th> |
||||
<th width='120'>유효성 검사</th> |
||||
<th>이름</th> |
||||
<th>생년 월일</th> |
||||
<th>자격 번호</th> |
||||
<th>조종 자격</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td width='80'>1</td> |
||||
<td width='120' className='success'> |
||||
성공 |
||||
</td> |
||||
<td>홍*동</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='success'>여</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
</tbody> |
||||
</Table> |
||||
</div> |
||||
</div> |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color='primary' onClick={() => setTestModal(!testModal)}> |
||||
확인 |
||||
</Button>{' '} |
||||
</ModalFooter> |
||||
</Modal> |
||||
</div> |
||||
<div className='main-data'> |
||||
<div className='data-box-btn'> |
||||
<Card> |
||||
<div className='data-box-btn-list'> |
||||
<h4>지도유형</h4> |
||||
<div className='map-btn'> |
||||
<ButtonGroup> |
||||
<Button |
||||
color={mapType === 'TERRAIN' ? 'primary' : ''} |
||||
onClick={e => setMapType('TERRAIN')} |
||||
> |
||||
지형지도 |
||||
</Button> |
||||
<Button |
||||
color={mapType === 'SATELLITE' ? 'primary' : ''} |
||||
onClick={e => setMapType('SATELLITE')} |
||||
> |
||||
위성지도 |
||||
</Button> |
||||
<Button |
||||
color={mapType === '2D' ? 'primary' : ''} |
||||
onClick={e => setMapType('2D')} |
||||
> |
||||
2D |
||||
</Button> |
||||
</ButtonGroup> |
||||
</div> |
||||
</div> |
||||
<div className='data-box-btn-list'> |
||||
<h4>거리측정</h4> |
||||
<div className='map-btn'> |
||||
<ButtonGroup> |
||||
<Button primary>직선</Button> |
||||
<Button primary>다각형</Button> |
||||
</ButtonGroup> |
||||
</div> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</div> |
||||
<MapControl /> |
||||
</div> |
||||
<div className='left-menu'> |
||||
<h1 className='logo'> |
||||
<img src={logo} width='80' /> |
||||
<span>UTM</span> |
||||
</h1> |
||||
<ul className='left-menu-footer'> |
||||
<li> |
||||
<AiOutlinePoweroff |
||||
size={25} |
||||
className='logout-btn' |
||||
onClick={handlerLogout} |
||||
/> |
||||
</li> |
||||
<li> |
||||
<WebsocketClient /> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
{!isPopup && ( |
||||
<div className='right-menu active flight-approval-layer-wrap'> |
||||
<div |
||||
className='right-layer active flight-approval-layer' |
||||
ref={rightMenuRef} |
||||
> |
||||
<div className='layer-content'> |
||||
<NewFlightApprovalsReport |
||||
handlerSearch={handlerSearch} |
||||
handleDragEnd={handleDragEnd} |
||||
filterId={filterId} |
||||
setFilterId={setFilterId} |
||||
district={district} |
||||
setDistrict={setDistrict} |
||||
filterArea={filterArea} |
||||
setFilterArea={setFilterArea} |
||||
/> |
||||
<ControlApprovalsTable |
||||
filter={filter} |
||||
filterArea={filterArea} |
||||
filterId={filterId} |
||||
startDate={startDate} |
||||
endDate={endDate} |
||||
selected={selected} |
||||
handlerDetail={handlerDetail} |
||||
/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
)} |
||||
</> |
||||
); |
||||
} |
@ -0,0 +1,655 @@
|
||||
import { useEffect, useRef, useState, lazy, Suspense } from 'react'; |
||||
import { useDispatch, useSelector } from '@src/redux/store'; |
||||
import NewFlightApprovalsReport from '../../components/flight/NewFlightApprovalsReport'; |
||||
import { |
||||
InitFeature, |
||||
handlerFitBounds, |
||||
handlerGetCircleCoord, |
||||
flightlayerWayPoint, |
||||
flightlayerPolyline, |
||||
flightlayerPolygon, |
||||
flightlayerBuffer |
||||
} from '../../utility/MapUtils'; |
||||
import { useHistory } from 'react-router-dom'; |
||||
import useMapType from '@hooks/useMapType'; |
||||
import { clientSaveAreaCoordinateList } from '@src/redux/features/laanc/laancSlice'; |
||||
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 { getLaancAprvList } from '@src/redux/features/laanc/laancThunk'; |
||||
import dayjs from 'dayjs'; |
||||
import { setLogout } from '@src/redux/features/account/auth/authThunk'; |
||||
import logo from '../../assets/images/logo/kac_logo_ icon.svg'; |
||||
import { AiOutlinePoweroff } from 'react-icons/ai'; |
||||
import WebsocketClient from '../../components/websocket/WebsocketClient'; |
||||
import { |
||||
Card, |
||||
ButtonGroup, |
||||
Button, |
||||
Modal, |
||||
ModalHeader, |
||||
ModalBody, |
||||
ModalFooter, |
||||
Table |
||||
} from '@component/ui'; |
||||
import { FiUsers, FiFileText } from 'react-icons/fi'; |
||||
|
||||
export default function OperationApprovalsContainer({ mode }) { |
||||
const dispatch = useDispatch(); |
||||
const history = useHistory(); |
||||
|
||||
const [selected, setSelected] = useState(null); |
||||
const [isMapLoading, setIsMapLoading] = useState(false); |
||||
// 비행구역 그리기
|
||||
|
||||
const [filter, setFilter] = useState(''); |
||||
// 지도
|
||||
const [mapObject, setMapObject] = useState(); |
||||
const [mapType, setMapType] = useMapType(); |
||||
const { areaCoordList, isOpenModal } = useSelector(state => state.laancState); |
||||
|
||||
const [startDate, setStartDate] = useState(dayjs().format('YYYY-MM-DD')); |
||||
const [endDate, setEndDate] = useState(dayjs().format('YYYY-MM-DD')); |
||||
|
||||
// 식별번호
|
||||
const [filterId, setFilterId] = useState(''); |
||||
|
||||
// 지역
|
||||
const [filterArea, setFilterArea] = useState('전체'); |
||||
|
||||
// 시군구
|
||||
const [district, setDistrict] = useState('전체'); |
||||
// 미니맵 레이어
|
||||
const [previewLayer, setPreviewLayer] = useState(); |
||||
|
||||
const map = useSelector(state => state.mapState.map); |
||||
|
||||
// popup
|
||||
const [isPopup, setIsPopup] = useState(false); |
||||
const [popup, setPopup] = useState(null); |
||||
const popupRef = useRef(null); |
||||
const rightMenuRef = useRef(null); |
||||
const savedRightMenuRef = useRef(null); |
||||
const [clientX, setClientX] = useState(0); |
||||
|
||||
const previewGeo2 = { |
||||
type: 'FeatureCollection', |
||||
features: [] |
||||
}; |
||||
|
||||
useEffect(() => { |
||||
const searchStDt = dayjs().format('YYYY-MM-DD'); |
||||
const searchEndDt = dayjs().format('YYYY-MM-DD'); |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt, |
||||
searchEndDt |
||||
}) |
||||
); |
||||
}, []); |
||||
|
||||
useEffect(() => { |
||||
if (areaCoordList.length !== 0) { |
||||
handlerPreviewDraw(); |
||||
} |
||||
}, [areaCoordList]); |
||||
|
||||
useEffect(() => { |
||||
if (map) { |
||||
window._mapbox = map; |
||||
let mapInstance = mode === 'container' ? map : window.opener._mapbox; |
||||
setMapObject(mapInstance); |
||||
} |
||||
}, [map]); |
||||
|
||||
useEffect(() => { |
||||
const childMessage = e => { |
||||
if (e.data.type) { |
||||
const { type } = e.data; |
||||
const { payload } = e.data; |
||||
console.log(payload); |
||||
switch (type) { |
||||
case 'initalState': |
||||
popupRef.current.postMessage({ |
||||
type: 'initalState', |
||||
payload: { |
||||
filter, |
||||
selected, |
||||
startDate, |
||||
endDate |
||||
} |
||||
}); |
||||
|
||||
return; |
||||
case 'search': |
||||
const { search, searchDate, filterArea } = payload; |
||||
|
||||
handlerSearch(search, searchDate, filterArea); |
||||
return; |
||||
case 'detail': |
||||
const { area } = payload; |
||||
handlerDetail(area); |
||||
return; |
||||
case 'closedSync': |
||||
popupRef.current.close(); |
||||
// localStorage.removeItem('popupState');
|
||||
return; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
let timer; |
||||
if (rightMenuRef.current) { |
||||
savedRightMenuRef.current = rightMenuRef.current.getBoundingClientRect(); |
||||
} |
||||
if (popup) { |
||||
timer = setInterval(() => { |
||||
if (popup.closed) { |
||||
setIsPopup(false); |
||||
clearInterval(timer); |
||||
} |
||||
|
||||
if (savedRightMenuRef.current) { |
||||
const popupX = popup.screenX; |
||||
|
||||
const parentX = window.screenX + savedRightMenuRef.current.left - 70; |
||||
|
||||
const parentWidth = savedRightMenuRef.current.width; |
||||
|
||||
if (popupX >= parentX && popupX <= parentX + parentWidth) { |
||||
popup.close(); |
||||
setIsPopup(false); |
||||
clearInterval(timer); |
||||
} |
||||
} |
||||
}, 500); // 1초마다 체크
|
||||
} |
||||
window.addEventListener('message', childMessage); |
||||
|
||||
return () => { |
||||
clearInterval(timer); |
||||
window.removeEventListener('message', childMessage); |
||||
}; |
||||
}, [popup]); |
||||
|
||||
useEffect(() => { |
||||
const handleBeforeUnload = e => { |
||||
localStorage.removeItem('persist:root'); |
||||
|
||||
if (popupRef.current) { |
||||
popupRef.current.close(); |
||||
} |
||||
}; |
||||
|
||||
window.addEventListener('beforeunload', handleBeforeUnload); |
||||
|
||||
return () => { |
||||
window.removeEventListener('beforeunload', handleBeforeUnload); |
||||
}; |
||||
}, []); |
||||
|
||||
const handleDragEnd = e => { |
||||
setIsPopup(true); |
||||
const el = document.querySelector('.flight-approval-layer'); |
||||
|
||||
const popupWidth = el.offsetWidth; // 팝업의 너비
|
||||
const popupHeight = el.offsetHeight; // 팝업의 너비
|
||||
|
||||
// const popupX = window.screenX + window.outerWidth - e.clientX; // 오른쪽 끝에 띄우기
|
||||
// const popupY = Math.round(
|
||||
// window.screenY + window.outerHeight / 2 - popupHeight / 2
|
||||
// );
|
||||
// setClientX(window.screenX + window.outerWidth - e.clientX);
|
||||
const popupX = |
||||
window.screenX + |
||||
(window.outerWidth - popupWidth) / 2 + |
||||
e.clientX - |
||||
window.innerWidth / 2; // 드래그 끝나는 지점
|
||||
const popupY = Math.round( |
||||
window.screenY + window.outerHeight / 2 - popupHeight / 2 |
||||
); |
||||
const option = `width=${popupWidth},height=${popupHeight},left=${popupX},top=${popupY}`; |
||||
popupRef.current = window.open('/rightMenu', 'NewWindow', option); |
||||
// setPopupOption(option);
|
||||
setPopup(popupRef.current); |
||||
}; |
||||
|
||||
const handlerSearch = (search, searchDate, filterArea) => { |
||||
setStartDate(searchDate.startDate); |
||||
setEndDate(searchDate.endDate); |
||||
|
||||
if ( |
||||
search != '' && |
||||
(search === '승인' || |
||||
search === '미승인' || |
||||
search === '비대상' || |
||||
search === '조건부승인') |
||||
) { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea, |
||||
approvalCd: |
||||
search === '승인' |
||||
? 'S' |
||||
: search === '미승인' |
||||
? 'F' |
||||
: search === '조건부승인' |
||||
? 'C' |
||||
: 'U' |
||||
}) |
||||
); |
||||
} else if (search != '') { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea, |
||||
applyNo: search |
||||
}) |
||||
); |
||||
} else { |
||||
dispatch( |
||||
getLaancAprvList({ |
||||
searchStDt: searchDate.startDate, |
||||
searchEndDt: searchDate.endDate, |
||||
selectZone: filterArea |
||||
}) |
||||
); |
||||
} |
||||
// );
|
||||
setFilter(search); |
||||
if (popup) { |
||||
popupRef.current.postMessage({ |
||||
type: 'handlerSearchRs', |
||||
payload: { search } |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
const handlerDetail = area => { |
||||
setSelected(area.planAreaSno); |
||||
|
||||
dispatch(clientSaveAreaCoordinateList([area])); |
||||
|
||||
handlerMapInit(); |
||||
}; |
||||
|
||||
const handlerMapInit = () => { |
||||
let mapInstance = mode === 'container' ? map : window.opener._mapbox; |
||||
|
||||
if (mapInstance.getSource('preview')) { |
||||
} else { |
||||
mapInstance.addSource('preview', { |
||||
type: 'geojson', |
||||
data: previewGeo2 |
||||
}); |
||||
mapInstance.addLayer(flightlayerWayPoint('preview')); |
||||
mapInstance.addLayer(flightlayerBuffer('preview')); |
||||
mapInstance.addLayer(flightlayerPolygon('preview')); |
||||
mapInstance.addLayer(flightlayerPolyline('preview')); |
||||
} |
||||
|
||||
dispatch(clientSetIsMapLoading(true)); |
||||
const preview = mapInstance.getSource('preview'); |
||||
|
||||
if (preview) setPreviewLayer(preview); |
||||
|
||||
setIsMapLoading(true); |
||||
|
||||
setMapObject(mapInstance); |
||||
|
||||
dispatch(clientMapInit(mapInstance)); |
||||
}; |
||||
|
||||
const handlerPreviewDraw = () => { |
||||
if (areaCoordList.length > 0) { |
||||
const areas = areaCoordList[0]; |
||||
|
||||
previewGeo2.features = []; |
||||
|
||||
let fitZoomPaths = []; |
||||
const radius = areas.bufferZone; |
||||
const circleCoords = handlerGetCircleCoord( |
||||
[areas.lon, areas.lat], |
||||
radius |
||||
); |
||||
|
||||
const circle = InitFeature('Polygon', 'CIRCLE'); |
||||
circle.properties.center = [areas.lon, areas.lat]; |
||||
circle.geometry.coordinates = circleCoords; |
||||
|
||||
previewGeo2.features.push(circle); |
||||
|
||||
mapObject?.setCenter(circle.properties.center); |
||||
|
||||
fitZoomPaths = circleCoords[0]; |
||||
|
||||
handlerFitBounds(mapObject, fitZoomPaths, 400, 'CIRCLE', 'flight'); |
||||
|
||||
// mapObject.setPaintProperty('waypoint', 'circle-radius', 10);
|
||||
mapObject?.getSource('preview').setData(previewGeo2); |
||||
} |
||||
}; |
||||
|
||||
const handlerLogout = async () => { |
||||
const { payload } = await dispatch(setLogout()); |
||||
|
||||
if (payload === 'SUCCESS') { |
||||
history.replace('/account/login'); |
||||
} |
||||
}; |
||||
|
||||
const [testModal, setTestModal] = useState(false); |
||||
|
||||
return ( |
||||
<> |
||||
<div className='map' style={{ width: '100%' }}> |
||||
<div className='test_modal'> |
||||
<Button |
||||
color='primary' |
||||
size='lg' |
||||
onClick={() => setTestModal(!testModal)} |
||||
> |
||||
test |
||||
</Button> |
||||
</div> |
||||
<div> |
||||
<Modal |
||||
isOpen={testModal} |
||||
toggle={() => setTestModal(!testModal)} |
||||
className='modal-dialog-centered modal-lg' |
||||
> |
||||
<ModalHeader toggle={() => setTestModal(!testModal)}> |
||||
유효성 검사 상세보기 |
||||
</ModalHeader> |
||||
<ModalBody className='onestop-validation'> |
||||
<div className='pal-table-warp'> |
||||
<h5 className='table-ti'> |
||||
<FiFileText /> |
||||
기체 정보 |
||||
</h5> |
||||
<div className='pal-table'> |
||||
<Table responsive> |
||||
<thead> |
||||
<tr> |
||||
<th width='80'>No</th> |
||||
<th width='120'>유효성 검사</th> |
||||
<th>제작 번호</th> |
||||
<th>기체 용도</th> |
||||
<th>기체 중량</th> |
||||
<th>보험 가입</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td width='80'>1</td> |
||||
<td width='120' className='success'> |
||||
성공 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='success'>가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>163CGBEROA529Y</td> |
||||
<td>영리</td> |
||||
<td>25kg이하</td> |
||||
<td className='fail'>미 가입</td> |
||||
</tr> |
||||
</tbody> |
||||
</Table> |
||||
</div> |
||||
</div> |
||||
<div className='pal-table-warp'> |
||||
<h5 className='table-ti'> |
||||
<FiUsers /> |
||||
조종자 정보 |
||||
</h5> |
||||
<div className='pal-table'> |
||||
<Table> |
||||
<thead> |
||||
<tr> |
||||
<th width='80'>No</th> |
||||
<th width='120'>유효성 검사</th> |
||||
<th>이름</th> |
||||
<th>생년 월일</th> |
||||
<th>자격 번호</th> |
||||
<th>조종 자격</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td width='80'>1</td> |
||||
<td width='120' className='success'> |
||||
성공 |
||||
</td> |
||||
<td>홍*동</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='success'>여</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
<tr> |
||||
<td width='80'>2</td> |
||||
<td width='120' className='fail'> |
||||
실패 |
||||
</td> |
||||
<td>김*혜</td> |
||||
<td>1995****</td> |
||||
<td>91-****12</td> |
||||
<td className='fail'>부</td> |
||||
</tr> |
||||
</tbody> |
||||
</Table> |
||||
</div> |
||||
</div> |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color='primary' onClick={() => setTestModal(!testModal)}> |
||||
확인 |
||||
</Button>{' '} |
||||
</ModalFooter> |
||||
</Modal> |
||||
</div> |
||||
<div className='main-data'> |
||||
<div className='data-box-btn'> |
||||
<Card> |
||||
<div className='data-box-btn-list'> |
||||
<h4>지도유형</h4> |
||||
<div className='map-btn'> |
||||
<ButtonGroup> |
||||
<Button |
||||
color={mapType === 'TERRAIN' ? 'primary' : ''} |
||||
onClick={e => setMapType('TERRAIN')} |
||||
> |
||||
지형지도 |
||||
</Button> |
||||
<Button |
||||
color={mapType === 'SATELLITE' ? 'primary' : ''} |
||||
onClick={e => setMapType('SATELLITE')} |
||||
> |
||||
위성지도 |
||||
</Button> |
||||
<Button |
||||
color={mapType === '2D' ? 'primary' : ''} |
||||
onClick={e => setMapType('2D')} |
||||
> |
||||
2D |
||||
</Button> |
||||
</ButtonGroup> |
||||
</div> |
||||
</div> |
||||
<div className='data-box-btn-list'> |
||||
<h4>거리측정</h4> |
||||
<div className='map-btn'> |
||||
<ButtonGroup> |
||||
<Button primary>직선</Button> |
||||
<Button primary>다각형</Button> |
||||
</ButtonGroup> |
||||
</div> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</div> |
||||
<MapControl /> |
||||
</div> |
||||
<div className='left-menu'> |
||||
<h1 className='logo'> |
||||
<img src={logo} width='80' /> |
||||
<span>UTM</span> |
||||
</h1> |
||||
<ul className='left-menu-footer'> |
||||
<li> |
||||
<AiOutlinePoweroff |
||||
size={25} |
||||
className='logout-btn' |
||||
onClick={handlerLogout} |
||||
/> |
||||
</li> |
||||
<li> |
||||
<WebsocketClient /> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
{!isPopup && ( |
||||
<div className='right-menu active flight-approval-layer-wrap'> |
||||
<div |
||||
className='right-layer active flight-approval-layer' |
||||
ref={rightMenuRef} |
||||
> |
||||
<div className='layer-content'> |
||||
<NewFlightApprovalsReport |
||||
handlerSearch={handlerSearch} |
||||
handleDragEnd={handleDragEnd} |
||||
filterId={filterId} |
||||
setFilterId={setFilterId} |
||||
district={district} |
||||
setDistrict={setDistrict} |
||||
filterArea={filterArea} |
||||
setFilterArea={setFilterArea} |
||||
/> |
||||
<OperationApprovalsTable |
||||
filter={filter} |
||||
filterArea={filterArea} |
||||
filterId={filterId} |
||||
startDate={startDate} |
||||
endDate={endDate} |
||||
selected={selected} |
||||
handlerDetail={handlerDetail} |
||||
/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
)} |
||||
</> |
||||
); |
||||
} |
@ -0,0 +1,13 @@
|
||||
import '@styles/react/libs/flatpickr/flatpickr.scss'; |
||||
import '@styles/react/libs/tables/react-dataTable-component.scss'; |
||||
import '../assets/css/custom.css'; |
||||
import ControlRightMenuContainer from '../containers/controlRightMenuContainer'; |
||||
import FlightApprovalsContainer from '../containers/flight/flightApprovalsContainer'; |
||||
|
||||
export default function rightMenuView() { |
||||
return ( |
||||
<div className='pal-container'> |
||||
<ControlRightMenuContainer /> |
||||
</div> |
||||
); |
||||
} |
@ -1,15 +1,15 @@
|
||||
import '@styles/react/libs/flatpickr/flatpickr.scss'; |
||||
import '@styles/react/libs/tables/react-dataTable-component.scss'; |
||||
import '../../assets/css/custom.css'; |
||||
import NewFlightApprovalsContainer from '../../containers/flight/NewFlightApprovalsContainer'; |
||||
import ControlApprovalsContainer from '../../containers/flight/ControlApprovalsContainer'; |
||||
|
||||
export default function NewFlightView() { |
||||
export default function OperationApprovalsView() { |
||||
return ( |
||||
<div className='pal-container'> |
||||
{/* <Helmet> |
||||
<title>관제시스템</title> |
||||
</Helmet> */} |
||||
<NewFlightApprovalsContainer mode='container' />; |
||||
<ControlApprovalsContainer mode='container' />; |
||||
</div> |
||||
); |
||||
} |
@ -0,0 +1,15 @@
|
||||
import '@styles/react/libs/flatpickr/flatpickr.scss'; |
||||
import '@styles/react/libs/tables/react-dataTable-component.scss'; |
||||
import '../../assets/css/custom.css'; |
||||
import OperationApprovalsContainer from '../../containers/flight/OperationApprovalsContainer'; |
||||
|
||||
export default function OperationApprovalsView() { |
||||
return ( |
||||
<div className='pal-container'> |
||||
{/* <Helmet> |
||||
<title>관제시스템</title> |
||||
</Helmet> */} |
||||
<OperationApprovalsContainer mode='container' />; |
||||
</div> |
||||
); |
||||
} |
Loading…
Reference in new issue