From a5d2964e8e7b4d763ed832fb06a4b4b7ee4c76be Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 6 May 2026 16:44:26 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[2089]=20=EC=A0=80=EA=B5=AC=EB=B0=B0+?= =?UTF-8?q?=ED=8A=B9=EC=A0=95=EA=B8=B0=EC=99=80=20=EC=8B=9C=EA=B3=B5=20?= =?UTF-8?q?=EB=A7=A4=EB=89=B4=EC=96=BC=20=EC=95=88=EB=82=B4=20confirm=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../placementShape/PlacementShapeSetting.jsx | 19 +++++ .../roofcover/useRoofAllocationSetting.js | 38 ++++++++- src/locales/ja.json | 1 + src/locales/ko.json | 1 + src/util/roof-pitch-warning.js | 80 +++++++++++++++++++ 5 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 src/util/roof-pitch-warning.js diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index 9ba4e342..ee75ea20 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -23,6 +23,11 @@ import { useRoofFn } from '@/hooks/common/useRoofFn' import { usePlan } from '@/hooks/usePlan' import { normalizeDecimal } from '@/util/input-utils' import { CalculatorInput } from '@/components/common/input/CalcInput' +// [LOW-PITCH-WARN 2026-05-06] 저구배 + 특정 기와 사용 시 시공 매뉴얼 안내 alert +import { useSwal } from '@/hooks/useSwal' +import { notifyLowPitchRestrictionForRoofs, isLowPitchRestricted, LOW_PITCH_RESTRICTED_ROOF_IDS } from '@/util/roof-pitch-warning' +// [LOW-PITCH-DIAG 2026-05-06 TEMP] 진단용 — 검증 후 import + 호출 함께 제거 +import { debugCapture } from '@/util/debugCapture' /** * 지붕 레이아웃 @@ -69,6 +74,8 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla hajebichi: useRef(null), } const { saveCanvas } = usePlan() + // [LOW-PITCH-WARN 2026-05-06] + const { swalFire } = useSwal() /** * 치수 입력방법(복시도입력/실측값입력/육지붕) */ @@ -244,6 +251,18 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla index: 0, } + // [LOW-PITCH-DIAG 2026-05-06 TEMP] 진단용 — 검증 후 제거 + debugCapture.log('LOW-PITCH-DIAG handleSaveBtn', { + roofMatlCd: roofInfo.roofMatlCd, + pitch: roofInfo.pitch, + angle: roofInfo.angle, + idMatched: LOW_PITCH_RESTRICTED_ROOF_IDS.has(roofInfo.roofMatlCd), + restricted: isLowPitchRestricted(roofInfo), + }) + + // [LOW-PITCH-WARN 2026-05-06] 6종 기와 + (2 ≤ 寸 < 2.5 OR 11.31 ≤ 度 < 14.04) 일 때 안내 alert. OK 누르면 저장 진행 + await notifyLowPitchRestrictionForRoofs([roofInfo], swalFire, getMessage) + const newAddedRoofs = [...addedRoofs] newAddedRoofs[0] = { ...roofInfo } setAddedRoofs(newAddedRoofs) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index cd0fc1a6..e99c122b 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -32,6 +32,10 @@ import { roofsState } from '@/store/roofAtom' import { useText } from '@/hooks/useText' import { QLine } from '@/components/fabric/QLine' import { calcLineActualSize2 } from '@/util/qpolygon-utils' +// [LOW-PITCH-WARN 2026-05-06] 저구배 + 특정 기와 사용 시 시공 매뉴얼 안내 alert +import { notifyLowPitchRestrictionForRoofs, isLowPitchRestricted, LOW_PITCH_RESTRICTED_ROOF_IDS } from '@/util/roof-pitch-warning' +// [LOW-PITCH-DIAG 2026-05-06 TEMP] 진단용 — 검증 후 import + 호출 함께 제거 +import { debugCapture } from '@/util/debugCapture' export function useRoofAllocationSetting(id) { const canvas = useRecoilValue(canvasState) @@ -306,7 +310,22 @@ export function useRoofAllocationSetting(id) { /** * 선택한 지붕재로 할당 */ - const handleSave = () => { + const handleSave = async () => { + // [LOW-PITCH-DIAG 2026-05-06 TEMP] 진단용 — 검증 후 제거 + debugCapture.log('LOW-PITCH-DIAG handleSave', { + count: currentRoofList?.length, + list: currentRoofList?.map((r) => ({ + roofMatlCd: r.roofMatlCd, + pitch: r.pitch, + angle: r.angle, + idMatched: LOW_PITCH_RESTRICTED_ROOF_IDS.has(r.roofMatlCd), + restricted: isLowPitchRestricted(r), + })), + }) + + // [LOW-PITCH-WARN 2026-05-06] 등록된 모든 지붕재에 대해 6종+저구배 검사 후 안내 alert. OK 누르면 저장 진행 + await notifyLowPitchRestrictionForRoofs(currentRoofList, swalFire, getMessage) + /** * 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정 */ @@ -345,7 +364,22 @@ export function useRoofAllocationSetting(id) { /** * 지붕재 오른쪽 마우스 클릭 후 단일로 지붕재 변경 필요한 경우 */ - const handleSaveContext = () => { + const handleSaveContext = async () => { + // [LOW-PITCH-DIAG 2026-05-06 TEMP] 진단용 — 검증 후 제거 + debugCapture.log('LOW-PITCH-DIAG handleSaveContext', { + count: currentRoofList?.length, + list: currentRoofList?.map((r) => ({ + roofMatlCd: r.roofMatlCd, + pitch: r.pitch, + angle: r.angle, + idMatched: LOW_PITCH_RESTRICTED_ROOF_IDS.has(r.roofMatlCd), + restricted: isLowPitchRestricted(r), + })), + }) + + // [LOW-PITCH-WARN 2026-05-06] 등록된 모든 지붕재에 대해 6종+저구배 검사 후 안내 alert. OK 누르면 저장 진행 + await notifyLowPitchRestrictionForRoofs(currentRoofList, swalFire, getMessage) + const newRoofList = currentRoofList.map((roof, idx) => { if (roof.index !== idx) { // 기존 저장된 지붕재의 index 수정 diff --git a/src/locales/ja.json b/src/locales/ja.json index dee07342..443b1d8f 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -38,6 +38,7 @@ "modal.roof.shape.setting.patten.a": "Aパターン", "modal.roof.shape.setting.patten.b": "Bパターン", "modal.roof.shape.setting.side": "個別に設定", + "modal.roof.material.low.pitch.warning": "2寸以上2.5寸未満の瓦を使用する場合、適用可能な瓦製品および構造物に制限があります。必ず施工マニュアルをご確認ください。", "plan.menu.roof.cover": "伏せ図入力", "plan.menu.roof.cover.outline.drawing": "外壁線を描く", "plan.menu.roof.cover.roof.shape.setting": "屋根形状の設定", diff --git a/src/locales/ko.json b/src/locales/ko.json index 79f691ad..3e5ed786 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -38,6 +38,7 @@ "modal.roof.shape.setting.patten.a": "A 패턴", "modal.roof.shape.setting.patten.b": "B 패턴", "modal.roof.shape.setting.side": "변별로 설정", + "modal.roof.material.low.pitch.warning": "2寸 이상 2.5寸 미만의 기와를 사용하는 경우, 적용 가능한 기와 제품 및 구조물에 제한이 있습니다. 반드시 시공 매뉴얼을 확인해 주세요.", "plan.menu.roof.cover": "지붕덮개", "plan.menu.roof.cover.outline.drawing": "외벽선 그리기", "plan.menu.roof.cover.roof.shape.setting": "지붕형상 설정", diff --git a/src/util/roof-pitch-warning.js b/src/util/roof-pitch-warning.js new file mode 100644 index 00000000..17099f91 --- /dev/null +++ b/src/util/roof-pitch-warning.js @@ -0,0 +1,80 @@ +/** + * [LOW-PITCH-WARN 2026-05-06] + * 저구배 + 특정 기와 제품 조합 사용 시 시공 매뉴얼 안내 alert. + * + * 트리거 (호출 위치): + * 1) PlacementShapeSetting handleSaveBtn — 배치면 초기설정 저장 + * 2) useRoofAllocationSetting handleSave — 屋根面の割り当て + * 3) useRoofAllocationSetting handleSaveContext — 屋根材変更 후 割り当て + * + * 조건: + * roofMatlCd ∈ 6종 AND (2 ≤ pitch < 2.5 OR 11.31 ≤ angle < 14.04) + * + * 동작: + * 안내 alert (OK 만). 중단 흐름 없음 — OK 누르면 저장 진행. + * 등록된 지붕재 여러 개일 경우, 해당하는 지붕재마다 순차로 alert. + * 세션 캐시 없음 — 트리거마다 매번 표시. + */ + +/** 경고 대상 屋根材 ID 6종 */ +export const LOW_PITCH_RESTRICTED_ROOF_IDS = new Set([ + 'ROOF_ID_WA_53A', + 'ROOF_ID_WA_53B', + 'ROOF_ID_HIRA_C', + 'ROOF_ID_HIRA_D', + 'ROOF_ID_HIRA_S', + 'ROOF_ID_HIRA_SEME', +]) + +/** 寸 하한(포함) ~ 寸 상한(제외) */ +const PITCH_LOW_INCLUSIVE = 2 +const PITCH_HIGH_EXCLUSIVE = 2.5 +/** 度 하한(포함) ~ 度 상한(제외) — 2寸≈11.31°, 2.5寸≈14.04° */ +const ANGLE_LOW_INCLUSIVE = 11.31 +const ANGLE_HIGH_EXCLUSIVE = 14.04 + +/** + * 단일 지붕재가 경고 대상인지 판정. + * pitch / angle 둘 중 하나라도 범위에 들면 true. + * (roofAngleSet 이 'slope' 면 pitch 만, 'flat' 이면 angle 만 갱신될 수 있어 OR.) + */ +export const isLowPitchRestricted = (roof) => { + if (!roof) return false + if (!LOW_PITCH_RESTRICTED_ROOF_IDS.has(roof.roofMatlCd)) return false + + const pitch = Number(roof.pitch) + const angle = Number(roof.angle) + + const pitchInRange = + !Number.isNaN(pitch) && pitch >= PITCH_LOW_INCLUSIVE && pitch < PITCH_HIGH_EXCLUSIVE + const angleInRange = + !Number.isNaN(angle) && angle >= ANGLE_LOW_INCLUSIVE && angle < ANGLE_HIGH_EXCLUSIVE + + return pitchInRange || angleInRange +} + +/** + * 지붕재 배열에 대해 순차로 안내 alert 띄우기. + * OK 만 있는 단순 안내 — 중단 흐름 없음. await 로 모달 닫힘까지 대기. + * + * @param {Array} roofs - 지붕재 배열 (단일이면 [roof]) + * @param {Function} swalFire - useSwal().swalFire + * @param {Function} getMessage - useMessage().getMessage + * @returns {Promise} + */ +export const notifyLowPitchRestrictionForRoofs = async (roofs, swalFire, getMessage) => { + if (!Array.isArray(roofs) || roofs.length === 0) return + + for (const roof of roofs) { + if (!isLowPitchRestricted(roof)) continue + + await new Promise((resolve) => { + swalFire({ + type: 'alert', + icon: 'warning', + text: getMessage('modal.roof.material.low.pitch.warning'), + confirmFn: () => resolve(), + }) + }) + } +} From cee4f9e4e931f351b7d45331c5d8db7b79241162 Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 6 May 2026 16:46:06 +0900 Subject: [PATCH 2/3] claude --- .claude/memory/MEMORY.md | 3 +- .claude/memory/feedback_handover_sync.md | 34 ++++++++++++++------ .claude/memory/project_low_pitch_warning.md | 28 +++++++++++++++++ .gitignore | 10 +++++- docs/UI_MODAL_HANDOVER.md | 35 ++++++++++++++++++++- 5 files changed, 97 insertions(+), 13 deletions(-) create mode 100644 .claude/memory/project_low_pitch_warning.md diff --git a/.claude/memory/MEMORY.md b/.claude/memory/MEMORY.md index f9d56492..c0fcfad5 100644 --- a/.claude/memory/MEMORY.md +++ b/.claude/memory/MEMORY.md @@ -1,6 +1,6 @@ - [잘 되는 코드 건들지 않기](feedback_no_touch_working_code.md) — 버그 수정 시 정상 코드 수정 금지, 최소 지점만 수정 - [코드 수정 시 주석 + 커밋 메시지 둘 다 제시](feedback_inline_change_tag.md) — 인라인 `[태그명 YYYY-MM-DD]` + 작업 단락 끝에 git commit 메시지 제안 (사용자가 직접 입력) -- [작업 완료 = 코드+인수인계서+커밋 메시지 한번에](feedback_handover_sync.md) — 코드 변경 시 docs/ 인수인계서 동시 갱신, 한국어 커밋 메시지 제시(직접 커밋 X), 해시는 post-commit 훅 자동 기록 +- [작업 완료 = 코드+인수인계서/메모리, 커밋은 2단계](feedback_handover_sync.md) — 같은 응답에 코드+docs+memory 모두 작성. 단 커밋은 1차 코드만→해시확보→[TBD-CODE] 치환→2차 docs/memory. 커밋 메시지 2개 제시. - [커밋 로그 파일 위치](reference_commit_log.md) — qcast-front 는 post-commit 훅으로 `memory/commit_log.txt` 에 매 커밋 자동 append - [오버 이동은 평행 위치까지 클램핑](feedback_over_move_silent_skip.md) — useMovementSetting handleSave OVER_GUARD. 정책 flag `OVER_MOVE_POLICY` (clamp/reject/passthrough) 상단 상수로 전환 가능 - [moveLine + offset 이중 적용 방지](feedback_moveline_offset.md) — drawHelpLine 호출 전 roof.moveFlowLine/moveUpDown 설정 금지. 순서: baseLines 복원 → drawHelpLine(moveLine 없이) → moveLine 속성 복원 @@ -22,3 +22,4 @@ - [마루이동 x/y-overlap 사전필터](project_marudo_xoverlap_prefilter.md) — useMovementSetting FLOW_LINE 거리최소필터 이전 overlap 사전필터. 2차 이동 반대측 wall 오선택 차단. - [hip pitch 폴백](project_hip_pitch_fallback.md) — processEavesEdge:2915 outerLine.pitch ?? roof.pitch ?? 0. hip 의 plane===actual 메뉴토글 무효 버그 수정. - [auxiliaryLine integrate 제외](project_aux_line_integrate_exclusion.md) — integrateExtensionLines sk-finder 에서 aux 제외. ext+aux 코너끝점 손실 → split 실패 차단. +- [저구배+특정기와 안내 alert](project_low_pitch_warning.md) — 6종 기와(和瓦A/B,平板瓦C/D,S瓦,セメント瓦)+寸 2~2.5미만 시 시공 매뉴얼 안내 alert(OK만). util `roof-pitch-warning.js` diff --git a/.claude/memory/feedback_handover_sync.md b/.claude/memory/feedback_handover_sync.md index 5375846e..d5cf4c5a 100644 --- a/.claude/memory/feedback_handover_sync.md +++ b/.claude/memory/feedback_handover_sync.md @@ -1,20 +1,20 @@ --- -name: 작업 완료 시 인수인계서 동시 갱신 + 커밋 메시지 제안 + 커밋 해시는 자동 로그 참조 -description: 코드 변경이 끝나면 같은 응답 안에서 관련 인수인계서까지 자동 갱신하고 한국어 커밋 메시지 한 줄을 제시한다. 커밋 해시는 post-commit 훅이 누적하는 commit_log.txt 에서 참조. +name: 작업 완료 시 인수인계서 동시 갱신 + 2단계 커밋 (선코드 후문서) + 커밋 해시는 자동 로그 참조 +description: 코드 변경이 끝나면 같은 응답에서 인수인계서/메모리도 작성. 단, 커밋은 2단계 — 1차 코드만 커밋 → 해시 확인 → 인수인계서/메모리에 해시 명시 → 2차 커밋. 해시 자리는 [TBD-CODE] placeholder. type: feedback --- -## 1. 커밋 메시지 제안 +## 1. 커밋 메시지 제안 (2단계) -작업이 완료되면 항상 git 커밋에 사용할 메시지를 함께 제시한다. +작업이 완료되면 git 커밋 메시지를 **2개** 제시한다 — 1차 코드용 / 2차 문서·메모리용. -**Why:** 사용자가 직접 커밋한다 (Claude 가 `git commit` 하지 말 것). 매번 메시지 작성 요청을 반복시키지 않기 위함. +**Why:** 사용자가 직접 커밋한다 (Claude 가 `git commit` 하지 말 것). 또한 인수인계서/메모리에 자기참조 해시를 쓰려면 코드 커밋이 먼저 이뤄져야 한다 (§3 참조). **How to apply:** -- 작업 완료 응답 마지막에 별도 섹션으로 커밋 메시지 제시 -- 한국어 **타이틀 한 줄** (한 작업 = 한 커밋, 본문 불릿은 일반적으로 불필요) +- 작업 완료 응답 마지막에 **커밋 메시지 2개** 별도 섹션으로 제시 + - **1차 (코드)**: 한국어 타이틀 한 줄. 변경 핵심 간결히. + - **2차 (문서/메모리)**: `docs: <태그> 인수인계서/메모리 갱신 (<해시>)` 형식. `<해시>` 자리는 사용자가 1차 커밋 후 알려주면 채움. - **티켓번호(`[1845]` 등) prefix 는 임의로 붙이지 말 것** — 사용자가 직접 추가 -- 변경 핵심을 간결히 (무엇을/어디서/어떻게 압축형) ## 2. 작업 완료 시 인수인계서 동시 갱신 (커밋 신호 기다리지 말 것) @@ -33,9 +33,23 @@ type: feedback - 갱신 방식: **기존 §번호 보존**, 새 항목은 **마지막 번호 다음에 신규 섹션으로 append** (기존 §번호를 미루지 말 것 — diff 부담↑) - 새 섹션에는 **작성일(YYYY-MM-DD)**, 배경(무슨 일이 있었나), 한 일(파일·라인·태그), 후속 확인 포인트 - 사소한 변경(오타·포맷팅 등) 인수인계서 정책에 영향 없으면 갱신 생략 가능. 의심되면 갱신 쪽으로 기울 것. -- 인수인계서 갱신은 같은 커밋에 묶을지 별도 커밋으로 분리할지 사용자에게 안내 +- **커밋 해시 자리는 `[TBD-CODE]` placeholder** 로 두고 §3 의 2단계 커밋 흐름에 맡긴다. -## 3. 커밋 해시는 자동 로그에서 참조 (수동 기록 X) +## 3. 2단계 커밋 — 선(先)코드, 후(後)문서/메모리 (2026-05-06 신규) + +**Why:** UI_MODAL_HANDOVER §1.1 표처럼 "어떤 작업이 어느 커밋인지" 인수인계서/메모리에 정확한 해시로 추적하려면, 코드 커밋이 먼저 이뤄지고 그 해시를 사후에 인수인계서/메모리에 적어야 함. 한 번에 커밋하면 자기참조 해시를 쓸 수 없음. + +**How to apply:** +- 응답 구성: 코드 + 인수인계서/메모리 모두 같은 응답에서 작성. 해시가 들어갈 자리는 `[TBD-CODE]` placeholder 표시. +- 커밋 가이드 (사용자가 직접): + 1. **1차 — 코드 파일만 stage 후 커밋** → 해시 확보 (`git rev-parse --short HEAD`) + 2. 사용자가 해시를 Claude 에게 알려줌 + 3. Claude 가 인수인계서/메모리의 `[TBD-CODE]` → 실제 해시로 교체 + 4. **2차 — 인수인계서 + 메모리 stage 후 커밋** +- post-commit 훅의 `commit_log.txt` 에는 두 커밋 모두 자동 append (§4 참조) +- 인수인계서/메모리 변경만 단독으로 있는 작업(코드 변경 없음)은 1단계로 끝. + +## 4. 커밋 해시는 자동 로그에서 참조 (수동 기록 X) 2026-05-06 부터 qcast-front 저장소에 git `post-commit` 훅이 설치되어, 커밋 직후 자동으로 `~/.claude/projects/-Users-cha-Works-Projects-qcast3-qcast-front/memory/commit_log.txt` 에 한 줄 append 된다. diff --git a/.claude/memory/project_low_pitch_warning.md b/.claude/memory/project_low_pitch_warning.md new file mode 100644 index 00000000..1471c5f1 --- /dev/null +++ b/.claude/memory/project_low_pitch_warning.md @@ -0,0 +1,28 @@ +--- +name: 저구배+특정기와 안내 alert +description: 6종 기와(和瓦A/B, 平板瓦C/D, S瓦, セメント瓦) + 寸 2~2.5 미만 저장 시 시공 매뉴얼 안내 alert (OK 만) +type: project +--- + +저구배(2 ≤ pitch < 2.5 寸 OR 11.31 ≤ angle < 14.04°) 에서 6종 기와 사용 시 저장 직전에 안내 alert 띄움 (OK 만, 중단 흐름 없음). (도입 커밋 `[TBD-CODE]`) + +대상 6종 (`getRoofMaterialList` 응답 기준): +- `ROOF_ID_WA_53A`, `ROOF_ID_WA_53B` +- `ROOF_ID_HIRA_C`, `ROOF_ID_HIRA_D`, `ROOF_ID_HIRA_S`, `ROOF_ID_HIRA_SEME` + +트리거 3 곳: +- `PlacementShapeSetting.handleSaveBtn` (배치면 초기설정 저장) +- `useRoofAllocationSetting.handleSave` (屋根面の割り当て) +- `useRoofAllocationSetting.handleSaveContext` (屋根材変更 후 할당) + +판정/confirm 헬퍼: `src/util/roof-pitch-warning.js` +i18n 키: `modal.roof.material.low.pitch.warning` + +**Why:** 저구배에서 이 6종 기와 제품군은 적용 가능한 부속/구조물에 제한이 있어, 사용자에게 시공 매뉴얼 사전 확인을 강제할 필요. 일본어 메시지 "2寸以上2.5寸未満の瓦を使用する場合…" 그대로 번역 사용. + +**How to apply:** +- 6종 변경 시 `LOW_PITCH_RESTRICTED_ROOF_IDS` Set 만 수정 (3 트리거 핸들러는 수정 불필요) +- 임계값 변경 시 `PITCH_LOW_INCLUSIVE` / `PITCH_HIGH_EXCLUSIVE` / `ANGLE_*` 상수 수정 +- 寸 / 度 둘 다 OR 조건 — `roofAngleSet` 이 `'slope'` 면 pitch 만, `'flat'` 이면 angle 만 갱신될 수 있어서. 한쪽만 검사하면 빠뜨림. +- 등록된 지붕재마다 순차 alert. OK 만 있고 중단 흐름 없음 — OK 누르면 저장 진행. +- 세션 캐시 없음, 매번 표시. diff --git a/.gitignore b/.gitignore index 317a0dab..7d3a45bb 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,12 @@ yarn.lock package-lock.json pnpm-lock.yaml certificates -.ai \ No newline at end of file +.ai + +# [auto-untracked 2026-05-06] 머신별 자동 산출물 / 알 수 없는 파일 +.claude/memory/commit_log.txt +.claude/settings.local.json +last_updated.log +claude-501/ +sl + diff --git a/docs/UI_MODAL_HANDOVER.md b/docs/UI_MODAL_HANDOVER.md index c1db22d5..87c24aa7 100644 --- a/docs/UI_MODAL_HANDOVER.md +++ b/docs/UI_MODAL_HANDOVER.md @@ -125,11 +125,44 @@ UI 옵션을 켜고 끄는 토글 스위치를 일본어 모달들에 추가한 --- -## 9. 인수 직후 권장 절차 +## 9. 저구배 + 특정 기와 시공 매뉴얼 안내 (2026-05-06, 커밋 `[TBD-CODE]`) + +### 9.1 무엇을 +6종 기와 제품을 **저구배 (2 ≤ 寸 < 2.5 또는 11.31° ≤ 角度 < 14.04°)** 로 사용하려 할 때, 저장 직전에 시공 매뉴얼 확인 안내 alert 을 띄움 (OK 만 — 중단 흐름 없음). + +### 9.2 트리거 3 곳 +| # | 트리거 | 핸들러 | +|---|---|---| +| 1 | 配置面 초기설정 → 「保存」 | `PlacementShapeSetting.jsx` `handleSaveBtn` | +| 2 | 平面図入力 → 「屋根面の割り当て」 | `useRoofAllocationSetting.js` `handleSave` | +| 3 | [配置面]屋根材変更 → 「屋根面の割り当て」 | `useRoofAllocationSetting.js` `handleSaveContext` | + +### 9.3 대상 屋根材 6종 (`getRoofMaterialList` 응답 기준) +`ROOF_ID_WA_53A` (和瓦A), `ROOF_ID_WA_53B` (和瓦B), `ROOF_ID_HIRA_C` (平板瓦C), `ROOF_ID_HIRA_D` (平板瓦D), `ROOF_ID_HIRA_S` (S瓦), `ROOF_ID_HIRA_SEME` (セメント瓦) + +### 9.4 동작 +- 안내 alert (OK 만). **중단 흐름 없음** — OK 누르면 저장 진행. +- 등록된 지붕재가 여러 개일 경우, 해당 항목마다 **순차로** alert. +- 세션 캐시 없음 — 트리거마다 매번 표시. + +### 9.5 핵심 파일 +| 파일 | 역할 | +|------|------| +| `src/util/roof-pitch-warning.js` | `LOW_PITCH_RESTRICTED_ROOF_IDS` Set + `isLowPitchRestricted` + `notifyLowPitchRestrictionForRoofs` | +| `src/locales/{ko,ja}.json` | i18n 키 `modal.roof.material.low.pitch.warning` | + +### 9.6 임계 변경 시 주의 +- 寸 / 度 둘 다 OR 조건. `roofAngleSet` 이 `'slope'` 면 `pitch` 만, `'flat'` 이면 `angle` 만 갱신될 수 있어서. +- 6종 ID 변경은 `LOW_PITCH_RESTRICTED_ROOF_IDS` 만 수정하면 됨. 트리거 3개 핸들러는 수정 불필요. + +--- + +## 10. 인수 직후 권장 절차 1. 이 문서 통독. 2. 계산기 토글 모달 1개 (예: 보조선 사이즈변경) 직접 띄워서 동작 확인. 3. 외벽선 확정 다이얼로그 동작 확인 (Enter 키 포함). 4. 라인 오버 → 평행 자동 전환 확인 (SK 트랙 §8.1 와 함께). +5. 저구배 + 6종 기와 confirm 동작 확인 (배치면 초기설정에서 和瓦A + 寸=2 저장 → 팝업 확인). 끝. From 4ebe4cb455d7b693071254b228d92667be01d5d7 Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 6 May 2026 17:43:29 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[1845]=20=EC=8B=A4=EB=82=B4=EC=A7=91?= =?UTF-8?q?=EC=A4=91=ED=98=95=20PCS=20=EC=88=98=EB=8F=99=20=ED=9A=8C?= =?UTF-8?q?=EB=A1=9C=EB=B6=84=ED=95=A0=20=EB=8F=99=EC=9D=BC=20=EB=B0=A9?= =?UTF-8?q?=EC=9C=84/=EA=B5=AC=EB=B0=B0=20=EA=B0=95=EC=A0=9C=20=E2=80=94?= =?UTF-8?q?=20INDFCS=20+=20=EC=88=98=EB=8F=99=20=EB=B6=84=ED=95=A0?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=8B=A4=EB=B0=A9=EC=9C=84/=EB=8B=A4?= =?UTF-8?q?=EA=B5=AC=EB=B0=B0=20=EC=A7=81=EB=A0=AC=ED=9A=8C=EB=A1=9C=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1=20=EC=B0=A8=EB=8B=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/MODULE_CIRCUIT_HANDOVER.md | 9 +++++++++ .../step/type/PassivityCircuitAllocation.jsx | 10 +++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/MODULE_CIRCUIT_HANDOVER.md b/docs/MODULE_CIRCUIT_HANDOVER.md index 0e0171b3..444a4254 100644 --- a/docs/MODULE_CIRCUIT_HANDOVER.md +++ b/docs/MODULE_CIRCUIT_HANDOVER.md @@ -49,6 +49,15 @@ - **태그**: `[INDFCS-SAME-ROOF 2026-05-06]` - **회귀 체크**: ① INDFCS 동일 방위/구배 누적 ② INDFCS 1차 동향 후 2차 남향 추가 차단 ③ 북면 혼합 차단(기존) ④ OUTDMULTI 다방위 차단(기존) ⑤ 자동분할 영향 없음. +### 1.8 [1845] 보정 — 검증 단위를 PCS → 회로번호로 좁힘 (커밋 `[TBD-CODE]`) +- **작성일**: 2026-05-06 +- **배경**: 1.7 수정 직후 회귀에서 **자동분할 후 수동분할 이어가는 시나리오** 차단 발견. 자동으로 회로 1=남(south-5.5) 4장이 부여된 같은 PCS 에, 수동으로 회로 2=서(west-5.5) 4장을 묶으려 하면 alert 차단됨. 회로 1 과 회로 2 는 서로 다른 직렬회로이므로 통과돼야 정상. +- **원인**: 1.7 의 (b) 누적 패턴이 같은 PCS 의 **모든** 기존 모듈을 합집합 대상으로 잡아, 다른 회로번호까지 검증에 포함됨. HANASYS 보고 본문 "**同一の直列回路として**構成できてしまう" 에 비춰 보면 검증 단위는 PCS 가 아니라 **회로번호** 가 맞음. +- **수정**: `originHaveThisPcsModules` 를 `getCircuitNumber()` 결과 (이번 부여하려는 회로번호 문자열) 와 일치하는 모듈로 필터링한 `sameCircuitExisting` 으로 교체. 이번 선택 모듈 + 같은 회로번호 기존 모듈만 합집합 검증. +- **진단 경로**: `debugCapture.log('INDFCS-VALIDATION', ...)` 임시 삽입 → `NEXT_PUBLIC_ENABLE_LOGGING=true` 로 재현 → `debug/debug.log` 에서 `uniqueKeys` 와 `existingSameCircuitCount` 확인 후 정식화 시 제거. +- **태그**: `[INDFCS-SAME-ROOF 2026-05-06]` (1.7 과 동일 태그, 주석에 "보정 2026-05-06" 명시) +- **회귀 체크 추가**: ⑥ 자동(회로 1 = 남) → 수동(회로 2 = 서) 통과 ⑦ 수동에서 같은 회로번호로 다른 방위 묶기는 여전히 차단. + --- ## 2. 모듈 배치 / 설치 영역 diff --git a/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx b/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx index 73e52848..2a6cb226 100644 --- a/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx @@ -161,11 +161,15 @@ export default function PassivityCircuitAllocation(props) { } // [INDFCS-SAME-ROOF 2026-05-06] 실내집중형 PCS 수동 회로분할 시 - // 동일 PCS 의 같은 직렬회로 안에서 다른 방위 또는 다른 구배의 지붕면 모듈이 섞이지 않도록 차단. + // "동일한 직렬회로(=같은 회로번호)" 안에서만 다른 방위/구배 모듈이 섞이지 않도록 차단. + // 검증 대상 = 이번 선택 모듈 + 같은 PCS·같은 회로번호의 기존 모듈 합집합. // (HANASYS 보고: INDFCS + 수동 분할에서만 다방위/다구배 직렬회로 구성 가능했던 버그) - // 검증 대상 = 이번에 선택한 모듈 + 같은 PCS 에 이미 묶인 기존 모듈 (북면 검사와 동일한 누적 패턴) + // (보정 2026-05-06: 1차에서 PCS 단위 합집합으로 검증해 다른 회로번호까지 차단되던 것을 + // "同一の直列回路として" 본문 의도에 맞춰 회로번호 단위로 좁힘) + const newCircuitNumberStr = getCircuitNumber() + const sameCircuitExisting = originHaveThisPcsModules.filter((obj) => obj.circuitNumber === newCircuitNumberStr) const indfcsCombinedSurfaceTypes = new Set( - targetModuleInfos.concat(originHaveThisPcsModules).map((obj) => { + targetModuleInfos.concat(sameCircuitExisting).map((obj) => { const surface = canvas.getObjects().find((s) => s.id === obj.surfaceId) return surface ? `${surface.direction}-${surface.roofMaterial.pitch}` : '' }),