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/flatpickr/flatpickr.scss'; |
||||||
import '@styles/react/libs/tables/react-dataTable-component.scss'; |
import '@styles/react/libs/tables/react-dataTable-component.scss'; |
||||||
import '../../assets/css/custom.css'; |
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 ( |
return ( |
||||||
<div className='pal-container'> |
<div className='pal-container'> |
||||||
{/* <Helmet> |
{/* <Helmet> |
||||||
<title>관제시스템</title> |
<title>관제시스템</title> |
||||||
</Helmet> */} |
</Helmet> */} |
||||||
<NewFlightApprovalsContainer mode='container' />; |
<ControlApprovalsContainer mode='container' />; |
||||||
</div> |
</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