diff --git a/docs/dictionary.txt b/docs/dictionary.txt new file mode 100644 index 00000000..cef0921e --- /dev/null +++ b/docs/dictionary.txt @@ -0,0 +1,26 @@ +지붕재: roofMaterial +할당: allocation +외벽선: WallLine +지붕선: roofLine +지붕면: roofSurface +회로번호: circuitNumber +흐름방향: flowDirection +가대: trestle +집계표: summaryTable +복도치수(입력치수): inputSize +실제치수: actualSize +테두리만: borderOnly +라인해치: lineHatching +문자글꼴: textFont +흐름방향글꼴 : flowDirectionFont +회로번호글꼴: circuitNumberFont +치수글꼴: sizeFont +출폭: offset +폭: width +경사(구배): pitch +이구배: doublePitch +소매: sleeve +개구: openSpace +도머: dormer +그림자: shadow + diff --git a/package.json b/package.json index 78fd71b3..5355cae6 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,14 @@ "react-colorful": "^5.6.1", "react-datepicker": "^7.3.0", "react-dom": "^18", - "react-hook-form": "^7.53.0", "react-draggable": "^4.4.6", + "react-hook-form": "^7.53.0", "react-icons": "^5.3.0", "react-responsive-modal": "^6.4.2", "react-toastify": "^10.0.5", "recoil": "^0.7.7", + "sweetalert2": "^11.14.1", + "sweetalert2-react-content": "^5.0.7", "uuid": "^10.0.0" }, "devDependencies": { diff --git a/public/static/images/canvas/grid_option_arr.svg b/public/static/images/canvas/grid_option_arr.svg new file mode 100644 index 00000000..c2c6cbbd --- /dev/null +++ b/public/static/images/canvas/grid_option_arr.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/static/images/common/Logo.svg b/public/static/images/common/Logo.svg index a72d0b4e..fa04f4d3 100644 --- a/public/static/images/common/Logo.svg +++ b/public/static/images/common/Logo.svg @@ -1,17 +1,17 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/public/static/images/common/datepicker.svg b/public/static/images/common/datepicker.svg new file mode 100644 index 00000000..e892f202 --- /dev/null +++ b/public/static/images/common/datepicker.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/static/images/common/select_light_arr.svg b/public/static/images/common/select_light_arr.svg new file mode 100644 index 00000000..97afb284 --- /dev/null +++ b/public/static/images/common/select_light_arr.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/static/images/main/clock.svg b/public/static/images/main/clock.svg new file mode 100644 index 00000000..3d005f59 --- /dev/null +++ b/public/static/images/main/clock.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/main/download.svg b/public/static/images/main/download.svg new file mode 100644 index 00000000..42d1d127 --- /dev/null +++ b/public/static/images/main/download.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/main/drawing_icon.svg b/public/static/images/main/drawing_icon.svg new file mode 100644 index 00000000..0e66181f --- /dev/null +++ b/public/static/images/main/drawing_icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/static/images/main/home_icon.svg b/public/static/images/main/home_icon.svg new file mode 100644 index 00000000..107bc4f5 --- /dev/null +++ b/public/static/images/main/home_icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/main/id_delete.svg b/public/static/images/main/id_delete.svg new file mode 100644 index 00000000..3e5d6119 --- /dev/null +++ b/public/static/images/main/id_delete.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/main/id_icon.svg b/public/static/images/main/id_icon.svg new file mode 100644 index 00000000..a84b6172 --- /dev/null +++ b/public/static/images/main/id_icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/static/images/main/loaction_arr.svg b/public/static/images/main/loaction_arr.svg new file mode 100644 index 00000000..590932c8 --- /dev/null +++ b/public/static/images/main/loaction_arr.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/static/images/main/login-arr.svg b/public/static/images/main/login-arr.svg new file mode 100644 index 00000000..7de03bcf --- /dev/null +++ b/public/static/images/main/login-arr.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/static/images/main/login-logo.svg b/public/static/images/main/login-logo.svg new file mode 100644 index 00000000..eb5d29f4 --- /dev/null +++ b/public/static/images/main/login-logo.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/main/login_bg.png b/public/static/images/main/login_bg.png new file mode 100644 index 00000000..e78e68d7 Binary files /dev/null and b/public/static/images/main/login_bg.png differ diff --git a/public/static/images/main/login_id.svg b/public/static/images/main/login_id.svg new file mode 100644 index 00000000..74af51d6 --- /dev/null +++ b/public/static/images/main/login_id.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/main/login_password.svg b/public/static/images/main/login_password.svg new file mode 100644 index 00000000..ac4757ac --- /dev/null +++ b/public/static/images/main/login_password.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/main/mail.svg b/public/static/images/main/mail.svg new file mode 100644 index 00000000..c55567f4 --- /dev/null +++ b/public/static/images/main/mail.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/main/main_search.svg b/public/static/images/main/main_search.svg new file mode 100644 index 00000000..25f56b42 --- /dev/null +++ b/public/static/images/main/main_search.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/static/images/main/more_btn.svg b/public/static/images/main/more_btn.svg new file mode 100644 index 00000000..5102903f --- /dev/null +++ b/public/static/images/main/more_btn.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/static/images/main/password_hidden.svg b/public/static/images/main/password_hidden.svg new file mode 100644 index 00000000..28aeac74 --- /dev/null +++ b/public/static/images/main/password_hidden.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/static/images/main/password_visible.svg b/public/static/images/main/password_visible.svg new file mode 100644 index 00000000..dcbc2a99 --- /dev/null +++ b/public/static/images/main/password_visible.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/main/product_ico01.svg b/public/static/images/main/product_ico01.svg new file mode 100644 index 00000000..c3a25714 --- /dev/null +++ b/public/static/images/main/product_ico01.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/static/images/main/product_ico02.svg b/public/static/images/main/product_ico02.svg new file mode 100644 index 00000000..7391dbdb --- /dev/null +++ b/public/static/images/main/product_ico02.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/main/product_ico03.svg b/public/static/images/main/product_ico03.svg new file mode 100644 index 00000000..d2ca10e1 --- /dev/null +++ b/public/static/images/main/product_ico03.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/static/images/main/product_ico04.svg b/public/static/images/main/product_ico04.svg new file mode 100644 index 00000000..da17d7a1 --- /dev/null +++ b/public/static/images/main/product_ico04.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/main/product_ico05.svg b/public/static/images/main/product_ico05.svg new file mode 100644 index 00000000..26b0e3d6 --- /dev/null +++ b/public/static/images/main/product_ico05.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/static/images/main/store-arr.svg b/public/static/images/main/store-arr.svg new file mode 100644 index 00000000..96487334 --- /dev/null +++ b/public/static/images/main/store-arr.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/static/images/main/user.svg b/public/static/images/main/user.svg new file mode 100644 index 00000000..a421788a --- /dev/null +++ b/public/static/images/main/user.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/static/images/sub/copy_grid_ico.svg b/public/static/images/sub/copy_grid_ico.svg new file mode 100644 index 00000000..68b34dca --- /dev/null +++ b/public/static/images/sub/copy_grid_ico.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/sub/copy_ico.svg b/public/static/images/sub/copy_ico.svg new file mode 100644 index 00000000..f259e2a0 --- /dev/null +++ b/public/static/images/sub/copy_ico.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/sub/file_delete.svg b/public/static/images/sub/file_delete.svg new file mode 100644 index 00000000..721bcc02 --- /dev/null +++ b/public/static/images/sub/file_delete.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/static/images/sub/pagination_first.svg b/public/static/images/sub/pagination_first.svg new file mode 100644 index 00000000..61073b50 --- /dev/null +++ b/public/static/images/sub/pagination_first.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/static/images/sub/pagination_prev.svg b/public/static/images/sub/pagination_prev.svg new file mode 100644 index 00000000..e162ae42 --- /dev/null +++ b/public/static/images/sub/pagination_prev.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/static/images/sub/tooltips.svg b/public/static/images/sub/tooltips.svg new file mode 100644 index 00000000..d4634f25 --- /dev/null +++ b/public/static/images/sub/tooltips.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/floor-plan/FloorPlanProvider.js b/src/app/floor-plan/FloorPlanProvider.js new file mode 100644 index 00000000..cb23116b --- /dev/null +++ b/src/app/floor-plan/FloorPlanProvider.js @@ -0,0 +1,9 @@ +'ues client' + +import { ErrorBoundary } from 'next/dist/client/components/error-boundary' +import ServerError from '../error' + +export const FloorPlanProvider = ({ children }) => { + console.log('FloorPlanProvider') + return }>{children} +} diff --git a/src/app/floor-plan/layout.js b/src/app/floor-plan/layout.js new file mode 100644 index 00000000..829599af --- /dev/null +++ b/src/app/floor-plan/layout.js @@ -0,0 +1,11 @@ +'use client' +import { FloorPlanProvider } from './FloorPlanProvider' + +export default function FloorPlanLayout({ children }) { + console.log('FloorPlanLayout') + return ( + <> + {children} + + ) +} diff --git a/src/app/layout.js b/src/app/layout.js index 30a6e8ee..b77e75d0 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -1,4 +1,4 @@ -import { Inter } from 'next/font/google' +// import { Inter } from 'next/font/google' import { headers } from 'next/headers' import { redirect } from 'next/navigation' @@ -8,14 +8,15 @@ import UIProvider from './UIProvider' import { ToastContainer } from 'react-toastify' +import { QcastProvider } from './QcastProvider' import Header from '@/components/header/Header' import QModal from '@/components/common/modal/QModal' -import { QcastProvider } from './QcastProvider' import './globals.css' import '../styles/style.scss' +import Dimmed from '@/components/ui/Dimmed' -const inter = Inter({ subsets: ['latin'] }) +// const inter = Inter({ subsets: ['latin'] }) export const metadata = { title: 'Create Next App', @@ -30,27 +31,50 @@ export default async function RootLayout({ children }) { // const isLoggedIn = await checkSession() const session = await getSession() console.log('session[layout]:', session) + + let sessionProps = {} + + if (session.isLoggedIn) { + sessionProps = { + userId: session.userId, + saleStoreId: session.saleStoreId, + name: session.name, + mail: session.mail, + tel: session.tel, + storeId: session.storeId, + userNm: session.userNm, + userNmKana: session.userNmKana, + category: session.category, + telNo: session.telNo, + fax: session.fax, + email: session.email, + pwdInitYn: session.pwdInitYn, + isLoggedIn: session.isLoggedIn, + } + } + if (!headerPathname.includes('/login') && !session.isLoggedIn) { redirect('/login') } return ( - - - {/*{headerPathname !== '/login' && }*/} - + + + + {/*{headerPathname !== '/login' && }*/}
-
-
- +
+ +
+ {children} - -
+
+
-
- - + + +
) } diff --git a/src/common/common.js b/src/common/common.js index c78673ee..84cf60a3 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -1,21 +1,30 @@ export const MENU = { + PLAN_DRAWING: 'planDrawing', INITIAL_CANVAS_SETTING: 'initialCanvasSetting', // 배치면 초기설정 ROOF_COVERING: { EXTERIOR_WALL_LINE: 'exteriorWallLine', // 외벽선 그리기 ROOF_SHAPE_SETTINGS: 'roofShapeSettings', // 지붕형상 설정 + ROOF_SHAPE_PASSIVITY_SETTINGS: 'roofShapePassivitySettings', // 지붕형상 수동설정 ROOF_SHAPE_EDITING: 'roofShapeEditing', // 지붕형상 편집 HELP_LINE_DRAWING: 'helpLineDrawing', // 보조선 그리기 + EAVES_KERAVA_EDIT: 'eavesKeravaEdit', // 처마.케라마 변경 + MOVEMENT_SHAPE_UPDOWN: 'movementShapeUpdown', // 동선이동.형올림내림 + OUTLINE_EDIT_OFFSET: 'outlineEditOffset', // 외벽선 편집 및 오프셋 + ROOF_SHAPE_ALLOC: 'rootShapeAlloc', // 지붕면 항당 DEFAULT: 'roofCoveringDefault', // 아무것도 선택 안할 경우 }, // 지붕덮개 BATCH_CANVAS: { + SLOPE_SETTING: 'slopeSetting', // 경사 설정 BATCH_DRAWING: 'batchDrawing', // 배치면 그리기 SURFACE_SHAPE_BATCH: 'surfaceShapeBatch', // 면형상 배치 OBJECT_BATCH: 'objectBatch', // 오브젝트 배치 + ALL_REMOVE: 'allRemove', // 전체 삭제 DEFAULT: 'batchCanvasDefault', // default }, // 배치면 MODULE_CIRCUIT_SETTING: { BASIC_SETTING: 'basicSetting', // 기본설정 CIRCUIT_TRESTLE_SETTING: 'circuitTrestleSetting', // 회로가대설정 + PLAN_ORIENTATION: 'planOrientation', // 도면 방위적용 DEFAULT: 'moduleCircuitSettingDefault', }, // 모듈회로구성 ESTIMATE: 'estimate', // todo 견적서 @@ -41,6 +50,25 @@ export const Mode = { DEFAULT: 'default', } +export const LINE_TYPE = { + WALLLINE: { + /** + * 처마 / 캐라바 / 벽 / 팔작지붕 / 반절처 / 한쪽흐름 + */ + EAVES: 'eaves', + GABLE: 'gable', + WALL: 'wall', + HIPANDGABLE: 'hipAndGable', + JERKINHEAD: 'jerkinhead', + SHED: 'shed', + }, + SUBLINE: { + /** + * + */ + }, +} + export const LineType = { EAVES: 'eaves', // 처마 RIDGE: 'ridge', // 용마루.... diff --git a/src/components/Intro.jsx b/src/components/Intro.jsx index 840aff7a..a6825af7 100644 --- a/src/components/Intro.jsx +++ b/src/components/Intro.jsx @@ -14,10 +14,11 @@ import { Button } from '@nextui-org/react' import SingleDatePicker from './common/datepicker/SingleDatePicker' import RangeDatePicker from './common/datepicker/RangeDatePicker' import QGrid from './common/grid/QGrid' -import { toastUp } from '@/hooks/useToast' +import { useSwal } from '@/hooks/useSwal' export default function Intro() { const { get } = useAxios() + const { swalFire } = useSwal() // const [open, setOpen] = useState(false) const [startDate, setStartDate] = useState(new Date()) @@ -127,9 +128,8 @@ export default function Intro() { + +
+ +
+
+ +
) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 3cfff65c..c5f7d147 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -135,6 +135,7 @@ export default function Roof2(props) { createRoofRack, drawRoofPolygon, drawCellInTrestle, + drawCellManualInTrestle, setDirectionTrestles, cutHelpLines, } = useMode() @@ -638,6 +639,32 @@ export default function Roof2(props) { const setDirectionStringToArrow = () => { drawDirectionStringToArrow(canvas, globalCampass) + + /** + * 나중에 유틸로 다시 구현 + */ + // const groupShapes = canvas?.getObjects().filter((obj) => obj.name === 'cellGroup') + + // console.log('groupShapes', groupShapes) + + // groupShapes.forEach((obj) => { + // let originAngle = obj._objects.find((array) => array.originAngle !== undefined).originAngle + + // console.log('originAngle', originAngle) + + // let rotateAngle = globalCampass + + // // let rotateAngle = originAngle + globalCampass + // // if (rotateAngle > 360) { + // // rotateAngle -= 360 + // // } + + // console.log('rotateAngle', rotateAngle) + + // obj.set({ angle: rotateAngle, originX: 'center', originY: 'center' }) + // obj.setCoords() + // }) + canvas?.renderAll() } // 팔작지붕, 합각지붕 @@ -842,6 +869,9 @@ export default function Roof2(props) { + +
  • handleClickSelectOption(option)}> +
  • ))} diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index f9bee2a4..9c8c9d38 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -1,14 +1,32 @@ +'use client' + +import { useEffect, useRef } from 'react' + import { useCanvas } from '@/hooks/useCanvas' -import { useRef } from 'react' import { useEvent } from '@/hooks/useEvent' -export default function CanvasFrame() { +export default function CanvasFrame({ plan }) { const canvasRef = useRef(null) - useCanvas('canvas') + const { canvas } = useCanvas('canvas') useEvent() + const loadCanvas = () => { + if (canvas) { + canvas?.clear() // 캔버스를 초기화합니다. + if (plan?.canvasStatus) { + canvas?.loadFromJSON(JSON.parse(plan.canvasStatus), function () { + canvas?.renderAll() // 캔버스를 다시 그립니다. + }) + } + } + } + + useEffect(() => { + loadCanvas() + }, [plan]) + return ( -
    +
    ) diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 9990aa21..5d3817a2 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -1,43 +1,86 @@ 'use client' import { useEffect, useState } from 'react' -import CanvasFrame from './CanvasFrame' import { useRecoilState, useRecoilValue } from 'recoil' -import { currentMenuState, stepState } from '@/store/canvasAtom' +import CanvasFrame from './CanvasFrame' +import { usePlan } from '@/hooks/usePlan' +import { globalLocaleStore } from '@/store/localeAtom' +import { currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom' export default function CanvasLayout() { - const [plans, setPlans] = useState([ - { id: 0, name: 'Plan 1', isCurrent: false }, - { id: 1, name: 'Plan 2', isCurrent: false }, - { id: 2, name: 'Plan 3', isCurrent: false }, - ]) - const [idxNum, setIdxNum] = useState(null) + const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요 + const [addCanvasPlans, setAddCanvasPlans] = useState([]) + const [planNum, setPlanNum] = useState(0) + const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) + const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) + const globalLocaleState = useRecoilValue(globalLocaleStore) - const onClickPlane = (num) => { - setIdxNum(num) + const { getCanvasByObjectNo } = usePlan() + + const handleCurrentPlan = (newCurrentId) => { + if (!currentCanvasPlan?.id || currentCanvasPlan.id !== newCurrentId) { + setInitCanvasPlans((plans) => + plans.map((plan) => { + return { ...plan, isCurrent: plan.id === newCurrentId } + }), + ) + setAddCanvasPlans((plans) => + plans.map((plan) => { + return { ...plan, isCurrent: plan.id === newCurrentId } + }), + ) + } } + useEffect(() => { + setCurrentCanvasPlan([...initCanvasPlans, ...addCanvasPlans].find((plan) => plan.isCurrent) || null) + }, [initCanvasPlans, addCanvasPlans]) const handleDeletePlan = (e, id) => { e.stopPropagation() // 이벤트 버블링 방지 - setPlans(plans.filter((plan) => plan.id !== id)) // 삭제할 아이디와 다른 아이템만 남김 + + // 삭제할 아이디와 다른 아이템만 남김 + const filterInitPlans = initCanvasPlans.filter((plan) => plan.id !== id) + setInitCanvasPlans(filterInitPlans) + const filterAddPlans = addCanvasPlans.filter((plan) => plan.id !== id) + setAddCanvasPlans(filterAddPlans) + + const combinedPlans = [...filterInitPlans, ...filterAddPlans] + if (combinedPlans.length === 0) { + // 모든 데이터가 삭제된 경우 + setPlanNum(0) + } else { + const lastPlanId = combinedPlans.at(-1).id + if (id !== lastPlanId) { + handleCurrentPlan(lastPlanId) + } + } } const addNewPlan = () => { - setPlans([...plans, { id: plans.length, name: `Plan ${plans.length + 1}` }]) + setAddCanvasPlans([...addCanvasPlans, { id: planNum, name: `Plan ${planNum + 1}`, objectNo: `${objectNo}` }]) + handleCurrentPlan(planNum) + setPlanNum(planNum + 1) } useEffect(() => { - if (plans.length === 1) { - setPlans([{ id: 0, name: 'Plan 1', isCurrent: false }]) - } + getCanvasByObjectNo(objectNo).then((res) => { + console.log('canvas 목록 ', res) + if (res.length > 0) { + setInitCanvasPlans(res) + handleCurrentPlan(res.at(-1).id) // last 데이터에 포커싱 + setPlanNum(res.length) + } else { + addNewPlan() + } + }) }, []) return (
    - {plans.map((plan, idx) => ( - @@ -47,7 +90,7 @@ export default function CanvasLayout() {
    - + plan.isCurrent === true)} />
    ) } diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index c916a0fc..99b3e048 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -1,162 +1,135 @@ 'use client' import { useEffect, useState } from 'react' + +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' + import MenuDepth01 from './MenuDepth01' import QSelectBox from '@/components/common/select/QSelectBox' + import { useMessage } from '@/hooks/useMessage' -import { post } from '@/lib/Axios' -import { useRecoilState, useRecoilValue } from 'recoil' -import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' -import { canvasZoomState, verticalHorizontalModeState } from '@/store/canvasAtom' +import { usePlan } from '@/hooks/usePlan' +import { canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' +import { sessionStore } from '@/store/commonAtom' +import { outerLinePointsState } from '@/store/outerLineAtom' +import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' +import { MENU } from '@/common/common' + +import KO from '@/locales/ko.json' +import JA from '@/locales/ja.json' +import { settingModalFirstOptionsState } from '@/store/settingAtom' + +const canvasMenus = [ + { index: 0, name: 'plan.menu.plan.drawing', icon: 'con00', title: MENU.PLAN_DRAWING }, + { index: 1, name: 'plan.menu.placement.surface.initial.setting', icon: 'con01', title: MENU.INITIAL_CANVAS_SETTING }, + { index: 2, name: 'plan.menu.roof.cover', icon: 'con02', title: MENU.ROOF_COVERING.DEFAULT }, + { index: 3, name: 'plan.menu.placement.surface', icon: 'con03', title: MENU.BATCH_CANVAS.DEFAULT }, + { index: 4, name: 'plan.menu.module.circuit.setting', icon: 'con04', title: MENU.MODULE_CIRCUIT_SETTING.DEFAULT }, + { index: 5, name: 'plan.menu.estimate', icon: 'con06', title: MENU.ESTIMATE.DEFAULT }, + { index: 6, name: 'plan.menu.simulation', icon: 'con05', title: MENU.POWER_GENERATION_SIMULATION.DEFAULT }, +] export default function CanvasMenu(props) { - const [objectNo] = useState('test123240912001') // 이후 삭제 필요 const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal } = props + const [menuNumber, setMenuNumber] = useState(null) - const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) - const [vertical, setVertical] = useState(true) const [type, setType] = useState('') + + const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) + const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) + const setCurrentMenu = useSetRecoilState(currentMenuState) + const setPoints = useSetRecoilState(outerLinePointsState) + const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) + + const globalLocale = useRecoilValue(globalLocaleStore) + const canvas = useRecoilValue(canvasState) + const sessionState = useRecoilValue(sessionStore) + const { getMessage } = useMessage() - const canvasZoom = useRecoilValue(canvasZoomState) + const { saveCanvas } = usePlan() + const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] - const onClickNav = (number) => { - setMenuNumber(number) - if (number === 2) setType('outline') - if (number === 3) setType('surface') - if (number === 4) setType('module') + const onClickNav = (menu) => { + setMenuNumber(menu.index) + setCurrentMenu(menu.title) + + switch (menu.index) { + case 2: + setType('outline') + break + case 3: + setType('surface') + break + case 4: + setType('module') + break + } } const menuProps = { setShowOutlineModal, type, } - const firstOptions = useRecoilState(settingModalFirstOptionsState) - const secondOptions = useRecoilState(settingModalSecondOptionsState) + const settingsModalOptions = useRecoilState(settingModalFirstOptionsState) - useEffect(() => {}, [menuNumber, type]) + useEffect(() => { + if (menuNumber !== 2 && showOutlineModal) setShowOutlineModal(false) + }, [menuNumber, type]) // 저장버튼(btn08) 클릭 시 호출되는 함수 - const handleSaveSettings = async () => { - try { - // 서버에 전송할 데이터 - const dataToSend = { - firstOption1: firstOptions[0].option1.map((item) => ({ - column: item.column, - selected: item.selected, - })), - firstOption2: firstOptions[0].option2.map((item) => ({ - column: item.column, - selected: item.selected, - })), - // secondOption1: secondOptions[0].option1.map((item) => ({ - // name: item.name, - // // 필요한 경우 추가 데이터 항목 - // })), - secondOption2: secondOptions[0].option2.map((item) => ({ - column: item.column, - selected: item.selected, - })), - } - - const patternData = { - objectNo, - assignDisplay: dataToSend.firstOption1[0].selected, - drawDisplay: dataToSend.firstOption1[1].selected, - gridDisplay: dataToSend.firstOption1[2].selected, - charDisplay: dataToSend.firstOption1[3].selected, - flowDisplay: dataToSend.firstOption1[4].selected, - hallwayDimenDisplay: dataToSend.firstOption1[5].selected, - actualDimenDisplay: dataToSend.firstOption1[6].selected, - noDimenDisplay: dataToSend.firstOption1[7].selected, - trestleDisplay: dataToSend.firstOption1[8].selected, - coordiDisplay: dataToSend.firstOption1[9].selected, - drawConverDisplay: dataToSend.firstOption1[10].selected, - onlyBorder: dataToSend.firstOption2[0].selected, - lineHatch: dataToSend.firstOption2[1].selected, - allPainted: dataToSend.firstOption2[2].selected, - adsorpRangeSmall: dataToSend.secondOption2[0].selected, - adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected, - adsorpRangeMedium: dataToSend.secondOption2[2].selected, - adsorpRangeLarge: dataToSend.secondOption2[3].selected, - } - - // HTTP POST 요청 보내기 - await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }) - - // 응답 처리 - alert('설정이 저장되었습니다.') - } catch (error) { - console.error('설정을 저장하는 동안 오류가 발생했습니다:', error) - alert('설정을 저장하는 중 오류가 발생했습니다.') - } + const handleSaveCanvas = () => { + saveCanvas(sessionState.userId) } + const handleClear = () => { + setPoints([]) + canvas?.clear() + } + + const handleZoomClear = () => { + setCanvasZoom(100) + canvas.set({ zoom: 1 }) + canvas.viewportTransform = [1, 0, 0, 1, 0, 0] + canvas.renderAll() + } + + useEffect(() => { + if (globalLocale === 'ko') { + setAppMessageState(KO) + } else { + setAppMessageState(JA) + } + }, [menuNumber, type, globalLocale]) + return (
      -
    • onClickNav(0)}> - -
    • -
    • onClickNav(1)}> - -
    • -
    • { - onClickNav(2) - }} - > - -
    • -
    • onClickNav(3)}> - -
    • -
    • onClickNav(4)}> - -
    • -
    • onClickNav(5)}> - -
    • -
    • onClickNav(6)}> - -
    • + {canvasMenus.map((menu) => { + return ( +
    • onClickNav(menu)}> + +
    • + ) + })}
    {menuNumber !== 6 && menuNumber !== 5 && ( <> + { +
    + {getMessage('plan.mode.vertical.horizontal')} + +
    + }
    - {menuNumber !== 4 && ( -
    - {getMessage('plan.mode.vertical.horizontal')} - -
    - )}
    @@ -168,11 +141,11 @@ export default function CanvasMenu(props) {
    {canvasZoom}% - +
    - - + +
    diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 33e522ee..c610948f 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -1,14 +1,16 @@ 'use client' +import { useEffect, useState } from 'react' +import { useRecoilState, useRecoilValue } from 'recoil' +import { useAxios } from '@/hooks/useAxios' +import { globalLocaleStore } from '@/store/localeAtom' +import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' +import '@/styles/contents.scss' import CanvasMenu from '@/components/floor-plan/CanvasMenu' import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal01' import CanvasLayout from '@/components/floor-plan/CanvasLayout' -import '@/styles/contents.scss' import OuterLineWall from '@/components/floor-plan/modal/outerlinesetting/OuterLineWall' -import { useEffect, useState } from 'react' -import { globalLocaleStore } from '@/store/localeAtom' -import { useRecoilValue } from 'recoil' -import { useAxios } from '@/hooks/useAxios' +import DotLineGrid from '@/components/floor-plan/modal/grid/DotLineGrid' export default function FloorPlan() { const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) @@ -16,8 +18,16 @@ export default function FloorPlan() { const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) + const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) + const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) + const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 + + const [showDotLineGridModal, setShowDotLineGridModal] = useState(true) + const [showGridCopyModal, setShowGridCopyModal] = useState(false) + const [showGridMoveModal, setShowGridMoveModal] = useState(false) const canvasSettingProps = { setShowCanvasSettingModal, + setShowDotLineGridModal, } const outlineProps = { @@ -30,6 +40,41 @@ export default function FloorPlan() { setShowOutlineModal, } + useEffect(() => { + console.log('FloorPlan useEffect 실행') + fetchSettings() + }, [showOutlineModal, objectNo]) + + // Canvas Setting 조회 + const fetchSettings = async () => { + try { + const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` }) + const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] })) + const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] })) + const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item })) + const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] })) + const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ + ...item, + })) + // 데이터 설정 + setSettingModalFirstOptions({ + option1: optionData1, + option2: optionData2, + dimensionDisplay: optionData5, + }) + setSettingModalSecondOptions({ + option3: optionData3, + option4: optionData4, + }) + } catch (error) { + console.error('Data fetching error:', error) + } + } + const dotLineProps = { + showDotLineGridModal, + setShowDotLineGridModal, + } + useEffect(() => {}, [showOutlineModal]) return ( @@ -40,6 +85,7 @@ export default function FloorPlan() { {showCanvasSettingModal && } {showOutlineModal && } + {showDotLineGridModal && }
    diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index cf0ad1e8..a6b12877 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -1,76 +1,80 @@ 'use client' -import { ToggleonMouse } from '@/components/header/Header' import { useMessage } from '@/hooks/useMessage' import { useEffect, useState } from 'react' +import { MENU } from '@/common/common' +import { currentMenuState } from '@/store/canvasAtom' +import { useSetRecoilState } from 'recoil' export default function MenuDepth01(props) { const { setShowOutlineModal, type } = props const { getMessage } = useMessage() const [activeMenu, setActiveMenu] = useState() - const onClickMenu = (menuNum) => { - setActiveMenu(menuNum) - setShowOutlineModal(menuNum === 0) + const setCurrentMenu = useSetRecoilState(currentMenuState) + const onClickMenu = ({ id, menu, name }) => { + setActiveMenu(menu) + setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) + setCurrentMenu(menu) + if (type === 'outline') { + setShowOutlineModal(id === 0) + } } - const menus = [ - { id: 0, name: '外壁線を描' }, - { id: 1, name: '補助線を描' }, - { id: 2, name: '屋根形状設定' }, - { id: 3, name: '軒下変更' }, - { id: 4, name: '外壁線の上げ下げ' }, - { id: 5, name: '銅線移動' }, - { id: 6, name: '特殊コーナー形状' }, - ] + useEffect(() => { + setActiveMenu(null) + }, [type]) const menuInfo = { outline: [ // 지붕덮개 - { id: 0, name: 'plan.menu.roof.cover.outline.drawing' }, - { id: 1, name: 'plan.menu.roof.cover.roof.shape.setting' }, - { id: 2, name: 'plan.menu.roof.cover.roof.shape.edit' }, - { id: 3, name: 'plan.menu.roof.cover.auxiliary.line.drawing' }, + { id: 0, name: 'plan.menu.roof.cover.outline.drawing', menu: MENU.ROOF_COVERING.EXTERIOR_WALL_LINE }, + { id: 1, name: 'plan.menu.roof.cover.roof.shape.setting', menu: MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS }, + { + id: 2, + name: 'plan.menu.roof.cover.roof.shape.passivity.setting', + menu: MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS, + }, + { id: 3, name: 'plan.menu.roof.cover.auxiliary.line.drawing', menu: MENU.ROOF_COVERING.HELP_LINE_DRAWING }, + { id: 4, name: 'plan.menu.roof.cover.eaves.kerava.edit', menu: MENU.ROOF_COVERING.EAVES_KERAVA_EDIT }, + { id: 5, name: 'plan.menu.roof.cover.movement.shape.updown', menu: MENU.ROOF_COVERING.MOVEMENT_SHAPE_UPDOWN }, + { id: 6, name: 'plan.menu.roof.cover.outline.edit.offset', menu: MENU.ROOF_COVERING.OUTLINE_EDIT_OFFSET }, + { id: 7, name: 'plan.menu.roof.cover.roof.surface.alloc', menu: MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC }, ], surface: [ // 배치면 - { id: 0, name: 'plan.menu.placement.surface.drawing' }, - { id: 1, name: 'plan.menu.placement.surface.surface' }, - { id: 2, name: 'plan.menu.placement.surface.object' }, + { 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 }, ], module: [ // 모듈, 회로 구성 - { id: 0, name: 'plan.menu.module.circuit.setting.default' }, - { id: 1, name: 'plan.menu.module.circuit.setting.circuit.trestle.setting' }, + + { id: 0, name: 'plan.menu.module.circuit.setting.default', menu: MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING }, + { + id: 1, + name: 'plan.menu.module.circuit.setting.circuit.trestle.setting', + menu: MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING, + }, + { + id: 2, + name: 'plan.menu.module.circuit.setting.plan.orientation', + menu: MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION, + }, ], } - - useEffect(() => { - menus.forEach((menu) => { - menu.isActive = menu.id === activeMenu - }) - }, [menus, activeMenu]) return (
      {menuInfo[type].map((menu) => { return ( -
    • - +
    • +
    • ) })}
    -
      -
    • ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}> - -
    • -
    • ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}> - -
    • -
    • ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}> - -
    • -
    ) } diff --git a/src/components/floor-plan/modal/grid/DotLineGrid.jsx b/src/components/floor-plan/modal/grid/DotLineGrid.jsx new file mode 100644 index 00000000..d0e06fa9 --- /dev/null +++ b/src/components/floor-plan/modal/grid/DotLineGrid.jsx @@ -0,0 +1,328 @@ +import WithDraggable from '@/components/common/draggable/withDraggable' +import QSelectBox from '@/components/common/select/QSelectBox' +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' +import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom' +import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' +import { onlyNumberInputChange } from '@/util/input-utils' +import { fabric } from 'fabric' +import { gridColorState } from '@/store/gridAtom' + +const TYPE = { + DOT: 'DOT', + LINE: 'LINE', +} + +export default function DotLineGrid(props) { + // const [modalOption, setModalOption] = useRecoilState(modalState); //modal 열림닫힘 state + const [close, setClose] = useState(false) + const { setShowDotLineGridModal } = props + const gridColor = useRecoilValue(gridColorState) + const canvas = useRecoilValue(canvasState) + + const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState) + const resetDotLineGridSetting = useResetRecoilState(dotLineGridSettingState) + const interval = useRecoilValue(dotLineIntervalSelector) + + const { getMessage } = useMessage() + const SelectOption = [ + { id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 }, + { id: 2, name: '1/2', value: 1 / 2 }, + { + id: 3, + name: '1/4', + value: 1 / 4, + }, + { id: 4, name: '1/10', value: 1 / 10 }, + ] + const [selectOption, setSelectOption] = useState(SelectOption[0]) + + const HandleClickClose = () => { + // setClose(true) + // setTimeout(() => { + // setModalOption({ ...modalOption, gridoption: false }) + // setClose(false) + // }, 180) + } + + const handleCheckBoxChange = (e) => { + const { value, checked } = e.target + setDotLineGridSettingState((prev) => { + return { + ...prev, + [value]: checked, + } + }) + } + + const handleSave = () => { + // 1. 점.선 그리드 설정으로 만들어진 기존 오브젝트 제거 + canvas + ?.getObjects() + .filter((obj) => obj.name === 'lineGrid') + .forEach((obj) => canvas?.remove(obj)) + canvas + ?.getObjects() + .filter((obj) => obj.name === 'dotGrid') + .forEach((obj) => canvas?.remove(obj)) + + const horizontalInterval = interval.horizontalInterval + const verticalInterval = interval.verticalInterval + + if (dotLineGridSetting.DOT) { + const circle = new fabric.Circle({ + radius: 2, + fill: 'red', + strokeWidth: 0.7, + originX: 'center', + originY: 'center', + selectable: false, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + }) + + const patternSourceCanvas = new fabric.StaticCanvas(null, { + width: horizontalInterval, + height: verticalInterval, + }) + + patternSourceCanvas.add(circle) + + circle.set({ + left: patternSourceCanvas.width / 2, + top: patternSourceCanvas.height / 2, + }) + + patternSourceCanvas.renderAll() + + const pattern = new fabric.Pattern({ + source: patternSourceCanvas.getElement(), + repeat: 'repeat', + }) + + const backgroundPolygon = new fabric.Polygon( + [ + { x: 0, y: 0 }, + { x: canvas.width, y: 0 }, + { x: canvas.width, y: canvas.height }, + { x: 0, y: canvas.height }, + ], + { + fill: pattern, + selectable: false, + name: 'dotGrid', + }, + ) + + canvas.add(backgroundPolygon) + backgroundPolygon.sendToBack() + canvas.renderAll() + } + + if (dotLineGridSetting.LINE) { + for (let i = 0; i < canvas.height / verticalInterval + 1; i++) { + const horizontalLine = new fabric.Line( + [0, i * verticalInterval - verticalInterval / 2, canvas.width, i * verticalInterval - verticalInterval / 2], + { + stroke: gridColor, + strokeWidth: 1, + selectable: true, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: 'lineGrid', + strokeDashArray: [5, 2], + opacity: 0.3, + direction: 'horizontal', + }, + ) + canvas.add(horizontalLine) + } + + for (let i = 0; i < canvas.width / horizontalInterval + 1; i++) { + const verticalLine = new fabric.Line( + [i * horizontalInterval - horizontalInterval / 2, 0, i * horizontalInterval - horizontalInterval / 2, canvas.height], + { + stroke: 'black', + strokeWidth: 1, + selectable: true, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: 'lineGrid', + strokeDashArray: [5, 2], + opacity: 0.3, + direction: 'vertical', + }, + ) + canvas.add(verticalLine) + } + } + + canvas.renderAll() + } + + const handleRadioChange = (e) => { + const { value, name, checked, selected } = e.target + + setDotLineGridSettingState((prev) => { + return { + ...prev, + INTERVAL: { + ...prev.INTERVAL, + type: Number(value), + }, + } + }) + } + + const changeInput = (value, e) => { + const { name } = e.target + setDotLineGridSettingState((prev) => { + return { + ...prev, + INTERVAL: { + ...prev.INTERVAL, + [name]: value, + }, + } + }) + } + + const changeDimension = (result) => { + const { value } = result + setDotLineGridSettingState((prev) => { + return { + ...prev, + INTERVAL: { + ...prev.INTERVAL, + dimension: value, + }, + } + }) + } + + // 초기화 + const reset = () => { + canvas + ?.getObjects() + .filter((obj) => obj.name === 'lineGrid') + .forEach((obj) => canvas?.remove(obj)) + canvas + ?.getObjects() + .filter((obj) => obj.name === 'dotGrid') + .forEach((obj) => canvas?.remove(obj)) + resetDotLineGridSetting() + setSelectOption(SelectOption[0]) + } + + return ( + +
    +
    +

    {getMessage('modal.canvas.setting.grid.dot.line.setting')}

    + +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    + {getMessage('modal.canvas.setting.grid.dot.line.setting.horizon')} +
    + onlyNumberInputChange(e, changeInput)} + /> +
    + mm +
    +
    + {getMessage('modal.canvas.setting.grid.dot.line.setting.vertical')} +
    + onlyNumberInputChange(e, changeInput)} + /> +
    + mm +
    +
    +
    +
    + + +
    +
    + {getMessage('modal.canvas.setting.grid.dot.line.setting.ratio')} +
    + onlyNumberInputChange(e, changeInput)} + /> +
    + mm +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    + ) +} diff --git a/src/components/floor-plan/modal/grid/GridCopy.jsx b/src/components/floor-plan/modal/grid/GridCopy.jsx new file mode 100644 index 00000000..92609d80 --- /dev/null +++ b/src/components/floor-plan/modal/grid/GridCopy.jsx @@ -0,0 +1,43 @@ +import WithDraggable from '@/components/common/draggable/withDraggable' +import { useMessage } from '@/hooks/useMessage' + +export default function GridCopy(props) { + const { setShowGridMoveModal, setShowGridCopyModal } = props + const { getMessage } = useMessage() + + return ( + +
    +
    +

    {getMessage('modal.grid.copy')}

    + +
    +
    +
    {getMessage('modal.grid.copy.info')}
    +
    +
    +
    + {getMessage('modal.grid.copy.length')} +
    + +
    + mm +
    +
    + + + + +
    +
    +
    +
    + +
    +
    +
    +
    + ) +} diff --git a/src/components/floor-plan/modal/grid/GridMove.jsx b/src/components/floor-plan/modal/grid/GridMove.jsx new file mode 100644 index 00000000..1da8b6fa --- /dev/null +++ b/src/components/floor-plan/modal/grid/GridMove.jsx @@ -0,0 +1,55 @@ +import WithDraggable from '@/components/common/draggable/withDraggable' +import { useMessage } from '@/hooks/useMessage' + +export default function GridMove(props) { + const { setShowGridMoveModal, setShowGridCopyModal } = props + const { getMessage } = useMessage() + + return ( + +
    +
    +

    {getMessage('modal.grid.move')}

    + +
    +
    +
    {getMessage('modal.grid.move.info')}
    +
    +
    + + +
    +
    +
    +

    {getMessage('modal.grid.move.length')}

    +
    +
    + +
    + mm +
    +
    +
    + +
    + mm +
    +
    +
    + + + + +
    +
    +
    +
    + +
    +
    +
    +
    + ) +} diff --git a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx index 05d7cb0c..6e9f1c34 100644 --- a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx @@ -1,457 +1,20 @@ 'use client' -import { useEffect, useRef } from 'react' import WithDraggable from '@/components/common/draggable/withDraggable' -import { useRecoilState, useRecoilValue } from 'recoil' import { useMessage } from '@/hooks/useMessage' -import { useEvent } from '@/hooks/useEvent' -import { canvasState, verticalHorizontalModeState } from '@/store/canvasAtom' -import { - OUTER_LINE_TYPE, - outerLineArrow1State, - outerLineArrow2State, - outerLineLength1State, - outerLineLength2State, - outerLinePointsState, - outerLineTypeState, -} from '@/store/outerLineAtom' -import { QLine } from '@/components/fabric/QLine' -import { useLine } from '@/hooks/useLine' -import { distanceBetweenPoints } from '@/util/canvas-util' +import { OUTER_LINE_TYPE } from '@/store/outerLineAtom' +import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils' +import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall' export default function OuterLineWall(props) { const { setShowOutlineModal } = props const { getMessage } = useMessage() - const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners } = useEvent() - const { addLineText, removeLineText } = useLine() - const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) + const { points, length1, setLength1, length2, setLength2, length1Ref, length2Ref, arrow1, arrow2, type, setType, handleFix, handleRollback } = + useOuterLineWall() - const length1Ref = useRef(null) - const length2Ref = useRef(null) - const [length1, setLength1] = useRecoilState(outerLineLength1State) - const [length2, setLength2] = useRecoilState(outerLineLength2State) - const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State) - const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State) - const [points, setPoints] = useRecoilState(outerLinePointsState) - const [type, setType] = useRecoilState(outerLineTypeState) - const arrow1Ref = useRef(arrow1) - const arrow2Ref = useRef(arrow2) - - const canvas = useRecoilValue(canvasState) - - useEffect(() => { - removeAllMouseEventListeners() - addCanvasMouseEventListener('mouse:down', mouseDown) - }, [verticalHorizontalMode, points]) - - useEffect(() => { - arrow1Ref.current = arrow1 - }, [arrow1]) - - useEffect(() => { - arrow2Ref.current = arrow2 - }, [arrow2]) - - useEffect(() => { - removeAllDocumentEventListeners() - addDocumentEventListener('keydown', document, keydown[type]) - clear() - }, [type]) - - const clear = () => { - setLength1(0) - setLength2(0) - - setArrow1('') - setArrow2('') - } - - const mouseDown = (e) => { - const pointer = canvas.getPointer(e.e) - if (points.length === 0) { - setPoints((prev) => [...prev, pointer]) - } else { - const lastPoint = points[points.length - 1] - let newPoint = { x: pointer.x, y: pointer.y } - const length = distanceBetweenPoints(lastPoint, newPoint) - if (verticalHorizontalMode) { - const vector = { - x: pointer.x - points[points.length - 1].x, - y: pointer.y - points[points.length - 1].y, - } - const slope = Math.abs(vector.y / vector.x) // 기울기 계산 - - let scaledVector - if (slope >= 1) { - // 기울기가 1 이상이면 x축 방향으로 그림 - scaledVector = { - x: 0, - y: vector.y >= 0 ? Number(length) : -Number(length), - } - } else { - // 기울기가 1 미만이면 y축 방향으로 그림 - scaledVector = { - x: vector.x >= 0 ? Number(length) : -Number(length), - y: 0, - } - } - - const verticalLength = scaledVector.y - const horizontalLength = scaledVector.x - - newPoint = { - x: lastPoint.x + horizontalLength, - y: lastPoint.y + verticalLength, - } - } - setPoints((prev) => [...prev, newPoint]) - } - } - - useEffect(() => { - canvas - ?.getObjects() - .filter((obj) => obj.name === 'outerLine') - .forEach((obj) => { - canvas?.remove(obj) - removeLineText(obj) - }) - - canvas - ?.getObjects() - .filter((obj) => obj.name === 'helpGuideLine' || (obj.name === 'lengthTxt' && obj.parent?.name === 'helpGuideLine')) - .forEach((obj) => canvas?.remove(obj)) - - canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint')) - - // point가 변경 될때마다 이벤트 리스너를 제거하고 다시 등록 - removeAllDocumentEventListeners() - addDocumentEventListener('keydown', document, keydown[type]) - - if (points.length === 0) { - return - } - - if (points.length === 1) { - const point = new fabric.Circle({ - radius: 5, - fill: 'transparent', - stroke: 'red', - left: points[0].x - 5, - top: points[0].y - 5, - selectable: false, - name: 'startPoint', - }) - - canvas?.add(point) - } else { - points.forEach((point, idx) => { - if (idx === 0) { - return - } - drawLine(points[idx - 1], point, idx) - }) - - const lastPoint = points[points.length - 1] - const firstPoint = points[0] - - if (points.length < 3) { - return - } - - if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) { - return - } - - if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) { - const line = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], { - stroke: 'grey', - strokeWidth: 1, - selectable: false, - name: 'helpGuideLine', - }) - - canvas?.add(line) - addLineText(line) - } else { - const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], { - stroke: 'grey', - strokeWidth: 1, - strokeDashArray: [1, 1, 1], - name: 'helpGuideLine', - }) - - const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], { - stroke: 'grey', - strokeWidth: 1, - strokeDashArray: [1, 1, 1], - name: 'helpGuideLine', - }) - if (guideLine1.length > 0) { - canvas?.add(guideLine1) - addLineText(guideLine1) - } - - canvas?.add(guideLine2) - - addLineText(guideLine2) - } - } - }, [points]) - - const drawLine = (point1, point2, idx) => { - const line = new QLine([point1.x, point1.y, point2.x, point2.y], { - stroke: 'black', - strokeWidth: 3, - idx: idx, - selectable: false, - name: 'outerLine', - }) - - canvas?.add(line) - addLineText(line) - } - - // 직각 완료될 경우 확인 - const checkRightAngle = () => { - const length1Num = Number(length1Ref.current.value) / 10 - const length2Num = Number(length2Ref.current.value) / 10 - - if (points.length === 0) { - return - } - - if (length1Num === 0 || length2Num === 0 || arrow1Ref.current === '' || arrow2Ref.current === '') { - return - } - - if (arrow1Ref.current === '↓' && arrow2Ref.current === '→') { - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y + length1Num }] - }) - } else if (arrow1Ref.current === '↓' && arrow2Ref.current === '←') { - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y + length1Num }] - }) - } else if (arrow1Ref.current === '↑' && arrow2Ref.current === '→') { - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y - length1Num }] - }) - } else if (arrow1Ref.current === '↑' && arrow2Ref.current === '←') { - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y - length1Num }] - }) - } else if (arrow1Ref.current === '→' && arrow2Ref.current === '↓') { - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y + length2Num }] - }) - } else if (arrow1Ref.current === '→' && arrow2Ref.current === '↑') { - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y - length2Num }] - }) - } else if (arrow1Ref.current === '←' && arrow2Ref.current === '↓') { - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y + length2Num }] - }) - } else if (arrow1Ref.current === '←' && arrow2Ref.current === '↑') { - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y - length2Num }] - }) - } - } - - const keydown = { - outerLine: (e) => { - if (points.length === 0) { - return - } - const key = e.key - - if (!length1Ref.current) { - return - } - - const lengthNum = Number(length1Ref.current.value) / 10 - if (lengthNum === 0) { - return - } - switch (key) { - case 'Down': // IE/Edge에서 사용되는 값 - case 'ArrowDown': { - setArrow1('↓') - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y + lengthNum }] - }) - break - } - case 'Up': // IE/Edge에서 사용되는 값 - case 'ArrowUp': - setArrow1('↑') - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y - lengthNum }] - }) - break - case 'Left': // IE/Edge에서 사용되는 값 - case 'ArrowLeft': - setArrow1('←') - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x - lengthNum, y: prev[prev.length - 1].y }] - }) - break - case 'Right': // IE/Edge에서 사용되는 값 - case 'ArrowRight': - setArrow1('→') - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[prev.length - 1].x + lengthNum, y: prev[prev.length - 1].y }] - }) - break - } - }, - rightAngle: (e) => { - if (points.length === 0) { - return - } - const key = e.key - - const activeElem = document.activeElement - - switch (key) { - case 'Down': // IE/Edge에서 사용되는 값 - case 'ArrowDown': { - if (activeElem === length1Ref.current) { - setArrow1('↓') - arrow1Ref.current = '↓' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') { - break - } - setArrow2('↓') - arrow2Ref.current = '↓' - checkRightAngle() - } - - break - } - case 'Up': // IE/Edge에서 사용되는 값 - case 'ArrowUp': - if (activeElem === length1Ref.current) { - setArrow1('↑') - arrow1Ref.current = '↑' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') { - break - } - setArrow2('↑') - arrow2Ref.current = '↑' - checkRightAngle() - } - - break - case 'Left': // IE/Edge에서 사용되는 값 - case 'ArrowLeft': - if (activeElem === length1Ref.current) { - setArrow1('←') - arrow1Ref.current = '←' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '←' || arrow1Ref.current === '→') { - break - } - setArrow2('←') - arrow2Ref.current = '←' - checkRightAngle() - } - - break - case 'Right': // IE/Edge에서 사용되는 값 - case 'ArrowRight': - if (activeElem === length1Ref.current) { - setArrow1('→') - arrow1Ref.current = '→' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '←' || arrow1Ref.current === '→') { - break - } - setArrow2('→') - arrow2Ref.current = '→' - checkRightAngle() - } - - break - } - }, - leeGubae: (e) => { - console.log('leegubae') - }, - angle: (e) => { - console.log('angle') - }, - diagonalLine: (e) => { - console.log('diagonalLine') - }, - } - - /** - * 일변전으로 돌아가기 - */ - const handleRollback = () => { - //points의 마지막 요소를 제거 - setPoints((prev) => prev.slice(0, prev.length - 1)) - } - - const handleFix = () => { - if (points.length < 3) { - return - } - setPoints((prev) => { - if (prev.length === 0) { - return [] - } - return [...prev, { x: prev[0].x, y: prev[0].y }] - }) - } return ( - -
    + +

    {getMessage('modal.cover.outline.drawing')}

    @@ -520,9 +81,7 @@ export default function OuterLineWall(props) { className="input-origin block" value={length1} ref={length1Ref} - onChange={(e) => { - setLength1(e.target.value.replace(/[^-0-9]/g, '')) - }} + onChange={(e) => onlyNumberInputChange(e, setLength1)} placeholder="3000" />
    @@ -537,9 +96,7 @@ export default function OuterLineWall(props) { className="input-origin block" value={length2} ref={length2Ref} - onChange={(e) => { - setLength2(e.target.value.replace(/[^-0-9]/g, '')) - }} + onChange={(e) => onlyNumberInputChange(e, setLength2)} placeholder="3000" />
    @@ -548,6 +105,30 @@ export default function OuterLineWall(props) {
    + ) : type === OUTER_LINE_TYPE.ANGLE ? ( +
    +
    + + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + /> +
    +
    + + onlyNumberWithDotInputChange(e, setAngle1)} + className="input-origin block" + /> +
    +
    ) : ( <> )} diff --git a/src/components/floor-plan/modal/setting01/FirstOption.jsx b/src/components/floor-plan/modal/setting01/FirstOption.jsx index 8c292a05..ac8f08d6 100644 --- a/src/components/floor-plan/modal/setting01/FirstOption.jsx +++ b/src/components/floor-plan/modal/setting01/FirstOption.jsx @@ -1,65 +1,189 @@ import { useRecoilState } from 'recoil' -import { settingModalFirstOptionsState } from '@/store/settingAtom' +import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' import { useMessage } from '@/hooks/useMessage' import React, { useEffect, useState } from 'react' -import { get } from '@/lib/Axios' +import { useAxios } from '@/hooks/useAxios' +import { useSwal } from '@/hooks/useSwal' +import { adsorptionPointAddModeState } from '@/store/canvasAtom' export default function FirstOption() { - const [objectNo] = useState('test123240912001') // 이후 삭제 필요 - const [settingsModalOptions, setSettingModalOptions] = useRecoilState(settingModalFirstOptionsState) - const { option1, option2 } = settingsModalOptions + const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 + const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) + const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) + const { option1, option2, dimensionDisplay } = settingModalFirstOptions + const { option3, option4 } = settingModalSecondOptions const { getMessage } = useMessage() - - const [isFetched, setIsFetched] = useState(false) // 조회 여부 상태 + const { get, post } = useAxios() + const { swalFire } = useSwal() // 데이터를 최초 한 번만 조회 useEffect(() => { console.log('FirstOption useEffect 실행') - if (!isFetched) { - // 조회가 안 되었을 때만 fetchSettings 실행 - fetchSettings() - } - }, [isFetched]) // isFetched 상태가 변할 때마다 확인 + fetchSettings() + }, [objectNo]) // Canvas Setting 조회 및 초기화 const fetchSettings = async () => { try { const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` }) - - const options1 = [ - 'assignDisplay', - 'drawDisplay', - 'gridDisplay', - 'charDisplay', - 'flowDisplay', - 'hallwayDimenDisplay', - 'actualDimenDisplay', - 'noDimenDisplay', - 'trestleDisplay', - 'coordiDisplay', - 'drawConverDisplay', - ] - const option1 = settingsModalOptions.option1.map((item, index) => ({ ...item, selected: res[options1[index]] })) - - const options2 = ['onlyBorder', 'lineHatch', 'allPainted'] - const option2 = settingsModalOptions.option2.map((item, index) => ({ ...item, selected: res[options2[index]] })) + const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] })) + const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] })) + const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item, selected: res[item.column] })) + const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item })) + const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] })) // 데이터 설정 - setSettingModalOptions({ - option1, - option2, + setSettingModalFirstOptions({ + option1: optionData1, + option2: optionData2, + dimensionDisplay: optionData5, }) - setIsFetched(true) // 조회가 완료되면 isFetched를 true로 설정 + setSettingModalSecondOptions({ + option3: optionData3, + option4: optionData4, + }) } catch (error) { console.error('Data fetching error:', error) } } - const onClickOption = (option) => { + const onClickOption = async (option) => { option.selected = !option.selected - setSettingModalOptions({ option1, option2 }) + setSettingModalFirstOptions({ option1, option2, dimensionDisplay }) + setSettingModalSecondOptions({ option3, option4 }) + + try { + // 서버에 전송할 데이터 + const dataToSend = { + firstOption1: option1.map((item) => ({ + column: item.column, + selected: item.selected, + })), + firstOption2: option2.map((item) => ({ + column: item.column, + selected: item.selected, + })), + firstOption3: dimensionDisplay.map((item) => ({ + column: item.column, + selected: item.selected, + })), + // secondOption1: secondOptions[0].option1.map((item) => ({ + // name: item.id, + // name: item.name, + // // 필요한 경우 데이터 항목 추가 + // })), + secondOption2: option4.map((item) => ({ + column: item.column, + selected: item.selected, + })), + } + + const patternData = { + objectNo, + //디스플레이 설정(다중) + allocDisplay: dataToSend.firstOption1[0].selected, + outlineDisplay: dataToSend.firstOption1[1].selected, + gridDisplay: dataToSend.firstOption1[2].selected, + lineDisplay: dataToSend.firstOption1[3].selected, + wordDisplay: dataToSend.firstOption1[4].selected, + circuitNumDisplay: dataToSend.firstOption1[5].selected, + flowDisplay: dataToSend.firstOption1[6].selected, + trestleDisplay: dataToSend.firstOption1[7].selected, + totalDisplay: dataToSend.firstOption1[8].selected, + //차수 표시(다건) + corridorDimension: dataToSend.firstOption3[0].selected, + realDimension: dataToSend.firstOption3[1].selected, + noneDimension: dataToSend.firstOption3[2].selected, + //화면 표시(다중) + onlyBorder: dataToSend.firstOption2[0].selected, + lineHatch: dataToSend.firstOption2[1].selected, + allPainted: dataToSend.firstOption2[2].selected, + //흡착범위 설정(단건) + adsorpRangeSmall: dataToSend.secondOption2[0].selected, + adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected, + adsorpRangeMedium: dataToSend.secondOption2[2].selected, + adsorpRangeLarge: dataToSend.secondOption2[3].selected, + } + + // HTTP POST 요청 보내기 + await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => { + swalFire({ text: getMessage(res.returnMessage) }) + }) + } catch (error) { + swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) + } + } + + const onClickDimension = async (item) => { + const options = settingModalFirstOptions?.dimensionDisplay.map((option) => { + option.selected = option.id === item.id + return option + }) + + setSettingModalFirstOptions({ option1, option2, dimensionDisplay }) + + try { + // 서버에 전송할 데이터 + const dataToSend = { + firstOption1: option1.map((item) => ({ + column: item.column, + selected: item.selected, + })), + firstOption2: option2.map((item) => ({ + column: item.column, + selected: item.selected, + })), + firstOption3: dimensionDisplay.map((item) => ({ + column: item.column, + selected: item.selected, + })), + // secondOption1: secondOptions[0].option1.map((item) => ({ + // name: item.id, + // name: item.name, + // // 필요한 경우 데이터 항목 추가 + // })), + secondOption2: option4.map((item) => ({ + column: item.column, + selected: item.selected, + })), + } + + const patternData = { + objectNo, + //디스플레이 설정(다중) + allocDisplay: dataToSend.firstOption1[0].selected, + outlineDisplay: dataToSend.firstOption1[1].selected, + gridDisplay: dataToSend.firstOption1[2].selected, + lineDisplay: dataToSend.firstOption1[3].selected, + wordDisplay: dataToSend.firstOption1[4].selected, + circuitNumDisplay: dataToSend.firstOption1[5].selected, + flowDisplay: dataToSend.firstOption1[6].selected, + trestleDisplay: dataToSend.firstOption1[7].selected, + totalDisplay: dataToSend.firstOption1[8].selected, + //차수 표시(다건) + corridorDimension: dataToSend.firstOption3[0].selected, + realDimension: dataToSend.firstOption3[1].selected, + noneDimension: dataToSend.firstOption3[2].selected, + //화면 표시(다중) + onlyBorder: dataToSend.firstOption2[0].selected, + lineHatch: dataToSend.firstOption2[1].selected, + allPainted: dataToSend.firstOption2[2].selected, + //흡착범위 설정(단건) + adsorpRangeSmall: dataToSend.secondOption2[0].selected, + adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected, + adsorpRangeMedium: dataToSend.secondOption2[2].selected, + adsorpRangeLarge: dataToSend.secondOption2[3].selected, + } + + // HTTP POST 요청 보내기 + await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => { + swalFire({ text: getMessage(res.returnMessage) }) + }) + } catch (error) { + swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) + } } return ( @@ -67,23 +191,37 @@ export default function FirstOption() {

    {getMessage('modal.canvas.setting.first.option.info')}

    - {settingsModalOptions?.option1?.map((item) => ( - - ))} + {settingModalFirstOptions && + settingModalFirstOptions.option1.map((item) => ( + + ))} +
    +
    +
    +

    {getMessage('modal.canvas.setting.first.option.dimension')}

    +
    + {settingModalFirstOptions && + settingModalFirstOptions.dimensionDisplay.map((item) => ( + + ))}

    {getMessage('modal.canvas.setting.first.option.display')}

    - {settingsModalOptions?.option2?.map((item) => ( - - ))} + {settingModalFirstOptions && + settingModalFirstOptions.option2.map((item) => ( + + ))}
    diff --git a/src/components/floor-plan/modal/setting01/GridOption.jsx b/src/components/floor-plan/modal/setting01/GridOption.jsx new file mode 100644 index 00000000..edf1e802 --- /dev/null +++ b/src/components/floor-plan/modal/setting01/GridOption.jsx @@ -0,0 +1,80 @@ +import React, { useEffect } from 'react' +import { useRecoilState } from 'recoil' +import { settingModalGridOptionsState } from '@/store/settingAtom' +import { useMessage } from '@/hooks/useMessage' +import { adsorptionPointAddModeState } from '@/store/canvasAtom' +import { useTempGrid } from '@/hooks/useTempGrid' +import { gridColorState } from '@/store/gridAtom' +import { useColor } from 'react-color-palette' + +export default function GridOption(props) { + const { setShowDotLineGridModal } = props + const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState) + const [gridColor, setGridColor] = useRecoilState(gridColorState) + const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState) + const { getMessage } = useMessage() + const { tempGridMode, setTempGridMode } = useTempGrid() + + const [color, setColor] = useColor(gridColor) + + useEffect(() => { + console.log(color) + setGridColor(color.hex) + }, [color]) + + const onClickOption = (option) => { + const newGridOptions = [...gridOptions] + newGridOptions.map((item) => { + if (item.id === option.id) { + item.selected = !item.selected + } + }) + + if (option.id === 1) { + // 점 그리드 + setAdsorptionPointAddMode(false) + setTempGridMode(option.selected) + newGridOptions[2].selected = false + } + + if (option.id === 2) { + // 점.선 그리드 + if (option.selected) { + setShowDotLineGridModal(true) + } else { + setShowDotLineGridModal(false) + } + } + + if (option.name === 'modal.canvas.setting.grid.absorption.add') { + setAdsorptionPointAddMode(!adsorptionPointAddMode) + setTempGridMode(false) + newGridOptions[0].selected = false + } + + if (option.id === 4) { + // 그리드 색 설정 + if (option.selected) { + setColorPickerShow(true) + } + } + + setGridOptions(newGridOptions) + } + + return ( + <> +
    +

    {getMessage('modal.canvas.setting.grid')}

    +
    + {gridOptions?.map((option) => ( + + ))} +
    +
    + + ) +} diff --git a/src/components/floor-plan/modal/setting01/SecondOption.jsx b/src/components/floor-plan/modal/setting01/SecondOption.jsx index 0a568b67..f6cf6e30 100644 --- a/src/components/floor-plan/modal/setting01/SecondOption.jsx +++ b/src/components/floor-plan/modal/setting01/SecondOption.jsx @@ -1,75 +1,145 @@ -import { useRecoilState } from 'recoil' -import { settingModalSecondOptionsState } from '@/store/settingAtom' +import { useRecoilState, useSetRecoilState } from 'recoil' +import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' import { useMessage } from '@/hooks/useMessage' import React, { useEffect, useState } from 'react' -import { get } from '@/lib/Axios' +import { useAxios } from '@/hooks/useAxios' +import { useSwal } from '@/hooks/useSwal' +import { adsorptionPointModeState, adsorptionRangeState } from '@/store/canvasAtom' export default function SecondOption() { - const [objectNo] = useState('test123240912001') // 이후 삭제 필요 - const [settingsModalOptions, setSettingModalOptions] = useRecoilState(settingModalSecondOptionsState) - const { option1, option2 } = settingsModalOptions - const { getMessage } = useMessage() + const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 + const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) + const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) + const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState) + const setAdsorptionRange = useSetRecoilState(adsorptionRangeState) - const [isFetched, setIsFetched] = useState(false) // 조회 여부 상태 + const { option1, option2, dimensionDisplay } = settingModalFirstOptions + const { option3, option4 } = settingModalSecondOptions + const { getMessage } = useMessage() + const { get, post } = useAxios() + const { swalFire } = useSwal() // 데이터를 최초 한 번만 조회 useEffect(() => { console.log('SecondOption useEffect 실행') - if (!isFetched) { - // 조회가 안 되었을 때만 fetchSettings 실행 - fetchSettings() - } - }, [isFetched]) // isFetched 상태가 변할 때마다 확인 + fetchSettings() + }, [objectNo]) + // Canvas Setting 조회 및 초기화 const fetchSettings = async () => { try { const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` }) + const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] })) + const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] })) + const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item, selected: res[item.column] })) + const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item })) + const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] })) - //const options1 = ['1', '2', '3', '4'] - //const option1 = settingsModalOptions.option1.map((item, index) => ({ ...item, selected: res[options1[index]] })) - - const options2 = ['adsorpRangeSmall', 'adsorpRangeSmallSemi', 'adsorpRangeMedium', 'adsorpRangeLarge'] - const option2 = settingsModalOptions.option2.map((item, index) => ({ ...item, selected: res[options2[index]] })) - - // 데이터 설정 - setSettingModalOptions({ - option1, - option2, + setSettingModalFirstOptions({ + option1: optionData1, + option2: optionData2, + dimensionDisplay: optionData5, + }) + setSettingModalSecondOptions({ + option3: optionData3, + option4: optionData4, }) - - setIsFetched(true) // 조회가 완료되면 isFetched를 true로 설정 } catch (error) { console.error('Data fetching error:', error) } } - const onClickOption = (option) => { - // option2에서 한 개만 선택 가능하도록 처리 - const updatedOption2 = option2.map((item) => (item.id === option.id ? { ...item, selected: true } : { ...item, selected: false })) + const onClickOption = async (option) => { + // option4에서 한 개만 선택 가능하도록 처리 + const updatedOption4 = option4.map((item) => (item.id === option.id ? { ...item, selected: true } : { ...item, selected: false })) - setSettingModalOptions({ option1, option2: updatedOption2 }) + setSettingModalFirstOptions({ option1, option2, dimensionDisplay }) + setSettingModalSecondOptions({ option3, option4: updatedOption4 }) + + try { + // 서버에 전송할 데이터 + const dataToSend = { + firstOption1: option1.map((item) => ({ + column: item.column, + selected: item.selected, + })), + firstOption2: option2.map((item) => ({ + column: item.column, + selected: item.selected, + })), + firstOption3: dimensionDisplay.map((item) => ({ + column: item.column, + selected: item.selected, + })), + // secondOption1: secondOptions[0].option3.map((item) => ({ + // name: item.id, + // name: item.name, + // // 필요한 경우 데이터 항목 추가 + // })), + secondOption2: updatedOption4.map((item) => ({ + column: item.column, + selected: item.selected, + })), + } + const patternData = { + objectNo, + //디스플레이 설정(다중) + allocDisplay: dataToSend.firstOption1[0].selected, + outlineDisplay: dataToSend.firstOption1[1].selected, + gridDisplay: dataToSend.firstOption1[2].selected, + lineDisplay: dataToSend.firstOption1[3].selected, + wordDisplay: dataToSend.firstOption1[4].selected, + circuitNumDisplay: dataToSend.firstOption1[5].selected, + flowDisplay: dataToSend.firstOption1[6].selected, + trestleDisplay: dataToSend.firstOption1[7].selected, + totalDisplay: dataToSend.firstOption1[8].selected, + //차수 표시(다건) + corridorDimension: dataToSend.firstOption3[0].selected, + realDimension: dataToSend.firstOption3[1].selected, + noneDimension: dataToSend.firstOption3[2].selected, + //화면 표시(다중) + onlyBorder: dataToSend.firstOption2[0].selected, + lineHatch: dataToSend.firstOption2[1].selected, + allPainted: dataToSend.firstOption2[2].selected, + //흡착범위 설정(단건) + adsorpRangeSmall: dataToSend.secondOption2[0].selected, + adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected, + adsorpRangeMedium: dataToSend.secondOption2[2].selected, + adsorpRangeLarge: dataToSend.secondOption2[3].selected, + } + + // HTTP POST 요청 보내기 + await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => { + swalFire({ text: getMessage(res.returnMessage) }) + }) + } catch (error) { + swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) + } + setAdsorptionRange(option.range) } return ( <>

    {getMessage('modal.canvas.setting.font.plan.edit')}

    - {settingsModalOptions.option1.map((item, index) => ( - - ))} + {settingModalSecondOptions && + settingModalSecondOptions.option3.map((item) => ( + + ))}

    {getMessage('modal.canvas.setting.font.plan.absorption')}

    - {settingsModalOptions.option2.map((item, index) => ( - - ))} + {settingModalSecondOptions && + settingModalSecondOptions.option4.map((item) => ( + + ))}
    +
    diff --git a/src/components/floor-plan/modal/setting01/SettingModal01.jsx b/src/components/floor-plan/modal/setting01/SettingModal01.jsx index 6ce186b7..293d29df 100644 --- a/src/components/floor-plan/modal/setting01/SettingModal01.jsx +++ b/src/components/floor-plan/modal/setting01/SettingModal01.jsx @@ -5,15 +5,16 @@ import FirstOption from './FirstOption' import WithDraggable from '@/components/common/draggable/withDraggable' import SecondOption from '@/components/floor-plan/modal/setting01/SecondOption' import { useMessage } from '@/hooks/useMessage' +import GridOption from '@/components/floor-plan/modal/setting01/GridOption' export default function SettingModal01(props) { - const { setShowCanvasSettingModal } = props + const { setShowCanvasSettingModal, setShowDotLineGridModal } = props const [buttonAct, setButtonAct] = useState(1) const { getMessage } = useMessage() return ( - -
    + +

    {getMessage('modal.canvas.setting')}

    +
    {buttonAct === 1 && } {buttonAct === 2 && } + {buttonAct === 3 && }
    diff --git a/src/components/header/Header.jsx b/src/components/header/Header.jsx index e8a4e872..456cb09f 100644 --- a/src/components/header/Header.jsx +++ b/src/components/header/Header.jsx @@ -1,7 +1,12 @@ 'use client' -import { Fragment, useState } from 'react' +import { Fragment, useCallback, useEffect, useState } from 'react' + import Link from 'next/link' -import { usePathname, useRouter } from 'next/navigation' +import { usePathname } from 'next/navigation' + +import { useRecoilState, useRecoilValue } from 'recoil' +import { dimmedStore, sessionStore } from '@/store/commonAtom' + import { useMessage } from '@/hooks/useMessage' import { logout } from '@/lib/authActions' @@ -23,7 +28,8 @@ export const ToggleonMouse = (e, act, target) => { } export default function Header(props) { - const { loginedUserNm } = props + const { userSession } = props + const [sessionState, setSessionState] = useRecoilState(sessionStore) const { getMessage } = useMessage() const pathName = usePathname() // if (pathName.includes('login') || pathName.includes('join')) { @@ -31,6 +37,9 @@ export default function Header(props) { // } const [selected, setSelected] = useState('') + const dimmedState = useRecoilValue(dimmedStore) + const isDimmed = dimmedState ? 'opacity-50 bg-black' : '' + const SelectOptions = [ { id: 0, name: 'オンライン保証シ', link: '' }, { id: 1, name: 'ステム', link: '' }, @@ -60,6 +69,14 @@ export default function Header(props) { }, ] + const syncSession = useCallback(() => { + setSessionState({ ...userSession }) + }) + + useEffect(() => { + syncSession() + }, [userSession]) + const onChangeSelect = (option) => { setSelected(option) } @@ -108,7 +125,7 @@ export default function Header(props) { return ( !(pathName.includes('login') || pathName.includes('join')) && ( -
    +

    @@ -120,7 +137,7 @@ export default function Header(props) {

    - +