Browse Source

공통모달 Laanc 컴포넌트 적용

master
junh_eee(이준희) 7 months ago
parent
commit
c008ea5920
  1. 4
      src/_redux/features/comn/message/messageState.ts
  2. 4
      src/components/account/find/AccountFindPassword.js
  3. 24
      src/components/laanc/LaancQr.js
  4. 31
      src/components/laanc/map/FlightArea.js
  5. 9
      src/components/laanc/map/LaancAreaMap.js
  6. 65
      src/components/laanc/map/LaancDrawControl.js
  7. 278
      src/components/laanc/step/LaancStep1.js
  8. 2
      src/components/laanc/step/LaancStep3.js
  9. 4
      src/configs/msgConst.ts
  10. 5
      src/containers/account/mypage/AccountMypageContainer.js
  11. 2
      src/containers/laanc/LaancPlanContainer.js

4
src/_redux/features/comn/message/messageState.ts

@ -1,6 +1,8 @@
import { ReactNode } from 'react';
export interface IMsgState {
header: string;
body: string;
body: string | ReactNode;
isOpen: boolean;
isHistoryBack: boolean;
isRefresh: boolean;

4
src/components/account/find/AccountFindPassword.js

@ -155,10 +155,6 @@ export const AccountFindPassword = props => {
};
const handlerConfirm = async () => {
const modalHeader = {
send: '인증번호 발송',
confirm: '인증번호 인증'
};
if (!inputId) {
handlerOpenModal(MODAL_HEADER.crtfyhp, '아이디를' + MODAL_BODY.valid);
return;

24
src/components/laanc/LaancQr.js

@ -1,18 +1,16 @@
import { useEffect, useState, useRef } from 'react';
import { ModalHeader, ModalBody, ModalFooter, Button } from '@component/ui';
import ErrorModal from '../../components/modal/ErrorModal';
import axios from '../../modules/utils/customAxiosUtil';
import { checkLaancTsQr } from '@src/_redux/features/laanc/laancThunk';
import { useDispatch, useSelector } from '@store/storeConfig/store';
import { openModal } from '@src/_redux/features/comn/message/messageSlice';
import { MODAL_BODY, MODAL_CRTFYHP, MODAL_HEADER } from '@src/configs/msgConst';
// 이제 handleUserEvent는 300ms 동안 추가 호출이 없을 때만 실행됩니다.
export default function LaancQr({ isPopUp, setIsPopUp, data, handlerStep }) {
const dispatch = useDispatch();
const [isPolling, setIsPolling] = useState(true);
const [isErrorModal, setIsErrorModal] = useState({
isOpen: false,
title: '',
desc: ''
});
const pollingIntervalRef = useRef(null);
// 언마운트시 폴링 중지
@ -59,11 +57,7 @@ export default function LaancQr({ isPopUp, setIsPopUp, data, handlerStep }) {
setIsPopUp(false);
handlerStep(2);
} else if (res.data.errorCode === 'QR001') {
setIsErrorModal({
isOpen: true,
title: '인증 만료',
desc: <>인증 시간이 만료되었습니다.</>
});
dispatch(openModal({ header: '인증 만료', body: MODAL_CRTFYHP.expire }));
setIsPopUp(false);
}
// else if (type === 'user') {
@ -74,12 +68,7 @@ export default function LaancQr({ isPopUp, setIsPopUp, data, handlerStep }) {
// axios 호출 에러 처리 로직
const handleError = error => {
console.log('>>', error);
setIsErrorModal({
isOpen: true,
title: '오류',
desc: <>처리중 오류가 발생하였습니다</>
});
dispatch(openModal({ header: MODAL_HEADER.error, body: MODAL_BODY.error }));
};
// 사용자 확인 버튼 헨들러
@ -117,7 +106,6 @@ export default function LaancQr({ isPopUp, setIsPopUp, data, handlerStep }) {
QR코드 스캔 드론자격증명 앱을통해 유효성 검사 진행 부탁드립니다.{' '}
</p>
</ModalFooter>
<ErrorModal modal={isErrorModal} setModal={setIsErrorModal} />
</>
);
}

31
src/components/laanc/map/FlightArea.js

@ -47,6 +47,7 @@ import gimpo from '../../map/geojson/gimpoAirportAirArea.json';
import threebox from 'threebox-plugin';
import { initFlightBasState } from '@src/_redux/features/laanc/laancState';
import { openModal } from '@src/_redux/features/comn/message/messageSlice';
const LaancAreaMap = lazy(() => import('./LaancAreaMap'));
@ -57,6 +58,7 @@ const FeatureAirZone = lazy(() =>
import('../../map/mapbox/feature/FeatureAirZone')
);
const LaancDrawModal = lazy(() => import('./LaancDrawModal'));
export default function FlightArea({
centeredModal,
setCenteredModal,
@ -86,7 +88,7 @@ export default function FlightArea({
// 날씨 모달
const [formModal, setFormModal] = useState(false);
// 비행구역 설정 관련 모달
// 특별비행신청 모달(드론 원스톱 바로가기 기능)
const [modal, setModal] = useState({
title: '',
desc: '',
@ -135,7 +137,7 @@ export default function FlightArea({
}, [areaCoordList, centeredModal, previewLayer]);
/**
* 비행구역 설정 관련 모달 표출
* 특별비행신청 모달 표출
*/
const handlerModal = () => {
setModal(!modal);
@ -147,17 +149,18 @@ export default function FlightArea({
*/
const handlerDrawType = val => {
if (drawObj.getMode().includes('draw')) {
setModal({
title: '비행 구역 설정',
desc: (
<>
비행구역 설정이 완료되지 않았습니다.
<br />
비행구역 설정 완료 타입 변경 부탁드립니다.
</>
),
isOpen: true
});
dispatch(
openModal({
header: '비행 구역 설정',
body: (
<>
비행구역 설정이 완료되지 않았습니다.
<br />
비행구역 설정 완료 타입 변경 부탁드립니다.
</>
)
})
);
} else {
dispatch(clientDrawTypeChange(val));
}
@ -516,6 +519,7 @@ export default function FlightArea({
) : null}
</div>
{page === 1 ? (
// 비행구역 설정 모달
<Modal
isOpen={centeredModal}
toggle={() => {
@ -625,6 +629,7 @@ export default function FlightArea({
<LaancDrawModal modal={modal} handler={handlerModal} />
</Suspense>
) : null}
{/* 날씨 모달 */}
<Modal
isOpen={formModal}
toggle={handlerWeather}

9
src/components/laanc/map/LaancAreaMap.js

@ -18,6 +18,8 @@ import {
handlerCreateAirSpace
} from '../../../utility/MapUtils';
import gimpo from '../../map/geojson/gimpoAirportAirArea.json';
import { openModal } from '@src/_redux/features/comn/message/messageSlice';
import { MODAL_HEADER } from '@src/configs/msgConst';
const LaancMapSearch = lazy(() => import('./LaancMapSearch'));
const FeatureAirZone = lazy(() =>
@ -220,7 +222,12 @@ export default function LaancAreaMap({
*/
const handlerConfirm = areaList => {
if (areaList === undefined) {
alert('영역을 설정해 주세요.');
dispatch(
openModal({
header: MODAL_HEADER.error,
body: '영역을 설정해 주세요.'
})
);
return;
}

65
src/components/laanc/map/LaancDrawControl.js

@ -20,6 +20,8 @@ import axios from '../../../modules/utils/customAxiosUtil';
import createSupplementaryPointsForCircle from 'mapbox-gl-draw-circle/lib/utils/create_supplementary_points_circle';
import createSupplementaryPoints from 'mapbox-gl-draw-circle/node_modules/@mapbox/mapbox-gl-draw/src/lib/create_supplementary_points';
import Constants from 'mapbox-gl-draw-circle/node_modules/@mapbox/mapbox-gl-draw/src/constants';
import { openModal } from '@src/_redux/features/comn/message/messageSlice';
import { MODAL_BODY, MODAL_HEADER } from '@src/configs/msgConst';
const ErrorModal = lazy(() => import('../../modal/ErrorModal'));
export default function LaancDrawControl(props) {
@ -44,13 +46,6 @@ export default function LaancDrawControl(props) {
// 지도 렌더 횟수
const [number, setNumber] = useState(0);
// 에러 모달창 정보
const [isErrorModal, setIsErrorModal] = useState({
isOpen: false,
title: '',
desc: ''
});
// 비행구역 타입 변경에 따른 그리기모드 셋팅
useEffect(() => {
if (drawType === 'DONE') {
@ -274,11 +269,12 @@ export default function LaancDrawControl(props) {
// 좌표 최소 개수 체크
if (pointLength < minPoint) {
props.setModal({
title: '좌표 최소 개수',
desc: <>좌표를 {desc} 점으로 이어주세요.</>,
isOpen: true
});
dispatch(
openModal({
header: '좌표 최소 개수',
body: <>좌표를 {desc} 점으로 이어주세요.</>
})
);
handlerRemoveError(obj.id);
return;
}
@ -359,28 +355,28 @@ export default function LaancDrawControl(props) {
}
if (elev1.data[0] === 0 || elev2.length > 0) isBreak = true;
} catch (error) {
{
setIsErrorModal({
isOpen: true,
title: '오류',
desc: '처리중 오류가 발생하였습니다'
});
}
dispatch(
openModal({
header: MODAL_HEADER.error,
body: MODAL_BODY.error
})
);
return;
}
if (isBreak) {
props.setModal({
title: '비행 불가 지역',
desc: (
<>
설정하신 비행구역 허용고도가 0m인 구역이 있습니다.
<br />
비행구역 설정 허용고도를 다시 확인해주시기 바랍니다.
</>
),
isOpen: true
});
dispatch(
openModal({
header: '비행 불가 지역',
body: (
<>
설정하신 비행구역 허용고도가 0m인 구역이 있습니다.
<br />
비행구역 설정 허용고도를 다시 확인해주시기 바랍니다.
</>
)
})
);
handlerRemoveError(id);
return;
} else {
@ -411,6 +407,7 @@ export default function LaancDrawControl(props) {
}
if (isBreak) {
// 드론원스톱 버튼 있음.
props.setModal({
title: '특별 비행 신청',
desc: (
@ -860,11 +857,5 @@ export default function LaancDrawControl(props) {
}
};
return (
<>
<Suspense fallback=''>
<ErrorModal modal={isErrorModal} setModal={setIsErrorModal} />
</Suspense>
</>
);
return null;
}

278
src/components/laanc/step/LaancStep1.js

@ -1,4 +1,4 @@
import React, { useEffect, useState, useRef, lazy, Suspense } from 'react';
import { useEffect, useState, useRef, lazy, Suspense } from 'react';
import { useLocation } from 'react-router-dom';
import { useDispatch, useSelector } from '@store/storeConfig/store';
import Flatpickr from 'react-flatpickr';
@ -25,12 +25,12 @@ import {
Modal,
Popover
} from '@component/ui';
import { openModal } from '@src/_redux/features/comn/message/messageSlice';
import { MODAL_BODY, MODAL_HEADER } from '@src/configs/msgConst';
const LaancModal = lazy(() => import('../LaancModal'));
const LaancQr = lazy(() => import('../../../components/laanc/LaancQr'));
const FlightArea = lazy(() => import('../map/FlightArea'));
const ErrorModal = lazy(() => import('../../modal/ErrorModal'));
const InfoModal = lazy(() => import('../../modal/InfoModal'));
export default function LaancStep1({
detailData,
setDetailData,
@ -72,16 +72,6 @@ export default function LaancStep1({
const [popoverSchFltEndDt, setPopoverSchFltEndDt] = useState(false);
// 모달
const [isErrorModal, setIsErrorModal] = useState({
isOpen: false,
title: '',
desc: ''
});
const [isInfoModal, setIsInfoModal] = useState({
isOpen: false,
title: '',
desc: ''
});
const [isLaancModal, setIsLaancModal] = useState({
isOpen: false,
title: '',
@ -123,20 +113,17 @@ export default function LaancStep1({
maxElev;
if (controlledAltitudeExceededWarning) {
setIsErrorModal({
isOpen: true,
title: '검토 결과 사전안내',
desc: (
<>
유효성 검사에 실패하여 승인 대상입니다.
<br />
제출하신 비행계획서의 고도는 {laancElev[0]}m이하에서만 비행이
가능합니다.
<br />
고도 설정을 다시 확인해주시기 바랍니다.
</>
)
});
handlerOpenModal(
'검토 결과 사전안내',
<>
유효성 검사에 실패하여 승인 대상입니다.
<br />
제출하신 비행계획서의 고도는 {laancElev[0]}m이하에서만 비행이
가능합니다.
<br />
고도 설정을 다시 확인해주시기 바랍니다.
</>
);
handleChange({
type: 'area',
name: 'fltElev',
@ -146,6 +133,16 @@ export default function LaancStep1({
}
}, [[laancElev]]);
// 단순 메시지 표출 모달
const handlerOpenModal = (header, body) => {
dispatch(
openModal({
header: header,
body: body
})
);
};
// 비행계획서 작성 핸들러
const handleChange = ({ name, value, type, index, pIndex }) => {
const arrName = `${type}List`;
@ -264,124 +261,81 @@ export default function LaancStep1({
detailData.arcrftList[0].arcrftWghtCd == '11');
if (!detailData.fltType) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행 종류(상업/비상업)를 선택해주세요.'
});
handlerOpenModal(
MODAL_HEADER.valid,
'비행 종류(상업/비상업)를 선택해 주세요.'
);
return false;
} else if (
!schFltStDt.isAfter(currentDate) ||
!schFltEndDt.isAfter(currentDate)
) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행 일자가 이미 지난 일자입니다.'
});
handlerOpenModal(MODAL_HEADER.valid, '비행 일자가 이미 지난 일자입니다.');
return false;
} else if (schFltStDt.isAfter(schFltEndDt)) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행일자를 확인해주세요.'
});
handlerOpenModal(MODAL_HEADER.valid, '비행일자를 확인해 주세요.');
return false;
} else if (schFltStDt.format('A h:mm') === 'PM 11:00') {
setIsErrorModal({
setIsLaancModal({
isOpen: true,
title: '특별 비행',
title: '특별 비행 신청',
desc: (
<>
야간 비행 특별 비행에 해당됩니다.
야간 비행 신청의 경우 특별 비행 신청을 진행하셔야 니다.
<br />
특별 비행의 경우 드론원스톱을 통해서 신청해주시기 바랍니다.
드론원스톱을 통해서 신청해 주시기 바랍니다.
</>
)
),
type: '드론원스톱 바로가기',
url: 'https://drone.onestop.go.kr/'
});
return false;
} else if (schFltStDt.format('A h:mm') === 'PM 5:00') {
setIsErrorModal({
isOpen: true,
title: '비행구역 및 비행일자 중복',
desc: (
<>
설정하신 비행구역 비행시간에 이미 승인완료된 신청건이 있습니다.
<br /> 다시 설정 부탁드립니다.
</>
)
});
handlerOpenModal(
'비행구역 및 비행일자 중복',
<>
설정하신 비행구역 비행시간에 이미 승인완료된 신청건이 있습니다.
<br /> 다시 설정 부탁드립니다.
</>
);
return false;
} else if (!detailData.fltPurpose) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행목적을 선택해 주세요.'
});
handlerOpenModal(MODAL_HEADER.valid, '비행목적을 선택해 주세요.');
return false;
} else if (
!detailData.areaList[0].fltElev ||
detailData.areaList[0].fltElev === 0
) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '고도를 입력해 주세요.'
});
handlerOpenModal(MODAL_HEADER.valid, '고도를 입력해 주세요.');
return false;
} else if (!detailData.areaList[0].bufferZone) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '반경을 입력해 주세요.'
});
handlerOpenModal(MODAL_HEADER.valid, '반경을 입력해 주세요.');
return false;
} else if (
detailData.areaList[0].concatBufferZone !=
detailData.areaList[0].bufferZone &&
detailData.areaList[0].areaType === 'LINE'
) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: <>적용 버튼을 누르지 않고 값을 변경 없습니다.</>
});
handlerOpenModal(
MODAL_HEADER.valid,
'적용 버튼을 누르지 않고 값을 변경 할 수 없습니다.'
);
} else if (!detailData.areaList[0].fltMethod) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행방식를 입력해 주세요.'
});
handlerOpenModal(MODAL_HEADER.valid, '비행방식을 입력해 주세요.');
return false;
} else if (
detailData.areaList[0].fltMethod === '00' &&
!detailData.areaList[0].fltMothoeRm
) {
// 비행 방식 직접 입력칸 활성화 후 작성 시 조건문
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행방식을 입력해 주세요.'
});
handlerOpenModal(MODAL_HEADER.valid, '비행방식을 입력해 주세요.');
return false;
} else if (validateAircraftWeightCode) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '기체 종류를 입력해 주세요.'
});
handlerOpenModal(MODAL_HEADER.valid, '기체 종류를 입력해 주세요.');
return false;
} else if (validateidntfNumCode) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '기체 신고 번호를 입력해 주세요.'
});
handlerOpenModal(MODAL_HEADER.valid, '기체 신고 번호를 입력해 주세요.');
return false;
} else {
handlerLaanc();
@ -407,27 +361,18 @@ export default function LaancStep1({
detailData.areaList
);
if (elev.data[0] === 0) {
setIsErrorModal({
title: '비행 불가 지역',
desc: (
<>
설정하신 비행구역 허용고도가 0m인 구역이 있습니다.
<br />
버퍼존을 다시 확인해주시기 바랍니다.
</>
),
isOpen: true
});
handlerOpenModal(
'비행 불가 지역',
<>
설정하신 비행구역 허용고도가 0m인 구역이 있습니다.
<br />
버퍼존을 다시 확인해주시기 바랍니다.
</>
);
}
dispatch(laancAltitudeData.success(elev.data));
} catch (error) {
{
setIsErrorModal({
isOpen: true,
title: '오류',
desc: '처리중 오류가 발생하였습니다'
});
}
handlerOpenModal(MODAL_HEADER.error, MODAL_BODY.error);
}
}
}
@ -455,11 +400,10 @@ export default function LaancStep1({
value: 0
});
fltElevRef.current.blur();
setIsErrorModal({
isOpen: true,
title: '비행 구역 설정',
desc: '비행 구역 설정을 먼저 설정 해 주세요'
});
handlerOpenModal(
'비행 구역 설정',
'비행 구역 설정을 먼저 설정 해 주세요.'
);
return false;
}
break;
@ -497,11 +441,10 @@ export default function LaancStep1({
// 날짜 선택 핸들러
const handleOpenFlatpickr = () => {
if (detailData.areaList[0].coordList[0].lat === 0) {
setIsErrorModal({
isOpen: true,
title: '비행 구역 설정',
desc: '비행 구역 설정을 먼저 설정 해 주세요'
});
handlerOpenModal(
'비행 구역 설정',
'비행 구역 설정을 먼저 설정 해 주세요.'
);
closeFlatpickr();
return false;
}
@ -546,33 +489,27 @@ export default function LaancStep1({
!laancArea?.duplicated &&
parseInt(value.replace('/^0+/', 'm', ''), 10) <= maxElev
) {
setIsErrorModal({
isOpen: true,
title: '고도 재설정 알림',
desc: (
<>
관제권 비행금지 공역을 제외한 지역에서는 주간에 150m이하
<br />
고도에서는 비행승인없이 비행가능합니다.
</>
)
});
handlerOpenModal(
'고도 재설정 알림',
<>
관제권 비행금지 공역을 제외한 지역에서는 주간에 150m이하
<br />
고도에서는 비행승인없이 비행가능합니다.
</>
);
}
if (controlledAltitudeExceededWarning) {
setIsErrorModal({
isOpen: true,
title: '검토 결과 사전안내',
desc: (
<>
유효성 검사에 실패하여 승인 대상입니다.
<br />
제출하신 비행계획서의 고도는 {laancElev[0]}m이하에서만 비행이
가능합니다.
<br />
고도 설정을 다시 확인해주시기 바랍니다.
</>
)
});
handlerOpenModal(
'검토 결과 사전안내',
<>
유효성 검사에 실패하여 승인 대상입니다.
<br />
제출하신 비행계획서의 고도는 {laancElev[0]}m이하에서만 비행이
가능합니다.
<br />
고도 설정을 다시 확인해주시기 바랍니다.
</>
);
handleChange({
type: 'area',
name: 'fltElev',
@ -613,20 +550,17 @@ export default function LaancStep1({
const maxElev = 150;
if (laancNotRequired) {
setIsErrorModal({
isOpen: true,
title: '검토 결과 사전안내',
desc: (
<>
검토 결과 승인 대상입니다.
<p>
제줄하신 비행계획서는 별도의 승인이 필요없습니다.
<br />
조종자 준수사항에 유의하여 비행하시기 바랍니다.
</p>
</>
)
});
handlerOpenModal(
'검토 결과 사전안내',
<>
검토 결과 승인 대상입니다.
<p>
제줄하신 비행계획서는 별도의 승인이 필요없습니다.
<br />
조종자 준수사항에 유의하여 비행하시기 바랍니다.
</p>
</>
);
return;
} else if (detailData.areaList[0].fltMethod === '군집비행') {
handleChange({
@ -673,11 +607,7 @@ export default function LaancStep1({
// setIsPopUp(true); 잠시 주석
return;
} catch (error) {
setIsErrorModal({
isOpen: true,
title: '오류',
desc: <>처리중 오류가 발생하였습니다</>
});
handlerOpenModal(MODAL_HEADER.error, MODAL_BODY.error);
}
}
}
@ -1375,8 +1305,6 @@ export default function LaancStep1({
/> */}
</Modal>
<Suspense fallback=''>
<ErrorModal modal={isErrorModal} setModal={setIsErrorModal} />
<InfoModal modal={isInfoModal} setModal={setIsInfoModal} />
<LaancModal modal={isLaancModal} setModal={setIsLaancModal} />
</Suspense>
</>

2
src/components/laanc/step/LaacnStep3.js → src/components/laanc/step/LaancStep3.js

@ -19,7 +19,7 @@ import { HOST } from '../../../configs/constants';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
const FlightArea = lazy(() => import('../map/FlightArea'));
export default function LaacnStep3({
export default function LaancStep3({
disabledAnimation,
data,
setDisabledAnimation,

4
src/configs/msgConst.ts

@ -1,7 +1,7 @@
// 인증번호 모달 메시지
export const MODAL_CRTFYHP = {
send: '인증번호를 발송하였습니다.',
expire: '인증번호가 만료되었습니다.',
expire: '인증시간이 만료되었습니다.',
confirm: '인증되었습니다.',
failed: '인증번호가 잘못되었습니다.',
valid: '인증번호를 입력해주세요.',
@ -23,6 +23,6 @@ export const MODAL_BODY = {
save: '저장을 완료하였습니다.',
delete: '삭제를 완료하였습니다.',
confirm: '확인이 완료되었습니다.',
valid: ' 입력해주세요.',
valid: ' 입력해 주세요.',
failed: ' 실패하였습니다.'
};

5
src/containers/account/mypage/AccountMypageContainer.js

@ -154,10 +154,7 @@ const AccountMypageContainer = () => {
return;
}
if (sendCount >= 3) {
handlerOpenModal(
MODAL_HEADER.crtfyhp,
'인증번호 발송은 3회까지만 가능합니다.'
);
handlerOpenModal(MODAL_HEADER.crtfyhp, MODAL_CRTFYHP.count);
return;
}
if (inputHpno.length < 11) {

2
src/containers/laanc/LaancPlanContainer.js

@ -8,7 +8,7 @@ import { clientDrawTypeChange } from '@src/_redux/features/control/map/mapSlice'
const LaancStep1 = lazy(() => import('../../components/laanc/step/LaancStep1'));
const LaancStep2 = lazy(() => import('../../components/laanc/step/LaancStep2'));
const LaancStep3 = lazy(() => import('../../components/laanc/step/LaacnStep3'));
const LaancStep3 = lazy(() => import('../../components/laanc/step/LaancStep3'));
const ErrorModal = lazy(() => import('../../components/modal/ErrorModal'));
export default function LaancPlanContainer({

Loading…
Cancel
Save