'use client' import { useContext, useEffect, useState } from 'react' import { usePathname, useRouter, useSearchParams } from 'next/navigation' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { v4 as uuidv4 } from 'uuid' import MenuDepth01 from './MenuDepth01' import QSelectBox from '@/components/common/select/QSelectBox' import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal01' import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' import EstimateCopyPop from '../estimate/popup/EstimateCopyPop' import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { useMessage } from '@/hooks/useMessage' import { usePlan } from '@/hooks/usePlan' import { useSwal } from '@/hooks/useSwal' import { useEvent } from '@/hooks/useEvent' import { usePopup } from '@/hooks/usePopup' import { useCanvasEvent } from '@/hooks/useCanvasEvent' import { useCommonUtils } from '@/hooks/common/useCommonUtils' import useMenu from '@/hooks/common/useMenu' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { useAxios } from '@/hooks/useAxios' import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' import { sessionStore } from '@/store/commonAtom' import { outerLinePointsState } from '@/store/outerLineAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' import { addedRoofsState, basicSettingState, corridorDimensionSelector, selectedRoofMaterialSelector, settingModalFirstOptionsState, } from '@/store/settingAtom' import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom' import { commonUtilsState } from '@/store/commonUtilsAtom' import { menusState } from '@/store/menuAtom' import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { pwrGnrSimTypeState } from '@/store/simulatorAtom' import { isObjectNotEmpty } from '@/util/common-utils' import { POLYGON_TYPE } from '@/common/common' import KO from '@/locales/ko.json' import JA from '@/locales/ja.json' import { QcastContext } from '@/app/QcastProvider' import { useRoofFn } from '@/hooks/common/useRoofFn' import { usePolygon } from '@/hooks/usePolygon' export default function CanvasMenu(props) { const { selectedMenu, setSelectedMenu } = props const pathname = usePathname() const router = useRouter() const { addPopup } = usePopup() const canvasMenus = useRecoilValue(menusState) const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) const setAppMessageState = useSetRecoilState(appMessageStore) const setCurrentMenu = useSetRecoilState(currentMenuState) const setOuterLinePoints = useSetRecoilState(outerLinePointsState) const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState) const canvasSetting = useRecoilValue(canvasSettingState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const sessionState = useRecoilValue(sessionStore) const globalLocale = useRecoilValue(globalLocaleStore) const canvas = useRecoilValue(canvasState) const { handleZoomClear, handleZoom } = useCanvasEvent() const { handleMenu } = useMenu() // const urlParams = useSearchParams() const { handleEstimateSubmit, fetchSetting, estimateContextState, setEstimateContextState } = useEstimateController() const estimateRecoilState = useRecoilValue(estimateState) const [estimatePopupOpen, setEstimatePopupOpen] = useState(false) const [estimateCopyPopupOpen, setEstimateCopyPopupOpen] = useState(false) const { getMessage } = useMessage() const { saveCanvas, reloadCanvasStatus } = usePlan() const { swalFire } = useSwal() const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useEvent() // const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useContext(EventContext) const commonUtils = useRecoilValue(commonUtilsState) const { commonFunctions } = useCommonUtils() const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext) const [addedRoofs, setAddedRoofsState] = useRecoilState(addedRoofsState) const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState) const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) //견적서버튼 노출용 const [docDownButtonStyle, setDocDownButtonStyle] = useState('') //문서 다운로드 버튼 const [saveButtonStyle, setSaveButtonStyle] = useState('') //저장 버튼 const [resetButtonStyle, setResetButtonStyle] = useState('') //초기화 버튼 const [copyButtonStyle, setCopyButtonStyle] = useState('') //견적서 복사 버튼 const [lockButtonStyle, setLockButtonStyle] = useState('') //잠금 버튼 const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //견적서 화면용 물건번호리코일 // 발전시뮬레이션 메뉴 이동 const { objectNo, pid } = floorPlanState // 발전시물레이션 Excel/PDF 다운 const { promiseGet, promisePost } = useAxios(globalLocale) const pwrGnrSimTypeRecoil = useRecoilValue(pwrGnrSimTypeState) const { setIsGlobalLoading } = useContext(QcastContext) const { setSurfaceShapePattern } = useRoofFn() const { drawDirectionArrow } = usePolygon() //임시 const { selectedPlan } = usePlan() const handleExcelPdfFileDown = async (donwloadType, drawingFlg) => { const url = '/api/pwrGnrSimulation/excel-download' const params = { objectNo: objectNo, planNo: selectedPlan.planNo, schDownload: donwloadType, schDrawingFlg: drawingFlg, pwrGnrSimType: pwrGnrSimTypeRecoil.type, } const options = { responseType: 'blob' } setIsGlobalLoading(true) await promisePost({ url: url, data: params, option: options }) .then((resultData) => { if (resultData) { let fileName = 'unknow' const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' }) const fileUrl = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = fileUrl //서버에서 내려오는 파일명 const contentDisposition = resultData.headers['content-disposition'] if (contentDisposition) { fileName = contentDisposition.split('filename=')[1].replace(/['"]/g, '') } link.download = fileName document.body.appendChild(link) link.click() link.remove() window.URL.revokeObjectURL(fileUrl) } }) .catch((error) => { alert('File does not exist.') }) setIsGlobalLoading(false) } // roof 초기화 const initRoofs = () => { const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) roofs.forEach((roof) => { roof.set({ selectable: true }) setSurfaceShapePattern(roof, null, false, roof.roofMaterial) delete roof.moduleCompass drawDirectionArrow(roof, false) }) } const onClickNav = async (menu) => { switch (menu.type) { case 'drawing': swalFire({ text: getMessage('stuff.detail.move.confirmMsg'), type: 'confirm', confirmFn: () => { setIsGlobalLoading(true) router.push(`/management/stuff/detail?objectNo=${objectNo}`, { scroll: false }) }, }) break case 'placement': onClickPlacementInitialMenu() await reloadCanvasStatus(objectNo, pid) break case 'outline': await reloadCanvasStatus(objectNo, pid) break case 'surface': const modules = canvas .getObjects() .filter((obj) => [POLYGON_TYPE.MODULE_SETUP_SURFACE, POLYGON_TYPE.MODULE, POLYGON_TYPE.OBJECT_SURFACE].includes(obj.name)) if (modules.length > 0) { swalFire({ text: getMessage('module.delete.confirm'), type: 'confirm', confirmFn: () => { //해당 메뉴 이동시 배치면 삭제 const moduleSurfacesArray = canvas .getObjects() .filter((obj) => [POLYGON_TYPE.MODULE_SETUP_SURFACE, POLYGON_TYPE.MODULE, POLYGON_TYPE.OBJECT_SURFACE].includes(obj.name)) if (moduleSurfacesArray.length > 0) { // 모듈면 있을 경우 지붕면 할당 다시해야함 moduleSurfacesArray.forEach((moduleSurface) => { canvas.remove(moduleSurface) }) initRoofs() canvas.renderAll() onClickNav(menu) } }, denyFn: () => { return }, }) return } setSelectedMenu(menu.type) await reloadCanvasStatus(objectNo, pid) break case 'module': if (['placement', 'outline', 'surface'].some((menu) => menu === selectedMenu)) { if (!checkMenuAndCanvasState()) { swalFire({ text: getMessage('menu.validation.canvas.roof') }) return } else { setSelectedMenu('module') } } else { router.push(`/floor-plan?pid=${pid}&objectNo=${objectNo}`) setSelectedMenu('module') } await reloadCanvasStatus(objectNo, pid) break case 'estimate': setIsGlobalLoading(true) promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan.planNo}/detail` }).then((res) => { if (res.status === 200) { const estimateDetail = res.data if (estimateDetail.estimateDate !== null) { setSelectedMenu(menu.type) setCurrentMenu(menu.title) setFloorPlanObjectNo({ floorPlanObjectNo: objectNo }) setIsGlobalLoading(false) router.push(`/floor-plan/estimate/5?pid=${selectedPlan.planNo}&objectNo=${objectNo}`) if (pathname === '/floor-plan/estimate/5') { setIsGlobalLoading(false) } } else { setIsGlobalLoading(false) swalFire({ text: getMessage('estimate.menu.move.valid1') }) } } }) break case 'simulation': setIsGlobalLoading(true) promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan.planNo}/detail` }).then((res) => { if (res.status === 200) { const estimateDetail = res.data if (estimateDetail.estimateDate !== null && estimateDetail.docNo) { setSelectedMenu(menu.type) setCurrentMenu(menu.title) router.push(`/floor-plan/simulator/6?pid=${selectedPlan.planNo}&objectNo=${objectNo}`) if (pathname === '/floor-plan/simulator/6') { setIsGlobalLoading(false) } } else { setIsGlobalLoading(false) swalFire({ text: getMessage('simulator.menu.move.valid1') }) } } }) break } if (menu.type !== 'simulation' && menu.type !== 'estimate' && menu.type !== 'drawing') { setSelectedMenu(menu.type) setCurrentMenu(menu.title) } if (pathname !== '/floor-plan') { //견적서 or 발전시뮬레이션 탭에서 같은 탭 클릭시 화면 이동했다 돌아오지않도록.. if (menu.type !== 'drawing' && menu.type !== 'estimate' && menu.type !== 'simulation') { router.push(`/floor-plan?pid=${pid}&objectNo=${objectNo}`) } } } const changeSelectedRoofMaterial = (e) => { setBasicSetting({ ...basicSetting, selectedRoofMaterial: e }) const newAddedRoofs = addedRoofs.map((roof) => { if (roof.index === e.index) { return { ...roof, selected: true } } else { return { ...roof, selected: false } } }) setAddedRoofsState(newAddedRoofs) } const settingsModalOptions = useRecoilState(settingModalFirstOptionsState) useEffect(() => { console.log(selectedMenu) if (selectedMenu === 'placement') { onClickPlacementInitialMenu() } }, [selectedMenu]) // 저장버튼(btn08) 클릭 시 호출되는 함수 const handleSaveCanvas = async () => { await saveCanvas(true) } // 나가기 버튼 클릭 const handleLeaveCanvas = () => { swalFire({ text: getMessage('plan.message.leave'), type: 'confirm', confirmButtonText: getMessage('plan.message.corfirm.yes'), cancelButtonText: getMessage('plan.message.confirm.no'), confirmFn: async () => { await handleSaveCanvas() router.push(`/management/stuff`) }, }) } const [placementInitialId, setPlacementInitialId] = useState(uuidv4()) const placementInitialProps = { id: placementInitialId, pos: { x: 50, y: 180, }, planNo: selectedPlan?.planNo ? selectedPlan.planNo : pid, openPoint: 'canvasMenus', } /** * 배치면 초기설정 팝업 열기 */ const onClickPlacementInitialMenu = () => { addPopup(placementInitialId, 1, ) } const handleClear = () => { setOuterLinePoints([]) setPlacementPoints([]) canvas?.clear() } // // const handleZoomClear = () => { // setCanvasZoom(100) // canvas.set({ zoom: 1 }) // canvas.viewportTransform = [1, 0, 0, 1, 0, 0] // canvas.renderAll() // } const handlePopup = () => { const id = uuidv4() addPopup(id, 1, , true) } // 견적서 초기화 버튼 const handleEstimateReset = () => { swalFire({ text: getMessage('estimate.detail.reset.confirmMsg'), type: 'confirm', confirmFn: async () => { setIsGlobalLoading(true) const params = { objectNo: estimateRecoilState.objectNo, planNo: estimateRecoilState.planNo, userId: sessionState.userId, } try { await promisePost({ url: '/api/estimate/reset-estimate', data: params }).then((res) => { if (res.status === 201) { swalFire({ text: getMessage('estimate.detail.reset.alertMsg'), type: 'alert' }) fetchSetting(estimateRecoilState.objectNo, estimateRecoilState.planNo, 'R') } }) } catch (error) { setIsGlobalLoading(false) console.log('error::::::::::::', e.response.data.message) } }, denyFn: () => { setIsGlobalLoading(false) }, }) } useEffect(() => { if (globalLocale === 'ko') { setAppMessageState(KO) } else { setAppMessageState(JA) } }, [globalLocale]) const checkMenuState = (menu) => { return ( (['2', '3'].includes(canvasSetting?.roofSizeSet) && menu.type === 'outline') || (selectedMenu === 'module' && ['placement', 'outline'].includes(menu.type)) || (isExistModule() && ['placement', 'outline'].some((num) => num === menu.type)) || (['estimate', 'simulation'].includes(selectedMenu) && ['placement', 'outline', 'surface'].includes(menu.type)) ) } const isExistModule = () => { const modules = canvas?.getObjects().filter((module) => module.name === POLYGON_TYPE.MODULE) ?? [] return modules.length > 0 } /** * 모듈, 회로 구성 이전 메뉴에서 메뉴 클릭으로 넘어올때 * 지붕면 할당이 끝난 지붕이 하나라도 있는지 체크 * @returns {boolean} */ const checkMenuAndCanvasState = () => { const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // 지붕면 할당이 끝난 지붕이 하나라도 있는지 체크 const isExist = roofs?.some((roof) => roof.roofMaterial) console.log('🚀 ~ checkMenuAndCanvasState ~ isExist:', isExist) return isExist } useEffect(() => { if (isObjectNotEmpty(estimateContextState)) { const { createUser, tempFlg, lockFlg, docNo } = estimateContextState if (createUser && tempFlg && lockFlg) { if (createUser === 'T01' && sessionState.storeId !== 'T01') { setAllButtonStyles('none') } else { handleButtonStyles(tempFlg, lockFlg, docNo) } } } }, [estimateContextState?.createUser, estimateContextState?.tempFlg, estimateContextState?.lockFlg, estimateContextState.docNo]) const setAllButtonStyles = (style) => { setDocDownButtonStyle(style) setSaveButtonStyle(style) setResetButtonStyle(style) setCopyButtonStyle(style) setLockButtonStyle(style) } const handleButtonStyles = (tempFlg, lockFlg, docNo) => { if (tempFlg === '1') { setAllButtonStyles('none') setSaveButtonStyle('') } else if (tempFlg === '0' && lockFlg === '0') { setAllButtonStyles('') } else { setDocDownButtonStyle('') setSaveButtonStyle('none') setResetButtonStyle('none') setCopyButtonStyle('') setLockButtonStyle('') } if (!docNo) { setDocDownButtonStyle('none') } } /** * 견적서 잠금 / 해제 * lockFlg : 0 잠금해제상태 / 1 잠금상태 * --보낼때-- * 0잠금해제는 1잠금으로 * 1잠금 문서는 0 잠금해제로 */ const handleEstimateLockController = (estimateRecoilState) => { swalFire({ html: estimateRecoilState.lockFlg === '0' ? getMessage('estimate.detail.lock.alertMsg') : getMessage('estimate.detail.unlock.alertMsg'), confirmButtonText: estimateRecoilState.lockFlg === '1' ? getMessage('estimate.detail.unlock.confirmBtnName') : '', 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' const { createUser, tempFlg, lockFlg } = estimateRecoilState if (createUser && tempFlg && lockFlg) { if (createUser === 'T01' && sessionState.storeId !== 'T01') { setAllButtonStyles('none') } else { setEstimateContextState({ tempFlg: estimateRecoilState.tempFlg, lockFlg: estimateRecoilState.lockFlg }) handleButtonStyles(estimateRecoilState.tempFlg, estimateRecoilState.lockFlg, estimateContextState.docNo) } } } setIsGlobalLoading(false) }) } catch (error) { setIsGlobalLoading(false) console.log('error::::::::::::', e.response.data.message) } }, }) } // 문서다운로드 팝업에서 다운로드 하면 문서 잠금 const docDownPopLockFlg = () => { setDocDownButtonStyle('') setSaveButtonStyle('none') setResetButtonStyle('none') setCopyButtonStyle('') setLockButtonStyle('') } return (
num === selectedMenu) ? 'active' : ''}`}>
    {canvasMenus.map((menu) => { return (
  • { if (checkMenuState(menu)) return await onClickNav(menu) }} >
  • ) })}
{!['estimate', 'simulation'].some((num) => num === selectedMenu) && ( <> {
{getMessage('plan.mode.vertical.horizontal')}
}
{isObjectNotEmpty(selectedRoofMaterial) && addedRoofs.length > 0 && (
{ { return { ...roof, name: globalLocale === 'ko' ? roof.roofMatlNm : roof.roofMatlNmJp } }) } showKey={'name'} value={selectedRoofMaterial} onChange={changeSelectedRoofMaterial} sourceKey={'index'} targetKey={'index'} disabled={+basicSetting.roofSizeSet === 3} /> }
)}
{/**/}
{canvasZoom}%
)} {selectedMenu === 'estimate' && ( <>
{estimateRecoilState?.docNo !== null && (sessionState.storeId === 'T01' || sessionState.storeLvl === '1') && ( )}
)} {selectedMenu === 'simulation' && ( <>
)}
num === selectedMenu) ? 'active' : ''}`}> {['outline', 'surface', 'module'].some((num) => num === selectedMenu) && }
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */} {estimatePopupOpen && ( )} {/* 견적서(menuNumber ===5)복사 팝업 */} {estimateCopyPopupOpen && }
) }