From 68680e004267f1a3a5fc04b9b0dc22c3f3ad7312 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 2 Sep 2025 14:30:12 +0900 Subject: [PATCH 01/30] =?UTF-8?q?=EB=8F=99=EC=9D=BC=20=EA=B2=BD=EC=82=AC,?= =?UTF-8?q?=20=EB=8F=99=EC=9D=BC=20=EB=B0=A9=EB=A9=B4=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EB=90=98=EC=96=B4=EC=9E=88=EC=9D=84=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=EC=97=90=EB=A7=8C=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useCirCuitTrestle.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/hooks/useCirCuitTrestle.js b/src/hooks/useCirCuitTrestle.js index 12e1b28b..a35d3c8c 100644 --- a/src/hooks/useCirCuitTrestle.js +++ b/src/hooks/useCirCuitTrestle.js @@ -10,7 +10,7 @@ import { selectedModelsState, seriesState, } from '@/store/circuitTrestleAtom' -import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions' +import { selectedModuleState } from '@/store/selectedModuleOptions' import { useContext, useEffect } from 'react' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { useMessage } from './useMessage' @@ -101,7 +101,11 @@ export function useCircuitTrestle(executeEffect = false) { // result 배열에서 roofSurface 값을 기준으로 순서대로 정렬한다. - return groupSort(result) + if (pcsCheck.division) { + return groupSort(result) + } else { + return result + } } const groupSort = (arr) => { From 8a61f90971496c3d593aaec33a378c368fe3ab73 Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 2 Sep 2025 17:25:00 +0900 Subject: [PATCH 02/30] =?UTF-8?q?big.mjs:139=20Uncaught=20Error:=20[big.js?= =?UTF-8?q?]=20Invalid=20number=20=20at=20klass.setLength=20(QLine.js:72:3?= =?UTF-8?q?6)=20(NaN=20=3D>=200=20=EC=B2=98=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QLine.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index 7881712e..77254d5d 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -2,6 +2,7 @@ import { fabric } from 'fabric' import { v4 as uuidv4 } from 'uuid' import { getDirectionByPoint } from '@/util/canvas-util' import { calcLinePlaneSize } from '@/util/qpolygon-utils' +import { logger } from '@/util/logger' export const QLine = fabric.util.createClass(fabric.Line, { type: 'QLine', @@ -69,7 +70,14 @@ export const QLine = fabric.util.createClass(fabric.Line, { }, setLength() { - this.length = calcLinePlaneSize(this) / 10 + // Ensure all required properties are valid numbers + const { x1, y1, x2, y2 } = this; + if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) { + logger.error('Invalid coordinates in QLine:', { x1, y1, x2, y2 }); + this.length = 0; + return; + } + this.length = calcLinePlaneSize({ x1, y1, x2, y2 }) / 10; }, addLengthText() { From 1b678ecaa0efe30cc7b596579ad25faae48d00ea Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 2 Sep 2025 17:26:18 +0900 Subject: [PATCH 03/30] =?UTF-8?q?=20=3D>=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/modal/basic/step/Placement.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/floor-plan/modal/basic/step/Placement.jsx b/src/components/floor-plan/modal/basic/step/Placement.jsx index e96cf5c0..091129a8 100644 --- a/src/components/floor-plan/modal/basic/step/Placement.jsx +++ b/src/components/floor-plan/modal/basic/step/Placement.jsx @@ -169,6 +169,7 @@ const Placement = forwardRef((props, refs) => {
+ {moduleData.header.map((data) => ( ))} + {selectedModules?.itemList && @@ -216,7 +218,7 @@ const Placement = forwardRef((props, refs) => { className="input-origin block" name="row" value={props.layoutSetup[index]?.row ?? 1} - defaultValue={0} + //defaultValue={0} onChange={(e) => handleLayoutSetup(e, item.itemId, index)} /> @@ -228,7 +230,7 @@ const Placement = forwardRef((props, refs) => { className="input-origin block" name="col" value={props.layoutSetup[index]?.col ?? 1} - defaultValue={0} + //defaultValue={0} onChange={(e) => handleLayoutSetup(e, item.itemId, index)} /> From 043c369c0e23ccb75b021bc628b95bfd9a7abf5e Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 2 Sep 2025 17:28:37 +0900 Subject: [PATCH 04/30] =?UTF-8?q?value,=20defaultValue=20=EB=8F=99?= =?UTF-8?q?=EC=8B=9C=20=EC=A1=B4=EC=9E=AC=20=EC=95=88=EB=90=A8=20=3D>=20de?= =?UTF-8?q?faultValue=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx b/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx index 7c9d4f51..32364844 100644 --- a/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx +++ b/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx @@ -86,7 +86,7 @@ export default function RoofAllocationSetting(props) { return (
- +
{pitchText} From fb8ce256ffa0a88240fcefd1e94e4bf0b3bd7eae Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 5 Dec 2025 15:36:56 +0900 Subject: [PATCH 05/30] =?UTF-8?q?=EB=B3=B5=EC=82=AC=20=ED=9B=84=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/common/useCommonUtils.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/hooks/common/useCommonUtils.js b/src/hooks/common/useCommonUtils.js index c878bafd..9fe5a221 100644 --- a/src/hooks/common/useCommonUtils.js +++ b/src/hooks/common/useCommonUtils.js @@ -648,6 +648,7 @@ export function useCommonUtils() { lockMovementY: true, name: obj.name, editable: false, + selectable: true, // 복사된 객체 선택 가능하도록 설정 id: uuidv4(), //복사된 객체라 새로 따준다 }) @@ -656,19 +657,25 @@ export function useCommonUtils() { //배치면일 경우 if (obj.name === 'roof') { - clonedObj.setCoords() - clonedObj.fire('modified') - // clonedObj.fire('polygonMoved') + clonedObj.canvas = canvas // canvas 참조 설정 clonedObj.set({ direction: obj.direction, directionText: obj.directionText, roofMaterial: obj.roofMaterial, + stroke: 'black', // 복사된 객체는 선택 해제 상태의 색상으로 설정 + selectable: true, // 선택 가능하도록 설정 + evented: true, // 마우스 이벤트를 받을 수 있도록 설정 + isFixed: false, // containsPoint에서 특별 처리 방지 }) obj.lines.forEach((line, index) => { clonedObj.lines[index].set({ attributes: line.attributes }) }) + clonedObj.fire('polygonMoved') // 내부 좌표 재계산 (points, pathOffset) + clonedObj.fire('modified') + clonedObj.setCoords() // 모든 속성 설정 후 좌표 업데이트 + canvas.setActiveObject(clonedObj) canvas.renderAll() addLengthText(clonedObj) //수치 추가 drawDirectionArrow(clonedObj) //방향 화살표 추가 From d519f9f080309b2587bfa034e4e02721bfe9aa78 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 10 Dec 2025 10:24:37 +0900 Subject: [PATCH 06/30] =?UTF-8?q?=ED=94=8C=EB=9E=9C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=20=EC=8B=9C=20=EC=99=B8=EB=B2=BD=EC=84=A0=20=EB=82=A8=EC=95=84?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20?= =?UTF-8?q?=EA=B0=99=EC=9D=80=20=EC=A7=80=EB=B6=95=EC=9E=AC=EB=A5=BC=20?= =?UTF-8?q?=EA=B0=80=EC=A7=80=EA=B3=A0=20=EC=9E=88=EB=8A=94=20=EB=8B=A4?= =?UTF-8?q?=EB=A5=B8=20=EC=A7=80=EB=B6=95=EB=93=A4=EC=9D=98=20=EC=A7=80?= =?UTF-8?q?=EB=B6=95=EC=9E=AC=EB=8F=84=20=EB=B3=80=EA=B2=BD=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasFrame.jsx | 3 +++ src/hooks/roofcover/useRoofAllocationSetting.js | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 63dc523a..9441dc7c 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -32,6 +32,7 @@ import { useEvent } from '@/hooks/useEvent' import { compasDegAtom } from '@/store/orientationAtom' import { hotkeyStore } from '@/store/hotkeyAtom' import { usePopup } from '@/hooks/usePopup' +import { outerLinePointsState } from '@/store/outerLineAtom' export default function CanvasFrame() { const canvasRef = useRef(null) @@ -45,6 +46,7 @@ export default function CanvasFrame() { const totalDisplay = useRecoilValue(totalDisplaySelector) // 집계표 표시 여부 const { setIsGlobalLoading } = useContext(QcastContext) const resetModuleStatisticsState = useResetRecoilState(moduleStatisticsState) + const resetOuterLinePoints = useResetRecoilState(outerLinePointsState) const resetMakersState = useResetRecoilState(makersState) const resetSelectedMakerState = useResetRecoilState(selectedMakerState) const resetSeriesState = useResetRecoilState(seriesState) @@ -137,6 +139,7 @@ export default function CanvasFrame() { const resetRecoilData = () => { // resetModuleStatisticsState() + resetOuterLinePoints() resetMakersState() resetSelectedMakerState() resetSeriesState() diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index 8839516f..6165a0de 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -372,11 +372,18 @@ export function useRoofAllocationSetting(id) { setBasicSetting((prev) => { return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) } }) + const selectedRoofMaterial = newRoofList.find((roof) => roof.selected) + const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && obj.roofMaterial?.index === selectedRoofMaterial.index) + + roofs.forEach((roof) => { + setSurfaceShapePattern(roof, roofDisplay.column, false, { ...selectedRoofMaterial }, true) + drawDirectionArrow(roof) + }) setRoofList(newRoofList) setRoofMaterials(newRoofList) setRoofsStore(newRoofList) - const selectedRoofMaterial = newRoofList.find((roof) => roof.selected) + setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial, true) drawDirectionArrow(currentObject) modifyModuleSelectionData() From 66336778905fc5d8c8f8bf005cb5870304ca6e01 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 12 Dec 2025 15:20:28 +0900 Subject: [PATCH 07/30] =?UTF-8?q?=EB=8F=84=EB=A8=B8=20=EC=98=A4=ED=94=84?= =?UTF-8?q?=EC=85=8B=20=EC=84=A0=ED=83=9D=20=EC=8B=9C=20=EC=98=A4=EB=A5=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../floor-plan/modal/object/DormerOffset.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/floor-plan/modal/object/DormerOffset.jsx b/src/components/floor-plan/modal/object/DormerOffset.jsx index fd3eb70a..1881b18e 100644 --- a/src/components/floor-plan/modal/object/DormerOffset.jsx +++ b/src/components/floor-plan/modal/object/DormerOffset.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from 'react' +import { useEffect, useRef, useState } from 'react' import { useMessage } from '@/hooks/useMessage' import WithDraggable from '@/components/common/draggable/WithDraggable' import { useRecoilValue } from 'recoil' @@ -15,8 +15,8 @@ export default function DormerOffset(props) { const { closePopup } = usePopup() const [arrow1, setArrow1] = useState(null) const [arrow2, setArrow2] = useState(null) - const arrow1LengthRef = useRef() - const arrow2LengthRef = useRef() + const arrow1LengthRef = useRef(0) + const arrow2LengthRef = useRef(0) const [arrow1Length, setArrow1Length] = useState(0) const [arrow2Length, setArrow2Length] = useState(0) @@ -59,12 +59,12 @@ export default function DormerOffset(props) { name="" label="" className="input-origin block" - value={arrow1LengthRef.current.value} + value={arrow1LengthRef.current.value ?? 0} ref={arrow1LengthRef} onChange={(value) => setArrow1Length(value)} options={{ allowNegative: false, - allowDecimal: false + allowDecimal: false, }} />
From e9d0668ed2e864cfabc1b20d05ad5c9395fb8dd7 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 12 Dec 2025 15:27:26 +0900 Subject: [PATCH 08/30] =?UTF-8?q?group=20object=20=EC=A2=8C=ED=91=9C=20?= =?UTF-8?q?=EC=9E=AC=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/fabric-extensions.js | 37 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/util/fabric-extensions.js b/src/util/fabric-extensions.js index 9410f764..acccc601 100644 --- a/src/util/fabric-extensions.js +++ b/src/util/fabric-extensions.js @@ -29,22 +29,39 @@ fabric.Rect.prototype.getCurrentPoints = function () { /** * fabric.Group에 getCurrentPoints 메서드를 추가 (도머 그룹용) - * 그룹의 groupPoints를 다시 계산하여 반환 + * 그룹 내 객체들의 점들을 수집하여 현재 월드 좌표를 반환 */ fabric.Group.prototype.getCurrentPoints = function () { - // groupPoints를 다시 계산 + // 그룹 내 객체들로부터 실시간으로 점들을 계산 + if (this._objects && this._objects.length > 0) { + let allPoints = [] - // 그룹에 groupPoints가 있으면 해당 점들을 사용 (도머의 경우) - if (this.groupPoints && Array.isArray(this.groupPoints)) { - const matrix = this.calcTransformMatrix() - console.log('this.groupPoints', this.groupPoints) - return this.groupPoints.map(function (p) { - const point = new fabric.Point(p.x, p.y) - return fabric.util.transformPoint(point, matrix) + // 그룹 내 모든 객체의 점들을 수집 + this._objects.forEach(function (obj) { + if (obj.getCurrentPoints && typeof obj.getCurrentPoints === 'function') { + const objPoints = obj.getCurrentPoints() + allPoints = allPoints.concat(objPoints) + } else if (obj.points && Array.isArray(obj.points)) { + const pathOffset = obj.pathOffset || { x: 0, y: 0 } + const matrix = obj.calcTransformMatrix() + const transformedPoints = obj.points + .map(function (p) { + return new fabric.Point(p.x - pathOffset.x, p.y - pathOffset.y) + }) + .map(function (p) { + return fabric.util.transformPoint(p, matrix) + }) + allPoints = allPoints.concat(transformedPoints) + } }) + + if (allPoints.length > 0) { + // Convex Hull 알고리즘을 사용하여 외곽 점들만 반환 + return this.getConvexHull(allPoints) + } } - // groupPoints가 없으면 바운딩 박스를 사용 + // 객체가 없으면 바운딩 박스를 사용 const bounds = this.getBoundingRect() const points = [ { x: bounds.left, y: bounds.top }, From 8c7fd807433ccfa41f39f1148e5782da85cf9091 Mon Sep 17 00:00:00 2001 From: yscha Date: Sat, 13 Dec 2025 17:19:18 +0900 Subject: [PATCH 09/30] =?UTF-8?q?=ED=95=A0=EB=8B=B9=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 3ca095a8..8186b449 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -336,8 +336,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { if (types.every((type) => type === LINE_TYPE.WALLLINE.EAVES)) { // 용마루 -- straight-skeleton console.log('용마루 지붕') - //drawRidgeRoof(this.id, this.canvas, textMode) - drawSkeletonRidgeRoof(this.id, this.canvas, textMode); + drawRidgeRoof(this.id, this.canvas, textMode) + //drawSkeletonRidgeRoof(this.id, this.canvas, textMode); } else if (isGableRoof(types)) { // A형, B형 박공 지붕 console.log('패턴 지붕') From cd97ada0df125f2e9405e9e16105ba4f0d8b50e8 Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 16 Dec 2025 11:09:05 +0900 Subject: [PATCH 10/30] skeleton open --- src/components/fabric/QPolygon.js | 4 ++-- src/lib/authActions.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 8186b449..d2f7db92 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -336,8 +336,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { if (types.every((type) => type === LINE_TYPE.WALLLINE.EAVES)) { // 용마루 -- straight-skeleton console.log('용마루 지붕') - drawRidgeRoof(this.id, this.canvas, textMode) - //drawSkeletonRidgeRoof(this.id, this.canvas, textMode); + ///drawRidgeRoof(this.id, this.canvas, textMode) + drawSkeletonRidgeRoof(this.id, this.canvas, textMode); } else if (isGableRoof(types)) { // A형, B형 박공 지붕 console.log('패턴 지붕') diff --git a/src/lib/authActions.js b/src/lib/authActions.js index 5e7055d0..4a5b2c94 100644 --- a/src/lib/authActions.js +++ b/src/lib/authActions.js @@ -49,6 +49,7 @@ export async function setSession(data) { session.custCd = data.custCd session.isLoggedIn = true session.builderNo = data.builderNo + session.custNm = data.custNm await session.save() } From 552cd7cc26a2ba028c4dab0447e0421d9d75caf8 Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 30 Jan 2026 14:34:57 +0900 Subject: [PATCH 11/30] =?UTF-8?q?[1444]=EC=9C=A1=EC=A7=80=EB=B4=89=20TypeE?= =?UTF-8?q?rror:=20Cannot=20destructure=20property=20'rackYn'=20of=20'tres?= =?UTF-8?q?tleDetail'=20as=20it=20is=20null.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../circuitTrestle/CircuitTrestleSetting.jsx | 3 +++ src/hooks/module/useModule.js | 4 ++-- src/hooks/module/useModuleBasicSetting.js | 19 ++++++++++++++++++- src/hooks/module/useTrestle.js | 8 ++++++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx index 4c4ea028..02e9d171 100644 --- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -124,6 +124,9 @@ export default function CircuitTrestleSetting({ id }) { */ const validateModuleSizeForRack = (surface) => { const { modules, direction, trestleDetail } = surface + if (!trestleDetail) { + return true // 상세 정보 없음 + } const { rackYn, moduleIntvlHor, moduleIntvlVer } = trestleDetail if (rackYn === 'N' || !modules || modules.length < 2) { diff --git a/src/hooks/module/useModule.js b/src/hooks/module/useModule.js index 45b3b50f..8e59a1e7 100644 --- a/src/hooks/module/useModule.js +++ b/src/hooks/module/useModule.js @@ -749,7 +749,7 @@ export function useModule() { const copyModules = [] const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId) let isWarning = false - const { moduleIntvlHor, moduleIntvlVer } = moduleSetupSurface.trestleDetail + const { moduleIntvlHor = 0, moduleIntvlVer = 0 } = moduleSetupSurface.trestleDetail || {} canvas.discardActiveObject() targetModules.forEach((module) => { const { top, left } = getPosotion(module, type, moduleIntvlHor, true) @@ -859,7 +859,7 @@ export function useModule() { const copyModules = [] const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId) let isWarning = false - const { moduleIntvlHor, moduleIntvlVer } = moduleSetupSurface.trestleDetail + const { moduleIntvlHor = 0, moduleIntvlVer = 0 } = moduleSetupSurface.trestleDetail || {} canvas.discardActiveObject() targetModules.forEach((module) => { const { top, left } = getPosotion(module, type, moduleIntvlVer, true) diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js index 45fd8a53..5ddf69d0 100644 --- a/src/hooks/module/useModuleBasicSetting.js +++ b/src/hooks/module/useModuleBasicSetting.js @@ -338,10 +338,27 @@ export function useModuleBasicSetting(tabNum) { }) let isNorth = false + const defaultTrestleDetail = { + rackYn: 'N', + moduleIntvlHor: +roofSizeSet === 3 ? 300 : 0, + moduleIntvlVer: +roofSizeSet === 3 ? 100 : 0, + rack: null, + rackQty: 0, + rackIntvlPct: 0, + cvrPlvrYn: 'N', + lessSupFitIntvlPct: 0, + lessSupFitQty: 0, + } const isExistSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.parentId === roof.id) + const normalizedTrestleDetail = trestleDetail + ? { ...defaultTrestleDetail, ...trestleDetail } + : isExistSurface?.trestleDetail + ? { ...defaultTrestleDetail, ...isExistSurface.trestleDetail } + : defaultTrestleDetail if (isExistSurface) { + isExistSurface.set({ trestleDetail: normalizedTrestleDetail }) if (canvasSetting.roofSizeSet != '3') { //북면이 있지만 if (roof.directionText && roof.directionText.indexOf('北') > -1) { @@ -424,7 +441,7 @@ export function useModuleBasicSetting(tabNum) { originY: 'center', modules: [], roofMaterial: roof.roofMaterial, - trestleDetail: trestleDetail, + trestleDetail: normalizedTrestleDetail, isNorth: isNorth, perPixelTargetFind: true, isSaleStoreNorthFlg: moduleSelectionData.common.saleStoreNorthFlg == '1' ? true : false, //북면설치가능점 여부 diff --git a/src/hooks/module/useTrestle.js b/src/hooks/module/useTrestle.js index 88980cad..e4ee52a8 100644 --- a/src/hooks/module/useTrestle.js +++ b/src/hooks/module/useTrestle.js @@ -65,6 +65,10 @@ export const useTrestle = () => { if (+roofSizeSet === 3) { return } + const trestleDetail = surface.trestleDetail + if (!trestleDetail) { + return + } const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction if (!construction) { return @@ -76,8 +80,8 @@ export const useTrestle = () => { let isSnowGuard = construction.setupSnowCover let cvrLmtRow = construction.cvrLmtRow const direction = parent.direction - const rack = surface.trestleDetail.rack - let { rackQty, rackIntvlPct, rackYn, cvrPlvrYn, lessSupFitIntvlPct, lessSupFitQty } = surface.trestleDetail + const rack = trestleDetail.rack + let { rackQty, rackIntvlPct, rackYn, cvrPlvrYn, lessSupFitIntvlPct, lessSupFitQty } = trestleDetail if (!rack && lessSupFitIntvlPct === 0 && lessSupFitQty === 0) { //25/02/06 가대없음의 경우 랙정보가 없음 From 37fb19071c28d51ae5441419c343a9ee212849bc Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 30 Jan 2026 15:51:46 +0900 Subject: [PATCH 12/30] =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=84=A4=EC=B9=98?= =?UTF-8?q?=20=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EC=95=88=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/module/useModuleBasicSetting.js | 145 ++++++++++++++++------ 1 file changed, 109 insertions(+), 36 deletions(-) diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js index 5ddf69d0..8d07b18f 100644 --- a/src/hooks/module/useModuleBasicSetting.js +++ b/src/hooks/module/useModuleBasicSetting.js @@ -17,7 +17,7 @@ import { import { calculateVisibleModuleHeight, getDegreeByChon, polygonToTurfPolygon, rectToPolygon, toFixedWithoutRounding } from '@/util/canvas-util' import '@/util/fabric-extensions' // fabric 객체들에 getCurrentPoints 메서드 추가 import { basicSettingState, roofDisplaySelector } from '@/store/settingAtom' -import offsetPolygon, { calculateAngle, createLinesFromPolygon, cleanSelfIntersectingPolygon } from '@/util/qpolygon-utils' +import offsetPolygon, { calculateAngle, cleanSelfIntersectingPolygon, createLinesFromPolygon } from '@/util/qpolygon-utils' import { QPolygon } from '@/components/fabric/QPolygon' import { useEvent } from '@/hooks/useEvent' import { BATCH_TYPE, LINE_TYPE, MODULE_SETUP_TYPE, POLYGON_TYPE } from '@/common/common' @@ -2122,7 +2122,7 @@ export function useModuleBasicSetting(tabNum) { } //흐름 방향이 남쪽(아래) - const downFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => { + const downFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) => { let setupModule = [] const trestleDetailData = moduleSetupSurface.trestleDetail @@ -2201,6 +2201,11 @@ export function useModuleBasicSetting(tabNum) { calcAreaHeight = isNaN(calcAreaHeight) ? moduleSetupSurface.height : calcAreaHeight let calcModuleHeightCount = calcAreaHeight / (height + intvVer) + // 대칭 지붕을 위해 south의 calcAreaWidth 저장 (north에서 참조) + if (symmetricWidthRef && moduleIndex === 0) { + symmetricWidthRef.south = calcAreaWidth + } + if (type === MODULE_SETUP_TYPE.LAYOUT) { calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol calcModuleHeightCount = layoutRow @@ -2304,7 +2309,7 @@ export function useModuleBasicSetting(tabNum) { } } - const topFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => { + const topFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) => { let setupModule = [] const trestleDetailData = moduleSetupSurface.trestleDetail @@ -2383,9 +2388,22 @@ export function useModuleBasicSetting(tabNum) { //육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음 const moduleArray = [] - let calcAreaWidth = flowLines.right.x1 - flowLines.left.x1 //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직 + // 북쪽: 남쪽과 동일한 방식으로 계산 (대칭을 위해) + let calcAreaWidth = Math.abs(flowLines.right.x1 - flowLines.left.x1) //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직 + + // 대칭 지붕: south의 calcAreaWidth가 있고 north의 값이 south보다 10% 이상 작으면 south 값 사용 + if (symmetricWidthRef?.south && calcAreaWidth < symmetricWidthRef.south * 0.9) { + // flowLines 좌표도 보정 (중심점 유지하면서 너비 확장) + const center = (flowLines.right.x1 + flowLines.left.x1) / 2 + const halfWidth = symmetricWidthRef.south / 2 + flowLines.left.x1 = center - halfWidth + flowLines.right.x1 = center + halfWidth + + calcAreaWidth = symmetricWidthRef.south + } + let calcModuleWidthCount = calcAreaWidth / (width + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직 - let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1 + let calcAreaHeight = Math.abs(flowLines.bottom.y1 - flowLines.top.y1) let calcModuleHeightCount = calcAreaHeight / (height + intvVer) //단수지정 자동이면 @@ -2486,7 +2504,7 @@ export function useModuleBasicSetting(tabNum) { //남, 북과 같은 로직으로 적용하려면 좌우는 열 -> 행 으로 그려야함 //변수명은 bottom 기준으로 작성하여 동일한 방향으로 진행한다 - const leftFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => { + const leftFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) => { let setupModule = [] const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터 @@ -2574,6 +2592,11 @@ export function useModuleBasicSetting(tabNum) { let calcAreaHeight = Math.abs(flowLines.right.x1 - flowLines.left.x1) let calcModuleHeightCount = calcAreaHeight / (width + intvVer) + // 대칭 지붕을 위해 west의 calcAreaWidth 저장 (east에서 참조) + if (symmetricWidthRef && moduleIndex === 0) { + symmetricWidthRef.west = calcAreaWidth + } + //단수지정 자동이면 if (type === MODULE_SETUP_TYPE.LAYOUT) { calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol @@ -2672,7 +2695,7 @@ export function useModuleBasicSetting(tabNum) { } } - const rightFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => { + const rightFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) => { let setupModule = [] const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터 @@ -2753,9 +2776,22 @@ export function useModuleBasicSetting(tabNum) { //육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음 const moduleArray = [] - let calcAreaWidth = flowLines.bottom.y1 - flowLines.top.y1 //아래에서 y에서 위를 y를 뺀 가운데를 찾는 로직 + // 동쪽: 서쪽과 동일한 방식으로 계산 (대칭을 위해) + let calcAreaWidth = Math.abs(flowLines.bottom.y1 - flowLines.top.y1) //아래에서 y에서 위를 y를 뺀 가운데를 찾는 로직 + + // 대칭 지붕: west의 calcAreaWidth가 있고 east의 값이 west보다 10% 이상 작으면 west 값 사용 + if (symmetricWidthRef?.west && calcAreaWidth < symmetricWidthRef.west * 0.9) { + // flowLines 좌표도 보정 (중심점 유지하면서 높이 확장) + const center = (flowLines.bottom.y1 + flowLines.top.y1) / 2 + const halfHeight = symmetricWidthRef.west / 2 + flowLines.top.y1 = center - halfHeight + flowLines.bottom.y1 = center + halfHeight + + calcAreaWidth = symmetricWidthRef.west + } + let calcModuleWidthCount = calcAreaWidth / (height + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직 - let calcAreaHeight = flowLines.right.x1 - flowLines.left.x1 + let calcAreaHeight = Math.abs(flowLines.right.x1 - flowLines.left.x1) let calcModuleHeightCount = calcAreaHeight / (width + intvVer) //단수지정 자동이면 @@ -2765,15 +2801,14 @@ export function useModuleBasicSetting(tabNum) { } let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀 - // let totalModuleWidthCount = isChidori ? Math.abs(calcMaxModuleWidthCount) : Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다 - let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다 + let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) let calcStartPoint = flowLines.top.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * height) / 2 : 0 //반씩 나눠서 중앙에 맞춤 left 높이 기준으로 양변이 직선일때만 가운데 정렬 - let startPointX = flowLines.bottom.y2 - calcStartPoint //시작점을 만든다 + let startPointX = flowLines.bottom.y1 - calcStartPoint //시작점을 만든다 //근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다 if (flowLines.top.type === 'curve' && flowLines.bottom.type === 'curve') { - startPointX = flowLines.bottom.y2 - (calcAreaWidth - totalModuleWidthCount * height) / 2 + startPointX = flowLines.bottom.y1 - (calcAreaWidth - totalModuleWidthCount * height) / 2 } let heightMargin = 0 @@ -2860,7 +2895,18 @@ export function useModuleBasicSetting(tabNum) { } } - moduleSetupSurfaces.forEach((moduleSetupSurface, index) => { + // 대칭 지붕을 위한 calcAreaWidth 공유 객체 (south→north, west→east) + const symmetricWidthRef = { south: null, west: null } + + // 대칭 보정을 위해 south/west가 north/east보다 먼저 처리되도록 정렬 + const directionOrder = { south: 0, west: 1, north: 2, east: 3 } + const sortedModuleSetupSurfaces = [...moduleSetupSurfaces].sort((a, b) => { + const orderA = directionOrder[a.direction] ?? 4 + const orderB = directionOrder[b.direction] ?? 4 + return orderA - orderB + }) + + sortedModuleSetupSurfaces.forEach((moduleSetupSurface, index) => { moduleSetupSurface.fire('mousedown') const moduleSetupArray = [] @@ -2886,30 +2932,30 @@ export function useModuleBasicSetting(tabNum) { if (setupLocation === 'eaves') { // 흐름방향이 남쪽일때 if (moduleSetupSurface.direction === 'south') { - downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) + downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) } if (moduleSetupSurface.direction === 'west') { - leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) + leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) } if (moduleSetupSurface.direction === 'east') { - rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) + rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) } if (moduleSetupSurface.direction === 'north') { - topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) + topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) } } else if (setupLocation === 'ridge') { //용마루 if (moduleSetupSurface.direction === 'south') { - topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) + topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) } if (moduleSetupSurface.direction === 'west') { - rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) + rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) } if (moduleSetupSurface.direction === 'east') { - leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) + leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) } if (moduleSetupSurface.direction === 'north') { - downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) + downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) } } @@ -3089,13 +3135,26 @@ export function useModuleBasicSetting(tabNum) { type: 'flat', } } else { - rtnObj = { - target: index === 0 ? 'bottom' : 'top', - x1: pointX1, - y1: pointY1, - x2: pointX2, - y2: pointY2, - type: 'curve', + // NaN 체크: offset이 너무 커서 꼭짓점을 넘어가면 NaN 발생 + if (isNaN(pointX1) || isNaN(pointX2) || isNaN(pointY1) || isNaN(pointY2)) { + // NaN이면 꼭짓점 좌표 사용 (모듈 설치 영역 없음) + rtnObj = { + target: index === 0 ? 'bottom' : 'top', + x1: center.x1, + y1: center.y1, + x2: center.x1, + y2: center.y1, + type: 'curve', + } + } else { + rtnObj = { + target: index === 0 ? 'bottom' : 'top', + x1: pointX1, + y1: pointY1, + x2: pointX2, + y2: pointY2, + type: 'curve', + } } } @@ -3221,13 +3280,26 @@ export function useModuleBasicSetting(tabNum) { type: 'flat', } } else { - rtnObj = { - target: index === 0 ? 'left' : 'right', - x1: pointX1, - y1: pointY1, - x2: pointX2, - y2: pointY2, - type: 'curve', + // NaN 체크: offset이 너무 커서 꼭짓점을 넘어가면 NaN 발생 + if (isNaN(pointX1) || isNaN(pointX2) || isNaN(pointY1) || isNaN(pointY2)) { + // NaN이면 꼭짓점 좌표 사용 (모듈 설치 영역 없음) + rtnObj = { + target: index === 0 ? 'left' : 'right', + x1: center.x1, + y1: center.y1, + x2: center.x1, + y2: center.y1, + type: 'curve', + } + } else { + rtnObj = { + target: index === 0 ? 'left' : 'right', + x1: pointX1, + y1: pointY1, + x2: pointX2, + y2: pointY2, + type: 'curve', + } } } rtnObjArray.push(rtnObj) @@ -4118,6 +4190,7 @@ export function useModuleBasicSetting(tabNum) { left: leftRightFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'left'), right: leftRightFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'right'), } + return flowLines } From c1a59041ba12caf81ebca01c1451fa0e7140184a Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 2 Feb 2026 10:16:26 +0900 Subject: [PATCH 13/30] =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=BA=A1?= =?UTF-8?q?=EC=B3=90=20=EC=8B=9C=20=ED=95=B4=EC=83=81=EB=A0=A5=202?= =?UTF-8?q?=EB=B0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/floorPlan/useImgLoader.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/hooks/floorPlan/useImgLoader.js b/src/hooks/floorPlan/useImgLoader.js index 242206db..81383f13 100644 --- a/src/hooks/floorPlan/useImgLoader.js +++ b/src/hooks/floorPlan/useImgLoader.js @@ -58,7 +58,12 @@ export function useImgLoader() { canvas.renderAll() const formData = new FormData() - const dataUrl = canvas.toDataURL('image/png') + // 고해상도 캡처를 위해 multiplier 옵션 추가 (2배 해상도) + const multiplier = 2 + const dataUrl = canvas.toDataURL({ + format: 'png', + multiplier: multiplier, + }) const blobBin = atob(dataUrl.split(',')[1]) const array = [] for (let i = 0; i < blobBin.length; i++) { @@ -69,13 +74,13 @@ export function useImgLoader() { formData.append('objectNo', currentCanvasPlan.objectNo) formData.append('planNo', currentCanvasPlan.planNo) formData.append('type', type) - /** 이미지 크롭 좌표 계산 */ + /** 이미지 크롭 좌표 계산 (multiplier 배율 적용) */ const positionObj = getImageCoordinates() console.log('🚀 ~ handleCanvasToPng ~ positionObj:', positionObj) - formData.append('width', Math.round(positionObj[1].x - positionObj[0].x + 100)) - formData.append('height', Math.round(positionObj[1].y - positionObj[0].y + 100)) - formData.append('left', Math.round(positionObj[0].x)) - formData.append('top', Math.round(positionObj[0].y)) + formData.append('width', Math.round((positionObj[1].x - positionObj[0].x + 100) * multiplier)) + formData.append('height', Math.round((positionObj[1].y - positionObj[0].y + 100) * multiplier)) + formData.append('left', Math.round(positionObj[0].x * multiplier)) + formData.append('top', Math.round(positionObj[0].y * multiplier)) console.log('🚀 ~ handleCanvasToPng ~ formData:', formData) /** 이미지 크롭 요청 */ From 79a66390b6ea43e611e1e900c221f4622c4673e0 Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 2 Feb 2026 13:36:33 +0900 Subject: [PATCH 14/30] =?UTF-8?q?[1436]=EC=B2=98=EB=A7=88=20=EC=BB=A4?= =?UTF-8?q?=EB=B2=84=EC=9D=98=20=EC=B2=98=EC=9D=8C=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/modal/basic/step/Trestle.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/floor-plan/modal/basic/step/Trestle.jsx b/src/components/floor-plan/modal/basic/step/Trestle.jsx index 2ef723d0..17279d83 100644 --- a/src/components/floor-plan/modal/basic/step/Trestle.jsx +++ b/src/components/floor-plan/modal/basic/step/Trestle.jsx @@ -859,7 +859,7 @@ const Trestle = forwardRef((props, ref) => { type="checkbox" id={`ch01`} disabled={!cvrYn || cvrYn === 'N'} - checked={cvrChecked || true} + checked={cvrChecked ?? false} // onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, cvrChecked: !trestleState.cvrChecked } })} onChange={() => setCvrChecked(!cvrChecked)} /> From 6cdd89eb0cea288c1f56bcd120240a40480fd621 Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 2 Feb 2026 14:24:52 +0900 Subject: [PATCH 15/30] =?UTF-8?q?[1436]=EC=B2=98=EB=A7=88=20=EC=BB=A4?= =?UTF-8?q?=EB=B2=84=EC=9D=98=20=EC=B2=98=EC=9D=8C=20=EC=B2=B4=ED=81=AC2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/modal/basic/step/Trestle.jsx | 2 +- src/hooks/module/useModuleTrestle.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/floor-plan/modal/basic/step/Trestle.jsx b/src/components/floor-plan/modal/basic/step/Trestle.jsx index 17279d83..19169e4f 100644 --- a/src/components/floor-plan/modal/basic/step/Trestle.jsx +++ b/src/components/floor-plan/modal/basic/step/Trestle.jsx @@ -413,7 +413,7 @@ const Trestle = forwardRef((props, ref) => { setCvrYn(constructionList[index].cvrYn) setSnowGdPossYn(constructionList[index].snowGdPossYn) - setCvrChecked(false) + setCvrChecked(true) setSnowGdChecked(false) } } diff --git a/src/hooks/module/useModuleTrestle.js b/src/hooks/module/useModuleTrestle.js index fc588d09..0d219401 100644 --- a/src/hooks/module/useModuleTrestle.js +++ b/src/hooks/module/useModuleTrestle.js @@ -61,7 +61,7 @@ export function useModuleTrestle(props) { const [lengthBase, setLengthBase] = useState(0) const [hajebichi, setHajebichi] = useState(0) const [cvrYn, setCvrYn] = useState('N') - const [cvrChecked, setCvrChecked] = useState(false) + const [cvrChecked, setCvrChecked] = useState(true) const [snowGdPossYn, setSnowGdPossYn] = useState('N') const [snowGdChecked, setSnowGdChecked] = useState(false) const [eavesMargin, setEavesMargin] = useState(0) @@ -88,7 +88,7 @@ export function useModuleTrestle(props) { setKerabaMargin(selectedRoof?.kerabaMargin ?? 0) setLengthBase(Math.round(selectedRoof?.length ?? 0)) setCvrYn(selectedRoof?.construction?.cvrYn ?? 'N') - setCvrChecked(selectedRoof?.construction?.cvrChecked ?? false) + setCvrChecked(selectedRoof?.construction?.cvrChecked ?? true) setSnowGdPossYn(selectedRoof?.construction?.snowGdPossYn ?? 'N') setSnowGdChecked(selectedRoof?.construction?.snowGdChecked ?? false) setTrestleDetail(selectedRoof?.trestleDetail) From 9944c92351b571b1c4b0a2ccef7f6f1bf7442f2b Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 13 Feb 2026 13:09:08 +0900 Subject: [PATCH 16/30] =?UTF-8?q?=EC=B9=98=EC=88=98=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasFrame.jsx | 15 +++++++++-- src/components/floor-plan/CanvasMenu.jsx | 2 +- src/components/floor-plan/FloorPlan.jsx | 2 +- .../placementShape/PlacementShapeSetting.jsx | 25 ++++++++++--------- src/hooks/option/useCanvasSetting.js | 22 +++++++++------- 5 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 9441dc7c..20d6a348 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -33,6 +33,7 @@ import { compasDegAtom } from '@/store/orientationAtom' import { hotkeyStore } from '@/store/hotkeyAtom' import { usePopup } from '@/hooks/usePopup' import { outerLinePointsState } from '@/store/outerLineAtom' +import { canvasSettingState } from '@/store/canvasAtom' export default function CanvasFrame() { const canvasRef = useRef(null) @@ -59,6 +60,7 @@ export default function CanvasFrame() { const { basicSetting, fetchBasicSettings } = useCanvasSetting() const { selectedMenu, setSelectedMenu } = useCanvasMenu() const { initEvent } = useEvent() + const canvasSetting = useRecoilValue(canvasSettingState) const loadCanvas = () => { if (!canvas) return @@ -82,7 +84,12 @@ export default function CanvasFrame() { setSelectedMenu('module') }, 500) } else if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL).length > 0) { - setSelectedMenu('outline') + // canvasSetting.roofSizeSet에 따라 메뉴 결정 + if (canvasSetting?.roofSizeSet === '2') { + setSelectedMenu('surface') // 실측값입력 → surface + } else { + setSelectedMenu('outline') // 복시도입력 → outline + } } else { setTimeout(() => { setSelectedMenu('surface') @@ -121,7 +128,11 @@ export default function CanvasFrame() { if (currentCanvasPlan.planNo) { /* 약간의 지연을 줘서 roofMaterials가 로드될 시간을 확보 */ setTimeout(() => { - fetchBasicSettings(Number(currentCanvasPlan.planNo), null) + // 메뉴 이동 시 canvasSetting이 덮어쓰이는 것을 방지 + // 이미 canvasSetting에 roofSizeSet이 있으면 API 호출 건너뛰기 + if (!canvasSetting?.roofSizeSet) { + fetchBasicSettings(Number(currentCanvasPlan.planNo), null) + } }, 100) } }, [currentCanvasPlan, canvas]) diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 97d878a0..be42c977 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -426,7 +426,7 @@ export default function CanvasMenu(props) { return ( (['2', '3'].includes(canvasSetting?.roofSizeSet) && menu.type === 'outline') || (selectedMenu === 'module' && ['placement', 'outline'].includes(menu.type)) || - (isExistModule() && ['placement', 'outline'].some((num) => num === menu.type)) || + (isExistModule() && canvasSetting?.roofSizeSet !== '1' && ['placement', 'outline'].some((num) => num === menu.type)) || (['estimate', 'simulation'].includes(selectedMenu) && ['placement', 'outline', 'surface'].includes(menu.type)) ) } diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 417cc559..6098aec3 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -41,7 +41,7 @@ export default function FloorPlan({ children }) { promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => { if (res.status === 200) { const { data } = res - console.log(data) + //console.log(data) let surfaceTypeValue if (res.data.surfaceType === 'Ⅲ・Ⅳ') { diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index a537b065..14ff26e1 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -16,7 +16,7 @@ import { globalLocaleStore } from '@/store/localeAtom' import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util' import { usePolygon } from '@/hooks/usePolygon' -import { canvasState, currentMenuState } from '@/store/canvasAtom' +import { canvasState, canvasSettingState, currentMenuState } from '@/store/canvasAtom' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { MENU, POLYGON_TYPE } from '@/common/common' import { useRoofFn } from '@/hooks/common/useRoofFn' @@ -51,6 +51,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla const { drawDirectionArrow } = usePolygon() const { setSurfaceShapePattern } = useRoofFn() const canvas = useRecoilValue(canvasState) + const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) const roofDisplay = useRecoilValue(roofDisplaySelector) const { setPolygonLinesActualSize } = usePolygon() @@ -167,10 +168,6 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla }) }, [currentRoof]) - const handleRoofSizeSetChange = (value) => { - setCurrentRoof({ ...currentRoof, roofSizeSet: value }) - } - const handleRoofAngleSetChange = (value) => { setCurrentRoof({ ...currentRoof, roofAngleSet: value }) } @@ -239,12 +236,8 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla newAddedRoofs[0] = { ...roofInfo } setAddedRoofs(newAddedRoofs) - console.log('save Info', { - ...basicSetting, - selectedRoofMaterial: { - ...newAddedRoofs[0], - }, - }) + // currentRoof의 roofSizeSet을 canvasSetting에 반영 + setCanvasSetting({ ...canvasSetting, roofSizeSet: currentRoof?.roofSizeSet }) /** * 배치면초기설정 저장 (메뉴 변경/useEffect 트리거 없이) @@ -272,7 +265,15 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla /** 지붕면 존재 여부에 따라 메뉴 설정 */ const hasRoofs = canvas.getObjects().some((obj) => obj.name === POLYGON_TYPE.ROOF) - if (hasRoofs) { + + // roofSizeSet에 따라 메뉴 설정 + if (currentRoof?.roofSizeSet === '2') { + setSelectedMenu('surface') + setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) + } else if (currentRoof?.roofSizeSet === '1') { + setSelectedMenu('outline') + setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) + } else if (hasRoofs) { setSelectedMenu('surface') setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) } else { diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index edd74c22..01d49937 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -441,15 +441,19 @@ export function useCanvasSetting(executeEffect = true) { setAddedRoofs(addRoofs) if (openPoint !== 'basicSettingSave') { - setCanvasSetting({ - ...basicSetting, - roofMaterials: addRoofs[0], - planNo: roofsRow[0].planNo, - roofSizeSet: roofsRow[0].roofSizeSet, - roofAngleSet: roofsRow[0].roofAngleSet, - roofsData: roofsArray, - selectedRoofMaterial: addRoofs.find((roof) => roof.selected), - }) + // canvasSetting은 현재 값을 유지하고 basicSetting만 업데이트 + // 새로고침 시 canvasSetting이 바뀌는 문제 방지 + if (!canvasSetting?.roofSizeSet) { + setCanvasSetting({ + ...basicSetting, + roofMaterials: addRoofs[0], + planNo: roofsRow[0].planNo, + roofSizeSet: roofsRow[0].roofSizeSet, + roofAngleSet: roofsRow[0].roofAngleSet, + roofsData: roofsArray, + selectedRoofMaterial: addRoofs.find((roof) => roof.selected), + }) + } } } }) From 7836da9d430795835f990f3436dc28ac83c18892 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 20 Feb 2026 11:41:19 +0900 Subject: [PATCH 17/30] =?UTF-8?q?rack=EC=A0=95=EB=B3=B4=20=EC=A0=9C?= =?UTF-8?q?=EB=8C=80=EB=A1=9C=20=EB=AA=BB=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../circuitTrestle/CircuitTrestleSetting.jsx | 124 ++++++++++-------- 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx index 02e9d171..c98b75f0 100644 --- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -63,6 +63,7 @@ export default function CircuitTrestleSetting({ id }) { const originCanvasViewPortTransform = useRef([]) const [isFold, setIsFold] = useState(false) + const [showHiddenBasicSetting, setShowHiddenBasicSetting] = useState(true) const { makers, @@ -108,6 +109,14 @@ export default function CircuitTrestleSetting({ id }) { } }, []) + // 모듈/가대설정 팝업을 순간적으로 열었다가 닫아 초기화 로직 실행 + useEffect(() => { + const timer = setTimeout(() => { + setShowHiddenBasicSetting(false) + }, 300) + return () => clearTimeout(timer) + }, []) + // 모듈이 설치된 경우 rack설치 여부에 따라 설치 된 모듈 아래에 작은 모듈이 설치되어 있을 경우는 모듈 설치 메뉴 reopen useEffect(() => { const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE) @@ -998,63 +1007,70 @@ export default function CircuitTrestleSetting({ id }) { } return ( - - handleClose()} - isFold={isFold} - onFold={() => setIsFold(!isFold)} - /> - -
-
-
-
{getMessage('modal.circuit.trestle.setting.power.conditional.select')}
- -
- {getMessage('modal.circuit.trestle.setting.circuit.allocation')}({getMessage('modal.circuit.trestle.setting.step.up.allocation')}) + <> + {showHiddenBasicSetting && ( +
+ +
+ )} + + handleClose()} + isFold={isFold} + onFold={() => setIsFold(!isFold)} + /> + +
+
+
+
{getMessage('modal.circuit.trestle.setting.power.conditional.select')}
+ +
+ {getMessage('modal.circuit.trestle.setting.circuit.allocation')}({getMessage('modal.circuit.trestle.setting.step.up.allocation')}) +
+ {tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && } + {tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && ( + + )} + {tabNum === 2 && }
- {tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && } - {tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && ( - - )} - {tabNum === 2 && } -
- {tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && ( -
- - -
- )} - {tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && ( -
- - -
- )} - {tabNum === 2 && ( -
- - {/* -
- )} - - + {tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && ( +
+ + +
+ )} + {tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && ( +
+ + +
+ )} + {tabNum === 2 && ( +
+ + {/* +
+ )} + + + ) } From f64d5c438efcde1b7cfacb54c5545d3b24e1e5e9 Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 23 Feb 2026 10:40:37 +0900 Subject: [PATCH 18/30] =?UTF-8?q?[1490]=EB=A9=94=EB=89=B4=20=ED=91=9C?= =?UTF-8?q?=EA=B8=B0=20=EB=B3=80=EA=B2=BD=20=EC=9A=94=EC=B2=AD=20(?= =?UTF-8?q?=EC=8B=A4=EC=B8=A1=EA=B0=92=EC=9E=85=EB=A0=A5->=20=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=EB=A9=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/ja.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/ja.json b/src/locales/ja.json index 2953688e..feb9945f 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -76,7 +76,7 @@ "common.setting.rollback": "前に戻る", "modal.cover.outline.remove": "外壁の取り外し", "modal.cover.outline.select.move": "外壁選択の移動", - "plan.menu.placement.surface": "実測値入力", + "plan.menu.placement.surface": "配置面", "plan.menu.placement.surface.slope.setting": "傾斜設定", "plan.menu.placement.surface.drawing": "配置面の描画", "modal.placement.surface.drawing.straight.line": "直線", From 64db226721d1106a66e0d0f2c5abd970b710861e Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 23 Feb 2026 11:40:34 +0900 Subject: [PATCH 19/30] =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=8B=9C=20spare=200.5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/module/useModule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/module/useModule.js b/src/hooks/module/useModule.js index 8e59a1e7..c81c54cb 100644 --- a/src/hooks/module/useModule.js +++ b/src/hooks/module/useModule.js @@ -1059,7 +1059,7 @@ export function useModule() { } const isOutsideSurface = (module, moduleSetupSurface) => { - return !checkModuleDisjointSurface(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true)) + return !checkModuleDisjointSurface(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true), 0.5) } const getRowModules = (target) => { From fc9623d76dfe874f8c3670afdd9abf9669939d4a Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 23 Feb 2026 16:48:16 +0900 Subject: [PATCH 20/30] =?UTF-8?q?[1480]=20=EC=84=A4=EC=A0=95=EA=B0=92=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20(=EC=9D=B4=EC=A0=84=ED=94=8C=EB=9E=9C?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20->=20=ED=98=84=ED=94=8C=EB=9E=9C=EC=A0=80?= =?UTF-8?q?=EC=9E=A5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../placementShape/PlacementShapeSetting.jsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index 14ff26e1..87ad753c 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -16,7 +16,7 @@ import { globalLocaleStore } from '@/store/localeAtom' import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util' import { usePolygon } from '@/hooks/usePolygon' -import { canvasState, canvasSettingState, currentMenuState } from '@/store/canvasAtom' +import { canvasState, canvasSettingState, currentCanvasPlanState, currentMenuState } from '@/store/canvasAtom' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { MENU, POLYGON_TYPE } from '@/common/common' import { useRoofFn } from '@/hooks/common/useRoofFn' @@ -52,6 +52,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla const { setSurfaceShapePattern } = useRoofFn() const canvas = useRecoilValue(canvasState) const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) + const currentCanvasPlan = useRecoilValue(currentCanvasPlanState) const roofDisplay = useRecoilValue(roofDisplaySelector) const { setPolygonLinesActualSize } = usePolygon() @@ -123,6 +124,15 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla if (openPoint && openPoint === 'canvasMenus') fetchBasicSettings(planNo, openPoint) }, []) + /** + * 현재 활성 플랜이 변경될 때 currentRoof.planNo 업데이트 + */ + useEffect(() => { + if (currentCanvasPlan?.planNo && currentRoof) { + setCurrentRoof(prev => ({ ...prev, planNo: currentCanvasPlan.planNo })) + } + }, [currentCanvasPlan?.planNo]) + /** * 배치면초기설정 데이터 조회 후 화면 오픈 */ @@ -132,7 +142,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla setRaftCodes(raftCodeList) setCurrentRoof({ ...addedRoofs[0], - planNo: planNo, + planNo: currentCanvasPlan?.planNo || planNo, roofSizeSet: String(basicSetting.roofSizeSet), roofAngleSet: basicSetting.roofAngleSet, }) @@ -221,7 +231,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla const handleSaveBtn = async () => { const roofInfo = { ...currentRoof, - planNo: basicSetting.planNo, + planNo: currentCanvasPlan?.planNo || basicSetting.planNo, roofCd: roofRef.roofCd.current?.value, width: roofRef.width.current?.value, length: roofRef.length.current?.value, @@ -244,6 +254,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla */ basicSettingSave({ ...basicSetting, + planNo: currentCanvasPlan?.planNo || basicSetting.planNo, /** * 선택된 지붕재 정보 */ From f08b3cef5b9245dc83a5472fc1745e071b2080e1 Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 23 Feb 2026 17:28:15 +0900 Subject: [PATCH 21/30] =?UTF-8?q?[1475]=20=ED=94=8C=EB=9E=9C=EB=B3=B5?= =?UTF-8?q?=EC=82=AC(=EB=8B=A4=EC=A4=91=EC=A7=80=EB=B6=95=20=EB=B3=B5?= =?UTF-8?q?=EC=82=AC=EC=95=88=EB=90=A8=20-=20=EB=8B=A8=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=EB=B6=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/option/useCanvasSetting.js | 54 ++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index 01d49937..54f6c6fd 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -570,12 +570,44 @@ export function useCanvasSetting(executeEffect = true) { */ const basicSettingCopySave = async (params) => { try { - const patternData = { - objectNo: correntObjectNo, - planNo: Number(params.planNo), - roofSizeSet: Number(params.roofSizeSet), - roofAngleSet: params.roofAngleSet, - roofMaterialsAddList: params.roofsData.map((item) => ({ + // roofsData가 단일 항목인 경우, 모든 추가된 지붕재(addedRoofs)를 사용하여 다중 항목으로 확장 + let roofMaterialsList = [] + + if (params.roofsData && params.roofsData.length === 1) { + // 단일 항목인 경우 addedRoofs의 모든 항목을 사용 + if (addedRoofs && addedRoofs.length > 0) { + roofMaterialsList = addedRoofs.map((roof, index) => ({ + planNo: Number(params.planNo), + roofApply: roof.selected || index === 0, // 첫 번째 또는 선택된 항목 + roofSeq: index, + roofMatlCd: roof.roofMatlCd, + roofWidth: roof.width || roof.roofWidth, + roofHeight: roof.length || roof.roofHeight, + roofHajebichi: roof.hajebichi || 0, + roofGap: roof.raft || roof.roofGap, + roofLayout: roof.layout || 'P', + roofPitch: roof.pitch || 0, + roofAngle: roof.angle || 0, + })) + } else { + // addedRoofs가 비어있을 경우 원래 단일 항목 사용 + roofMaterialsList = params.roofsData.map((item) => ({ + planNo: Number(item.planNo), + roofApply: item.roofApply, + roofSeq: item.roofSeq, + roofMatlCd: item.roofMatlCd, + roofWidth: item.roofWidth, + roofHeight: item.roofHeight, + roofHajebichi: item.roofHajebichi, + roofGap: item.roofGap, + roofLayout: item.roofLayout, + roofPitch: item.roofPitch, + roofAngle: item.roofAngle, + })) + } + } else { + // 다중 항목인 경우 기존 방식 사용 + roofMaterialsList = params.roofsData.map((item) => ({ planNo: Number(item.planNo), roofApply: item.roofApply, roofSeq: item.roofSeq, @@ -587,7 +619,15 @@ export function useCanvasSetting(executeEffect = true) { roofLayout: item.roofLayout, roofPitch: item.roofPitch, roofAngle: item.roofAngle, - })), + })) + } + + const patternData = { + objectNo: correntObjectNo, + planNo: Number(params.planNo), + roofSizeSet: Number(params.roofSizeSet), + roofAngleSet: params.roofAngleSet, + roofMaterialsAddList: roofMaterialsList, } await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => { From 022278a248812d370e5bd814c3d2cf67eb4c55fe Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 25 Feb 2026 10:11:21 +0900 Subject: [PATCH 22/30] =?UTF-8?q?[1506]=20=EB=8F=99,=ED=98=84=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20sk=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/skeleton-utils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index aeb63ffa..7d6b1b23 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -470,7 +470,9 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { //마루이동 if (moveFlowLine !== 0 || moveUpDown !== 0) { - roofLineContactPoints = movingLineFromSkeleton(roofId, canvas) + const movedPoints = movingLineFromSkeleton(roofId, canvas) + roofLineContactPoints = movedPoints + changRoofLinePoints = movedPoints } // changRoofLinePoints 좌표를 roof.skeletonPoints에 저장 (원본 roof.points는 유지) From 5a64ff945aa470a52328838a2afd0d82a1c610de Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 25 Feb 2026 10:29:51 +0900 Subject: [PATCH 23/30] =?UTF-8?q?#1478=20=EC=99=B8=EB=B2=BD=EC=84=A0=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=EC=9C=BC=EB=A1=9C=20=EC=A7=80=EB=B6=95?= =?UTF-8?q?=EB=A9=B4=20=ED=95=A0=EB=8B=B9=20=EC=8B=9C=20=EA=B3=84=EC=82=B0?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 9481af67..40a3d85f 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1955,7 +1955,7 @@ export const usePolygon = () => { forceUpdate = true } - if (polygon.from !== 'surface') { + /*if (polygon.from !== 'surface') { // createdRoofs들의 모든 lines를 확인해서 length값이 1이하인 차이가 있으면 통일 시킨다. const allRoofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const allRoofLines = allRoofs.flatMap((roof) => roof.lines) @@ -1978,7 +1978,7 @@ export const usePolygon = () => { } } } - } + }*/ polygon.lines.forEach((line, index) => { if (line.attributes.isCalculated && !forceUpdate) { From 1a2fa7bbf6f07377e4d40d3d3234bfa6129d5818 Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 25 Feb 2026 17:49:26 +0900 Subject: [PATCH 24/30] =?UTF-8?q?sk=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/skeleton-utils.js | 124 +++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index 7d6b1b23..6a3c567e 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -394,79 +394,81 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { const isClosedPolygon = (points) => points.length > 1 && isSamePoint(points[0], points[points.length - 1]) - const roofLinePoints = isClosedPolygon(roof.points) ? roof.points.slice(0, -1) : roof.points const orderedBaseLinePoints = isClosedPolygon(baseLinePoints) ? baseLinePoints.slice(0, -1) : baseLinePoints - console.log('roofLinePoints:', roofLinePoints) - console.log('baseLinePoints:', orderedBaseLinePoints) - - // baseLinePoint에서 roofLinePoint 방향으로 45도 대각선 확장 후 가장 가까운 접점 계산 - // 각 포인트의 dx, dy, sign 정보 수집 - const contactData = orderedBaseLinePoints.map((point, index) => { - const roofPoint = roofLinePoints[index] - if (!roofPoint) return { dx: 0, dy: 0, signDx: 0, signDy: 0 } - - const dx = roofPoint.x - point.x - const dy = roofPoint.y - point.y - return { dx, dy, signDx: Math.sign(dx), signDy: Math.sign(dy) } - }) - - // maxStep: 모든 포인트 중 가장 큰 45도 step - const maxStep = contactData.reduce((max, data) => { - return Math.max(max, Math.min(Math.abs(data.dx), Math.abs(data.dy))) - }, 0) - - // baseLine 폴리곤의 중심 계산 (확장 방향 결정용) + // baseLine 폴리곤의 중심 계산 (offset 방향 결정용) const centroid = orderedBaseLinePoints.reduce((acc, p) => { acc.x += p.x / orderedBaseLinePoints.length acc.y += p.y / orderedBaseLinePoints.length return acc }, { x: 0, y: 0 }) - // 모든 포인트를 동일한 maxStep으로 45도 확장 - let roofLineContactPoints = orderedBaseLinePoints.map((point, index) => { - const data = contactData[index] - const { dx, dy } = data - - // dx 또는 dy가 0인 경우 중심에서 바깥쪽 방향으로 확장 - let signDx = data.signDx - let signDy = data.signDy - if (signDx === 0) { - signDx = point.x >= centroid.x ? 1 : -1 - } - if (signDy === 0) { - signDy = point.y >= centroid.y ? 1 : -1 - } - + // baseLine을 offset만큼 바깥으로 평행이동 + const offsetLine = (line) => { + const sp = line.startPoint, ep = line.endPoint + const offset = line.attributes?.offset ?? 0 + const edx = ep.x - sp.x, edy = ep.y - sp.y + const len = Math.hypot(edx, edy) + if (len === 0) return { sp: { ...sp }, ep: { ...ep } } + let nx = -edy / len, ny = edx / len + const mid = { x: (sp.x + ep.x) / 2, y: (sp.y + ep.y) / 2 } + if (nx * (centroid.x - mid.x) + ny * (centroid.y - mid.y) > 0) { nx = -nx; ny = -ny } return { - x: point.x + signDx * maxStep, - y: point.y + signDy * maxStep + sp: { x: sp.x + nx * offset, y: sp.y + ny * offset }, + ep: { x: ep.x + nx * offset, y: ep.y + ny * offset } } + } + + // 두 직선의 교차점 + const lineIntersect = (a1, a2, b1, b2) => { + const denom = (a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x) + if (Math.abs(denom) < 0.001) return null + const t = ((a1.x - b1.x) * (b1.y - b2.y) - (a1.y - b1.y) * (b1.x - b2.x)) / denom + return { x: a1.x + t * (a2.x - a1.x), y: a1.y + t * (a2.y - a1.y) } + } + + // 각 꼭짓점에서 인접 두 baseLine의 offset 교차점으로 확장 좌표 계산 + let changRoofLinePoints = orderedBaseLinePoints.map((point) => { + const adjLines = baseLines.filter(line => + isSamePoint(point, line.startPoint) || isSamePoint(point, line.endPoint) + ) + if (adjLines.length < 2) return { ...point } + const oL1 = offsetLine(adjLines[0]), oL2 = offsetLine(adjLines[1]) + return lineIntersect(oL1.sp, oL1.ep, oL2.sp, oL2.ep) || { ...point } }) - const maxContactDistance = Math.hypot(maxStep, maxStep) - - let changRoofLinePoints = orderedBaseLinePoints.map((point, index) => { - const contactPoint = roofLineContactPoints[index] - if (!contactPoint) return point - - const dx = contactPoint.x - point.x - const dy = contactPoint.y - point.y - const len = Math.hypot(dx, dy) - - // 거리가 0이면 이미 같은 위치 - if (len === 0) return point - - const step = maxContactDistance - if (step === 0) return point - - const nextX = point.x + (dx / len) * step - const nextY = point.y + (dy / len) * step - return { - x: nextX, - y: nextY + // 중복 좌표 및 일직선 위의 불필요한 점 제거 (L자 확장 시 사각형으로 단순화) + const simplifyPolygon = (pts) => { + let result = [...pts] + let changed = true + while (changed) { + changed = false + for (let i = result.length - 1; i >= 0; i--) { + const next = result[(i + 1) % result.length] + if (Math.abs(result[i].x - next.x) < 0.5 && Math.abs(result[i].y - next.y) < 0.5) { + result.splice(i, 1) + changed = true + } + } + for (let i = result.length - 1; i >= 0 && result.length > 3; i--) { + const prev = result[(i - 1 + result.length) % result.length] + const curr = result[i] + const next = result[(i + 1) % result.length] + const cross = (curr.x - prev.x) * (next.y - prev.y) - (curr.y - prev.y) * (next.x - prev.x) + if (Math.abs(cross) < 1) { + result.splice(i, 1) + changed = true + } + } } - }) + return result + } + changRoofLinePoints = simplifyPolygon(changRoofLinePoints) + + let roofLineContactPoints = [...changRoofLinePoints] + + console.log('baseLinePoints:', orderedBaseLinePoints) + console.log('changRoofLinePoints:', changRoofLinePoints) //마루이동 if (moveFlowLine !== 0 || moveUpDown !== 0) { @@ -489,7 +491,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // 스켈레톤 데이터를 기반으로 내부선 생성 roof.innerLines = roof.innerLines || [] roof.innerLines = createInnerLinesFromSkeleton(roofId, canvas, skeleton, textMode) - //console.log("roofInnerLines:::", roof.innerLines); + console.log("roofInnerLines:::", roof.innerLines); // 캔버스에 스켈레톤 상태 저장 if (!canvas.skeletonStates) { canvas.skeletonStates = {} From 95811760dc9324dc7275634b0a294b9dc9d4a45a Mon Sep 17 00:00:00 2001 From: ysCha Date: Thu, 26 Feb 2026 15:48:35 +0900 Subject: [PATCH 25/30] =?UTF-8?q?[1386]=201,2=EC=B0=A8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 16 ++++++++++++++-- src/components/management/StuffSubHeader.jsx | 7 ++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 75db2623..ed998dc6 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -357,7 +357,13 @@ export default function StuffDetail() { if (res?.data?.createSaleStoreId === 'T01') { if (session?.storeId !== 'T01') { - setShowButton('none') + //T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다. + if(session?.storeId !== res?.data?.saleStoreId){ + if(session?.storeId !== res?.data?.firstAgentId) { + setShowButton('none') + } + } + } } if (isObjectNotEmpty(res.data)) { @@ -1658,7 +1664,13 @@ export default function StuffDetail() { const getCellDoubleClicked = (params) => { if (managementState?.createSaleStoreId === 'T01') { if (session?.storeId !== 'T01') { - return false + //T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다. + if(session?.storeId !== managementState?.saleStoreId){ + if(session?.storeId !== managementState?.firstAgentId) { + return false + } + } + } } diff --git a/src/components/management/StuffSubHeader.jsx b/src/components/management/StuffSubHeader.jsx index 6cbfd862..b0ff2725 100644 --- a/src/components/management/StuffSubHeader.jsx +++ b/src/components/management/StuffSubHeader.jsx @@ -39,7 +39,12 @@ export default function StuffSubHeader({ type }) { if (isObjectNotEmpty(managementState)) { if (managementState?.createSaleStoreId === 'T01') { if (session?.storeId !== 'T01') { - setButtonStyle('none') + //T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다. + if(session?.storeId !== managementState?.saleStoreId){ + if(session?.storeId !== managementState?.firstAgentId) { + setButtonStyle('none') + } + } } } } From ab04911ac67921795c02a9f48a13b1f6424e287a Mon Sep 17 00:00:00 2001 From: ysCha Date: Thu, 26 Feb 2026 16:44:00 +0900 Subject: [PATCH 26/30] =?UTF-8?q?[1386]=201,2=EC=B0=A8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index ed998dc6..fa852d71 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -294,8 +294,13 @@ export default function StuffDetail() { docDownButtonStyle = 'none' } else { if (params?.data?.createSaleStoreId === 'T01' && session?.storeId !== 'T01') { - estimateDetailButtonStyle = 'none' + if(session?.storeId !== params?.data?.saleStoreId){ + if(session?.storeId !== params?.data?.firstAgentId) { + estimateDetailButtonStyle = 'none' + } + } } + if (params?.data?.tempFlg === '1' || !params?.data?.docNo) { docDownButtonStyle = 'none' } @@ -391,7 +396,15 @@ export default function StuffDetail() { }) } if (isNotEmptyArray(res.data.planList)) { - setPlanGridProps({ ...planGridProps, planGridData: res.data.planList }) + + const planGridData = res.data.planList.map(plan => ({ + ...plan, + createSaleStoreId: res.data.createSaleStoreId, + saleStoreId: res.data.saleStoreId, + firstAgentId: res.data.firstAgentId + })) + + setPlanGridProps({ ...planGridProps, planGridData }) } else { setPlanGridProps({ ...planGridProps, planGridData: [] }) } From 7801f5a2d126296974dbbcb3c78f84e89fe1b253 Mon Sep 17 00:00:00 2001 From: yscha Date: Mon, 2 Mar 2026 12:29:57 +0900 Subject: [PATCH 27/30] =?UTF-8?q?rackInfos=20is=20null:=20=EB=9E=99?= =?UTF-8?q?=EC=9D=84=20=EA=B7=B8=EB=A6=AC=EB=A0=A4=EA=B3=A0=20=ED=95=A0=20?= =?UTF-8?q?=EB=95=8C=20=EB=9E=99=20=EC=A0=95=EB=B3=B4=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/module/useTrestle.js | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/hooks/module/useTrestle.js b/src/hooks/module/useTrestle.js index e4ee52a8..f8bc9901 100644 --- a/src/hooks/module/useTrestle.js +++ b/src/hooks/module/useTrestle.js @@ -90,9 +90,13 @@ export const useTrestle = () => { let rackInfos = [] if (rack) { + console.log('Original rack data:', rack) rackInfos = Object.keys(rack).map((key) => { return { key, value: rack[key] } }) + console.log('Processed rackInfos:', rackInfos) + } else { + console.log('Rack is null or undefined') } // 모듈들의 centerPoint들을 이용해 각 모듈의 정보(가장 아랫라인 모듈, 가장 윗라인 모듈, 접면, 반접면 등 계산) @@ -451,6 +455,16 @@ export const useTrestle = () => { })?.value.racks mostRowsModule = Math.max(leftRows, rightRows, centerRows, mostRowsModule) + console.log('=== Debug rackInfos ===') + console.log('rackInfos:', rackInfos) + console.log('leftRowsInfo:', leftRowsInfo) + console.log('rightRowsInfo:', rightRowsInfo) + console.log('centerRowsInfo:', centerRowsInfo) + console.log('leftRacks:', leftRacks) + console.log('rightRacks:', rightRacks) + console.log('centerRacks:', centerRacks) + console.log('rackYn:', rackYn) + console.log('========================') if (rackYn === 'Y') { drawRacks(leftRacks, rackQty, rackIntvlPct, module, direction, 'L', rackYn) @@ -1082,14 +1096,19 @@ export const useTrestle = () => { const drawRacks = (rackInfos, rackQty, rackIntvlPct, module, direction, l, rackYn) => { const { width, height, left, top, lastX, lastY, surfaceId } = module const surface = canvas.getObjects().find((obj) => obj.id === surfaceId) - if (!rackInfos) { - const maxRows = surface.trestleDetail.moduleMaxRows - const maxCols = surface.trestleDetail.moduleMaxCols - const msg = `段数の上限は${maxRows}段です。 上限より上の段には設置できません` - swalFire({ title: msg, type: 'alert' }) - throw new Error('rackInfos is null') - } + // if (!rackInfos) { + // const maxRows = surface.trestleDetail.moduleMaxRows + // const maxCols = surface.trestleDetail.moduleMaxCols + // const msg = `段数の上限は${maxRows}段です。 上限より上の段には設置できません` + // swalFire({ title: msg, type: 'alert' }) + // throw new Error('rackInfos is null') + // } + if (!rackInfos) { + const msg = '該当モジュールタイプに関するラック情報がありません。' + swalFire({ title: msg, type: 'alert' }) + return + } const roof = canvas.getObjects().find((obj) => obj.id === surface.parentId) const degree = getDegreeByChon(roof.roofMaterial.pitch) rackIntvlPct = rackIntvlPct === 0 ? 1 : rackIntvlPct // 0인 경우 1로 변경 From 8d91a78dee2659dc04a06419ec854b79a3decd51 Mon Sep 17 00:00:00 2001 From: rjy1537 Date: Tue, 3 Mar 2026 18:44:36 +0900 Subject: [PATCH 28/30] =?UTF-8?q?2=EC=B0=A8=EC=A0=90=20=EC=9D=B4=EC=83=81?= =?UTF-8?q?=EC=9D=B4=20=EB=93=A4=EC=96=B4=EC=98=A8=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EB=AC=BC=EA=B1=B4=EB=B2=88=ED=98=B8=EC=97=90=EC=84=9C=201?= =?UTF-8?q?=EC=B0=A8=ED=8C=90=EB=A7=A4=EC=A0=90=20=EC=98=81=EC=97=AD=20?= =?UTF-8?q?=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index fa852d71..c2d96b0f 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -1867,7 +1867,7 @@ export default function StuffDetail() {
-
+
{data.type === 'check' ? ( @@ -181,6 +182,7 @@ const Placement = forwardRef((props, refs) => { )}
From 61dc83d7db53f3e9a4b09948803b8b910703fc52 Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 3 Mar 2026 19:30:23 +0900 Subject: [PATCH 29/30] !!! --- src/components/management/StuffSubHeader.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/management/StuffSubHeader.jsx b/src/components/management/StuffSubHeader.jsx index b0ff2725..aa538150 100644 --- a/src/components/management/StuffSubHeader.jsx +++ b/src/components/management/StuffSubHeader.jsx @@ -39,7 +39,7 @@ export default function StuffSubHeader({ type }) { if (isObjectNotEmpty(managementState)) { if (managementState?.createSaleStoreId === 'T01') { if (session?.storeId !== 'T01') { - //T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다. + //T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다..... if(session?.storeId !== managementState?.saleStoreId){ if(session?.storeId !== managementState?.firstAgentId) { setButtonStyle('none') From bbe5418f1c315aa9d133120d3cd65f41c894c1d3 Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 3 Mar 2026 19:54:40 +0900 Subject: [PATCH 30/30] !!! --- src/components/management/StuffSubHeader.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/management/StuffSubHeader.jsx b/src/components/management/StuffSubHeader.jsx index aa538150..284382a7 100644 --- a/src/components/management/StuffSubHeader.jsx +++ b/src/components/management/StuffSubHeader.jsx @@ -39,7 +39,7 @@ export default function StuffSubHeader({ type }) { if (isObjectNotEmpty(managementState)) { if (managementState?.createSaleStoreId === 'T01') { if (session?.storeId !== 'T01') { - //T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다..... + //T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다.. if(session?.storeId !== managementState?.saleStoreId){ if(session?.storeId !== managementState?.firstAgentId) { setButtonStyle('none')