diff --git a/src/components/Main.jsx b/src/components/Main.jsx index a6c00444..cbdea01d 100644 --- a/src/components/Main.jsx +++ b/src/components/Main.jsx @@ -28,11 +28,12 @@ export default function MainPage() { const [searchForm, setSearchForm] = useRecoilState(searchState) - const { qcastState } = useContext(QcastContext) + const { qcastState, setIsGlobalLoading } = useContext(QcastContext) // 엔터 이벤트 const handleByOnKeyUp = (e) => { if (e.key === 'Enter') { + setIsGlobalLoading(true) //물건번호 일떄 if (searchRadioType === 'object') { setStuffSearch({ @@ -55,6 +56,7 @@ export default function MainPage() { // 돋보기 클릭 const handleOnSubmit = () => { + setIsGlobalLoading(true) if (searchRadioType === 'object') { setStuffSearch({ ...stuffSearch, diff --git a/src/components/community/Faq.jsx b/src/components/community/Faq.jsx index 17254f5c..d1304106 100644 --- a/src/components/community/Faq.jsx +++ b/src/components/community/Faq.jsx @@ -7,12 +7,16 @@ import Search from '@/components/community/Search' import Pagination from '@/components/community/Pagination' import Table from '@/components/community/Table' +import { useContext } from 'react' + import { useEffect, useState } from 'react' import { useResetRecoilState, useRecoilValue, useRecoilState } from 'recoil' import { useMessage } from '@/hooks/useMessage' import { searchState } from '@/store/boardAtom' +import { QcastContext } from '@/app/QcastProvider' + export default function Faq() { const { getMessage } = useMessage() const resetSearch = useResetRecoilState(searchState) @@ -21,10 +25,14 @@ export default function Faq() { const search = useRecoilValue(searchState) const [searchForm, setSearchForm] = useRecoilState(searchState) + const { setIsGlobalLoading } = useContext(QcastContext) + useEffect(() => { if (search.mainFlag === 'N') { resetSearch() } else { + //메인에서 FAQ 조회 왔을때 로딩바 해제 + setIsGlobalLoading(false) setSearchForm({ ...searchForm, mainFlag: 'N' }) } setIsInitialized(true) diff --git a/src/components/estimate/Estimate.jsx b/src/components/estimate/Estimate.jsx index 71affac4..6f5fb140 100644 --- a/src/components/estimate/Estimate.jsx +++ b/src/components/estimate/Estimate.jsx @@ -19,7 +19,10 @@ import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from import ProductFeaturesPop from './popup/ProductFeaturesPop' import { v4 as uuidv4 } from 'uuid' import { correntObjectNoState } from '@/store/settingAtom' -import { useSearchParams } from 'next/navigation' +import { useRouter, useSearchParams } from 'next/navigation' +import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' +import { usePlan } from '@/hooks/usePlan' +import { usePopup } from '@/hooks/usePopup' export default function Estimate({}) { const [uniqueData, setUniqueData] = useState([]) @@ -70,6 +73,9 @@ export default function Estimate({}) { //견적서 상세데이터 const { estimateContextState, setEstimateContextState, addItem, handleEstimateFileDownload } = useEstimateController(currentPid) + const { selectedPlan } = usePlan() + const router = useRouter() + //견적특이사항 List const [specialNoteList, setSpecialNoteList] = useState([]) const [popShowSpecialNoteList, setPopShowSpecialNoteList] = useState([]) @@ -81,6 +87,8 @@ export default function Estimate({}) { const { setMenuNumber } = useCanvasMenu() + const { closeAll } = usePopup() + const currentObjectNo = searchParams.get('objectNo') setCurrentObjectNo(currentObjectNo) @@ -90,11 +98,12 @@ export default function Estimate({}) { setUploadFiles: setFiles, } - useEffect(() => { + const initEstimate = (currPid = currentPid) => { + console.log('🚀 ~ initEstimate ~ currPid:', currPid) setMenuNumber(5) setObjectNo(objectRecoil.floorPlanObjectNo) - setPlanNo(currentPid) + setPlanNo(currPid) // 공통코드 const code1 = findCommonCode(200800) @@ -121,6 +130,16 @@ export default function Estimate({}) { setPopShowSpecialNoteList(res) } }) + } + + useEffect(() => { + console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan) + if (selectedPlan) initEstimate(selectedPlan.ordering) + }, [selectedPlan]) + + useEffect(() => { + closeAll() + initEstimate() }, []) useEffect(() => { @@ -294,6 +313,7 @@ export default function Estimate({}) { const originReset = () => { setOriginFiles([]) + estimateContextState.resetFlag = 'N' } // 삭제누른 첨부파일 복원 const returnOriginFile = (no) => { @@ -543,7 +563,8 @@ export default function Estimate({}) { //주택PKG input 변경 const onChangePkgAsp = (value) => { if (estimateContextState.estimateType === 'YJSS') { - let newValue = value.replace(/[^0-9.]/g, '') + // let newValue = value.replace(/[^0-9.]/g, '') + let newValue = (value || '0').replace(/[^0-9.]/g, '') if (newValue.length > 1) { newValue = newValue.replace(/(^0+)/, '') if (newValue.length === 0) { @@ -894,6 +915,7 @@ export default function Estimate({}) { } } }) + // console.log('itemList::', itemList) let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0 totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000 diff --git a/src/components/estimate/popup/DocDownOptionPop.jsx b/src/components/estimate/popup/DocDownOptionPop.jsx index 6fe73c53..01a979cf 100644 --- a/src/components/estimate/popup/DocDownOptionPop.jsx +++ b/src/components/estimate/popup/DocDownOptionPop.jsx @@ -3,9 +3,9 @@ import { useState } from 'react' import { useMessage } from '@/hooks/useMessage' import { useAxios } from '@/hooks/useAxios' import { useRecoilValue } from 'recoil' -import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' +import { floorPlanObjectState, estimateState } from '@/store/floorPlanObjectAtom' -export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) { +export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDownPopLockFlg }) { const { getMessage } = useMessage() const { promisePost } = useAxios() @@ -22,6 +22,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) { // recoil 물건번호 const objectRecoil = useRecoilValue(floorPlanObjectState) + const estimateRecoilState = useRecoilValue(estimateState) //문서 다운로드 const handleFileDown = async () => { @@ -82,6 +83,9 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) { link.click() link.remove() window.URL.revokeObjectURL(fileUrl) + //문서 다운받으면 lockFlg = 1 잠금상태로! + estimateRecoilState.lockFlg = '1' + docDownPopLockFlg() } }) .catch((error) => { diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index b06f5e6e..0e9055ab 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -85,7 +85,11 @@ export default function CanvasMenu(props) { const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) //견적서버튼 노출용 - const [buttonStyle, setButtonStyle] = useState('') + const [buttonStyle1, setButtonStyle1] = useState('') //문서 다운로드 버튼 + const [buttonStyle2, setButtonStyle2] = useState('') //저장 버튼 + const [buttonStyle3, setButtonStyle3] = useState('') //초기화 버튼 + const [buttonStyle4, setButtonStyle4] = useState('') //견적서 복사 버튼 + const [buttonStyle5, setButtonStyle5] = useState('') //잠금 버튼 // 발전시뮬레이션 메뉴 이동 const { objectNo, pid } = floorPlanState @@ -191,7 +195,7 @@ export default function CanvasMenu(props) { setCurrentMenu(menu.title) } - if (pathname !== '/floor-plan' && pathname !== '/floor-plan/estimate/5') { + if (pathname !== '/floor-plan' && pathname !== '/floor-plan/estimate/5' && pathname !== '/floor-plan/simulator/6') { router.push(`/floor-plan?pid=${pid}&objectNo=${objectNo}`) } } @@ -256,22 +260,19 @@ export default function CanvasMenu(props) { // 견적서 초기화 버튼 const handleEstimateReset = () => { swalFire({ - //저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까? - //물건정보 text: getMessage('estimate.detail.reset.confirmMsg'), type: 'confirm', confirmFn: async () => { + setIsGlobalLoading(true) const params = { objectNo: objectNo, planNo: pid, userId: sessionState.userId, } - //디테일 호출 try { await promisePost({ url: '/api/estimate/reset-estimate', data: params }).then((res) => { - setIsGlobalLoading(true) if (res.status === 201) { - swalFire({ text: getMessage('estimate.detail.copy.alertMsg'), type: 'alert' }) + swalFire({ text: getMessage('estimate.detail.reset.alertMsg'), type: 'alert' }) fetchSetting(objectNo, pid, 'R') } }) @@ -281,7 +282,7 @@ export default function CanvasMenu(props) { } }, denyFn: () => { - console.log('초기화하지 않음. 변경일시 갱신안함') + setIsGlobalLoading(false) }, }) } @@ -295,16 +296,18 @@ export default function CanvasMenu(props) { }, [type, globalLocale]) useEffect(() => { - if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet)) { - setMenuNumber(3) - setType('surface') - setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) - } else { - setMenuNumber(2) - setType('outline') - setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) - } - }, [canvasSetting]) + if (Object.keys(basicSetting).length === 0) return + // setMenuNumber(1) + // if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet)) { + // setMenuNumber(3) + // setType('surface') + // setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) + // } else { + // setMenuNumber(2) + // setType('outline') + // setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) + // } + }, [basicSetting]) const checkMenuState = (menu) => { return (['2', '3'].includes(canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2) @@ -314,12 +317,112 @@ export default function CanvasMenu(props) { if (isObjectNotEmpty(estimateRecoilState)) { if (estimateRecoilState?.createUser === 'T01') { if (sessionState.userId !== 'T01') { - setButtonStyle('none') + setButtonStyle1('none') + setButtonStyle2('none') + setButtonStyle3('none') + setButtonStyle4('none') + setButtonStyle5('none') + } + } else { + if (estimateRecoilState?.tempFlg === '1') { + setButtonStyle1('none') + setButtonStyle2('') + setButtonStyle3('none') + setButtonStyle4('none') + setButtonStyle5('none') + } else { + if (estimateRecoilState?.tempFlg === '0' && estimateRecoilState?.lockFlg === '0') { + setButtonStyle1('') + setButtonStyle2('') + setButtonStyle3('') + setButtonStyle4('') + setButtonStyle5('') + } else { + setButtonStyle1('') + setButtonStyle2('none') + setButtonStyle3('none') + setButtonStyle4('') + setButtonStyle5('') + } } } } }, [estimateRecoilState]) + /** + * 견적서 잠금 / 해제 + * lockFlg : 0 잠금해제상태 / 1 잠금상태 + * --보낼때-- + * 0잠금해제는 1잠금으로 + * 1잠금 문서는 0 잠금해제로 + */ + const handleEstimateLockController = (estimateRecoilState) => { + swalFire({ + text: estimateRecoilState.lockFlg === '0' ? getMessage('estimate.detail.lock.alertMsg') : getMessage('estimate.detail.unlock.alertMsg'), + type: 'confirm', + confirmFn: async () => { + setIsGlobalLoading(true) + const params = { + objectNo: estimateRecoilState.objectNo, + planNo: estimateRecoilState.planNo, + lockFlg: estimateRecoilState.lockFlg === '0' ? '1' : '0', + userId: sessionState.userId, + } + try { + await promisePost({ url: '/api/estimate/save-estimate-lock', data: params }).then((res) => { + if (res.status === 201) { + estimateRecoilState.lockFlg = estimateRecoilState.lockFlg === '0' ? '1' : '0' + if (estimateRecoilState?.createUser === 'T01') { + if (sessionState.userId !== 'T01') { + setButtonStyle1('none') + setButtonStyle2('none') + setButtonStyle3('none') + setButtonStyle4('none') + setButtonStyle5('none') + } + } else { + if (estimateRecoilState?.tempFlg === '1') { + setButtonStyle1('none') + setButtonStyle2('') + setButtonStyle3('none') + setButtonStyle4('none') + setButtonStyle5('none') + } else { + if (estimateRecoilState?.tempFlg === '0' && estimateRecoilState?.lockFlg === '0') { + setButtonStyle1('') + setButtonStyle2('') + setButtonStyle3('') + setButtonStyle4('') + setButtonStyle5('') + } else { + setButtonStyle1('') + setButtonStyle2('none') + setButtonStyle3('none') + setButtonStyle4('') + setButtonStyle5('') + } + } + } + } + setIsGlobalLoading(false) + }) + } catch (error) { + setIsGlobalLoading(false) + console.log('error::::::::::::', e.response.data.message) + } + }, + }) + } + + // 문서다운로드 팝업에서 다운로드 하면 문서 잠금 + const docDownPopLockFlg = () => { + setButtonStyle1('') + setButtonStyle2('none') + setButtonStyle3('none') + setButtonStyle4('') + setButtonStyle5('') + } + return (
num === menuNumber) ? 'active' : ''}`}>
@@ -406,16 +509,17 @@ export default function CanvasMenu(props) { {menuNumber === 5 && ( <>
- - )} -
@@ -463,7 +580,9 @@ export default function CanvasMenu(props) { {[2, 3, 4].some((num) => num === menuNumber) && }
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */} - {estimatePopupOpen && } + {estimatePopupOpen && ( + + )} {/* 견적서(menuNumber ===5)복사 팝업 */} {estimateCopyPopupOpen && }
diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index 921d5fdb..866c6dea 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -43,59 +43,63 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set // 데이터를 최초 한 번만 조회 useEffect(() => { + if (!basicSetting || !currentRoof || Object.keys(currentRoof).length === 0 || Object.keys(basicSetting).length === 0) return const raftCodeList = findCommonCode('203800') setRaftCodes(raftCodeList) + // setCurrentRoof({ ...currentRoof, roofSizeSet: basicSetting.roofMaterials.roofSizeSet, roofAngleSet: basicSetting.roofMaterials.roofAngleSet }) }, []) useEffect(() => { - console.log('🚀 ~ useEffect ~ basicSetting:', currentRoof) - setBasicSettings({ - ...basicSetting, - roofSizeSet: currentRoof.roofSizeSet, - roofAngleSet: currentRoof.roofAngleSet, - roofsData: { - roofApply: true, - roofSeq: 0, - roofMatlCd: currentRoof.roofMatlCd, - roofWidth: currentRoof.width, - roofHeight: currentRoof.length, - roofHajebichi: currentRoof.hajebichi, - roofGap: currentRoof.raft, - roofLayout: currentRoof.layout, - }, - }) + if (!currentRoof || Object.keys(currentRoof).length === 0) return + console.log(basicSetting) + setBasicSettings({ + ...basicSetting, + roofSizeSet: String(currentRoof.roofSizeSet), + roofAngleSet: currentRoof.roofAngleSet, + roofsData: { + roofApply: true, + roofSeq: 0, + roofMatlCd: currentRoof.roofMatlCd, + roofWidth: currentRoof.width, + roofHeight: currentRoof.length, + roofHajebichi: currentRoof.hajebichi, + roofGap: currentRoof.raft, + roofLayout: currentRoof.layout, + roofPitch: currentRoof.pitch, + roofAngle: currentRoof.angle, + }, + }) }, [currentRoof]) const handleRoofSizeSetChange = (value) => { - setCurrentRoof({...currentRoof, roofSizeSet: value}) + setCurrentRoof({ ...currentRoof, roofSizeSet: value }) } const handleRoofAngleSetChange = (value) => { - setCurrentRoof({...currentRoof, roofAngleSet: value}) + setCurrentRoof({ ...currentRoof, roofAngleSet: value }) } // Function to update the roofType and corresponding values const handleRoofTypeChange = (value) => { const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value) - console.log('🚀 ~ handleRoofTypeChange ~ selectedRoofMaterial:', selectedRoofMaterial) - setCurrentRoof({...selectedRoofMaterial, index: 0, roofSizeSet: currentRoof.roofSizeSet, roofAngleSet: currentRoof.roofAngleSet}) + setCurrentRoof({ ...selectedRoofMaterial, index: 0, roofSizeSet: String(currentRoof.roofSizeSet), roofAngleSet: currentRoof.roofAngleSet }) } const changeInput = (value, e) => { const { name } = e.target - setCurrentRoof({...currentRoof, [name]: Number(value)}) + setCurrentRoof({ ...currentRoof, [name]: Number(value) }) } const handleRafterChange = (value) => { - setCurrentRoof({...currentRoof, raft: value}) + setCurrentRoof({ ...currentRoof, raft: value }) } const handleRoofLayoutChange = (value) => { - setCurrentRoof({...currentRoof, layout: value}) + setCurrentRoof({ ...currentRoof, layout: value }) } const handleSaveBtn = () => { - const roofInfo = { + const roofInfo = { ...currentRoof, roofCd: roofRef.roofCd.current?.value, width: roofRef.width.current?.value, @@ -106,7 +110,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set layout: currentRoof.layout, index: 0, } - + const newAddedRoofs = [...addedRoofs] if (addedRoofs.length === 1) { newAddedRoofs[0] = { ...roofInfo } @@ -118,7 +122,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set selectedRoofMaterial: { // 선택된 지붕재 정보 roofInfo, - } + }, }) setBasicSettings({ @@ -139,6 +143,8 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set roofLayout: currentRoof.layout, roofSizeSet: currentRoof.roofSizeSet, roofAngleSet: currentRoof.roofAngleSet, + roofPitch: currentRoof.pitch, + roofAngle: currentRoof.angle, }, }) @@ -181,9 +187,9 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set name="roofSizeSet" id="ra01" value="1" // roofSizeSet 값이 '1'인 경우 - checked={currentRoof.roofSizeSet === '1'} // 선택 여부 확인 + checked={currentRoof?.roofSizeSet === '1'} // 선택 여부 확인 //onChange={(e) => setBasicSettings({ ...basicSetting, roofSizeSet: e.target.value })} // 상태 업데이트 - onClick={() => handleRoofSizeSetChange(1)} + onClick={() => handleRoofSizeSetChange('1')} /> @@ -193,9 +199,9 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set name="roofSizeSet" id="ra02" value="2" // roofSizeSet 값이 '2'인 경우 - checked={currentRoof.roofSizeSet === '2'} // 선택 여부 확인 + checked={currentRoof?.roofSizeSet === '2'} // 선택 여부 확인 //onChange={(e) => setBasicSettings({ ...basicSetting, roofSizeSet: e.target.value })} // 상태 업데이트 - onClick={() => handleRoofSizeSetChange(2)} + onClick={() => handleRoofSizeSetChange('2')} /> @@ -205,9 +211,9 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set name="roofSizeSet" id="ra03" value="3" // roofSizeSet 값이 '3'인 경우 - checked={currentRoof.roofSizeSet === '3'} // 선택 여부 확인 + checked={currentRoof?.roofSizeSet === '3'} // 선택 여부 확인 //onChange={(e) => setBasicSettings({ ...basicSetting, roofSizeSet: e.target.value })} // 상태 업데이트 - onClick={() => handleRoofSizeSetChange(3)} + onClick={() => handleRoofSizeSetChange('3')} /> @@ -224,7 +230,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set name="roofAngleSet" id="ra04" value="slope" // 첫 번째 라디오 버튼의 값 - checked={currentRoof.roofAngleSet === 'slope'} // 현재 선택된 값인지 확인 + checked={currentRoof?.roofAngleSet === 'slope'} // 현재 선택된 값인지 확인 //onChange={(e) => setBasicSettings({ ...basicSetting, roofAngleSet: e.target.value })} // 상태 업데이트 onClick={() => handleRoofAngleSetChange('slope')} /> @@ -236,7 +242,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set name="roofAngleSet" id="ra05" value="flat" // 두 번째 라디오 버튼의 값 - checked={currentRoof.roofAngleSet === 'flat'} // 현재 선택된 값인지 확인 + checked={currentRoof?.roofAngleSet === 'flat'} // 현재 선택된 값인지 확인 //onChange={(e) => setBasicSettings({ ...basicSetting, roofAngleSet: e.target.value })} // 상태 업데이트 onClick={() => handleRoofAngleSetChange('flat')} /> @@ -256,17 +262,19 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
{ return { ...roof, name: globalLocale === 'ko' ? roof.roofMatlNm : roof.roofMatlNmJp } })} - value={currentRoof.roofSizeSet == 3 ? null : currentRoof.roofMatlCd} + value={currentRoof?.roofSizeSet === '3' ? null : currentRoof?.roofMatlCd} onChange={(e) => handleRoofTypeChange(e.roofMatlCd)} sourceKey="id" targetKey="id" showKey="name" - disabled={currentRoof.roofSizeSet == 3} + disabled={currentRoof?.roofSizeSet === '3'} /> {/* +
+ r.clCode === (currentRoof?.raft === undefined ? currentRoof?.raftBaseCd : currentRoof?.raft)) + .clCodeNm + } + value={currentRoof?.raft === undefined ? currentRoof?.raftBaseCd : currentRoof?.raft} + onChange={(e) => handleRafterChange(e.clCode)} + sourceKey="clCode" + targetKey={currentRoof?.raft ? 'raft' : 'raftBaseCd'} + showKey="clCodeNm" + disabled={currentRoof?.roofSizeSet === '3'} + /> + {/* */} -
+
)}
)} @@ -377,31 +388,33 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set value={parseInt(currentRoof.hajebichi)} onChange={(e) => onlyNumberInputChange(e, changeInput)} readOnly={currentRoof.roofPchAuth === 'R'} - disabled={currentRoof.roofSizeSet == 3} + disabled={currentRoof.roofSizeSet === '3'} /> )} -
-
- - + {currentRoof && ( +
+
+ + +
-
+ )} diff --git a/src/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting.jsx b/src/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting.jsx index 23993cb0..e8942948 100644 --- a/src/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting.jsx +++ b/src/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting.jsx @@ -10,6 +10,7 @@ import { basicSettingState } from '@/store/settingAtom' import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCommonCode } from '@/hooks/common/useCommonCode' +import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom' export default function ContextRoofAllocationSetting(props) { const contextPopupPosition = useRecoilValue(contextPopupPositionState) @@ -29,9 +30,12 @@ export default function ContextRoofAllocationSetting(props) { handleChangeLayout, handleSaveContext, currentRoofList, + handleChangeInput, } = useRoofAllocationSetting(id) const { findCommonCode } = useCommonCode() + const pitchText = useRecoilValue(pitchTextSelector) + const currentAngleType = useRecoilValue(currentAngleTypeSelector) const [raftCodes, setRaftCodes] = useState([]) useEffect(() => { const raftCodeList = findCommonCode('203800') @@ -104,6 +108,22 @@ export default function ContextRoofAllocationSetting(props) { {index !== 0 && }
+
+
+ {getMessage('slope')} +
+ { + handleChangeInput(e, currentAngleType === 'slope' ? 'pitch' : 'angle', index) + }} + defaultValue={currentAngleType === 'slope' ? roof.pitch : roof.angle} + /> +
+ {pitchText} +
+
{(roof.widAuth || roof.lenAuth) && (
{roof.widAuth && ( diff --git a/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx b/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx index 91268678..eabe825b 100644 --- a/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx +++ b/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx @@ -119,7 +119,7 @@ export default function RoofAllocationSetting(props) { type="text" className="input-origin block" onChange={(e) => { - handleChangeInput(e, 'pitch', index) + handleChangeInput(e, currentAngleType === 'slope' ? 'pitch' : 'angle', index) }} defaultValue={currentAngleType === 'slope' ? roof.pitch : roof.angle} /> diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 767c9581..5d1c32de 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -10,7 +10,7 @@ import { globalLocaleStore } from '@/store/localeAtom' import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils' import { useMessage } from '@/hooks/useMessage' import { useForm } from 'react-hook-form' -import { useRecoilValue, useSetRecoilState, useResetRecoilState } from 'recoil' +import { useRecoilValue, useSetRecoilState, useResetRecoilState, useRecoilState } from 'recoil' import { SessionContext } from '@/app/SessionProvider' import FindAddressPop from './popup/FindAddressPop' import PlanRequestPop from './popup/PlanRequestPop' @@ -23,7 +23,12 @@ import { GlobalDataContext } from '@/app/GlobalDataProvider' import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' import { stuffSearchState } from '@/store/stuffAtom' import { QcastContext } from '@/app/QcastProvider' + +import { useSwal } from '@/hooks/useSwal' + export default function StuffDetail() { + const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) + const { swalFire } = useSwal() const { setIsGlobalLoading } = useContext(QcastContext) const resetStuffRecoil = useResetRecoilState(stuffSearchState) const stuffSearchParams = useRecoilValue(stuffSearchState) @@ -118,13 +123,13 @@ export default function StuffDetail() { field: 'planNo', headerName: getMessage('stuff.detail.planGridHeader.planNo'), width: 100, - cellStyle: { justifyContent: 'center' }, + cellStyle: { justifyContent: 'center', cursor: 'pointer' }, }, { field: 'orderFlg', headerName: getMessage('stuff.detail.planGridHeader.orderFlg'), width: 80, - cellStyle: { justifyContent: 'center' }, + cellStyle: { justifyContent: 'center', cursor: 'pointer' }, cellRenderer: (params) => { //1일때만 동그라미 let orderFlg @@ -137,13 +142,31 @@ export default function StuffDetail() { headerName: getMessage('stuff.detail.planGridHeader.moduleModel'), flex: 1, wrapText: true, - cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ }, + cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' }, + // cellRenderer: (params) => { + // let origin = params.value + // console.log('모듈:::', origin) + // if (origin !== null) { + // return ( + // <> + // {origin?.split('、').map((it, idx) => ( + // + // {it} + //
+ //
+ // ))} + // + // ) + // } else { + // return null + // } + // }, }, { field: 'capacity', headerName: getMessage('stuff.detail.planGridHeader.capacity'), width: 120, - cellStyle: { justifyContent: 'flex-end' /* 우측정렬*/ }, + cellStyle: { justifyContent: 'flex-end' /* 우측정렬*/, cursor: 'pointer' }, cellRenderer: (params) => { let origin = params.value let capacity @@ -162,9 +185,8 @@ export default function StuffDetail() { width: 140, wrapText: true, autoHeight: true, - cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ }, + cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' }, cellRenderer: (params) => { - //콤마가 아니라 、.. 일본콤마? let origin = params.value if (origin !== null) { return ( @@ -187,7 +209,7 @@ export default function StuffDetail() { headerName: getMessage('stuff.detail.planGridHeader.constructSpecificationMulti'), wrapText: true, autoHeight: true, - cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ }, + cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' }, cellRenderer: (params) => { let origin = params.value if (origin !== null) { @@ -211,7 +233,7 @@ export default function StuffDetail() { headerName: getMessage('stuff.detail.planGridHeader.supportMethodIdMulti'), wrapText: true, autoHeight: true, - cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ }, + cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' }, cellRenderer: (params) => { let origin = params.value if (origin !== null) { @@ -236,7 +258,7 @@ export default function StuffDetail() { flex: 1, wrapText: true, autoHeight: true, - cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ }, + cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' }, cellRenderer: (params) => { let origin = params.value if (origin !== null) { @@ -282,7 +304,7 @@ export default function StuffDetail() { onClick={() => { //mid:5(견적서), /pid:플랜번호 setFloorPlanObjectNo({ floorPlanObjectNo: params.data.objectNo }) - // router.push(`/floor-plan/estimate/5/${params.data.planNo}`) + setIsGlobalLoading(true) router.push(`/floor-plan/estimate/5?pid=${params.data.planNo}&objectNo=${params.data.objectNo}`) }} > @@ -325,13 +347,10 @@ export default function StuffDetail() { } promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => { if (res.status === 200) { - // console.log(res.data.createUser) - // console.log(session) if (res?.data?.createUser === 'T01' && session?.userId !== 'T01') { //createUser가 T01인데 로그인사용자가 T01이 아니면 버튼숨기기 setShowButton('none') } - // console.log('상세::', res.data) if (isObjectNotEmpty(res.data)) { let surfaceTypeValue if (res.data.surfaceType === 'Ⅲ・Ⅳ') { @@ -342,8 +361,13 @@ export default function StuffDetail() { setManagementState({ ...res.data, surfaceTypeValue: surfaceTypeValue }) } else { setManagementState({}) - alert(getMessage('stuff.detail.header.notExistObjectNo')) - router.push('/management/stuff', { scroll: false }) + swalFire({ + text: getMessage('stuff.detail.header.notExistObjectNo'), + type: 'alert', + confirmFn: () => { + router.push('/management/stuff', { scroll: false }) + }, + }) } if (isNotEmptyArray(res.data.planList)) { setPlanGridProps({ ...planGridProps, planGridData: res.data.planList }) @@ -354,8 +378,13 @@ export default function StuffDetail() { setManagementState({}) setPlanGridProps({ ...planGridProps, planGridData: [] }) - alert(getMessage('stuff.detail.header.notExistObjectNo')) - router.push('/management/stuff', { scroll: false }) + swalFire({ + text: getMessage('stuff.detail.header.notExistObjectNo'), + type: 'alert', + confirmFn: () => { + router.push('/management/stuff', { scroll: false }) + }, + }) } }) } else { @@ -923,9 +952,6 @@ export default function StuffDetail() { //팝업에서 넘어온 설계의뢰 정보로 바꾸기 const setPlanReqInfo = (info) => { - // console.log('session 정보:::::::', session) - // console.log('설계의뢰에서 넘어온 정보:::::::', info) - form.setValue('planReqNo', info.planReqNo) form.setValue('objectStatusId', info.building) @@ -1118,7 +1144,7 @@ export default function StuffDetail() { const onSearchDesignRequestPopOpen = () => { const saleStoreId = form.watch('saleStoreId') if (saleStoreId === '') { - alert(getMessage('stuff.planReqPopup.error.message2')) + swalFire({ text: getMessage('stuff.planReqPopup.error.message2'), type: 'alert' }) } else { setShowDesignRequestButtonValid(true) } @@ -1128,7 +1154,7 @@ export default function StuffDetail() { const onSearchWindSpeedPopOpen = () => { const prefName = form.watch('prefName') if (prefName === '') { - alert(getMessage('stuff.windSelectPopup.error.message1')) + swalFire({ text: getMessage('stuff.windSelectPopup.error.message1'), type: 'alert' }) } else { setShowWindSpeedButtonValid(true) } @@ -1219,7 +1245,7 @@ export default function StuffDetail() { } if (Object.keys(errors).length > 0) { - return alert(getMessage('stuff.detail.save.valierror3', [errors])) + return swalFire({ text: getMessage('stuff.detail.save.valierror3', [errors]), type: 'alert' }) } const apiUrl = '/api/object/save-object' @@ -1259,10 +1285,10 @@ export default function StuffDetail() { let height = params.installHeight if (snow === '0') { - return alert(getMessage('stuff.detail.save.valierror1')) + return swalFire({ text: getMessage('stuff.detail.save.valierror1'), type: 'alert' }) } if (height === '0') { - return alert(getMessage('stuff.detail.save.valierror2')) + return swalFire({ text: getMessage('stuff.detail.save.valierror2'), type: 'alert' }) } if (managementState) { @@ -1316,50 +1342,65 @@ export default function StuffDetail() { delete params_sort.workName if (Object.entries(detail_sort).toString() === Object.entries(params_sort).toString()) { - return alert(getMessage('stuff.detail.noChgData')) + return swalFire({ text: getMessage('stuff.detail.noChgData'), type: 'alert' }) } } if (params?.receiveUser !== '') { if (params?.receiveUser.trim().length > 10) { - return alert(getMessage('stuff.detail.tempSave.message2')) + return swalFire({ text: getMessage('stuff.detail.tempSave.message2'), type: 'alert' }) } } //로그인이 2차점인데 otherSaleStoreId가 없으면 알럿 if (session.storeLvl !== '1') { if (params.saleStoreLevel === '1') { - return alert(getMessage('stuff.detail.tempSave.message4')) + return swalFire({ text: getMessage('stuff.detail.tempSave.message4'), type: 'alert' }) } } if (editMode === 'NEW') { await promisePost({ url: apiUrl, data: params }) .then((res) => { + setIsGlobalLoading(true) //상세화면으로 전환 if (res.status === 201) { - alert(getMessage('stuff.detail.save')) setFloorPlanObjectNo({ floorPlanObjectNo: objectNo }) - router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) + swalFire({ + text: getMessage('stuff.detail.save'), + type: 'alert', + confirmFn: () => { + setIsGlobalLoading(false) + + router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) + }, + }) } }) .catch((error) => { console.log('error::::::', error) - alert(error?.response.data.message) + setIsGlobalLoading(false) }) } else { // 수정모드일때는 PUT - // await promisePut({ url: apiUrl, data: params }).then((res) => { await promisePut({ url: apiUrl, data: params }) .then((res) => { + setIsGlobalLoading(true) + if (res.status === 201) { setFloorPlanObjectNo({ floorPlanObjectNo: res.data.objectNo }) - alert(getMessage('stuff.detail.save')) - router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) + swalFire({ + text: getMessage('stuff.detail.save'), + type: 'alert', + confirmFn: () => { + setIsGlobalLoading(false) + router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) + }, + }) } }) .catch((error) => { console.log('error::::::', error) - alert(error?.response.data.message) + setIsGlobalLoading(false) }) } } @@ -1406,7 +1447,7 @@ export default function StuffDetail() { // 담당자 자리수 체크 if (params?.receiveUser !== '') { if (params?.receiveUser.trim().length > 10) { - return alert(getMessage('stuff.detail.tempSave.message2')) + return swalFire({ text: getMessage('stuff.detail.tempSave.message2'), type: 'alert' }) } } @@ -1414,26 +1455,40 @@ export default function StuffDetail() { if (objectNo) { await promisePut({ url: apiUrl, data: params }) .then((res) => { + setIsGlobalLoading(true) if (res.status === 201) { - alert(getMessage('stuff.detail.tempSave.message1')) - router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) + swalFire({ + text: getMessage('stuff.detail.tempSave.message1'), + type: 'alert', + confirmFn: () => { + setIsGlobalLoading(false) + router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) + }, + }) } }) .catch((error) => { + setIsGlobalLoading(false) console.log('error::::::', error) - alert(error?.response.data.message) }) } else { await promisePost({ url: apiUrl, data: params }) .then((res) => { + setIsGlobalLoading(true) if (res.status === 201) { - alert(getMessage('stuff.detail.tempSave.message1')) - router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) + swalFire({ + text: getMessage('stuff.detail.tempSave.message1'), + type: 'alert', + confirmFn: () => { + setIsGlobalLoading(false) + router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) + }, + }) } }) .catch((error) => { console.log('error::::::', error) - alert(error?.response.data.message) + setIsGlobalLoading(false) }) } } @@ -1445,25 +1500,63 @@ export default function StuffDetail() { userId: session.userId, } if (specificationConfirmDate != null) { - alert(getMessage('stuff.detail.delete.message1')) + swalFire({ text: getMessage('stuff.detail.delete.message1'), type: 'alert' }) } else { - if (confirm(getMessage('common.message.data.delete'))) { - // setIsGlobalLoading(true) - del({ url: `/api/object/${objectNo}?${queryStringFormatter(delParams)}` }) - .then(() => { - setFloorPlanObjectNo({ floorPlanObjectNo: '' }) - if (session.storeId === 'T01') { - stuffSearchParams.code = 'DELETE' - } else { - resetStuffRecoil() - } - // setIsGlobalLoading(false) - router.push('/management/stuff', { scroll: false }) - }) - .catch(() => { - // setIsGlobalLoading(false) - }) - } + swalFire({ + text: getMessage('common.message.data.delete'), + type: 'confirm', + confirmFn: () => { + setFloorPlanObjectNo({ floorPlanObjectNo: '' }) + del({ url: `/api/object/${objectNo}?${queryStringFormatter(delParams)}` }) + .then((res) => { + setIsGlobalLoading(true) + setFloorPlanObjectNo({ floorPlanObjectNo: '' }) + if (session.storeId === 'T01') { + stuffSearchParams.code = 'DELETE' + } else { + if (session.storeLvl === '1') { + stuffSearch.schObjectNo = '' + stuffSearch.schAddress = '' + stuffSearch.schObjectName = '' + stuffSearch.schSaleStoreName = '' + stuffSearch.schReceiveUser = '' + stuffSearch.schDispCompanyName = '' + stuffSearch.schDateType = 'U' + stuffSearch.schTempFlg = '' + stuffSearch.schMyDataCheck = false + stuffSearch.startRow = 1 + stuffSearch.endRow = 100 + stuffSearch.schSortType = 'U' + stuffSearch.pageNo = 1 + stuffSearch.pageSize = 100 + stuffSearch.code = 'S' + } else { + stuffSearch.schObjectNo = '' + stuffSearch.schAddress = '' + stuffSearch.schObjectName = '' + stuffSearch.schSaleStoreName = '' + stuffSearch.schReceiveUser = '' + stuffSearch.schDispCompanyName = '' + stuffSearch.schDateType = 'U' + stuffSearch.schTempFlg = '' + stuffSearch.schMyDataCheck = false + stuffSearch.startRow = 1 + stuffSearch.endRow = 100 + stuffSearch.schSortType = 'U' + stuffSearch.pageNo = 1 + stuffSearch.pageSize = 100 + stuffSearch.code = 'S' + } + } + setIsGlobalLoading(false) + router.push('/management/stuff', { scroll: false }) + }) + .catch((error) => { + console.log('error::::::', error) + setIsGlobalLoading(false) + }) + }, + }) } } @@ -1491,11 +1584,14 @@ export default function StuffDetail() { // 그리드 더블 클릭 const getCellDoubleClicked = (params) => { - if (params?.column?.colId !== 'estimateDate') { - if (params?.data?.planNo && params?.data?.objectNo) { - let objectNo = params?.data?.objectNo - let planNo = params?.data?.planNo - router.push(`/floor-plan/estimate/5?pid=${planNo}&objectNo=${objectNo}`) + if (params.data.estimateDate != null) { + if (params?.column?.colId !== 'estimateDate') { + if (params?.data?.planNo && params?.data?.objectNo) { + setIsGlobalLoading(true) + let objectNo = params?.data?.objectNo + let planNo = params?.data?.planNo + router.push(`/floor-plan/estimate/5?pid=${planNo}&objectNo=${objectNo}`) + } } } } diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 96cd314a..ab4d0b47 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -343,7 +343,6 @@ export default function StuffSearchCondition() { } else { if (session?.storeLvl === '2') { if (otherSaleStoreList.length > 1) { - // handleClear2() setOtherSaleStoreId(session.storeId) stuffSearch.schOtherSelSaleStoreId = session.storeId stuffSearch.schObjectNo = '' diff --git a/src/components/simulator/Simulator.jsx b/src/components/simulator/Simulator.jsx index 140e21a9..ef752582 100644 --- a/src/components/simulator/Simulator.jsx +++ b/src/components/simulator/Simulator.jsx @@ -15,6 +15,8 @@ import { useMessage } from '@/hooks/useMessage' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { convertNumberToPriceDecimal } from '@/util/common-utils' +import { usePlan } from '@/hooks/usePlan' +import { usePopup } from '@/hooks/usePopup' // import { useSearchParams } from 'next/navigation' export default function Simulator() { @@ -24,6 +26,7 @@ export default function Simulator() { // const searchParams = useSearchParams() // const objectNo = searchParams.get('objectNo') // const pid = searchParams.get('pid') + const { selectedPlan } = usePlan() const chartRef = useRef(null) @@ -40,6 +43,12 @@ export default function Simulator() { // 차트 관련 const [chartData, setChartData] = useState([]) + const { closeAll } = usePopup() + + useEffect(() => { + closeAll() + }, []) + const data = { labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], datasets: [ @@ -105,6 +114,7 @@ export default function Simulator() { } useEffect(() => { + console.log('🚀 ~ useEffect ~ selectedPlan:', selectedPlan) /* 초기화 작업 */ setChartData([]) setObjectDetail({}) @@ -121,7 +131,7 @@ export default function Simulator() { setPwrGnrSimType('D') setPwrRecoil({ ...pwrRecoil, type: 'D' }) } - }, [objectNo, pid]) + }, [objectNo, pid, selectedPlan]) // 물건 상세 정보 조회 const [objectDetail, setObjectDetail] = useState({}) @@ -224,8 +234,8 @@ export default function Simulator() {
{/* 시스템용량 */}
-
{getMessage('simulator.title.sub3')}
-
{objectDetail.capacity ? `${convertNumberToPriceDecimal(objectDetail.capacity)}kW` : ''}
+
{getMessage('simulator.title.sub3')}
+
{objectDetail.capacity ? `${convertNumberToPriceDecimal(objectDetail.capacity)} kW` : ''}
{/* 연간예측발전량 */}
@@ -247,7 +257,7 @@ export default function Simulator() { {/* 적설조건 */}
{getMessage('simulator.title.sub7')}
-
{objectDetail.snowfall}
+
{objectDetail.snowfall} cm
{/* 풍속조건 */}
@@ -346,7 +356,7 @@ export default function Simulator() { {/* 지붕면 */} {moduleInfo.roofSurface} {/* 경사각 */} - {convertNumberToPriceDecimal(moduleInfo.slopeAngle)}寸 + {convertNumberToPriceDecimal(moduleInfo.slopeAngle)}{moduleInfo.classType == 0 ? "寸":"º"} {/* 방위각(도) */} {convertNumberToPriceDecimal(moduleInfo.azimuth)} {/* 태양전지모듈 */} diff --git a/src/hooks/common/useMenu.js b/src/hooks/common/useMenu.js index 75905a73..a7b6519c 100644 --- a/src/hooks/common/useMenu.js +++ b/src/hooks/common/useMenu.js @@ -58,9 +58,9 @@ export default function useMenu() { if (type === 'surface') { switch (currentMenu) { - case MENU.BATCH_CANVAS.SLOPE_SETTING: - addPopup(popupId, 1, ) - break + // case MENU.BATCH_CANVAS.SLOPE_SETTING: + // addPopup(popupId, 1, ) + // break case MENU.BATCH_CANVAS.BATCH_DRAWING: addPopup(popupId, 1, ) break diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js index 981d81a2..ed7fcde4 100644 --- a/src/hooks/floorPlan/estimate/useEstimateController.js +++ b/src/hooks/floorPlan/estimate/useEstimateController.js @@ -61,6 +61,8 @@ export const useEstimateController = (planNo) => { res.data.originFiles = [] res.data.originFile = [] res.data.resetFlag = 'Y' + } else { + res.data.resetFlag = 'N' } if (res.data.itemList.length > 0) { res.data.itemList.map((item) => { @@ -160,16 +162,19 @@ export const useEstimateController = (planNo) => { let itemFlg = true if (estimateData?.charger === null || estimateData?.charger?.trim().length === 0) { flag = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredCharger')) } if (estimateData?.objectName === null || estimateData?.objectName?.trim().length === 0) { flag = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredObjectName')) } if (isNaN(Date.parse(estimateData.estimateDate))) { flag = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredEstimateDate')) } @@ -177,6 +182,7 @@ export const useEstimateController = (planNo) => { let pkgAsp = estimateData.pkgAsp if (pkgAsp === '0') { flag = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredPkgAsp')) } } @@ -204,6 +210,7 @@ export const useEstimateController = (planNo) => { if (fileFlg) { if (estimateData.fileFlg === '0') { fileFlg = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredFileUpload')) } } @@ -221,6 +228,7 @@ export const useEstimateController = (planNo) => { if (item.addFlg) { if (item.itemId === '') { itemFlg = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredItemId')) } } @@ -236,6 +244,7 @@ export const useEstimateController = (planNo) => { if (item.amount < 1) { itemFlg = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredAmount')) } @@ -247,6 +256,7 @@ export const useEstimateController = (planNo) => { if (item.openFlg !== '1') { if (item.salePrice < 1) { itemFlg = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredSalePrice')) } } @@ -257,6 +267,7 @@ export const useEstimateController = (planNo) => { if (item.pkgMaterialFlg === '1') { if (isNaN(item.salePrice)) { itemFlg = false + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredSalePrice')) } } @@ -274,6 +285,7 @@ export const useEstimateController = (planNo) => { } }) if (delCnt === estimateData.itemList.length) { + setIsGlobalLoading(false) return alert(getMessage('estimate.detail.save.requiredItem')) } } @@ -292,6 +304,7 @@ export const useEstimateController = (planNo) => { await post({ url: '/api/file/fileUpload', data: formData }).then((res) => { setFileList(res) + setIsGlobalLoading(false) }) } else { setFileList([]) @@ -360,15 +373,12 @@ export const useEstimateController = (planNo) => { } //2. 상세데이터 저장 - // return try { + setIsGlobalLoading(true) await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => { - setIsGlobalLoading(true) if (res.status === 201) { estimateData.newFileList = [] - //알럿창 변경 swalFire({ text: getMessage('estimate.detail.save.alertMsg'), type: 'alert' }) - // alert(getMessage('estimate.detail.save.alertMsg')) fetchSetting(objectRecoil.floorPlanObjectNo, estimateData.planNo) } }) @@ -401,7 +411,6 @@ export const useEstimateController = (planNo) => { userId: session.userId, } - // return await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => { if (res.status === 201) { if (isObjectNotEmpty(res.data)) { diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index dc2488f2..d793e4f3 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -7,6 +7,7 @@ import { planSizeSettingState, dotLineGridSettingState, canvasSettingState, + currentMenuState, } from '@/store/canvasAtom' import { globalLocaleStore } from '@/store/localeAtom' import { useMessage } from '@/hooks/useMessage' @@ -23,13 +24,15 @@ import { selectedRoofMaterialSelector, addedRoofsState, } from '@/store/settingAtom' -import { POLYGON_TYPE } from '@/common/common' +import { MENU, POLYGON_TYPE } from '@/common/common' import { globalFontAtom } from '@/store/fontAtom' import { dimensionLineSettingsState } from '@/store/commonUtilsAtom' import { gridColorState } from '@/store/gridAtom' import { useColor } from 'react-color-palette' import { useMasterController } from '@/hooks/common/useMasterController' import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' +import { useCanvasMenu } from '../common/useCanvasMenu' +import { menuTypeState } from '@/store/menuAtom' const defaultDotLineGridSetting = { INTERVAL: { @@ -43,7 +46,7 @@ const defaultDotLineGridSetting = { LINE: false, } -let previousRoofMaterialsYn = 'N'; // 지붕재 select 정보 비교 후 변경된 것이 없으면 1회만 실행 +let previousRoofMaterialsYn = 'N' // 지붕재 select 정보 비교 후 변경된 것이 없으면 1회만 실행 export function useCanvasSetting() { const canvas = useRecoilValue(canvasState) @@ -73,7 +76,7 @@ export function useCanvasSetting() { ) const [gridColor, setGridColor] = useRecoilState(gridColorState) const [color, setColor] = useColor(gridColor ?? '#FF0000') - + const { menuNumber, setMenuNumber } = useCanvasMenu() const [settingsData, setSettingsData] = useState({ ...settingModalFirstOptions, ...settingModalSecondOptions, @@ -99,7 +102,8 @@ export function useCanvasSetting() { const { getRoofMaterialList, getModuleTypeItemList } = useMasterController() const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom) const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) - + const [type, setType] = useRecoilState(menuTypeState) + const setCurrentMenu = useSetRecoilState(currentMenuState) const SelectOptions = [ { id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 }, { id: 2, name: '1/2', value: 1 / 2 }, @@ -113,7 +117,7 @@ export function useCanvasSetting() { if (roofMaterials.length !== 0) { return } - addRoofMaterials() + addRoofMaterials() }, []) //지붕재 초기세팅 @@ -149,21 +153,21 @@ export function useCanvasSetting() { setBasicSettings({ ...basicSetting, selectedRoofMaterial: selectedRoofMaterial }) } - const previousRoofMaterialsRef = useRef(null); + const previousRoofMaterialsRef = useRef(null) useEffect(() => { // 지붕재 select 정보가 존재해야 배치면초기설정 DB 정보 비교 후 지붕재 정보를 가져올 수 있음 if (roofMaterials.length !== 0 && JSON.stringify(previousRoofMaterialsRef.current) !== JSON.stringify(roofMaterials)) { // 지붕재 select 정보 비교 후 변경된 것이 없으면 1회만 실행 if (roofMaterials && previousRoofMaterialsYn === 'N') { - fetchBasicSettings(); - previousRoofMaterialsYn = 'Y'; + fetchBasicSettings() + previousRoofMaterialsYn = 'Y' } // 이전 값을 업데이트 - previousRoofMaterialsRef.current = roofMaterials; + previousRoofMaterialsRef.current = roofMaterials } - }, [roofMaterials]); + }, [roofMaterials]) useEffect(() => { if (!canvas) { @@ -275,14 +279,13 @@ export function useCanvasSetting() { const fetchBasicSettings = async () => { try { await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}` }).then((res) => { - let roofsRow = {} let roofsArray = {} if (res.length > 0) { roofsRow = res.map((item) => { return { - roofSizeSet: item.roofSizeSet, + roofSizeSet: String(item.roofSizeSet), roofAngleSet: item.roofAngleSet, } }) @@ -296,12 +299,14 @@ export function useCanvasSetting() { roofHajebichi: item.roofHajebichi, roofGap: item.roofGap, roofLayout: item.roofLayout, + roofPitch: item.roofPitch, + roofAngle: item.roofAngle, } }) } else { roofsRow = [ { - roofSizeSet: 1, + roofSizeSet: '1', roofAngleSet: 'slope', }, ] @@ -316,6 +321,7 @@ export function useCanvasSetting() { roofHajebichi: 0, roofGap: 'HEI_455', roofLayout: 'P', + roofPitch: 4, }, ] } @@ -324,23 +330,30 @@ export function useCanvasSetting() { const addRoofs = [] roofMaterials?.map((material) => { if (material.roofMatlCd === roofsArray[0].roofMatlCd) { - addRoofs.push({ ...material, selected: true - , index: 0 - , width: roofsArray[0].roofWidth - , length: roofsArray[0].roofHeight - , hajebichi: roofsArray[0].roofHajebichi - , raft: roofsArray[0].roofGap - , layout: roofsArray[0].roofLayout - , roofSizeSet: roofsRow[0].roofSizeSet - , roofAngleSet: roofsRow[0].roofAngleSet - }) - + addRoofs.push({ + ...material, + selected: true, + index: 0, + width: roofsArray[0].roofWidth, + length: roofsArray[0].roofHeight, + hajebichi: roofsArray[0].roofHajebichi, + raft: roofsArray[0].roofGap, + layout: roofsArray[0].roofLayout, + roofSizeSet: roofsRow[0].roofSizeSet, + roofAngleSet: roofsRow[0].roofAngleSet, + pitch: roofsArray[0].roofPitch, + angle: roofsArray[0].roofAngle, + }) + setAddedRoofs(addRoofs) - setBasicSettings({ ...basicSetting, roofMaterials: addRoofs[0] - , roofSizeSet: roofsRow[0].roofSizeSet - , roofAngleSet: roofsRow[0].roofAngleSet - , roofsData: roofsArray - , selectedRoofMaterial: addRoofs[0] }) + setBasicSettings({ + ...basicSetting, + roofMaterials: addRoofs[0], + roofSizeSet: roofsRow[0].roofSizeSet, + roofAngleSet: roofsRow[0].roofAngleSet, + roofsData: roofsArray, + selectedRoofMaterial: addRoofs[0], + }) } }) }) @@ -359,20 +372,32 @@ export function useCanvasSetting() { try { const patternData = { objectNo: correntObjectNo, - roofSizeSet: basicSetting.roofSizeSet, + roofSizeSet: Number(basicSetting.roofSizeSet), roofAngleSet: basicSetting.roofAngleSet, roofMaterialsAddList: [ { roofApply: true, roofSeq: 0, - roofMatlCd: basicSetting.roofsData.roofMatlCd === null || basicSetting.roofsData.roofMatlCd === undefined ? 'ROOF_ID_WA_53A' : basicSetting.roofsData.roofMatlCd, - roofWidth: basicSetting.roofsData.roofWidth === null || basicSetting.roofsData.roofWidth === undefined ? 0 : basicSetting.roofsData.roofWidth, - roofHeight: basicSetting.roofsData.roofHeight === null || basicSetting.roofsData.roofHeight === undefined ? 0 : basicSetting.roofsData.roofHeight, - roofHajebichi: basicSetting.roofsData.roofHajebichi === null || basicSetting.roofsData.roofHajebichi === undefined ? 0 : basicSetting.roofsData.roofHajebichi, - roofGap: basicSetting.roofsData.roofGap === null || basicSetting.roofsData.roofGap === undefined ? 'HEI_455' : basicSetting.roofsData.roofGap, - roofLayout: basicSetting.roofsData.roofLayout === null || basicSetting.roofsData.roofLayout === undefined ? 'P' : basicSetting.roofsData.roofLayout, - roofPitch: 4, - roofAngle: 21.8, + roofMatlCd: + basicSetting.roofsData.roofMatlCd === null || basicSetting.roofsData.roofMatlCd === undefined + ? 'ROOF_ID_WA_53A' + : basicSetting.roofsData.roofMatlCd, + roofWidth: + basicSetting.roofsData.roofWidth === null || basicSetting.roofsData.roofWidth === undefined ? 0 : basicSetting.roofsData.roofWidth, + roofHeight: + basicSetting.roofsData.roofHeight === null || basicSetting.roofsData.roofHeight === undefined ? 0 : basicSetting.roofsData.roofHeight, + roofHajebichi: + basicSetting.roofsData.roofHajebichi === null || basicSetting.roofsData.roofHajebichi === undefined + ? 0 + : basicSetting.roofsData.roofHajebichi, + roofGap: + basicSetting.roofsData.roofGap === null || basicSetting.roofsData.roofGap === undefined ? 'HEI_455' : basicSetting.roofsData.roofGap, + roofLayout: + basicSetting.roofsData.roofLayout === null || basicSetting.roofsData.roofLayout === undefined ? 'P' : basicSetting.roofsData.roofLayout, + roofPitch: + basicSetting.roofsData.roofPitch === null || basicSetting.roofsData.roofPitch === undefined ? 0 : basicSetting.roofsData.roofPitch, + roofAngle: + basicSetting.roofsData.roofAngle === null || basicSetting.roofsData.roofAngle === undefined ? 0 : basicSetting.roofsData.roofAngle, }, ], } @@ -380,12 +405,23 @@ export function useCanvasSetting() { console.log('basicSettingSave patternData ', patternData) await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => { + console.log('basicSettingSave res ', res) swalFire({ text: getMessage(res.returnMessage) }) }) //Recoil 설정 - setCanvasSetting({ ...basicSetting }) + //setCanvasSetting({ ...basicSetting })\ + console.log(basicSetting) fetchBasicSettings() + if (['2', '3'].includes(basicSetting?.roofSizeSet)) { + setMenuNumber(3) + setType('surface') + setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) + } else { + setMenuNumber(2) + setType('outline') + setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) + } } catch (error) { swalFire({ text: error.message, icon: 'error' }) } diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index f0884969..2a46f355 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -33,9 +33,7 @@ export function useRoofAllocationSetting(id) { const { drawDirectionArrow, addLengthText, splitPolygonWithLines, splitPolygonWithSeparate } = usePolygon() const [popupId, setPopupId] = useState(uuidv4()) const { addPopup, closePopup, closeAll } = usePopup() - const { getMessage } = useMessage() - const currentObject = useRecoilValue(currentObjectState) - const { swalFire } = useSwal() + const currentObject = useRecoilValue(currentObjectState) const { setMenuNumber } = useCanvasMenu() const setMenuType = useSetRecoilState(menuTypeState) const roofMaterials = useRecoilValue(roofMaterialsSelector) @@ -46,9 +44,10 @@ export function useRoofAllocationSetting(id) { const [editingLines, setEditingLines] = useState([]) const [currentRoofList, setCurrentRoofList] = useState(roofList) const currentAngleType = useRecoilValue(currentAngleTypeSelector) - const globalLocaleState = useRecoilValue(globalLocaleStore) const { get, post } = useAxios(globalLocaleState) + const { getMessage } = useMessage() + const { swalFire } = useSwal() const { setSurfaceShapePattern } = useRoofFn() @@ -165,38 +164,38 @@ export function useRoofAllocationSetting(id) { // 지붕면 할당 저장 const basicSettingSave = async () => { - const patternData = { - objectNo: correntObjectNo, - roofSizeSet: basicSetting.roofSizeSet, - roofAngleSet: basicSetting.roofAngleSet, - roofMaterialsAddList: currentRoofList.map((item, index) => ({ - roofApply: item.selected === null || item.selected === undefined ? 'true' : item.selected, - roofSeq: index, - roofMatlCd: item.roofMatlCd === null || item.roofMatlCd === undefined ? 'ROOF_ID_WA_53A' : item.roofMatlCd, - roofWidth: item.width === null || item.width === undefined ? 0 : item.width, - roofHeight: item.length === null || item.length === undefined ? 0 : item.length, - roofHajebichi: item.hajebichi === null || item.hajebichi === undefined ? 0 : item.hajebichi, - roofGap: item.raft === null || item.raft === undefined ? 'HEI_455' : item.raft, - roofLayout: item.layout === null || item.layout === undefined ? 'P' : item.layout, - roofPitch: item.pitch === null || item.pitch === undefined ? 4 : item.pitch, - roofAngle: item.angle === null || item.angle === undefined ? 21.8 : item.angle, - })), - } + try { + const patternData = { + objectNo: correntObjectNo, + roofSizeSet: Number(basicSetting.roofSizeSet), + roofAngleSet: basicSetting.roofAngleSet, + roofAllocationList: currentRoofList.map((item, index) => ({ + roofApply: item.selected === null || item.selected === undefined ? 'true' : item.selected, + roofSeq: index, + roofMatlCd: item.roofMatlCd === null || item.roofMatlCd === undefined ? 'ROOF_ID_WA_53A' : item.roofMatlCd, + roofWidth: item.width === null || item.width === undefined ? 0 : Number(item.width), + roofHeight: item.length === null || item.length === undefined ? 0 : Number(item.length), + roofHajebichi: item.hajebichi === null || item.hajebichi === undefined ? 0 : Number(item.hajebichi), + roofGap: item.raft === null || item.raft === undefined ? 'HEI_455' : item.raft, + roofLayout: item.layout === null || item.layout === undefined ? 'P' : item.layout, + roofPitch: item.pitch === null || item.pitch === undefined ? 4 : Number(item.pitch), + roofAngle: item.angle === null || item.angle === undefined ? 21.8 : Number(item.angle), + })), + } console.log('🚀 ~ basicSettingSave ~ patternData >>>>>>>>>>>>> :', patternData) - // HTTP POST 요청 보내기 - await post({ url: `/api/canvas-management/roof-allocation-settings`, data: patternData }) - .then((res) => { - swalFire({ text: getMessage(res.returnMessage) }) + await post({ url: `/api/canvas-management/roof-allocation-settings`, data: patternData }).then((res) => { + console.log('roof-allocation-settings res ', res) + swalFire({ text: getMessage(res.returnMessage) }) + }) - //Recoil 설정 - // setCanvasSetting({ ...basicSetting }) - fetchBasicSettings() - }) - .catch((error) => { - swalFire({ text: error.message, icon: 'error' }) - }) + //Recoil 설정 + //setCanvasSetting({ ...basicSetting }) + fetchBasicSettings() + } catch (error) { + swalFire({ text: error.message, icon: 'error' }) + } } const onAddRoofMaterial = () => { @@ -457,6 +456,7 @@ export function useRoofAllocationSetting(id) { return } + const newRoofList = currentRoofList.map((roof, idx) => { if (idx === index) { return { ...roof, [type]: value } diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index cd2b662d..f7848be5 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -1,7 +1,7 @@ 'use client' import { useContext, useEffect, useState } from 'react' -import { usePathname, useRouter } from 'next/navigation' +import { usePathname, useRouter, useSearchParams } from 'next/navigation' import { useRecoilState } from 'recoil' import { v4 as uuidv4 } from 'uuid' @@ -218,16 +218,18 @@ export function usePlan(params = {}) { }), ) } + useEffect(() => { setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null) setSelectedPlan(plans.find((plan) => plan.isCurrent)) + handleCurrentPlanUrl() // setBgImage() }, [plans]) - // 현재 plan이 변경될 때 마다 현재 plan의 링크로 이동 - useEffect(() => { - handlePlanMove() - }, [currentCanvasPlan]) + const handleCurrentPlanUrl = () => { + const currentPlan = plans.find((plan) => plan.isCurrent) + if (currentPlan) router.push(`${pathname}?pid=${currentPlan?.ordering}&objectNo=${floorPlanState?.objectNo}`) + } const setBgImage = () => { // readImage(selectedPlan?.id) diff --git a/src/locales/ja.json b/src/locales/ja.json index abf15ebc..00cd7e31 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -168,6 +168,7 @@ "plan.menu.estimate.reset": "初期化", "plan.menu.estimate.copy": "見積書のコピー", "plan.menu.estimate.unLock": "ロック解除", + "plan.menu.estimate.lock": "ロック", "plan.menu.simulation": "発展シミュレーション", "plan.menu.simulation.excel": "Excel", "plan.menu.simulation.pdf": "PDF", @@ -931,7 +932,10 @@ "estimate.detail.save.requiredItemId": "製品を選択してください.", "estimate.detail.save.requiredAmount": "数量は0より大きい値を入力してください.", "estimate.detail.save.requiredSalePrice": "単価は0より大きい値を入力してください.", + "estimate.detail.reset.alertMsg": "初期化されました.", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", + "estimate.detail.lock.alertMsg": "見積書を[ロック]すると、変更できません。見積もりを編集するには、ロックを解除してください.", + "estimate.detail.unlock.alertMsg": "見積書を修正して保存", "estimate.detail.alert.delFile": "添付ファイルを完全に削除するには[保存]ボタンをクリックしてください", "estimate.detail.alert.selectDelItem": "削除する商品を選択してください.", "simulator.title.sub1": "物件番号", diff --git a/src/locales/ko.json b/src/locales/ko.json index 72d6f194..2a6fd228 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -172,6 +172,7 @@ "plan.menu.estimate.reset": "초기화", "plan.menu.estimate.copy": "견적서 복사", "plan.menu.estimate.unLock": "잠금 해제", + "plan.menu.estimate.lock": "잠금", "plan.menu.simulation": "발전 시뮬레이션", "plan.menu.simulation.excel": "Excel", "plan.menu.simulation.pdf": "PDF", @@ -940,7 +941,10 @@ "estimate.detail.save.requiredItemId": "제품을 선택해주세요.", "estimate.detail.save.requiredAmount": "수량은 0보다 큰값을 입력해주세요.", "estimate.detail.save.requiredSalePrice": "단가는 0보다 큰값을 입력해주세요.", + "estimate.detail.reset.alertMsg": "초기화 되었습니다.", "estimate.detail.reset.confirmMsg": "수기 변경(저장)한 견적 정보가 초기화되고, 최근 저장된 도면정보가 반영됩니다. 정말로 초기화하시겠습니까?", + "estimate.detail.lock.alertMsg": "견적서를 [잠금]하면, 수정할 수 없습니다. 견적서를 수정하려면 잠금해제를 하십시오", + "estimate.detail.unlock.alertMsg": "견적서를 수정하고, 저장하십시오", "estimate.detail.alert.delFile": "첨부파일을 완전히 삭제하려면 [저장]버튼을 클릭하십시오.", "estimate.detail.alert.selectDelItem": "삭제할 제품을 선택하세요.", "simulator.title.sub1": "물건번호", diff --git a/src/store/menuAtom.js b/src/store/menuAtom.js index 834498f0..2cf4b841 100644 --- a/src/store/menuAtom.js +++ b/src/store/menuAtom.js @@ -49,11 +49,11 @@ export const subMenusState = atom({ ], surface: [ // 배치면 - { id: 0, name: 'plan.menu.placement.surface.slope.setting', menu: MENU.BATCH_CANVAS.SLOPE_SETTING }, - { id: 1, name: 'plan.menu.placement.surface.drawing', menu: MENU.BATCH_CANVAS.BATCH_DRAWING }, - { id: 2, name: 'plan.menu.placement.surface.arrangement', menu: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH }, - { id: 3, name: 'plan.menu.placement.surface.object', menu: MENU.BATCH_CANVAS.OBJECT_BATCH }, - { id: 4, name: 'plan.menu.placement.surface.all.remove', menu: MENU.BATCH_CANVAS.ALL_REMOVE }, + // { id: 0, name: 'plan.menu.placement.surface.slope.setting', menu: MENU.BATCH_CANVAS.SLOPE_SETTING }, + { id: 0, name: 'plan.menu.placement.surface.drawing', menu: MENU.BATCH_CANVAS.BATCH_DRAWING }, + { id: 1, name: 'plan.menu.placement.surface.arrangement', menu: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH }, + { id: 2, name: 'plan.menu.placement.surface.object', menu: MENU.BATCH_CANVAS.OBJECT_BATCH }, + { id: 3, name: 'plan.menu.placement.surface.all.remove', menu: MENU.BATCH_CANVAS.ALL_REMOVE }, ], module: [ // 모듈, 회로 구성